- BrainTools - https://www.braintools.ru -

На днях мы выкатили третье поколение языковых моделей Cotype — флагманскую Cotype Pro 3 и ее облегченную версию Cotype Light 3 (к слову, лайт мы выкатили чутка раньше, но это не имеет значения). Почитать о характеристиках моделей можно вот в этом посте. Тут же мы расскажем вам о нюансах обучения [1] — точнее о том, как мы учим наши модели выполнять многошаговые сценарии без запинок, что совершенно необходимо, так как они выступают ключевым компонентом наших корпоративных ИИ-агентов и мультиагентных систем.
Представим, что клиент просит изменить заказ и называет свой e-mail. ИИ-агент, подключенный к каналу обслуживания, ищет по нему клиента и получает «не найдено» — адрес устарел. Дальше есть три варианта развития событий:
можно повторить тот же запрос и снова получить ту же ошибку [2];
можно сообщить, что клиент не найден, и закрыть диалог;
можно изменить признак поиска — найти клиента по имени и индексу — и продолжить.
Получить довольного клиента можно, только если пойти по третьему пути.
Большие языковые модели обычно без проблем понимают, что от них хотят, и успешно пробегают все шаги по этой дорожке: свериться со статусом перед записью, не потерять идентификатор между вызовами, не переспрашивать уже названное, не повторять [3] упавший вызов вместо обходного пути. А вот компактные модельки с такими механическими вещами не дружат, хотя и не хуже больших понимают, что нужно сделать.
Эта проблема — не проблема для компаний, которые могут позволить себе LLM на сотни миллиардов параметров. Нашим же клиентам нужны модели, которые можно установить в контур и которые не разорят бизнес на инференсе. Поэтому даже наша флагманская модель Cotype содержит всего лишь 27 млрд параметров, и это меньше, чем у предыдущего поколения. А облегченная версия — и вовсе 9 млрд.
Мы учим наши компактные Cotype надежно вести многошаговые задачи с инструментами: сверяться с состоянием перед записью, работать с контекстом, искать обходной путь после ошибки. Данные для этого собираем так, что выдумать их нельзя — каждый вызов в трейсе исполняется в настоящей базе через отдельный исполнитель инструментов, поэтому ошибки и восстановление в диалогах не постановочные.
Наш пайплайн собран на τ²-bench (tau2) — открытом фреймворке для оценки tool-calling-агентов в режиме dual-control, где роли агента и клиента играют отдельные модели. Под свою задачу мы его существенно доработали: добавили генерацию доменов, сбор трейсов с исполнением вызовов в базе и тренировочный контур. RL-обучение (GRPO) идет поверх verl-agent gym.
На отложенном тесте (телеком, финансы, HR) дообучение модели на 9 млрд параметров через GRPO поднимает основную метрику с 0,794 до 0,856; причем основной прирост — на автономных задачах: ST 0,727 → 0,821.
Ну а тем, у кого есть время читать, расскажу подробнее, как мы, собственно, собирали трейсы для обучения на примере агентских сценариев для автоматизации задач клиентской поддержки.
Прежде чем что-то генерировать, мы разобрали сценарии работы ИИ-агента в клиентской поддержке и составили карту того, где он теряет корректность. Ошибки уложились в пять повторяющихся классов — под них и собирали корпус.
|
Класс ситуации |
Что требуется от агента |
Доля в корпусе |
|
Перенос данных через контекст |
Удерживать и переносить значения — идентификаторы, суммы, статусы — из ответов инструментов и реплик клиента в аргументы следующих вызовов |
40% |
|
Ветвление по состоянию |
Перед действием свериться с состоянием сущности и выбрать ветку: операция допустима или нет |
25% |
|
Действие без лишних уточнений |
Если нужное уже сказано — действовать, а не переспрашивать по второму кругу |
20% |
|
Восстановление после ошибки инструмента |
После неудачного вызова сменить стратегию, а не повторять то же действие |
10% |
|
Корректное завершение диалога |
Закрыть многошаговую задачу: подвести итог сделанного и завершить |
5% |
Таблица 1. Типичные ошибки ИИ-агентов на примере сценариев клиентской поддержки
За каждой строкой — конкретное место, где компактная модель теряет корректность и где удачный обучающий пример приносит больше всего пользы. Доли в правой колонке — целевое покрытие корпуса.
Обучающие трейсы можно получать двумя путями:
Первый — писать диалоги на словах: «сочинить» с помощью LLM правдоподобный разговор и тут же придумать к нему ответы инструментов. Быстро и дешево, но ответы инструментов, ошибки, идентификаторы — все выдумано. Модель учится на том, что лишь похоже на реальную работу с инструментами.
Второй — каждый вызов инструмента в трейсе исполнять в настоящей базе данных с реальными записями о пользователях, товарах и пр., а не выдуманных LLM на лету. Тогда то, что происходит в диалоге, происходит на самом деле:
Ошибка инструмента не постановочная — база отвергает неверный аргумент, и агент сам ищет обходной путь.
Развилка — это статус сущности, который агент прочитал из базы и на который отреагировал.
Разные варианты прохождения задачи возникают сами: на разные данные база отвечает по-разному.
Идентификаторы и суммы переходят из шага в шаг, потому что их вернул вызов. Другого источника у них нет.
Это то, что называется grounding — когда диалог не просто выглядит правдоподобно, а подтвержден реальным действием.
Мы пошли по второму пути. Но давайте опишу пайплайн целиком.
Вся работа по созданию трейсов у нас разделена на два этапа:
Подготовка среды. LLM собирает статический мир: скиллами проектирует домены, наполняет базу, пишет инструменты и политику, формулирует задачи, первые сообщения и сценарии.
Собственно генерация трейсов. Диалог разыгрывают две LLM — одна за агента, другая за клиента. Каждый предложенный агентом вызов уходит в исполнитель инструментов, тот загружает состояние диалога, исполняет вызов со всеми проверками и изменениями в базе и возвращает фактический результат. Сценарий при этом лишь расставляет ловушку — подставляет устаревший e-mail, переводит заказ в статус «доставлен», а проходит ее LLM-агент сам. Его попытки, ошибки и развилки попадают в трейс как есть.
Смотрите Схему 1 с комментариями для наглядности.
Собственно, всего в рамках этих двух этапов выполняется 6 шагов:
Подготовка среды
① ARCHITECT (/architect-train-domains) проектирует набор доменов — спецификации, не код — по нескольким предметным темам. Каждый домен сконструирован так, чтобы провоцировать целевые классы ошибок: инструменты, возвращающие идентификаторы; операции, завязанные на состояние; вызовы, которые умеют падать.
② BUILD (/build-mini-domain) компилирует каждую спецификацию в исполняемый домен: модель данных, инструменты, база, политика. Отрапортовать об успехе субагент может только после самотестирования — домен регистрируется, инструменты загружаются, проверяется реакция [5] на недопустимое состояние.
③ ROUTE (/route-corpus) раскладывает целевые диалоги по слотам вида «домен × класс ситуации» и выдает каждому слоту непересекающийся пул сущностей, чтобы параллельные генераторы не конфликтовали за одни и те же данные.
Генерация трейсов
④ SCENARIO (/gen-scenario) пишет по сценарию на слот: задача, первое сообщение клиента, что он знает и сообщит, что «не помнит» (последнее заставляет агента искать данные, а не получать их готовыми), и эталонная цепочка действий. Цепочку прогоняют в базе от начала до конца — провалов нет.
⑤ РОЗЫГРЫШ — диалог разыгрывают две LLM, одна агентом, другая клиентом. Каждый предложенный агентом вызов уходит в базу, фактический результат возвращается в реплику.
⑥ ПРОВЕРКА (verify_grounding.py [6]) переисполняет каждый вызов из готового диалога и сверяет с записанным результатом. Разошлось — диалог в брак. Часть диалогов так отсеивается.
На выходе — сырые проверенные трейсы. Приведение к конкретному формату обучения — отдельный технический шаг SFT/DPO, в сам конвейер он не входит.
Стадии сборки и написания сценариев (2 и 4) идут волнами субагентов (это когда кодовый агент вызывает других агентов самостоятельно). Каждый работает в своем пуле сущностей, так что волны не конфликтуют за данные.
Покажу, как все это выглядит, на примерах.
# Клиент: зачислить карту GC-000001 на заказ ORD-10001, списать весь баланс.
lookup_gift_card(“GC-000001”)
→ {“balance”: 25.0, “status”: “active”}
# сумму 25.0 агент берет из ответа базы и подставляет в следующий вызов
apply_gift_card_to_order(“GC-000001”, “ORD-10001”, amount=25.0)
→ {“redemption_id”: “RED-001”, “remaining_balance”: 0.0}
# идентификатор RED-001 уходит клиенту дословно
# Агент: готово, списано 25 EUR, операция RED-001, остаток 0.
Суммы и идентификаторы агент не выдумывает, а переносит из ответа базы в следующий вызов и в финальную реплику. Каждый диалог стартует со своей копии базы: это независимые сессии, а не один общий мир.
Ниже еще три трейса по разных классам ошибок: ветвление по состоянию, более сложный перенос и восстановление после ошибки.
Студент просит: «Перенесите мой экзамен EX-0024 на попозже». Сценарий заранее перевел экзамен в статус «уже сдан»:
lookup_exam(exam_id=”EX-0024″) → {“status”: “taken”, “reschedule_count”: 1, …}
# status == “taken” → ветка отказа, в базе ничего не меняем
“Экзамен EX-0024 уже сдан, перенести его нельзя.”
Агент сначала читает статус и только потом решает: переносить нечего.
Проверенный трейс из четырех вызовов, где каждый аргумент берется из предыдущего ответа:
lookup_transaction(“TXN-000001″) → amount=100.0, atm_id=”ATM-001″, status=”posted”
lookup_atm_log(“ATM-001”, “10:59:00”) → dispensed=false # по политике диспут допустим
file_dispute(“TXN-000001″, reason) → dispute_id=”DSP-0001”
issue_provisional_credit(“DSP-0001″, amount=100.0) → credit_id=”CR-0001”
Здесь два переноса сразу: dispute_id из третьего вызова в четвертый и сумма 100.0 из первого. Ветвление по состоянию зашито в политику.
Тот самый случай из начала статьи, теперь в виде трейса. Клиент назвал неактуальный e-mail, первый поиск падает, агент меняет признак поиска:
find_user_by_email(“john.smith@unknown.com [7]“) → ошибка: пользователь не найден
# вместо повтора того же вызова — другой признак поиска:
find_user_by_name_zip(“John”, “Smith”, “10024”) → user_id=”U-7829″ # продолжаем
Мы дообучили на сгенерированных по вышеописанному пайплайну трейсах модель Qwen3.5-9B, и прогнали ее вместе с конкурентами большего размера на отложенном тесте из трех областей: телеком, финансы и HR (кадровые задачи).
Тест и обучение не пересекаются. Каждая задача идет в двух режимах: MT — диалог с симулятором клиента; и ST — вся информация выдается одним сообщением, дальше агент работает автономно. Итого 90 задач, по 5 прогонов на каждую на русском языке.
Роль клиента играет Qwen3.5-9B, следуя структурированному сценарию с персоной и границей знаний.
Роль ИИ-агента — Cotype Light 3 (та же Qwen3.5-9B, но уже дообученная).
Роль судьи по текстовым утверждениям — GPT-OSS-120B с голосованием по трем прогонам.
Тут возможен вопрос про циркулярность: клиент-симулятор и обучаемая модель — обе из семейства Qwen3.5-9B (клиент — базовая модель, агент под оценкой — обученная; модель не судит сама себя). На скоринг это все равно не влияет. Оси DB и ACTION детерминированы и считаются против эталона, а не через клиента; текстовые требования судит gpt-oss-120B — другое семейство, работа судьи провалидирана и не имеет определенного байеса. Клиент следует фиксированному сценарию с заданной персоной и границей знаний, то есть подыгрывать агенту не волен.
Метрика — мультипликативная тройка, где провал по любой оси обнуляет результат:
primary=DB×ACTION×NL_frac
primary=DB×ACTION×NL_frac
DB — совпадение итогового состояния базы с эталоном, ACTION — совпадение цепочки вызовов без приоритета очереди, NL_frac (NL — natural language) — доля выполненных требований на естественном языке, сюда относим комментирование результатов и общение с пользователем.
|
Модель |
Параметры |
ALL |
MT |
ST |
|
Qwen3.5-122B-A10B |
122B MoE |
0,833 |
0,844 |
0,822 |
|
Qwen3.5-27B |
27B |
0,817 |
0,855 |
0,779 |
|
GPT-OSS-120B |
120B MoE |
0,802 |
0,817 |
0,787 |
|
Qwen3.5-9B (база) |
9B |
0,794 |
0,861 |
0,727 |
|
Qwen3-VL-32B |
32B |
0,729 |
0,724 |
0,733 |
|
Qwen3.5-4B |
4B |
0,669 |
0,817 |
0,520 |
|
Qwen3-VL-8B |
8B |
0,592 |
0,564 |
0,620 |
Из таблицы видно две вещи. Первое: на этих задачах размер сам по себе помогает слабо. Базовая 9B (0,794) идет вплотную к 27B (0,817) и 122B MoE (0,833) — разрыв в десятки раз по параметрам дает единицы процентов по метрике. Второе — и для нас главное: режим ST стабильно тяжелее MT. В диалоге агент может переспросить и неявно исправиться, в автономном режиме такой страховки нет. Резче всего это на 4B — 0,817 в диалоге против 0,520 автономно. Автономные задачи и есть то место, где обучение должно дать максимум.
Базовая модель для обучения — Qwen3.5-9B, все методы на LoRA rank 16.
SFT — 600 образцовых трасс от учителей (учителя — Qwen3.5-9B и 27B).
DPO — тот же корпус, что и в SFT, в паре с трассы с нулевой наградой.
GRPO — онлайн-RL в той же среде с исполнением вызовов в базе, с полностью детерминированной наградой, без LLM-судьи: формат, совпадение действий, аргументы, состояние базы. Роллауты генерируются на лету на автономных задачах — это в 5–10 раз быстрее.
|
Метод |
ALL |
MT |
ST |
|
База (9B) |
0,794 |
0,861 |
0,727 |
|
SFT |
0,819 |
0,867 |
0,770 |
|
DPO |
0,827 |
0,907 |
0,748 |
|
GRPO (Cotype Light 3) |
0,856 |
0,890 |
0,821 |
|
Qwen3.5-27B (ориентир) |
0,817 |
0,855 |
0,779 |
|
Qwen3.5-122B MoE (ориентир) |
0,833 |
0,844 |
0,822 |
Главный результат — на автономных задачах: GRPO поднимает ST с 0,727 до 0,821. Именно там у базовой 9B был провал, и такие данные его закрывают. По агрегату GRPO дает 0,856 против 0,794 у базы.
Навык при этом переносится с ST на MT: GRPO учился только на автономных задачах, но подтянул и диалоговый режим (0,861 → 0,890). После обучения 9B оказывается сопоставима по метрике с 27B и 122B MoE.
Где именно прибавляет, видно по динамике обучения. Какие инструменты звать, модель знает с самого начала — ось действий держится около 0,91 и почти не растет. А вот доводить изменение базы до конца она учится: ось состояния растет с 0,53 до 0,97.
pass^k — вероятность, что успешны все k случайно выбранных прогонов задачи. Чем больше k, тем строже планка: одной удачной попытки уже недостаточно. Прогон здесь засчитывается по строгому бинарному критерию: NL оценивается бинарно — все утверждения должны быть выполнены, а не дробной долей NL_frac, как в основной метрике. Поэтому абсолютные значения тут ниже, чем primary из § 5.3, — это разные шкалы, и сравнивать pass^k нужно между моделями, а не с primary.

