Webrium
Подписаться

Оценка качества агента до релиза

Почему один успешный прогон агента ничего не доказывает и как выстроить оценку, которая до релиза покажет форму распределения и характерные отказы

Сетка прогонов агента: успешными заполнена лишь часть ячеек, рядом счётчик типов отказовтипы отказов

Агент собран: на демо он разобрал тикет, поправил конфиг, сам себя проверил, отчитался. Дальше вопрос, на котором ломается привычная инженерная рефлексия: что значит «готов к релизу»? Для кода ответ дают тесты — зелёное, едем. У агента зелёного не бывает: модель сэмплирует, окружение отвечает, каждый следующий шаг зависит от того, что вышло на предыдущем. Один успешный прогон — одна точка из распределения, о форме которого вы пока не знаете ничего.

Арифметика работает против агента. Задача решается цепочкой шагов, и на каждом есть свои способы ошибиться: не тот инструмент, не тот аргумент, факт, выпавший из контекста, уверенно выдуманное состояние. Если шансы пройти шаг чисто перемножить по всей цепочке, вероятность безошибочного прогона падает быстрее, чем подсказывает интуиция: интуиция настроена на ошибки, которые складываются, а в цепочке они перемножаются. Отсюда практическое следствие: «вроде работает» после нескольких запусков — не данные, а настроение.

Оценка до релиза — не проверка «работает ли», а достаточно плотная выборка прогонов, чтобы увидеть форму распределения: где агент справляется, как часто и какого рода у него отказы. Отдельная тема — отказы, которые вылезают уже после выката, когда накапливается контекст и расползается состояние; мы разбирали их в материале о том, что отказывает на второй неделе агента в продакшене. Здесь — о том, что реально поймать до релиза.

Тупик: погонять руками

Первое, что пробуют почти все, — запустить агента и посмотреть. Сидишь, задаёшь задачи, киваешь, поправляешь промпт, запускаешь снова. Выглядит как тестирование, ощущается как тестирование — и не является им по устройству.

Во-первых, задачи придумываете вы. Значит, вы сэмплируете то подпространство, которое уже знаете, — а агент писался, пока вы смотрели ровно на эти же задачи. Успех на них почти ничего не говорит об остальном пространстве: это проверка ученика по конспекту, который он держал открытым.

Во-вторых, хвост распределения в ручном режиме не случается. Отказы, которые решают исход, требуют длинных цепочек шагов и странного состояния мира: наполовину сделанная работа, конфликтные инструкции, база, испорченная предыдущим прогоном. Вручную вы неосознанно разворачиваетесь от таких ситуаций — их неудобно воспроизводить, и рука сама тянется к чистому входу.

В-третьих, пропускная способность. Каждый новый инструмент умножает пространство состояний, а скорость ручной проверки остаётся человеческой. Гонка проигрывается заранее: пространство растёт быстрее, чем ваши вечера. И четвёртое: оценщик плывёт. Ближе к концу сессии «почти получилось» начинает засчитываться, планка опускается незаметно для самого себя.

Инженерный инстинкт затем предлагает два спасения, и оба уходят в тот же тупик. Первое: раз это софт, пишем тесты. Ассерт на точное совпадение ответа умирает при первом же повторном запуске — текст в этот раз другой. Ассерт ослабляют до подстроки — и тест начинает проходить всегда, включая прогоны, где агент сделал не то: «готово» в ответе есть, файла нет. Проверяется текст, а не результат. Второе: вернуть детерминизм — зафиксировать параметры генерации, чтобы прогоны повторялись. Тесты зеленеют, но меряют закреплённую траекторию, тогда как в релизе агент живёт незакреплённым: это измерение не той величины. К тому же живое окружение отвечает по-разному и без генерации — появляются мигающие тесты, команду приучают перезапускать до зелёного, и красное перестаёт быть сигналом. Это хуже, чем отсутствие тестов.

