Наш агент преобразования текста в запросы сократил время выполнения с 45 секунд на frontier-моделях до 2 секунд на GLM 5.3 Flash, сохранив ту же точность при стоимости в 20 раз ниже.
Бóльшая часть нашей недавней работы с ИИ в Conversion была сосредоточена на общей маркетинговой аналитике.
Команды маркетинговой автоматизации выполняют широкий спектр задач в различных системах: исследование аккаунтов, построение аудиторий, планирование кампаний, написание контента и анализ данных о производительности. Мы создавали агентов, способных рассуждать в рамках этих рабочих процессов и использовать те же инструменты, что и опытный маркетолог.
Такие системы выигрывают от использования мощных универсальных моделей. Работа является открытой, и зачастую хорошее суждение важнее быстрого выполнения задачи.
Но у нас также был накоплен список более мелких, узконаправленных функций на базе ИИ. Одной из них были фильтры на естественном языке: позволить пользователю описать аудиторию простым английским языком и преобразовать это описание в фильтр, который можно просмотреть и отредактировать в существующем конструкторе выражений Conversion. (В Conversion фильтр называется выражением.)
Поначалу это казалось простой задачей структурированной генерации. Дать модели доступные поля, описать формат вывода и попросить её сгенерировать JSON. На деле всё оказалось значительно сложнее.

