0Pricing
AI Agents · レッスン

曖昧なデータベース質問への対応

確認質問、スキーマの曖昧さの解消、複数テーブル結合の推論を学びます。

「曖昧なデータベース質問への対応」はCoddyKit上の無料AI Agentsレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAI Agents学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 AI Agentsコースには全4レッスンが含まれています。

NL-to-SQLにおける曖昧さの問題

自然言語には本質的に曖昧さがあります。誰かが「最近の注文を表示してください」と尋ねたとき、人間なら最近とは誰にとっての最近ですか?期間はどれくらいですか?どのような順序で並べますか?と確認します。

デフォルトを黙って仮定するエージェントは、ユーザーが期待していない結果を返します。優れたエージェントは曖昧さを検出し、的を絞った確認質問をします。

曖昧さの種類

データベースのコンテキストでは、曖昧な質問に共通するカテゴリーが4つあります。

  • 時間の曖昧さ:「最近」「最新」「古い」「今年」
  • 範囲の曖昧さ:「上位の顧客」—何の指標で上位ですか?
  • テーブルの曖昧さ:複数のテーブルで質問に答えられる
  • フィルターの曖昧さ:「アクティブユーザー」—何をもってアクティブと定義しますか?
# Examples of each type
ambiguous_examples = {
    'temporal': 'Show me recent orders',         # last 7 days? 30 days? 1 year?
    'scope':    'Who are the top customers?',     # by revenue, order count, or recency?
    'table':    'Show me user activity',          # from users, sessions, or audit_log?
    'filter':   'List all active products'        # active = in_stock? not discontinued?
}

if __name__ == '__main__':
    print('Types of ambiguous questions:')
    for kind, example in ambiguous_examples.items():
        print(f'  {kind}: "{example}"')

LLMによる曖昧さの検出

曖昧さのルールをハードコーディングするのではなく、LLMに曖昧さを検出させてください。スキーマを渡し、この質問は確定的なSQLクエリを生成できるほど明確か、と尋ねます。

import json

AMBIGUITY_CHECK_PROMPT = '''You are a SQL assistant. Given a database schema and a user question,
determine if the question is clear enough to write a single correct SQL query.

Schema:
{schema}

Question: {question}

Respond with JSON:
- If clear: {{"ambiguous": false, "sql": "SELECT ..."}}
- If ambiguous: {{"ambiguous": true, "clarification": "What time range counts as recent?",
  "options": ["Last 7 days", "Last 30 days", "Last 90 days"]}}

JSON:'''

def check_and_generate(question, schema):
    response = llm_call(AMBIGUITY_CHECK_PROMPT.format(
        schema=schema, question=question
    ))
    return json.loads(response)

明確化ループ

曖昧さが検出されたら、明確化ループに入ります。ユーザーに的を絞った質問をし、その回答を受け取り、補足されたコンテキストでSQL生成を再試行します。

ループは2回の明確化ラウンドに制限してください。質問が多すぎるとユーザーが不満を感じます。

def nl_to_sql_with_clarification(user_question, schema, conn, ask_user_fn):
    for attempt in range(2):  # max 2 clarification rounds
        result = check_and_generate(user_question, schema)

        if not result.get('ambiguous'):
            # Clear question — execute
            rows = execute_query(conn, result['sql'])
            return format_results(rows, user_question)

        # Ambiguous — ask user
        clarification = result['clarification']
        options = result.get('options', [])
        user_reply = ask_user_fn(clarification, options)

        # Enrich the question with the answer
        user_question = f'{user_question} ({clarification}: {user_reply})'
        print(f'Enriched question: {user_question}')

    # After 2 rounds, generate with best guess
    return check_and_generate(user_question, schema)

「最近」の時間的な曖昧さの解消

「最近」「最新」「今週」「新しい」などの時間表現は非常によく使われます。曖昧な用語をデフォルトの期間に対応付ける専用の時間解決機能を構築しつつ、確信度が低い場合はLLMが質問できるようにしてください。

from datetime import datetime, timedelta

TEMPORAL_DEFAULTS = {
    'recent':   7,    # days
    'latest':   1,    # days
    'new':      30,
    'old':      365,
    'this week': 7,
    'this month': 30,
    'this year': 365
}

def resolve_temporal(question):
    lower = question.lower()
    for term, days in TEMPORAL_DEFAULTS.items():
        if term in lower:
            since = (datetime.now() - timedelta(days=days)).strftime('%Y-%m-%d')
            return question + f" ('{term}' means since {since})"
    return question

