0Pricing
AI Agents · Урок

Как работают агенты NL-to-SQL

Внедрение схемы, генерация и выполнение запросов, форматирование результатов.

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

Что такое агент преобразования естественного языка в SQL

Агент преобразования естественного языка в SQL переводит вопросы на обычном языке в SQL-запросы, выполняет их в базе данных и возвращает ответы, понятные человеку.

Вместо написания SELECT COUNT(*) FROM orders WHERE status='pending' пользователи просто спрашивают: «Сколько у нас заказов в ожидании?»

Основная архитектура

Каждый агент преобразования естественного языка в SQL использует один и тот же конвейер:

  1. Внедрение схемы — внедрить структуру БД в инструкцию
  2. Генерация SQL с помощью LLM — модель создает запрос
  3. Выполнение — выполнить запрос в базе данных
  4. Форматирование результатов — преобразовать строки в понятный текст
  5. Возврат ответа — ответить пользователю
# High-level pipeline
def nl_to_sql_agent(user_question, db_connection):
    schema = get_schema(db_connection)
    sql = llm_generate_sql(user_question, schema)
    rows = execute_query(db_connection, sql)
    answer = format_results(rows, user_question)
    return answer

Объяснение внедрения схемы

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

Компактное описание схемы сообщает модели: «В таблице заказов есть столбцы: идентификатор, user_id, статус, общая сумма, created_at».

def build_schema_prompt(schema_info):
    lines = []
    for table in schema_info:
        cols = ', '.join(
            f"{c['name']} ({c['type']})"
            for c in table['columns']
        )
        lines.append(f"Table {table['name']}: {cols}")
    return '\n'.join(lines)

# Output:
# Table users: id (INT), email (VARCHAR), created_at (TIMESTAMP)
# Table orders: id (INT), user_id (INT), status (VARCHAR), total (FLOAT)

if __name__ == '__main__':
    demo_schema = [
        {'name': 'users', 'columns': [{'name': 'id', 'type': 'INT'}, {'name': 'email', 'type': 'VARCHAR'}]},
        {'name': 'orders', 'columns': [{'name': 'id', 'type': 'INT'}, {'name': 'user_id', 'type': 'INT'}]},
    ]
    print(build_schema_prompt(demo_schema))

Промпт для генерации SQL с помощью LLM

Промпт должен сообщать LLM три вещи: схему, вопрос и явные инструкции возвращать только корректный SQL.

Для безопасности и корректности крайне важно явно указать режим только SELECT и целевой диалект SQL (PostgreSQL, MySQL, SQLite).

SYSTEM_PROMPT = '''You are a SQL expert. Given a database schema and a question,
generate a valid {dialect} SELECT query. Return ONLY the SQL query, no explanation.
Do not use INSERT, UPDATE, DELETE, or DROP.

Schema:
{schema}
'''

def llm_generate_sql(question, schema, dialect='PostgreSQL'):
    prompt = SYSTEM_PROMPT.format(schema=schema, dialect=dialect)
    response = client.chat.completions.create(
        model='gpt-4o',
        messages=[
            {'role': 'system', 'content': prompt},
            {'role': 'user', 'content': question}
        ]
    )
    return response.choices[0].message.content.strip()

Выполнение сгенерированного SQL

После того как LLM вернет SQL, выполните его в реальной базе данных. По возможности используйте параметризованные запросы и всегда обрабатывайте исключения: LLM может создать некорректный SQL.

Оборачивание выполнения в конструкцию try/except позволяет повторить попытку, передав LLM подсказку об ошибке.

import psycopg2

def execute_query(conn, sql):
    try:
        with conn.cursor() as cur:
            cur.execute(sql)
            columns = [desc[0] for desc in cur.description]
            rows = cur.fetchmany(100)  # limit rows
            return {'columns': columns, 'rows': rows}
    except psycopg2.Error as e:
        return {'error': str(e), 'sql': sql}

Форматирование результатов для пользователя

Необработанные строки из базы данных неудобны для пользователя. Агент должен преобразовать их в ответ на естественном языке.

Для небольших наборов результатов передайте строки обратно LLM для интерпретации. Для больших наборов сначала вычислите сводные статистические показатели.

def format_results(result, original_question):
    if 'error' in result:
        return f'Query failed: {result["error"]}'

    rows = result['rows']
    columns = result['columns']

    if not rows:
        return 'No results found.'

    # For simple counts/aggregates — just return the value
    if len(columns) == 1 and len(rows) == 1:
        return f'Result: {rows[0][0]}'

    # For multi-row results — summarize
    summary = f'Found {len(rows)} rows.\n'
    for row in rows[:5]:  # show first 5
        summary += ', '.join(f'{columns[i]}: {row[i]}' for i in range(len(columns))) + '\n'
    return summary

if __name__ == '__main__':
    demo_result = {'rows': [[42]], 'columns': ['count']}
    print(format_results(demo_result, 'How many users signed up?'))
    demo_result2 = {'rows': [], 'columns': ['id']}
    print(format_results(demo_result2, 'Any orders today?'))

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

Неоднозначность — самая большая проблема. Рассмотрим вопрос: «Покажите мне лучших клиентов».

  • Лучших по выручке? По количеству заказов? По давности?
  • За последний месяц? За всё время?
  • 10 лучших? 100 лучших?

