CognxSafeTrack commited on
Commit ·
9ca5873
1
Parent(s): 4967196
fix(core): remove AI jus-de-fruits hallucination, expand onboarding sectors to 8, streamline pedagogy menu and lock REPRISE badge
Browse files
apps/api/src/services/ai/index.ts
CHANGED
|
@@ -117,8 +117,9 @@ ${criteriaContext}
|
|
| 117 |
|
| 118 |
1. RÈGLE D'OR : ANTI-HALLUCINATION
|
| 119 |
Agnosticisme Sectoriel : Tu ne dois JAMAIS utiliser d'exemples liés aux forages, à l'irrigation, aux agriculteurs ou aux fourneaux, SAUF si l'activité déclarée de l'étudiant (${activityLabel}) y est explicitement liée.
|
| 120 |
-
Zéro-Shot Contextuel :
|
| 121 |
-
|
|
|
|
| 122 |
|
| 123 |
2. LOGIQUE D'ÉVALUATION
|
| 124 |
Pour chaque feedback, base-toi strictement sur les CRITÈRES D'ÉVALUATION fournis :
|
|
@@ -190,10 +191,11 @@ ${businessContext}
|
|
| 190 |
${prevContext}
|
| 191 |
|
| 192 |
1. RÈGLE D'OR : ANTI-HALLUCINATION
|
| 193 |
-
Adapte TOUS les exemples de la leçon pour qu'ils soient directement liés à la véritable activité de l'étudiant (${activityLabel}).
|
| 194 |
Agnosticisme Sectoriel : Tu ne dois JAMAIS utiliser d'exemples liés aux forages, à l'irrigation, aux agriculteurs ou aux fourneaux, SAUF si l'activité déclarée de l'étudiant (${activityLabel}) y est explicitement liée.
|
| 195 |
-
Zéro-Shot Contextuel :
|
| 196 |
-
|
|
|
|
| 197 |
|
| 198 |
2. NORMALISATION WOLOF & TONE
|
| 199 |
STT Cleanup : Applique les règles de normalisation Wolof (ex: 'damae' -> 'damay', 'jendi' -> 'jënd') dans ton texte.
|
|
|
|
| 117 |
|
| 118 |
1. RÈGLE D'OR : ANTI-HALLUCINATION
|
| 119 |
Agnosticisme Sectoriel : Tu ne dois JAMAIS utiliser d'exemples liés aux forages, à l'irrigation, aux agriculteurs ou aux fourneaux, SAUF si l'activité déclarée de l'étudiant (${activityLabel}) y est explicitement liée.
|
| 120 |
+
Zéro-Shot Contextuel : Adapte tous tes exemples UNIQUEMENT au métier réel de l'étudiant (${activityLabel}). Interdiction formelle d'inventer un secteur d'activité.
|
| 121 |
+
Règle de Sécurité : Si l'utilisateur n'a pas défini son projet ou si l'activité est inconnue, utilise le terme générique "ton business" et reste sur des principes théoriques abstraits.
|
| 122 |
+
Interdiction de généralisation paresseuse : Reste concis, précis et ancré dans la réalité de l'utilisateur.
|
| 123 |
|
| 124 |
2. LOGIQUE D'ÉVALUATION
|
| 125 |
Pour chaque feedback, base-toi strictement sur les CRITÈRES D'ÉVALUATION fournis :
|
|
|
|
| 191 |
${prevContext}
|
| 192 |
|
| 193 |
1. RÈGLE D'OR : ANTI-HALLUCINATION
|
| 194 |
+
Adapte TOUS les exemples de la leçon pour qu'ils soient directement liés à la véritable activité de l'étudiant (${activityLabel}).
|
| 195 |
Agnosticisme Sectoriel : Tu ne dois JAMAIS utiliser d'exemples liés aux forages, à l'irrigation, aux agriculteurs ou aux fourneaux, SAUF si l'activité déclarée de l'étudiant (${activityLabel}) y est explicitement liée.
|
| 196 |
+
Zéro-Shot Contextuel : Adapte tous tes exemples UNIQUEMENT au métier réel de l'étudiant (${activityLabel}). Interdiction formelle d'inventer un secteur d'activité.
|
| 197 |
+
Règle de Sécurité : Si l'utilisateur n'a pas défini son projet ou si l'activité est inconnue, utilise le terme générique "ton business" et reste sur des principes théoriques abstraits.
|
| 198 |
+
Interdiction de généralisation paresseuse : Reste direct et ancré dans le métier de l'utilisateur. Garde la VALEUR PÉDAGOGIQUE EXACTEMENT la même.
|
| 199 |
|
| 200 |
2. NORMALISATION WOLOF & TONE
|
| 201 |
STT Cleanup : Applique les règles de normalisation Wolof (ex: 'damae' -> 'damay', 'jendi' -> 'jënd') dans ton texte.
|
apps/api/src/services/whatsapp.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
import { prisma } from './prisma';
|
| 2 |
-
import { scheduleMessage, enrollUser, whatsappQueue, scheduleInteractiveButtons } from './queue';
|
| 3 |
|
| 4 |
export class WhatsAppService {
|
| 5 |
private static normalizeCommand(text: string): string {
|
|
@@ -223,11 +223,25 @@ export class WhatsAppService {
|
|
| 223 |
? "Parfait, nous allons continuer en Français ! 🇫🇷\nDans quel domaine d'activité te trouves-tu ?"
|
| 224 |
: "Baax na, dinanu wéy ci Wolof ! 🇸🇳\nCi ban mbir ngay yëngu ?";
|
| 225 |
|
| 226 |
-
await
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 231 |
return;
|
| 232 |
}
|
| 233 |
|
|
|
|
| 1 |
import { prisma } from './prisma';
|
| 2 |
+
import { scheduleMessage, enrollUser, whatsappQueue, scheduleInteractiveButtons, scheduleInteractiveList } from './queue';
|
| 3 |
|
| 4 |
export class WhatsAppService {
|
| 5 |
private static normalizeCommand(text: string): string {
|
|
|
|
| 223 |
? "Parfait, nous allons continuer en Français ! 🇫🇷\nDans quel domaine d'activité te trouves-tu ?"
|
| 224 |
: "Baax na, dinanu wéy ci Wolof ! 🇸🇳\nCi ban mbir ngay yëngu ?";
|
| 225 |
|
| 226 |
+
await scheduleInteractiveList(
|
| 227 |
+
user.id,
|
| 228 |
+
newLang === 'FR' ? "Ton secteur" : "Sa Mbir",
|
| 229 |
+
promptText,
|
| 230 |
+
newLang === 'FR' ? "Secteurs" : "Tànn",
|
| 231 |
+
[{
|
| 232 |
+
title: newLang === 'FR' ? 'Liste' : 'Mbir',
|
| 233 |
+
rows: [
|
| 234 |
+
{ id: 'SEC_COMMERCE', title: newLang === 'FR' ? 'Commerce / Vente' : 'Njaay' },
|
| 235 |
+
{ id: 'SEC_AGRI', title: newLang === 'FR' ? 'Agri / Élevage' : 'Mbay / Samm' },
|
| 236 |
+
{ id: 'SEC_FOOD', title: newLang === 'FR' ? 'Alimentation / Rest.' : 'Lekk / Restauration' },
|
| 237 |
+
{ id: 'SEC_COUTURE', title: newLang === 'FR' ? 'Couture / Mode' : 'Couture' },
|
| 238 |
+
{ id: 'SEC_BEAUTE', title: newLang === 'FR' ? 'Beauté / Bien-être' : 'Rafet' },
|
| 239 |
+
{ id: 'SEC_TRANSPORT', title: newLang === 'FR' ? 'Transport / Livr.' : 'Transport / Yëgël' },
|
| 240 |
+
{ id: 'SEC_TECH', title: newLang === 'FR' ? 'Tech / Digital' : 'Tech / Digital' },
|
| 241 |
+
{ id: 'SEC_AUTRE', title: newLang === 'FR' ? 'Autre secteur' : 'Beneen mbir' }
|
| 242 |
+
]
|
| 243 |
+
}]
|
| 244 |
+
);
|
| 245 |
return;
|
| 246 |
}
|
| 247 |
|
apps/whatsapp-worker/src/pedagogy.ts
CHANGED
|
@@ -126,8 +126,18 @@ export async function sendLessonDay(userId: string, trackId: string, dayNumber:
|
|
| 126 |
let badgeText = '';
|
| 127 |
const badges = (userProgress?.badges as string[]) || [];
|
| 128 |
if (badges.length > 0) {
|
| 129 |
-
|
| 130 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
}
|
| 132 |
|
| 133 |
const dayDisplay = dayNumber === 1.5 ? '1bis' : Math.floor(dayNumber).toString();
|
|
@@ -273,39 +283,39 @@ export async function sendLessonDay(userId: string, trackId: string, dayNumber:
|
|
| 273 |
|
| 274 |
// 🌟 3. Send Action LIST menu ────────────────────────────────────────────────
|
| 275 |
// Shown after every lesson so the user knows their options
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
]
|
| 307 |
-
|
| 308 |
-
|
| 309 |
|
| 310 |
// 🌟 4. Update User Progress to PENDING 🌟
|
| 311 |
await prisma.userProgress.upsert({
|
|
|
|
| 126 |
let badgeText = '';
|
| 127 |
const badges = (userProgress?.badges as string[]) || [];
|
| 128 |
if (badges.length > 0) {
|
| 129 |
+
let lastBadge = badges[badges.length - 1];
|
| 130 |
+
|
| 131 |
+
// 🚨 REMEDIATION BADGE GUARD: Hide 'REPRISE' on normal integer days
|
| 132 |
+
if (lastBadge === 'REPRISE' && dayNumber % 1 === 0) {
|
| 133 |
+
const nonRepriseBadges = badges.filter(b => b !== 'REPRISE');
|
| 134 |
+
if (nonRepriseBadges.length > 0) {
|
| 135 |
+
lastBadge = nonRepriseBadges[nonRepriseBadges.length - 1];
|
| 136 |
+
badgeText = `\nBadge : ${lastBadge} ${BADGE_EMOJIS[lastBadge] || '🏅'}`;
|
| 137 |
+
}
|
| 138 |
+
} else {
|
| 139 |
+
badgeText = `\nBadge : ${lastBadge} ${BADGE_EMOJIS[lastBadge] || '🏅'}`;
|
| 140 |
+
}
|
| 141 |
}
|
| 142 |
|
| 143 |
const dayDisplay = dayNumber === 1.5 ? '1bis' : Math.floor(dayNumber).toString();
|
|
|
|
| 283 |
|
| 284 |
// 🌟 3. Send Action LIST menu ────────────────────────────────────────────────
|
| 285 |
// Shown after every lesson so the user knows their options
|
| 286 |
+
if (dayNumber === 1) {
|
| 287 |
+
// Direct invitation to respond for Day 1 to reduce friction
|
| 288 |
+
await sendTextMessage(
|
| 289 |
+
user.phone,
|
| 290 |
+
isWolof
|
| 291 |
+
? "🎙️ Lëjj bi: Tontul kàddu gi ci dëbb (vocal) walla mbind (texte)."
|
| 292 |
+
: "🎙️ À toi de jouer ! Réponds à l'exercice ci-dessus par message vocal ou texte."
|
| 293 |
+
);
|
| 294 |
+
} else {
|
| 295 |
+
await sendInteractiveListMessage(
|
| 296 |
+
user.phone,
|
| 297 |
+
isWolof ? `Jour ${dayNumber}` : `Leçon ${dayNumber}`,
|
| 298 |
+
isWolof
|
| 299 |
+
? "Seetee ci suuf ban jëf ngay def :"
|
| 300 |
+
: "Que veux-tu faire maintenant ?",
|
| 301 |
+
isWolof ? "Tànnal" : "Choisir",
|
| 302 |
+
[{
|
| 303 |
+
title: isWolof ? "Jëfandikoo" : "Actions",
|
| 304 |
+
rows: [
|
| 305 |
+
{
|
| 306 |
+
id: `DAY${dayNumber}_REPLAY`,
|
| 307 |
+
title: isWolof ? "🎧 Dégg ci kanam" : "🎧 Réécouter",
|
| 308 |
+
description: isWolof ? "Waxtu bi ci kaw" : "Réécouter la leçon"
|
| 309 |
+
},
|
| 310 |
+
{
|
| 311 |
+
id: `DAY${dayNumber}_EXERCISE`,
|
| 312 |
+
title: isWolof ? "🎙️ Yónni tontu" : "🎙️ Répondre",
|
| 313 |
+
description: isWolof ? "Dëbb (vocal) walla mbind" : "Message vocal ou texte"
|
| 314 |
+
}
|
| 315 |
+
]
|
| 316 |
+
}]
|
| 317 |
+
);
|
| 318 |
+
}
|
| 319 |
|
| 320 |
// 🌟 4. Update User Progress to PENDING 🌟
|
| 321 |
await prisma.userProgress.upsert({
|