DOCS/v2/talking-head-plan.md
Thomas Fransolet a5a8ecdb20 Documentation interne MyInfoMate / Unov
Import initial de la documentation : statut, roadmap, plans V1/V2,
specs verticales (creche, sport), audits securite, plan de test,
analyse concurrentielle et maquettes de design.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 11:17:01 +02:00

151 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Avatar Parlant (Talking Head) — Plan d'intégration
> **Contexte** : Delta technique #6 issu de `myinfomate-ai-persona-analysis.md`.
> Feature V2 — app mobile et borne uniquement. Hors scope smart glasses (pas d'écran).
---
## Concept
Portrait 2D animé par lipsync synchronisé au TTS pré-généré ou dynamique.
3 frames PNG par persona, animées côté Flutter selon les timestamps retournés par Google Cloud TTS.
---
## Assets par persona
| Frame | État bouche | Usage |
|---|---|---|
| `frame_0.png` | Fermée | Idle, fin de phrase, consonnes labiales (M, B, P) |
| `frame_1.png` | Mi-ouverte | Consonnes courantes, transition |
| `frame_2.png` | Ouverte | Voyelles (A, E, I, O, U) |
### Modèle de génération — À tester
Deux candidats dans l'écosystème Google (même billing GCP que Gemini et Cloud TTS) :
| Modèle | Prix/image | 3 frames | Notes |
|---|---|---|---|
| **Gemini 2.5 Flash Image** | $0.039 | ~$0.12 | Bon rapport qualité/prix |
| **Gemini 3 Pro Image** | $0.134 | ~$0.40 | Meilleure qualité, meilleure consistance de style |
**Test à faire** : générer les 3 frames d'un même personnage avec chaque modèle et comparer la **cohérence de style entre les frames** (critère numéro 1 pour un lipsync crédible). Si Gemini 3 Pro est significativement plus consistant, le surcoût de $0.28 par persona est négligeable.
Par défaut dans le code : **Gemini 2.5 Flash Image**, configurable via constante pour switcher facilement.
### Prompts de génération
Trois appels séquentiels — les prompts 2 et 3 **doivent** envoyer l'image de référence (frame 0) pour garantir la cohérence visuelle.
**Prompt 1 — Base (bouche fermée)**
```
2D cartoon illustration of a friendly [description du persona],
warm smile, mouth closed, facing slightly to the right,
bust portrait, clean white background, consistent art style,
flat colors, soft shading. Reference sheet style.
```
**Prompt 2 — Mi-ouverte** *(+ image de référence)*
```
Same character as the reference image, exact same art style,
same colors, same angle, same lighting,
mouth slightly open as if mid-speech.
```
**Prompt 3 — Ouverte** *(+ image de référence)*
```
Same character as the reference image, exact same art style,
same colors, same angle, same lighting,
mouth open as if speaking a vowel sound.
```
### Coût
- Gemini 2.5 Flash : ~$0.12 par persona (one-shot)
- Gemini 3 Pro : ~$0.40 par persona (one-shot)
---
## Stockage Firebase
```
{instanceId}/personas/{wakewordId}/frame_0.png ← bouche fermée
{instanceId}/personas/{wakewordId}/frame_1.png ← mi-ouverte
{instanceId}/personas/{wakewordId}/frame_2.png ← ouverte
```
Les URLs sont stockées dans la config du persona (aux côtés de `VoiceName` et `PersonaPrompt`).
---
## UX dans le CMS (manager-app)
Dans la page "Configuration du guide IA", section par persona :
1. Champ `PersonaPrompt` (description du personnage : "chevalier médiéval jovial")
2. Bouton **"Générer l'avatar"** → appel backend → DALL-E 3 × 3 → upload Firebase → prévisualisation des 3 frames
3. Possibilité de re-générer (coût ~$0.12 à chaque fois, à afficher clairement)
4. Prévisualisation : animation en loop des 3 frames dans le CMS pour validation
---
## Lipsync Flutter
### Mode simple — loop (V2 MVP)
```dart
// Pendant la lecture audio : boucle les 3 frames
Timer.periodic(Duration(milliseconds: 90), (timer) {
if (!isPlaying) { setState(() => currentFrame = 0); timer.cancel(); return; }
setState(() => currentFrame = (currentFrame + 1) % 3);
});
```
~30 lignes, zéro dépendance. Suffisant pour une première version convaincante.
### Mode avancé — word timestamps (V2+)
Google Cloud TTS avec `enable_time_pointing: SSML_MARK` (ou `WORD`) retourne des timestamps par mot.
Mapping côté Flutter :
```dart
String getFirstSignificantChar(String word) => word.toUpperCase().trimLeft()[0];
int frameForWord(String word) {
final c = getFirstSignificantChar(word);
if ('MBP'.contains(c)) return 0; // bouche fermée
if ('AEIOU'.contains(c)) return 2; // bouche ouverte
return 1; // mi-ouverte
}
```
À chaque timestamp : `setState(() => currentFrame = frameForWord(word))`.
> Note : `enable_time_pointing` retourne des timestamps au niveau du **mot**, pas du phonème. Le mapping par première lettre est une approximation raisonnable — suffisant visuellement à la vitesse de lecture normale.
---
## Intégration avec le TTS pré-généré
Pour le contenu pré-généré (articles, POIs), les timestamps sont générés **en même temps que le MP3** et stockés à côté :
```
{instanceId}/tts/article/{sectionId}/{wakewordId}/{lang}.mp3
{instanceId}/tts/article/{sectionId}/{wakewordId}/{lang}.json ← timestamps
```
Le JSON contient la liste `[{word, startTime, endTime}]` — Flutter le charge une fois et l'utilise pour l'animation.
Pour les **questions libres** (TTS dynamique), les timestamps sont retournés en même temps que l'audio dans la réponse de l'endpoint `/ask`.
---
## Dépendances avec les autres deltas
| Delta | Lien |
|---|---|
| #2 — Persona config | `PersonaConfig` stocke les URLs des 3 frames + le `PersonaPrompt` utilisé pour DALL-E |
| #4 — TTS pré-généré | Génère le MP3 + timestamps en même temps ; même job Hangfire |
| #3 — Endpoint `/ask` | Retourne audio + timestamps pour questions libres |
| #5 — Canal Flutter | Intègre le widget Talking Head ; smart glasses = hors scope |