Люди понимают контекст, а LLM делают предположения. Агентам нужны стратегии для обработки неоднозначных вопросов или уточнения их смысла.

AMBIGUITY_PROMPT = '''If the question is ambiguous, respond with JSON:
{"needs_clarification": true, "question": "your clarifying question"}

If clear, respond with the SQL query directly.

User question: {question}
'''

def generate_or_clarify(question, schema):
    response = llm_call(AMBIGUITY_PROMPT.format(
        question=question, schema=schema
    ))
    if '"needs_clarification"' in response:
        import json
        return json.loads(response)
    return {'sql': response}

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

Корпоративные базы данных могут содержать сотни таблиц и тысячи столбцов. Внедрение полной схемы превысило бы размер контекстного окна LLM.

Возможные решения: поиск по схеме (встроить описания таблиц и извлечь релевантные), фильтрация таблиц (сначала спросить LLM, какие таблицы нужны) и сжатие схемы (исключить столбцы индексов и аудита).

# Two-phase approach for large schemas
def get_relevant_tables(question, all_tables):
    prompt = f'''Given these tables: {all_tables}
Which 3-5 tables are most relevant to answer: "{question}"?
Return a JSON list of table names only.'''
    response = llm_call(prompt)
    import json
    return json.loads(response)

def nl_to_sql_large_db(question, conn):
    all_tables = list_all_tables(conn)  # just names
    relevant = get_relevant_tables(question, all_tables)
    schema = get_schema_for_tables(conn, relevant)
    return llm_generate_sql(question, schema)

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

SQL не является универсальным. LIMIT в PostgreSQL и MySQL превращается в TOP в SQL Server. Функции для работы с датами различаются в разных базах данных. LLM должна знать, какой диалект использовать.

Всегда указывайте целевой диалект в системной инструкции и рассмотрите возможность добавления примеров для конкретного диалекта в подсказки с несколькими примерами.

DIALECT_EXAMPLES = {
    'postgresql': 'Use LIMIT for row limits. Use NOW() for current time.',
    'mysql': 'Use LIMIT for row limits. Use NOW() for current time.',
    'sqlite': 'Use LIMIT. Use datetime("now") for current time.',
    'mssql': 'Use TOP N for row limits. Use GETDATE() for current time.',
    'bigquery': 'Use LIMIT. Use CURRENT_TIMESTAMP() for current time. Use backtick for table names.'
}

def get_dialect_hint(dialect):
    return DIALECT_EXAMPLES.get(dialect.lower(), '')

if __name__ == '__main__':
    for dialect in ['postgresql', 'sqlite', 'mssql']:
        print(f'{dialect}: {get_dialect_hint(dialect)}')

Цикл восстановления после ошибок

Сгенерированный SQL часто не выполняется с первой попытки. Надежный агент реализует цикл восстановления после ошибок: отправляет LLM неудачный SQL и сообщение об ошибке и просит исправить запрос.

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

def nl_to_sql_with_retry(question, schema, conn, max_retries=3):
    sql = llm_generate_sql(question, schema)
    for attempt in range(max_retries):
        result = execute_query(conn, sql)
        if 'error' not in result:
            return format_results(result, question)
        # Ask LLM to fix the error
        fix_prompt = f'The SQL query failed with error: {result["error"]}\n'\
                     f'Original SQL: {sql}\n'\
                     f'Please fix the SQL query.'
        sql = llm_call(fix_prompt)
        print(f'Retry {attempt + 1} with fixed SQL')
    return 'Could not generate a valid query after retries.'

Объединение всех компонентов

Рабочий агент преобразования естественного языка в SQL объединяет все компоненты: извлечение схемы, построение промпта, генерацию SQL, проверку, выполнение, восстановление после ошибок и форматирование результатов.

Добавление кэширования запросов (один и тот же вопрос → один и тот же SQL) значительно сокращает задержку и расходы на LLM для повторяющихся запросов.

import hashlib

query_cache = {}

def cached_nl_to_sql(question, schema_hash, conn):
    cache_key = hashlib.md5((question + schema_hash).encode()).hexdigest()
    if cache_key in query_cache:
        print('Cache hit!')
        sql = query_cache[cache_key]
    else:
        schema = get_schema(conn)
        sql = llm_generate_sql(question, schema)
        query_cache[cache_key] = sql

    result = execute_query(conn, sql)
    return format_results(result, question)

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

Каков правильный порядок действий в конвейере агента преобразования естественного языка в SQL?

Итоги: архитектура преобразования естественного языка в SQL

Агенты преобразования естественного языка в SQL превращают вопросы на естественном языке в выполняемые SQL-запросы с помощью структурированного конвейера: внедрить схему → сгенерировать SQL → execute → format → вернуть.

Основные проблемы — неоднозначность вопросов пользователей, большой размер схем, превышающий контекстные окна, и различия диалектов SQL в разных базах данных. Циклы восстановления после ошибок обрабатывают сгенерированный LLM SQL, который не выполняется с первой попытки.

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

Урок «Как работают агенты NL-to-SQL» бесплатный?

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

Чему я научусь в уроке «Как работают агенты NL-to-SQL»?

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

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

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

Сколько времени занимает урок «Как работают агенты NL-to-SQL»?

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

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

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

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

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