КейсыРастИИшка
Сопровождение ИИ

Circuit breaker для ИИ-агента: остановить сбой до бесконечных повторов

Обсудить задачу → Оставить заявку

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

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

Общий маршрут: от сбоя зависимости до контролируемого восстановления
Общий маршрут: от сбоя зависимости до контролируемого восстановления. Авторская схема 404ai.

Сцена, в которой агент застревает

Условный ИИ-продавец в мессенджере отправляет заказ в условную систему учёта. В 11:40 та отвечает тайм-аутом, но заказ на самом деле создан. Агент считает попытку неудачной и повторяет запрос каждые 20 секунд. За следующие полчаса агент делает ещё 90 попыток с интервалом 20 секунд, менеджер видит три из них и не понимает, какой настоящий.

Механизм проблемы простой: агент не отличает «сервис недоступен» от «ответ потерялся по дороге». Для безопасного повтора нужно знать контракт операции и её фактический исход; одного тайм-аута недостаточно. Пока это различие не зафиксировано в коде и в рабочем объекте, любая защита от сбоев превращается в ускоритель дублей. Circuit breaker (предохранитель) решает не задачу «повторить поумнее», а задачу «перестать долбить зависимость и вернуться к ней контролируемо». Это разные вещи, и путаница между ними стоит дороже, чем сам сбой.

Если ваша задача — настроить остановку повторов, на странице соответствующего направления 404ai можно посмотреть опубликованные возможности и оставить описание текущего процесса. Конкретные подключения, исходные данные и критерии приёмки согласуем отдельно.

Почему повтор — не лечение, а симптом

Классический предохранитель из документации Microsoft описывает три состояния: закрыт (запросы идут), открыт (запросы сразу отклоняются), полуоткрыт (пропускается пробный запрос). Логика простая, но работает она только тогда, когда у нас есть чем измерить «зависимость больна». Обычно берут долю ошибок за окно времени. И тут начинается первая развилка: что считать ошибкой.

Тайм-аут, ответ 500 и «соединение сброшено» — это разные вещи с точки зрения повтора. Тайм-аут после отправки запроса не говорит, выполнилась ли операция на той стороне. Ответ 500 означает внутреннюю ошибку сервера и не говорит, выполнена ли операция. Сброс соединения — вообще лотерея. Если все три считать одинаково, предохранитель будет открываться и закрываться по случайному сигналу, а очередь продолжит копиться в моменты, когда он закрыт.

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

Предохранитель зависимости: карточка зависимости, а не флаг «включено»

Предохранитель, который живёт в виде булева флага в конфиге, бесполезен, потому что не помнит, почему открылся и что делать дальше. Нужен рабочий объект — карточка зависимости. У неё есть имя, адрес, тип операции (идемпотентная/нет), текущее состояние, время последнего перехода, причина перехода и владелец, который отвечает за ручное вмешательство.

Поле карточки Зачем нужно Пример условного значения
Идентификатор зависимости Отличать сбой одной системы от другой billing-api
Класс операции Решает, можно ли повторять create_invoice — не идемпотентна
Состояние Закрыт / открыт / полуоткрыт / ручная блокировка открыт
Причина Что именно триггернуло переход 5 тайм-аутов за 60 с
Окно наблюдения На чём считается доля ошибок 60 с, минимум 10 запросов
Политика повтора Сколько и как часто не повторять без ключа
Владелец Кто разбирает ручную блокировку дежурный по интеграциям

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

Карточка зависимости: поля рабочего объекта
Карточка зависимости: поля рабочего объекта. Авторская схема 404ai.

Эпизод первый: тайм-аут после выполнения

Условный интегратор подключил агента к условной CRM клиента. Агент создаёт сделку и сразу пишет клиенту «ваша заявка принята». В 15:02 CRM отвечает тайм-аутом через 30 секунд, хотя сделку создала. Агент по умолчанию повторяет запрос — и через минуту в CRM две сделки на одного клиента. Клиент получает два подтверждения.

Что должно было произойти. Перед отправкой агент генерирует ключ идемпотентности и кладёт его в карточку операции. При тайм-ауте операция переходит в состояние «результат неизвестен», а не «ошибка». Дальше агент не повторяет создание, а запрашивает статус по ключу — это идемпотентное чтение. Если CRM поддерживает такой запрос, дубль исключён. Если не поддерживает, агент останавливается и передаёт операцию человеку с явной пометкой «возможно, выполнено, проверьте».

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

