Тихая катастрофа: агент, который не признает ошибку
Классический сценарий в продакшене: агент должен выполнить 4 запроса к внутреннему API для обработки счетов. Первые два успешны, но из-за истекших учетных данных третий и четвертый запросы возвращают ошибку 401 Unauthorized. Вместо того чтобы сообщить о сбое, модель начинает перебирать аргументы, пытаясь «угадать» правильный запрос, а затем выдает связный, уверенный ответ, основанный на выдуманных данных. Статус выполнения — Success, дашборд зеленый, но бизнес получает ложную информацию.
Проблема не в том, что модель глупая, а в том, что для LLM текст ошибки — просто еще один токен в контексте. Если инструкция требует ответить на вопрос, модель найдет способ сделать это, даже без данных. Умная модель просто лучше маскирует этот провал.
Архитектура защиты: Разделение ошибок на уровне кода
Главный вывод автора: модель не способна различать типы сбоев, поэтому сортировку нужно вынести в plain-code обертку перед вызовом LLM. Ключевое правило: не все ошибки требуют повторной попытки (retry).
Автор предлагает разделять ошибки на три категории:
- Временные сбои (Retryable): Таймауты, 503, 504. Требуют повторного запроса с джиттером (jitter) для предотвращения синхронных повторных попыток в кластере агентов.
- Фатальные ошибки (No Retry): 401, 403, 404. Повторный запрос бессмысленен, он только сжигает токены. Ошибка должна быть передана модели с инструкцией не гадать.
- Неоднозначные состояния: Частичные успехи (например, платеж мог пройти). Здесь код должен остановиться и передать задачу человеку.
Реализация: Обертка для инструментов
Ниже приведен пример функции-обертки, которая ограничивает количество попыток (MAX_TRIES=3) и перехватывает ошибки HTTP-статусов. Обратите внимание на список RETRYABLE, который исключает клиентские ошибки 4xx (кроме 429 Too Many Requests).
| Категория ошибки | HTTP Статусы | Действие системы | Причина |
|---|---|---|---|
| Временный сбой | 500, 502, 503, 504, 429 | Повторный запрос (Retry) | Сервер может восстановиться; 429 требует уважения заголовка Retry-After |
| Фатальная ошибка | 401, 403, 404 | Прерывание вызова (ToolDown) | Проблема в аутентификации или ресурсе; повтор не поможет |
| Сетевая ошибка | Timeout, ConnectionError | Повторный запрос (Retry) | Отсутствие ответа трактуется как нестабильность сервера (аналог 5xx) |
Два уровня защиты: Инструктаж и Финальная проверка
Ограничение ретраев — только половина решения. Вторая часть — управление контекстом модели и финальный guard.
- Фильтрация контекста: Вместо передачи сырого тела ошибки (которое модель попытается интерпретировать), обертка возвращает строгую инструкцию:
"TOOL_FAILED: {name} returned {status}. Do not guess. Tell the user this lookup failed.". Это запрещает модели импровизировать. - Финальный чек (Final Answer Guard): После завершения задачи система проверяет счетчик успешных вызовов. Если
run["succeeded"] == 0, агент обязан выдать отказ:"I could not reach the data needed to answer this, so I am not going to guess.".
Важный нюанс: пустой список результатов (например, «найдено 0 счетов») считается успешным вызовом. Проверка должна смотреть на факт возврата ответа, а не на его содержимое.
Наблюдаемость (Observability): Что действительно важно мониторить
Стандартные метрики доступности (uptime) API здесь бесполезны, так как API может быть онлайн, но возвращать 401. Автор предлагает отслеживать три ключевые метрики агента:
- Доля запусков, в которых не было ни одного успешного вызова инструмента.
- Топ-5 самых часто падающих инструментов с кодами ошибок (например, кластер 401-х).
- Количество случаев, когда guard принудительно заблокировал ответ агента.
Этот подход превращает «тихую» галлюцинацию в измеримую метрику отказа, позволяя быстро выявлять проблемы с интеграциями, а не с качеством модели.
Источник: Towards Data Science ↗
