ADR-0045: Tier 3 LLM matcher + matching moderation queue
Status: accepted Date: 2026-04-29 Deciders: agent-claude
Контекст
Tier 1 (точное MPN) + Tier 2 (отпечаток) уже работают в matcher-worker.
Probable cases (Tier 2 score 0.5–0.8) тихо пишутся как
MatchConfidenceProbable без верификации. Конфликты Tier 1 (один MPN —
несколько canonical-ов) застревают со статусом MatchStatusConflict.
Manual review pipeline отсутствует.
ADR-0044 ввёл platform/llm через CLIProxy + sashabaranov SDK. Этот ADR
описывает первый consumer LLM на match-стороне, плюс matching-specific
moderation queue для weak/probable LLM-результатов.
Решение
- Tier 3 LLM-валидация синхронно в
MatcherService.RunBatch. После Tier 1+2 неоднозначные кейсы собираются в pending list; единый batched LLM call (chunks of 20); результаты apply’ятся с маппингом per spec §4.3. - Два разных prompt builders — binary для probable (1 candidate), conflict для multi-candidate selection. Два LLM call’а: один батч для probable, один для conflict.
- Confidence маппинг строго per spec §4.3. ≥0.85 strong, 0.7–0.85 probable, <0.7 weak, !match unmatched.
- Matching-specific moderation queue (
match_moderation_queue) — таблица в matching BC, UNIQUE partial idx(offer_id) WHERE status='pending'для идемпотентности. Общий cross-source Moderation BC выделим когда появится второй consumer. - Admin HTTP endpoints в
matching/api/httpс Bearer-token middleware (ENVADMIN_API_KEY). Empty key → 503 (нельзя случайно залить prod без auth). - LLM failure handling без auto-retry. Chunk dropped с метрикой
match.tier3.chunk_dropped{reason}. Decisions остаются с pre-LLM статусом. Manual re-trigger через CLI — отдельным циклом. - Reuse
platform/llm—LLMClient.Chat(model=ModelMatch=sonnet, JSONMode=true). Никаких extension’ов в platform/llm.
Последствия
Плюсы:
- Probable + conflict cases больше не висят без обработки.
- Реальный pipeline для ручного review с явным lifecycle.
- Базис для будущего общего Moderation BC.
Минусы / accepted trade-offs:
- LLM cost растёт: Sonnet дороже Haiku, conflict prompts длиннее. Мониторинг через CLIProxy usage stats.
- Reject на tier1-conflict не откатывает LLM-выбор (известное ограничение первого цикла).
- Нет multi-reviewer claim, нет auto-retry.
Альтернативы, отклонённые
- Async Tier 3 в отдельном воркере — отклонено: добавляет процесс, удлиняет feedback loop.
- Без moderation queue (только confidence) — отклонено per user signal: «довести этап максимально».
- Общий Moderation BC сразу — отклонено: преждевременная абстракция.
Реализация
Затронутые компоненты:
backend/internal/core/matching/:
domain/llm_validator.go,moderation.go,prompt.go,metrics.goapp/llm_validator.go(BatchedValidator)app/matcher.go(расширение — pending collection + apply)app/moderation_service.goapp/metrics.go(AtomicMatchingMetrics)infra/postgres/moderation_repo.goinfra/postgres/decision_repo.go(+llm_confidence column)infra/postgres/candidate_reader.go(+GetCanonicalDetails, +GetOfferDigest)api/http/moderation_handler.go,admin_auth.go,dto.godi.go(full rewrite — providers + handler registrar)
backend/migrations/:
0038_create_match_moderation_queue.sql0039_match_decisions_llm_confidence.sql
backend/cmd/matcher-worker/main.go — добавлен llm.Module.
backend/internal/platform/config/config.go — Admin{APIKey} struct.
Deferred items
- Общий cross-source Moderation BC (после второго consumer).
- AI-агентная модерация в sandbox (Phase 4 AI-слой).
- Auto-retry on LLM failure.
- Multi-reviewer claim mechanism.
- LLM cost tracking aggregation.
- Reject-handler для tier1-conflict (manual override).
- UI для moderation review.
- Expired auto-cleanup для stale pending items.
Ссылки
- ADR-0044 — LLM gateway + char-pipeline foundation
- ADR-0030 — Backend DI rule
- Spec Phase 2 от 21 апреля —
docs/superpowers/specs/2026-04-21-phase2-matcher-llm-design.md - Spec этого цикла —
docs/superpowers/specs/2026-04-29-tier3-llm-matcher-design.md - Plan этого цикла —
docs/superpowers/plans/2026-04-29-tier3-llm-matcher.md