Составное выражение, сгенерированное менее чем за 5 секунд с использованием GLM 5.3 Flash.
Возьмём следующий пример:
Найти контакты, которые отправили демо-форму хотя бы один раз за последние 30 дней и работают в софтверной компании с открытой сделкой на сумму более 50 000 долларов.
Это требует от системы:
- Найти конкретную форму, которую пользователь подразумевает под «демо-формой»
- Определить, какое поле представляет отрасль компании
- Узнать, как в данном рабочем пространстве представлено «программное обеспечение», что означает просмотр фактических значений, хранящихся в этом поле, а не догадки
- Перейти от контакта к его компании, а затем к сделкам этой компании
- Убедиться, что условия «открытая» и «более 50 000 долларов» применяются к одной и той же сделке
- Применить относительное временное окно события
Кроме того, всё это нужно было делать достаточно быстро, чтобы напоминать интерфейс фильтра, а не исследовательского агента.
То, что выглядело как небольшая задача по инжинирингу промптов, превратилось в ограниченную задачу преобразования текста в запрос. Для её решения потребовались агент, использующий инструменты, промежуточное представление (IR), детерминированный компилятор и семантический бенчмарк.
Мы прогнали восемь моделей через получившийся бенчмарк, Statement Bench, включая Claude Opus 5, Kimi K3, GLM 5.3 Flash и выпущенный сегодня утром Gemini 3.8 Flash. Результаты приведены ниже.
Предоставление агенту инструментов
Большая часть информации, необходимой для ответа на запрос выше, специфична для окружения клиента. Одно рабочее пространство может содержать сотни миллионов исторических значений полей, а также свои активы и объекты. По очевидным причинам мы не могли поместить всё это в один промпт.
Нашим первым полезным архитектурным решением было перестать рассматривать проблему как обычную структурированную генерацию. Вместо этого модель получает небольшой набор инструментов. Она может искать поля, просматривать исторические значения и разрешать бизнес-специфичные активы, такие как формы, кампании, письма и аудитории. Она использует эти инструменты только тогда, когда этого требует запрос.
Бóльшая часть этой поисковой инфраструктуры была взята из нашей недавней работы над Global Search, которая обеспечивает текстовый и семантический поиск по всем записям в Conversion. Мы планируем рассказать об этом подробнее в ближайшее время!
Базовый поток выглядит так:
1Запрос на естественном языке2 |3 v4 Агент, использующий инструменты <-----------------+5 / | \ |6поля активы связи | отклонение с объяснением7 \ | / |8 v |9 Ограниченное IR |10 | |11 v |12 Валидатор и компилятор -------------------------------+13 |14 v15 Продуктовое выражение
Это сохраняет начальный контекст небольшим. Это также делает ошибки гораздо более понятными. Если выражение неверно, мы можем определить, нашёл ли агент не тот актив, выбрал ли не то поле, неправильно ли понял связь, некорректно ли представил правильную идею или же ошибка в компиляторе. Это различие позже стало важным для нашего цикла оценки.
Создание более компактного языка
Использование инструментов решило проблему контекста. Оно не решило проблему задержки.
Один из уроков ранней обратной связи: пользователи терпят гораздо меньшую задержку в специализированном интерфейсе, чем в чате.
Это указывает на более широкий парадокс. Мы устанавливаем ожидания по задержке, исходя из того, насколько сложной задача кажется нам, а не насколько она сложна для системы. Написание контента кажется сложным, потому что мы видим работу. Описание фильтра кажется простым, потому что наш разум молча разрешает контекст, сущности, связи и намерения. Для модели реконструкция этих скрытых предположений и есть задача. Чем меньше работы видит пользователь, тем меньше времени он даёт системе на её выполнение.
Основываясь на ранних отзывах, мы поставили две цели: точность более 95 процентов и время отклика около 5 секунд для типовых запросов.
У Conversion есть выразительный внутренний язык запросов. В наших ранних тестах, при прямом использовании продуктового формата, только самые большие модели, такие как Claude Opus, могли генерировать его надёжно. Даже простые выражения занимали около 45 секунд.
Визуальный конструктор выражений предоставляет доступ только к подмножеству полного языка. Мы создали более компактное, удобное для агентов промежуточное представление для этого подмножества. Меньшие модели могли генерировать его, используя меньше токенов, в то время как детерминированный компилятор обрабатывал полный продуктовый формат.
Рассмотрим выражение:
Название должности содержит «Директор».
Исходное продуктовое выражение выглядит так:
1{2 "type": "LOGICAL",3 "version": 1,4 "logical": {5 "operator": "OR",6 "operands": [7 {8 "type": "LOGICAL",9 "version": 1,10 "logical": {11 "operator": "AND",12 "operands": [13 {14 "type": "VARIABLE",15 "version": 1,16 "variable": {17 "variableSchemaId": "550e8400-e29b-41d4-a716-446655440000",18 "where": {19 "type": "LOGICAL",20 "version": 1,21 "logical": {22 "operator": "AND",23 "operands": [24 {25 "type": "LOGICAL",26 "version": 1,27 "logical": {28 "operator": "CONTAINS",29 "operands": [30 {31 "type": "ATTRIBUTE",32 "version": 1,33 "attribute": {34 "name": "value"35 }36 },37 {38 "type": "CONSTANT",39 "version": 1,40 "constant": {41 "value": "Director"42 }43 }44 ]45 }46 }47 ]48 }49 }50 }51 }52 ]53 }54 }55 ]56 }57}
Представление того же фильтра для модели выглядит так:
1{2 "field": "550e8400-e29b-41d4-a716-446655440000",3 "op": "contains",4 "value": "Director"5}
IR прошло уже несколько поколений, и последнее было сформировано наблюдением за тем, как маленькие модели терпят неудачу на предыдущих. Одним из крупных улучшений стало введение лучшей семантики «одной записи» (то, что валидация схемы не может отловить):
1{2 "related": "OPPORTUNITY",3 "all": [4 { "field": "<uuid этапа>", "op": "equals", "value": "Closed Won" },5 { "field": "<uuid суммы>", "op": "gt", "value": 100000 }6 ]7}
Это разделение между моделью и кодом дало нам несколько полезных свойств:
- Неподдерживаемые выражения сложно выразить
- Семантика связей «одной записи» видна
- Ссылки на поля и связи могут быть проверены
- Компилятор может быть протестирован независимо от модели
- Сгенерированные выражения остаются редактируемыми в существующем UI
В конечном итоге IR упрощает задачу модели: агент разрешает намерение пользователя и создаёт ограниченный план; код обрабатывает продуктовый формат.
Создание семантического бенчмарка
Вывод может быть полностью валидным, но при этом неверным. Возьмём этот запрос:
Контакты в компаниях с выигранной сделкой на сумму более 100 000 долларов.
Контакт принадлежит компании, а у компании может быть много сделок. Соответствие этому фильтру означает прохождение по связям (контакт → компания, компания → сделки) и проверку двух условий по пути: сделка выиграна и сделка стоит более 100 000 долларов.
Сложность в том, что эти условия должны выполняться для одной и той же сделки. Если они проверяются независимо, компания с выигранной сделкой на 20 000 долларов и открытой сделкой на 150 000 долларов удовлетворяет обоим: каждое условие соответствует своей сделке. Валидация схемы никогда этого не поймает.
Как только несколько подобных примеров были пройдены, редактирование промпта рисковало вызвать регрессию в них. Нам нужен был способ проверять смысл, а не только валидность, и проверять его каждый раз, когда что-то менялось.
Мы построили Statement Bench на основе поведения продукта, полученного из анонимизированных паттернов аудитории, которые наши клиенты создавали ранее. Набор теперь содержит 100 кейсов по пятнадцати категориям, таким как простые условия по полям, события, относительные и календарные временные окна, связи и составные запросы.
Каждый кейс выполняется в реалистичном песочном рабочем пространстве. Агент получает те же данные и инструменты, что и в продакшене.
Оценщик проверяет несколько уровней:
- Вернул ли агент выражение?
- Соответствует ли IR своей схеме?
- Существуют ли указанные поля и связи?
- Может ли выражение скомпилироваться и пройти продуктовую валидацию?
- Представляет ли оно запрошенный смысл?
- Сколько шагов модели, вызовов инструментов, токенов и отклонённых попыток потребовалось?
Пятый пункт — самый интересный, так как валидность не гарантирует семантического равенства.
Семантические проверки читают скомпилированное выражение, проверяя такие вещи, как «одно условие по сделке, содержащее как этап, так и сумму», «событие email, тип которого — клик, а не открытие» или «условие по вебинару, а не по пользовательской кампании».
Запуск цикла оптимизации на основе eval
Бенчмарк изменил то, как мы могли продолжать работу над функцией. Вместо того чтобы просить кодирующего агента «улучшить промпт» или «реализовать новое IR», мы могли дать ему исполняемое определение улучшения.
Цикл выглядел так:
- Запустить бенчмарк
- Сгруппировать ошибки по их основной причине
- Изучить траекторию использования инструментов агентом и отправленное IR
- Изменить промпт, инструменты, валидаторы или компилятор
- Запустить полный бенчмарк снова
- Сохранить изменение только в том случае, если оно улучшает систему без внесения регрессий
Кодирующие агенты могли использовать бенчмарк для сравнения моделей, экспериментов с IR, улучшения описаний инструментов и автономного уточнения промпта. Запуск полного набора после каждого изменения также не давал нам переобучаться на отдельных ошибках, и мы оставили ещё 50 кейсов для подтверждения этого.
Несколько изменений дали наибольший прирост результатов:
- Перенести пути, типы и структуру в компилятор. Наше первое IR заставляло модель явно прописывать все связи: контакт → компания, компания → сделка. Метаданные поля уже подразумевают этот путь, поэтому компилятор теперь выводит его сам. Мы сделали то же самое для дат, приведения типов, размещения отрицаний и группировки вложений. Перенос правил в компилятор упростил IR и сократил количество ошибок схемы.
- Отклонять с объяснениями и исправлениями. Каждое отклонение от схемы или компилятора говорит, что написать вместо этого (когда это возможно): «gt нельзя отрицать для этого поля; используйте lte», «скопируйте id из campaign_list». Маленькие модели сходятся за одну-две попытки, а продуктовая модель отклоняется на нескольких запросах из сотни.
- Структурировать промпт для маленьких моделей. Реорганизация промпта не изменила точность, но вдвое сократила количество повторных попыток, что напрямую улучшило задержку. Это было вдохновлено Лучшими практиками промптинга от Anthropic.
- Использовать примеры вместо описаний. Два дополнительных примера в нашем справочнике по формату устранили класс ошибок, которые не исправляли абзацы объяснений, сократив количество отклонённых попыток примерно вдвое.
- Давать полный контекст или никакого. Модели хватаются за то, что есть в контексте, прежде чем вызвать инструмент. Когда контекст содержал частичный или немаркированный набор полей, модель использовала ближайшее, а не искала, что приводило к семантически неверным выражениям. Уменьшив частичный контекст в пользу вызовов инструментов, мы увеличили скорость сборки и сократили входные токены на пятую часть.
Финальная продуктовая конфигурация, GLM 5.3 Flash, выполнила все 100 кейсов бенчмарка с медианной задержкой 2,3 секунды и 95-м процентилем 7,1 секунды. И 97 из 100 были семантически верны. По сравнению с исходным подходом с использованием продуктового формата, простые фильтры перешли с примерно 45 секунд до чуть более одной секунды при стоимости в 20 раз ниже.
Сравнение моделей на Statement Bench
Бенчмарк также дал нам способ сравнивать модели на реальной задаче.
2 сентября 2026 года мы прогнали те же 100 кейсов через восемь моделей. Каждая модель получила одинаковый промпт, инструменты, IR, компилятор и 30-секундный тайм-аут запроса.
Маршрутизация провайдера, кэширование промптов и временная нагрузка на инференс влияют на задержку.
Модель
Валидные сборки
Семантически верно
P50 задержка
P95 задержка
Чтение кэша
Вызовы инструментов
Отклонённые попытки
Ориент. стоимость на 1000 запросов
Claude Opus 5
100/100 (100%)
100/100 (100%)
3.16с
8.53с
91.1%
162
0
$27.51
GLM 5.2
100/100 (100%)
100/100 (100%)
4.38с
13.02с
93.5%
201
5
$14.94
Kimi K3
100/100 (100%)
100/100 (100%)
5.17с
11.84с
34.3%
157
0
$48.51
GLM 5.3 Flash
100/100 (100%)
97/100 (97%)
2.34с
7.07с
92.8%
163
2
$1.33
DeepSeek V4 Pro
96/100 (96%)
96/96 (100%)
5.53с
24.31с
47.8%
172
1
$12.00
Gemini 3.7 Flash
77/100 (77%)
77/77 (100%)
15.14с
30.01с
26.5%
228
1
$18.24
Gemini 3.8 Flash
76/100 (76%)
76/76 (100%)
14.29с
30.01с
35.4%
266
1
$27.44
DeepSeek V4 Flash
56/100 (56%)
55/56 (98%)
6.79с
30.00с
41.5%
100
1
$0.56
Оценочная стоимость рассчитана на 1000 попыток запросов с использованием наблюдаемых входных, кэшированных входных и выходных токенов по опубликованным неакционным тарифам каждого провайдера на 2 сентября 2026 года. Кэшированный ввод тарифицируется по опубликованной ставке чтения кэша, если провайдер её публикует, и по полной ставке ввода в противном случае.

