253 таблицы, 3 550 колонок и три комментария к схеме на всю базу. К этой красоте у нас прикручен агент, который пишет SQL за аналитиков: про него была первая статья. Агент работает через MCP: модель сама вызывает инструменты — посмотреть список таблиц, прочитать схему, выполнить запрос.

И вот на ревью очередной доработки мы разругались. Половина отдела: MCP это правильно, модель должна исследовать базу как человек. Вторая половина, включая меня: дорого и медленно, надо вшить схему в промт и не гонять модель по десять раз за схемой, которая не меняется. Спорили аргументами уровня «мне кажется». Потом надоело, и мы сделали то, что надо было сделать сразу: собрали бенчмарк на своих данных и померили.

Спойлер: не подтвердилась ни одна из позиций спора. А главную ошибку эксперимента совершили не модели.

Сначала калибровка

Финтех, миллион с лишним пользователей, шесть стран. База: MSSQL, 253 таблицы, самая широкая — 274 колонки. Описаний в схеме нет: 3 записи MS_Description на всю базу, семантика полей живёт в C#-коде приложения в виде енамов. Рабочая модель: MiniMax-M2, температура 0. Все цифры ниже — с этой конфигурации; на моделях другого класса и цены выводы могут развернуться, я это покажу.

Дисклеймер тот же, что всегда: кейсы реальные, несущественные детали изменены, цифры компании округлены. Метрики эксперимента точные.

Три подхода

A. MCP. Боевой вариант: инструменты db_list_tables / db_describe / db_query, агент сам исследует схему и выполняет запросы. Позиция первой половины отдела.

B. Schema linking. Два вызова модели: сначала ей показывают каталог всех 253 таблиц (имя плюс число колонок, всего 2 234 токена), она выбирает до 10 релевантных; потом получает полный DDL выбранных таблиц с их енамами и пишет SQL. На вопрос уходит 0.6-2.8k токенов. Моя позиция.

C. Вся схема в промт. Наивный вариант, который мы добавили третьим, чтобы было от чего отталкиваться: весь DDL плюс все 346 енамов, 58 727 токенов в каждый запрос. Никто в отделе за него не топил.

Про C мы заранее посчитали деньги, потому что «так никто не делает, это дорого». Оказалось: весь прогон C, до трёх попыток на каждый вопрос по 58k токенов — примерно 110 рублей, и это ещё без промт-кеширования, с ним дешевле. На MiniMax input стоит $0.26 за миллион токенов, и аргумент про деньги на нашей модели не работает. На Claude или GPT-4 та же арифметика дала бы другой результат, держите это в голове.

Как мерили

Сравнивать сгенерированный SQL с эталонным по тексту бессмысленно: два разных запроса могут быть оба правильными. Поэтому execution accuracy, как в академических бенчмарках Spider и BIRD: выполняем запрос, сравниваем result set с эталонным. Порядок строк и имена колонок не важны, числа с округлением, NULL и пустая строка — разные вещи.

Золотой набор: 29 вопросов, отобранных вручную из реальной истории обращений аналитиков к агенту (дубли и слишком специфичные убраны). 12 простых (одна таблица), 11 средних (2-3 джойна), 6 сложных (агрегации, оконные функции). На вопрос даётся 3 попытки. EX@1 значит совпадение с первой попытки, EX@3 значит хотя бы с одной из трёх. После неудачной попытки модель получает обратную связь по фиксированному шаблону, одинаковому для всех веток: текст ошибки БД либо «результат неверен, ожидались примерно такие колонки», без данных. Это слабая, но всё же подсказка, поэтому EX@3 — метрика с самоисправлением, в лоб со Spider её сравнивать нельзя.

