中文翻译暂不可用,显示俄语原文。
Реализовать 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 очередь для отложенных действий |
Если нет реального инструмента — симулируем:
- Создать минимальное FastAPI‑приложение с двумя эндпоинтами:
POST /send-emailиDELETE /items/{id}. - На фронтенде (React) добавить кнопки “Отправить письмо” и “Удалить”, которые сначала не требуют подтверждения — для baseline.
- Затем поэтапно внедрить HITL: диалоговое окно с кнопками “Подтвердить” / “Отмена” и таймером обратного отсчёта (например, 10 секунд).
- В качестве системы очередей использовать in‑memory asyncio.Queue или Redis (если доступен).
3. Технологический стек
| Компонент | Инструменты | Назначение |
|---|---|---|
| Бэкенд | Python 3.11+, FastAPI / Django REST | Реализация эндпоинтов и логики HITL |
| Фронтенд | React 18+ (или Vue 3) + TypeScript | UI‑диалоги подтверждения |
| Очередь / задержка | Celery + Redis / APScheduler / asyncio | Отложенное выполнение после таймера |
| Мониторинг | Prometheus + Grafana | Метрики: количество подтверждений/отмен, время отклика |
| Логирование | structlog / loguru | Фиксация каждого HITL‑события |
| CI/CD | GitHub Actions (опционально) | Автоматические тесты HITL‑логики |
4. Этапы выполнения
Этап 1: Проектирование HITL‑интерфейса и требований (30 мин)
Действия
- Составить список критических действий: отправка email, удаление записи, удаление аккаунта (если есть).
- Определить UX‑паттерн:
- Модальное окно с заголовком-предупреждением (“Вы уверены? Это действие нельзя отменить.”), кнопками “Подтвердить” (активная) и “Отмена”.
- Таймер (5–15 секунд), по истечении которого действие автоматически отменяется (если не нажато “Подтвердить”).
- Для email: возможность “Отправить с задержкой в 30 секунд” (undo‑период).
- Набросать wireframe (можно текстово или в Figma).
Ожидаемый результат этапа Документ с требованиями к UI/UX и логике поведения (таблица действий, типы подтверждений).
Этап 2: Реализация бэкенд‑логики с поддержкой отложенного выполнения (2–3 часа)
Действия
- Создать модель 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": {...}})) - Реализовать эндпоинт
POST /actions/initiate, который:- Принимает тип действия и данные.
- Создаёт запись в Redis/PendingAction с TTL (equal to undo‑window).
- Возвращает
action_idиexpires_at.
- Реализовать эндпоинт
POST /actions/confirm, который:- Принимает
action_id. - Находит запись, проверяет не истекла ли.
- Выполняет реальное действие (отправка email / удаление).
- Удаляет запись из хранилища.
- Принимает
- Реализовать эндпоинт
POST /actions/cancel, который просто удаляет запись (ничего не делает). - Создать фоновый воркер (Celery или asyncio‑задача), который будет выполнять действие, если подтверждения не было, но TTL истёк (автоотмена). Для этого можно использовать подписку на ключи Redis с истечением (Redis Keyspace Notifications).
Ожидаемый результат этапа Бэкенд‑API с тремя эндпоинтами (+ health check) и фоновый воркер (или TTL‑логика). Запись в лог каждого события.
Этап 3: Фронтенд — диалог подтверждения (2–3 часа)
Действия
- Создать компонент ConfirmDialog с пропсами:
- title,
message,onConfirm,onCancel, timeout (сек). - Отображение обратного таймера (progress bar или цифры).
- По истечении таймера — автоматически вызывать
onCancel.
- title,
- В компоненте, где есть кнопка “Отправить” или “Удалить”, изменить обработчик:
- Показать ConfirmDialog вместо прямого вызова API.
- При подтверждении — вызвать
POST /actions/confirmс полученным action_id. - При отмене —
POST /actions/cancel. - При таймауте (автоотмена) — ничего не делать (бэкенд сам отменит).
- Добавить loader-состояние и обработку ошибок (например, таймаут сети).
Ожидаемый результат этапа UI, в котором все критические действия требуют двухэтапного подтверждения. Клиентский код логирует подтверждения/отмены.
Этап 4: Интеграция, логирование и метрики (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') - Добавить структурированное логирование через 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") - Создать простой Grafana dashboard (или хотя бы Prometheus query) для отображения:
- Количество подтверждений vs отмен vs таймаутов за последний час.
- Распределение по типам действий.
Ожидаемый результат этапа Метрики и логи доступны, дашборд (или скриншот) показывает нулевое количество accidental отправок (все прошли через HITL).
Этап 5: Тестирование и регрессия (1 час)
Действия
- Написать юнит‑тесты для бэкенд‑логики:
test_initiate_action()— проверяет создание записи в Redis.test_confirm_before_expiry()— действие выполняется.test_confirm_after_expiry()— возвращает 410 Gone.test_cancel()— запись удаляется, действие не выполняется.test_auto_cancel_on_ttl()— без подтверждения запись исчезает.
- Написать интеграционный тест (с использованием TestClient FastAPI + fakeredis):
- Полный цикл: инициировать → подтвердить → проверить, что email отправлен.
- Цикл: инициировать → таймаут → проверить, что email не отправлен.
- Провести ручное тестирование фронтенда: проверить, что диалог появляется, таймер работает, кнопка “Отмена” работает даже на последней секунде.
Ожидаемый результат этапа Все тесты проходят, покрытие > 80% по HITL‑коду.
5. Критерии приемки (Definition of Done)
- Ни одно критическое действие (отправка email, удаление записи) не выполняется без явного подтверждения через UI.
- Пользователь может отменить действие в течение заданного undo‑окна (5–30 секунд).
- После истечения undo‑окна без подтверждения действие автоматически отменяется (не выполняется).
- Все HITL‑события (создание, подтверждение, отмена, таймаут) логируются в структурированном формате.
- Метрики Prometheus для подтверждений/отмен/таймаутов экспортируются и доступны в Grafana.
- Юнит‑тесты покрывают 100% ключевых сценариев (создание, подтверждение, отмена, истечение).
- Нет ложных срабатываний: если действие не инициировано через HITL, оно не выполняется.
- Все тесты проходят в CI (GitHub Actions).
6. Ожидаемый результат
- Основной артефакт Работающее веб‑приложение с HITL для двух критических действий.
- Дополнительные артефакты
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. Связанные вопросы из базы знаний
| Вопрос | Тема |
|---|---|
| 82 | HITL, подтверждение действий, undo‑механизмы |
| 17 | UX: проектирование модальных окон |
| 43 | Обработка конкурентных запросов (race condition) |
| 91 | Метрики качества UX (SUPR‑Q, отмены/подтверждения) |
| 145 | Использование Redis для временных данных |
| 223 | Тестирование асинхронных сценариев (тайм‑ауты) |
| 311 | Prometheus: кастомные счётчики и гистограммы |
| 470 | Security: предотвращение случайных деструктивных действий |
| 512 | Frontend: диалоговые компоненты и управление состоянием |
| 678 | A/B‑тестирование времени undo‑окна |
10. Чек-лист самопроверки
- Я развернул(а) приложение локально или на тестовом сервере.
- Я проверил(а), что при нажатии кнопки “Отправить” появляется диалог, а не сразу выполняется действие.
- Я подтвердил действие — письмо ушло/запись удалилась — и метрика confirm увеличилась.
- Я нажал “Отмена” и отменил действие — письмо не ушло, запись осталась — метрика cancel увеличилась.
- Я дождался истечения таймера без нажатия кнопок — действие отменилось автоматически, метрика timeout увеличилась.
- Я проверил, что логи содержат записи для всех трёх сценариев.
- Я запустил тесты (pytest) — все зелёные.
- Я открыл Grafana и увидел дашборд с ненулевыми значениями хотя бы одной метрики.