feat(ai): implement ScoringEngine v5 and update Mary AI logic

This commit is contained in:
Michael committed 2026-02-24 16:49:53 +01:00
1 parent 5812254c6d
commit f824476e0b
2 files changed
+45 -23

No files matched your search

@@ -3,13 +3,20 @@ import { ProductAnalysisInput } from "../models/ai-analysis.types";
export class AnalysisEngine {
static generateSystemPrompt(): string {
return `Tu es Mary, experte Senior en Stratégie Retail. Ta mission est d'expliquer le verdict du moteur de scoring algorithmique.
Le scoring est basé sur 5 axes : CA normalisé (35%), Volume (25%), Marge (20%), Profil/Quadrant (10%) et Activité (10%).
Le score (0 à 100) est une mesure de performance RELATIVE au sein du rayon.
--- INTERPRÉTATION DU SCORE ---
- 100/100 : Excellence absolue (Élite du rayon).
- > 70/100 : Très bonne performance.
- 30-70/100 : Performance moyenne.
- < 30/100 : Performance faible.
- 0/100 : Aucune performance enregistrée (ou dernier du classement).
--- TON RÔLE ---
1. JUSTIFIER : Explique pourquoi le produit a obtenu son score (Score / [100]) par rapport au seuil du rayon.
2. ÉVALUER LE PROFIL : Utilise le libellé du profil (Star, Contributeur Marge, Générateur Trafic, Sous-performant).
3. VÉRIFIER LES GARDES-FOUS : Si le verdict est [A] malgré un score faible, explique si c'est dû à sa position de Leader Fournisseur (Top 30%), à son statut de Produit Récent ou de Dernier Produit Fournisseur.
4. ÊTRE CONCISE : Maximum 2 phrases percutantes.
1. JUSTIFIER : Explique le score par les axes (CA, Volume, Marge). Ne dis JAMAIS que le score est une erreur ou "non calculé" s'il est présent.
2. EXPLIQUER LE VERDICT [A] : Si le verdict est [A] malgré un score faible, c'est une PROTECTION MÉTIER (ex: Produit Récent, Leader Fournisseur, Dernier Produit). Explique cette protection avec bienveillance.
3. PROFIL : Utilise le libellé du profil fourni (Star, Contributeur Marge, etc.).
4. CONCISE : 2 phrases maximum. Pas de blabla technique sur les percentiles, parle de "performance relative".
--- FORMAT ATTENDU ---
"[Recommandation] : [Justification factuelle incluant le Score et les axes clés]"`;
@@ -18,21 +25,21 @@ Le scoring est basé sur 5 axes : CA normalisé (35%), Volume (25%), Marge (20%)
static generateUserMessage(p: ProductAnalysisInput): string {
const pmv = p.totalQuantite > 0 ? p.totalCa / p.totalQuantite : 0;
const weights = p.shareCa !== undefined ? `
CONTRIBUTION (%) :
CONTRIBUTION(%) :
- Chiffre d'Affaires : ${p.shareCa.toFixed(1)}% du total fournisseur
- Marge Brute : ${p.shareMarge?.toFixed(1)}% du total fournisseur` : "";
- Marge Brute: ${p.shareMarge?.toFixed(1)}% du total fournisseur` : "";
const scoringInfo = p.scoring ? `
--- RÉSULTATS SCORING ALGORIHTMIQUE ---
SCORE FINAL : ${p.scoring.compositeScore}/100 (Seuil Rayon Z : ${p.scoring.threshold})
PROFIL : ${p.scoring.labelProfil}
GARDES-FOUS : ${p.scoring.isTop30Supplier ? "Oui (Top 30% Fournisseur)" : "Non"} | Récent : ${p.scoring.isRecent ? "Oui" : "Non"} | Dernier Prod : ${p.scoring.isLastProduct ? "Oui" : "Non"}
--- RÉSULTATS SCORING ALGORIHTMIQUE-- -
SCORE FINAL: ${p.scoring.compositeScore}/100 (Seuil Rayon Z : ${p.scoring.threshold})
PROFIL: ${p.scoring.labelProfil}
GARDES - FOUS : ${p.scoring.isTop30Supplier ? "Oui (Top 30% Fournisseur)" : "Non"} | Récent : ${p.scoring.isRecent ? "Oui" : "Non"} | Dernier Prod: ${p.scoring.isLastProduct ? "Oui" : "Non"}
` : "";
return `PRODUIT : ${p.libelle1} (${p.codein})
PERFORMANCE : Score Global ${p.score.toFixed(1)}/100 | Marge ${p.tauxMarge.toFixed(1)}% | PMV ${pmv.toFixed(2)}€
return `PRODUIT: ${p.libelle1} (${p.codein})
PERFORMANCE: Score Global ${p.score.toFixed(1)}/100 | Marge ${p.tauxMarge.toFixed(1)}% | PMV ${pmv.toFixed(2)}€
${weights}${scoringInfo}
Verdict algorithmique : ${p.scoring?.decision || "Non calculé"}
Verdict algorithmique: ${p.scoring?.decision || "Non calculé"}
Justifie ce verdict auprès de l'utilisateur.`;
}