Реализовать human‑in‑the‑loop для критических действий

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Реализовать human‑in‑the‑loop для критических действий

1. Цель задачи

Разработать и внедрить механизм human‑in‑the‑loop (HITL) для критических операций — отправка email‑сообщений и удаление записей/аккаунтов. Система должна требовать явного подтверждения от пользователя перед выполнением действия и предоставлять возможность отмены в течение заданного интервала времени. Цель — свести к нулю количество случайных вредоносных действий (accidental harmful actions) за счёт обязательного двухэтапного подтверждения.

Ключевой результат Реализованный HITL‑конвейер, при котором ни одно критическое действие не выполняется без явного подтверждения, а количество отменённых действий (undo) фиксируется в метриках.


2. Исходные данные

Что нужноОткуда взять
Веб‑приложение с функционалом отправки email и удаления записейСуществующий проект (Pet‑проект, стажёрский проект) или новое минимальное приложение (FastAPI + React)
Бэкенд‑эндпоинты для отправки и удаленияИсходный код проекта, Swagger/OpenAPI спецификация
Фронтенд‑компоненты для отображения диалогов подтвержденияUI‑библиотека (React, Vue) или чистый HTML/CSS/JS
Возможность логирования и метрикPrometheus client + Grafana (опционально)
Система очередей (желательно)Redis + Celery или in‑memory очередь для отложенных действий

Если нет реального инструмента — симулируем:

  1. Создать минимальное FastAPI‑приложение с двумя эндпоинтами: POST /send-email и DELETE /items/{id}.
  2. На фронтенде (React) добавить кнопки “Отправить письмо” и “Удалить”, которые сначала не требуют подтверждения — для baseline.
  3. Затем поэтапно внедрить HITL: диалоговое окно с кнопками “Подтвердить” / “Отмена” и таймером обратного отсчёта (например, 10 секунд).
  4. В качестве системы очередей использовать in‑memory asyncio.Queue или Redis (если доступен).

3. Технологический стек

КомпонентИнструментыНазначение
БэкендPython 3.11+, FastAPI / Django RESTРеализация эндпоинтов и логики HITL
ФронтендReact 18+ (или Vue 3) + TypeScriptUI‑диалоги подтверждения
Очередь / задержкаCelery + Redis / APScheduler / asyncioОтложенное выполнение после таймера
МониторингPrometheus + GrafanaМетрики: количество подтверждений/отмен, время отклика
Логированиеstructlog / loguruФиксация каждого HITL‑события
CI/CDGitHub Actions (опционально)Автоматические тесты HITL‑логики

4. Этапы выполнения

Этап 1: Проектирование HITL‑интерфейса и требований (30 мин)

Действия

  1. Составить список критических действий: отправка email, удаление записи, удаление аккаунта (если есть).
  2. Определить UX‑паттерн:
    • Модальное окно с заголовком-предупреждением (“Вы уверены? Это действие нельзя отменить.”), кнопками “Подтвердить” (активная) и “Отмена”.
    • Таймер (5–15 секунд), по истечении которого действие автоматически отменяется (если не нажато “Подтвердить”).
    • Для email: возможность “Отправить с задержкой в 30 секунд” (undo‑период).
  3. Набросать wireframe (можно текстово или в Figma).

Ожидаемый результат этапа Документ с требованиями к UI/UX и логике поведения (таблица действий, типы подтверждений).


Этап 2: Реализация бэкенд‑логики с поддержкой отложенного выполнения (2–3 часа)

