Когда панель управления показывает зеленый цвет, а агент действует неправильно: отслеживание состояния
Панель управления зеленая, и агент ошибается: сбои скрываются в состоянии, передаваемом между работающими этапами. Какие записи трейсинга, маршрутизации и оценки необходимо фиксировать.
Источники: When the Dashboard Is Green and the Agent Is Wrong: Tracing Multi-Step Failures Past the LLM Call
Зеленая панель управления и неверный ответ
Отладка ИИ-агентов имеет раздражающую особенность: экран мониторинга может утверждать, что всё в порядке. Запрос к модели обработан, инструменты завершили работу, API ответили кодом 200, задержка кажется нормальной, а использование токенов соответствует ожидаемым значениям. С точки зрения инфраструктуры ничего не происходит, но итоговый результат может оказаться совершенно неверным.
Это не тот очевидный вид ошибки, когда модель выдумывает что-то абсурдное. Такие сбои проще, потому что хотя бы ясно, с чего начать. Сложнее всего, когда каждый шаг кажется разумным, технически ничего не ломается, а ответ настолько правдоподобен, что кто-то может действовать на его основе.
Проблема усугубляется по мере того, как системы эволюционируют от простых вызовов больших языковых моделей к агентам и многоэтапным рабочим процессам. Прежде чем появится ответ, соответствующая информация могла пройти через несколько моделей, инструментов, API, операций преобразования и маршрутизаторов, поэтому окончательная модель может выполнять свою работу идеально, но работать с данными, которые уже были повреждены на пяти предыдущих этапах. Этой стороне наблюдаемости агентов требуется гораздо больше внимания, чем ей уделяется.
Изменение схемы, которого никто не замечает
Рассмотрим типовую задачу по обеспечению удовлетворенности клиентов. От агента требуется найти клиентов, у которых истекает срок подписки в течение 30 дней, отметить аккаунты, которые, по-видимому, могут быть отказаны, и подготовить отчет для команды по работе с клиентами. При этом он может запрашивать данные из CRM, рассчитывать показатель риска, загружать последние запросы на поддержку, проверять уровень использования продукта, а затем использовать большую языковую модель для написания отчета.
Теперь предположим, что CRM изменяет одну из своих схем ответов. Вчера структура данных выглядела так.
{
"health_score": 42
}
Сегодня оно выглядит так.
{
"health": {
"score": 42
}
}
API по-прежнему работает, запрос успешен, а JSON корректно парсится. В зависимости от кода интеграции не требуется выбрасывать никаких исключений. Проблемы возникают позже, когда слой нормализации продолжает искать значение health_score, а на следующем этапе получается null.
Возможно, механизм оценки рисков считает отсутствующее значение нейтральным, или в инструкциях указано использовать любое доступное значение. В обоих случаях рабочий процесс продолжается и заканчивается достоверным отчетом, в котором не упоминается аккаунт, о существовании которого команде следовало знать.
Указывая на итоговый результат, легко обвинить модель, и люди начинают переписывать инструкции, менять модели или настраивать параметры температуры, не затрагивая настоящую причину проблемы. Лучше задать вопрос о том, когда состояние впервые стало неверным, — при таком подходе отладка агента выглядит совершенно иначе.
События успеха не доказывают понимание
В одной недавней статье о том, как веб превращается в API для агентов, говорится о том, что программное обеспечение переходит от предоставления информации к выполнению действий от имени пользователя. В ней утверждается, что предоставление инструментов модели — это лишь часть процесса, поскольку намерения, разрешения, контроль во время выполнения и возможность аудита имеют важное значение, когда агенты начинают выполнять реальные действия.
То же самое касается отладки этих действий. Информация о том, какой инструмент был выполнен, полезна, но она не показывает, понял ли остальной рабочий процесс результаты работы этого инструмента. Представьте трейс, содержащий такую информацию.
crm.search_accounts
status: success
duration: 184ms
records: 47
Вы узнаете, что CRM вернул 47 записей. Остается неизвестным, добрались ли все 47 записей целыми до следующего этапа. Возможно, существовало ещё шесть страниц, и пагинация заканчивалась после первой. Возможно, сумма была пересчитана из долларов в центы, или временная метка UTC позже была интерпретирована как местное время. Возможно, в значении типа enum изменилась орфография с «cancelled» на «canceled», или API начал сообщать об ошибках на уровне приложения в ответе, у которого статус HTTP казался в порядке.
Всё это обычные программные ошибки. Особенность с использованием агентов заключается в том, что соответствующая модель обычно продолжает работу, принимая к рассмотрению кажущиеся достоверными данные и анализируя их. Поэтому более совершенная модель не обязательно что-то исправит. Если на входе уже присутствуют ошибки, модель просто решает неверную проблему более умело.
На важных этапах процесса вам следует иметь возможность просматривать входные данные, результаты, применённые к ответу преобразования, а также то, что получено на следующем этапе. Нет необходимости хранить все исходные данные навсегда; достаточно сохранять столько информации, сколько нужно для восстановления процесса после сбоя.
Отслеживание отсутствующего конкурента
Инструменты становятся всё лучше. Семантические конвенции GenAI от OpenTelemetry начинают стандартизировать способ описания вызовов моделей и выполнения инструментов, а такие фреймворки, как OpenAI Agents SDK, отслеживают процессы генерации, вызовы инструментов, передачу данных и существующие ограничения. Это полезный исходный материал, но сделать отслеживание полезным всё ещё сложно, когда рабочий процесс завершён, а фактический сбой скрыт в данных, передаваемых между двумя работающими этапами.
Рассмотрим упрощённый вариант выполнения процесса, показанный на рисунке.
Предположим, что в окончательном отчете отсутствует конкурент, который явно должен там быть. Раньше принято было открыть окончательный запрос и проверить ответ модели. Возможно, инструкции были недостаточно четкими, модель проигнорировала часть контекста или другая модель справилась бы лучше. Однако сейчас такой подход уже не является правильным.
Лучше работать в обратном порядке. Получила ли окончательная модель отсутствующего конкурента? Если нет, проблема была на этапе ранжирования? Если он исчез до ранжирования, то был ли он присутствовал после извлечения данных? Его наличие после извлечения, но отсутствие после нормализации означает, что проблема заключается именно в одной из границ обработки данных.
{
"company": "Example Corp",
"website": {
"url": "https://example.com"
}
}
хотя шаг нормализации и ожидал этого.
{
"company": "Example Corp",
"website": "https://example.com"
}
Ничего драматичного в этом не было. В середине процесса объект изменил свою форму, и один из шагов обработки прошёл некорректно, однако большинство соседних операций всё ещё выглядели нормально. Подобные ошибки заслуживают отслеживания, поскольку в противном случае вина падает на тот компонент, который наиболее заметен, независимо от того, что произошло ранее.
Практический урок заключается не в необходимости использования какого-то конкретного инструмента для отслеживаемости. Суть в том, что система отслеживания должна позволять следить за состоянием в ходе рабочего процесса, а не просто перечислять, какие компоненты выполнялись. Существует большая разница между знанием того, что был выполнен Шаг 4, и знанием того, что получил от него Шаг 5.
Плановое отклонение кажется разумным
Как только вы обнаруживаете сбой, связанный с ограничениями инструмента, ситуация становится довольно ясной. Однако сбой, связанный с отклонением от плана, сложнее выявить, поскольку агент может выполнять первоначальное задание с помощью шагов, каждый из которых кажется разумным. Представьте, что агенту поручено изучить пять поставщиков, оценить их цены и надежность и составить рекомендацию, при этом ему запрещено с ними связываться.
Агент находит поставщиков и начинает собирать данные. У двух из них нет указанных цен, поэтому он ищет другой способ решения этой проблемы. В конце концов он находит инструмент для отправки электронных писем. Его логика не абсурдна: нужны цены, они не являются общедоступными, а сообщение поставщику может решить этот вопрос. Однако этот шаг нарушает одно из самых важных правил задания.
Когда правило представляет собой простое предложение в верхней части растущего окна контекста, всё зависит от того, будет ли модель продолжать считать его важным после того, как накопится тысячи токенов результатов поиска, выводов инструментов и промежуточных состояний. Ограничения, имеющие реальные последствия, требуют чёткого формулирования.
{
"objective": "Compare suppliers and prepare a recommendation",
"constraints": [
"Do not contact suppliers"
],
"completed_steps": [
"discover_suppliers",
"collect_public_pricing"
],
"next_action": "request_missing_prices",
"requested_tool": "send_email"
}
Теперь у приложения есть что проверять перед выполнением действия. Оно может отклонить запрос к инструменту, потребовать утверждения человека или отправить агента на поиск другого пути. Правильный ответ зависит от рабочего процесса, но главное — ограничения больше не заключаются исключительно в формулировках промптов.
Правила вроде лимита расходов, «создать черновик, но не отправлять» или «никогда не трогать продакшн» работают аналогичным образом. Простой язык хорош для передачи намерений пользователя, но важные разрешения и ограничения также должны реализовываться в виде механизмов контроля во время выполнения задачи.
Программное обеспечение справляется с этим во всех остальных случаях. Никто бы не написал в комментарии «пожалуйста, не позволяйте неуполномоченным пользователям удалять базу данных» и называл это контролем доступа, а агенты не заслуживают исключений только потому, что их инструкции написаны на английском.
Маршрутизация моделей устанавливает границы
Маршрутизация создаёт аналогичную проблему. Выбор модели перестаёт быть простым параметром конфигурации, когда необходимо сбалансировать качество, стоимость, задержку, использование инструментов и различные возможности моделей в рамках производственной системы, причём решение о маршрутизации становится частью трассировки, необходимой для отладки.
Допустим, шаг 6 обычно выполняется с использованием модели A. Во время реального запуска у поставщика ограничивается скорость обработки, и маршрутизатор переключается на модель B. Это является запасным вариантом, работающим согласно замыслу, поскольку рабочий процесс продолжает выполняться, а не сбоит. Однако модель B форматирует структурированные аргументы для следующего инструмента немного иначе. Они остаются действительными, и инструмент работает успешно, но одно из полей считывается таким образом, что изменяется итоговый результат.
Если отчеты о наблюдаемости указывают только на модель, настроенную для рабочего процесса, кто-то может тратить часы на попытки воспроизвести сбой в модели A, в то время как запрос обрабатывалась модель B, вызвавшая сбой. Поэтому трейс должен фиксировать модель, по которой был сделан запрос, ту, что ответила, любые альтернативные варианты, действующую политику маршрутизации и то, была ли попытка вызова повторной. Это не особо интересная информация, но большинство полезных инструментов для отладки в производственных условиях тоже не являются захватывающими. Помимо демонстраций, большая часть работы заключается в выявлении тех незаметно изменившихся незначительных деталей.
Состояние трейса, а не только события
Современные инструменты агента хорошо отображают структуру выполнения задачи, показывая вызовы моделей, используемые инструменты, передачу данных, попытки повтора и другие события в удобных для анализа видах отчетов. Ошибкой будет предполагать, что запись большего количества событий автоматически обеспечивает высокую степень наблюдаемости. Даже если десять этапов выполнения тщательно задокументированы, это не поможет, если невозможно понять, как менялось состояние системы между ними.
Короткий список вопросов помогает разобраться с неудачным выполнением задачи. Чего изначально хотел пользователь? Какие ограничения действовали? Какой план следовала система? Что возвращал каждый инструмент и что получал следующий этап? Какая модель принимала каждое решение? Изменилось ли что-то во внешнем мире? В какой момент результаты выполнения перестали соответствовать ожиданиям?
Ответы на эти вопросы требуют меньше данных, чем полная запись всего процесса, причем сбор всей информации сопряжен со своими проблемами. Запросы, аргументы инструментов и их ответы могут содержать конфиденциальные данные клиентов, учетные данные или другую чувствительную информацию, которая не должна бездумно попадать на платформу для мониторинга. Руководство OpenTelemetry по использованию генеративного ИИ указывает на это, а его детальные спецификации позволяют собирать входные и выходные данные модели только по желанию, а не по умолчанию.
Цель заключается в том, чтобы сделать возможным воссоздание только relevantных частей процесса выполнения, а не в логировании всего процесса целиком. Иногда достаточно скрытого ответа, иногда — версии схемы вместе с идентификатором объекта, а иногда для промежуточного результата требуется снимок, поскольку ничто другое не может воссоздать процесс выполнения. Принимайте решение осознанно, чтобы в случае инцидента единственной сохраненной информацией не оказался статус status: success.
Что это означает для оценки
Отчет о ходе действий объясняет, что произошло, но не указывает, был ли результат положительным, поэтому оценка по-прежнему необходима. Компания Anthropic утверждает то же самое в своей статье об оценке агентов. Оценить агентов сложнее, чем отдельные ответы, поскольку они выполняются в течение нескольких этапов, вызывают инструменты, изменяют состояние и адаптируются к предыдущим действиям.
Некоторые результаты можно проверить без дополнительной модели. Если агенту было сказано не отправлять электронное письмо, но он его отправил, это означает его неудачу; также явно отклоняющимся считается случай, когда задача по извлечению данных, предполагающая возврат 27 записей, возвращает лишь 11. Другие случаи более неоднозначны: был ли исследовательский отчет достаточно подробным? Были ли источники качественными? Решил ли ответ службы поддержки проблему клиента? Для таких случаев могут потребоваться человеческий аудит, отзывы пользователей, наборы данных или оценочные инструменты на основе моделей.
Настоящая ценность заключается в сочетании оценки с отслеживанием процесса. Отрицательная оценка рядом с сохраненным окончательным ответом лишь указывает на то, что пользователю он не понравился. Благодаря связи с всем процессом выполнения такой сигнал позволяет просмотреть полученные данные, используемые инструменты, задействованные модели и моменты, когда изменилось состояние системы.
Как только выявляется истинная причина сбоя, данный экземпляр выполнения сохраняет свою ценность даже после инцидента, поскольку может стать примером для оценки, помогающим избежать повторения проблем при будущих изменениях запросов, моделей или рабочих процессов. Это гораздо лучше, чем постепенная корректировка плохих ответов в надежде, что больше ничего не сломается.
Перестаньте начинать с окончательного запроса
Вероятно, самым значительным изменением при отладке ИИ-систем является именно это. В простом приложении на основе больших языковых моделей, которое выдает некорректный ответ, достаточно просто прочитать вводные данные и результат, поскольку мест для возникновения проблем практически нет. Однако по мере роста сложности система такой подход становится менее эффективным. Представьте себе конвейер, включающий несколько моделей, инструменты поиска и извлечения информации, внешние API, маршрутизаторы и цикл проверки: последние вводные данные могут оказаться тем местом, где наконец проявляется ранее возникшая ошибка.
RAG уже дал соответствующий урок. Когда механизм поиска вставляет в окно контекста неверный документ, более сложная модель генерации не сможет это исправить, и придётся возвращаться назад, чтобы настроить процесс поиска. Агенты следуют той же логике, но с дополнительными ограничениями. Поиск работает корректно? Посмотрите, каким стал результат. И он тоже в порядке? Обратите внимание на следующее решение. Выбрано правильное инструмент? Проверьте аргументы и результат. Состояние всё ещё верно? Продолжайте работу, пока не наткнётесь на первую ошибку, отклоняющуюся от ожидаемого результата.
На бумаге это звучит просто, но люди забывают об этом, потому что окончательный ответ находится прямо перед ними, и обычно его легче всего винить.
Возможность наблюдения за всем процессом
Агенты становятся всё более сложными. MCP предоставляет им больше инструментов, многокомпонентные архитектуры агентов включают возможности делегирования задач и их передачи, маршрутизаторы выбирают модели в реальном времени, а длительные рабочие процессы обеспечивают сохранение состояния, возможность повторных попыток и возобновления работы. Утверждение человека может потребоваться на любом этапе выполнения задачи, а агенты, работающие в браузере или на компьютере, могут изменять систему гораздо шире, чем просто в рамках чата. Всё это делает агентов более функциональными, но в то же время увеличивает риски ошибок, поскольку каждый компонент может сообщать о успехе, даже если происходят проблемы.
Именно поэтому возможности мониторинга должны выходить за рамки стандартных метрик больших языковых моделей. Важны показатели задержки, количества токенов, ошибок и затрат; панель управления должна оповещать о том, что агент застрял в цикле или тратит кредиты API в 2 часа ночи. Однако эти показатели в основном указывают лишь на то, что система функционирует. Многоэтапному агенту также необходим способ подтверждения того, что происходящее по-прежнему соответствует задаче пользователя.
На практике это означает отслеживание первоначальной цели, ключевых ограничений, инструментов и моделей, которые фактически использовались, состояния, передаваемого между этапами, и конечного результата. При неверном результате следует иметь возможность вернуться к первой точке, в которой состояние отошло от реальности. Благодаря такой способности проблема с некорректным выполнением превращается в обычную инженерную задачу, которую можно исследовать, воспроизводить и тестировать. В отсутствие такой способности приходится смотреть на конечный ответ и догадываться, какая часть системы вышла из строя ранее.
Связанные материалы
- Практические заметки: создание агента для программирования в частном облаке: OpenCode + локальная LLM — Подробное руководство по практическим заметкам: создание агента для программирования в частном облаке: OpenCode + локальная LLM: контракты, проверки и готовые блоки кода для команд, использующих эту схему.