Остановка и повторы
Этот блок про то, как заканчивается цикл и какие сбои стоит повторять.
Агент Northwind вызывает issue_refund, а платёжный сервис лежит. Модель пробует снова, получает ту же ошибку и пробует ещё раз. Ничто не гарантирует, что она остановится.
Кто говорит «хватит»
Вернёмся к телефону. Коллега снова и снова просит позвонить в платёжный отдел, а там не берут трубку. Просить он будет, пока вы отвечаете. Поэтому решение остановиться за вами, и это должно быть правило, которое можно посчитать: пять попыток, потом дело уходит другому.
Из цикла два выхода.
Обычный выход — это ответ без tool call. В API Anthropic он приходит со stop_reason, равным end_turn. Ответ с max_tokens оборван, считать его ответом нельзя.
Принудительный выход — это бюджет, который считает ваш код:
Бюджет | Что ловит |
|---|---|
Вызовы модели за прогон | Бесконечные вызовы инструментов |
Время прогона | Прогоны, которые затянулись |
Токены или деньги за прогон | Прогоны, которые становятся дорогими |
Одинаковые сбои | Один вызов падает одинаково раз за разом |
Бюджеты считает цикл. Лимит, который есть только в промпте, — это просьба к модели.
Зависший инструмент — это вы на удержании у телефона. Цикл ждёт, и счётчик никто не проверяет. Поэтому таймаут нужен и на каждый вызов инструмента.
Что получает покупатель
Когда срабатывает бюджет, покупатель получает короткое честное сообщение, а дело вместе с историей переходит к человеку:
Не получилось завершить возврат автоматически. Вашим обращением уже занимается сотрудник, он ответит в этом чате.
Никаких stack trace и внутренних текстов ошибок. Если возврат уже прошёл, так и скажите, причём берите этот факт из платёжных записей: текст модели записью не является.
Занято или нет такого номера
Когда вы кому-то звоните, бывает два исхода. Занято, и вы перезваниваете через минуту. Или такого номера не существует, и сколько ни звони, результат будет тот же.
Сбои делятся так же:
Сбой | Что делать |
|---|---|
429, лимит запросов | Занято. Подождать, учесть |
5xx при вызове модели, включая 529 у Anthropic | Занято. Retry с backoff |
Ошибка соединения или таймаут вызова модели | Занято. Retry с backoff |
400, 401, 403 | Нет номера. Чинить запрос, ключ, права или лимит расходов |
429 из-за лимита расходов или кредитов | Нет номера. Повторы не помогут: поменять лимит или ждать его сброса |
Бизнес-правило отклонило возврат | Нет номера. Вернуть модели как ошибку инструмента |
По одному статусу два вида 429 не отличить, поэтому смотрите на код ошибки.
Как правильно ждать
Backoff увеличивает паузу после каждой попытки до верхней границы: секунда, две, четыре.
У одного backoff есть изъян. Если тысяча клиентов потеряла связь в один момент, все они и перезвонят в один момент, и линия снова занята. Jitter добавляет в каждую паузу случайность, и звонки расходятся.
Официальные Python SDK от Anthropic и OpenAI по умолчанию сами делают два retry при ошибках соединения, 408, 409, 429 и 5xx. Ваш цикл повторов поверх них умножает число попыток.
С таймаутом или 5xx на записи сложнее: неизвестно, прошёл ли возврат. Об этом следующий блок.
Вывод
- Прогон заканчивает ваш код, по бюджетам, которые считает сам.
- Покупатель получает честное сообщение и человека.
- Повторяются только сбои, которые могут пройти сами, с растущими и случайными паузами.