Агент поддержки в проде: Temporal, сага и circuit breaker спасают возвраты от сбоев
Агент поддержки в одном файле работает в демо, а в продакшене падает: перезапуск стирает диалоги, а сбой между возвратом и ответом оставляет вопрос, ушли ли деньги. Автор Habr собирает систему по шагам: API и база, Temporal для долгих процессов, LLM-шлюз с circuit breaker, сага для возврата и оператор для крупных сумм. Каждый компонент добавляется, только когда появилась конкретная боль.
AI-обработка оригинала Habr AI; редакция Hamidun News
Клиент пишет: «Посылка не пришла, верните деньги». Агента, который на это ответит, можно написать за вечер: цикл, три инструмента, и в демо всё работает. А потом процесс перезапускается посреди возврата, провайдер модели ложится на полчаса, а клиент жмёт «отправить» дважды. Оказывается, что агент — лишь малая часть системы. Заключительная часть серии на Habr собирает эту систему шаг за шагом, и главная ценность в том, что каждый компонент появляется как ответ на конкретную поломку.
От одного файла к API, воркеру и Temporal
Начальная версия держит историю диалога в памяти процесса. Перезапуск стирает все диалоги, клиентов обслуживается по одному, а если процесс упал между вызовом refund_payment и ответом, никто не знает, вернулись деньги или нет.
Первый шаг: разделить скрипт на API и воркер. API принимает сообщение, пишет его в базу и сразу отвечает чату. Модель он не вызывает, поэтому отвечает за миллисекунды. Агентный цикл живёт в отдельных воркерах, которых можно добавлять под нагрузку. История теперь в базе и переживает рестарты.
Но воркер всё равно может упасть на середине обращения. Повторять возврат опасно: можно заплатить дважды. Не повторять тоже плохо: можно не заплатить вообще. Здесь нужен Temporal: он пишет каждый шаг в журнал, и другой воркер продолжает с того же места, не выполняя готовые шаги заново. Цикл агента живёт в workflow, а вызовы модели и инструментов становятся activities.
Два важных уточнения. У Temporal своя база, и бизнес-данные клиентов в ней не хранят: журнал одного выполнения ограничен 51 200 событий или 50 МБ. Воркеры с вашим кодом запускаете вы сами, сервис Temporal их не выполняет. А Workflow ID задаёте вы: возьмите id диалога, и повторная отправка не создаст дубль. Только не кладите в ID персональные данные, он виден в интерфейсе и логах.
Ретрай — это не circuit breaker
Когда провайдер модели лежит полчаса, Temporal честно ретраит activity. Сто воркеров и тысяча открытых обращений дружно долбят упавший API, а когда он поднимается, на него обрушивается вся очередь.
Ретрай рассчитан на сбой, который пройдёт за секунды. Для долгого отказа нужен circuit breaker. Он считает сбои, после порога размыкается и сразу возвращает ошибку, не ходя к сервису, а через время пропускает пробный вызов. Состояние выключателя должно быть общим для всех воркеров, иначе каждый будет заново узнавать, что провайдер лёг. Поэтому его место в LLM-шлюзе между воркерами и провайдерами. LiteLLM, например, выводит модель из ротации после серии сбоев, переключает на запасную и хранит состояние в Redis.
Практический совет автора: пусть activity различает два вида ошибок. Временную повторяет политика Temporal. Ошибку «выключатель разомкнут» не нужно ретраить: workflow сам решает, уйти ли на запасную модель, подождать или передать обращение оператору. Те же выключатели ставят перед службой доставки и платёжкой.
Сага, оператор и роутер
Возврат затрагивает три системы без общей транзакции: этикетка у службы доставки, деньги в платёжке, уведомление клиента и обновление заказа. Это сага: у каждого шага есть компенсация. Ключевое понятие — точка невозврата (pivot transaction). До возврата денег шаги можно отменить, после него письмо клиенту и обновление заказа нужно довести до конца, сколько бы ретраев ни понадобилось. Каждое действие получает ключ идемпотентности, например id решения, и платёжка не спишет деньги повторно.
Крупные суммы модель проводить не должна. Workflow показывает решение в консоли оператора и ждёт сигнал. Ожидание записано в журнал и не занимает воркер, так что оператор может ответить через час.
Последний шаг касается стоимости. Не каждое обращение требует полного агентного цикла. Роутер на маленькой модели отправляет FAQ в один вызов, вопрос «где заказ» в заранее прописанную цепочку, и только спорный возврат попадает к агенту. Принцип простой: брать самый низкий уровень сложности, который надёжно решает задачу.
Что с этим делать
Сразу строить всю схему не стоит. Автор прямо советует добавлять компонент, когда появилась боль, которую он лечит. Если у вас один клиент за раз и нет денег в игре, хватит и базы. Как только появляются побочные эффекты вроде платежей, нужны идемпотентность и durable execution. Как только появляется зависимость от внешнего провайдера, нужен общий выключатель.
Ограничения тоже есть. Код в статье — псевдокод. Утверждение, что на Temporal работает веб-агент Codex, исходит от самого вендора, и независимого подтверждения автор не нашёл. Но как карта типовых отказов агентной системы текст полезен любому, кто выпускает агента в прод. Начните с вопроса, что случится, если процесс упадёт после самого рискованного вызова.
- 12 нояб. 2026 г.Claude in Chrome требует разрешать каждый клик на сайтах с категорией 3
- 12 нояб. 2026 г.Norwest собрал рекордный фонд на $4 млрд и ставит на агентов, голос и физический ИИ
- 12 нояб. 2026 г.AWS показала, как 100% точности поиска скрывают провал ИИ-агента
- 12 нояб. 2026 г.Garage Eight убрала чужие апрувы в 69 MR внутреннего сервиса с AI-ревью
Вопросы и ответы по материалу
Что известно по теме: от одного файла к API, воркеру и Temporal?
Начальная версия держит историю диалога в памяти процесса. Перезапуск стирает все диалоги, клиентов обслуживается по одному, а если процесс упал между вызовом refund_payment и ответом, никто не знает, вернулись деньги или нет. Первый шаг: разделить скрипт на API и воркер. API принимает сообщение, пишет его в базу и сразу отвечает чату. Модель он не вызывает, поэтому отвечает за миллисекунды. Агентный цикл живёт в отдельных воркерах, которых можно добавлять под нагрузку. История теперь в базе и переживает рестарты.
Что известно по теме: ретрай — это не circuit breaker?
Когда провайдер модели лежит полчаса, Temporal честно ретраит activity. Сто воркеров и тысяча открытых обращений дружно долбят упавший API, а когда он поднимается, на него обрушивается вся очередь. Ретрай рассчитан на сбой, который пройдёт за секунды. Для долгого отказа нужен circuit breaker. Он считает сбои, после порога размыкается и сразу возвращает ошибку, не ходя к сервису, а через время пропускает пробный вызов.
Что известно по теме: сага, оператор и роутер?
Возврат затрагивает три системы без общей транзакции: этикетка у службы доставки, деньги в платёжке, уведомление клиента и обновление заказа. Это сага: у каждого шага есть компенсация. Ключевое понятие — точка невозврата (pivot transaction). До возврата денег шаги можно отменить, после него письмо клиенту и обновление заказа нужно довести до конца, сколько бы ретраев ни понадобилось. Каждое действие получает ключ идемпотентности, например id решения, и платёжка не спишет деньги повторно.
Внедряйте ИИ быстрее рынка вместе с инженерами и фаундерами
Внутри Vibe Club (hamidun.com/club): еженедельные живые мастермайнды и разборы архитектуры с Жемалом Хамидуном, закрытый Tech Radar, LMS и обновляемая база из 370+ боевых скиллов.
Главное из мира ИИ — раз в неделю
7 ключевых событий недели, отобранных вручную. Без шума, репостов и пресс-релизов.
Готово! Проверьте почту — мы отправили подтверждение.