The Jerusalem PostAlabama launches OpenAI probe after artificial intelligence agent hacks Hugging Face during testingCNN TürkCumhurbaşkanlığı Kabinesi Ahlat'ta toplanacak! Yeni mesaj geliyorRTP DesportoJoão Palhinha prestes a ser anunciado como reforço do BenficaInquirerMimaropa measles-rubella vaccination coverage reaches 44%PunchThe Ark auditorium has 109,000 seats, 126 escalators, 29 elevators, says OyedepoDaily MaverickTHE INTERVIEW: Irene Bourquin: The doula putting education at the centre of pregnancy careוואלהרוכב קורקינט חשמלי בן 16 נפצע באורח בינוני בתאונה עצמית בת"אBollywood HungamaEmraan Hashmi returns to action with Gunmaaster G9, first glimpse out한겨레성공회대 이상윤 교수, 한국경영학회 융합학술대회 최우수논문발표상 수상Inquirer EntertainmentKim Chiu reminds fellow breadwinners to prioritize themselves tooSözcüABD'nin en büyük çip şirketinde ajan alarmı: Çalıp çalıp Çin'e göndermişThe Sydney Morning HeraldPrime minister dodges Perth byelection dominated by One Nation
The Daily Newsstand · Free, Always
Tuesday, August 25, 2026

Подключили LLM к базе на 253 таблицы тремя способами. Больше всех ошибались не модели

Translate

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 как идее, реабилитационный матч — в следующем эксперименте.

View the original on Хабр

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.