1. Contexte : que signifie « mode vocal » pour ChatGPT App
Pour commencer, il est important de comprendre que dans le cadre du ChatGPT Apps SDK, vous n’écrivez pas votre propre client audio, vous ne contrôlez pas le micro et vous ne streamez pas l’audio vous‑même. C’est le client ChatGPT (application web ou mobile) qui s’en charge.
Nous partons du principe que vous avez déjà une compréhension de base du widget, de callTool et de GiftGenius vus dans les modules précédents — ici, nous examinons les mêmes éléments à travers le prisme du mode vocal.
De votre point de vue de développeur d’App, tout se présente ainsi :
- L’utilisateur parle dans le micro. Le client ChatGPT effectue la reconnaissance vocale et envoie déjà du texte au modèle.
- Dans le flux, vous « voyez » la même chose que si l’utilisateur tapait un message, simplement les messages arrivent plus vite et de manière plus conversationnelle.
- Le modèle répond en texte, que le client vocalise.
- En parallèle, le modèle peut appeler vos outils (callTool), changer le displayMode du widget, mettre à jour le widgetState et proposer des follow‑ups — comme en mode texte.
La différence clé est que l’utilisateur peut presque ne pas regarder l’écran, ou n’y jeter qu’un coup d’œil rapide sur son téléphone. Autrement dit, votre UI cesse d’être le canal principal d’interaction et devient un complément à la voix, et non l’inverse.
Il en découle deux conséquences :
- Tout ce qui est vraiment important doit être compréhensible « à l’oreille », via les répliques de GPT.
- Le widget doit être « glanceable » au bon sens du terme : d’un coup d’œil, on doit voir immédiatement l’état et les options clés, sans lire du petit texte.
Pour notre GiftGenius, c’est un indice immédiat : le scénario « je conduis, trouve un cadeau pour maman » n’est pas qu’un simple chat texte. C’est un dialogue multimodal, où la voix mène et l’UI sécurise.
2. En quoi un scénario vocal diffère d’un scénario texte
Pour éviter le piège du « c’est pareil, sauf que l’utilisateur parle », il est utile de comparer les modes texte et vocal selon plusieurs axes.
| Aspect | Mode texte | Mode vocal |
|---|---|---|
| Attention de l’utilisateur | Regarde l’écran, lit, fait défiler | Peut ne pas regarder du tout (mains libres) |
| Forme des requêtes | Plus structurée, l’utilisateur édite | Conversationnelle, bribes de phrases, « euh », « encore » |
| Tolérance aux silences | 1–2 secondes de silence sont normales | Un long silence est perçu comme pénible |
| Rôle de l’UI | Porteur principal des détails | Secondaire, « tableau » avec un repère visuel bref |
| Erreurs de saisie | Fautes de frappe, mais le texte est visible | Élocution peu claire, bruit, « oui/non » erronés |
Plusieurs conclusions importantes en découlent.
- On ne peut pas compter sur le fait que l’utilisateur « ira lire dans une carte ». Les éléments critiques doivent être énoncés : ce que vous avez compris, ce que vous allez faire, quel résultat vous avez obtenu.
- L’UI doit tenir le scénario « un regard d’une seconde ». État, progression, choix principal — tout doit être visible en gros caractères. Les détails sont secondaires.
- Il faut combler les silences. Pendant que votre serveur MCP traite une requête lourde, le modèle doit verbaliser ce qui se passe, et le widget doit montrer la progression, pour éviter l’impression d’un assistant figé.
On peut penser au mode vocal comme à un livre audio avec illustrations : vous avez un narrateur (GPT) et des images (le widget). Il faut les synchroniser pour qu’ils se complètent, sans se dupliquer ni se contredire.
3. Rôle du widget en mode vocal : du « panneau de contrôle » au « tableau d’affichage »
En scénario texte, le widget sert souvent d’interface complète : formulaires, tableaux, carrousels avec filtres, boutons d’action. En mode vocal, son rôle change. Les recommandations sur les interfaces multimodales et la VUI montrent que, pour les scénarios vocaux, l’UI devient plutôt un tableau d’information (glanceable UI) : il sert aux vérifications et confirmations rapides, pas à un travail soutenu des yeux.
Pour GiftGenius, cela implique ce qui suit.
Quand l’utilisateur suit un assistant vocal, à l’écran, en inline‑widget ou en plein écran, on affiche :
- Un état en gros : « Étape 2 sur 3 : budget et type de cadeau ».
- Un minimum de texte mais des libellés clairs : « Budget jusqu’à 50 $ », « Préférence pour un cadeau numérique ».
- Deux CTA bien visibles, si le scénario vocal autorise les clics : « Modifier le budget », « Continuer ».
- Une seule barre de progression ou un stepper simple, plutôt que dix petits indicateurs.
Exemple d’un « tableau » simple dans un inline‑widget pour un scénario vocal (TypeScript + React, très simplifié) :
type VoiceUiMode = "default" | "voiceGlance";
interface GiftStepProps {
step: number;
totalSteps: number;
summary: string; // brève description de ce qui a déjà été collecté
uiMode: VoiceUiMode;
}
export function GiftVoiceStep(props: GiftStepProps) {
const fontSize = props.uiMode === "voiceGlance" ? "text-lg" : "text-sm";
return (
<div className="rounded-xl border p-3 flex flex-col gap-2">
<div className={`${fontSize} font-semibold`}>
Étape {props.step} sur {props.totalSteps}
</div>
<div className={`${fontSize} text-muted-foreground`}>
{props.summary}
</div>
</div>
);
}
Il n’y a rien de « vocal » en soi ici, mais l’idée est claire : lorsque uiMode === "voiceGlance", on rend tout plus grand et plus simple. Le signal indiquant que le mode vocal est actif peut venir de différentes sources : d’indices indirects à un indicateur explicite que le modèle place dans le widgetState ou dans la réponse d’un tool.
4. Synchronisation des modalités : ce que dit GPT et ce que montre l’App
Le principe clé du voice‑UX pour les Apps est la synchronisation des modalités : la voix et l’UI visuelle doivent raconter la même histoire, mais à des niveaux de détail différents.
Erreur classique : le développeur amène le modèle à lire à haute voix tout ce que le widget affiche : longues listes de cadeaux, structures JSON avec filtres, etc. C’est une torture. Recommandation : la voix donne un bref résumé, et l’UI affiche les détails.
Exemple de bonne synchronisation pour GiftGenius.
Utilisateur : « Trouve un cadeau pour maman, elle aime le jardinage, budget jusqu’à 50 $ ».
Modèle (à voix haute) : « J’ai sélectionné quelques options. La meilleure, à mon avis, est un kit d’outils de jardinage à 45 $. J’ai affiché deux options similaires à l’écran. Tu veux que je détaille ou qu’on passe directement au choix ? »
Widget (inline) : affiche trois cartes de cadeaux, une courte description et des boutons CTA « Choisir » / « Afficher similaires ».
Représentation dialoguée en pseudo‑JSON d’une étape (ce n’est pas un vrai protocole, juste une illustration de la logique) :
{
"user": "Trouve un cadeau pour maman...",
"assistant_text": "J'ai trouvé quelques options...",
"widget": {
"displayMode": "inline",
"state": {
"view": "gift_list",
"items": [
{ "id": "g1", "title": "Kit d’outils de jardinage", "price": 45 },
{ "id": "g2", "title": "Tablier de jardinier", "price": 30 },
{ "id": "g3", "title": "Lot de graines de fleurs", "price": 20 }
]
}
}
}
Détail important : dans le system‑prompt, vous pouvez préciser explicitement la façon dont le modèle doit parler de l’UI, afin d’éviter qu’il « ne lise du JSON » : « Si tu affiches une liste d’options dans le widget, ne lis pas chacune en entier. Décris brièvement la meilleure option et indique que les autres sont visibles à l’écran. »
À l’avenir, lorsque vous travaillerez avec le Realtime API et vos propres clients vocaux, le principe restera le même : l’UI et le flux audio doivent être alignés. La différence sera simplement que vous aurez alors un contrôle direct sur le streaming.
5. Realtime et latence : comment éviter un silence gênant
Techniquement, les tool_calls en mode vocal sont identiques à ceux du mode texte : le modèle décide d’appeler votre outil, vous renvoyez une réponse, le widget se met à jour. Mais en vocal apparaît un nouveau problème d’UX — la latence. Pendant que votre serveur MCP contacte des API externes ou calcule un rapport complexe, l’utilisateur n’entend… rien. Et c’est perçu bien plus négativement que l’attente d’un texte dans un chat.
Deux niveaux de protection existent : vocal et visuel.
- Au niveau vocal, le system‑prompt doit autoriser (et encourager) le modèle à verbaliser « je travaille » et à poser des questions supplémentaires pendant que l’outil calcule encore. Par exemple : « Je vais chercher des cadeaux, cela prendra quelques secondes. En attendant, dis‑moi s’il y a d’autres contraintes. »
- Au niveau visuel, votre widget doit montrer très clairement la progression : un loader, l’état « Je recherche des options… », l’étape en cours. Sans cela, l’utilisateur pensera que tout a planté et recommencera à parler, perturbant le flux vocal.
En pratique, il est pratique de résoudre cela via une tâche différée : l’outil renvoie immédiatement un statut "pending" et un jobId, tandis que la recherche se poursuit en arrière‑plan. Le widget, à la vue du "pending", affiche la progression, et la voix signale qu’elle « travaille ».
Schéma minimal d’un outil côté serveur qui renvoie un « bouchon » avec un job‑id au lieu de se bloquer jusqu’au résultat complet :
// Pseudocode de l’outil côté serveur GiftGenius
export async function startGiftSearch(params: SearchParams) {
const jobId = await createBackgroundJob(params); // on met la tâche dans une file
return {
status: "pending",
jobId,
message: "Recherche de cadeaux lancée"
};
}
Le widget, voyant status : "pending", peut basculer l’UI en mode progression :
if (toolOutput.status === "pending") {
return (
<div className="p-4 rounded-xl border flex items-center gap-3">
<Spinner />
<div className="text-base">
Je cherche des cadeaux… Cela prendra quelques secondes.
</div>
</div>
);
}
Et le modèle, en réponse à ce même tool‑output, conformément aux instructions, dira à voix haute à peu près la même chose et, éventuellement, posera une question de clarification. Plus tard, lorsque la tâche en arrière‑plan se termine et que, par exemple, une notification MCP job.completed arrive, le widget est mis à jour avec la liste des cadeaux et la voix en annonce le résumé.
Ainsi, on obtient un comportement au plus proche du temps réel, même si le backend n’est pas instantané.
6. Sécurité et confirmations en mode vocal
L’interface vocale est piégeuse pour les actions critiques : paiement, suppression de données, modification de paramètres. La reconnaissance vocale est imparfaite, les utilisateurs parlent « en mouvement », et un « hm‑hm » peut facilement devenir « oui, achète ». Pour les scénarios vocaux, les parcours de confirmation sont donc particulièrement importants.
Deux patrons de base existent.
- Confirmation vocale explicite (Explicit Voice Confirmation). Pour les actions sensibles, vous exigez une phrase précise. Par exemple : « Pour confirmer l’achat, dites : “Je confirme l’achat” » — et, dans le system‑prompt, vous interdisez d’exécuter le paiement sur des « d’accord », « ok », « vas‑y » vagues.
- Confirmation uniquement visuelle (Visual Confirmation Only). Le modèle guide vocalement l’utilisateur vers l’action (« J’ai préparé la commande, l’écran affiche le montant final et le contenu du panier »), mais le déclencheur effectif est l’appui sur le bouton « Payer » dans le widget. C’est particulièrement pertinent pour les scénarios e‑commerce, et nous y reviendrons dans le module 14.
Pour GiftGenius, cela peut donner ceci.
Modèle : « J’ai sélectionné un excellent kit de jardinage à 45 $. Je peux finaliser l’achat via ChatGPT. Le prix final et l’adresse de livraison sont affichés à l’écran. Pour confirmer à la voix, dites “Je confirme l’achat”, ou appuyez sur le bouton “Payer” à l’écran. »
Widget (plein écran) : affiche la commande finale, met en évidence en gras le montant et l’adresse, et deux boutons visibles : « Payer » et « Annuler ».
Dans le widget, vous pouvez refléter l’état de confirmation :
type CheckoutState = "review" | "waiting_voice_confirm" | "confirmed";
if (state.phase === "waiting_voice_confirm") {
return (
<div className="space-y-3">
<h2 className="text-xl font-semibold">Presque prêt</h2>
<p className="text-base">
Confirmez l’achat à la voix avec la phrase
« Je confirme l’achat » ou appuyez sur le bouton « Payer ».
</p>
<Button variant="primary">Payer</Button>
<Button variant="ghost">Annuler</Button>
</div>
);
}
Ainsi, si le modèle interprète mal quelque chose dans la voix, il reste à l’utilisateur une couche visuelle de « sécurité ».
7. Commandes vocales simples et conception des outils
Un utilisateur en mode vocal ne formulera pas des commandes qui épousent exactement les variables de votre outil. Il dira « choisis le premier », « montre moins cher », « sans électronique ». La tâche du développeur est de concevoir ses outils et le system‑prompt pour que le modèle fasse facilement correspondre ces phrases aux appels de vos outils (callTool).
Pour GiftGenius, on peut prévoir par exemple ces actions :
- Choisir l’une des options affichées par index ou id.
- Préciser le budget : « moins cher », « jusqu’à 30 $ ».
- Filtrer par type : « cadeaux numériques uniquement », « rien qui nécessite un envoi postal ».
On peut l’exprimer via un outil avec un paramètre enum simple action et des champs supplémentaires :
// Pseudoschéma d’outil en TypeScript
type VoiceActionInput =
| { action: "select_item"; itemId: string }
| { action: "refine_budget"; maxPrice: number }
| { action: "filter_type"; type: "digital" | "physical" };
export function handleVoiceAction(input: VoiceActionInput) {
switch (input.action) {
case "select_item":
// marquer le cadeau comme sélectionné
break;
case "refine_budget":
// recalculer la sélection pour le nouveau budget
break;
case "filter_type":
// filtrer la liste existante
break;
}
}
Dans le system‑prompt, vous décrivez comment ces actions correspondent aux commandes vocales : « Si l’utilisateur dit “choisis le premier”, appelle l’outil gift.voiceAction avec action="select_item" et l’identifiant du premier cadeau à l’écran », etc.
Du point de vue UX, cela réduit la charge cognitive : l’utilisateur n’a pas à trouver des formulations exactes du type « Affine les filtres pour n’avoir que des cadeaux numériques jusqu’à 30 $ ». Il parle naturellement, et le modèle traduit en structure de données.
8. Scénario vocal GiftGenius : trois étapes
Assemblons tout et concevons un scénario vocal complet pour GiftGenius, sans plonger pour l’instant dans le Realtime API bas niveau.
Imaginez l’utilisateur : il conduit et lance le mode vocal de ChatGPT. Il dit : « S’il te plaît, trouve un cadeau pour ma mère, elle aime le jardin, budget jusqu’à 50 $ ».
Étape 1. Collecte d’informations à la voix
Modèle : « Super, trouvons un cadeau. Je précise deux choses : pour quand est prévu le cadeau — dans les prochains jours ou plus tard ? Et y a‑t‑il des contraintes, par exemple rien de lourd ou d’encombrant ? »
Widget (inline) : pour l’instant, juste un petit panneau avec l’état « On choisit un cadeau pour : maman, jardinage, jusqu’à 50 $ ». Les polices sont un peu plus grandes que d’habitude, pour être lisibles d’un coup d’œil.
Un état de widget pourrait ressembler à ceci :
interface GiftSessionState {
mode: "voice" | "text";
step: 1 | 2 | 3;
recipientSummary: string;
budget?: number;
}
const [state, setState] = useState<GiftSessionState>({
mode: "voice",
step: 1,
recipientSummary: "Maman, aime le jardinage"
});
La partie serveur met à jour au fil des réponses de l’utilisateur le recipientSummary et le budget, et le widget réagit.
Étape 2. Recherche et attente
Une fois que le modèle a recueilli suffisamment d’informations, il appelle votre outil de recherche de cadeaux. Celui‑ci peut lancer une tâche en arrière‑plan, si la sélection est complexe, et renvoyer status : "pending". Pendant que l’arrière‑plan travaille, le modèle dit : « Je vais chercher des options adaptées, cela prendra quelques secondes. En attendant, peux‑tu me dire si elle préfère des cadeaux physiques ou si des cartes numériques lui conviennent ? »
Le widget bascule en mode type PiP si l’utilisateur va ailleurs dans l’interface, ou reste inline avec une progression : « Je cherche des cadeaux… » et un petit indicateur.
Étape 3. Résultats et choix
Quand les résultats sont prêts, le modèle : « J’ai trouvé trois options. La première — un kit d’outils de jardinage à 45 $. La deuxième — un tablier de jardinier à 30 $. Je les ai affichées à l’écran. Dis “choisis la première” ou “montre moins cher”. »
Le widget affiche trois grandes cartes avec les prix et de courtes descriptions. Chaque carte a les CTA « Choisir » et « Similaires ». Plus un bouton séparé « Afficher plus d’options ».
Si l’utilisateur dit : « Choisis la deuxième », le modèle appelle votre outil voiceAction avec action="select_item" et l’id du deuxième cadeau. Le widget le surligne comme sélectionné, et le modèle énonce : « Parfait, on a choisi le tablier de jardinier à 30 $. »
Étape facultative 4. Paiement
Si l’App est intégrée aux paiements (dans le futur module 14), le checkout commence. Le modèle énonce les conditions et demande une confirmation à la voix ou via bouton. Le widget passe en assistant plein écran avec les étapes « Vérification de la commande » → « Adresse de livraison » → « Confirmation ».
Important : à chaque étape, l’essentiel est énoncé à la voix, et le widget sert de repère visuel, surtout si l’utilisateur s’est arrêté et regarde l’écran.
9. Notes pratiques d’implémentation et limites de l’Apps SDK
Toutes les étapes décrites pour GiftGenius sont mises en œuvre dans une App ChatGPT ordinaire — sans client audio dédié ni WebRTC. Il est important de ne pas oublier les limites de la pile.
Il est très facile de « déraper » vers les sujets Realtime API, WebRTC, streaming audio et d’imaginer sa propre plateforme vocale. Un module 20 distinct y est consacré dans ce cours. Dans cette leçon, retenez les limites de l’App ChatGPT à l’intérieur du client ChatGPT.
Dans l’architecture actuelle :
- Le flux audio est géré par le client ChatGPT. Vous n’envoyez ni ne recevez des octets audio dans le widget.
- Côté backend, vous voyez toujours des appels d’outils et des messages texte, mais le modèle peut être en mode vocal, et ses réponses seront vocalisées.
- La plateforme peut fournir des indices indirects que le mode vocal est actif (via le user-agent ou des champs d’environnement). Mais il ne faut pas établir une dépendance rigide là‑dessus : l’API peut évoluer, et votre App doit rester utile en mode purement texte également.
Une bonne stratégie d’implémentation est donc la suivante. Concevez d’abord une UX qui fonctionne correctement à la fois pour le texte et pour la voix : états concis, CTA clairs, étapes de progression compréhensibles. Ajoutez ensuite quelques améliorations spécifiques à la voix : des polices un peu plus grandes en mode "voiceGlance", une progression plus visible, l’accent sur des états comme « Étape 2 sur 3 » et des situations évidentes comme « En attente de confirmation ».
En complément, décrivez dans le system‑prompt le comportement vocal du modèle : comment il commente l’état du widget, quelles phrases il utilise pour les confirmations, quels mots éviter (par exemple, ne pas lire du JSON, ne pas énoncer chaque broutille d’une liste).
Si, plus tard, vous développez votre propre Custom Voice Client sur le Realtime API, toutes ces décisions UX « migreront » sans heurt. La différence portera sur le niveau d’accès aux événements et au streaming, pas sur les principes.
10. Erreurs typiques avec le contexte Voice / Realtime
Erreur n°1 : « Lire l’UI à voix haute » au lieu d’un résumé.
Parfois, les développeurs conçoivent des outils de sorte que le modèle commence à lire à voix haute tout le contenu de la réponse JSON ou la liste complète des cartes. En mode vocal, c’est un désastre UX : l’utilisateur perd le fil, et vous gaspillez des tokens. Il vaut mieux que la voix annonce un court résumé et se focalise sur une ou deux options, en laissant le reste à l’écran.
Erreur n°2 : Absence totale de feedback visuel en vocal.
La tentation est grande de penser : « Puisque l’utilisateur parle, il écoute, l’UI est inutile ». En pratique, il jette souvent un coup d’œil à l’écran ou y revient au bout d’une minute. Si, à ce moment‑là, il n’y a ni état, ni progression, ni résultat compréhensible, il pensera que l’App a figé ou n’a rien fait. Affichez impérativement « Je réfléchis », « Étape 2 sur 3 », « Résultats prêts », etc.
Erreur n°3 : Actions sensibles sans forte confirmation.
En mode texte, déclencher « Payer » sur un seul bouton est déjà risqué, et en mode vocal, exécuter un achat sur un « hm‑hm » flou l’est encore plus. Ignorer des parcours de confirmation explicites (vocaux et/ou visuels) mène à des achats erronés et à une perte de confiance dans l’App. Pensez aux actions qui exigent une double confirmation, et explicitez‑le dans le system‑prompt et l’UI.
Erreur n°4 : Concevoir uniquement pour l’œil, pas pour l’oreille.
Parfois, l’App est conçue comme si l’utilisateur lisait toujours : formulations trop complexes, libellés de boutons trop longs, descriptions surchargées. En mode vocal, tout cela doit aussi être dit — on obtient une « soupe verbale ». Faites en sorte que l’essentiel tienne dans des phrases courtes et simples, faciles à comprendre à l’écoute.
Erreur n°5 : Confusion entre Apps SDK et client Voice sur‑mesure.
Certains étudiants cherchent dans l’Apps SDK des événements micro, le streaming audio, le WebRTC, comme dans le Realtime API, et se disent déçus que « rien de tout cela n’existe ». Il est crucial de comprendre : l’App ChatGPT vit dans le client ChatGPT, et la voix est gérée par la plateforme. Vous travaillez avec du texte, des appels d’outils et l’état du widget, et vous concevez l’UX pour que le mode vocal « fonctionne simplement bien ». Si vous avez besoin d’un contrôle total sur la voix, c’est un projet distinct, plus complexe, avec le Realtime API.
Erreur n°6 : Absence de stratégie face aux latences.
Si vous ne prévoyez pas ce que le modèle dit et ce que le widget affiche pendant les opérations longues, l’utilisateur va interrompre, poser de nouvelles questions et rompre votre flow. Les latences en vocal se ressentent plus fortement qu’en texte. Utilisez des états intermédiaires, un traitement en arrière‑plan des tâches et des « je travaille, raconte‑moi pendant ce temps… » vocaux, pour que le silence ne devienne pas un bug.
GO TO FULL VERSION