Idempotency и Retries для AI-систем: как не дублировать side effects

Idempotency и retries в 2026: safe retries, backoff, duplicate suppression и commit boundaries для LLM-агентов, tools и webhook-driven workflows.

В AI-системах retry почти всегда выглядит безобидно, пока у вас нет side effect. Повторить classification, summary или extraction - обычно нормально. Повторить refund, create_ticket, send_email, write_to_crm или run_tool - уже риск двойного действия, inconsistent state и трудноуловимых инцидентов.

Поэтому в 2026 retries и idempotency полезно мыслить не как общую backend-гигиену, а как обязательный слой вокруг agent/tool workflows. Как только система:

  • вызывает инструменты;
  • ждёт вебхуки;
  • работает через queue;
  • может переживать timeout, partial failure или duplicate delivery,

вам нужна ясная граница между можно безопасно повторить и повтор может удвоить side effect.

Idempotency означает: если один и тот же запрос случайно пришёл дважды, система ведёт себя так, как будто он был один. Это особенно важно там, где один клик или один tool call меняет внешний мир.
Retry без idempotency в агентном контуре - это не "повышение надёжности", а часто просто более дорогой способ случайно сделать одно и то же действие два раза.

Короткая версия

Для AI-систем полезно различать:

  1. Safe retries - read-only steps, generation, retrieval, ranking
  2. Risky retries - любые действия с внешним side effect

Надёжный workflow обычно включает:

  • idempotency key;
  • retry policy с backoff и jitter;
  • commit boundary;
  • dedupe storage;
  • ясную модель статусов pending / committed / failed.
Без техники
Tool timeout произошёл после неизвестного состояния. Система просто нажимает `retry`, и клиент получает две одинаковые рассылки или двойной refund.
С техникой
Запрос имеет idempotency key, tool executor знает commit boundary, а повторный вызов либо возвращает тот же результат, либо безопасно игнорируется как duplicate.
ПромптRetry intuition
Почему retry полезен для retrieval, но опасен для payment-like action?
Ответ модели

Потому что retrieval обычно read-only и повтор не меняет внешний мир. Payment-like action может уже частично закоммититься, и слепой retry превращает timeout в duplicate side effect.

1. Где retries в AI-стеке действительно нужны

Retry полезен почти везде, где есть transient failure:

  • rate limit;
  • network timeout;
  • flaky downstream API;
  • временно недоступный tool;
  • queue redelivery;
  • webhook delivery retry.

Но этим же retry-path-ом нельзя одинаково пользоваться для всех шагов. Production-команда должна явно разделять:

Тип шагаRetry policy
Generation / classificationобычно safe retry
Retrieval / searchsafe retry с bounded attempts
Tool readsafe-ish retry, если executor read-only
Tool writeтолько с idempotency / commit awareness
Webhook consumerdedupe + idempotent handler

2. Commit boundary - ключевая идея

Самый важный вопрос не "что ответил API", а произошёл ли внешний commit.

Например:

  • email уже ушёл, но клиент не получил response;
  • refund создался, но webhook потерялся;
  • CRM-запись обновилась, а agent loop считает, что action failed;
  • tool успел сделать write, а timeout случился на response path.

Если система этого не понимает, любой retry становится лотереей.

Перед тем как добавлять retry к tool call, ответьте письменно на вопрос: "как мы узнаем, был ли side effect уже зафиксирован?" Если ответа нет, retry policy пока не готова.

3. Idempotency key нужен не только платежам

Команды часто связывают idempotency только с payment APIs. Для agent systems это слишком узко.

Idempotency key полезен в:

  • create ticket;
  • send message;
  • issue refund;
  • create order;
  • schedule job;
  • trigger workflow;
  • update record через API gateway.

Ключевая идея:

  • одинаковое намерение клиента или агента получает один stable key;
  • повторный запрос с тем же key не создаёт новый side effect;
  • система возвращает already-known result или статус.

4. Retries без backoff тоже ломают систему

Даже safe retry-path может быть вредным, если он:

  • мгновенно штурмует упавший dependency;
  • делает synchronized retry storms;
  • съедает token budget внутри agent loop;
  • маскирует реальную деградацию под шум повторных попыток.

Поэтому production retry обычно включает:

  • exponential backoff;
  • jitter;
  • upper bound на attempts;
  • circuit breaker или graceful fallback.

Это особенно важно для LLM-heavy workflows, где каждый лишний retry - это ещё и деньги, latency и downstream pressure.

5. Webhooks и duplicate delivery

AI-продукты всё чаще живут не только в request/response, но и в webhook-driven flows:

  • long-running jobs;
  • human approvals;
  • external SaaS callbacks;
  • background agent tasks.

Webhook delivery по своей природе может дублироваться. Поэтому обработчик должен быть idempotent even if upstream behaves correctly most of the time.

Минимальная защита:

  • event ID;
  • dedupe store;
  • processed status;
  • safe re-run semantics;
  • observability по duplicate events.

6. Где AI-системы особенно уязвимы

Agent retries after tool ambiguity

Модель не уверена, выполнился ли tool, и предлагает повторить действие.

Human-in-the-loop resume

После approval workflow случайно переигрывает уже committed step.

Queue redelivery

Worker падает после commit, но до acknowledge.

Side effect hidden behind "generation"

Команда думает, что это "просто LLM step", хотя за ним стоит tool executor с write semantics.

7. Что полезно хранить в status model

Чтобы retries были безопаснее, workflow обычно хранит:

  • intent_received
  • execution_started
  • commit_unknown
  • committed
  • failed_retryable
  • failed_terminal

Особенно важен commit_unknown. Это неприятное, но честное состояние. Оно лучше, чем симулировать определённость там, где её нет.

8. Что полезно мерить

Минимальный reliability dashboard:

  • retry count per step type;
  • duplicate suppression hits;
  • idempotency key reuse rate;
  • stuck commit_unknown count;
  • post-retry incident rate;
  • average attempts before success;
  • cost of retries in token and latency terms.

Плюсы

  • Idempotency позволяет переживать timeouts и duplicate delivery без двойных side effects
  • Bounded retries повышают устойчивость к transient failures
  • Commit-aware workflows упрощают agent reliability и debugging
  • Dedupe layer особенно полезен для webhooks, queues и human approvals

Минусы

  • Требует явной модели статусов и durable storage
  • Commit boundary не всегда легко наблюдать
  • Слепые retries быстро превращаются в cost amplifier
  • Без idempotency key agent loops легко дублируют внешние действия

Пример idempotent tool wrapper

def execute_write_tool(intent_id, tool_name, args):
    existing = load_by_idempotency_key(intent_id)
    if existing:
        return existing.result

    mark_started(intent_id, tool_name, args)
    result = call_external_tool(tool_name, args)
    store_committed(intent_id, result)
    return result

Пример bounded retry

def retryable_call(fn, attempts=3):
    delay = 0.5
    for i in range(attempts):
        try:
            return fn()
        except TransientError:
            if i == attempts - 1:
                raise
            sleep(with_jitter(delay))
            delay *= 2

Практический совет: логируйте original_intent_id, attempt_number и commit_state отдельно. Тогда при инциденте видно, у вас правда был duplicate side effect или просто noisy transport around one committed action.

Проверьте себя

1. Почему retry без idempotency особенно опасен для агентных tools?

2. Что означает commit boundary?

3. Какой статус особенно полезен в uncertain workflow?