Passer au contenu principal
gold.decision_vector est la couche de scoring. Il lit decision_context et émet une ligne par (organization_id, variant_id, shop_id, snapshot_date, domain) portant un vecteur de risque — des scores dans [0, 1] — plus la porte des actions (allowed_actions / forbidden_actions) et une piste d’audit (applied_rules).
ConceptValeur
Clé primaire(organization_id, variant_id, shop_id, snapshot_date, domain)
domain"restock", "rebalance" ou "markdown"
Mode d’écritureMERGE sur la PK — les domaines s’empilent sans migration DDL
WritersBuildDecisionVectorTask (restock), BuildDecisionVectorRebalanceTask, BuildDecisionVectorMarkdownTask
variant_id / shop_id sont NOT NULL ici (la clé MERGE l’exige), bien qu’elles soient nullables en amont sur decision_context.

La STRUCT scores

Une seule STRUCT NOT NULL porte les champs de score de chaque domaine côte à côte. Les sous-champs sont nullables : une ligne remplie par un domaine laisse NULL les sous-champs des autres — gardant la table d’une seule forme entre domaines. La non-nullité du domaine rempli est garantie par construction : chaque entrée NULL en amont est ramenée à une valeur neutre avant le scoring.
Sous-champDomainePlageSignification
restock_urgencyrestock[0, 1]urgence opérationnelle de réassort
stockout_riskrestock[0, 1]probabilité de rupture sur les 30 prochains jours
overstock_riskrestock[0, 1]probabilité de surstock en fin de saison
surplus_scorerebalance[0, 1]mesure du dépassement de la cible
deficit_scorerebalance[0, 1]mesure du déficit sous la cible
transfer_urgencyrebalance[0, 1]déficit × confiance de prévision × proximité de saison
surplus_unitsrebalance≥ 0excédent explicite max(0, stock − cible)
deficit_unitsrebalance≥ 0manque explicite max(0, cible − stock)

La porte des actions

Chaque ligne porte aussi trois tableaux NOT NULL :
  • allowed_actions — la base émet le domaine lui-même, p. ex. ["restock"].
  • forbidden_actions[] en v1.1 ; de futures règles SOURCING / SIZING le rempliront.
  • applied_rules — piste d’audit, toujours non vide (size(applied_rules) > 0). Le premier élément est un qualifieur de domaine (p. ex. markdown_domain_qualifier:v1) ; les suivants sont les IDs des règles métier de scoring qui se sont déclenchées.

Scoring par domaine

Source : build_decision_vector/scoring.py. Toutes les pondérations sont des constantes de module.restock_urgency[0, 1]
cover_signal    = 1 - min(days_of_cover / 30, 1)
forecast_signal = min(forecast_30d / 100, 1)
margin_signal   = clamp(gross_margin_pct / 100, 0, 1)
aged_signal     = 1 if aged_stock_flag else 0

urgency = clamp01(
    0.5 * cover_signal       # W_COVER
  + 0.3 * forecast_signal    # W_FORECAST
  + 0.2 * margin_signal      # W_MARGIN
  - 0.1 * aged_signal        # W_AGED_PENALTY (soustractif)
)
stockout_risk[0, 1]
cover_factor      = clamp01(1 - days_of_cover / 30)
forecast_modifier = 0.5 + 0.5 * min(forecast_30d / 100, 1)   # ∈ [0.5, 1.0]
risk              = cover_factor * forecast_modifier
overstock_risk[0, 1]
cover_factor = clamp01((days_of_cover - 30) / (90 - 30))
aged_factor  = 1 if aged_stock_flag else 0
risk         = 0.6 * cover_factor + 0.4 * aged_factor    # W_OVERSTOCK_COVER / _AGED
Défauts NULL : days_of_cover → 30 (neutre), forecast_30d → 0 (pas de demande), gross_margin_pct → 0, aged_stock_flag → false. Une ligne tout-NULL score (0, 0, 0).

Validation

  • Erreurs (échouent la tâche) : decision_vector_not_empty, decision_vector_required_fields, decision_vector_pk_unique, decision_vector_applied_rules_non_empty, decision_vector_domain_allowed_v11, et des bornes BETWEEN 0 AND 1 (NULL-safe) sur les scores rebalance (surplus_score, deficit_score, transfer_urgency).
  • Avertissements : contrôles de plage sur les scores restock (restock_urgency, stockout_risk, overstock_risk).
Les scores sont clampés par construction, donc un dépassement de plage signale un vrai bug — échec bruyant.
Les pondérations de scoring sont des constantes de module aujourd’hui ; le réglage passe par une PR revue, pas par un bouton de réglages. Un futur chemin de calibration pourra les exposer via les réglages gold une fois que les données de production le justifieront.

Source

  • Schéma : pipelines/shared/schemas/gold/decision_vector.py
  • Scoring : build_decision_vector/{scoring.py, scoring_markdown.py}, build_decision_vector_rebalance/scoring_rebalance.py
  • Lookup markdown : pipelines/shared/config/markdown_discount_lookup.yaml
  • Doc repo : docs/gold/decision-vector.md