Перейти к основному содержимому

0061. Персистентная очередь подтверждений планов ИИ-агента

Копия ADR из репозитория: backend/docs/adr/0061-ai-agent-persistent-approvals.md. Канонический текст — в репозитории; портал обновляется командой npm run sync:adr.

  • Статус: accepted
  • Дата: 2026-09-28
  • Связанные задачи: OP-126/127 (5.10); опирается на ADR-0054 (управление кампанией), OP-45 (журнал аудита)

Контекст и проблема​

Подтверждение действий агента было stateless: POST /ai-agent/tasks/plan возвращал план, фронт держал его в памяти и сам отправлял действия в POST /ai-agent/actions/execute. После перезагрузки страницы очередь пропадала, отклонение нигде не фиксировалось, а история (GET /ai-agent/actions/history) не различала кабинеты.

Рассмотренные варианты​

  1. Хранить очередь только в аудите — нет состояния «ожидает решения», атомарно решить один раз нельзя.
  2. Отдельная таблица ai_agent_approvals — статус pending → approved | rejected, условный UPDATE гарантирует однократное решение.

Решение​

Вариант 2. План с действиями сохраняется при планировании (в ответ добавлен approvalId); действия хранятся с уже подставленными advertId/nmId из контекста задачи. approve атомарно переводит статус и выполняет действия через AgentActionExecutorService (guardrails и аудит — там), итог сохраняется в execution_result. reject пишет аудит ai-agent.action.rejected (metadata.source = 'user'). Повторное решение — 409. Доступ — по кабинету (WbAccountsService.getById), чужое → 404.

Все аудит-записи агента теперь несут metadata.wbAccountId (и approvalId, если выполнение из подтверждения) — по нему история фильтруется по кабинету.

pause_campaign выключает автобиддер кампании (autoEnabled=false, ADR-0054), а не ставит кампанию на паузу в WB: агент прекращает автоторговлю, откат включает её обратно.

Последствия​

  • Положительные: очередь переживает перезагрузку, решение однократно, история скоупится по кабинету.
  • Отрицательные / компромиссы: записи аудита до этого изменения не имеют metadata.wbAccountId и под фильтр по кабинету не попадают. POST /ai-agent/actions/execute оставлен для обратной совместимости.
  • Дальше: истечение (expired) старых pending — при необходимости.