Разработчик поправил инструкцию агента поддержки ради одной жалобы клиента. Жалоба ушла, а через неделю выяснилось, что агент перестал проверять статус оплаты перед возвратом. Тестов, которые это поймали бы, не было: агент отвечал вежливо и по делу, только делал это по другому пути.
У агента два результата: ответ и путь к нему. Проверять нужно оба, и делать это не глазами на трёх примерах, а набором, который прогоняется на каждое изменение. Эта статья про то, как такой набор устроен.
Одинаковый ответ на разных путях: тест траектории ловит пропущенную проверку, которую тест итога не видит.
Почему обычный тест не годитсяспросят на собеседовании
Модульный тест сравнивает результат с ожидаемым значением. У агента результат каждый раз сформулирован по-другому, и даже при одинаковом входе путь может отличаться: температура и порядок данных от инструментов меняют решения модели. Сравнить строку ответа со строкой эталона нельзя, а один прогон ничего не доказывает.
Отсюда три отличия от привычных тестов. Проверяют не равенство, а свойства. Прогоняют не один пример, а набор, и считают долю прошедших. И проверяют не только итог, но и то, какие инструменты агент звал и с какими аргументами.
Проверка итогаспросят на собеседовании
Самая дешёвая проверка: ответ обладает нужными свойствами. Если агент возвращает структурированный вывод, проверки пишутся обычным кодом: поле status равно refund_created, сумма совпадает с суммой заказа в тестовой базе, в тексте клиенту есть номер заказа.
Проверка итога отвечает на вопрос «сделал ли агент то, что нужно», но не «как». Для запросов, где путь не важен, её хватает. Для агентов, которые трогают деньги и данные, нет: правильный ответ на неправильном пути означает, что в следующий раз ответ тоже будет неправильным.
Проверка траекторииспросят на собеседовании
Траектория это список вызовов инструментов с аргументами за один прогон. Её пишет журнал прогона, и тест сравнивает его с ожиданиями.
{
"input": "Верните деньги за заказ 4512, товар не подошёл",
"expect_calls_in_order": ["find_order", "check_payment", "create_refund"],
"forbid_calls": ["delete_order"],
"expect_args": { "create_refund": { "order_id": "4512" } }
}
Сравнивать траекторию целиком почти никогда не нужно: агент может лишний раз прочитать заказ, и это не ошибка. Обычно проверяют три вещи: обязательные вызовы стоят в нужном порядке, запрещённых вызовов нет, у критичных вызовов правильные аргументы. Так тест не ломается от безобидных отличий и ловит опасные.
Рубрика и модель-судьяспросят на собеседовании
Часть свойств кодом не проверить: вежлив ли ответ, не обещает ли он того, чего компания не делает, ответил ли агент на вопрос, а не на соседний. Для них берут вторую модель и дают ей рубрику: список вопросов с ответом «да» или «нет».
{
"rubric": [
"Ответ называет номер заказа из запроса?",
"Ответ не обещает сроков, которых нет в данных инструментов?",
"Ответ не содержит внутренних кодов и названий систем?"
]
}
Судья с рубрикой ошибается заметно реже, чем судья с просьбой «оцени от 1 до 10»: узкий вопрос проще, чем общая оценка. Самого судью тоже проверяют: размечают руками несколько десятков ответов и смотрят, как часто он соглашается с человеком. Судья, который согласен в семи случаях из десяти, годится для тренда, но не для решения о выкате.
Набор примеров и порогспросят на собеседовании
Все проверки работают на наборе примеров. В него попадают типичные запросы, края (пустой заказ, чужой заказ, просьба на другом языке) и каждый случай, на котором агент уже ошибся в проде: ошибка из жалобы становится примером, чтобы не вернуться. Хватает пятидесяти-ста примеров, если они разные.
Каждый пример прогоняют несколько раз, обычно три-пять, потому что один прогон может случайно пройти. Итог считают долей: 96 из 100 прошли траекторию, 91 рубрику. Перед выкатом сравнивают с прошлой версией, и правило выката записывают заранее: например, ни один пример про деньги не упал, общая доля не ниже прошлой. Так правка ради одной жалобы перестаёт молча ломать остальные.
Глубже: что ещё меритьрасширенное
Кроме правильности, у агента есть цена и время: число шагов, токены, длительность прогона. Правка, которая не меняет точность, но удваивает шаги, это регрессия по деньгам. Эти числа пишут в тот же отчёт прогона.
Набор устаревает: продукт меняется, и примеры перестают быть типичными. Его пересматривают так же, как тесты: раз в квартал выкидывают то, что больше не встречается, и добавляют свежие случаи из журналов.
Коротко
- У агента проверяют итог и траекторию: правильный ответ на неправильном пути это будущая ошибка.
- Итог проверяют свойствами, кодом: поля, суммы, номера; строку с эталоном не сравнивают.
- Траектория: обязательные вызовы в порядке, запрещённых нет, аргументы критичных вызовов верные.
- Модель-судья работает по рубрике из вопросов «да или нет», и сама проверяется на ручной разметке.
- Набор 50–100 разных примеров, каждый прогоняется 3–5 раз, итог считается долей.
- Каждая ошибка из прода становится примером; правило выката записано до прогона.
Что почитать дальше
- Из чего состоит LLM-фича — три уровня тестов для одного вызова модели.
- Мультиагентные системы — где траектория важнее всего: координатор выбирает путь.
- Агент в продакшене — откуда берётся журнал прогона и как его собирать.
- Приёмка результата AI — критерии приёмки из спецификации.