Выручка за сентябрь: как AI‑агент получает три разных ответа. dwh.. dwh. SQL.. dwh. SQL. text-to-sql.. dwh. SQL. text-to-sql. Анализ и проектирование систем.. dwh. SQL. text-to-sql. Анализ и проектирование систем. аналитика данных.. dwh. SQL. text-to-sql. Анализ и проектирование систем. аналитика данных. бизнес-метрики.. dwh. SQL. text-to-sql. Анализ и проектирование систем. аналитика данных. бизнес-метрики. Блог компании OTUS.. dwh. SQL. text-to-sql. Анализ и проектирование систем. аналитика данных. бизнес-метрики. Блог компании OTUS. ии-агенты.. dwh. SQL. text-to-sql. Анализ и проектирование систем. аналитика данных. бизнес-метрики. Блог компании OTUS. ии-агенты. искусственный интеллект.. dwh. SQL. text-to-sql. Анализ и проектирование систем. аналитика данных. бизнес-метрики. Блог компании OTUS. ии-агенты. искусственный интеллект. Карьера в IT-индустрии.. dwh. SQL. text-to-sql. Анализ и проектирование систем. аналитика данных. бизнес-метрики. Блог компании OTUS. ии-агенты. искусственный интеллект. Карьера в IT-индустрии. качество данных.. dwh. SQL. text-to-sql. Анализ и проектирование систем. аналитика данных. бизнес-метрики. Блог компании OTUS. ии-агенты. искусственный интеллект. Карьера в IT-индустрии. качество данных. семантический слой.. dwh. SQL. text-to-sql. Анализ и проектирование систем. аналитика данных. бизнес-метрики. Блог компании OTUS. ии-агенты. искусственный интеллект. Карьера в IT-индустрии. качество данных. семантический слой. Хранилища данных.

Всем привет, меня зовут Сергей Прощаев, я Tech Lead и руководитель направления Java | Kotlin разработки в FinTech & E‑commerce и преподаю на курсах разработки и архитектуры в ОТУС.

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

Знакомая картина.

На демо вендор открывает чат, пишет «покажи выручку за сентябрь по регионам», и через пять секунд на экране таблица и красивый график. Пилот согласован. Через месяц агента подключают к рабочему хранилищу, финансовый директор задаёт тот же вопрос и получает число, которое не совпадает ни с отчётом бухгалтерии, ни с дашбордом продаж. Переформулирует вопрос — получает третье.

Модель здесь обычно не главная проблема. Она хорошо пишет SQL там, где задача уже формализована.

В корпоративном хранилище часть этой формализации живёт вне схемы: в определениях метрик, в кардинальности связей, в терминах компании и в том, по какой дате принято резать период.

Если этот контекст не дан машине явно, модель восстанавливает его наугад.

Рис. 1. На учебной схеме агент легко находит ответ, а на реальном хранилище может получить несколько правдоподобных вариантов

Рис. 1. На учебной схеме агент легко находит ответ, а на реальном хранилище может получить несколько правдоподобных вариантов

От 91% до нуля: почему цифры бенчмарков не переносятся на ваше хранилище

Задача, о которой пойдёт речь, называется enterprise text‑to‑SQL — генерация SQL по вопросу на естественном языке поверх корпоративного хранилища.

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

Но тенденция одна: переход от открытых учебных данных к закрытым корпоративным резко усложняет задачу.

  • Первая — бенчмарк Spider 2.0. В версии, опубликованной на ICLR 2025, авторы собрали 632 задачи из реальных корпоративных сценариев: базы на BigQuery и Snowflake, нередко больше тысячи колонок, несколько диалектов SQL. Их агент на модели o1-preview решил 21,3% задач, тогда как на учебном Spider 1.0 — 91,2%, на BIRD — 73,0%. На 69,9 процентного пункта ниже, примерно в 4,3 раза.

Честности ради: исходный вариант бенчмарка с тех пор убрали, а на новом Spider 2.0-Snow (547 задач на Snowflake) лучшие агентные системы уже выше 90%. Так что 21,3% — не потолок, а снимок конца 2024 года.

  • Вторая — бенчмарк BEAVER. Его авторы взяли запросы не у студентов, а из логов закрытых корпоративных хранилищ: 9 128 пар «вопрос — SQL», 812 таблиц, 19 предметных областей. В среднем запрос затрагивает 4 таблицы и содержит 5,7 соединения, а заодно CTE, вложенные запросы и аналитические функции. Базовый агентный фреймворк ReFoRCE на GPT-5.2 показывает здесь 10,8%, на Claude 4.5 Sonnet — 11,4%.

Самое интересное в BEAVER — опыт с подсказками. Авторы делят задачу на пять подзадач: найти нужные таблицы, определить ключи соединения, сопоставить колонки, извлечь знание предметной области и разложить вопрос на шаги.