Действия

  1. Создать модель PendingAction в БД (или хранить в Redis):
    # Redis schema: pending:{action_id} = {user_id, action_type, payload, expires_at}
    import json, redis
    r = redis.Redis()
    action_id = str(uuid.uuid4())
    r.setex(f"pending:{action_id}", 30, json.dumps({"user": user_id, "type": "send_email", "payload": {...}}))
    
  2. Реализовать эндпоинт POST /actions/initiate, который:
    • Принимает тип действия и данные.
    • Создаёт запись в Redis/PendingAction с TTL (equal to undo‑window).
    • Возвращает action_id и expires_at.
  3. Реализовать эндпоинт POST /actions/confirm, который:
    • Принимает action_id.
    • Находит запись, проверяет не истекла ли.
    • Выполняет реальное действие (отправка email / удаление).
    • Удаляет запись из хранилища.
  4. Реализовать эндпоинт POST /actions/cancel, который просто удаляет запись (ничего не делает).
  5. Создать фоновый воркер (Celery или asyncio‑задача), который будет выполнять действие, если подтверждения не было, но TTL истёк (автоотмена). Для этого можно использовать подписку на ключи Redis с истечением (Redis Keyspace Notifications).

Ожидаемый результат этапа Бэкенд‑API с тремя эндпоинтами (+ health check) и фоновый воркер (или TTL‑логика). Запись в лог каждого события.


Этап 3: Фронтенд — диалог подтверждения (2–3 часа)

Действия

  1. Создать компонент ConfirmDialog с пропсами:
    • title, message, onConfirm, onCancel, timeout (сек).
    • Отображение обратного таймера (progress bar или цифры).
    • По истечении таймера — автоматически вызывать onCancel.
  2. В компоненте, где есть кнопка “Отправить” или “Удалить”, изменить обработчик:
    • Показать ConfirmDialog вместо прямого вызова API.
    • При подтверждении — вызвать POST /actions/confirm с полученным action_id.
    • При отмене — POST /actions/cancel.
    • При таймауте (автоотмена) — ничего не делать (бэкенд сам отменит).
  3. Добавить loader-состояние и обработку ошибок (например, таймаут сети).

Ожидаемый результат этапа UI, в котором все критические действия требуют двухэтапного подтверждения. Клиентский код логирует подтверждения/отмены.


Этап 4: Интеграция, логирование и метрики (1 час)

Действия

  1. Настроить Prometheus метрики в бэкенде:
    from prometheus_client import Counter, Histogram
    confirm_counter = Counter('hitl_confirm_total', 'Total confirms', ['action'])
    cancel_counter = Counter('hitl_cancel_total', 'Total cancels', ['action'])
    timeout_counter = Counter('hitl_timeout_total', 'Total timeouts', ['action'])
    confirm_latency = Histogram('hitl_confirm_latency_seconds', 'Confirm latency')
    
    Инкрементировать соответствующие счётчики в эндпоинтах.
  2. Добавить структурированное логирование через structlog:
    import structlog
    logger = structlog.get_logger()
    logger.info("hitl.action.created", action_id=..., type="send_email")
    logger.info("hitl.action.confirmed", action_id=..., type="send_email")
    logger.info("hitl.action.cancelled", action_id=..., type="send_email")
    
  3. Создать простой Grafana dashboard (или хотя бы Prometheus query) для отображения:
    • Количество подтверждений vs отмен vs таймаутов за последний час.
    • Распределение по типам действий.

Ожидаемый результат этапа Метрики и логи доступны, дашборд (или скриншот) показывает нулевое количество accidental отправок (все прошли через HITL).


Этап 5: Тестирование и регрессия (1 час)

Действия

  1. Написать юнит‑тесты для бэкенд‑логики:
    • test_initiate_action() — проверяет создание записи в Redis.
    • test_confirm_before_expiry() — действие выполняется.
    • test_confirm_after_expiry() — возвращает 410 Gone.
    • test_cancel() — запись удаляется, действие не выполняется.
    • test_auto_cancel_on_ttl() — без подтверждения запись исчезает.
  2. Написать интеграционный тест (с использованием TestClient FastAPI + fakeredis):
    • Полный цикл: инициировать → подтвердить → проверить, что email отправлен.
    • Цикл: инициировать → таймаут → проверить, что email не отправлен.
  3. Провести ручное тестирование фронтенда: проверить, что диалог появляется, таймер работает, кнопка “Отмена” работает даже на последней секунде.

Ожидаемый результат этапа Все тесты проходят, покрытие > 80% по HITL‑коду.


