TECH BLOG
LangGraphで作る“壊れにくい”データ分析AIエージェント設計 -Text2SQLを捨てたら、営業分析AIがちゃんと動き始めた話-
目次
こんにちは、ARISE analyticsの多田です。約1年前に中途入社して以降、AIエージェントの開発と顧客導入支援に携わっています。
昨今、AIエージェントへの関心が急速に高まっており、多くの企業で導入に向けた検討が進められています。一方で、多様なスキーマ・日々更新される業務データをリアルタイムに分析し回答を返す──“よしな”な分析ができるAIエージェントの実現には、まだまだ技術的な課題があるのが現状です。
本記事では、私たちが営業向けのデータ分析エージェントを作る中で直面した課題と、Text2SQL(一発で自然言語→SQL)ではない方法で課題を解決するための工夫として、 LangGraphを用いてデータ分析を段階的に制御する設計を紹介します。
この記事のゴール
-
Text2SQLの課題感を知る
- LangGraphで段階的フローにより、ツール選択の自由度を制御しながらデータ分析を行う設計を理解する
はじめに
数字に求める厳格さ
私たちのプロジェクトでは、営業部門へのAIエージェント導入を支援しています。
「AI活用において成功の鍵を握るのは、業務ごとに「許容できないズレ」の単位が何かを正確に理解することです。
例えば営業現場では、提示される数値がそのまま行動判断や評価に用いられます。特に「達成したか・しなかったか」が重要視される指標では、達成率が限りなく目標に近くても、一件の未達が契約条件やインセンティブといった形で即座かつ確定的な影響を及ぼすため、「1件」や「1台」といった単位のズレが許容されません。
これは、他の領域の精度が重要でないという意味ではありません。例えばマーケティングでは、需要予測の数%のズレが、生産計画や広告予算の大きな機会損失に繋がります。カスタマーサポートにおいては、一件の誤った案内が顧客の信頼を大きく損なう原因となります。ただしこれらの領域においては、単一の数値の厳密さよりも、傾向や相対的な変化から得られる示唆が価値を持つことが多いため、AIエージェントの導入が比較的進みやすい傾向があります。
示唆やトレンドが意思決定を支える領域と、数値の正確性が意思決定の前提となる領域──この前提条件の違いが、AIに求められる設計思想を分け、後者におけるAI活用を難しくしています。

データ分析エージェントとは(RAGとの違い)
RAGは、基本的に「既に存在する文書・ナレッジ」を検索して答えるユースケースで使用されます。例えばカスタマーサポート領域(膨大なマニュアルから該当箇所を見つけ出して回答する)などにおいては、非常に強力な技術です。
一方で、データ分析において扱うのは、多くの場合リアルタイムな数字です。既存のナレッジをもとに答えを一つに定めるRAGとは異なり、分析では時々刻々と変わるデータをもとに答えを出す必要があります。
例えば、営業の分析業務はざっくりと以下のようなフローをたどることが多いです。エージェントで実現する場合でも、リアルタイムにデータを取得し、その場で集計・加工し、必要なら追加のデータを取りに行く反復処理が必要です。
データ管理の体系的なフレームワークを理解する上では、DMBOK(Data Management Body of Knowledge)[3]を引用するのが良いでしょう。
DMBOKでは、適切なデータ管理を行う上で必要な10の知識領域を定義しています。これらの領域は相互に連携し、組織全体のデータ資産価値を最大化する包括的なエコシステムを形成すると主張しています。
■例:営業の分析業務
- 問いを立てる(「この店舗の売上が悪い要因となる商材は?」「この商材の売上が低いのは、販売プロセスのどこが原因?」)
- データソースを特定する(カテゴリ・粒度・期間・対象組織)
- データ抽出(SQL/BI/Excel)
- 加工(並べ替え、上位/下位抽出、前年差・前月比、構成比など)
- 追加の深掘り(関連指標、分解、比較)
- 答えを導く(根拠とともに)
これをルールベースで作りこむことは、分岐が爆発的な規模になり、現実的ではありません。また、この領域でありがちな手法としては、Text2SQL(自然言語の問いからSQLクエリを生成しデータ取得を行う)がありますが、この手法に関する課題は次の章で触れます。
なぜText2SQLでは難しいのか
Text2SQLの特徴:一発変換に寄りやすい設計
Text2SQLは、自然言語をSQLに変換して実行し、結果を返すアプローチです。
構造としてはシンプルですが、設計上「一発で正しいSQLを生成する」方向に寄りやすいです。
一方、実務の分析質問には、SQLに落ちる前の不確実性が多く含まれます。
- 曖昧語(「悪い」「伸びている」「主力」)
- 定義依存(達成率の分母、期間、粒度)
- 手順依存(まず並べる → 下位を抽出する、など)
- スキーマ選択の不確実性
これらはSQLの構文能力の問題というより、分析意図や手順が明示されていないこと自体が難所になっています。一発変換に寄せるほど、この曖昧さは内部で解釈され、外から検証しにくい形でSQLに固定されてしまいます。

