Перейти к содержанию
Innovialabs

Text-to-SQL: как задавать вопросы базе данных обычным языком

Практика4 мин

Коротко

LLM может превращать вопрос «сколько заказов было в марте по филиалам» в SQL, но работает это надёжно только с описанной схемой, отдельным read-only пользователем, проверкой запроса перед запуском и показом самого SQL рядом с ответом.

Как это работает

Text-to-SQL — это схема, в которой пользователь пишет вопрос обычным языком, языковая модель составляет SQL-запрос, система выполняет его в базе и возвращает таблицу или короткий ответ.

Цепочка выглядит так:

  1. Пользователь спрашивает: «Какие пять товаров продавались лучше всего в прошлом месяце?»
  2. Бэкенд добавляет к вопросу описание схемы базы и правила.
  3. LLM возвращает SQL.
  4. Код проверяет запрос и запускает его от пользователя с правами только на чтение.
  5. Результат показывается вместе с текстом запроса, чтобы его можно было перепроверить.

Модель не «знает» ваши данные. Она знает только то, что вы ей передали в контексте, поэтому качество почти целиком зависит от подготовки.

Контекст схемы: главное, что влияет на точность

Если отдать модели голый CREATE TABLE, она будет угадывать смысл колонок. Угадывает она часто неверно: status = 3 для неё ничего не значит.

Что стоит передавать:

  • Только нужные таблицы. Для вопросов о продажах не нужны таблицы логов и настроек. Лишние таблицы путают модель и тратят токены.
  • Описания колонок человеческим языком. «amount — сумма заказа в сумах с НДС», «deleted_at не пустой — заказ удалён».
  • Значения справочников. Какие статусы бывают и что они означают.
  • Связи между таблицами — какие ключи с чем соединяются.
  • Бизнес-определения. Что считается «выручкой», «активным клиентом», «прошлым месяцем» — по дате создания или по дате оплаты.
  • Несколько примеров «вопрос → правильный SQL» для типичных запросов.

Хороший приём — сделать для аналитики представления (views) с понятными именами и уже применёнными фильтрами: v_paid_orders вместо сырых таблиц с десятком условий. Модели проще, ошибок меньше.

Безопасность: только чтение и ограничения

Сгенерированный SQL — это код от непредсказуемого источника. Относитесь к нему соответственно.

  • Отдельный пользователь БД с правами только SELECT и только на разрешённые таблицы или представления. Это главная защита, а не промпт «не пиши DELETE».
  • Реплика вместо основной базы, если она есть, чтобы тяжёлый запрос не замедлил рабочую систему.
  • Таймаут на выполнение и лимит строк в результате.
  • Скрытие персональных данных. Если менеджерам не нужны телефоны клиентов, их не должно быть в доступных представлениях.
  • Журнал запросов: кто спросил, какой SQL получился, сколько он выполнялся.

Проверка запроса перед запуском

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

  • Разобрать SQL парсером и убедиться, что это ровно один оператор SELECT (или WITH ... SELECT).
  • Проверить, что используются только разрешённые таблицы.
  • Добавить LIMIT, если его нет.
  • Выполнить EXPLAIN и отклонить запрос, если план слишком тяжёлый.
import sqlglot

def validate(sql: str, allowed: set[str]) -> str:
    statements = sqlglot.parse(sql, read="postgres")
    if len(statements) != 1:
        raise ValueError("Ожидается один запрос")
    tree = statements[0]
    if tree.key not in ("select", "union"):
        raise ValueError("Разрешён только SELECT")
    tables = {t.name for t in tree.find_all(sqlglot.exp.Table)}
    if not tables <= allowed:
        raise ValueError(f"Недоступные таблицы: {tables - allowed}")
    return tree.sql(dialect="postgres")

Если запрос упал с ошибкой, можно один-два раза вернуть модели текст ошибки и попросить исправить. Бесконечные повторы не нужны.

Как ловить неправильные ответы

Опаснее всего не ошибка выполнения, а правдоподобный неверный ответ: запрос отработал, число выглядит нормально, но посчитано не то.

Что помогает:

  • Показывать SQL рядом с ответом и коротко пересказывать его смысл: «посчитаны оплаченные заказы с 1 по 31 марта».
  • Набор контрольных вопросов с заранее известными ответами. Прогоняйте его после каждого изменения промпта, схемы или модели.
  • Просить уточнение, если вопрос неоднозначен: «выручка с НДС или без?» лучше, чем молчаливый выбор.
  • Сверка с отчётами. Ключевые метрики сравнивайте с тем, что уже считает BI или бухгалтерия.
  • Кнопка «ответ неверный», чтобы собирать сложные случаи и добавлять их в примеры.

Типичные ошибки

  • Дать модели доступ к основной базе с правами записи.
  • Передать всю схему из сотен таблиц без описаний.
  • Не определить бизнес-термины, из-за чего «клиент» и «выручка» считаются по-разному в разных ответах.
  • Показывать только число без запроса — проверить его потом невозможно.
  • Ожидать, что Text-to-SQL заменит аналитика на сложных вопросах с многоэтапной логикой.

Частые вопросы

Модель видит схему и вопрос, а не обязательно сами данные: результат запроса можно не отправлять обратно в LLM. Если же нельзя передавать даже схему, рассматривайте модель, развёрнутую на своих серверах.

Поможем с этим

Если хотите не разбираться самим, а получить результат — этим мы занимаемся каждый день.

Обсудить проект

Читайте также

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.