diff --git a/src/features/ai-copilot/business/analysis-engine.ts b/src/features/ai-copilot/business/analysis-engine.ts index 244eb86..46c9ac7 100644 --- a/src/features/ai-copilot/business/analysis-engine.ts +++ b/src/features/ai-copilot/business/analysis-engine.ts @@ -2,25 +2,26 @@ import { ProductAnalysisInput } from "../models/ai-analysis.types"; export class AnalysisEngine { static generateSystemPrompt(): string { - return `Tu es un expert en analyse de gammes de produits B2B pour un acheteur retail professionnel. + return `Tu es Mary, experte en analyse de gammes retail B2B. Ta mission est d'aider un acheteur à arbitrer son assortiment. -En analysant les données de ventes fournies, génère une recommandation de gamme impérative parmi : -- [A] - PERMANENT : Produit de fond de rayon avec une rotation régulière et prévisible. Doit être maintenu toute l'année. -- [C] - SAISONNIER : Produit dont les ventes sont concentrées sur des périodes spécifiques (pics saisonniers). -- [Z] - SORTIE : Produit en fin de cycle de vie, sans rotation significative ou en chute libre, devant être retiré du référencement. +RÈGLES D'OR : +1. VÉRACITÉ ABSOLUE : Ne déforme JAMAIS les chiffres fournis (score, volumes, marges). Si le score est de 53.5, ne dis jamais qu'il est < 30. +2. ANALYSE MULTI-CRITÈRES : Le score est un indicateur important mais pas unique. Analyse la cohérence entre le score, le volume de vente et la régularité. +3. JUSTIFICATION FACTUELLE : Base tes recommandations [A], [C] ou [Z] sur les FAITS fournis, même s'ils contredisent les tendances générales. -Critères de pondération et Règles Métier Strictes : -1. Normalisation Totale (Base 2 magasins) : TOUTES les statistiques fournies sont PONDÉRÉES par 2 si le produit n'est au catalogue que de 1 magasin. -2. Potentiel Annuel (Run Rate) : Si un produit est récent (Régularité < 12 mois), base ton jugement sur sa "Projection 12m" plutôt que sur son volume brut cumulé. -3. Détection Saisonnalité/Fin de vie : Si un produit est inactif depuis plus de 2 mois (Inactivité > 2), la projection 12 mois devient INCERTAINE. - - Si Inactivité > 2 et Régularité < 6 : Probable SAISONNIER [C] ou SORTIE [Z]. - - Si Inactivité <= 2 et Régularité < 12 : Probable LANCEMENT (Permanent [A]). -4. Segmentation par Rayon (Univers) : Privilégie la comparaison "Intra-Rayon". Un produit doit être performant par rapport aux standards de son propre Rayon (Niveau 2). -5. Score de Performance Global (0-100) : Un score > 70 est un indicateur fort pour "Permanent" (A). Un score < 30 est un indicateur fort pour "Sortie" (Z). +Options de recommandation : +- [A] - PERMANENT : Rotation régulière, produit de fond de rayon. +- [C] - SAISONNIER : Pics de ventes concentrés, inactivité hors saison. +- [Z] - SORTIE : Fin de cycle, rotation insuffisante ou en chute libre. + +Critères d'Aide à la Décision (Indicatifs) : +- Score Global > 70 : Forte présomption pour [A]. +- Score Global < 30 : Forte présomption pour [Z]. +- Entre 30 et 70 : Zone pivot nécessitant une analyse fine de la marge et de la régularité. +- Inactivité > 2 mois : Signal d'alerte pour [Z] ou [C]. Ta réponse doit être courte, directe et sans complaisance. -Format impératif : "[Recommandation] : [Texte brut de l'explication sans aucun préfixe du type 'Justification:' ou 'Pourquoi:']" -Exemple : "A : Volume de vente et score élevés justifiant le maintien en rayon."`; +Format : "[Recommandation] : [Justification factuelle]"`; } static generateUserMessage(p: ProductAnalysisInput): string { @@ -31,7 +32,7 @@ Exemple : "A : Volume de vente et score élevés justifiant le maintien en rayon const volumeInfo = `Stats Réelles : ${Math.round(p.totalQuantite)}u (${p.totalCa.toFixed(2)}€) sur ${p.storeCount} mag. Stats Pondérées (Base 2 mag) : ${Math.round(p.weightedTotalQuantite || 0)}u (${(p.weightedTotalCa || 0).toFixed(2)}€)`; - const projectionInfo = p.regularityScore < 12 ? ` + const projectionInfo = p.regularityScore > 0 && p.regularityScore < 12 ? ` --- ANALYSE DE POTENTIEL (Produit récent : ${p.regularityScore}/12 mois active) --- Projection 12 mois (Run Rate) : ${Math.round(p.projectedTotalQuantite || 0)}u (${(p.projectedTotalCa || 0).toFixed(2)}€)` : ""; @@ -40,19 +41,19 @@ Projection 12 mois (Run Rate) : ${Math.round(p.projectedTotalQuantite || 0)}u ($ : ""; const benchmarks = ` -Benchmarks Fournisseur (Global) : -- Moyenne 1 mag: ${Math.round(p.avgQtyGroup1 || 0)}u | Multi-mag: ${Math.round(p.avgQtyGroup2 || 0)}u Benchmarks Rayon ("${p.libelleNiveau2}") : - Moyenne 1 mag: ${Math.round(p.avgQtyRayon1 || 0)}u | Multi-mag: ${Math.round(p.avgQtyRayon2 || 0)}u`; - return `Produit : "${p.libelle1}" (Ref: ${p.codein}) -Rayon : ${p.libelleNiveau2 || "Non classé"} -Gamme actuelle : ${p.codeGamme ?? "Non définie"} -${volumeInfo}${projectionInfo}${activityAlert}${benchmarks} -Indicateurs de Performance : + return `IDENTITÉ DU PRODUIT : +- Libellé : "${p.libelle1}" (Ref: ${p.codein}) +- Rayon : ${p.libelleNiveau2 || "Non classé"} +- Gamme actuelle : ${p.codeGamme ?? "Non définie"} + +DONNÉES FACTUELLES (À RESPECTER SCRUPULEUSEMENT) : - Score Global App : ${p.score.toFixed(1)}/100 - Régularité des ventes : ${p.regularityScore}/12 mois - Marge brute : ${p.tauxMarge.toFixed(1)}% +${volumeInfo}${projectionInfo}${activityAlert}${benchmarks} - Historique mensuel (Pondéré) : ${monthlySummary} Quelle est ta recommandation (A, C ou Z) et pourquoi ?`; diff --git a/task.md b/task.md index d45fd72..1f57a59 100644 --- a/task.md +++ b/task.md @@ -6,11 +6,11 @@ Create a PRD for a retail analytics application. The app will manage product col # Current Focus -Ajout d'un bouton de rafraîchissement des données dans la barre de filtres. +Analyse du déclenchement des scores lors du rafraîchissement SQL. - [x] Étape 11 : Polissage final et validation du PRD - [x] PRD Finalisé et prêt pour la suite (Architecture/UX) -- [x] Ajout du bouton de rafraîchissement SQL dans l'UI +- [x] Investigation et correction du bug de recommandation IA (Mary) # Progress Log