私たちのユースケースでは
私たちの営業向けデータ分析エージェントに当てはめると、Text2SQLの弱点がそのまま表に出てきます。
- スキーマがデータカテゴリごとに異なる
粒度・結合キー・テーブル構造が切り替わるため、ユーザーの質問とスキーマの対応付けが難しい。 - 表記揺れが多い
組織名・指標名が入力揺れし、DB上の正式キーと一致しにくい。 - “良し悪し”が条件ではなく手順で定義される
「達成率が悪い」は閾値よりも、ソートして下位を取る、という分析手順で語られる。
これらを一発のSQL生成に押し込めるほど、複雑さと失敗時の切り分け不能性が増します。
エビデンス:Text2SQLは依然として「構造的に難しい」
研究コミュニティにおいても、LLM時代に入った現在でも、Text2SQLは依然として構造的に難しい課題であると整理されています。近年の包括的なサーベイでは、Text2SQLにおける主要な課題として、以下の点が挙げられています[1]。
- schema linking(参照すべきテーブルやカラムの選択)
- JOIN や GROUP BY、ネスト構造を含む複雑なクエリ生成
- 曖昧さの解消や、マルチターンでの自己修正
- 実行結果に基づく妥当性検証
特に、エラー分析を行った研究では、Text2SQLの失敗要因として、schema linking、JOIN、GROUP BY に起因するものが大きな割合を占めることが示されています[2,3]。これは、SQLが文法的に正しいかどうか以前に、「どのテーブル・どのカラムを参照すべきか」という判断で誤りやすいことを意味しています。
また、Spiderに代表される複数ドメイン・複数データベースを跨ぐベンチマークは、Text2SQLの評価対象として、単純なSELECT文ではなく、クロスドメインかつマルチテーブルな構造を前提に設計されています[4]。このこと自体が、Text2SQLの難しさが実世界のデータ構造に起因していることを示しています。
そのため、近年では制約付きデコーディングのように、必ず妥当なSQL構文を生成するための手法も提案されています[5]。しかし、これらの手法は主に構文レベルでの安定化を目的としたものであり、業務定義の曖昧さや、どのスキーマを選択すべきかといった根本的な難しさそのものを解消するものではないと整理されています[1]。
私たちの解決アプローチ全体像
ここからは、前章で整理した課題に対して、私たちが採用している設計上の工夫を紹介します。
本稿では、営業向けリアルタイム分析エージェントを成立させるために、次の4つの工夫を組み合わせています。
- 分析プロセスを段階に分ける「段階的パイプライン」
- 状態に応じてLLMの自由度を制御する「段階的なツール解放」
- ツールごとに責務を分離する「堅牢なツール設計」
- 失敗に強くするための「内省フェーズとループ構造」
これらの4つの工夫を、以下で順に説明します。

