Почему «повторить шаг» опаснее, чем кажется

AI‑агент редко ограничивается ответом в чате. В рабочем процессе он читает заявку, ищет данные, формирует решение, меняет карточку в CRM, создаёт счёт в ERP, отправляет письмо или ставит задачу сотруднику. Как только появляется внешний эффект, обычный повтор после ошибки перестаёт быть безобидным.

Типичный сбой выглядит так: агент успешно создал заказ, но не успел сохранить ответ ERP из‑за обрыва сети. Для оркестратора шаг остался незавершённым, поэтому он запускает его снова. Если принимающая система не распознаёт повтор, бизнес получает два заказа, две задачи или два письма.

Контрольные точки помогают восстановить ход процесса, однако сами по себе не гарантируют однократность внешнего эффекта. Документация LangGraph прямо предупреждает: задача может быть выполнена повторно после сбоя, поэтому API‑вызовы следует выносить в отдельные задачи и проектировать идемпотентными. AWS описывает ту же проблему для распределённых API: клиент не всегда знает, завершился ли запрос до тайм‑аута, и безопасный повтор требует стабильного идентификатора намерения.

Что именно нужно сохранять

Надёжный агент — это не длинный промпт с функцией повторного запуска. Это конечный автомат, у которого каждое состояние можно однозначно восстановить. Для одного экземпляра процесса стоит хранить как минимум:

  • `run_id` — идентификатор всего запуска;
  • `step_id` и версию схемы процесса;
  • исходные бизнес‑идентификаторы: заявка, клиент, заказ, документ;
  • проверенные входы текущего шага и хеш существенных параметров;
  • результат модели отдельно от решения о выполнении действия;
  • статус согласования, автора и время решения;
  • стабильный `idempotency_key` для каждого внешнего эффекта;
  • состояние команды: `planned`, `approved`, `dispatched`, `confirmed`, `failed` или `needs_reconciliation`;
  • ссылку на подтверждение внешней системы: ID заказа, задачи, письма или проводки;
  • число попыток, класс ошибки и ближайшее допустимое время повтора.

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

Разделите рассуждение и исполнение

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

Такое разделение даёт несколько преимуществ:

  • модель не получает прямой универсальный доступ на запись;
  • предложенное действие можно проверить по формальной схеме;
  • один и тот же план допускает повторную валидацию без повторного исполнения;
  • опасные операции можно поставить на ручное согласование;
  • факт выполнения подтверждается ответом целевой системы, а не фразой модели «готово».

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

Идемпотентность: один ключ на одно бизнес‑намерение

Идемпотентная команда при повторной отправке с тем же ключом возвращает прежний результат и не создаёт новый объект. Ключ должен описывать бизнес‑намерение, а не сетевую попытку. Например, для создания счёта он может вычисляться из идентификатора заявки, типа операции и версии утверждённого плана.

Правила просты, но важны:

  • ключ создаётся до первого внешнего вызова и сохраняется в контрольной точке;
  • при повторах используется тот же ключ, а не новый UUID;
  • вместе с ключом сохраняется хеш значимых параметров;
  • тот же ключ с другим набором параметров отклоняется как конфликт;
  • срок хранения ключей покрывает максимальное окно повторов и ручного возобновления;
  • ответ первого успешного выполнения сохраняется и возвращается повторному запросу.

Если внешняя система поддерживает idempotency key, используйте её механизм. Если нет, добавьте адаптер: таблицу команд с уникальным индексом по ключу, поиск существующего объекта по бизнес‑идентификатору или операцию `upsert` вместо безусловного `create`. Для необратимых действий — платежей, массовых рассылок, юридически значимых документов — одного локального флага недостаточно: нужна сверка с системой‑получателем перед повтором.

Где нужна transactional outbox

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

Паттерн transactional outbox решает эту проблему так:

1. Бизнес‑изменение и запись о будущей команде или событии фиксируются одной транзакцией в одной базе.
2. Отдельный worker читает необработанные записи outbox и отправляет их получателю.
3. Получатель дедуплицирует сообщения по стабильному идентификатору.
4. Worker отмечает подтверждённую доставку; зависшие записи попадают на сверку.

Microsoft подчёркивает, что доставка через такую схему обычно остаётся «как минимум один раз». Значит, outbox защищает от потери события, но не отменяет идемпотентного потребителя. Для агента это полезно при создании задач, уведомлений, заявок на закупку и других действий, где между локальным решением и внешней системой есть очередь или интеграционный сервис.

Контрольная точка должна стоять на правильной стороне действия

Удобно разбить каждый опасный шаг на четыре части:

1. `prepare` — нормализовать параметры и вычислить ключ;
2. `approve` — проверить политику и при необходимости получить решение человека;
3. `execute` — отправить одну идемпотентную команду;
4. `confirm` — сохранить ID и фактический статус внешнего объекта.

Контрольная точка перед `execute` должна уже содержать утверждённые параметры и ключ. После ответа нужно сохранить подтверждение до перехода к следующему шагу. Если процесс упал между отправкой и подтверждением, состояние переводится не в обычный `failed`, а в `needs_reconciliation`: сначала спрашиваем целевую систему, существует ли объект с этим ключом или бизнес‑идентификатором, и только потом решаем, повторять ли запрос.

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

Повторы должны иметь бюджет

Автоматический retry полезен для коротких сетевых сбоев, временной недоступности и ограничения частоты. Он вреден при ошибке в данных, запрете доступа, конфликте бизнес‑правил или неопределённом результате внешней операции.

Для каждого инструмента задайте:

  • какие коды считаются временными;
  • максимальное число попыток;
  • экспоненциальную задержку со случайным разбросом;
  • общий дедлайн шага;
  • предел одновременных повторов;
  • правило перехода на ручную сверку;
  • запрещённые для автоматического повтора операции.

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

Как проверить архитектуру до запуска

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

  • оборвите соединение сразу после отправки команды;
  • перезапустите worker между записью outbox и доставкой;
  • дважды отправьте один и тот же ключ;
  • отправьте тот же ключ с изменёнными параметрами;
  • остановите процесс на согласовании и возобновите через сутки;
  • искусственно задержите старый ответ до прихода новой попытки;
  • проверьте восстановление после обновления версии процесса.

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

Экономика для малого и среднего бизнеса

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

Минимальный контур включает рабочую базу, уникальный индекс идемпотентности, очередь или outbox‑таблицу, worker, экран ручной сверки и метрики. Отдельный кластер оркестратора оправдан, когда процессы живут часами или днями, требуют нескольких согласований, часто переживают сбои и уже не помещаются в простую очередь заданий.

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

Следующий шаг

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

Надёжность AI‑агента начинается не с обещания «он продолжит после сбоя», а с точного ответа на вопрос: что уже произошло в бизнес‑системе и как это доказать без повторного действия.