GRPO держит первое место во всем диапазоне, и с ростом k отрыв растет: к k = 5 он заметно опережает и базу, и SFT с DPO. RL дает не разовый успех, а воспроизводимый — а в проде задача должна решаться каждый раз, а не в среднем по прогонам.
Для ручной проверки мы взяли 30 случайных диалогов из тестового набора и отдали их команде из 11 аннотаторов, с перекрестной проверкой.
|
Метрика |
Значение |
|
Качество сценария (среднее) |
3,83/5 |
|
Доля «отлично» (=5) |
47,8% |
|
Доля «непригодно» (=1) |
17,4% |
|
Согласие судьи и эксперта |
95,6% |
|
Выход симулятора-юзера из роли |
4,3% |
|
Ложные срабатывания судьи |
1 |
|
Пропуски судьи |
2 |
Мы проектируем домены прицельно под конкретные классы ошибок; собираем диалоги, где ошибки, восстановление и развилки возникают сами, потому что инструменты исполняются в базе; переисполняем каждый вызов на проверке и отбрасываем расхождения. На выходе — корпус, где каждый ответ инструмента воспроизводим, а каждый диалог показывает живую работу с состоянием.
На отложенном тесте (телеком, финансы, HR) дообучение через GRPO (в таблицах и графиках мы обозначили так Cotype Light 3) поднимает основную метрику с 0,794 до 0,856. Главный прирост — на автономных задачах, где у компактной модели меньше всего права на ошибку: ST 0,727 → 0,821. Cotype Light 3 выходит на 0,856 — выше базовой 27B (0,817) и 122B MoE (0,833), при заметно меньшем инференсе, чем у 27B. А человеческая валидация (95,6% согласия судьи с экспертами) показывает, что и сами данные качественные.
Автор: sir-timio
Источник [8]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/33279
URLs in this post:
[1] обучения: http://www.braintools.ru/article/5125
[2] ошибку: http://www.braintools.ru/article/4192
[3] повторять: http://www.braintools.ru/article/4012
[4] логикой: http://www.braintools.ru/article/7640
[5] реакция: http://www.braintools.ru/article/1549
[6] grounding.py: http://grounding.py
[7] john.smith@unknown.com: mailto:john.smith@unknown.com
[8] Источник: https://habr.com/ru/companies/mts_ai/articles/1060112/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1060112
Нажмите здесь для печати.