У всех попыток один корень: проверяются тексты и траектории вместо состояния задачи. Выход — сменить сам объект проверки.

Постусловия вместо текстов

Прогон агента — переход мира из состояния А в состояние Б. Проверять надо Б. Постусловие — программная проверка результата: файл изменился ожидаемым образом, тесты проекта проходят, тикет закрыт, временного мусора не осталось. Это самый сильный вид проверки, потому что его нельзя обмануть вежливым текстом: либо состояние мира таково, либо нет. Если постусловие можно написать — пишите его, а не ассерт на ответ.

Не всё выражается постусловиями: «ответ по делу», «не наврал ли агент в рассуждении». Здесь нужен судья — модель с рубрикой. Судья сам вероятностный, поэтому его закрепляют: одна модель на всю серию, неизменные инструкции, никаких правок рубрики посреди замера — иначе меряется дрейф судьи, а не агента. Судью градуируют вручную: горсть прогонов оценивают сами и сравнивают с его вердиктами; расхождение — дефект рубрики, а не повод верить судье вопреки себе. При сомнении зовут второго судью: расхождение вердиктов само по себе сигнал, что критерий сформулирован размыто.

Рубрика при этом спрашивает о фактах, а не о впечатлении: «названа ли причина», «создан ли файл с ожидаемым содержимым», а не «хороший ли ответ». На общий вопрос судья охотно отвечает общим «да»: у него нет вашего знания задачи, и он опирается на уверенность и связность текста, а не на суть.

Когда постусловие упало, нужно найти шаг, где всё поехало. Финальный текст этого не скажет: агент может уверенно отчитаться об успехе на фоне испорченного состояния. Без трассировки шагов оценка — гадание по последней строке; как её собирать, мы разбирали в практике о трассировке шагов вместо логов.

Сценарии: начальное состояние — половина теста

Сценарий — начальное состояние, задача, постусловия и бюджет: сколько шагов считается нормой для этой задачи. Про начальное состояние забывают чаще всего, а между тем одна и та же задача в чистом репозитории и в репозитории с чужими недоделками — два разных сценария с разными исходами; гонять их как один — смешивать выборки.

Здесь же живёт большинство багов самой обвязки: забыли сбросить базу — прогоны наследуют состояние друг от друга; забыли пересоздать окружение — результаты плавают без всякой стохастичности агента. Правило одно: каждый прогон стартует из воспроизводимого состояния — контейнер, снимок базы, чистая ветка. Если это не обеспечено, дальше можно не читать: измеряется не агент.

Откуда брать задачи — из реальной работы, ради которой агента делали; из отказов, которые уже видели: каждый пойманный отказ становится сценарием, иначе он вернётся в прод; из граничных состояний: пустой вход, конфликтные инструкции, состояние, которое агент сам испортил в прошлом прогоне. Синтетические капканы почти бесполезны — в жизни агент встречает настоящие формы задач, а не ваши.

Каждый сценарий гоняется многократно: один прогон на сценарий — снова точка, а не распределение. Основная цифра — доля успешных прогонов по сценарию; сколько именно прогонов, решает бюджет, потому что прогон стоит шагов и вызовов модели. Доли не усредняют в одну: сценарии весят по-разному, и веса расставляет предметная область, а не арифметика.

Набор сценариев — живой артефакт: он растёт из каждого отказа, найденного руками или в проде, и пересматривается, когда меняется окружение агента. Замороженный набор быстро становится самообманом — он тестирует агента, которого уже нет.

Порог релиза: классы отказов вместо среднего

Средняя оценка по набору — самая бесполезная цифра: она смешивает «не смог и честно сказал» с «сделал не то и уверенно отчитался». Отказы классифицируют. Обратимый: агент остановился, сообщил, состояние можно вернуть. Разрушительный: данные испорчены, сделано лишнее, отчёт врёт. Это разные валюты — складывать их в среднее всё равно что суммировать рубли с часами простоя.

