はじめに
生成AIを使った NL2SQL では、次のような流れをよく見かけます。
自然言語を入力する
↓
LLM が SQL を生成する
↓
SQL を実行する
一見すると、かなりシンプルです。
しかし、実際の業務データでは、ユーザーが入力した質問だけで SQL を一意に決められないことがあります。
たとえば、次の質問です。
今年、売上が高い部署を見たい
短くて分かりやすい質問に見えます。
しかし、SQL を作るためには、まだ決まっていないことがあります。
「今年」は暦年か、会計年度か
「売上」は受注金額か、請求金額か
「部署」は現在の所属部署か、売上発生時点の部署か
「高い」は合計か、平均か
何件表示するか
ここが大事です。
自然言語として意味が通じることと、SQL の条件が確定していることは同じではありません。
そこで必要になるのが、SQL を生成する前の「AI要件確認」です。
■ 先に結論
NL2SQL の要件確認は、単なるチャット機能ではありません。
ユーザーの質問
↓
意図を構造化する
↓
不足・曖昧な条件を検出する
↓
最も重要なことを1つ質問する
↓
ユーザーの回答で意図を更新する
↓
内容を確認してからSQLを生成する
つまり、SQL 生成前に行う小さな要件定義です。
すべてを質問する必要はありません。
SQL の意味を変える重要項目だけを確認し、並び順や最大件数などは、既定値を明示して補完する形が使いやすいと思います。
■ なぜ要件確認が必要なのか
1. 同じ言葉が複数の項目に一致する
たとえば「部署情報」という言葉が、次の両方に一致することがあります。
DEPARTMENT_ID
DEPARTMENT_NAME
このとき、システム内部では「複数の schema 要素に一致した」と判断できます。
ただし、ユーザーに次のような質問を見せても意味がありません。
Embedding 検索だけでは業務要素を一意に確定できません。
これはシステムの診断情報です。
ユーザーが答えられる質問へ変換する必要があります。
確認したい部署情報を選んでください。
□ 部署名
例:営業部、開発部
□ 部署ID
例:10、20
技術的な曖昧性を、そのまま技術用語で表示しないことが重要です。
2. 指標名だけでは計算方法が決まらない
業務で使う「売上」「利益」「顧客数」には、企業ごとの定義があります。
売上
├─ 受注金額
├─ 出荷金額
├─ 請求金額
└─ 入金金額
どれも SQL としては正しく書けます。
しかし、業務上正しい SQL は一つとは限りません。
この問題は、schema だけを LLM に渡しても解決できません。
必要なのは、指標定義、同義語、業務ルール、利用可能な関係などを管理する semantic layer です。
3. JOINできることと、JOINしてよいことは違う
外部キーが存在すれば、技術的にはテーブルを結合できます。
しかし、業務的に正しい関係とは限りません。
社員
├─ 現在の所属部署
└─ 売上発生時点の所属部署
同じ社員と部署でも、どの時点の関係を使うかによって結果が変わります。
そのため、NL2SQL では「結合可能な path」ではなく、「業務上承認された relationship path」を使う必要があります。
■ 要件確認で管理する項目
ユーザーの質問は、次のような slot に分解できます。
| 項目 | 確認する内容 | SQLへの影響 |
|---|---|---|
| Profile | どの業務定義を使うか | 使用可能なデータとルール |
| 業務対象 | 顧客、部署、商品など | FROM |
| 指標 | 売上、利益、件数など | 集計式 |
| 時間 | 期間、基準日、会計年度 | WHERE |
| フィルター | 地域、状態、商品など | WHERE |
| 粒度 | 日別、月別、部署別など | GROUP BY |
| 関係 | どの業務関係で結ぶか | JOIN |
| 表示項目 | 結果に何を出すか | SELECT |
| 並び順・件数 | 上位順、最大100件など |
ORDER BY / LIMIT
|
Fine-grained Query Understanding の研究でも、自然言語をそのまま parser に渡すだけでなく、entity recognition、entity linking、semantic parsing を分離し、質問中の表現を schema や値へ明示的に対応付ける構成が提案されています。EMNLP Industry 2022 論文
要件確認でも同じように、最初に質問を構造化することが重要です。
■ 全項目を順番に質問してはいけない
slot が8個あるからといって、8回質問すると使いにくくなります。
対象は何ですか?
指標は何ですか?
期間はいつですか?
粒度は何ですか?
並び順は何ですか?
これでは、AIというより入力フォームです。
研究では、複数の SQL 候補の不確実性を最も減らす質問を優先する Expected Information Gain の考え方が提案されています。Interactive Text-to-SQL via Expected Information Gain
考え方はシンプルです。
候補SQLが10個ある
↓
「期間」を聞いても8個残る
「売上の定義」を聞くと2個まで減る
↓
先に「売上の定義」を聞く
つまり、固定された質問順ではなく、SQL の候補を最も絞り込める質問から聞きます。
■ Sphinteractから参考にできること
Sphinteract は、NL2SQL の曖昧性をユーザーとの対話で解決する研究です。
中心となるのは、次の SRA です。
Summarize
現在の理解をまとめる
Review
曖昧な部分を確認する
Ask
必要な質問だけを聞く
少ないユーザーフィードバックを取り込み、KaggleDBQA と BIRD を使った実験では、最大42%の精度改善が報告されています。Sphinteract 論文
ここから参考にできるポイントは次の3つです。
1. まずAIの理解内容を見せる
2. 一度に一つだけ質問する
3. 回答後に質問を明確な形へ書き直す
ユーザーは SQL を修正するのではありません。
AIが理解した業務内容を修正します。
■ AmbiSQLから参考にできること
AmbiSQL は、曖昧性を検出し、選択式の質問で解決してから Text-to-SQL へ渡す仕組みです。
処理の流れは次のようになります。
自然言語の質問
↓
曖昧な表現を検出
↓
曖昧性を分類
↓
選択肢を生成
↓
ユーザーが選択
↓
質問を明確に書き直す
↓
既存のText-to-SQLへ渡す
評価では、曖昧性検出の precision 87.2%、Text-to-SQL と組み合わせた場合の exact match accuracy 改善が報告されています。AmbiSQL
ここで重要なのは、NL2SQL 本体を全部作り直さなくても、要件確認を前処理として追加できることです。
■ 中国のChatBI製品から参考にできること
Quick BI
Quick BI では、自然言語入力だけでなく、事前のデータ準備を重視しています。
主なポイントは次のとおりです。
・業務で理解しやすいデータセット名
・フィールドの説明
・データセット種別
・指標とディメンション
・同義語
・業務ロジック
・おすすめ質問
特に、企業固有の用語を「データ解釈」「同義語」「適用範囲」として知識化する考え方が参考になります。Quick BI データ準備、Quick BI 業務ロジック
つまり、実行時に毎回 LLM へ考えさせるのではありません。
事前に確定できる業務定義は、管理された知識として持たせます。
Tencent ChatBI
Tencent ChatBI の提問ガイドでは、分析質問を次の要素で整理しています。
時間 + 条件 + ディメンション + 指標
たとえば、次の質問です。
2024年の牛乳の月別売上を確認したい
これは次のように分解できます。
時間 2024年
条件 商品名 = 牛乳
ディメンション 月
指標 売上
また、曖昧な「状況」「一番良い」といった表現ではなく、具体的な指標で質問することを推奨しています。Tencent ChatBI 提問ガイド
さらに、業務習慣や時間の既定条件を知識として登録できます。Tencent ChatBI 知識庫
ここから参考にできるのは、ユーザーに質問文の書き直しを要求するだけでなく、不足 slot をシステム側で補助することです。
火山引擎 DataAgent
火山引擎の API では、曖昧な要求に対して次のような構造を返します。
{
"fuzzyRequirements": "時間範囲と指標の定義を確認する必要があります",
"suggestions": [
{
"type": "時間範囲",
"options": []
},
{
"type": "指標口径",
"options": []
}
]
}
ユーザーの選択は userChoices として次のリクエストへ渡します。火山引擎 需要澄清 API
この形は API 設計として分かりやすいです。
AIの文章だけを返す
のではなく、
何が不足しているか
どんな選択肢があるか
ユーザーが何を選んだか
を構造化して管理できます。
■ Palantir Ontologyから参考にできること
Palantir Ontology は、物理テーブルをそのままユーザーへ見せるのではなく、データを業務上の object、property、link として表現します。Palantir Ontology Overview
物理データ
EMPLOYEE.DEPARTMENT_ID
DEPARTMENT.DEPARTMENT_ID
↓ Ontology
業務オブジェクト
社員 ── 所属する ── 部署
AIP Analyst も Ontology 上の object type、object、property、link、object set を使って検索と分析を進めます。AIP Analyst Capabilities
ここから参考にできるポイントは次のとおりです。
・ユーザーには業務オブジェクトを見せる
・AIには承認済みの関係を使わせる
・検索範囲をOntology単位で制限する
・利用したデータと判断根拠を確認できるようにする
・既存の権限をそのまま継承する
「部署名を使いますか、それとも ADMIN.DEPARTMENT.DEPARTMENT_NAME を使いますか」と聞く必要はありません。
ユーザーには「部署名」と表示します。
物理列名は、管理者や開発者が必要なときだけ確認できれば十分です。
■ 実装はルールとLLMの二層に分ける
要件確認をすべて LLM に任せると、同じ質問でも結果が変わる可能性があります。
一方、すべてをルールで実装すると、表現の揺れに対応できません。
そのため、二層に分ける形が扱いやすいです。
ユーザーの質問
↓
決定論的な解析
・日付
・数値
・並び順
・最大件数
・完全一致する同義語
・承認済みの関係
↓
AIによる解析
・業務用語の曖昧性
・指標候補
・複数の解釈候補
・自由入力の再解釈
↓
厳密なschema validation
↓
ユーザー確認
構造化された選択肢を選んだ場合は、LLM を再度呼ぶ必要はありません。
自由入力や新しい曖昧性が発生した場合だけ、AI で再解釈します。
■ Query Sessionとして管理する
要件確認は、通常のチャット履歴だけで管理しない方が安全です。
たとえば、次のような状態を持ちます。
{
"status": "needs_answer",
"required_total": 3,
"required_confirmed": 1,
"missing_required": [
"metric_definition",
"time_range"
],
"can_generate_sql": false,
"turn_count": 1,
"intent_version": 2
}
回答 API には、現在の version を渡します。
{
"base_version": 2,
"question_id": "question_metric_001",
"selected_option_ids": [
"metric_billed_amount"
]
}
ここが大事です。
古い画面から送信された回答で、最新の意図を上書きしてはいけません。
version が古い場合は 409 Conflict として最新 session を返します。
同じ回答の二重送信を防ぐため、Idempotency-Key も使用します。
■ 何をブロックするか
すべての不足情報を blocker にすると、質問が多くなります。
逆に、何でも既定値で補うと、意味の違う SQL を生成します。
分け方は次のようになります。
| 項目 | 扱い |
|---|---|
| 業務対象 | 未確定ならブロック |
| 指標と計算定義 | 未確定ならブロック |
| 必須の時間範囲 | 未確定ならブロック |
| JOIN path | 未確定ならブロック |
| 必須フィルター | 未確定ならブロック |
| 並び順 | 既定値を利用可能 |
| 最大件数 | 既定値を利用可能 |
| 表示形式 | 既定値を利用可能 |
つまり、
意味が変わる項目は確認する
見せ方だけの項目は既定値を使う
という分け方です。
■ 「回答できない」も正式な結果にする
対象 Profile に必要なデータがない場合、AI が近い列を探して SQL を作るのは危険です。
このProfileでは回答できません。
不足している情報:
・請求金額の指標定義
・部署と請求データの承認済み関係
このように、回答できない理由と次の行動を表示します。
別のProfileを選ぶ
管理者に指標登録を依頼する
検索条件を変更する
「分からないので止まる」ことも、本番向け NL2SQL の重要な機能です。
■ セキュリティで確認すること
要件確認では schema、サンプル値、業務定義を LLM に渡す可能性があります。
そのため、次の境界が必要です。
・権限外のtableやcolumnを候補にしない
・masked columnのsample valueを送らない
・質問に関係するschemaだけを取得する
・自由入力やsample valueをlogへ残さない
・AIが返した未知のnodeやoptionを信用しない
・最終的にserver側でscopeを再検証する
ボタンを非表示にするだけでは認可になりません。
API を直接呼ばれても、同じ制約が適用される必要があります。
■ 評価方法
SQL の正解率だけでは、要件確認の品質を評価できません。
確認したい指標は次のとおりです。
・曖昧性を正しく検出できた割合
・不要な質問をした割合
・要件確認前後のSQL execution accuracy
・確認に必要だった平均turn数
・ユーザーが「その他」を選んだ割合
・既定値が後から修正された割合
・unanswerable判定の精度
・deterministic fallback率
また、質問文そのものも評価します。
ユーザーが質問の意味を理解できたか
選択肢の違いを説明できたか
技術用語を知らなくても回答できたか
SQL が正しくても、ユーザーが意味を理解できない確認画面では、本番業務では使いにくいと思います。
■ まとめ
NL2SQL の精度を上げる方法というと、model、prompt、few-shot example に注目しがちです。
しかし、実際にはその前に確認すべきことがあります。
ユーザーの質問は、SQLを一意に決められるほど完全か
今回の要点を3行でまとめると、次のようになります。
- NL2SQL の要件確認は、業務対象、指標、時間、条件、粒度、関係、表示項目を SQL 生成前に確定する仕組みです。
- ルール、Ontology、LLM、ユーザー選択を組み合わせ、最も SQL の候補を減らせる質問から一つずつ確認します。
- ユーザーには業務用語を見せ、Embedding、物理表名、列名などの技術情報は管理者向けの補助情報として扱います。
自然言語から SQL を生成できることは重要です。
ただし、本番環境では、
SQLを生成できる
だけではなく、
なぜこのSQLになったのかを、
ユーザー自身が確認できる
ところまで設計する必要があります。