В среднем по моделям точность без подсказок — 9,5%. Подсказки по трём подзадачам схемы (таблицы, ключи, колонки) поднимают её до 18,9%, все пять — до 25,9%.

Цифра 30,1% из аннотации авторов получена в другом разрезе, в одну цепочку с этими её ставить не стоит.

И даже с полным набором подсказок ошибки остаются: 46,8% из них структурные (группировки, сортировки, сложные функции), 35,2% — снова выбор элементов схемы, 18,0% — фильтры.

  • Третья — статья Майкла Стоунбрейкера и Питера Чена в Communications of the ACM от 23 сентября 2026 года. Стоунбрейкер руководил проектом POSTGRES и получил премию Тьюринга, так что в нелюбви к базам данных его не заподозришь.

    По их наблюдению, «чистая» LLM на BEAVER набрала ноль, RAG, промпт‑инжиниринг и агенты подняли результат примерно до 10%, а передача модели нужных таблиц и условий соединения — примерно до 30%. По словам авторов, это разница между «у технологии есть потенциал» и «технология не работает».

Меня в этих цифрах зацепила пропорция.

Подсказки о схеме и связях дают заметный прирост, хотя и не закрывают задачу целиком. Значит, один из главных дефицитов здесь — не столько умение писать SQL, сколько умение связать вопрос с конкретными данными, бизнес‑правилами и структурой запроса.

А это и есть работа аналитика.

История из первоисточника: хранилище, которое знает про J‑Term

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

Команда Стоунбрейкера два года пробовала text‑to‑SQL на настоящих хранилищах. Начали с административного хранилища MIT: Oracle, больше 1 400 таблиц, к которому ходят многие подразделения университета.

Исследователи взяли реальные запросы из лога Oracle и вместе с пользователями восстановили, какой вопрос стоял за каждым.

Авторы называют четыре проблемы, которых почти нет в учебных бенчмарках:

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

  2. Схема «гниёт». Бизнес меняется, компания покупает и продаёт подразделения, приходят требования регулятора. Стоунбрейкер приводит пример: в разных таблицах оказывается шесть колонок «salary», и одна означает сумму на руки, а другая — до вычетов.

  3. У каждого хранилища свой словарь. В данных MIT встречается J‑Term — месячный учебный период в январе, а здания там не названы, а пронумерованы. Из схемы этого не вывести.

  4. Реальные запросы сложнее учебных: по наблюдению авторов, им обычно нужны два‑три соединения, а в BEAVER в среднем уже 5,7.

Помню, как на одном из проектов у меня была похожая ситуация, только без всякого AI. Три отчёта о выручке за один месяц расходились, и каждый автор был уверен в своей цифре.

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

Ни одна из команд не ошибалась в SQL. Они по‑разному понимали слово «выручка».

Эксперимент: три запроса на один вопрос

Чтобы не рассуждать вокруг темы, я собрал учебную схему интернет‑магазина в SQLite 3 и сгенерировал данные с фиксированным seed: 1 000 заказов с конца августа по конец сентября.

Типы намеренно упрощены: суммы — целые рубли, даты — строки вида ГГГГ‑ММ‑ДД. Часть заказов отменена, часть ждёт оплаты, 165 заказов оплачены двумя платежами, по 42 есть возвраты.

Всё, что ниже, можно повторить у себя: схема, данные, запросы и разбор ошибок лежат в репозитории text‑to‑sql‑dwh‑demo на GitHub, ссылка в конце статьи.

Команда ./run.sh собирает базу с нуля, печатает результаты и сверяет их с эталоном, а CI на каждый push проверяет, что данные воспроизводятся байт в байт. Нужен только sqlite3.

CREATE TABLE orders (
  order_id    INTEGER PRIMARY KEY,
  customer_id INTEGER,
  created_at  TEXT,      -- дата создания заказа
  status      TEXT,      -- completed / pending / cancelled
  amount      INTEGER    -- сумма заказа, рубли
);

CREATE TABLE payments (
  payment_id INTEGER PRIMARY KEY,
  order_id   INTEGER,    -- связь с заказом не объявлена внешним ключом
  paid_at    TEXT,
  amount     INTEGER
);

CREATE TABLE refunds (
  refund_id   INTEGER PRIMARY KEY,
  order_id    INTEGER,
  refunded_at TEXT,
  amount      INTEGER
);

Вопрос бизнеса: «Какая выручка за сентябрь?». Ниже три запроса. Каждый из них я видел в живых отчётах, и каждый правдоподобно мог бы сгенерировать агент, глядя только на схему.

Запрос A — «самый очевидный»: сумма заказов, созданных в сентябре. Зерно — заказ.

SELECT SUM(amount)
FROM orders
WHERE created_at >= '2026-09-01'
  AND created_at <  '2026-10-01';

