Эта статья посвящена Jev, модели System One от TypeSafe, и тому, какие результаты она показала в бенчмарке Agentic Analytics. Jev не генерирует текст. Вы передаете ей некоторое состояние и типизированные вопросы (ответы «да/нет», рассчитанные как калиброванная вероятность, выбор одного варианта из нескольких, оценка по рубрикарю), а она возвращает типизированные ответы с оценкой уверенности примерно за 70–500 миллисекунд. Один вызов может содержать множество вопросов, и ничто в вызове не привязано к конкретному бенчмарку: состояние — это то, что вы передаете, а вопросы представляют собой простые инструкции. Это делает ее классификатором для любых переданных ей данных, и мы поручили ей две задачи: анализировать неудачный запуск и указывать, что пошло не так, а также анализировать вопрос и определять, какие таблицы для него необходимы.
- Jev классифицирует каждый сбой менее чем за секунду. При тестовом прогоне на четырех моделях более детальная таксономия показала, что наибольшую долю составляют ошибки выбора данных (преимущественно сопоставление сущностей, а не выбор неверной таблицы), а также выявила 18 ответов, которые система проверки, вероятно, оценила неверно.
- Jev также ранжирует таблицы хранилища для каждого вопроса с показателем nDCG@10 на уровне 0.951 за 0.4 секунды без какой-либо тонкой настройки (fine-tuning) и использования эталонных данных во время работы.
- Если вернуть это ранжирование агенту в качестве подсказки прямо в цикле выполнения, точность среди 20 моделей остается неизменной, а экономия шагов на наборе для разработки не подтверждается на общем тестовом наборе. Само ранжирование ценно как результат, но подсказка на его основе — нет.
Это вторая часть второго раунда. В сопутствующей статье в таблицу лидеров были добавлены GPT-6, Claude Opus 5.5 и Claude Sonnet 5.5; в этой же статье исследуется, может ли дешевый компонент поиска на базе Jev улучшить эти показатели. В первой статье было обнаружено, что обнаружение таблиц важнее их соединения (join). Модели из верхней половины таблицы лидеров находят необходимые размерные таблицы CRM в 87–99% случаев, когда они нужны; модели из нижней половины часто не находят их вовсе. На наборе из 201 вопроса пять самых точных моделей запрашивают 78% необходимых размерных таблиц, пять наименее точных — 49%, а в 43% неудачных запусков агент вообще не обращается хотя бы к одной таблице, используемой в эталонном ответе.
Это проблема поиска, а поиск — это то, что можно внедрить перед любой моделью без ее переобучения. Поэтому мы задались вопросом: если дешевый компонент ранжирует таблицы до первого запроса и агент будет проинформирован об этом, исчезнут ли сбои, связанные с выбором неверных таблиц? Ниже представлены два набора вопросов. Набор из 201 вопроса зафиксирован и оценивает каждую модель ровно один раз. Набор для разработки из 136 вопросов — это отдельный набор на том же хранилище из 18 таблиц, созданный для того, чтобы мы могли проводить эксперименты и устанавливать пороговые значения, не затрагивая основной набор. Всё, на чем настраивалась Jev, взято из набора для разработки; основной набор используется для финальной проверки на отложенных данных.
В первой статье каждый сбой был классифицирован по пяти типам (нет попытки, неверный план, неверный выбор данных, неверная реализация, ошибка во время выполнения) с использованием классификатора на базе передовой модели. Это работает, но подход является грубым и предполагает, что сбой действительно имел место. Нам требовалось решение, достаточно дешевое для запуска на каждой оценке, которое также отвечает на два более детальных вопроса: что именно пошло не так и был ли прав проверяющий модуль?
Для каждого неудачного запуска классификатору передается траектория (вопрос, эталонный и полученный SQL-код, результаты, финальный ответ) и за один вызов задаются четыре вопроса: какой из шестнадцати подтипов сбоев подходит лучше всего, какое правило системного промпта предотвратило бы его, помогла ли бы подсказка по схеме в самом начале и выглядит ли ответ правильным вопреки вердикту о сбое. Всё, что можно проверить по тексту SQL-кода (например, выполнялась ли фильтрация по дате снимка), определяется программным кодом и передается в виде факта, поэтому классификатор принимает решения только в ситуациях, требующих оценки.
В коде эти четыре вопроса представляют собой два выбора из вариантов и две вероятности да/нет, которые отправляются одним вызовом вместе с траекторией в качестве состояния. Ниже приведена усеченная структура классификатора в том виде, в котором он поставляется в репозитории:
Состояние представляет собой словарь: вопрос, эталонный SQL-код и ответ, SQL-код кандидата, наборы результатов, финальный ответ, а также факты, уже определенные кодом (например, выполнялась ли фильтрация по дате снимка). Jev отвечает на все четыре вопроса на основе этого единственного полезного блока данных.
Мы запустили его на наборе для разработки для четырех моделей (Claude Sonnet 4.6, GPT-4.1, Qwen3-Coder 30B, Gemini 2.5 Flash): 277 сбоев на 544 попытки. В результате выяснилось три вещи.
Более детальная таксономия меняет распределение самой крупной категории. Каждый подтип вложен ровно в один из пяти основных типов, поэтому оба представления сопоставимы. Согласно основным типам, неверный план является наиболее частым сбоем. Согласно подтипам, наибольшую долю составляют ошибки выбора данных (87 из 277), причем большинство из них связаны с разрешением сущностей: использование неверного идентификатора или литерала для организации (29) или отсутствие разрешения именованной организации перед запросом фактов (20). Вместе они превышают количество ошибок выбора неверной таблицы (28) или колонки (10). Подтипы выделяют ошибки разрешения сущностей и выбора неверных таблиц из категории «планирование» и локализуют их. Именно это указало на поиск таблиц как на потенциальное средство решения проблемы.
Профили сбоев зависят от модели. Gemini 2.5 Flash сдается после получения пустого результата. GPT-4.1 пишет неработоспособный SQL-код и демонстрирует около половины всех ошибок выполнения. Qwen3-Coder 30B распределяет ошибки между планом, данными и отсутствием попыток. Sonnet 4.6 ошибается в сути и допускает 6 из 7 случаев некоррректной работы проверяющего модуля. Уверенность классификатора возрастает по мере снижения возможностей моделей, что вполне ожидаемо: однозначные сбои проще идентифицировать.
Судья нуждается в аудите, и классификатор способен его провести. Метрика «Правильно вопреки сбою» оценивается для каждого сбоя, а не только для тех, которые помечены как проблемы проверки, поэтому она выявляет 37 из 277 случаев как возможные ошибки проверяющего модуля; пороговое значение 0.65 сужает этот список до 18. Анализ случаев с высокой уверенностью указал на одну особенность нашего детерминированного компаратора: он возвращает «не равно» при разнице в количестве строк еще до того, как доходит до сопоставления столбцов. В результате правильный ответ, который разбивает один вопрос на несколько запросов (по одному на каждую сущность) или возвращает временной ряд там, где эталон требует снимок, оценивается как сбой. Эта ошибка в компараторе теперь отслеживается. Категория «Правильно вопреки сбою» также недоступна в рамках пяти стандартных типов сбоев, поскольку те изначально предполагают реальность самого сбоя.
277 сбоев при тестовом прогоне на четырех моделях в разрезе подтипов, с цветовой индикацией основного типа, в который они вложены. Самый большой отдельный столбец соответствует преждевременной остановке после пустого результата, почти целиком состоящей из ошибок модели Gemini 2.5 Flash. Если суммировать по цветам, наибольшую категорию составляют ошибки выбора данных, причем большинство из них связаны с разрешением сущностей, а не с выбором неверных таблиц. Серые столбцы не являются сбоями моделей: это семь ответов, признанных правильными вопреки вердикту о сбое, и один амбивалентный вопрос.
Один пример. На запрос месячного регулярного дохода (MRR) конкретной организации модель GPT-4.1 разбила название компании «White, Leonard and Kelly» на три отдельных имени, получила пустой результат и сообщила, что таких организаций не существует. Классификатор определяет это как неверный идентификатор с уровнем уверенности 0.94, относит к категории отсутствия заданных правил (верно: в промпте нет инструкций о разделении имен, а все механические проверки SQL-кода прошли успешно), оценивает вероятность того, «что помогла бы подсказка по схеме», в 0.78, а «правильно вопреки сбою» — в 0.20. Чисто семантический сбой, который не улавливается никакими регулярными выражениями, классифицирован за один вызов.
Для задачи ранжирования состоянием является вопрос плюс схема хранилища, а вопросы представляют собой оценку «да/нет» для каждой таблицы: «нужна ли эта таблица для ответа на вопрос?»
При оценке по таблицам, используемым в эталонном SQL-запросе: среднее nDCG@10 составляет 0.951, recall@10 — 0.991, а для 84% вопросов первые пять таблиц уже содержат все таблицы, необходимые для ответа. Около 0.4 секунды на вопрос.
Качество ранжирования таблиц на наборе для разработки из 136 вопросов, оцененное по таблицам, к которым обращается эталонный SQL. Полнота (recall) для k — это доля необходимых таблиц, которые появляются в числе первых k; все-требуемые для k — доля вопросов, у которых первые k уже содержат каждую таблицу, нужную для ответа. Обе величины растут быстро: для пяти таблиц полнота составляет 95%, и 84% вопросов имеют всё необходимое, а для десяти таблиц полнота составляет 99%. Разрыв между двумя линиями — это вопросы, которым нужны две или три таблицы, причём одна из них получает низкий ранг; именно поэтому ранжирование выдается в качестве подсказки и никогда не используется для усечения схемы.
Частично такая полнота обусловлена небольшим размером хранилища, состоящего из 18 таблиц. В сопутствующем пилотном исследовании на публичных каталогах text-to-SQL с числом таблиц от 75 до 710 (BEAVER, BIRD, Spider, MMQA, 250 кейсов) та же ранжировка «да/нет» достигла 87% recall@10 и нашла каждую требуемую таблицу в 73% случаев, при p90 в 3.4 секунды и стоимости около $0.50 за весь прогон. Это значительно ниже 0.991, но всё же опережает двух агентов, которые исследовали те же схемы с помощью SQL-запросов — они нашли меньше необходимых таблиц и потратили от 25 до 83 секунд на один кейс. Таким образом, метод применим к большим каталогам; почти идеальная полнота — нет, и на реальном хранилище следует ожидать первого.
Хорошее ранжирование полезно только в том случае, если агент действует на его основе, но агент не принадлежит нам для переобучения: это та модель, которая участвует в бенчмарке, со схемой в ее промпте и инструментом, выполняющим SQL. Поэтому ранжирование должно поступать к модели в виде текста, который она может прочитать, и то, как именно это происходит, определяется двумя ограничениями.
Во-первых, ранжирование не может просто заменить схему. Примерно для каждого шестого вопроса таблица, необходимая для ответа, находится за пределами первой пятерки, поэтому удаление таблиц с более низким рангом из промпта сделало бы такие вопросы неразрешимыми. Полная схема остается в промпте, а ранжирование лишь подсказывает, где искать в первую очередь.
Во-вторых, помещение ранжированных таблиц в системный промпт не помогает. В пилотном тестировании с двумя модельями добавление первой пятерки таблиц в начале каждого вопроса изменило количество успешных прохождений на +6 из 136 для GPT-4.1 и на +7 из 125 для Qwen3-Coder 30B, в то время как переоценка тех же базовых прогонов изменила 21 и 28 вердиктов: это находится в пределах шума оценки. Добавление их только в прогоны, в которых была пропущена нужная таблица, сработало лучше. GPT-4.1 восстановила 11 вопросов и не ухудшила ни одного, Qwen3-Coder 30B восстановила 13 и ухудшила 3. Но для такого отбора нужен эталонный ответ.
Учитывая, что в реальной жизни у нас нет доступа к эталонному ответу, мы используем прокси: множество с высокой уверенностью классификатора. Оно определяется как набор таблиц, получивших оценку 0.5 или выше. Запрос, который не считывает ни одну из них, обычно обращается не к той таблице. Это и есть шлюз внутри цикла.
В цикле. Шлюз проверяет каждый запрос, выполняемый моделью. Если ни одна из таблиц, прочитанных к данному моменту, не входит в множество с высокой уверенностью, он добавляет к результату этого запроса короткое примечание, содержащее:
- Таблицу, которую прочитал запрос
- Топ-3 ранжированные таблицы
- Инструкцию проверить, содержит ли таблица запрашиваемые данные, и в противном случае отправить запрос к таблице с наивысшим рангом
Как упоминалось ранее, полная схема остается в системном промпте. Шлюз проверяет каждый запрос, но добавляет свое примечание не более одного раза за прогон. Примечание добавляется к результату спровоцировавшего его запроса, а не отправляется как отдельное сообщение, поэтому оно не требует от модели дополнительных шагов, а прогон со шлюзом и его базовый вариант сравниваются при том же бюджете в 60 шагов.
Для каждого вопроса классификатор оценивает каждую из 18 таблиц с вероятностью да/нет, и таблицы с оценкой 0.5 и выше образуют множество с высокой уверенностью. Сначала первая пятерка таблиц добавляется в начало промпта. В цикле шлюз проверяет каждый запрос, выполняемый агентом; если ни одна из прочитанных к настоящему моменту таблиц не входит в множество с высокой уверенностью, он добавляет к результату этого запроса примечание с указанием таблиц с наивысшим рангом. Ни один из путей не удаляет ничего из схемы, которую видит агент.
Пример из практики. Возьмем вопрос: «график MRR по месяцам по субрегионам / AE / территориям в регионе EMEA North, последние 12 месяцев». Четыре таблицы имеют оценку выше 0.5, а четырнадцать — значительно ниже. Одна из двух таблиц, нужных для ответа, входит в множество с высокой уверенностью; другая, таблица ежемесячного MRR, имеет оценку 0.27 и не входит. Это приводит к следующему:
- Запрос, который считывает измерение учетных записей, находится внутри множества, поэтому ничего не происходит.
- Запрос, который считывает измерение сервисов (0.08), находится вне его, поэтому срабатывает примечание и указывает на измерение учетных записей.
Два прогона из набора для разработки по этому вопросу демонстрируют оба результата. o4-mini ни разу не прочитала измерение учетных записей в своем базовом прогоне. При включенном шлюзе ее первые запросы считывали таблицы вне множества с высокой уверенностью, примечание сработало один раз, назвало измерение учетных записей, и модель запросила его. Оба прогона прошли успешно за 12 и 14 шагов. Базовый вариант уже имел ответ, поэтому примечание могло изменить только путь, и оно это сделало: этот пример показывает срабатывание механизма по задумке, а не восстановление. Claude Haiku 4.5 потерпела неудачу за два шага, не прочитав ни одной из нужных таблиц, но прочитанные ею таблицы входили в множество с высокой уверенностью, поэтому шлюз так и не сработал, и прогон со шлюзом завершился неудачно тем же образом.
Вероятность для каждой таблицы для одного реального вопроса. Четыре таблицы преодолевают отметку 0.5 и образуют множество с высокой уверенностью. Звездочками отмечены таблицы, к которым обращается эталонный SQL: одна находится внутри множества, другая — нет. Запрос, который считывает данные внутри множества, выполняется без комментариев; запрос, считывающий данные за его пределами, получает примечание.
Мы запустили шлюз в парном режиме (включен и выключен для одного и того же вопроса и модели при бюджете в 60 шагов) на 20 моделях на наборе для разработки из 136 вопросов и наборе для тестирования из 201 вопроса, с оценкой обычным жюри бенчмарка. Дельты ниже приведены для ячеек, где шлюз действительно сработал (14% ячеек набора для разработки, 20% ячеек набора для тестирования). Интервалы представляют собой 95% бутстрэп-оценки.
Точность не изменилась. Набор для разработки склоняется в одну сторону, набор для тестирования — в другую, а в совокупности сальдо составляет +12 с интервалом, охватывающим ноль. Механизм выполняет то, для чего он создан: 34 из 121 случая восстановления следуют точной цепочке (базовый вариант пропустил золотую таблицу, примечание назвало ее, прогон со шлюзом запросил ее). Но 19 из 109 случаев ухудшения прошли ту же цепочку, и модель все равно дала неверный ответ, а остальное с обеих сторон является шумом от прогона к прогону. Знание правильной таблицы не является узким местом; ее правильное использование — вот где проблема, и это сбой плана, который подсказка исправить не может.
Ходы: экономия на проверочном наборе данных, которая исчезает на контрольных вопросах, по причине, которую стоит знать. На проверочном наборе управляемые прогоны использовали в совокупности на 176 запросов-ходов меньше. На лидерборде эта экономия исчезает. Механизм в обоих случаях идентичен: чрезмерно исследующие прогоны (10 или более базовых ходов) составляют 22% сработавших ячеек на обоих наборах, и фильтр сокращает их количество на 17% по ходам и на 17% по выходным токенам в обоих случаях. Отличаются остальные 78%. Прогоны, которые и так были короткими, платят «налог» при появлении примечания: +6% ходов на проверочном наборе, +17% на лидерборде. Экономия не исчезла на контрольных вопросах. Ее перекрыл больший налог на прогоны, которые изначально выполнялись нормально.
Выходные токены: показатель выразителен, но находится в пределах погрешности. Сокращение выходных токенов направлено в ту же сторону на обоих наборах, и ни один из интервалов не исключает ноль. Совокупный результат формируется за счет нескольких длинных прогонов, и этот «хвост» работает в обе стороны: одна ячейка Gemma 4 31B перешла от 29 ходов и 1628 выходных токенов в базовом варианте к лимиту в 60 ходов и 48 910 токенам с примечанием. При амортизации по всем прогонам, учитывая, что фильтр срабатывает примерно на одном прогоне из пяти-семи, экономия вывода составляет около 1%, а эффект по ходам равен нулю.
Для каждой модели: изменение количества ходов и выходных токенов в ячейках, где сработал фильтр, на проверочном наборе и на контрольном лидерборде. Столбец представляет собой совокупный показатель по этим ячейкам, который и входит в общие итоги в таблице выше; отметка задает медианную ячейку. На проверочном наборе медианная ячейка остается на прежнем уровне для большинства моделей, в то время как общий показатель тянут вниз несколько чрезмерно исследующих прогонов. На лидерборде тот же «хвост» превращается в издержки, причем одна ячейка Gemma 4 31B доходит до лимита ходов. Каждая модель опирается на количество сработавших ячеек от 8 до 61, поэтому воспринимайте столбцы как разброс, а не как окончательные результаты.
Классификатор — это инструмент, который мы теперь используем при каждой оценке на проверочном наборе: один автономный проход дает общую классификацию сбоев, более детальные подрежимы и аудит оценщика менее чем за секунду на один сбой. Его более детальная таксономия также меняет главный тезис первой публикации: часть того, что мы называли неверным планом, на самом деле является неверной таблицей или неверной сущностью, что относится к поиску информации, а не к рассуждениям.
Ранжирование при поиске — это то, что работает: подсекундный, практически бесплатный компонент, который выводит правильные таблицы наверх списка для заданного вопроса без предварительного тонкого обучения и эталонных данных во время выполнения. Любой, кто создает аналитического агента со сложной схемой данных, должен его иметь.
Подсказка в цикле — это отрицательный результат, и демонстрация его важности является нашей целью. Она не меняет точность, сокращает количество ходов только там, где модель чрезмерно увлекалась исследованием, а на контрольных вопросах это сокращение нивелируется налогом, который она накладывает на нормальные прогоны. Лидерборд показал нам ту же картину с обратной стороны. В сопутствующей публикации Sonnet 5.5 улучшила результаты по вопросам с размерностями, но находила таблицы не чаще, чем Sonnet 5. Передовые модели уже умеют находить таблицы. Ошибаются они в том, что с ними делать.
Ничто здесь не привязано конкретно к нашему хранилищу или ClickHouse. Инструментальная обвязка та же, что описывалась в первой публикации: инструментированный помощник, смоделированная схема хранилища, курированный и анонимизированный набор вопросов с эталонными ответами комитета, а также панель судьей, в которой ни одна модель не оценивает собственные ответы. Добавление модели требует лишь записи в реестре и одной команды, а фиксация версии движка позволяет воспроизвести результаты лидерборда при повторном запуске.
В вашем хранилище будет собственный дискриминатор. Наш — это слой мер в CRM; вашим может быть медленноменяющееся измерение, полуструктурированная таблица событий или негласное соглашение об именовании. Единственный способ найти его — запустить бенчмарк на вашей схеме, с вашими вопросами и посмотреть, где модели дают сбой.
Вопрос об архитектуре с разделением ролей из первой публикации (мощнейшая модель планирует, самая дешевая выполняет) по-прежнему остается нашей главной целью для дальнейшего изучения, и нулевой результат по подсказкам делает его еще более актуальным: если модели ошибаются на этапе планирования, то именно для планирования и нужна дорогая модель. Что касается поиска, ранжирование было протестировано на хранилище из 18 таблиц и в публичном пилотном проекте из 250 кейсов; открытым остается вопрос о том, при каком размере каталога подсказка начинает окупаться.
- Обвязка: github.com/ClickHouse/data-agent-mnist, Apache 2.0. Классификатор и ранжировщик поставляются как опциональное расширение Jev. В разделе README «Optional: ranking and labeling with Jev» описана настройка, для которой требуется ключ TypeSafe. schema_retrieval.py оценивает ранжирование, schema_retrieval_agent.py передает его агенту заранее или в цикле и измеряет дельту, label_failure_modes.py размечает оцененный прогон. Набор правил, проверяемый классификатором, представляет собой конфигурационный файл, который вы копируете для своего хранилища.
- Классификатор и ранжировщик поставляются как опциональное расширение Jev
- В разделе README «Optional: ranking and labeling with Jev» описана настройка, для которой требуется ключ TypeSafe.
- schema_retrieval.py оценивает ранжирование
- schema_retrieval_agent.py передает его агенту заранее или в цикле и измеряет дельту
- label_failure_modes.py размечает оцененный прогон. Набор правил, проверяемый классификатором, представляет собой конфигурационный файл, который вы копируете для своего хранилища.