print(resolve_temporal('Show me recent orders'))
# Show me recent orders ('recent' means since 2024-05-22)

スキーマの曖昧さの解消:複数のテーブル

複数のテーブルで質問に答えられる場合、エージェントはどのテーブルが最も適切かを推論する必要があります。たとえば、「ユーザーアクティビティ」はsessions、audit_log、user_eventsのいずれにも存在する可能性があります。

TABLE_SEMANTIC_MAP = {
    'user activity':    ['sessions', 'user_events', 'audit_log'],
    'purchases':        ['orders', 'transactions', 'invoices'],
    'product catalog':  ['products', 'items', 'listings'],
    'sign-ups':         ['users', 'registrations', 'accounts']
}

def disambiguate_tables(question, schema_dict, ask_user_fn):
    lower = question.lower()
    for concept, tables in TABLE_SEMANTIC_MAP.items():
        if concept in lower:
            available = [t for t in tables if t in schema_dict]
            if len(available) > 1:
                chosen = ask_user_fn(
                    f"Which table should I query for '{concept}'?",
                    available
                )
                return question + f" (use the {chosen} table)"
    return question

if __name__ == '__main__':
    def ask_user_fn(prompt, options):
        print(f'{prompt} -> choosing "{options[0]}" (demo default)')
        return options[0]

    demo_schema = {'sessions': [], 'transactions': [], 'orders': []}
    resolved = disambiguate_tables('Show me user activity', demo_schema, ask_user_fn)
    print('Resolved question:', resolved)

「Top N」の範囲の曖昧さへの対処

「上位の顧客」「最も優れた製品」「最もアクティブなユーザー」では、いずれもどの指標に基づくかを知る必要があります。1つを黙って選ぶのではなく、指標の選択肢をユーザーに提示してください。

RANKING_AMBIGUITY_PROMPT = '''The question asks for a ranking but the metric is unclear.

Question: {question}
Table columns available: {columns}

List 2-3 reasonable ranking metrics as a JSON array of objects:
[{{"label": "By total revenue", "sql_expr": "SUM(total) DESC"}},
 {{"label": "By order count", "sql_expr": "COUNT(*) DESC"}}]

JSON:'''

def resolve_ranking(question, columns, ask_user_fn):
    import json
    response = llm_call(RANKING_AMBIGUITY_PROMPT.format(
        question=question, columns=columns
    ))
    options = json.loads(response)
    labels = [o['label'] for o in options]
    chosen_label = ask_user_fn('How should I rank the results?', labels)
    chosen = next(o for o in options if o['label'] == chosen_label)
    return question + f" (rank by: {chosen['sql_expr']})", chosen['sql_expr']

インテリジェントなデフォルトの提示

毎回ユーザーに尋ねるのは煩わしい場合があります。よりスマートな方法は、妥当なデフォルトを選び、実行し、何を仮定したかをユーザーに伝えることです。次のような注記を含めてください:「『最近』を過去30日間と仮定しました。別の期間を意味していますか?」

def nl_to_sql_with_assumptions(question, schema, conn):
    # Resolve common ambiguities with defaults
    enriched = resolve_temporal(question)
    result = check_and_generate(enriched, schema)

    if result.get('ambiguous'):
        # Still ambiguous — pick default option
        options = result.get('options', ['the most common interpretation'])
        default = options[0]
        enriched = enriched + f' ({result["clarification"]}: {default})'
        result = check_and_generate(enriched, schema)

    rows = execute_query(conn, result['sql'])
    answer = format_results(rows, question)

    # Append assumption note
    if enriched != question:
        assumption = enriched[len(question):].strip().strip('()')
        answer += f'\n\n[Note: I assumed {assumption}]'

    return answer

列レベルの曖昧さ

曖昧さが列レベルにある場合もあります。「日付順に並べた注文を表示してください」—どの日付でしょうか?created_at、updated_at、shipped_at、それともdelivery_dateでしょうか?

スキーマの注入に列の説明を含めると、LLMの助けになり、列レベルの曖昧さを減らせます。

COLUMN_DESCRIPTIONS = {
    ('orders', 'created_at'):   'When the order was placed',
    ('orders', 'updated_at'):   'When the order was last modified',
    ('orders', 'shipped_at'):   'When the order was shipped to customer',
    ('orders', 'delivery_date'): 'Expected or actual delivery date'
}