Дальше честность требует признания. Эталонные ответы должен подтверждать живой аналитик, а у нас его не было: роль судьи взял агент на Claude, тот же, что собирал испытательный стенд. Компенсировали двумя проверками на каждый эталон: второй независимый SQL, написанный другим путём, обязан дать тот же результат, плюс сверка с реальностью: цифра обязана быть правдоподобной для бизнеса. Судья и испытуемые — модели разных вендоров, это снижает риск одинаковых ошибок, но не убирает его. Почему я так подробно об этом — увидите в следующем разделе.

Судья ошибся трижды. Один раз его поправила испытуемая модель

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

Ошибка первая. Вопрос «покажи склады с регионами» судья объявил неотвечаемым: в таблице Warehouses региона нет. На прогоне все три ветки дружно нашли другую таблицу, где регион есть, с реальными данными. Составитель бенчмарка ошибся ровно тем способом, который принято приписывать моделям: искал по имени вместо смысла.

Ошибка вторая, на два порядка. «Сколько активных партнёров» судья посчитал через колонку BusinessmanStatus = 1 и получил 432. Перекрёстный запрос ошибку пропустил: оба запроса честно считали одно и то же неверное множество. Поймал только sanity-чек на соседнем вопросе: по той же логике в одной из стран активных партнёров вышло ноль, при нескольких тысячах пользователей. Полез в код: BusinessmanStatus — это статус ИП для выплат, а роль партнёра лежит в таблице ролей. Правильный ответ: 33 434. Ошибка в 77 раз, со статусом «подтверждён» после перекрёстной проверки.

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

Вывод, который дороже всей таблицы результатов: перекрёстная проверка ловит ошибки SQL, но не ошибки понимания предметной области. Их ловят только инварианты бизнес-реальности («в стране с тысячами пользователей не может быть ноль активных партнёров» — вот это инвариант) и несогласие испытуемых с эталоном.

И следствие, которое надо проговорить до таблиц: три пойманные ошибки на 29 эталонов — это десять процентов известного шума разметки. Сколько ошибок не поймано, я не знаю. Поэтому дальше я не интерпретирую разницы между ветками меньше десятка процентных пунктов: они внутри шума эталонов.

Живая база и четыре дефекта, которые выглядели как результаты

Эксперимент шёл на боевой базе, доступ только на чтение. Пилот на 5 вопросах, три ветки, 15 прогонов — и 1 зачёт. При том что на простейшем вопросе все три ветки написали буквально эталонный SQL. Разбор показал: база принимает около 11 регистраций и 39 заказов в час, прогон вопроса тремя ветками идёт 2-3 минуты, а эталон считался один раз в начале. COUNT(*) успевал уехать. Сравнение мерило скорость регистраций, а не качество SQL.

Это был первый из четырёх дефектов механики, и у всех четырёх общая черта: ни один не упал с ошибкой, каждый выдавал правдоподобные цифры. Второй: прогон, запущенный не из той директории, молча взял дефолтные настройки и ушёл на другого провайдера с другой моделью — спас только чужой WAF с ответом 403, иначе мы бы измерили не ту модель и не узнали об этом. Третий жил во второй метрике (о ней ниже): из-за порядка операций она выходила строже основной, что логически невозможно — по этой невозможности его и нашли. Четвёртый: скрипт пересчёта импортировал модуль прогона и перезапустил эксперимент, потратив деньги на дубликаты.

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

Результаты

Основная метрика EX — полное совпадение result set с эталоном (EX@1 с первой попытки, EX@3 хотя бы с одной из трёх).

Ветка

EX@1

EX@3

trunc50@3

медиана латентности

₽/вопрос

tool-calls

A (MCP)

13.8%

37.9%

44.8%

104 с

4.76

24.5

B (schema linking)

31.0%

55.2%

55.2%

43 с

0.39

0

C (вся схема)

24.1%

58.6%

62.1%

22 с

3.30

0

Про вторую метрику. У 12 вопросов из 29 эталонный ответ длиннее 50 строк, а боевой промт ветки A предписывает узкие выборки с TOP N — на таких вопросах полное совпадение для неё недостижимо по построению, а не из-за качества SQL. EX@trunc50 сравнивает совпадение при усечении эталона до 50 строк (после канонизации порядка, одинаково для всех веток) и показывает подходы в прод-роли «ответь аналитику». На тех самых 12 вопросах она поднимает A с 8.3% до 25.0% по EX@3.