解決アプローチ①段階的パイプライン
本稿で採用しているアプローチは、分析処理を段階に分解し、各段階でLLMの責務を明確に制御することにあります。いきなりSQLを生成させるのではなく、処理の流れを固定し、段階ごとに役割を分けることで、表記揺れの吸収や分析手順の安定化を実現します。また、データカテゴリ毎にツールを分けることで、異なるスキーマに対応します。(ツールの設計については、解決アプローチ③で触れます)
全体の流れは、次のような段階的パイプラインとして設計しています。
- 正規化
- データ取得
- 集計・加工
- 内省(充足判定)
ユーザーの質問から最終回答に至るまでの処理フローは、概念的には以下の通りです。
ユーザー質問
↓
(1) 正規化:SQLに埋め込むパラメータを正式キーに変換
↓
(2) データ取得:カテゴリ別の取得ツールで必要なデータを抽出(SQLはツール内に閉じる)
↓
(3) 集計・加工:ルール付きの加工ツールでソート・抽出・比較を実行
↓
(4) 内省:回答に必要な情報が揃っているかを判定
(不足していれば (1)〜(3) を再実行)
↓
最終回答
重要なのは、(1)〜(3)の各ステップで入力と出力の形式を固定し、LLMの自由度を小さくしている点です。これにより、処理の再現性と切り分け可能性を高めています。
LangGraphによる実装全体像(ステートマシンとしての設計)
この段階的なフローは、LangGraphが提供する「状態を持つグラフ」としての実行モデルと適合します。LangGraphでは、Stateを持つワークフローをノード間の遷移として定義します。
本設計では、全体を次のような構造で実装しています。
START
↓
init_state # 状態を初期化
↓
react_flow # ツール選択と実行(段階に応じて制限)
↓
reflect_task # 充足判定
(不足していれば react_flow に戻る)
↓
create_answer # 根拠に基づく回答生成
↓
create_suggestions # 次の質問候補を生成(任意)
↓
END
充足判定(十分かどうかの判定)と実行処理(ツール実行)を分離することで、必要な情報がそろっているかどうかにのみ集中した、精度の高い判定が可能になります。
LangGraphを用いる理由
LangGraphを採用した理由は、段階的な処理制御を明示的に記述できる点にあります。
状態に基づくツール制御
各ステップの進行状況(正規化済みか、取得済みか等)を状態として保持し、前提条件や処理順序が満たされていない操作の実行を制限できます。具体的な「段階に応じたツールの解放・制限」の設計は、次章(解決アプローチ②)で詳述します。
実行状態の永続化とデバッグ性
LangGraphはチェックポイントによる状態保存をサポートしており、スレッド単位で実行の途中状態を追跡できます。運用時に、どの段階で処理が停止したかを把握しやすくなります。
判断ノードと実行ノードの分離
ツールを実行する処理と、結果が十分かどうかを判定する処理を別ノードとして分離することで、設計が安定します。推論と行動を組み合わせる ReAct 的な実行とも相性の良い構成です。
stateに持たせる情報
stateは、処理の進行状況と収集済み情報を保持するための構造です。
最低限、以下の情報を持たせています。
- messages
会話履歴およびツール実行結果 - gathered_info
正規化結果や取得済み情報 - retrieved_data_ids / result_ids
取得データや加工結果を参照するためのID - challenge_count
内省ループの回数 - previous_available_tools
これまでに解放されたツールの履歴
解決アプローチ②段階的なツール解放
ここでは、解決アプローチ①で触れた“状態に基づくツール制御”を、実装上の「段階的なツール解放」という形に落とし込みます。
処理の段階に応じて、使用可能なツールを制限します。状態と解放されるツールの対応は、例えば次のようになります。
|
状態 |
使用可能なツール(例) |
目的 |
|---|---|---|
|
初期 |
input_normalize_* |
入力の曖昧さを解消 |
|
keyの正規化済み |
actual_data_retrieve_* |
データ取得 |
|
data_id取得済み |
actual_data_processing |
集計・加工 |
実装上は、状態に応じて使用可能なツール一覧を動的に構築します。
# 疑似コード
available = ["tool_guide", "input_normalize_a", "input_normalize_b"]
if all_inputs_normalized:
available += [
"actual_data_retrieve_a",
"actual_data_retrieve_b",
"actual_data_retrieve_c",
]
if retrieved_data_ids:
available += ["actual_data_processing"]
このように、処理可能な操作を段階的に制限することで、誤った順序でのツール実行を防ぎやすくなります。
解決アプローチ③堅牢なツール設計
ここでは、段階的パイプラインを成立させるためのツール設計の考え方を整理します。
本設計では、処理を分解すること自体が目的ではなく、失敗したときに原因を切り分けやすくすることを重視しています。そのため、各ツールには明確な責務を持たせ、後段に影響を持ち込まない設計を採用しています。
名称正規化ツール(SQLパラメータ正規化)
役割:SQLに渡すパラメータの曖昧さを解消し、後段が厳密一致で動ける状態を作る
分析パイプラインにおいて、ユーザー入力の中でも特に不安定なのが、SQLの条件句に直接渡されるパラメータです。
例えば、
「ABC代理店のXXX商材の目標達成率を知りたい」
といった質問では、次のような曖昧さが含まれます。
- 組織名は略称や表記揺れが多い
(例:株式会社ABC、ABC代理店、エービーシー など) - 指標名は業務用語・略語・言い換えが混在する
(例:目標達成率、達成率、KPI達成率 など)
これらをそのままSQLに渡すと、条件不一致や誤取得の原因になります。
そこで本設計では 、SQL文に渡すパラメータを正規化するツールを明示的に切り出します。
名称正規化ツールでは、マスタ情報に基づくRAGを用いて候補を特定し、LLMおよびルールベースの後処理を組み合わせることで、最終的なパラメータを確定させます。
ここで確定した正規化キーは、後続の取得・加工フェーズでそのまま利用できる形式にします。
正規化結果を後段で変更しない前提にすることで、以降の処理に不確実性を持ち込まない設計としています。
データ取得ツール(カテゴリ別)
役割:スキーマ差とSQL差をツール内に閉じ、LLMの入力を限定する
Text2SQLで問題になりやすいのは、どのテーブル・どのカラムを使うかという判断です。
これを避けるため、データ取得は以下の方針で設計します。
- データのカテゴリごとに取得ツールを分ける
(例:- 取引・実績に関するデータ
- イベントや時系列ログに関するデータ
- 商品・サービス単位で管理されるデータ
など)
- 引数は必要最小限に限定する
- 正規化済み組織キー
- 正規化済み指標キー
- 粒度
- 期間
- 任意フィルタ
- SQLはツール内部でテンプレート化・固定化する
この構成により、LLMは「どのデータ取得ツールを呼ぶか」だけを判断すればよくなります。スキーマやSQLの詳細をLLMに意識させないことで、失敗の起点を減らします。
データ加工ツール(制約付き加工)
役割:分析の手順をSQLから切り離し、再利用可能にする
営業分析では、データ取得後に次のような加工が頻出します。
- 並べ替え(昇順・降順)
- 上位・下位の抽出
- 前月比・前年差
- 構成比や寄与の算出
これらをすべてSQLで表現すると、クエリが複雑になり、失敗時の切り分けも難しくなります。
そのため、
- 取得はデータ取得ツールで完結させる
- 集計・加工はデータ加工ツールで実行する
という分離を行います。
データ加工ツールの実装としては、
- 小規模データの場合は、LLMによる直接加工
- 加工手順をLLMにJSON形式で構造化させ、ルールベース処理に落としこむ
- 自然言語をもとに生成したPandas処理を実行できるモジュールを使用[6]
といった複数手法を使い分けています。
いずれの場合も、許可された操作だけを行わせることが重要です。
ツール設計の共通原則
以上の設計は、次の原則に基づいています。
- 名称正規化ツール
表記揺れはここで解消し、出力は後段で変更しない - データ取得ツール
LLMはツール選択のみを行い、SQLとスキーマ差はツール内に閉じる - データ加工ツール
元の質問意図を変えない
指標定義を勝手に作らない
抽出は原則として「ソート → 上位/下位」で表現する
複数データの結合は、キーと前提条件を明示する
また、加工結果は result_id として保存し、回答生成時にはこのIDを参照して表を生成します。これにより、回答文中で根拠のない表が生成されることを防ぎます。
🔍コラム:ツールに関わる制約はどこに書くと良いのか
ツールに関わる制約は、1か所にすべてを書き切ろうとするよりも、レイヤーを分けて配置した方が壊れにくく、運用しやすくなります。特に、LLMを介してツールを呼び出す設計では、「どこで何を保証するのか」を分けて考えることが重要です。
本稿では、制約を4つのレイヤーに分けて整理します。
レイヤー1:ツールのI/Oを“型”で制約する(最優先)
最も強い制約は、ツールの入出力を型として定義することです。
- 引数の必須・任意の区別
- 列挙値の明示
- 禁止値や制約条件の明文化
これらをスキーマとして定義することで、不正な呼び出し自体を防ぐことができます。このレイヤーは、プロンプトによる指示よりも強制力が高く、設計上の最優先のガードレールになります。
レイヤー2:システムプロンプトで原則を示す
次に、システムプロンプトで全体に共通する原則を明示します。
例えば、
- 正規化は必ず先に行うこと
- 一度確定した正規化結果は変更しないこと
- “良し悪し”の判断は、原則としてソート結果に基づいて行うこと
といった方針です。
ここでは細かな手順を書くのではなく、判断の軸となる原則を共有することを意識します。
レイヤー3:段階に応じた追加ルールの注入
段階的にツールを解放する設計では、その段階で解放されたツールに関する注意点だけを追加で渡すのが有効です。初期プロンプトにすべての制約を書いてしまうと、内容が肥大化し、可読性も下がります。
代わりに、
- 今使えるツールは何か
- そのツールを使う際の注意点は何か
だけを、追加メッセージとして都度注入します。
これにより、必要なタイミングで必要な制約だけを提示できます。
なお、経験則的には、この段階でユーザーメッセージとして追加ルールを注入することが、エージェントの振る舞いを最も制御できると感じています。この点について、明確な根拠をお持ちの方がいれば、ぜひ教えてください。
レイヤー4:ツール内部での検証と拒否
最後に、ツールの内部で検証や拒否を行う仕組みを用意します。これは、最終的な防衛線にあたります。
例えば、データ加工ツールの内部で、
- 指標の定義を勝手に作らない
- 複数データの結合は、条件を満たす場合のみ許可する
- 危険性のある処理は明示的に拒否する
といったチェックを行います。
LLM側の制御がすり抜けた場合でも、ツール自体が不正な処理を実行しないようにすることが重要です。
解決アプローチ④内省フェーズとループ構造
段階的パイプラインでは、ツールを正しく実行できるだけでは不十分です。
「今の状態で本当に答えを出してよいか」を確認する工程を明示的に挟むことで、失敗に強い構造になります。
本設計では、この役割を担う内省(Reflection)フェーズをツール呼び出しとは分離した判断専用のステップとして実装しています。
内省(Reflection)を独立させる目的
内省フェーズの目的はシンプルです。答えを生成する前に、必要な情報が揃っているかを点検することにあります。具体的には、次のような観点で状態を評価します。
- 正規化結果と、実際にツールに渡した引数が一致しているか
- 「良し悪し」の判定方法(ソート、上位/下位抽出など)が質問意図に合っているか
- データ取得だけで止まっており、集計・加工工程が抜けていないか
- 深掘りに必要な追加データの取得が残っていないか
これらをツール実行の流れとは切り離し、純粋に「今の状態をどう評価するか」だけを考えさせることで、判断の安定性を高めています。
構造化出力による次アクションの安定化
内省フェーズの出力は、自由文ではなく構造化された形式で返させます。例えば、以下のような項目です。
is_completed:この状態で回答可能かadvice:不足している工程や、やり直すべき点ask_user:ユーザーに追加で確認すべき事項があるか
このように出力を構造化することで、次に取るべきアクション(再取得、再加工、質問、回答生成)が明確になります。
不足情報を再ループで補う(無限ループを防ぐ)
段階的なエージェント設計では、内省と再実行を組み合わせることで柔軟性が高まる一方、放置すると無限ループに陥りやすくなります。そのため、本設計では明示的に上限を設けています。
- ツール実行ループ:最大 N 回
- 内省のリトライ回数:最大 M 回
- 最終ループのみ、制約をやや緩めて探索を許可する
このように上限を設計しておくことで、失敗時の挙動が予測可能になり、運用面での扱いやすさが向上します。
おわりに
本記事では、営業向けのリアルタイムデータ分析エージェントを題材に、Text2SQLに頼らず、LangGraphを用いて分析プロセスを段階的に制御する設計を紹介しました。
振り返ると、本稿のゴールは大きく2つでした。
1つ目は、Text2SQLが実務のデータ分析、とくに営業分析では難しくなりやすい理由を整理することです。曖昧な業務用語、定義依存の指標、手順として語られる「良し悪し」、カテゴリごとに異なるスキーマ。これらを一発のSQL生成に押し込めると、失敗時の切り分けができなくなる、という課題感を共有しました。
2つ目は、その課題に対して、段階的パイプラインと段階的ツール解放という設計でどう向き合ったかを示すことです。正規化・取得・加工・内省という工程に分け、LLMには「判断」を、ツールには「実行」を担当させることで、再現性と運用性のある分析エージェントを目指しました。
この設計の面白さは、モデルをより賢くすることではなく、「どこで何をさせるか」を設計で決めている点にあります。SQLを直接生成させない、使えるツールを段階的に制限する、内省を独立した判断として扱う。これらはすべて、LLMの振る舞いを“当てにいく”のではなく、“壊れにくくする”ための工夫です。
リアルタイムなデータ分析エージェントは、RAGとは異なり、取得・加工・判断をその場で繰り返す必要があります。この領域では、モデル性能以上に、フローと責務の切り方が効いてくると感じています。
本記事が、「Text2SQLがうまくいかない理由を整理したい方」「分析エージェントの設計をもう一段ちゃんと考えたい方」にとって、何かしらのヒントになれば幸いです。
参考文献
[1] Li et al., A Survey on Text-to-SQL Parsing: Concepts, Methods, and Future Directions, 2023.
[2] Wang et al., RAT-SQL: Relation-Aware Schema Encoding and Linking for Text-to-SQL Parsers, ACL 2020.
[3] Deng et al., Benchmarking Text-to-SQL Parsers on Realistic Workloads, 2021.
[4] Yu et al., Spider: A Large-Scale Human-Labeled Dataset for Complex and Cross-Domain Text-to-SQL Task, EMNLP 2018.
[5] Scholak et al., PICARD: Parsing Incrementally for Constrained Auto-Regressive Decoding from Language Models, EMNLP 2021.
[6] LangChain Documentation, Pandas Dataframe Agent Toolkit.
Pandas Dataframe Agent — 🦜🔗 LangChain 0.0.107