Passer au contenu principal
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.
ConceptValeur
Clé primaire(organization_id, variant_id, shop_id, snapshot_date, domain)
domain"restock", "markdown", "rebalance"
Mode d’écritureMERGE sur la PK (idempotent)
BuilderBuildActionVectorTask (pipelines/layers/gold/tasks/build_action_vector/)
Sync Lakebase<env>.gold_lakebase.action_vector (CDC déclenché, merge-on-PK)

Colonnes

ColonneTypeSignification
recommended_qtyINT, nullableUnités à réassortir (restock) / transférer (rebalance) ; NULL pour markdown
recommended_discount_pctDOUBLE, nullableRemise markdown dans [0, 100] ; NULL pour restock / rebalance
recommendation_confidenceDOUBLE, nullableConfiance par recommandation dans [0, 1]
decision_vector_snapshotSTRING (JSON), NOT NULLLe « pourquoi » enrichi — tableaux context / formula / applied_rules
Exactement une de 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

  • Restockresolve_score_driven_position lit le dernier vecteur de décision restock et stock_snapshot.current_stock_qty par position et calcule recommended_qty. Sans ligne de vecteur de décision, il retombe proprement sur le calcul FillToTarget vers une cible de repli.
  • Markdownresolve_recommended_discount_position consomme markdown_score et la fraction de remise du vecteur de décision.
  • Rebalanceapply_rebalance_matching est 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 forte transfer_urgency puise dans la source de plus fort surplus_score, qty = min(surplus_units, deficit_units), départage sur shop_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.
Les positions sans ligne de vecteur de décision ne sont volontairement pas écrites — l’app ne trouve aucune ligne et retombe sur le resolver à la demande.

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 ligne action_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’actionStratégie par défaut
RESTOCKScoreDrivenQuantityStrategy(fallback_target=30)
REBALANCEScoreDrivenMatchingStrategy(surplus_threshold=0.5, deficit_threshold=0.5)
MARKDOWNRecommendedDiscountStrategy(max_discount_pct=70.0)
L’app passe sa propre stratégie choisie ; ce ne sont que les valeurs pré-sélectionnées. Une stratégie de la mauvaise famille pour l’action demandée est rejetée.

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_confidence dans [0, 1] ; recommended_discount_pct dans [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