По категориям сложности:

Ветка

simple EX@3

medium EX@3

complex EX@3

A (MCP)

66.7%

18.2%

16.7%

B (schema linking)

66.7%

54.5%

33.3%

C (вся схема)

75.0%

36.4%

66.7%

Наивный подход, за который никто не топил, оказался не хуже лучшего — и быстрее всех. Формально C впереди B по EX@3 на один вопрос, 17 против 16, и называть это победой по точности я не буду: это шум. Устойчивое различие в другом: C вдвое быстрее B и в пять раз быстрее A. Причина контринтуитивна: B делает два последовательных вызова модели, C один, и контекст в 20 раз больше оказывается быстрее лишнего round-trip.

Моя позиция выиграла деньги и первый выстрел. B точнее всех с первой попытки: 9 вопросов против 7 у C. Возможно, короткий контекст меньше отвлекает, но на этой выборке утверждать не берусь. И B в 8 раз дешевле C — правда, в абсолюте это 11 рублей против 96 за весь прогон. На дешёвых моделях экономия схлопнулась до погрешности; на дорогих она бы решала.

На сложных вопросах schema linking споткнулся об собственную архитектуру. Complex: C взял 4 вопроса из 6, B только 2. Выборка крошечная, но паттерн совпадает с выводом работы «The Death of Schema Linking?» (Maamari et al., 2024): на сильных long-context моделях отбор таблиц перестаёт помогать, потому что ошибка отбора необратима. Если нужная таблица не попала в выборку первого этапа, второй этап её уже не вернёт. Точную частоту таких потерь (как часто нужная таблица не попадала в топ-10) мы по логам ещё не считали — первый кандидат на продолжение.

MCP в нашей прод-конфигурации проиграл обеим. Здесь важная оговорка: A — единственная ветка в боевой конфигурации (промт с TOP N и узкими выборками), B и C собраны лабораторно. Так что это результат про наш прод, а не приговор MCP как идее — чистое сравнение потребовало бы ветку A с лабораторным промтом, и это кандидат на следующий эксперимент. Что видно уже сейчас: в среднем 24.5 обращения к инструментам на вопрос, модель тратит попытки на разведку схемы через INFORMATION_SCHEMA вместо ответа. Кстати, наши 13.8% EX@1 на базе с 3 550 колонками хорошо ложатся на данные Spider 2.0 (Lei et al., 2024): на реалистичных enterprise-базах агентные системы, решающие старый Spider на 90%+, падают до 10-25%.

Вопросы, которые не взял никто

8 из 29. И это самый полезный столбец эксперимента, потому что провалы не случайные.

Три вопроса упираются в одно: семантика живёт в коде, а не в базе. Роль партнёра не нашла ни одна ветка (та самая, на которой ошибся и судья), тип франчайзингового склада — тоже. Колонки хранят int, смысл которого задан C#-енамом; в базе смысла нет физически. Ни один из трёх подходов не имеет доступа к коду приложения, и на этих вопросах они равны в своей беспомощности. Ровно из-за этой проблемы бенчмарк BIRD выдаёт моделям external knowledge к каждому вопросу: на реальных базах схема не несёт смысла значений.

Остальные пять провалов: один вопрос оказался двусмысленным («активный каталог» все три ветки поняли шире моего эталона: претензия к формулировке вопроса, не к моделям, и эталон я задним числом не правил), один провал — невалидный T-SQL от ветки B (ORDER BY внутри UNION ALL, честный незачёт), остальное — сложные джойны и грязные данные.

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

  • поле страны доставки при одном и том же ID страны содержит «Германия» (17 794 записи), «Germany» (1 226) и пустую строку. Ветка, фильтровавшая текстом, молча потеряла 17 тысяч строк;

  • мёртвая колонка IsBusinessman: False у всех ~400 тысяч пользователей. Ветки, увидевшие говорящее имя, получили пустой результат;

  • у одного склада регион записан пустой строкой вместо NULL: IS NOT NULL завышает счёт на единицу;

  • колонки фискального кода товара существуют, но не заполнены ни у одного из ~3 тысяч товаров.

