3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

NL2SQL、SQLを生成する前に「要件確認」が必要だった

3
Last updated at Posted at 2026-09-06

はじめに

生成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行でまとめると、次のようになります。

  1. NL2SQL の要件確認は、業務対象、指標、時間、条件、粒度、関係、表示項目を SQL 生成前に確定する仕組みです。
  2. ルール、Ontology、LLM、ユーザー選択を組み合わせ、最も SQL の候補を減らせる質問から一つずつ確認します。
  3. ユーザーには業務用語を見せ、Embedding、物理表名、列名などの技術情報は管理者向けの補助情報として扱います。

自然言語から SQL を生成できることは重要です。

ただし、本番環境では、

SQLを生成できる

だけではなく、

なぜこのSQLになったのかを、
ユーザー自身が確認できる

ところまで設計する必要があります。

参考資料

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?