База знаний

Идемпотентность для ИИ-агентов: почему промпт не заменяет архитектуру

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

Именно в этот момент качество самой модели перестаёт быть единственным важным параметром.

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

В результате агент может выполнить правильное действие два раза.

И это уже не проблема «галлюцинаций». Это обычная инженерная проблема надёжности, с которой распределённые системы сталкиваются много лет.

Просто теперь к ней добавился ИИ, способный самостоятельно инициировать действия.

Самый неприятный сценарий выглядит очень просто

Представим агентную автоматизацию.

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

Внешний сервис команду принял и выполнил.

После этого соединение оборвалось.

Подтверждение обратно не пришло.

С точки зрения внешней системы всё успешно.

С точки зрения агента результат неизвестен.

Что делать дальше?

Если просто считать операцию неудачной, можно потерять выполненную работу и оставить процесс в неправильном состоянии.

Если повторить запрос, существует риск, что внешняя система выполнит то же действие ещё раз.

Именно здесь появляется один из самых неприятных классов ошибок автоматизации: операция произошла, но система, которая её инициировала, этого точно не знает.

Такое состояние намного сложнее обычной ошибки.

Когда запрос явно завершился неудачно, его можно повторить.

Когда запрос явно завершился успешно, можно идти дальше.

А здесь система находится между двумя состояниями.

ИИ не изобрёл эту проблему

Важно не делать из этого очередную страшилку про нейросети.

Проблема повторного выполнения операций существовала задолго до современных языковых моделей.

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

Сеть ненадёжна по определению.

Ответ может потеряться.

Запрос может быть доставлен повторно.

Операция может выполняться дольше ожидаемого.

Процесс может аварийно завершиться между изменением данных и записью собственного состояния.

Разработчики давно придумали способы работать с такими ситуациями.

Но у ИИ-агентов появляется дополнительная особенность: модель сама решает, что делать дальше.

Если система устроена плохо, агент может интерпретировать неопределённое состояние как необходимость «попробовать ещё раз».

С точки зрения рассуждения это выглядит вполне логично.

С точки зрения бизнеса результат может оказаться очень неприятным.

Повтор может быть безобидным, а может стоить денег

Не каждую операцию опасно выполнять несколько раз.

Если агент дважды запросит список товаров, ничего страшного не произойдёт.

Если повторно прочитает карточку клиента — тоже.

Но есть действия, которые изменяют состояние системы.

  • Создать заказ.
  • Отправить письмо.
  • Выдать компенсацию.
  • Изменить складской остаток.
  • Создать документ.
  • Перевести сделку на другой этап.
  • Запустить развёртывание новой версии системы.

Для таких операций повтор уже может иметь последствия.

Иногда это просто некрасиво. Клиент получает два одинаковых письма.

Иногда создаёт ручную работу. Сотрудник обнаруживает две одинаковые записи и должен разобраться, какую удалить.

А иногда цена ошибки существенно выше: дублируется финансовое действие или внешний сервис дважды запускает дорогостоящую операцию.

Поэтому один из первых вопросов при проектировании агента должен звучать не «насколько хорошо он понимает задачу», а:

что произойдёт, если любое его действие выполнится дважды?

Здесь появляется идемпотентность

Термин звучит сложно, но идея довольно простая.

Идемпотентная операция устроена так, что её повторное выполнение не создаёт второй результат.

Допустим, система создаёт заказ и присваивает этой операции уникальный идентификатор: ORDER-2026-001234.

Первый запрос приходит во внешний сервис. Заказ создаётся.

Связь обрывается.

Через несколько секунд агент повторяет тот же запрос с тем же идентификатором.

Вместо создания второго заказа принимающая система отвечает: «Операция с таким идентификатором уже выполнена».

В результате технических попыток было две, а бизнесовый результат остался один.

Это и есть ключевая идея.

Надёжность обеспечивается не обещанием модели «не повторять действия», а свойством самой системы.

Почему нельзя решить всё промптом

Можно написать агенту очень строгую инструкцию: «Никогда не выполняй одну операцию дважды».

Но модель не обладает всей информацией о состоянии внешнего мира.

Если ответ потерялся, она действительно может не знать, произошло действие или нет.

Можно добавить ещё одну инструкцию: «Перед повтором обязательно проверь результат».

Это уже лучше.

Но и проверка не всегда даёт однозначный ответ.

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

Сервис отвечает: «Нет».

Через секунду первая операция завершается, и заказ появляется.