Последний случай мы вынесли в отдельный мини-тест: вопрос, на который данных в базе нет, хотя колонки есть. Правильное поведение — сказать «данных нет». Результат: 0 из 3, все ветки уверенно выдали исполнимый SQL по пустым колонкам. Один вопрос — это кейс, а не метрика, но кейс показательный: аналитик получил бы пустую таблицу и сам решал бы, что она означает — «совпадений нет» или «поле не заполняется». Подход A, у которого был инструмент заглянуть в данные, им не воспользовался.

Что мы поняли

Спор «MCP против схемы в промте» был не о том. Разница между подходами оказалась меньше, чем влияние вещей, о которых мы не спорили: качества данных, места, где живёт семантика, и способности системы честно сказать «не знаю».

Если собрать выводы в порядке убывания пользы:

  1. Семантика в коде — потолок любого подхода «схема → SQL». Пока смысл значений живёт в C#-енамах, модель видит голые числа. Изолированной абляции «с енамами / без» мы не делали, но 3 из 8 общих провалов упираются именно в семантику из кода — больше, чем любая разница между архитектурами.

  2. Наивное решение обязано быть в бенчмарке. Вариант C мы добавили как грушу для битья, а он оказался не хуже лучшего и быстрее всех. Без него мы бы выбрали B и считали себя молодцами.

  3. На дешёвых моделях длинный контекст быстрее и не дороже лишнего round-trip. Быстрая прикидка для вашего случая: сравните «размер схемы в токенах × цена input × среднее число попыток» с ценой и латентностью второго вызова модели. Schema linking нужен, когда схема не влезает в контекст или input дорогой; иначе кладите всё.

  4. Перекрёстная проверка эталонов не ловит ошибки понимания домена. Инварианты бизнес-реальности — ловят. Стройте проверку на них.

  5. Бенчмарк на живой базе обязан считать эталон вплотную к запросу, иначе он меряет трафик, а не модель.

Хорошая новость для тех, кто захочет повторить: это вечер работы, если есть история вопросов. Три шага: взять 20-30 реальных вопросов аналитиков и построить к ним эталонные SQL с проверкой инвариантами; прогнать свои варианты подключения с фиксированными промтами, температурой 0 и одинаковым шаблоном обратной связи; сравнивать result set’ы, а не текст запросов. Подробности и готовые промты — в первом комментарии.

И отдельно: наши результаты не открытие, а производственное воспроизведение известных. Паритет полной схемы и schema linking на сильных моделях: The Death of Schema Linking?. Провал агентов на реальных enterprise-схемах: Spider 2.0. Необходимость внешних знаний к грязным базам: BIRD. Мы просто убедились, что всё это правда и на нашем проде, за 245 рублей.

Что меняем в проде: выбрали C. Вся схема с енамами уходит в системный промт, двухэтапный отбор таблиц не внедряем: точность не хуже, вдвое быстрее, и одна ступень вместо двух — меньше мест, где можно ошибиться необратимо. На нашей модели это решение практически бесплатное; если когда-нибудь переедем на дорогую модель или схема перерастёт контекст, вернёмся к прикидке из вывода 3.

Ограничения

