Résultats du calibrage (61 générations, coûts recoupés sur l'export fal), trois corrections de prompt, unité 1 crédit = 0,015 $ vendue avec marge, échantillons et figures d'archétypes embarqués ; planches contact dans outputs/studio-calibrage-2026-09-15. STATUS, roadmap, cartes 280 et 310 à jour. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011VxSQeGQYUvPmSEoGdnidA
20 lines
5.8 KiB
Markdown
20 lines
5.8 KiB
Markdown
---
|
|
title: Personnages — un objet, quatre facettes
|
|
area: visitapp backend
|
|
horizon: v2
|
|
tags: 3 repos, Studio IA
|
|
flag: warn | V2 · remplace le talking head
|
|
src: v2/studio-plan.md §3.8 — lots 7 et 8
|
|
---
|
|
<p>🛠️ <strong>Lot 7 en cours (15/09).</strong> Entité <code>Persona</code>, écran Personnages, <code>TtsVoice</code> en base, migration des colonnes <code>Instance.Guide*</code> faits. Figures des 12 archétypes générées et embarquées. <strong>Reste</strong> : l'écoute des voix (échantillons audio à produire, pas de clé Gemini en local), puis le lot 8 (narration attribuée).</p>
|
|
<p><strong>Trois plans décrivaient le même objet sans se croiser</strong> (relevé le 01-02/09) : le canon visuel (<code>studio-plan.md</code>), les 3 frames de lipsync (<code>talking-head-plan.md</code>) et <code>PersonaConfig { WakewordId, GuideName, PersonaPrompt, VoiceName }</code> (<code>tts-pregenerated-plan.md</code>). Une entité <code>Persona</code> les remplace : identité, visage (canon en <em>jeu de vues</em>), voix + intonation, parole.</p>
|
|
<p>⛔ <strong>Le talking head est abandonné, sa dépendance était déjà cassée.</strong> <code>talking-head-plan.md:11</code> anime ses frames « selon les timestamps retournés par <strong>Google Cloud TTS</strong> » (<code>enable_time_pointing</code>) — or le code livré tourne sur <strong>Gemini TTS</strong> (<code>GeminiTtsEngine</code>, <code>gemini-2.5-flash-preview-tts</code>) : aucune source de timestamps. Remplacé par le <strong>portrait canon statique</strong> à côté du lecteur audio, puis une boucle vidéo 6-8 s si du mouvement est voulu. En-tête d'abandon posé sur le fichier.</p>
|
|
<p>⚠️ <strong>« Le visiteur choisit entre Viva et Marco » était un choix de voix déguisé en choix de guide.</strong> Le besoin réel est celui du <strong>Bastogne War Museum</strong> : 3-4 personnages qui narrent chacun <em>leurs</em> stations, avec leur voix et leur intonation. Le conservateur assigne (<code>NarratorPersonaId</code> sur <code>GuidedStep</code>/<code>GeoPoint</code>/<code>SectionArticle</code>), le visiteur ne choisit rien. <strong>Bonne nouvelle : ça divise le stockage TTS par deux au lieu de le multiplier</strong> — chaque contenu n'existe plus qu'en une voix.</p>
|
|
<p><strong>Le wakeword devient une exigence du canal, pas une propriété du personnage.</strong> Il corrigeait un bug latent : <code>GuideName</code> est du texte libre sans lien avec le modèle OpenWakeWord, donc le visiteur dirait « Marco » à quelqu'un qui se présente comme « Léon ». Nouvelle règle : nom libre par défaut, contraint à un modèle disponible <strong>seulement si</strong> un canal mains-libres est actif (lunettes, casque VR). Mobile/web/kiosk : push-to-talk.</p>
|
|
<p>Deux champs à ajouter : <code>Persona.VoicePrompt</code> (l'intonation — aujourd'hui la <strong>constante de build</strong> <code>kGeminiTtsPrompt</code>, <code>visitapp constants.dart:27</code>) et une table <code>TtsVoice</code> à la place des deux constantes : deux voix ne suffisent pas à quatre narrateurs. Migration : les 4 colonnes <code>Instance.Guide*</code> deviennent une ligne <code>Persona</code>.</p>
|
|
<p>🔄 <strong>Révisé le 02/09 : le <code>Kind</code> Guide/Narrateur est abandonné.</strong> Un guide n'est rien d'autre qu'un personnage dont la facette <em>parole</em> est remplie. Donc un seul objet, et ce qu'il sait faire découle des facettes renseignées : <code>VoiceId</code> → il narre, <code>SystemPrompt</code> → il peut être guide, <code>WakewordId</code> → on l'appelle à la voix. <strong>Quatre combinaisons valides</strong>, y compris <em>visage seul</em> — une silhouette de décor qui revient huit fois a besoin d'un canon, pas d'une voix.</p>
|
|
<p><strong>Un guide par visite est possible</strong> : <code>Configuration.GuidePersonaId ?? Instance.GuidePersonaId</code>. Donc le gouverneur peut narrer les étapes 1, 3, 5 <em>et</em> répondre aux questions sur ce parcours, en personnage. Assignation : <em>le défaut se pose sur tout conteneur, la surcharge sur toute feuille</em> — narrateur sur <code>Instance</code> → <code>Configuration</code> → <code>GuidedPath</code>/<code>SectionMap</code>, surchargeable sur <code>GuidedStep</code>, <code>GeoPoint</code>, <code>SectionArticle</code>.</p>
|
|
<p>✅ <strong>Le mains-libres n'est pas la limite que je croyais</strong> (vérifié dans le code le 02/09) : <code>NativeWakeWordEngine</code> fait tourner <strong>N modèles en parallèle</strong> et l'événement dit lequel a déclenché (<code>detected:hey_marco</code>). Quatre tournent déjà, <strong>en dur</strong> (<code>voice_controller.dart:105</code>), sans lien avec le CMS. Cinq modèles sont embarqués : <code>hey_viva</code>, <code>hey_marco</code>, <code>hey_alba</code>, <code>hey_vasco</code>, <code>hey_visit</code>. La vraie contrainte est que le <strong>catalogue de noms est fini et embarqué au build</strong> — un autre prénom demande un entraînement et une republication de l'app.</p>
|
|
<p>🐛 <strong>Bug relevé au passage — le chemin mains-libres Android est cassé.</strong> Le moteur passe le <em>nom du modèle</em> dans <code>onDetectedWithCommand</code>, que <code>VoiceOrchestrator._onWakeWordWithCommand</code> (<code>voice_orchestrator.dart:132</code>) interprète comme <em>la question du visiteur</em> et envoie à <code>_dispatch()</code>. Le visiteur dit « Hey Marco », l'assistant répond à la question « hey_marco » et <strong>n'ouvre jamais le cycle d'écoute</strong>. À corriger indépendamment du Studio.</p>
|
|
<p>⚠️ <code>VisitorQuestion</code> a <code>ConfigurationId</code>, <code>AppType</code>, <code>IsVoice</code>, <code>ThemeId</code> — mais <strong>pas de <code>PersonaId</code></strong>. Sans lui, impossible de ventiler les stats par personnage, donc de voir qu'un personnage répond mal.</p>
|