Сценарий: новый offer → каноническое сопоставление
NOTE
Статус: Target design. Документ описывает целевую доменную модель. Соответствующий код реализован частично (см.
backend/internal/core/) или пока не начат. Правила маркировки — в50-processes/documentation-standard.md.
Триггер
SupplierOfferSeen(Offers) — первое появление(supplier_ref, supplier_sku).OfferCharacteristicsUpdated— single rematch.IdentityProfileChangedилиManufacturerAliasAdded(Catalog) — массовый rematch.
Участники
| BC | Роль |
|---|---|
| Matching | Owner. Вычисление identity_signature, попытка match, выбор уровня confidence. |
| Catalog | Источник Identity Profiles, Manufacturers, Characteristics, существующих Canonical. |
| Enrichment | Поставщик embedding similarity как second signal. |
| Offers | Subscriber MatchDecided — обновляет canonical_product_ref, match_confidence. |
| Moderation (AI-агент) | Резолюция orphans + match_review через детерминированный LLM-pipeline. Подробно — ai-moderation-flow.md. |
| Operator | Только escalation (low confidence / circuit broken / security-critical). По умолчанию не задействован. |
Sequence diagram
sequenceDiagram autonumber participant OFR as Offers participant M as Matching participant CAT as Catalog participant EN as Enrichment participant MOD as Moderation (AI-агент) participant OP as Operator (только escalation) OFR->>M: 🟧 SupplierOfferSeen M->>M: 🟦 AttemptMatch Note over M: extract manufacturer / mpn / characteristics M->>CAT: read Manufacturer aliases M->>CAT: select Identity Profile by matcher alt профиль найден M->>M: compute identity_signature M->>CAT: lookup canonical by signature alt canonical exists M-->>OFR: 🟧 MatchDecided{exact|strong} else canonical NOT exists M-->>OFR: 🟧 MatchDecided{unmatched}<br/>но identity_signature валиден M-->>CAT: 🟧 MatchDecided{unmatched} CAT->>CAT: 🟪 Policy: создать canonical CAT->>CAT: 🟦 СоздатьКаноническийТовар CAT-->>CAT: 🟧 КаноническийТоварСоздан Note over CAT: relink offers с этим signature end else профиль НЕ найден / нет critical attrs M->>M: try by description+characteristics alt нашёл probable M-->>OFR: 🟧 MatchDecided{probable} M->>EN: запрос embedding similarity EN-->>M: similarity score M->>M: check second signals alt есть second signal M->>M: 🟧 SecondSignalFound M-->>OFR: 🟧 MatchUpgraded → strong else нет second signal M->>MOD: 🟧 ModerationItemAdded(match_review) MOD->>MOD: AI-агент: gather + LLM + validate alt AI confidence >= threshold MOD->>M: 🟧 ProposedAction(MatchUpgrade) M->>M: 🟦 ApplyModerationDecision M-->>MOD: DecisionApplied{case_id} M-->>OFR: 🟧 MatchUpgraded else low confidence / circuit MOD->>OP: 🟧 CaseEscalated end end else weak M-->>OFR: 🟧 MatchDecided{weak} M->>MOD: 🟧 ModerationItemAdded else unmatched + no profile M-->>OFR: 🟧 MatchDecided{unmatched} M->>M: 🟦 OpenOrphan M-->>M: 🟧 OrphanItemAssigned M->>MOD: publish OrphanItemAssigned (orphan_resolution) MOD->>MOD: AI-агент: классификация / proposal MOD->>M: 🟧 ProposedAction(ResolveOrphan) M->>M: 🟦 ApplyModerationDecision M-->>MOD: DecisionApplied end end
Шаги
- Trigger:
SupplierOfferSeenилиOfferCharacteristicsUpdated. - Identity profile selection:
- Берём
characteristics_from_supplier+ classification_tags + parsed manufacturer/mpn. - Применяем все matcher’ы Identity Profile (
object_type == Xи т.д.). - Выбираем профиль с max priority; tie-break — больше critical_attribute_keys.
- Берём
- Identity signature:
- Если найден профиль и есть mpn (или достаточный набор critical attrs):
- Нормализуем
(manufacturer, mpn?, profile, critical_attrs). identity_signature = stable_hash(...).
- Нормализуем
- Если найден профиль и есть mpn (или достаточный набор critical attrs):
- Canonical lookup:
SELECT canonical WHERE identity_signature = ?.- Найден →
MatchDecided{exact}(если совпадение по mpn) или{strong}(расхождение в некритичных). - Не найден →
MatchDecided{unmatched}+ сигнал Catalog’у создать canonical (Policy).
- Если профиль НЕ найден (нет matcher’а):
- Попытка через embedding / описание.
- probable match: похожий canonical, но без identity. Требуется second signal:
- Embedding similarity ≥ 0.85.
- Совпадение classification_tag.
- Packaging fingerprint совпал.
- Multi-supplier consensus (≥ 2 поставщика прислали тот же набор critical → одинаковый identity_signature → автоматически exact/strong).
- Manual подтверждение модератором за последние 30 дней похожего matching на этом profile.
- weak — частичное совпадение, всегда moderation.
- unmatched + no profile — orphan.
- Moderation queue / orphan pool (publish для AI-агента в Moderation BC):
probableбез second signal →ModerationItemAdded(match_review).weakвсегда →ModerationItemAdded(match_review).unmatched + no profile→OpenOrphan→OrphanItemAssigned(orphan_resolution)с категорией (hot/warm/cold) и SLA.
- AI-агент Moderation (по умолчанию автоматически):
- Gather evidence: похожие resolved кейсы, embedding similarity, classification tags, multi-supplier consensus.
- LLM в sandbox (temperature=0, timeout 30 сек, function calling с JSON schema).
- Validate: confidence ≥ threshold, citations существуют, proposed_action валиден.
accept→ProposedAction(MatchUpgrade|ResolveOrphan)→ Matching применяет черезApplyModerationDecision→ ackDecisionApplied{case_id}.reject→ решение остаётсяprobable/weak/unclassifiable(для orphan).confidence < thresholdилиcircuit broken→CaseEscalated→ human queue (только при невозможности AI). Подробно —ai-moderation-flow.md.
- Catalog update (если match=exact/strong + новые characteristics из offer):
- Conflict resolution →
ОбновитьХарактеристикиТовара.
- Conflict resolution →
- Cascade rematch (при
IdentityProfileChanged/ManufacturerAliasAdded):RequestRematchмассовый для всех затронутых offers.RematchBatchCompletedevent для метрик.
Decision points
flowchart TD A["SupplierOfferSeen"] --> B{"Identity Profile<br/>matcher срабатывает?"} B -->|да| C["compute identity_signature"] C --> D{"Canonical с этим<br/>signature существует?"} D -->|да| E["🟧 MatchDecided<br/>{exact|strong}"] D -->|нет| F["🟧 MatchDecided{unmatched}<br/>+ valid signature"] F --> G["Catalog: создать canonical"] B -->|нет| H["попытка через<br/>описание + embeddings"] H --> I{"score?"} I -->|exact-like| E I -->|probable| J{"есть second signal?"} J -->|да| K["🟧 MatchUpgraded → strong"] J -->|нет| L["🟪 ModerationItemAdded"] I -->|weak| L I -->|none + no profile| M["🟦 OpenOrphan"] M --> N["🟧 OrphanItemAssigned<br/>{hot|warm|cold}"]
Edge cases
| Случай | Поведение |
|---|---|
| MPN отсутствует у offer и у профиля > 0 critical attrs | Identity signature использует sentinel ∅ для mpn + critical_attrs. Достаточно для дедупа, но требует более надёжной нормализации. |
| Два разных canonical с одинаковыми critical attrs (баг профиля) | Identity collision — alert. Профиль требует расширения critical_attribute_keys через ADR. |
| Manufacturer alias добавлен после первого match | Cascading RequestRematch для offers с этим alias parsed. Возможен MatchUpgraded. |
| Offer одновременно матчится к 2 canonical через разные эвристики | Hard conflict — moderation. Не auto-разрешается. |
| Critical attribute обогащён AI, не verified | Не используется в identity_signature (см. Catalog inv #5 + Enrichment inv). Может использоваться как сигнал (probable boost), но не для exact. |
| Поставщик переотправил тот же offer | Идемпотентно — повторная попытка дает то же MatchDecision (если входные данные не изменились). |
Инварианты сценария
- На один
SupplierOffer— ровно одно текущееMatchDecision. match_confidence = unmatched⇔canonical_product_refis null.probableпопадает в автомат только при наличии хотя бы одного из 5 second signals.weakвсегда требует ручной верификации.- Orphan SLA по категориям соблюдается (см. matching.md).
- AI-значения не входят в identity_signature.
Метрики и observability
match_decisions_total{confidence}— распределение confidence.match_upgrades_total{from, to},match_downgrades_total{from, to}.orphan_pool_size{category},orphan_resolution_time_seconds{category}(SLA).moderation_queue_size{kind}.identity_signature_collisions_total— alert при > 0.rematch_batch_completed{trigger}— для understanding load.
Связанные файлы
- Контексты:
../contexts/matching.md,../contexts/catalog.md,../contexts/enrichment.md,../contexts/offers.md. - Deep-dive:
../product-identity.md(Identity Signature, Match Confidence, Second Signals, Orphan SLA). - Сценарии:
ingestion-flow.md,update-and-diff.md.