Одна база (MSSQL), одна модель (MiniMax-M2 через официальный API, прогон 14.08.2026, температура 0), один прогон каждой ветки, 29 вопросов: всё на нашем наборе, без претензии на генерализацию. Повторных прогонов не было; при повторе цифры могут сдвинуться на 1-2 вопроса, а это сопоставимо со всеми разницами между B и C, поэтому их я и не интерпретирую. Устойчивые различия: латентность, деньги и профиль провалов. Эталоны строил и подтверждал агент на Claude, не человек, и трижды ошибся: дважды его поправили процедуры, один раз — сама испытуемая модель. Три пойманные ошибки из 29 эталонов дают оценку шума разметки снизу, непойманные не посчитаны. Правила зачёта фиксировались до прогонов. Эталоны, в которых доказана ошибка судьи, исправлялись, затронутые прогоны браковались целиком, и вопрос шёл в прогон заново; перезачётов задним числом не было. Сырые логи: 614 записей, включая все забракованные с причинами. Сами вопросы и схему выложить не можем, NDA.

Вердикты по-нашему: C ? — берём в прод. B ? — хорош первым выстрелом и деньгами, вернёмся к нему, когда схема перерастёт контекст или модель подорожает. A ? — в текущей прод-конфигурации; это вердикт нашему конфигу, не MCP как идее, реабилитационный матч — в следующем эксперименте.

Комментарии (5)


  1. n_cto Автор
    25.08.2026 05:56

    Собрал в один файл всё, чтобы повторить эксперимент у себя: методику (execution accuracy, правила зачёта, шаблоны обратной связи), промты веток, скрипт сравнения result set'ов и чек-лист «инварианты для каждой метрики». Лежит в закрепе канала: https://t.me/n_cto

    Вопросы по методике задавайте здесь, отвечу. Особенно интересно, если у кого-то MCP-ветка ведёт себя иначе — покажите конфигурацию.


  1. mist56
    25.08.2026 05:56

    Почему не выбран достаточно очевидный путь - собрать скил, обогащённый знаниями о схеме?


    1. n_cto Автор
      25.08.2026 05:56

      Хороший вопрос. Короткий ответ: спор в отделе был про три варианта, которые у нас уже существовали, а скила не существовало. Длинный: ветка C с енамами — это по сути и есть автоматически собранный скил (DDL плюс смыслы значений, вытащенные парсером из кода), только без ручной курации.

      Следующий шаг ровно такой, как вы описываете: курируемый файл знаний о схеме, куда дописаны описания и бизнес-правила, которых нет ни в базе, ни в енамах. Роль партнёра из статьи, которую не нашёл никто, включая меня, — как раз такое знание. Подозреваю, скил закрыл бы часть из 8 нерешённых вопросов.

      Останавливает цена: руками описывать 253 таблицы дорого, и это протухает. Рабочий план: генерировать черновик скила из кода, курировать только горячие таблицы. Если у вас есть опыт со скилом над схемой такого размера — покажите цифры, очень интересно.


      1. mist56
        25.08.2026 05:56

        На много дороже бесмысленное сжигание токенов.

        Просто это обсуждение про виды отвёрток, не вспоминая про шуруповёрт. Именно знания, причём не просто описание полей, а конкретные запросы, результаты / примеры выборок, лог граблей - именно это и делает скилл для ЛЛМ мощным инструментом, а не описание, где надо походить и в каких сапогах по полю граблей.

        И зачем руками писать описание схемы? Опыт есть, не такой жирной базы, но всё же. Да, придётся много итераций правок / дополнений делать, но - тут же как с новым сотрудником, как его на базу онбордить будете? Вот и тут так же. Документация пишется / генерится планомерно и систематично, обогащается по мере нового опыта. Это не происходит за один день. Но после определённого порога количество резко переходит в качество, ЛЛМ начинает слёту конструтировать сразу относительно правильные запросы и порой находить такое, до чего бы и сам сразу бы не допёр. Кроме того скил можно потом в каком-то виде выдавать и не техническим людям, через MCP проекции или ещё как и потом уже компания способна без айтишников отвечать на многие вопросы по бизнесу самостоятельно. Это практика, не теория.


        1. n_cto Автор
          25.08.2026 05:56

          Спасибо за обратную связь! Будут результаты—расскажу на примере