gold.action_vector est la couche de résolution. Elle transforme les scores abstraits du
vecteur de décision en une recommandation
concrète — une quantité à réassortir, des unités à transférer, ou un pourcentage de remise —
plus une confiance et un snapshot enrichi du « pourquoi » que l’app affiche.
Elle est précalculée quotidiennement, juste après decision_vector, pour que l’app lise
les recommandations instantanément via Postgres au lieu d’exécuter le resolver à la demande
(~60 s) par article.
| Concept | Valeur |
|---|---|
| Clé primaire | (organization_id, variant_id, shop_id, snapshot_date, domain) |
domain | "restock", "markdown", "rebalance" |
| Mode d’écriture | MERGE sur la PK (idempotent) |
| Builder | BuildActionVectorTask (pipelines/layers/gold/tasks/build_action_vector/) |
| Sync Lakebase | <env>.gold_lakebase.action_vector (CDC déclenché, merge-on-PK) |
Colonnes
| Colonne | Type | Signification |
|---|---|---|
recommended_qty | INT, nullable | Unités à réassortir (restock) / transférer (rebalance) ; NULL pour markdown |
recommended_discount_pct | DOUBLE, nullable | Remise markdown dans [0, 100] ; NULL pour restock / rebalance |
recommendation_confidence | DOUBLE, nullable | Confiance par recommandation dans [0, 1] |
decision_vector_snapshot | STRING (JSON), NOT NULL | Le « pourquoi » enrichi — tableaux context / formula / applied_rules |
recommended_qty / recommended_discount_pct est remplie par ligne,
imposé par la règle de validation action_vector_domain_payload_consistent.
Le decision_vector_snapshot est une chaîne JSON opaque (les tableaux imbriqués ont une
forme arbitraire). L’UI d’explication de l’app affiche le context (conditions d’entrée), la
formula (valeurs intermédiaires) et les applied_rules (IDs des règles déclenchées). Pour
le rebalance, les magasins source/destination voyagent dans ce JSON
(source_shop_id / destination_shop_id).
Comment les scores deviennent des recommandations
- Restock —
resolve_score_driven_positionlit le dernier vecteur de décision restock etstock_snapshot.current_stock_qtypar position et calculerecommended_qty. Sans ligne de vecteur de décision, il retombe proprement sur le calculFillToTargetvers une cible de repli. - Markdown —
resolve_recommended_discount_positionconsommemarkdown_scoreet la fraction de remise du vecteur de décision. - Rebalance —
apply_rebalance_matchingest une optimisation globale, par org : il apparie les magasins en surplus avec ceux en déficit par matching biparti glouton (le déficit de plus fortetransfer_urgencypuise dans la source de plus fortsurplus_score,qty = min(surplus_units, deficit_units), départage surshop_id). Il écrit une ligne par transfert apparié, clé sur la destination. Des règles de phase SOURCING peuvent opposer un veto à des appariements précis.
Batch vs à la demande — mêmes cœurs, une seule source de vérité
Le batch quotidien et la simulation interactive de l’app appellent les mêmes cœurs de résolution par position, donc une ligneaction_vector précalculée correspond à ce qu’une
simulation à la demande produirait.
simulate_action() (pipelines/shared/workflow_execution/simulate.py) est un wrapper en
lecture seule — il ne persiste jamais de plan. Il valide que le ruleset_id appartient à l’org
(levant RulesetNotOwnedError sur un id cross-tenant) avant toute lecture de règle.
Stratégie par défaut par famille d’action
| Famille d’action | Stratégie par défaut |
|---|---|
RESTOCK | ScoreDrivenQuantityStrategy(fallback_target=30) |
REBALANCE | ScoreDrivenMatchingStrategy(surplus_threshold=0.5, deficit_threshold=0.5) |
MARKDOWN | RecommendedDiscountStrategy(max_discount_pct=70.0) |
Confiance
recommendation_confidence ∈ [0, 1] est calculée à partir du vecteur de scores — les entrées
incluent la séparation des scores restock (à quel point un score domine clairement), la
suffisance des données (complétude des entrées), et si des contraintes interdisent l’action.
C’est la valeur sur laquelle l’app filtre la matérialisation de tâches et le pré-remplissage.
(Les lignes restock peuvent légitimement porter une confiance NULL, que les consommateurs en
aval traitent comme « toujours garder ».)
Validation
- Erreurs :
action_vector_not_empty,action_vector_required_fields,action_vector_pk_unique,action_vector_domain_allowed_v1,action_vector_domain_payload_consistent(qté XOR remise par domaine). - Avertissements :
recommendation_confidencedans[0, 1];recommended_discount_pctdans[0, 100].
Source
- Schéma :
pipelines/shared/schemas/gold/action_vector.py - Builder :
pipelines/layers/gold/tasks/build_action_vector/{task.py, executor.py} - Cœurs de résolution :
pipelines/shared/workflow_execution/{items_resolver_score_driven.py, items_resolver_recommended_discount.py, scoring/rebalance_matching.py} - Entrée à la demande :
pipelines/shared/workflow_execution/simulate.py