Порог ставится на класс, а не на набор. Как выбрать без чужих цифр: возьмите отказы, которые уже видели, и по каждому решите, что сделает пользователь — промолчит и повторит, заметит и вернётся или уйдёт. Ответы по классам и есть ваши пороги, переведённые в долю успешных прогонов. Разрушительным классам — самый жёсткий порог, обратимым — мягче; это осознанное решение, а не усреднение.

Рядом с долей всегда меряется цена: шаги и вызовы модели на задачу. Правка промпта может поднять долю и одновременно утяжелить каждый прогон — вы просто купили результат дополнительным вниманием, и видно это только рядом с ценой.

Прогоны делят по частоте: дымовой набор — на каждое изменение промпта или инструмента, полный — перед релизом, ночной — если окружение живое и меняется само. Дымовой набор — не случайная выборка: короткие сценарии с дешёвыми постусловиями плюс классы отказов, которые уже ловили. И обязателен диф по сценариям, а не только общая цифра: правка, поднимающая один сценарий и роняющая другой, в среднем выглядит как успех.

Позиция редакции

Мы считаем: тест агента до релиза — это программные постусловия на состоянии задачи, многократные прогоны сценариев с долей успехов по каждому и пороги, назначенные на классы отказов. Ручные прогоны и ассерты на текст — ритуал, а не тестирование: они создают ощущение проверки, не проверяя ничего из того, что решает исход.

Мы неправы, если ваш агент — одиночный вызов модели без цикла и инструментов, с детерминированной обвязкой вокруг: тогда хватает обычных тестов на обвязку и нескольких проверок на ответ, и весь каркас выше — из пушки по воробьям. И второй случай: цена любого отказа ничтожна, полностью обратима и видна пользователю сразу — тогда хвост распределения можно не вылавливать, хватит дымового набора.

Во всех остальных вариантах — цикл, инструменты, доступ к чужим данным — релиз без распределения прогонов это лотерея, в которой вы не знаете своих шансов. Их сообщат тикеты поддержки.

С чем это связано

Метка над заголовком говорит, зачем туда идти: продолжить тему, перейти к практике или увидеть возражение.

Шесть растущих столбцов: при том же коде контекст запроса агента увеличивается с каждым шагом истории, в продакшене уходя далеко за уровень показаКонтекст растёт
Усиление

Агенты в продакшене: что отказывает на второй неделе

Сбои многошаговых агентов почти никогда не начинаются с модели — дело в арифметике шагов, которую можно просчитать до запуска. Разобрано, где ломается раньше всего и почему первое, что пробуют, не помогает.

Сетка из пятнадцати ячеек изображает ленту лога: девять заняты событиями, а рядом отдельный счётчик показывает лишь четыре причины решений — лента отвечает «гдепричины решений
Практика

Трассировка шагов вместо логов

Оценка качества до релиза скажет, насколько хорош агент, но не почему он решил именно так. Дальше — практика: заменяем ленту логов на дерево шагов и разбираем, где трассировка ломается.

Поле, разделённое швом: слева ассистент, который лишь отвечает текстом, справа агент, который сам выполняет действия; элементы пересекают границу, показывая, чтАссистент отвечаетАгент делает
Просто

Что такое ИИ-агент и почему о нём все спорят

Та же тема, но без терминов: чем агент отличается от ассистента и почему попытка проверить его как чат-бот заходит в тупик.

Цепочка передач между агентами: смысл теряется на границахпотеря смыслане то
Спор

Мультиагентность: зачем её пробуют и где она рассыпается

Возражение против схемы с ролями: между агентами передаётся только текст, и смысл теряется на каждой границе.

Письмо по вторникам

Один разбор недели и короткий список того, что изменилось. Без дайджестов на сорок ссылок.

Начните вводить — материалы появятся здесь.

выбрать · Enter открыть · Esc закрыть