Очередь модерации переполнена
Автомодерация помечает подозрительный контент, а разбирает его человек. Если входящий поток обгоняет ревьюеров — очередь растёт, и токсичный ответ висит часами до просмотра. Ловим два сигнала: длину очереди и возраст самого старого непросмотренного элемента. Второй важнее — он про SLA, а не про объём.
1. Проверка очереди по расписанию
Заголовок раздела «1. Проверка очереди по расписанию»Раз в несколько минут cloud-функция с timer-trigger смотрит длину и возраст головы очереди:
import os, time, requests
MAX_LEN = 50 # тревога, если ждёт больше стольких элементовSLA_MINUTES = 30 # ничто не должно ждать ревью дольше этого
def handler(event, context): pending = db_fetch_pending() # список dict с полем created_at (epoch) n = len(pending) if n == 0: return {"statusCode": 200}
oldest_age_min = (time.time() - min(p["created_at"] for p in pending)) / 60
if oldest_age_min > SLA_MINUTES: notify("🛡️ Модерация: просрочен SLA", f"Самый старый элемент ждёт {oldest_age_min:.0f} мин " f"(SLA {SLA_MINUTES} мин).\nВ очереди: {n}.", priority=9) elif n > MAX_LEN: notify("🛡️ Очередь модерации растёт", f"В очереди: {n} (лимит {MAX_LEN}).\n" f"Самый старый: {oldest_age_min:.0f} мин.", priority=7) return {"statusCode": 200}
def notify(title, message, priority): requests.post(f"{os.environ['NOTIFLY_URL']}/message", params={"token": os.environ["NOTIFLY_TOKEN"]}, json={"title": title, "message": message, "priority": priority}, timeout=5)Возраст (SLA) идёт с приоритетом выше длины: маленькая, но застоявшаяся очередь опаснее большой, но быстро разбираемой.
2. Дедупликация, пока очередь не разобрали
Заголовок раздела «2. Дедупликация, пока очередь не разобрали»Проверка бежит каждые 5 минут, а очередь может висеть переполненной час — не хочется двенадцать одинаковых пушей. Флаг с TTL:
import redisR = redis.from_url(os.environ["REDIS_URL"])
def notify_once(title, message, priority, cooldown=1800): if R.set("alert:" + title, "1", nx=True, ex=cooldown): notify(title, message, priority)3. Подкрепить heartbeat: «ревьюеры вообще работают?»
Заголовок раздела «3. Подкрепить heartbeat: «ревьюеры вообще работают?»»Отдельный тихий сигнал — не то что очередь длинная, а то что её перестали разбирать (все ушли на выходные, воркер завис). Пусть код, обрабатывающий элемент, пингует heartbeat при каждом ревью:
def on_item_reviewed(item): save_decision(item) requests.get(os.environ["MODERATION_PING_URL"], timeout=5) # heartbeatЗадайте intervalSec под ожидаемый темп ревью — и если ревью встало,
Notifly пришлёт push сам, даже когда очередь ещё в пределах лимита.
Что положить в текст алёрта
Заголовок раздела «Что положить в текст алёрта»- длину очереди и лимит;
- возраст самого старого элемента и SLA;
- разбивку по причинам флага (токсичность, PII, спам);
- ссылку на дашборд модерации через extras с URL.
Связанные рецепты
Заголовок раздела «Связанные рецепты»- Очередь LLM-задач растёт — тот же приём для очереди задач.
- PII в промпте или ответе — частая причина попадания в очередь.
- Сработал safety / prompt injection — что наполняет очередь.