La création de pipelines audio à très faible latence à l’aide de la nouvelle OpenAI Realtime API permet aux développeurs de lancer en production des agents vocaux conversationnels aux interactions humaines. Traditionnellement, la conception d’une interface vocale nécessitait d’enchaîner trois couches de modèles distinctes : la reconnaissance automatique de la parole (ASR), une couche logique textuelle LLM et la synthèse vocale (TTS). Ce pipeline en plusieurs étapes introduisait des délais réseau importants, rendant toute conversation naturelle impossible. Le traitement audio natif via une connexion WebSocket persistante change la donne, en réduisant la latence réseau à moins de 300 millisecondes. Ce guide explique comment établir des états de connexion, diffuser des tampons audio bruts et optimiser les configurations de session.
[!IMPORTANT] Avertissement de sécurité API : N’exposez jamais votre clé API OpenAI directement dans les scripts du navigateur côté client. Passez toujours par un middleware edge sécurisé (tel qu’un Cloudflare Worker) pour relayer la connexion WebSocket en y ajoutant les en-têtes d’autorisation avant de transférer les paquets à OpenAI.
Points clés à retenir :
- Connexion WebSocket : Connectez-vous directement à la passerelle WebSocket en temps réel d’OpenAI à l’aide de proxys edge.
- Modalités natives : Spécifiez à la fois
textetaudiodans la configuration initiale de mise à jour de votre session.- Format audio : Diffusez la parole de l’utilisateur sous forme de segments PCM16 mono encodés en base64 à 24 kHz.
- Interruption de parole : Surveillez les signaux de début de parole du serveur pour interrompre instantanément la lecture côté client.
L’architecture des modèles LLM audio natifs
Les solutions vocales traditionnelles considèrent les modèles de reconnaissance et de synthèse de la parole comme des enveloppes externes autour d’un modèle textuel central. Les modèles audio natifs éliminent cette surcharge en traitant directement la parole.
Avec les modèles temps réel d’OpenAI, le réseau traite les entrées et sorties audio de manière native. Le modèle reçoit des ondes audio brutes, analyse le ton, l’intonation et le contenu, puis génère directement une sortie vocale naturelle. Par conséquent, ce pipeline élimine les erreurs de transcription ASR et les goulots d’étranglement de synthèse TTS.
La gestion de cette connexion persistante repose sur les WebSockets. La connexion reste ouverte tout au long de l’appel, permettant à l’agent d’interrompre sa sortie s’il détecte que l’utilisateur prend la parole, offrant ainsi une expérience comparable à un véritable appel téléphonique.
Obtenez des services d'intégration d'IAPrérequis
Avant d’écrire la moindre ligne de code, assurez-vous que votre environnement est prêt. Ce guide part du principe que vous maîtrisez le JavaScript asynchrone et disposez des éléments suivants :
- Un compte OpenAI avec un accès Realtime et une clé API active et approvisionnée.
- Node.js 20 ou plus récent, ou un projet Cloudflare Workers initialisé avec
npm create cloudflare@latest. - Un client WebSocket — le package
wspour une passerelle Node.js, ou l’objet global natifWebSocketdisponible dans Workers. - Un frontend de navigateur capable de capturer l’audio du microphone via l’API Web Audio (
getUserMediacombiné avec unAudioWorkletpour le rééchantillonnage). - Des connaissances pratiques sur l’audio PCM et base64, car chaque trame envoyée ou reçue est un payload PCM16 encodé en base64.
Prenez le temps de configurer votre compte : l’accès Realtime et la facturation associée doivent être activés sur votre organisation pour que la passerelle accepte une session.
Établir des connexions WebSocket
Pour commencer, vous ouvrez une connexion vers la passerelle OpenAI Realtime en spécifiant le modèle temps réel dans vos en-têtes.
Le code JavaScript ci-dessous montre comment initialiser la connexion, configurer les modalités de session et gérer les tampons audio entrants et sortants en streaming :
1import WebSocket from "ws";
2
3export async function startVoiceAgent(env) {
4 // Connect to the OpenAI Realtime WebSocket gateway
5 const url = "wss://api.openai.com/v1/realtime?model=gpt-realtime";
6 const ws = new WebSocket(url, {
7 headers: {
8 "Authorization": `Bearer ${env.OPENAI_API_KEY}`,
9 "OpenAI-Beta": "realtime=v1"
10 }
11 });
12
13 ws.on("open", () => {
14 console.log("WebSocket connection established with OpenAI Realtime API");
15
16 // Configure session modalities and voice parameters
17 const sessionConfig = {
18 type: "session.update",
19 session: {
20 modalities: ["text", "audio"],
21 instructions: "You are a helpful customer service assistant for Mecanik.",
22 voice: "alloy",
23 input_audio_format: "pcm16",
24 output_audio_format: "pcm16",
25 temperature: 0.7
26 }
27 };
28 ws.send(JSON.stringify(sessionConfig));
29 });
30
31 ws.on("message", (data) => {
32 const event = JSON.parse(data);
33
34 // Handle incoming audio content from the server
35 if (event.type === "response.audio.delta") {
36 const audioBuffer = Buffer.from(event.delta, "base64");
37 // Output buffer to client audio player
38 playAudioChunk(audioBuffer);
39 }
40 });
41}
Lors de la création de ce gestionnaire, assurez-vous que votre clé API reste invisible pour le navigateur client. Plus précisément, implémentez un middleware edge sur votre serveur pour gérer l’autorisation avant de relayer la connexion WebSocket. Pour en savoir plus sur les configurations d’API edge, lisez notre guide sur la création d’une API serverless avec Cloudflare Workers .
Création du proxy Edge sécurisé
Le fragment de code ci-dessus établit la connexion depuis un serveur de confiance, mais il ne montre pas la partie essentielle qui protège votre clé : le proxy lui-même. Sur Cloudflare Workers, vous ne pouvez pas attacher d’en-têtes personnalisés au constructeur new WebSocket(). Vous devez donc ouvrir la connexion en amont avec un appel fetch et un en-tête Upgrade. Le Worker accepte le socket du navigateur, appelle OpenAI en y associant votre clé secrète, puis relaie les trames entre les deux.
1export default {
2 async fetch(request, env) {
3 if (request.headers.get("Upgrade") !== "websocket") {
4 return new Response("Expected a WebSocket upgrade", { status: 426 });
5 }
6
7 // 1. Accept the browser <-> Worker socket
8 const [client, server] = Object.values(new WebSocketPair());
9 server.accept();
10
11 // 2. Open the Worker <-> OpenAI socket with the secret key attached
12 const upstreamResponse = await fetch(
13 "https://api.openai.com/v1/realtime?model=gpt-realtime",
14 {
15 headers: {
16 Upgrade: "websocket",
17 Authorization: `Bearer ${env.OPENAI_API_KEY}`,
18 "OpenAI-Beta": "realtime=v1"
19 }
20 }
21 );
22
23 const upstream = upstreamResponse.webSocket;
24 if (!upstream) {
25 return new Response("Upstream refused the upgrade", { status: 502 });
26 }
27 upstream.accept();
28
29 // 3. Pipe frames in both directions
30 server.addEventListener("message", (e) => upstream.send(e.data));
31 upstream.addEventListener("message", (e) => server.send(e.data));
32
33 const close = () => { try { server.close(); upstream.close(); } catch {} };
34 server.addEventListener("close", close);
35 upstream.addEventListener("close", close);
36
37 return new Response(null, { status: 101, webSocket: client });
38 }
39};
Stockez la clé sous forme de secret chiffré via la commande wrangler secret put OPENAI_API_KEY plutôt que dans le fichier wrangler.toml, afin qu’elle ne figure jamais dans votre dépôt de code. Le navigateur se connecte désormais à wss://votre-worker.workers.dev sans jamais voir l’identifiant. Cloudflare documente ce modèle bidirectionnel dans sa référence Workers WebSockets
.
Gérer le flux audio de l’utilisateur
Une fois la mise à jour de session acceptée, votre client doit capturer l’entrée du microphone, la compresser en données PCM16 mono à 24 kHz et la transmettre sous forme de segments base64. La connexion étant persistante, la gestion des pertes de réseau est essentielle. Un tampon local de segments garantit que de courtes coupures de connexion cellulaire ne provoquent pas de pertes de paquets audio ou des réponses saccadées de l’agent : le client conserve le tampon et le rejoue immédiatement après la reconnexion.
1// Example of streaming user microphone data
2function streamMicrophoneChunk(ws, base64AudioChunk) {
3 const audioEvent = {
4 type: "input_audio_buffer.append",
5 audio: base64AudioChunk
6 };
7 ws.send(JSON.stringify(audioEvent));
8}
Dès que l’utilisateur s’arrête de parler, le serveur traite automatiquement le tampon audio accumulé et déclenche une réponse du modèle. Pour obtenir la liste complète des événements de l’API, consultez le guide OpenAI Realtime .
De plus, implémentez l’annulation d’écho dans votre lecteur frontend. Si le microphone capte la sortie du haut-parleur, l’agent interprétera sa propre voix comme une interruption de l’utilisateur, ce qui provoquera l’échec de la boucle de session. Pour en savoir plus sur l’optimisation frontend, consultez notre guide sur la comparaison entre WordPress et le développement web sur mesure .
Gérer la détection des tours de parole et les interruptions
Par défaut, vous devez indiquer au modèle quand s’arrête un tour de parole. L’activation de la détection d’activité vocale (VAD) côté serveur délègue cette tâche à OpenAI : la passerelle surveille le tampon entrant, détermine quand l’utilisateur a cessé de parler et déclenche automatiquement une réponse. Ajoutez un bloc turn_detection à la configuration de mise à jour de session envoyée lors de la phase de handshake (négociation).
1const sessionConfig = {
2 type: "session.update",
3 session: {
4 modalities: ["text", "audio"],
5 voice: "alloy",
6 input_audio_format: "pcm16",
7 output_audio_format: "pcm16",
8 turn_detection: {
9 type: "server_vad",
10 threshold: 0.5,
11 prefix_padding_ms: 300,
12 silence_duration_ms: 500
13 }
14 }
15};
Une fois la VAD active, le serveur émet l’événement input_audio_buffer.speech_started dès qu’il détecte que l’utilisateur parle en même temps que l’agent. Traitez cet événement comme un arrêt immédiat : videz tous les segments audio en attente dans votre lecteur, sinon la réponse précédente continuera d’être lue en même temps que la nouvelle arrive.
1let playbackQueue = [];
2
3ws.on("message", (raw) => {
4 const event = JSON.parse(raw);
5 if (event.type === "input_audio_buffer.speech_started") {
6 // User is interrupting: drop everything still buffered
7 playbackQueue = [];
8 stopSpeaker();
9 }
10});
Augmenter silence_duration_ms permet à l’agent d’attendre plus longtemps avant de répondre, ce qui convient aux personnes parlant lentement ou hésitantes ; diminuer cette valeur rend l’échange plus dynamique mais risque de couper la parole aux utilisateurs en milieu de phrase.
Déploiement étape par étape de l’agent vocal
Pour déployer le middleware de votre agent vocal, commencez par configurer une passerelle de routage WebSocket sécurisée sur des nœuds edge serverless afin de protéger vos identifiants OpenAI. Cela empêche les robots d’extraire vos clés de chiffrement.
Ensuite, rédigez vos prompts système et vos règles de comportement avec soin. Définissez le ton, le vocabulaire et les instructions de réponse dans la configuration initiale de mise à jour de session. De cette façon, vous délimitez clairement le cadre des flux conversationnels.
Implémentez enfin une logique robuste d’interruption de parole. Écoutez l’événement input_audio_buffer.speech_started et arrêtez immédiatement la lecture audio dans votre lecteur frontend. Optimisez la latence globale en déployant vos Workers à proximité géographique de vos utilisateurs afin de réduire le nombre de rebonds réseau (hops). Pour explorer les options d’hébergement edge, lisez notre tutoriel Cloudflare Workers AI
.
Pièges courants et dépannage
La plupart des échecs lors des premiers tests résultent de quelques erreurs prévisibles. Le tableau ci-dessous associe les symptômes constatés à leurs causes et correctifs habituels.
| Symptôme | Cause probable | Solution |
|---|---|---|
La connexion se ferme immédiatement avec une erreur 401 | En-tête Authorization manquant ou mal formé | Vérifiez que le proxy ajoute Bearer <clé> et que la clé dispose bien des accès Realtime |
| L’agent n’entend que du silence ou une voix inaudible | Audio envoyé avec un taux d’échantillonnage ou une profondeur de bits incorrects | Rééchantillonnez l’audio en PCM16 mono à 24 kHz avant de l’encoder en base64 |
| Le modèle ne répond jamais après que l’utilisateur a parlé | La détection de tours de parole (turn_detection) est désactivée et aucun envoi manuel (commit) n’est effectué | Activez server_vad, ou envoyez input_audio_buffer.commit puis response.create |
| L’agent parle en même temps que l’utilisateur | Le gestionnaire speech_started ne vide pas la file d’attente | Videz le tampon de lecture et arrêtez le haut-parleur dès la réception de cet événement |
| Les réponses s’interrompent au milieu d’une phrase | max_response_output_tokens défini trop bas | Augmentez la limite ou laissez la valeur par défaut à inf |
Deux problèmes plus discrets méritent attention. Un événement rate_limits.updated signalant un solde de tokens restant faible vous avertit que les sessions simultanées risquent d’être bridées ; enregistrez l’événement dans vos logs et réduisez le rythme plutôt que de multiplier les reconnexions. Si l’agent répond parfois à sa propre phrase précédente, le microphone capte la sortie du haut-parleur : renforcez l’annulation d’écho ou demandez aux testeurs d’utiliser un casque.
Considérations relatives aux tests et à la production
Les tests locaux s’avèrent délicats car vous devez faire transiter un flux audio réel dans les deux sens. La boucle de validation la plus rapide consiste à lancer le Worker avec wrangler dev, à y connecter une interface web légère et à surveiller le flux d’événements dans la console avant de vous préoccuper de la qualité sonore. Enregistrez chaque type d’événement entrant pendant le développement ; une fois que la séquence de session.updated, speech_started, response.audio.delta et response.done se déroule correctement, la connectivité est validée.
Avant de passer en production, évaluez ces contraintes réelles :
- Coût. L’audio temps réel est facturé à la minute de tokens audio d’entrée et de sortie, ce qui est beaucoup plus onéreux que les simples tokens textuels. Limitez la durée des sessions et envisagez un repli vers le texte pour les réponses informatives longues.
- Latence. Déployez le proxy au plus près de vos utilisateurs et limitez sa complexité. Chaque rebond supplémentaire entre le navigateur, le edge et OpenAI rallonge le temps de réponse que vous cherchez à minimiser.
- Reconnexion. Les connexions se déconnectent fréquemment sur les réseaux mobiles. Conservez un court tampon tournant côté client et rejouez-le après reconnexion afin qu’une perte de trame ne perturbe pas la conversation.
- Observabilité. Émettez des logs structurés pour le démarrage de session, le nombre d’interruptions et les erreurs de manière à détecter les baisses de performance avant les utilisateurs.
- Sécurité et confidentialité. La voix constitue une donnée personnelle. Mettez en place une politique explicite de conservation des données et ajoutez une modération côté serveur sur les transcriptions si votre agent traite des flux sensibles.
Un court projet pilote avec de vrais utilisateurs fera ressortir des cas particuliers liés aux accents, au bruit ambiant et aux interruptions qu’aucun test scripté ne peut couvrir ; menez-en un avant tout déploiement général.
Points clés à retenir
- L’OpenAI Realtime API traite l’audio nativement via des WebSockets persistants, maintenant la latence sous la barre des 300 ms.
- Configurez systématiquement les modalités, formats et prompts lors de la négociation WebSocket initiale.
- Diffusez le flux audio de l’utilisateur sous forme de segments PCM16 mono en base64 en temps réel.
- Gérez les événements d’interruption de parole en surveillant l’événement de début de voix du client.
- Garantissez la sécurité de vos accès à l’API en faisant transiter les détails de connexion par des routeurs de workers edge.
Foire aux questions (FAQ)
Qu’est-ce que l’OpenAI Realtime API ? L’OpenAI Realtime API est une interface WebSocket qui permet aux développeurs de diffuser des flux audio bruts en entrée et en sortie du modèle, en évitant l’utilisation de modules ASR/TTS distincts. En traitant l’audio de manière native, le modèle préserve le ton émotionnel, les inflexions d’accent et les nuances de la parole, tout en réduisant la latence à moins de 300 millisecondes.
Comment gérer les interruptions vocales ?
Écoutez l’événement serveur input_audio_buffer.speech_started. Dès sa réception, videz vos tampons audio frontend et arrêtez immédiatement la lecture du haut-parleur. Cela crée un flux conversationnel naturel, permettant à l’agent d’IA de cesser de parler instantanément lorsque l’utilisateur commence à s’exprimer.
Quels formats audio l’API prend-elle en charge ? L’API prend en charge nativement les formats audio mono PCM16 24 kHz (données brutes d’entiers signés 16 bits) et G.711 (lois u-law et a-law). Les développeurs doivent capturer les données du microphone, les convertir dans ces formats spécifiques côté client et les diffuser sous forme de chaînes encodées en base64 au sein de trames WebSocket JSON.
Ai-je besoin d’un serveur distinct pour coordonner le trafic WebSocket ? Oui. L’utilisation d’un coordinateur edge serverless (tel que Cloudflare Workers ou une passerelle Node.js légère) est fortement recommandée. Le proxy reçoit les entrées du microphone des navigateurs clients, y ajoute les en-têtes d’autorisation sécurisés et redirige le flux binaire vers la passerelle d’OpenAI.
Comment éviter les boucles d’écho avec les agents vocaux en temps réel ? Les boucles d’écho se produisent lorsque le son du haut-parleur est capté par le microphone du client, déclenchant de fausses interruptions de parole. Les développeurs doivent implémenter des algorithmes d’annulation d’écho côté client ou utiliser un casque pendant les tests pour éviter que ces interférences ne perturbent la session.
Commentaires