А агент тем временем уже отправил повторный запрос.

В итоге появляются два заказа.

То есть проблема не только в том, насколько аккуратно рассуждает модель. Есть временные состояния, которые нельзя надёжно определить одним дополнительным вопросом.

Поэтому некоторые гарантии должны обеспечиваться системой, которая принимает действие.

«Проверить перед повтором» всё равно полезно

Из предыдущего раздела не следует, что проверка состояния бесполезна.

Наоборот, она сильно снижает количество ошибок.

Если операция имеет естественный уникальный признак, система может перед повтором посмотреть, появился ли ожидаемый результат.

Например, есть ли уже объект с данным идентификатором.

Был ли отправлен документ.

Создана ли задача.

Изменён ли статус.

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

Особенно если внешняя система обновляется не мгновенно или имеет собственные очереди.

Повторные попытки тоже нельзя просто отключить

После всего сказанного может возникнуть простой вывод: чтобы избежать дублей, запретим автоматические повторы.

Это тоже плохое решение.

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

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

Надёжная автоматизация должна уметь повторять действия.

Вопрос не в том, нужны ли повторные попытки.

Вопрос в том, можно ли повторить конкретное действие безопасно.

Для чтения данных — обычно да.

Для изменения состояния — только если вокруг операции есть дополнительные механизмы контроля.

Нужно разделять попытку и бизнес-операцию

Это ещё одно полезное различие.

Представим, что агент три раза пытался выполнить одно действие.

С технической точки зрения это три запроса.

С точки зрения бизнеса должна существовать одна операция.

Например: operation_id = 847291.

Попытка №1 — тайм-аут.

Попытка №2 — соединение разорвано.

Попытка №3 — успешный ответ.

Все три попытки принадлежат одной и той же бизнес-операции.

Если система хранит только отдельные вызовы, разобраться после сбоя намного сложнее.

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

Это полезно не только для защиты от дублей, но и для аудита.

Журнал агента не доказывает, что всё прошло правильно

ИИ-агент может написать: «Заказ успешно создан».

И даже действительно получить успешный ответ.

Но окончательная проверка должна смотреть на состояние целевой системы.

Сколько заказов было создано?

Один или два?

Какой у них идентификатор?

Совпадают ли данные?

Есть ли незавершённые операции?

Это особенно важно потому, что агент оценивает процесс с собственной точки зрения.

Он знает, какие инструменты вызвал и какие ответы получил.

Но именно внешний сервис хранит фактический бизнес-результат.

Поэтому для критичных действий система мониторинга должна смотреть не только на сообщения агента, но и на реальное состояние интегрированных систем.

Ещё одна проблема — два агента одновременно

Повторная операция возникает не только после сбоя.

Представим очередь задач.

Два экземпляра агента одновременно видят одну и ту же необработанную запись.

Оба решают, что должны её выполнить.

Оба начинают работу.

И оба успешно завершают действие.

Если задача не была заранее «захвачена» одним исполнителем или если целевая операция не защищена от дублей, результат снова будет повторным.

Такие ошибки особенно неприятны тем, что в обычном тестировании встречаются редко.

Один разработчик запускает одного агента на одной задаче — всё работает идеально.

После масштабирования появляется несколько параллельных работников, и поведение меняется.

Поэтому тестирование конкурирующих исполнителей должно быть отдельной частью приёмки.

После падения задача не должна навсегда оставаться «в работе»

Есть и противоположная проблема.

Агент начал выполнять задачу и пометил её состоянием running.

Затем программа аварийно завершилась.

После перезапуска новая копия агента видит: status = running.

И решает, что задачей уже кто-то занимается.

Но старого процесса больше не существует.

В результате задача зависает навсегда.

Поэтому у промежуточных состояний должны быть сроки жизни и правила восстановления.

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

Можно ли безопасно продолжить?

Нужно ли проверить внешний результат?

Нужно ли передать ситуацию человеку?

Просто хранить статусы new / running / done недостаточно.

Неопределённый результат — тоже отдельный статус

Многие системы пытаются свести всё к двум состояниям: успех или ошибка.

Для надёжной автоматизации этого мало.

Есть важное третье состояние: результат неизвестен.

Команда отправлена.

Подтверждение не получено.

Проверка не даёт уверенного ответа.

В такой ситуации опасно автоматически считать операцию неудачной и начинать заново.

Гораздо лучше явно сохранить состояние unknown или requires_verification.

После этого система может запустить отдельную процедуру проверки или отправить задачу человеку.

