Почему просмотр трейса — это не валидация
Трейс — это запись уже произошедшего запуска. Он показывает, какие инструменты были вызваны, в каком порядке, с какими аргументами и что вернулось. Он полезен для отладки и для формирования интуиции о поведении агента. Но это не сигнал готовности.
Когда команда просматривает трейс и объявляет агента валидированным, она валидирует выбранный ею набор сценариев, а не среду, с которой агент столкнётся. Проходят те случаи, которые кто-то догадался запустить. Падают в продакшене, по определению, те, которые никто не догадался запустить. Трейс не может их содержать.
Это важно, потому что гейты согласования часто строятся на демонстрационных запусках. Стейкхолдер наблюдает, как агент выполняет задачу, видит чистый трейс и подписывает. Риск, который должен был быть снят на гейте, вместо этого переносится в окно развёртывания, где его дороже наблюдать и труднее откатить. Гейт не снизил риск; он его переместил.
Валидация должна быть активным процессом. Вы не читаете поведение агента и не выводите из него готовность. Вы возмущаете среду и наблюдаете, выдерживает ли путь восстановления агента. Это инъекция отказов, и для агентов это единственная форма валидации, которая даёт свидетельства об отказах, которых вы ещё не видели.
Классы отказов, которые стоит инжектировать
Инъекция отказов для агентов — это не обобщённый chaos-эксперимент. Значение имеют отказы, взаимодействующие с планированием агента, использованием инструментов и памятью. Практичный стартовый набор:
- Таймауты инструментов. Зависимый API перестаёт отвечать в пределах бюджета таймаута агента. Агент должен решить, повторять ли запрос, переключаться на запасной вариант или прерываться, и он не должен фиксировать план, предполагающий, что результат пришёл.
- Частичные ответы и дрейф схемы. Инструмент возвращает усечённый payload, или поле меняет тип либо исчезает. Агенты, разбирающие вывод инструмента с неявными допущениями, либо падают, либо, что хуже, молча неверно интерпретируют результат.
- Истечение учётных данных в середине запуска. Токен истекает после того, как план зафиксирован, но до финальной записи. Агент должен выявить отказ, а не повторять запрос с устаревшими учётными данными или продолжать без авторизации.
- Отказы в правах доступа после фиксации. Вызов инструмента отклоняется по причинам авторизации, а не доступности. Это иной путь восстановления, чем таймаут, и часто он наступает после того, как агент уже сообщил пользователю, что задача выполняется.
- Противоречивые инструкции от двух инструментов. Два источника расходятся в одном и том же факте. Агент должен обнаружить конфликт, а не выбрать последний прочитанный.
- Устаревший контекст, противоречащий свежему retrieval. Кэшированное значение в контексте агента конфликтует со свежим чтением. Агент должен предпочесть свежий источник и обновить своё состояние, а не согласовывать оба в правдоподобный, но неверный ответ.

Что измерять
Инъекция отказов без измерений порождает анекдоты. Значение имеют измерения, которые говорят вам, является ли путь восстановления спроектированным поведением или случайностью.
- Время восстановления. Сколько — по стенным часам или в ходах модели — требуется агенту, чтобы достичь стабильного состояния после отказа. Восстановление, занимающее больше времени, чем собственный дедлайн задачи, — это не восстановление.
- Шаги, потреблённые на восстановление. Сколько вызовов инструментов и циклов планирования тратится на отказ. Агент, который восстанавливается, но сжигает весь свой бюджет шагов, не оставляет ёмкости на задачу.
- Терминальное состояние. Завершается ли запуск в безопасном состоянии. Безопасно означает, что побочные эффекты либо зафиксированы корректно, либо не зафиксированы вовсе, и пользователю сообщено, что именно. Запуск, завершающийся частичной записью и сообщением об успехе, — это отказ, независимо от того, насколько изящно он восстановился.
- Дивергенция траектории. После каждой инжектированной ошибки сравните запланированную траекторию с исполненной. Дивергенция не обязательно плоха, но неизмеренная дивергенция — плоха. Если агент молча меняет план, вы потеряли способность рассуждать о его поведении.
Эти измерения — не единый балл. Это профиль. Запуск, который восстанавливается быстро, но завершается небезопасно, хуже того, который занимает больше времени и завершается чисто. Профиль позволяет явно принять этот компромисс.
Конкретный механизм: харнесс инъекции отказов
Механизм, делающий это воспроизводимым, — это харнесс, располагающийся между агентом и его инструментами. Это не мок. Это прокси, способный применить набор отказов к живым или записанным ответам инструментов.
class FaultInjector:
def __init__(self, faults, seed):
self.faults = faults
self.rng = random.Random(seed)
def wrap(self, tool):
def call(*args, **kwargs):
fault = self._select(tool.name)
if fault == "timeout":
raise ToolTimeout(tool.name)
if fault == "schema_drift":
return self._mutate_schema(tool(*args, **kwargs))
if fault == "partial":
return self._truncate(tool(*args, **kwargs))

Харнесс записывает для каждого инжектированного отказа время восстановления, потреблённые шаги, терминальное состояние и дивергенцию между запланированной и исполненной траекториями. Набор отказов версионируется вместе с агентом. Когда меняется промпт или схема инструмента, набор отказов перезапускается. Регрессия — это отказ, который ранее восстанавливался чисто, а теперь нет.
## Делая это воспроизводимым
Набор отказов, который не версионируется, — это разовый тест. Набор отказов, который версионируется, — это актив.
Практика состоит в том, чтобы рассматривать набор отказов как часть определения агента. Когда меняется промпт агента, набор отказов запускается. Когда меняется схема инструмента, набор отказов запускается. Когда обнаруживается новый продакшен-отказ, он становится новой фикстурой в наборе отказов, а не абзацем в постмортеме. Постмортем всё ещё существует, но его выход — воспроизводимый тест, а не нарратив.
Это меняет форму разговора о развёртывании. Вместо вопроса, справился ли агент с демо, вопрос становится таким: какие классы отказов были инжектированы, как выглядел профиль восстановления и падает ли теперь какой-либо ранее проходивший отказ. Это вопрос с ответом, основанным на свидетельствах.
Компромисс в том, что набор отказов растёт монотонно. Каждый продакшен-отказ добавляет фикстуру, и набор становится медленнее. В какой-то момент набор нужно расслоить: быстрый поднабор, запускаемый при каждом изменении промпта, и полный набор, запускаемый перед развёртыванием. Решение о расслоении само по себе является суждением о том, какие отказы наиболее вероятно повторятся, и его следует пересматривать по мере изменения среды агента.
## Вопрос валидации
Вопрос не в том, хорошо ли агент выглядел в трейсе. Вопрос в том, что происходит, когда инструмент, от которого он зависит, перестаёт отвечать, учётные данные, которые он держит, истекают, а контекст, который он несёт, противоречит только что прочитанному. Если вы не инжектировали эти отказы, вы не знаете. Если инжектировали, у вас есть профиль восстановления, набор фикстур и решение о развёртывании, опирающееся на свидетельства, а не на чистый запуск.
Валидация — это не чтение. Это инъекция.
