0Pricing
AI Agents · Урок

Обработка неоднозначных вопросов к базе данных

Уточняющие вопросы, устранение неоднозначности схемы и рассуждения о соединении нескольких таблиц.

«Обработка неоднозначных вопросов к базе данных» — бесплатный урок AI Agents на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения AI Agents, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс AI Agents содержит 4 уроков всего.

Проблема неоднозначности при преобразовании естественного языка в SQL

Естественный язык по своей природе неоднозначен. Когда кто-то спрашивает «Покажите мне недавние заказы», человек уточнит: недавние для кого? за какой период? в каком порядке отсортировать?

Агент, который молча использует значения по умолчанию, выдаст результаты, которых пользователь не ожидал. Хороший агент обнаруживает неоднозначность и задаёт точечные уточняющие вопросы.

Типы неоднозначности

В контексте баз данных встречаются четыре распространённые категории неоднозначных вопросов:

  • Неоднозначность времени: «недавние», «последние», «вчера», «в этом году»
  • Неоднозначность охвата: «лучшие клиенты» — по какому показателю?
  • Неоднозначность таблицы: на вопрос могут отвечать несколько таблиц
  • Неоднозначность фильтра: «активные пользователи» — что определяет активность?
# 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)

Неоднозначность охвата для «топ-N»

Для запросов «лучшие клиенты», «лучшие товары» и «самые активные пользователи» необходимо знать, по какому показателю выполняется оценка. Предлагайте пользователю варианты показателей, а не выбирайте один молча.

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()

Контекст многоходового диалога

В интерфейсе чата предыдущие реплики дают контекст. Если пользователь уже сказал: «Я просматриваю данные за 4-й квартал 2024 года», последующие вопросы о «недавних заказах» должны использовать этот период по умолчанию, а не жёстко заданные 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

Проверка знаний

Какую стратегию рекомендуется использовать, когда агент, преобразующий естественный язык в SQL, обнаруживает неоднозначность времени, например в запросе «недавние заказы»?

Итоги: обработка неоднозначных вопросов

Неоднозначность при преобразовании естественного языка в SQL бывает четырёх видов: временная, связанная с охватом, табличная и фильтрационная. Лучшие стратегии сочетают разумные значения по умолчанию (автоматически трактовать «недавние» как последние 30 дней), обнаружение неоднозначности с помощью LLM и точечные уточнения, когда вопрос действительно неясен.

Всегда сообщайте пользователю о сделанных предположениях. Используйте историю диалога для получения контекста и регистрируйте решения по неоднозначности, чтобы со временем улучшать значения по умолчанию.

Часто задаваемые вопросы

Урок «Обработка неоднозначных вопросов к базе данных» бесплатный?

Да — полный текст урока «Обработка неоднозначных вопросов к базе данных» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс AI Agents, подпишись на CoddyKit PRO. Курс AI Agents содержит 4 уроков всего.

Чему я научусь в уроке «Обработка неоднозначных вопросов к базе данных»?

Уточняющие вопросы, устранение неоднозначности схемы и рассуждения о соединении нескольких таблиц. Ты практикуешь AI Agents с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать AI Agents?

Предыдущий опыт не требуется. AI Agents на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Обработка неоднозначных вопросов к базе данных»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке AI Agents?

Да. Каждый урок AI Agents включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Как работают агенты NL-to-SQL
  2. Понимание и внедрение схемы
  3. Генерация и проверка SQL-запросов
  4. Обработка неоднозначных вопросов к базе данных
← Назад к AI Agents