def format_columns_with_descriptions(table, columns):
    parts = []
    for col in columns:
        desc = COLUMN_DESCRIPTIONS.get((table, col['name']), '')
        label = f"{col['name']} ({col['type']})"
        if desc:
            label += f' [{desc}]'
        parts.append(label)
    return ', '.join(parts)

if __name__ == '__main__':
    demo_columns = [
        {'name': 'created_at', 'type': 'timestamp'},
        {'name': 'shipped_at', 'type': 'timestamp'},
        {'name': 'total', 'type': 'float'},
    ]
    print(format_columns_with_descriptions('orders', demo_columns))

曖昧さの解消結果の記録

どの種類の曖昧さがどのくらいの頻度で発生し、どのデフォルトまたは明確化が選択されたかを記録してください。このデータは、デフォルト値を改善し、時間の経過とともに尋ねる質問数を減らすのに役立ちます。

import json
from datetime import datetime

ambiguity_log = []

def log_ambiguity(original_question, clarification, resolution, method):
    ambiguity_log.append({
        'timestamp': datetime.now().isoformat(),
        'question': original_question,
        'clarification': clarification,
        'resolution': resolution,
        'method': method  # 'asked_user' | 'default' | 'llm_inferred'
    })

# Periodically analyze to improve defaults
def analyze_ambiguity_log():
    from collections import Counter
    types = Counter(entry['clarification'] for entry in ambiguity_log)
    print('Most common ambiguities:')
    for q, count in types.most_common(5):
        print(f'  {count}x: {q}')

if __name__ == '__main__':
    log_ambiguity('Show recent orders', 'temporal', 'last 30 days', 'default')
    log_ambiguity('Show recent orders', 'temporal', 'last 7 days', 'asked_user')
    log_ambiguity('Top customers', 'scope', 'by revenue', 'llm_inferred')
    analyze_ambiguity_log()

マルチターン会話のコンテキスト

チャットインターフェースでは、以前のターンがコンテキストになります。ユーザーがすでに「2024年第4四半期のデータを見ています」と言っている場合、後から出てくる「最近の注文」に関する質問では、ハードコードされた30日間ではなく、その期間をデフォルトにするべきです。

def nl_to_sql_with_context(question, schema, conn, conversation_history):
    context_prompt = ''
    if conversation_history:
        context_prompt = 'Previous conversation context:\n'
        for turn in conversation_history[-3:]:  # last 3 turns
            context_prompt += f"User: {turn['user']}\n"
            if 'assumption' in turn:
                context_prompt += f"Assumption made: {turn['assumption']}\n"

    full_prompt = context_prompt + f'Current question: {question}'
    result = check_and_generate(full_prompt, schema)

    conversation_history.append({
        'user': question,
        'sql': result.get('sql', ''),
        'assumption': result.get('assumption', '')
    })
    return result

理解度チェック

NL-to-SQLエージェントが時間に関する曖昧さ(例:「最近の注文」)を検出した場合、推奨される戦略は何ですか?

まとめ:曖昧な質問への対処

NL-to-SQLにおける曖昧さには、時間、範囲、テーブル、フィルターの4つの形があります。最善の戦略は、インテリジェントなデフォルト(「最近」=過去30日間として自動的に解決する)、LLMベースの曖昧さ検出、そして質問が本当に不明確な場合の的を絞った明確化を組み合わせることです。

前提は必ずユーザーに伝えてください。コンテキストには会話履歴を使用し、時間の経過とともにデフォルトを改善できるよう、曖昧さの解消結果を記録してください。

よくある質問

「曖昧なデータベース質問への対応」レッスンは無料ですか?

はい。「曖昧なデータベース質問への対応」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、AI Agentsコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 AI Agentsコースには全4レッスンが含まれています。

「曖昧なデータベース質問への対応」で何を学びますか?

確認質問、スキーマの曖昧さの解消、複数テーブル結合の推論を学びます。 ブラウザで直接実行するハンズオンコードでAI Agentsを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

AI Agentsを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのAI Agentsは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「曖昧なデータベース質問への対応」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このAI Agentsレッスンでコードを書いて実行できますか?

はい。すべてのAI Agentsレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. NL-to-SQLエージェントの仕組み
  2. スキーマの理解と注入
  3. SQLクエリの生成と検証
  4. 曖昧なデータベース質問への対応
← AI Agentsに戻る