Это выглядит менее элегантно, чем полностью автономный агент.

Но именно такая «неэлегантность» часто защищает бизнес от реальных проблем.

Человек нужен не после каждой операции

Здесь легко уйти в другую крайность.

Если любую сетевую ошибку отправлять сотруднику, автоматизация потеряет смысл.

Задача не в том, чтобы человек подтверждал каждое действие.

Большинство ситуаций можно обрабатывать автоматически.

  • У операции есть ключ идемпотентности — повтор безопасен.
  • Состояние однозначно проверяется — система восстанавливается сама.
  • Ошибка временная и действие не меняет данные — можно повторить.

Человек нужен именно для небольшого класса случаев, где невозможно однозначно определить фактический результат.

Это хороший пример нормального human-in-the-loop: сотрудник занимается не рутиной, а неопределённостью.

Такие системы нужно специально ломать на тестировании

Обычная приёмка автоматизации часто выглядит слишком оптимистично.

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

Агент успешно выполнил все десять.

Значит, всё работает.

Но реальная надёжность проявляется не в идеальном сценарии.

Полезнее специально воспроизводить сбои.

  • Оборвать соединение сразу после отправки команды.
  • Повторно доставить одно и то же событие.
  • Запустить два экземпляра агента на одной задаче.
  • Завершить процесс посередине операции.
  • Задержать ответ внешнего сервиса.
  • Вернуть тайм-аут после фактического выполнения.
  • Перезапустить всю систему во время работы.

После каждого такого теста нужно смотреть не на лог агента, а на конечное состояние бизнеса.

  • Создан один объект или два?
  • Не потерялась ли задача?
  • Не осталась ли она навечно в промежуточном состоянии?
  • Можно ли восстановить историю событий?

Вот это уже нормальный тест production-автоматизации.

Самая сильная модель не заменяет архитектуру

У современных моделей быстро растёт способность планировать действия, пользоваться инструментами и самостоятельно исправлять ошибки.

Это полезно.

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

  • Нельзя поручать модели обеспечивать уникальность финансовой операции.
  • Нельзя полагаться на её память для предотвращения дублей.
  • Нельзя считать текстовое обещание «не повторять запрос» механизмом блокировки.
  • Нельзя доверять самой модели решать, действительно ли внешняя система применила изменение ровно один раз.

Всё это функции обычной программной инфраструктуры.

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

А строгие гарантии лучше оставлять строгому коду.

Что спросить у подрядчика перед запуском агента

Если агент получает право что-то менять во внешних системах, полезно задать несколько вполне конкретных вопросов.

  • Что произойдёт, если ответ от сервиса потеряется после успешной операции?
  • Как система отличает новую операцию от повторной попытки?
  • Есть ли постоянный идентификатор бизнес-операции?
  • Поддерживает ли целевой сервис защиту от повторных запросов?
  • Что произойдёт, если одно событие поступит дважды?
  • Могут ли два агента одновременно начать одну задачу?
  • Как восстанавливаются незавершённые операции после перезапуска?
  • Есть ли отдельное состояние для неопределённого результата?
  • Можно ли по журналу восстановить весь путь одной операции?
  • Что отправляется человеку, если система не может определить результат автоматически?

Если на эти вопросы нет ответа, автономность агента, скорее всего, пока выше зрелости самой автоматизации.

Это особенно важно именно сейчас

Пока ИИ использовался как чат, цена таких ошибок была невысокой.

Модель могла написать неудачный текст, предложить неправильную идею или неверно ответить на вопрос.

Человек видел результат перед действием.

С агентами ситуация меняется.

Теперь языковая модель становится частью системы, которая влияет на внешний мир.

И здесь качество ответа — только один слой надёжности.

Второй слой — правильность выполнения.

Третий — восстановление после сбоев.

Четвёртый — контроль фактического результата.

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

Только вместе они дают автоматизацию, которой можно доверить реальные рабочие процессы.

С чего начинать

Если компания хочет дать ИИ-агенту право выполнять действия, я бы не начинал с максимальной автономности.

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

Для каждой спросить: что будет, если она выполнится дважды?

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

Затем провести пилот.

Но не только на нормальных задачах.

Специально оборвать соединение.

Повторить событие.

Перезапустить агента.

Запустить двух исполнителей одновременно.

И посмотреть, что действительно произошло в конечной системе.

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

Надёжный агент — тот, который не превращает обычный технический сбой в двойное бизнес-действие.