5. Критерии приемки (Definition of Done)

  • Ни одно критическое действие (отправка email, удаление записи) не выполняется без явного подтверждения через UI.
  • Пользователь может отменить действие в течение заданного undo‑окна (5–30 секунд).
  • После истечения undo‑окна без подтверждения действие автоматически отменяется (не выполняется).
  • Все HITL‑события (создание, подтверждение, отмена, таймаут) логируются в структурированном формате.
  • Метрики Prometheus для подтверждений/отмен/таймаутов экспортируются и доступны в Grafana.
  • Юнит‑тесты покрывают 100% ключевых сценариев (создание, подтверждение, отмена, истечение).
  • Нет ложных срабатываний: если действие не инициировано через HITL, оно не выполняется.
  • Все тесты проходят в CI (GitHub Actions).

6. Ожидаемый результат

  • Основной артефакт Работающее веб‑приложение с HITL для двух критических действий.
  • Дополнительные артефакты
    • Исходный код с бэкенд‑логикой (FastAPI/Django), фронтенд‑компонентом и тестами.
    • Файл prometheus.yml или кусок конфигурации для сбора метрик.
    • README с описанием архитектуры HITL, последовательности вызовов.
    • Скриншоты UI‑диалогов и Grafana dashboard (опционально).

7. Возможные сложности и их решение

СложностьРешение
Гонка состояний: пользователь нажал “Подтвердить”, а таймер почти истёкИспользовать атомарные операции Redis (WATCH/MULTI/EXEC) или пессимистичную блокировку на уровне базы. Приоритет: подтверждение отменяет таймер.
Перезагрузка страницы во время undo‑окнаХранить состояние HITL на бэкенде (Redis). После перезагрузки показать уведомление “У вас есть ожидающее действие” (опционально advanced).
Отсутствие Redis в окруженииИспользовать in‑memory словарь с threading.Timer (для простоты) или SQLite с TTL‑колонкой и фоновым cleanup.
UX: пользователь случайно закрывает модалку (escape/клик вне)Интерпретировать как отмену. Или сделать модалку обязательной (no close on outside click).
Масштабирование: много одновременных HITL‑сессийRedis легко выдерживает тысячи ключей с TTL. Для high‑load можно шардировать по user_id.

8. Бюджет времени (оценка)

ЭтапВремя
Проектирование HITL‑интерфейса0.5 ч
Реализация бэкенд‑логики2.5 ч
Фронтенд — диалог подтверждения2.5 ч
Интеграция, логирование, метрики1 ч
Тестирование и регрессия1 ч
Итого7.5 ч

Примечание: Если проект уже существует и нужно лишь добавить HITL, время может сократиться до 4–5 часов. Для первого раза (с нуля) закладывайте до 10 часов для отладки.


9. Связанные вопросы из базы знаний

ВопросТема
82HITL, подтверждение действий, undo‑механизмы
17UX: проектирование модальных окон
43Обработка конкурентных запросов (race condition)
91Метрики качества UX (SUPR‑Q, отмены/подтверждения)
145Использование Redis для временных данных
223Тестирование асинхронных сценариев (тайм‑ауты)
311Prometheus: кастомные счётчики и гистограммы
470Security: предотвращение случайных деструктивных действий
512Frontend: диалоговые компоненты и управление состоянием
678A/B‑тестирование времени undo‑окна

10. Чек-лист самопроверки

  • Я развернул(а) приложение локально или на тестовом сервере.
  • Я проверил(а), что при нажатии кнопки “Отправить” появляется диалог, а не сразу выполняется действие.
  • Я подтвердил действие — письмо ушло/запись удалилась — и метрика confirm увеличилась.
  • Я нажал “Отмена” и отменил действие — письмо не ушло, запись осталась — метрика cancel увеличилась.
  • Я дождался истечения таймера без нажатия кнопок — действие отменилось автоматически, метрика timeout увеличилась.
  • Я проверил, что логи содержат записи для всех трёх сценариев.
  • Я запустил тесты (pytest) — все зелёные.
  • Я открыл Grafana и увидел дашборд с ненулевыми значениями хотя бы одной метрики.