Где остановиться и что считать пробой

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

Отложенная очередь — опасная штука. Если зависимость лежала час, а в очереди 200 заказов, то в момент восстановления они все уйдут разом и положат её снова. Поэтому в карточке нужен параметр «максимальный возраст отложенной операции». Всё, что старше, не отправляется автоматически, а уходит на ручной разбор. Это неприятно, но честнее, чем тихо отправить клиенту устаревшее подтверждение через два часа.

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

Проверка исключений: тайм-аут, частичный сбой, каскад
Проверка исключений: тайм-аут, частичный сбой, каскад. Авторская схема 404ai.

Эпизод второй: сервис ожил частично

Условный отдел продаж использует агента, который пишет первым в мессенджерах по согласованному процессу. Зависимость — условный шлюз сообщений. В 09:15 шлюз начинает отвечать 503 на часть запросов: отправка в один канал проходит, в другой — нет. Предохранитель настроен на общую долю ошибок и после порога открывается целиком. В итоге перестают уходить и те сообщения, которые шлюз бы принял.

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

Второй нюанс: частичное восстановление. Допустим, в условном примере шлюз отвечает успешно на 70% запросов. Формально доля ошибок ниже порога, предохранитель закрыт, но 30% клиентов не получают сообщение. Здесь помогает не предохранитель, а отдельный счётчик «недоставленные» с явной пометкой в интерфейсе сотрудника. Предохранитель защищает зависимость, а не гарантирует доставку. Смешивать эти роли — значит обещать себе то, чего механизм не делает.

Предохранитель зависимости на экране сотрудника

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

Что видит сотрудник Источник Действие
«5 заявок ждут отправки в CRM» Очередь отложенных Повторить вручную / отклонить
«Отправка в канал X приостановлена» Карточка маршрута Посмотреть причину
«Заявка №… возможно создана, проверьте» Операция с неизвестным результатом Открыть в CRM и подтвердить
«Автоповтор остановлен до 16:00» Ручная блокировка Снять блокировку (с подтверждением)

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

Ручная блокировка — отдельное состояние, не пересекающееся с автоматическим. Если дежурный закрыл зависимость вручную, автоматическая проба не должна её открывать. Иначе через пять минут после решения «не трогаем до утра» система сама начнёт слать пробные запросы. В карточке должно быть поле «кто и до какого времени заблокировал», и оно имеет приоритет над автоматом.

Разные зависимости — разные предохранители

Соблазн сделать один общий предохранитель на «внешние API» велик, но он смешивает несмешиваемое. Условный агент может зависеть от модели, от базы знаний, от мессенджера и от CRM. Падение модели не означает, что CRM недоступна. Если предохранитель общий, сбой одного компонента останавливает всё.

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

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

Приёмочные тесты на подставном сервисе
Приёмочные тесты на подставном сервисе. Авторская схема 404ai.

Предохранитель зависимости: исключения и проверка условий

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

Второе — операция, результат которой нельзя проверить постфактум. Например, отправка сообщения в канал без истории доставки. Если ответ потерялся, узнать, дошло ли, невозможно. Здесь помогает только предварительная запись намерения в собственный журнал: агент фиксирует «собираюсь отправить X клиенту Y в Z», и при неопределённости сотрудник видит намерение, а не факт. Журнал намерений — не гарантия, но он отделяет «мы не знаем» от «мы не пытались».

Третье — каскад. Зависимость A вызывает B, B вызывает C. Предохранитель на A открывается, но запросы уже ушли в B и C. Нужно, чтобы отмена распространялась вниз. В браузерном контексте для этого есть AbortController: он прекращает ожидание ответа на клиенте, но не гарантирует, что сервер не получил и не выполнил запрос. То есть отмена — это про экономию ресурсов, а не про откат. Это различие стоит держать в голове, когда настраиваешь тайм-ауты.

Четвёртое — асинхронный ответ. Документация Microsoft по async request-reply описывает схему, где клиент получает не результат сразу, а идентификатор и потом опрашивает статус. Для предохранителя это подарок: операция создания отделена от операции чтения статуса, и повтор чтения безопасен. Но это работает только если зависимость реально поддерживает такой паттерн. Приписывать его системе, которая отвечает синхронно, нельзя.