Запрос B — «аккуратный». Агент решает: раз показатель связан с оплатой, нужно присоединить payments, а отменённые заказы отбросить.

SELECT SUM(o.amount)
FROM orders o
JOIN payments p ON p.order_id = o.order_id
WHERE o.status = 'completed'
  AND o.created_at >= '2026-09-01'
  AND o.created_at <  '2026-10-01';

Запрос C — другая бизнес‑договорённость: чистые поступления денег. Деньги, пришедшие в сентябре, минус деньги, возвращённые в сентябре.

Я специально не называю это универсальной «выручкой»: в другой компании под выручкой может пониматься сумма отгрузок без НДС — в зависимости от принятой методологии учёта.

SELECT
  COALESCE((SELECT SUM(amount) FROM payments
            WHERE paid_at >= '2026-09-01' AND paid_at < '2026-10-01'), 0)
  -
  COALESCE((SELECT SUM(amount) FROM refunds
            WHERE refunded_at >= '2026-09-01' AND refunded_at < '2026-10-01'), 0)
  AS net_cash_collected;

Результаты на одних и тех же данных:

Запрос

Что считает

Результат

Отклонение от C

A

все заказы сентября по дате создания

4 465 500

+23,6%

B

завершённые заказы, соединённые с платежами

4 623 500

+28,0%

C

чистые поступления по дате движения денег

3 613 500

—

Самое показательное — запрос B.

Он выглядит строже A: отменённые заказы отфильтрованы, а результат получился больше. Проблем у него две, и обе независимы.

  • Первая — соединение. У payments другое зерно: одна строка — один платёж. Само размножение строк ещё не ошибка, ошибка в том, что после него суммируется o.amount — мера уровня заказа. Заказ на 20 000, оплаченный двумя платежами по 10 000, после join становится двумя строками, и SUM(o.amount) даёт 40 000. В моих данных так задвоились 138 сентябрьских заказов.

  • Вторая — время. Запрос задуман «про оплаты», а фильтр стоит по дате заказа: 44 заказа, созданные в сентябре, оплачены уже в октябре и всё равно попали в сумму.

Оба счётчика считает diagnostics.sql в репозитории.

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

Пять наблюдений

  1. Первое: модель промахивается не в синтаксисе, а в смысле. Проверкой «запрос отработал» такую ошибку не поймать, её видно только при сверке с эталоном.

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

  3. Третье: словарь компании не выводится из схемы. «Сентябрь» может быть календарным или фискальным, «Москва» — городом доставки, складом или юрлицом. Модель может узнать это из документации, каталога или логов запросов, если этот источник доступен системе и попадает в её контекст.

  4. Четвёртое: учебные бенчмарки завышают ожидания. Данные из них могли попасть в обучение, и даже современные публичные бенчмарки не воспроизводят целиком сочетание закрытых данных, корпоративного словаря и сложности рабочих запросов. Цифра из презентации вендора ничего не говорит о точности на вашем хранилище.

  5. Пятое: контекст о данных даёт заметный прирост, но не всё. В BEAVER подсказки по схеме почти удваивают точность. Ещё в 2023 году Секеда и коллеги из data.world получили похожую картину: GPT-4 отвечала на вопросы к корпоративной SQL‑базе с точностью 16%, а через граф знаний с онтологией и маппингами — 54%. При этом структурные ошибки остаются и с подсказками.

Что помогает: вынести часть смысла в машинно‑читаемую модель

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

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

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

Документация dbt прямо говорит, что MetricFlow знает типы ключей сущностей и поэтому избегает fan‑out и chasm join — ровно той ошибки, на которой споткнулся запрос B.

Отдельный рыночный сигнал дают опросы.

В исследовании Futurum Research среди более чем 800 руководителей по данным 44,5% планируют увеличить расходы на семантический слой в ближайшие 24 месяца, ещё 14,4% собираются его внедрить впервые.

Вот как выглядит метрика из запроса C в актуальной спецификации dbt Semantic Layer:

models:
  - name: fct_payments
    description: "Одна строка — один платёж. На один заказ бывает несколько платежей."
    semantic_model:
      enabled: true
    agg_time_dimension: paid_at
    columns:
      - name: payment_id
        entity: {type: primary, name: payment}
      - name: order_id
        entity: {type: foreign, name: order}
      - name: paid_at
        granularity: day
        dimension: {type: time}
    metrics:
      - name: paid
        label: Поступило денег
        type: simple
        agg: sum
        expr: amount

  - name: fct_refunds
    description: "Одна строка — один возврат."
    semantic_model:
      enabled: true
    agg_time_dimension: refunded_at
    columns:
      - name: refund_id
        entity: {type: primary, name: refund}
      - name: order_id
        entity: {type: foreign, name: order}
      - name: refunded_at
        granularity: day
        dimension: {type: time}
    metrics:
      - name: refunded
        label: Возвращено денег
        type: simple
        agg: sum
        expr: amount