Рисунок 1. Корректность в сравнении со стоимостью. GLM 5.3 Flash достигает 97 процентов примерно при одной двадцатой стоимости Claude Opus 5.

Рисунок 2. Распределение задержки, медиана и 95-й процентиль, упорядочено по P95.
Несколько выводов выделились.
Ни размер модели, ни цена не предсказывали задержку. Самая быстрая модель была самой маленькой и дешёвой. Вторая по скорости была самой большой и дорогой.
Ошибки сместились с неверных ответов на медленные ответы. Шесть из восьми моделей были семантически верны в каждом выражении, которое они завершили; различия между ними почти полностью заключаются в том, сколько запросов завершилось в пределах тайм-аута. На ранних итерациях IR и промптов большинство маленьких моделей проваливали бенчмарк на этапе сборки с семантической точностью <50%.
Токены рассуждения перевешивают вызовы инструментов. Gemini 3.8 Flash потратил 180 000 из своих 192 000 выходных токенов на рассуждения и сделал 266 вызовов инструментов; Claude Opus 5 потратил 813 токенов на рассуждения, сделал 162 и завершил каждый кейс. Наши усилия по Global Search сократили каждый поиск инструмента до миллисекундного диапазона, поэтому оставшаяся стоимость — это шаги модели между ними.
Вывод
Модели хороши в разрешении неоднозначности, код хорош в обеспечении точности, и большинство наших ранних ошибок было связано с тем, что мы просили модель делать и то, и другое. Создание этого агента было работой по определению того, кто из них двоих должен владеть каждой частью. Мы ожидаем, что то же самое верно для text-to-SQL и большинства других интерфейсов на естественном языке.
Если вас интересуют какие-либо из этих проблем, свяжитесь с нами! Мы нанимаем.