Как проверить, что предохранитель не врёт

Проверка начинается не с нагрузки, а с вопроса: что произойдёт, если зависимость ответит тайм-аутом после выполнения. Это тест-сценарий, который нужно прогнать на стенде с подставным сервисом. Подставной сервис умеет: отвечать успешно, отвечать 500, отвечать тайм-аутом после записи, отвечать частично. Прогон по каждому сценарию показывает, не появляются ли дубли и не теряются ли операции.

Сценарий подставного сервиса Ожидаемое поведение агента Что проверяем
Успех Операция завершена, ключ закрыт Нет лишних повторов
500 до записи Повтор по политике Счётчик ошибок растёт
Тайм-аут после записи Статус «неизвестно», без повтора Нет дубля
Частичный успех Предохранитель по маршруту Здоровые маршруты работают
Долгая недоступность Очередь с ограничением возраста Старое не уходит автоматом

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

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

Практикум: карточка для отправки предложения
Практикум: карточка для отправки предложения. Авторская схема 404ai.

Практикум: собрать карточку для одной операции

Возьмём условную операцию «отправить коммерческое предложение клиенту через мессенджер». Задача — настроить остановку повторов так, чтобы клиент не получил три одинаковых письма, а менеджер понимал, что происходит.

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

Шаг второй: определить границу. Предохранитель на маршрут «мессенджер-канал», а не на весь мессенджер, потому что каналы могут падать по отдельности.

Шаг третий: задать окно и порог. Условно: 5 ошибок за 60 секунд при минимуме 10 запросов. Числа — предмет проверки на стенде, а не догма.

Шаг четвёртый: задать поведение очереди. Максимальный возраст отложенной отправки — условно 15 минут. Старше — ручной разбор с пометкой «устарело».

Шаг пятый: описать интерфейс. Менеджер видит список отложенных и список «неизвестный результат». Кнопка повтора недоступна для второго списка без сверки.

Шаг шестой: прогнать сценарии из таблицы выше на подставном шлюзе. Зафиксировать результат в карточке: дата, кто проверял, что сломалось.

Что делать с уже накопленной очередью

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

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

Где заканчивается предохранитель

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

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

Один шаг проверки

Смежный вопрос разобран отдельно: Ошибка 429 у ИИ-агента: ожидание, пределы и полезный отказ.

Источники и границы разбора

Источники и статус проверки на 9 ноября 2026. Первичные документы поясняют указанные механизмы; доступность и границы каждого источника отмечены ниже. Методические рекомендации и условные примеры составляют самостоятельную часть разбора.

  1. Сопровождение ИИ-агентов

    Опубликованное направление работы 404ai. Предлагаемая в статье методика не означает, что каждое описанное действие уже доступно без настройки; состав решения согласуется отдельно.

  2. Microsoft: Circuit Breaker pattern

    Первичная документация указанного инструмента или механизма. Практические рекомендации и условные эпизоды статьи — самостоятельная методика, а не описание готовых функций 404ai.

  3. Microsoft: Asynchronous request-reply pattern

    Первичная документация указанного инструмента или механизма. Практические рекомендации и условные эпизоды статьи — самостоятельная методика, а не описание готовых функций 404ai.

  4. MDN: AbortController

    Первичная документация указанного инструмента или механизма. Практические рекомендации и условные эпизоды статьи — самостоятельная методика, а не описание готовых функций 404ai.

Частые вопросы

Почему тайм-аут после отправки нельзя считать ошибкой и повторять?

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

Каким запросом можно проверять зависимость в полуоткрытом состоянии?

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

Что делать с очередью, накопившейся за время открытого предохранителя?

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

Зачем нужна ручная блокировка и чем она отличается от автоматической?

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

Сколько предохранителей нужно, если агент зависит от нескольких систем?

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

404ai · задача вашей компании

Обсудим вашу задачу

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

Укажите телефон или Telegram для ответа

Не присылайте пароли, ключи и клиентские базы. Для первого обсуждения достаточно описания задачи и контакта. Доступы и обработку данных согласуем отдельно, если они понадобятся.

Заявка принята. Свяжемся, уточним задачу и согласуем следующий шаг.

Можно также написать напрямую: Telegram 404ai.

Подробнее: Сопровождение ИИ
Разбор задачи — бесплатноОтвет за 15 минут
Разобрать задачу