CognxSafeTrack commited on
Commit
74d7942
·
1 Parent(s): d9879cf

feat(ai): refactor coach feedback to 3-pillar format with mandatory browsing (Sprint 39)

Browse files
apps/api/src/routes/ai.ts CHANGED
@@ -223,13 +223,13 @@ export async function aiRoutes(fastify: FastifyInstance) {
223
  );
224
 
225
  // 🌟 Standard Feedback UX: 3 lines 🌟
226
- // 1. Encouragement/Validation (PRAISE)
227
- // 2. Diagnostic (REPHRASE)
228
- // 3. Action / Help (ACTION)
229
  const formattedFeedback = `✨ *Coach XAMLÉ* :\n\n` +
230
- `🌟 ${feedback.praise}\n` +
231
- `📝 ${feedback.rephrase}\n` +
232
- `💡 ${feedback.action}`;
233
 
234
  return {
235
  success: true,
 
223
  );
224
 
225
  // 🌟 Standard Feedback UX: 3 lines 🌟
226
+ // 1. Encouragement/Validation (VALIDATION)
227
+ // 2. Diagnostic (ENRICHED VERSION)
228
+ // 3. Action / Help (ACTIONABLE ADVICE)
229
  const formattedFeedback = `✨ *Coach XAMLÉ* :\n\n` +
230
+ `🌟 ${feedback.validation}\n\n` +
231
+ `🚀 ${feedback.enrichedVersion}\n\n` +
232
+ `💡 ${feedback.actionableAdvice}`;
233
 