metrics:
  - name: net_cash_collected
    label: Чистые поступления
    description: >
      Деньги, поступившие за период, минус возвраты за тот же период.
      Дата — дата движения денег, а не дата заказа.
    type: derived
    expr: paid - refunded
    input_metrics:
      - name: paid
      - name: refunded

Обратите внимание на время. У каждой простой метрики своя дата агрегации: paid считается по paid_at, refunded — по refunded_at.

При запросе за сентябрь слой режет каждую по её собственной дате, и именно это решение в запросе B никто не принимал.

В description задокументированы зерно таблицы и определение показателя, для человека и для агента, а машинная часть кардинальности задаётся через entities.

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

Не менее важно, что должен делать агент, если термина в слое нет. Если термин не определён или допускает несколько толкований, правильное поведение агента — не угадывать, а уточнить: «Считать по поступлению денег или по отгрузке?».

Для финансовой аналитики это важнее любой точности.

На рисунке 2 показаны пути вопроса к хранилищу: напрямую через схему и через описанный слой.

Рис. 2. Без описанного смысла агент угадывает, с ним — выбирает из записанного или переспрашивает

Рис. 2. Без описанного смысла агент угадывает, с ним — выбирает из записанного или переспрашивает

Главная мысль схемы: семантический слой не делает модель умнее. Он переносит знание о кардинальности, допустимых соединениях и определениях метрик из догадки модели в описание, которое можно проверить. А там, где описания нет, честнее спросить, чем выдать число.

Ограничения: где это не сработает

Семантический слой — не серебряная пуля.

  • Во‑первых, он не заменяет моделирование данных. dbt строит его поверх подготовленных моделей: если исходный слой запутан, сначала придётся собрать аналитические витрины, а уже на них фиксировать смысл. И кто‑то должен поддерживать описания: без этого слой гниёт так же, как хранилище, только незаметнее.

  • Во‑вторых, слой закрывает повторяющиеся вопросы про описанные метрики. Если нужной метрики в слое нет, системе нужен другой источник контекста: каталог, документация, примеры запросов. Без него агент снова восстанавливает смысл наугад.

И наконец, мой эксперимент демонстрирует механику ошибки, а не оценивает её вероятность в продакшене.

Плюс 23,6% и 28,0% — свойство моих синтетических данных, а не типичная погрешность. YAML выше — иллюстрация подхода: он лежит в репозитории рядом с запросами, но компиляция через dbt там не проверяется.

Что проверить у себя за вечер

Если вы планируете пилот AI‑аналитики или уже его ведёте, я бы начал не с выбора модели, а с такой проверки:

  1. Выпишите 10–15 вопросов, которые бизнес задаёт чаще всего, и для каждого — эталонный SQL, согласованный с владельцем метрики.

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

  3. Для каждой таблицы, которая участвует в ответах, запишите зерно: одна строка — это заказ, платёж или позиция заказа.

  4. Составьте словарь терминов компании: что такое выручка, активный клиент, регион, месяц.

  5. Опишите пять самых спрашиваемых метрик в семантическом слое и повторите шаг 2.

  6. Задайте агенту вопрос с термином, которого нет в словаре, и посмотрите: он переспросит или уверенно ответит.

Для разминки подойдёт и мой репозиторий: три таблицы и три вопроса, на которых видно, ошибётся ли ваш агент на соединении и на дате.

Мой вариант, который я обычно советую: шаги 1 и 2 сделать до разговора с вендором. Тогда на демо вы проверяете не красивый график, а свои пятнадцать вопросов.

Вывод

AI‑агенты сделали SQL дешёвым. Дорогим осталось другое — договориться о том, что значат данные, и записать это так, чтобы прочитала машина. Enterprise text‑to‑SQL — не один шаг генерации SQL, а цепочка разрешения контекста: какие данные нужны, как они связаны, что значат термины, по какой дате считать и какую структуру запроса построить. Часть этого контекста традиционно живёт в голове аналитика.

Поэтому, как мне кажется, роль аналитика с приходом агентов не уменьшается, а смещается: от написания каждого запроса к формализации смысла, на котором эти запросы строятся.

А как у вас: пробовали подключать AI‑агента к рабочему хранилищу? На чём он ошибался чаще всего — на соединениях, на терминах или на датах? Буду рад обсудить в комментариях.

Ссылка на мой репозиторий с экспериментом.

ИСТОЧНИКИ
Выручка за сентябрь: как AI‑агент получает три разных ответа - 3

Как интегрировать ИИ‑агентов с корпоративными системами и оценивать качество их работы?

Эти вопросы разберём на бесплатных демо-уроках OTUS:

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

Автор: sproshchaev

Источник