- Prompt_t1live.md : spec prompt examinateur T1, note anti-TD-22 (regle 7 du T2 non propagee), contrat WS complet (messages + close codes) pour le frontend 7b, comportement de fin (activityEnd final + relance terminale coupee). - TECH_DEBT-backend.md : TD-23 (non-determinisme Gemini Live T1 + decouverte flush inputTranscription a activityEnd en VAD manuel), TD-24 (dette nommage gate oral_t2_live couvrant aussi T1), TD-25 (dette DRY runT1LiveCorrection ~= runT2LiveCorrection, report conscient).
12 KiB
Prompt_t1live.md — Expria Backend
Spécification du prompt système T1 EO Live + contrat WebSocket
Document de référence — Sprint 7a À lire conjointement avec
Prompt_t2live.md(symétrie / divergences T1↔T2) etTECH_DEBT-backend.md(TD-22, TD-23, TD-24, TD-25). Source de vérité du prompt :buildT1SystemPromptdanssrc/lib/geminiLiveT1.ts.
⚠ NOTE LIMINAIRE — anti-TD-22 (symétrie avec
Prompt_t2live.md §3)La règle 7 du T2 (« STRICTE INTERDICTION DE POSER DES QUESTIONS / ban du point d'interrogation ») n'est PAS propagée au T1. En Tâche 1, l'examinateur DOIT relancer le candidat par des questions : c'est le cœur de son rôle. Le point d'interrogation est utilisé normalement.
Le prompt T1 (
buildT1SystemPrompt) et le prompt T2 (buildT2SystemPrompt) vivent dans des fonctions distinctes degeminiLive.ts/geminiLiveT1.tsprécisément pour éviter toute contamination de règle.
1. Contexte pédagogique
La Tâche 1 de l'Expression Orale TCF Canada est un entretien dirigé : le candidat se présente (identité, parcours, situation familiale, loisirs, projet d'immigration au Canada) sous forme de monologue, et l'examinateur le relance ponctuellement par des questions courtes pour approfondir.
Différence structurelle avec le T2 :
| Axe | T1 (entretien dirigé) | T2 (interaction de service) |
|---|---|---|
| Qui mène | L'examinateur relance | Le candidat mène |
| Questions de l'IA | Obligatoires (relances) | Interdites (rôle passif) |
| Forme candidat | Monologue + relances | Dialogue |
| Subject-based | Non (questionnaire candidat) | Oui (table sujets) |
2. Rôle de l'IA (examinateur)
L'IA joue un examinateur bienveillant du TCF Canada. Son comportement :
- Silencieux par défaut. Tant que le candidat parle, elle n'intervient jamais de sa propre initiative.
- Relance sur signal uniquement. Elle ne prend la parole que lorsque le
backend le lui signale (injection
clientContent). C'est le backend — via une horloge probabiliste — qui décide du TIMING ; l'examinateur, lui, formule librement une relance courte à partir de son contexte audio interne. Le backend ne lit PAS la transcription partielle pour décider (Modèle 1 acté — cf. ROADMAP /geminiLiveT1.ts). - Relance courte et unique. Une seule question de 10 à 20 mots, liée à ce que le candidat vient de dire ou à son contexte. Jamais d'enchaînement.
- Ton bienveillant et professionnel, français B2-C1.
- N'évalue jamais le candidat, ne corrige pas ses erreurs, ne commente pas sa langue.
- Ne sort jamais du rôle, ne mentionne jamais être une IA.
3. Prompt système (source : buildT1SystemPrompt)
Les variables ${...} sont substituées dynamiquement depuis les réponses du
questionnaire candidat (PresentationReponses) — il n'existe pas de sujet T1
en base (T1 EO n'est PAS subject-based).
RÔLE : Tu es un examinateur bienveillant de l'épreuve d'Expression Orale du TCF Canada (Tâche 1, entretien dirigé). Le candidat se présente en monologue : identité, parcours, situation familiale, loisirs, et projet d'immigration au Canada.
CONTEXTE DU CANDIDAT (pour formuler des relances pertinentes et personnalisées) :
- Identité : ${reponses.prenom_age_ville}
- Formation / métier : ${reponses.formation_metier}
- Situation familiale : ${reponses.situation_familiale}
- Loisirs : ${reponses.loisirs}
- Projet Canada : ${reponses.motivation_canada}
RÈGLES :
1. Tu parles TOUJOURS en français naturel et courant, niveau B2-C1, sur un ton bienveillant et professionnel.
2. Tu RESTES SILENCIEUX par défaut. Tant que le candidat parle, tu n'interviens JAMAIS de ta propre initiative.
3. Tu prends la parole UNIQUEMENT lorsqu'on te le signale, et alors UNIQUEMENT pour relancer le candidat par UNE question.
4. Ta relance est COURTE : une seule question de 10 à 20 mots, liée à ce que le candidat vient de dire ou à son contexte ci-dessus.
5. Tu PEUX et tu DOIS poser des questions : c'est le cœur de ton rôle d'examinateur en Tâche 1. Utilise le point d'interrogation normalement.
6. Une seule question à la fois. Jamais de liste, jamais d'enchaînement de plusieurs questions dans la même prise de parole.
7. Tu ne corriges JAMAIS les erreurs du candidat et tu ne commentes jamais sa langue, ses erreurs ou sa performance.
8. Tu restes toujours dans ton rôle d'examinateur. Tu ne mentionnes jamais que tu es une IA ou un modèle.
⚠ Spécificité T1 — règle 5 : elle est l'exact inverse de la règle 7 du T2. Toute fusion des deux prompts est interdite (TD-22 / TD-23).
Variables à substituer dynamiquement (depuis le questionnaire candidat, pas d'un sujet en base) :
prenom_age_ville,formation_metier,situation_familiale,loisirs,motivation_canada— validés parvalidateReponses(presentationController.ts).
4. Contrat WebSocket T1 (figé — la suite Sprint 7b en dépend)
Route : WS /t1/live?token=<jwt>
Auth : JWT Supabase + permission Premium oral_t2_live (réutilise
authenticate de t2live.ts — cf. dette de nommage TD-24).
4.1 Client → Backend
| Message | Forme | Effet |
|---|---|---|
| Contexte (1er message obligatoire) | {type:'context', reponses} |
Validé par validateReponses ; démarre la session Gemini. Absent/invalide → close 4004. |
| Audio candidat | {type:'audio', data} (PCM 16 kHz base64) |
Relayé à Gemini tant qu'un tour candidat est ouvert et qu'aucune interruption n'est en cours. |
| Fin de session | {type:'end'} |
Déclenche endSession() (flush terminal + correction). |
4.2 Backend → Client
| Message | Forme | Sens |
|---|---|---|
| Audio examinateur | frames Gemini verbatim (PCM 24 kHz) | Relances audio de l'examinateur. |
| Début d'interruption | {type:'interruption_start'} |
L'examinateur prend la parole ; le front doit suspendre la capture candidat. |
| Fin d'interruption | {type:'interruption_end'} |
Le candidat peut reprendre. |
| Avertissement temps | {type:'warning', message} |
30 s avant le timeout (T1_SESSION_WARNING_MS). |
| Rapport final | {type:'report', data} + close 1000 |
Évaluation EO_T1 prête. |
| Erreur applicative | {type:'error', code, message} |
Codes : EMPTY_TRANSCRIPT, PERSISTENCE_FAILED, CORRECTION_FAILED. |
4.3 Codes de fermeture WebSocket
| Close code | Cause | Origine |
|---|---|---|
| 1000 | Fin normale + rapport prêt (ou transcript vide) | runT1LiveCorrection |
| 1011 | PERSISTENCE_FAILED / CORRECTION_FAILED |
runT1LiveCorrection |
| 4001 | AUTH_REQUIRED (JWT absent/invalide) |
authenticate |
| 4003 | PLAN_INSUFFICIENT (pas Premium) |
authenticate |
| 4004 | CONTEXT_MISSING (1er message contexte absent/invalide) |
route t1live.ts |
| 4005 | GEMINI_CONFIG (clé API Gemini manquante côté serveur) |
openGeminiLiveT1Session |
| 4006 | GEMINI_DISCONNECTED (WS Gemini fermé/erreur) |
openGeminiLiveT1Session |
5. Comportement de fin de session (flush terminal)
Contrainte VAD manuel (découverte spike — TD-23). En VAD manuel
(realtimeInputConfig.automaticActivityDetection.disabled = true), Gemini ne
flushe inputTranscription (le texte candidat) qu'à l'envoi d'un
activityEnd, pas en continu. Le backend doit donc envoyer un activityEnd
FINAL aux bornes de tour pour récupérer le dernier segment candidat.
Effet de bord et son traitement. Cet activityEnd final déclenche AUSSI une
relance examinateur « terminale » non désirée. Elle est coupée :
- L'audio de cette relance terminale (
modelTurn … inlineData) n'est pas forwardé au client (le candidat ne l'entend jamais). - Le texte de cette relance terminale (
outputTranscription) est jeté (non ajouté au transcript). - Seul le texte candidat final (
inputTranscription) est conservé pour l'évaluation.
Le tri se fait champ par champ (pas message par message), car le segment
candidat à garder et la relance terminale à couper peuvent arriver dans le
même message Gemini. Implémentation : flag terminalFlush +
T1_TERMINAL_FLUSH_GRACE_MS (3 s) avant finalize()
(cf. geminiLiveT1.ts).
6. Évaluation finale (pipeline post-session)
runT1LiveCorrection (src/routes/t1live.ts) :
- Insert
productions:tache='EO_T1',sujet_id=null(T1 non subject-based),mode='entrainement',contenu=transcript. correctEO(transcript, 'EO_T1', 9, null)(DeepSeek — pas de consigne de sujet en T1).- Phonologie =
PHONOLOGY_STUB(TD-08 — pas d'audio brut côté backend) : score textuel /16 + phonologie /4 = /20. - Update
productions(rapport,score,nclc). {type:'report', data}+ close 1000.
Rappel TD-08 : la phonologie live reste gelée (stub) tant qu'aucun audio brut n'est bufferisé côté backend.
7. Spécifications audio
| Direction | Format | Sample rate | Encoding |
|---|---|---|---|
| Frontend → Gemini | PCM brut | 16 kHz | 16 bits, little-endian, mono |
| Gemini → Frontend | PCM brut | 24 kHz | 16 bits, little-endian, mono |
MIME envoyé à Gemini : audio/pcm;rate=16000 (T1_INPUT_AUDIO_MIME).
8. Constantes de session (source : geminiLiveT1.ts)
| Constante | Valeur | Rôle |
|---|---|---|
T1_SESSION_TIMEOUT_MS |
180 000 | Filet de sécurité (fin forcée). |
T1_SESSION_WARNING_MS |
150 000 | Émet {type:'warning'} 30 s avant timeout. |
T1_INTERRUPTION_P0/P1/P2 |
0.2 / 0.6 / 0.2 | Distribution du nombre de relances (0/1/2). |
T1_INTERRUPTION_WINDOW_START/END_MS |
25 000 / 75 000 | Fenêtre où placer les relances. |
T1_INTERRUPTION_MIN_SPACING_MS |
20 000 | Espacement minimal entre 2 relances. |
T1_TERMINAL_FLUSH_GRACE_MS |
3 000 | Délai après activityEnd final avant finalize. |