Почему «дать чат-боту доступ к базе» — плохая постановка задачи
Text-to-SQL обещает понятный интерфейс к управленческой аналитике: руководитель спрашивает обычным языком, система строит SQL и возвращает таблицу или короткий вывод. Для небольшого бизнеса это особенно привлекательно — не каждый вопрос нужно ставить в очередь аналитикам, а типовые срезы можно получать быстрее.
Но языковая модель не является системой управления правами и не гарантирует корректность запроса. Она может выбрать не ту таблицу, перепутать дату заказа с датой оплаты, забыть исключить отменённые операции или построить тяжёлое соединение. Если модель видит техническую схему целиком и исполняет произвольный SQL под широкими правами, ошибка превращается из неточного ответа в риск для конфиденциальности и доступности базы.
Поэтому безопасная архитектура начинается не с выбора модели. Сначала бизнес определяет, какие вопросы разрешены, какие показатели считаются официальными и какие данные может видеть конкретная роль. Модель в такой системе — переводчик намерения в черновик, а не администратор базы.
Правильная граница: отдельный аналитический контур
Первое правило — не подключать помощника к основной транзакционной базе с учётной записью приложения. Лучше дать ему отдельную точку чтения: реплику, витрину или аналитическую базу, где задержка обновления данных известна и приемлема для задачи.
В этом контуре стоит публиковать не все исходные таблицы, а небольшой набор представлений с понятными бизнес-названиями. Например, вместо десятков таблиц заказов, платежей и возвратов можно открыть представление `sales_daily` с уже согласованными определениями выручки, оплаченного заказа, возврата и канала продаж. PostgreSQL позволяет управлять правами на объекты, а представления могут скрывать ненужные столбцы и фиксировать допустимую форму данных.
Это даёт три преимущества:
- модель получает короткую и однозначную схему;
- чувствительные поля не попадают в доступный каталог;
- изменение внутренней структуры ERP не требует каждый раз переучивать логику помощника.
Представление само по себе не заменяет контроль доступа. Некоторые простые представления в PostgreSQL могут быть обновляемыми, поэтому роли всё равно нужно выдавать только необходимое право `SELECT`, а настройки `security_invoker`, `security_barrier` и политики базовых таблиц — проверять на реальных сценариях.
Семантический слой важнее большого промпта
Модель не должна угадывать смысл показателей по названиям колонок. Рядом со схемой ей нужен компактный каталог метрик:
- определение показателя и формула;
- допустимые измерения и фильтры;
- часовой пояс и календарь;
- правила работы с возвратами, НДС и отменами;
- владелец показателя и дата обновления;
- примеры корректных вопросов и запросов.
Если в компании два определения маржи, система должна не выбирать одно по вероятности, а запросить уточнение. Это важный класс отказа: хороший помощник умеет сказать, что вопрос неоднозначен, и объяснить, каких данных не хватает.
Каталог можно хранить как версионируемый YAML или JSON и подмешивать только релевантный фрагмент. Для старта не нужен сложный граф знаний: десять–двадцать согласованных метрик обычно полезнее, чем автоматический доступ к сотням таблиц.
Модель предлагает запрос, шлюз решает, можно ли его выполнить
Рекомендуемый поток состоит из нескольких независимых этапов:
1. Пользователь проходит обычную корпоративную аутентификацию; система получает его роль, подразделение и допустимый контур данных.
2. К вопросу добавляются только разрешённые описания витрин и метрик.
3. Модель возвращает структурированный черновик: намерение, SQL, использованные наборы данных, ожидаемую детализацию и предположения.
4. Детерминированный шлюз разбирает SQL в синтаксическое дерево и проверяет политику.
5. База строит план запроса без исполнения; шлюз оценивает объём, стоимость и риск.
6. Разрешённый запрос запускается в read-only транзакции с лимитами.
7. Результат проверяется, сокращается и только затем передаётся модели для объяснения.
Проверять строку регулярным выражением недостаточно: комментарии, вложенные запросы, функции и нестандартный синтаксис легко делают такую защиту хрупкой. Нужен полноценный SQL-парсер и политика по структуре запроса.
Минимальный список проверок:
- ровно один оператор и только `SELECT`;
- разрешённые схемы, представления и столбцы;
- запрет `INSERT`, `UPDATE`, `DELETE`, `MERGE`, DDL, `COPY` и вызовов неизвестных функций;
- ограничение количества соединений, подзапросов и временного диапазона;
- обязательный `LIMIT` для детальных выборок;
- запрет чтения системных каталогов и технических схем;
- проверка контекста пользователя и версии каталога метрик.
Белый список функций особенно важен. Формально запрос может начинаться с `SELECT`, но вызванная функция способна иметь побочные эффекты. PostgreSQL отдельно управляет правом `EXECUTE` на функции; сервисной роли помощника не нужно наследовать широкие разрешения.
Три независимых предохранителя в базе
Шлюз снижает риск, но не должен быть единственной защитой. В самой базе нужны независимые ограничения.
Первый предохранитель — отдельная роль с минимальными правами. Она получает `CONNECT`, доступ только к аналитической схеме и `SELECT` на конкретные объекты. Она не владеет таблицами, не может создавать объекты и не имеет `BYPASSRLS`.
Второй — режим read-only. Документация PostgreSQL указывает, что read-only транзакция запрещает обычные команды изменения данных и DDL. Это защита от случайной записи, а не универсальная песочница: временные объекты и функции требуют отдельного контроля. Поэтому read-only нужно сочетать с правами и белым списком функций.
Третий — Row-Level Security. Политики RLS ограничивают строки в зависимости от роли или выражения, а при включённой RLS без подходящей политики действует отказ по умолчанию. Так филиал может видеть только свои операции, даже если модель забыла фильтр. Важно помнить об исключениях: суперпользователи, роли с `BYPASSRLS` и обычно владелец таблицы обходят политики. Сервисная роль text-to-SQL не должна относиться ни к одной из этих категорий.
Как не положить базу «безопасным» SELECT
Запрос только на чтение всё равно может занять процессор, память и диски. Поэтому перед запуском полезно выполнять обычный `EXPLAIN` без `ANALYZE`: он показывает план, оценочную стоимость и ожидаемое число строк, не исполняя запрос. `EXPLAIN ANALYZE` для автоматического шлюза не подходит как предварительная проверка, потому что он действительно запускает оператор.
Оценки планировщика не являются гарантией, поэтому после предварительного фильтра нужны жёсткие лимиты исполнения:
- `statement_timeout` для сессии помощника;
- небольшой лимит возвращаемых строк и размера ответа;
- отдельный пул соединений и ограничение параллельных запросов;
- очередь с приоритетом ниже критичных операций;
- отмена запроса при разрыве клиентского запроса;
- мониторинг p95 времени, таймаутов и объёма прочитанных данных.
Лучший рубеж — отдельная реплика или витрина с собственным лимитом ресурсов. Тогда тяжёлый вопрос не блокирует оформление заказа. Но реплика добавляет задержку: пользователь должен видеть отметку свежести данных, а ответы о «сегодня» — учитывать лаг.
Ответ должен быть проверяемым
Показывать только красивую фразу опасно. Пользователь должен видеть:
- использованный период и фильтры;
- определение ключевого показателя;
- время актуальности данных;
- число строк или агрегатов в расчёте;
- SQL либо его понятное раскрытие;
- предупреждение об усечении результата;
- уровень уверенности и нерешённые неоднозначности.
Для финансовых, кадровых и договорных показателей полезен режим «двух шагов»: сначала система показывает запрос и ожидаемую выборку, затем человек подтверждает выполнение или публикацию результата. Любые действия на основе ответа — изменение цены, рассылка, запись в CRM — должны проходить через отдельный типизированный инструмент, а не через тот же SQL-канал.
Логи стоит разделить. В техническом журнале достаточно идентификатора пользователя, хеша нормализованного запроса, версии политики, времени, оценки плана и результата проверки. Сами строки результата могут содержать персональные данные, поэтому их хранение должно быть отдельным решением с понятным сроком и доступом.
Как провести пилот за две недели
Начните не со свободного чата по всей базе, а с 20–30 повторяющихся вопросов одного подразделения. Например: продажи по неделям, доля возвратов, просроченная дебиторская задолженность и загрузка менеджеров.
Для каждого вопроса зафиксируйте эталонный SQL, допустимые формулировки, правильные фильтры и владельца метрики. Затем прогоните реальные вариации вопросов и измеряйте отдельно:
- долю запросов, прошедших шлюз;
- точность выбора метрики, периода и фильтров;
- совпадение результата с эталоном;
- долю правильных отказов при неоднозначности;
- p95 времени ответа и долю таймаутов;
- стоимость одного принятого ответа с учётом проверки человеком.
Первый релиз разумно оставить в режиме черновика: помощник строит запрос и объяснение, а аналитик подтверждает результат. Автоматический показ руководителю включается только для классов вопросов, где качество стабильно и цена ошибки невысока.
Что взять руководителю
Text-to-SQL — не способ заменить права доступа естественным языком. Это управляемый аналитический продукт с узким каталогом метрик, отдельным read-only контуром и несколькими независимыми предохранителями.
Практический следующий шаг: выберите одну витрину, заведите отдельную роль только с `SELECT`, опишите десять ключевых метрик и соберите двадцать эталонных вопросов. Такой пилот быстро покажет, где мешает модель, а где настоящая проблема — несогласованные определения и качество данных. И главное: даже очень убедительный SQL остаётся черновиком, пока политика и база не разрешили его выполнить.