234
  return {
235
  success: true,
apps/api/src/services/ai/__fixtures__/mock-data.ts CHANGED
@@ -86,9 +86,9 @@ export const MOCK_DECK_COUTURE = {
86
 
87
  export const MOCK_FEEDBACK = {
88
  isQualified: true,
89
- praise: "Excellent travail ! Ta vision est claire et ambitieuse.",
90
- rephrase: "Tu proposes donc une solution innovante pour le marché local.",
91
- action: "Continue ainsi pour l'étape suivante !",
92
  confidence: 95,
93
  notes: "Réponse solide.",
94
  missingElements: []
 
86
 
87
  export const MOCK_FEEDBACK = {
88
  isQualified: true,
89
+ validation: "Excellent travail ! Ta vision est claire et ambitieuse (🌟 Validation).",
90
+ enrichedVersion: "Tu proposes donc une solution innovante pour le marché local qui répond aux 15% de déficit observé par l'ANSD (🚀 Enrichissement).",
91
+ actionableAdvice: "Continue ainsi pour l'étape suivante, et focalise-toi sur ta supply chain B2B (💡 Conseil).",
92
  confidence: 95,
93
  notes: "Réponse solide.",
94
  missingElements: []
apps/api/src/services/ai/index.ts CHANGED
@@ -138,19 +138,18 @@ class AIService {
138
 
139
  let searchContext = '';
140
  let searchResults: any[] | undefined = undefined;
141
- if (dayNumber === 2 || dayNumber === 5 || dayNumber === 10) {
142
- console.log(`[AI_SERVICE] 🔍 Triggering Market Search for Day ${dayNumber}...`);
143
- const query = `${activityLabel} ${region} Sénégal marché concurrence`;
144
- try {
145
- const results = await searchService.search(query);
146
- if (results && results.length > 0) {
147
- searchResults = results;
148
- searchContext = `\n🌐 DONNÉES DE MARCHÉ RÉELLES (Google Search) :\n${results.map(r => `- ${r.title}: ${r.snippet}`).join('\n')}\n`;
149
- console.log(`[AI_SERVICE] ✅ Search enrichment added (${results.length} results).`);
150
- }
151
- } catch (err) {
152
- console.error('[AI_SERVICE] Search enrichment failed:', err);
153
  }
 
 
154
  }
155
 
156
  const criteriaContext = exerciseCriteria
@@ -174,22 +173,22 @@ class AIService {
174
  RÉPONSE DE L'ÉTUDIANT :
175
  "${userInput}"
176
 
177
- MISSIONS STRATÉGIQUES "PREMIUM V4" :
178
- 1. Évaluation : Est-ce valide selon les critères ?
179
- 2. Extraction Métriques : Concurrence (J10), Projections (J11), The Ask (J12).
180
- 3. Feedback "Mini-Cours" : Rédige une Reformulation stratégique, un Praise enthousiaste et une Action (prochaine étape).
181
 
182
- RÈGLE DE DENSITÉ RÉDACTIONNELLE :
183
- - Le feedback ne doit pas être un simple 'bien'. Chaque réponse doit expliquer le POURQUOI stratégique (impact sur le business).
 
 
 
 
 
 
 
184
 
185
- PROTOCOLE DATA STEWARD (Rigueur) :
186
- - Cascade de Recherche : Régional > National > UEMOA. Interdiction du Niveau 1 (Quartier).
187
- - TRANSPARENCE : Cite toujours la source (ex: ANSD).
188
- - HONNÊTETÉ : Si aucune donnée fiable n'est trouvée, propose l'estimation de l'utilisateur.
189
-
190
- TON INSTITUTIONNEL (Browsing) :
191
- Utilise les données de marché pour impressionner l'élève :
192
- "Félicitations ! Ton approche est validée par les chiffres : selon [Source], le marché de [Secteur] est évalué à [Valeur]. J'ai intégré cette preuve de marché dans ta Slide 5."
193
 
194
  DÉTANCHÉITÉ LINGUISTIQUE : 100% ${userLanguage === 'WOLOF' ? 'WOLOF (ñ, ë, é)' : 'Français institutionnel'}. Zéro mélange.
195
  `;
 
138
 
139
  let searchContext = '';
140
  let searchResults: any[] | undefined = undefined;
141
+ // 🚀 Brique 1: Activation du Browsing (Obligatoire)
142
+ console.log(`[AI_SERVICE] 🔍 Triggering Market Search for Day ${dayNumber} (Always ON)...`);
143
+ const query = `${activityLabel} ${region} Sénégal marché chiffres statistiques data`;
144
+ try {
145
+ const results = await searchService.search(query);
146
+ if (results && results.length > 0) {
147
+ searchResults = results;
148
+ searchContext = `\n🌐 DONNÉES DE MARCHÉ RÉELLES (Google Search) :\n${results.map(r => `- ${r.title}: ${r.snippet}`).join('\n')}\n`;
149
+ console.log(`[AI_SERVICE] ✅ Search enrichment added (${results.length} results).`);
 
 
 
150
  }
151
+ } catch (err) {
152
+ console.error('[AI_SERVICE] Search enrichment failed:', err);
153
  }
154
 
155
  const criteriaContext = exerciseCriteria
 
173
  RÉPONSE DE L'ÉTUDIANT :
174
  "${userInput}"
175
 
176
+ MISSIONS STRATÉGIQUES "SENIOR BUSINESS COACH" (CORRECTION RADICALE) :
177
+ Tu es un consultant pragmatique, pas un professeur pointilleux. Rédige un feedback d'au minimum 15 lignes de haute densité.
 
 
178
 
179
+ LES 3 PILIERS OBLIGATOIRES DU FEEDBACK :
180
+ 1. 🌟 Validation (Pilier 1) : Félicite et valide l'idée de l'utilisateur avec l'enthousiasme d'un investisseur.
181
+ 2. 🚀 Version Enrichie (Pilier 2) : Réécris sa phrase de manière exécutive en y intégrant OBLIGATOIREMENT des données chiffrées réelles trouvées dans ta recherche (ex: taille du marché ANSD, nombre de boutiques) et des termes stratégiques (supply chain, B2B, rétention).
182
+ 3. 💡 Conseil Actionnable (Pilier 3) : Donne un conseil de terrain hyper concret basé là aussi sur la recherche web (ex: "Conseil : Le créneau 6h-8h est critique dans les boutiques de Dakar...").
183
+
184
+ ⚠️ INTERDICTION ABSOLUE (Anti-Remediation Loop) :
185
+ - Tu NE DOIS PLUS JAMAIS demander à l'utilisateur de s'expliquer davantage (ex: "Quel est l'âge exact ?", "Combien gagnes-tu ?").
186
+ - S'il manque un détail mais que l'idée est claire, c'est TOI qui apportes le savoir (donne les tranches d'âges classiques du secteur, donne les revenus moyens du pays).
187
+ - Tu es là pour ENRICHIR sa vision, pas pour lui faire passer un interrogatoire. Ne pose AUCUNE question bloquante à la fin.
188
 
189
+ PROTOCOLE DATA STEWARD (Intégrité Géographique) :
190
+ - Si l'utilisateur est à ${region}, donne lui les vrais chiffres de ${region}. S'ils n'existent pas, back-up sur les chiffres du Sénégal.
191
+ - TRANSPARENCE : Cite toujours la source formelle (ex: Agence Nationale de la Statistique et de la Démographie, Banque Mondiale, Direction du commerce, etc.).
 
 
 
 
 
192
 
193
  DÉTANCHÉITÉ LINGUISTIQUE : 100% ${userLanguage === 'WOLOF' ? 'WOLOF (ñ, ë, é)' : 'Français institutionnel'}. Zéro mélange.
194
  `;
apps/api/src/services/ai/types.ts CHANGED
@@ -50,9 +50,9 @@ export type PitchDeckData = z.infer<typeof PitchDeckSchema>;
50
 
51
  // Schema for AI Feedback (Coach)
52
  export const FeedbackSchema = z.object({
53
- rephrase: z.string().describe("Synthesized version of the student's answer"),
54
- praise: z.string().describe("Positive reinforcement"),
55
- action: z.string().describe("Next pedagogical step or correction"),
56
  isQualified: z.boolean().describe("Whether the answer meets the lesson's criteria"),
57
  missingElements: z.array(z.string()).describe("List of IDs from criteria that were not met"),
58
  confidence: z.number().min(0).max(100).describe("AI confidence in the evaluation"),
 
50
 
51
  // Schema for AI Feedback (Coach)
52
  export const FeedbackSchema = z.object({
53
+ validation: z.string().describe("Félicitations et validation claire de la réponse de l'utilisateur (Pilier 1)"),
54
+ enrichedVersion: z.string().describe("Réécriture de la phrase y intégrant des données chiffrées réelles et stratégiques issues de la recherche (Pilier 2)"),
55
+ actionableAdvice: z.string().describe("Conseil terrain concret, actionnable et documenté pour cette étape (Pilier 3)"),
56
  isQualified: z.boolean().describe("Whether the answer meets the lesson's criteria"),
57
  missingElements: z.array(z.string()).describe("List of IDs from criteria that were not met"),
58
  confidence: z.number().min(0).max(100).describe("AI confidence in the evaluation"),
apps/whatsapp-worker/src/index.ts CHANGED
@@ -89,8 +89,8 @@ const worker = new Worker('whatsapp-queue', async (job: Job) => {
89
 
90
  if (feedbackRes.ok) {
91
  feedbackData = await feedbackRes.json();
92
- if (feedbackData.rephrase && feedbackData.praise && feedbackData.action) {
93
- feedbackMsg = `${feedbackData.praise}\n\n${feedbackData.rephrase}\n\n💡 *Action :* ${feedbackData.action}`;
94
  } else if (feedbackData.text) {
95
  feedbackMsg = feedbackData.text;
96
  } else {
 
89
 
90
  if (feedbackRes.ok) {
91
  feedbackData = await feedbackRes.json();
92
+ if (feedbackData.validation && feedbackData.enrichedVersion && feedbackData.actionableAdvice) {
93
+ feedbackMsg = `🌟 ${feedbackData.validation}\n\n🚀 ${feedbackData.enrichedVersion}\n\n💡 Conseil de Terrain :\n${feedbackData.actionableAdvice}`;
94
  } else if (feedbackData.text) {
95
  feedbackMsg = feedbackData.text;
96
  } else {