Сценарий: AI-модерация (автоматическая обработка очереди)
NOTE
Статус: Target design. Документ описывает целевую доменную модель. Соответствующий код реализован частично (см.
backend/internal/core/) или пока не начат. Правила маркировки — в50-processes/documentation-standard.md.
Триггер
Любое событие из source BC, которое раньше требовало человека:
- Matching:
ModerationItemAdded(match_review),OrphanItemAssigned. - Catalog:
ManufacturerAliasProposalCreated,IdentityProfileProposalCreated. - Enrichment:
EnrichmentReviewRequired(для critical attribute). - Supplier Network:
InferredRelationshipProposed. - Pricing:
PriceRuleConflictDetected. - Credentials:
CredentialGroupCandidateOpened. - Customer:
LegalInfoVerificationEdge(ИНН ambiguous).
Участники
| BC / компонент | Роль |
|---|---|
| Source BC | Публикует ModerationItemAdded или специализированное событие через PL. |
| Moderation | Owner. Создаёт ModerationCase, собирает evidence, запускает агента, валидирует, публикует ProposedAction. |
| AgentRunner | Конфигурация агента per CaseKind: model, prompt, tools, threshold. |
| External LLM | Foundation model в sandbox (no network outside whitelisted RAG, no creds, timeout 30 сек). |
| RAG store | Индекс похожих кейсов с outcomes (для in-context learning). |
| Source BC consumer | Применяет ProposedAction через свою команду; публикует DecisionApplied{case_id}. |
| Operator | Только при escalation — security-critical или low-confidence fallback. |
Sequence diagram
sequenceDiagram autonumber participant SRC as Source BC participant MOD as Moderation participant Q as moderation_queue participant GE as GatherEvidence participant RAG as RAG store participant LLM as Sandboxed LLM participant V as Validator participant K as Kafka (moderation.actions.*) participant SC as Source BC consumer participant HQ as human_escalation_queue SRC->>MOD: ModerationItemAdded MOD->>Q: 🟦 CreateCase Q-->>MOD: ModerationCaseCreated MOD->>GE: 🟦 GatherEvidence(case_id) GE->>SRC: read-only queries (canonical, offer, supplier, etc.) GE->>RAG: search top-K похожих кейсов GE-->>MOD: EvidenceGathered (structured payload) MOD->>LLM: 🟦 RunAgent (prompt + evidence + tools) LLM-->>MOD: JSON output (verdict, confidence, reasoning, citations, proposed_action) MOD->>V: validate schema + citations + ranges alt валидно + confidence ≥ threshold + verdict ∈ {accept, reject} V-->>MOD: ok MOD-->>MOD: AgentDecisionEmitted alt verdict = accept MOD->>K: publish ProposedAction (case_id correlation) K->>SC: consume SC->>SC: применить через свою команду SC->>K: DecisionApplied{case_id, ack} K->>MOD: consume ack MOD-->>MOD: case = applied else verdict = reject MOD-->>MOD: case = closed (no action) end else невалидно или confidence < threshold или verdict = escalate V-->>MOD: fail/low MOD-->>MOD: AgentRunFailed (если schema fail) Note over MOD: retry до N раз с альтернативным prompt alt все retries исчерпаны или verdict = escalate MOD->>HQ: 🟧 CaseEscalated HQ-->>HQ: notify #tracium-ai-ops end else verdict = defer MOD-->>MOD: waiting (reopen триггер на новые данные) end
Шаги (детально)
1. CreateCase
Source BC публикует событие модерации (типизированное per kind). Moderation consumer создаёт ModerationCase со статусом queued. Дедупликация по (source.bc, source.aggregate_ref, kind) — повторное событие на тот же агрегат не создаёт duplicate, обновляет existing case.
2. GatherEvidence (детерминированный код, не LLM)
Запросы по evidence_query из AgentRunner конфига. Per CaseKind — свой набор:
CaseKind | Evidence |
|---|---|
match_review | offer characteristics, candidate canonical, embedding similarity score, classification tags, packaging fingerprint, multi-supplier consensus check, last 30d похожие resolved match decisions |
orphan_resolution | offer characteristics, ближайшие Identity Profiles (matcher partial fit), other orphans с похожими attrs, бренды/категории |
inferred_relationship_review | from/to suppliers full info, overlap метрики, observed evidence (raw payloads sample), declared relationships обоих |
enrichment_critical_attr_review | original offer description, AI-extracted value, alternative LLM extractions (другая модель / prompt), embedding sanity vs canonical-в-том-же-profile |
priority_overlap_review | obe Price Rules, sample 100 pricing_contexts, результат для каждого order |
credential_group_candidate | две credentials (без секретов!), customer info, supplier info, normalized identity field |
legal_info_verification_edge | ИНН-verify response, открытые источники (FNS API mock), customer registration data |
manufacturer_alias_proposal | proposed alias, existing aliases для всех manufacturers, embedding сравнение, бренд-каталоги |
identity_profile_proposal | cluster orphans, common characteristics, потенциальные critical_attribute_keys |
Evidence — JSON payload. Hashed для idempotency (evidence_hash).
Безопасность: evidence query ограничен read-only API source BC. Нет прямого PG-доступа. Секреты (auth_payload, password_hash, ПД) — НИКОГДА не в evidence (filter на репозиторном уровне).
3. RunAgent (LLM в sandbox)
prompt = AgentRunner.prompt_template
+ system_instructions("ты модератор Tracium для CaseKind=X, отвечай строго JSON по schema Y")
+ EvidenceGathered (JSON)
+ (опционально) RAG: top-3 похожих resolved кейсов
config:
temperature: 0
max_tokens: per kind
timeout: 30 сек
function_calling: enforce JSON schema на output
network: whitelist (RAG store only)
no_creds: gateway гарантирует
no_pii_in_logs: redact middleware
Output schema (для всех kinds):
{
"verdict": "accept | reject | escalate | defer",
"confidence": 0.0-1.0,
"reasoning": "короткое объяснение, до 500 символов",
"citations": ["ссылки на IDs из evidence"],
"proposed_action": { /* per CaseKind, см. ниже */ }
}Примеры proposed_action per kind:
// match_review
{ "kind": "MatchUpgrade", "from": "probable", "to": "strong", "second_signal": "embedding_similarity:0.91" }
// orphan_resolution
{ "kind": "ResolveOrphan", "assign_to_canonical_id": "uuid", "match_confidence": "probable", "reason": "..." }
// или
{ "kind": "ResolveOrphan", "create_new_canonical": true, "identity_profile_id": "uuid" }
// или
{ "kind": "ResolveOrphan", "mark_unclassifiable": true }
// inferred_relationship_review
{ "kind": "ConfirmInferredRelationship", "relationship_id": "uuid", "upgrade_source_to": "declared" }
// enrichment_critical_attr_review
{ "kind": "AcceptEnrichment", "characteristic_key": "voltage", "value": "220V", "confidence": 0.92 }
// priority_overlap_review
{ "kind": "SuggestPriority", "rule_id": "uuid", "priority": 80, "rationale": "..." }
// credential_group_candidate
{ "kind": "MergeCredentialGroup", "primary_credential_id": "uuid", "members": ["uuid", "uuid"] }4. Validate
- JSON schema валиден.
confidence ∈ [0, 1].citations[]— все IDs существуют (lookup в source BC).proposed_action.kindсоответствует ожидаемому perCaseKind.confidence ≥ confidence_thresholdагента (default 0.85, configurable).- Бизнес-санити: например, для
MatchUpgrade(probable→strong)должен быть указанsecond_signalиз 5 разрешённых типов.
Невалидный output → retry с альтернативным prompt. Max 2 retries → AgentRunFailed → escalate.
5. ApplyDecision
Moderation публикует ProposedAction в типизированный topic (например, moderation.actions.matching.v1):
{
"case_id": "uuid",
"case_kind": "match_review",
"action": { "kind": "MatchUpgrade", ... },
"evidence_hash": "...",
"model_ref": { "model_name": "...", "version": "..." },
"confidence": 0.91,
"emitted_at": "..."
}Source BC consumer (например, Matching):
- Дедупликация по
case_id. - Внутренняя команда (например,
ApplySecondSignal+MatchUpgrade). - Публикует
DecisionApplied{case_id, ack: true}.
Moderation marks case applied. Запись в moderation_decisions_log.
6. Если escalation
Кейс в human_escalation_queue (PG) + push в #tracium-ai-ops. Operator открывает admin UI:
- Вся evidence как у агента.
- Reasoning агента (что предлагал и почему confidence низкая).
- Alternative LLM extractions / похожие кейсы.
- Кнопки: accept proposed_action / reject / overwrite (свой выбор).
Решение оператора → CaseResolvedByHuman → ApplyDecision (как обычно). Запись source=human в moderation_decisions_log — основа для drift detection и переобучения.
Edge cases
| Случай | Поведение |
|---|---|
| LLM provider недоступен | Circuit breaker. Кейсы copyются в awaiting_llm queue, retry exponential backoff. SLA-alert если > 100 кейсов в queue 30 мин. |
| Output schema fail после max retries | AgentRunFailed → escalate с reason schema_validation_failed. |
proposed_action ссылается на несуществующий ID | Validation fail → retry с подсказкой «эти IDs не существуют». Если повтор — escalate. |
Source BC не подтвердил DecisionApplied за SLA (например, 60 сек) | Moderation marks case apply_pending; alert через 5 мин. Возможный rollback через operator. |
| Drift: accuracy агента упала ниже X% | AgentDisabledByCircuit для этого CaseKind → все кейсы → escalate (временный режим human-only). Trigger переобучения / prompt review. |
| Новая версия модели deployed | Schedule replay deferred + sample 100 ранее resolved (canary): сравнить outputs, если accuracy ≥ — switch full traffic. Иначе rollback. |
| Race: source BC изменил агрегат пока case в очереди | Validate citations при apply: если evidence stale — retry с fresh evidence. |
Кейс security-critical (credential_break_glass_decrypt) | НЕ вводится в Moderation BC. Это отдельный flow в Credentials с обязательным human 4-eyes. AI может только подсветить аномалию, не принимать решение. |
| LLM пытается вернуть creds или ПД в reasoning | PII-redact middleware в response chain + alert security. Agent run помечается failed. |
Инварианты сценария
- AI-агент не имеет прямого write-доступа к source BC. Только через published
ProposedAction+DecisionAppliedack loop. - Никогда не передаём в LLM: секреты, password, auth_payload, recovery codes, MFA secrets, full ПД (минимизация).
temperature = 0для всех модерационных агентов → детерминизм.- Replay одного и того же
(case_id, evidence_hash, model_ref)даёт идентичный output (verifiable). - Решение агента не финальное в смысле audit: customer / operator может оспорить →
CaseReopenedс пометкойdisputed. - Каждое applied решение можно откатить через source BC compensating команду (например,
MatchDowngrade) — Moderation не блокирует rollback. confidence_thresholdperCaseKindфиксирован конфигом; изменение = breaking → pre-prod evaluation.- Security-critical кейсы НЕ автоматизируются (см. Moderation BC invariant #4).
Метрики и observability
moderation_cases_total{kind, source}— input rate.moderation_decisions_total{kind, verdict, applied}— outcome distribution.agent_latency_seconds{kind, model_ref}— p95 < 30 сек.agent_confidence_distribution{kind}— histogram.agent_escalation_rate{kind, reason}— alert если spike.agent_accuracy{kind}— vs последующих human override / rollback / customer dispute, окно 7 дней.agent_drift_score{kind}— embedding similarity outputs vs baseline.decision_apply_lag_seconds{kind}— отAgentDecisionEmittedдоDecisionApplied.human_escalation_queue_size{kind},human_resolution_time{kind}.llm_token_cost_total{kind, model_ref}— для FinOps.
Связанные файлы
- Контекст:
../contexts/moderation.md. - Источники:
../contexts/matching.md,../contexts/enrichment.md,../contexts/supplier-network.md,../contexts/pricing.md,../contexts/credentials.md,../contexts/catalog.md,../contexts/customer.md. - Связанный сценарий:
matching-flow.md.