Créez un agent vocal en 20 minutes avec ElevenLabs et Twilio
- Publié
- Dernière mise à jour
ÉcouterÉcouter cet article
Un agent vocal peut répondre aux appels entrants, transcrire les appelants en temps réel avec Speech to Text (STT), générer une réponse avec un grand modèle de langage (LLM), puis répondre avec un module Text to Speech (TTS). Avec ElevenLabs et Twilio, vous pouvez avoir un agent opérationnel sur un vrai numéro de téléphone en environ 20 minutes.
Pour les développeurs, la stack complète utilisée ici est ElevenLabs pour la synthèse vocale (Flash v2.5) et la transcription (Scribe v2 Realtime), Twilio pour la téléphonie, et OpenAI ou Anthropic comme LLM. Tous ces éléments sont interchangeables, donc vous pouvez choisir les composants que vous connaissez le mieux.
Cet article montre comment créer un agent vocal en 20 minutes, avec Node.js et Typescript. Si vous préférez une alternative gérée qui prend en charge la gestion des tours de parole, les interruptions et la téléphonie sans gérer la cascade vous-même, rendez-vous sur ElevenAgents.
Comment fonctionne l’architecture d’un agent vocal
Avant d’écrire du code, il est utile de comprendre comment les trois services de votre stack technique sont connectés.
- Twilio : Gère l’appel téléphonique et le transport audio.
- ElevenLabs : Gère le STT via Scribe v2 Realtime et le TTS via Flash v2.5.
- LLM : Gère l’appel d’outils et la rédaction de la réponse.
Chaque étape est un simple adaptateur, ce qui permet d’en remplacer une par un autre service sans toucher au reste. Par exemple, vous pouvez remplacer le LLM OpenAI par Anthropic sans avoir à réécrire les autres composants.
Un appel téléphonique arrive sur votre serveur via Twilio. Twilio répond à l’appel PSTN, ouvre un WebSocket vers votre serveur et transmet l’audio de l’appelant sous forme de flux de trames mu-law encodées en base64. Votre serveur gère la cascade et renvoie l’audio synthétisé sur ce même WebSocket, que Twilio diffuse à l’appelant.

Voici le déroulement que vous utiliserez pour créer un agent vocal :
Un appelant compose votre numéro Twilio. Twilio récupère un document TwiML depuis votre webhook. Le TwiML indique à Twilio d’ouvrir un Media Stream vers votre endpoint WebSocket. Twilio transmet l’audio entrant sous forme d’événements JSON contenant des payloads mu-law base64 (ulaw_8000).
Votre serveur transmet les morceaux audio à Scribe v2 Realtime pour la transcription en streaming. Quand un tour de parole est terminé, vous envoyez la transcription au LLM, puis vous synthétisez la réponse avec Flash v2.5 en ulaw_8000. Vous renvoyez les trames mu-law synthétisées à Twilio via le WebSocket, encodées en base64, et Twilio les diffuse à l’appelant.
Scribe v2 Realtime fournit des transcriptions partielles avec environ 150 ms de latence, et Flash v2.5 fonctionne à environ 75 ms d’inférence, hors latence réseau et application. Le LLM est la partie la plus imprévisible du temps de réponse, et c’est là que la majorité de la latence se concentre. Pour réduire ce délai, nous diffusons la sortie du LLM token par token et commençons la synthèse avant la fin de la phrase.
Pour comprendre les choix de modèles, consultez la présentation des modèles et l’explication sur la gestion de la latence.
Ce qu’il vous faut avant de commencer à créer un agent vocal
Ce guide suppose que vous avez quatre éléments prêts. Chacun se configure rapidement, mais l’absence de l’un d’eux bloquera le serveur.
Voici les prérequis à vérifier :
- Un numéro Twilio avec la fonctionnalité Voice : Notez ce numéro ainsi que votre Account SID et Auth Token depuis la console Twilio.
- Clé API ElevenLabs : créée dans votre tableau de bord ElevenLabs. La clé circule dans l’en-tête xi-api-key et reste confidentielle, gardez-la uniquement côté serveur. Voir
- Clé API LLM : Ce tutoriel considère Anthropic Claude et OpenAI comme des backends interchangeables, choisissez donc l’un des deux.
- Ngrok (ou tout tunnel) pour le développement local : Twilio doit accéder à votre serveur via une URL HTTPS et WSS publique, et ngrok fournit cela sans déploiement.
Définissez vos secrets comme variables d’environnement, et ne les commitez jamais.
Ensuite, lancez un tunnel pointant vers le port utilisé par votre serveur :
Comprendre le protocole Twilio Media Streams
Twilio ne fournit pas de socket audio brut. Tout est encapsulé dans un protocole JSON structuré via WebSocket. Comprendre les quatre types d’événements et le format d’envoi vous permettra de bien écrire le handler WebSocket à l’étape 2.
Une fois connecté à votre WebSocket, Twilio envoie une séquence de messages texte JSON, qui peuvent avoir quatre types d’événements.
L’événement connected arrive en premier et confirme que le WebSocket est ouvert. L’événement start est envoyé une fois au début du flux média ; il contient un streamSid à stocker, car il sera nécessaire pour renvoyer de l’audio, ainsi que des métadonnées d’appel sous start.customParameters et start.callSid.
L’événement media est récurrent : media.payload est un morceau d’audio mu-law 8 kHz encodé en base64, 20 ms par trame, et media.track correspond à l’audio entrant de l’appelant. Enfin, stop est envoyé à la fin du flux, généralement quand l’appel est raccroché.
Pour rejouer de l’audio, vous envoyez un message de type media avec le même streamSid et un payload mu-law en base64. Pour interrompre l’audio déjà en file d’attente, vous envoyez un message clear avec le streamSid, ce qui vide le buffer sortant de Twilio.
Les encodages entrant et sortant sont identiques (ulaw_8000). Nous demandons ulaw_8000 à ElevenLabs Text to Speech et transmettons les octets directement à Twilio sans rééchantillonnage.
Étape 1 : Servir le webhook TwiML
Lorsqu’un appel arrive, Twilio fait une requête HTTP à votre webhook, et vous répondez avec du TwiML qui connecte l’appel à votre Media Stream. Le verbe <Connect><Stream> ouvre un WebSocket bidirectionnel. Utilisez <Connect> plutôt que <Start> ici : cela maintient l’appel actif pendant toute la durée du flux et vous permet de renvoyer de l’audio, ce qui est le but de cette configuration.
Le TwiML renvoyé par le webhook est :
En Express, il s’agit d’un simple handler POST qui complète l’hôte et renvoie le document :
Dans la console Twilio, définissez le webhook "A call comes in" du numéro sur https://your-subdomain.ngrok.app/incoming-call en HTTP POST.
Étape 2 : Accepter le WebSocket Media Stream
Le handler WebSocket lit les événements Twilio, gère la cascade et renvoie l’audio.
On conserve un petit état par appel : le streamSid, une connexion STT et un indicateur pour savoir si l’agent parle. Le handler décode chaque trame media entrante depuis base64 et transmet les octets mu-law bruts au STT :
Étape 3 : Transcrire avec Scribe v2 Realtime
Scribe v2 Realtime accepte les morceaux audio en streaming et renvoie des transcriptions partielles et finales. Il prend en charge l’encodage mu-law directement, donc on lui transmet les trames Twilio sans modification.
Il propose aussi la détection d’activité vocale (VAD) pour segmenter selon les silences et un contrôle manuel pour finaliser un segment. Pour un agent téléphonique, la segmentation basée sur la VAD est généralement le meilleur choix, car une pause naturelle est le signal le plus fiable de fin de tour de parole.
Voici les étapes : Ouvrir un flux STT au début de l’appel. Envoyer chaque morceau mu-law entrant. Réagir aux transcriptions finalisées en appelant le LLM.
L’interface du client STT temps réel évolue encore, donc la structure ci-dessous est encapsulée dans un petit adaptateur TypeScript (openRealtimeStt) à implémenter selon l’API en direct plutôt qu’un schéma figé. Considérez onFinal comme le point de passage d’un tour de parole terminé à l’étape suivante.
La latence de reconnaissance en temps réel est d’environ 150 ms pour les transcriptions partielles, ce qui réduit l’écart perçu entre la fin de parole de l’appelant et le début de réponse de l’agent. Pour la version batch et toutes les fonctionnalités, consultez la documentation Speech to Text et la page produit Speech to Text temps réel.
Étape 4 : Générer une réponse avec un LLM
C’est l’étape qui produit la réponse. Le LLM prend l’historique de la conversation et renvoie le texte de l’assistant. Diffusez la réponse pour pouvoir commencer la synthèse dès la première phrase.
Ici, c’est OpenAI qui est utilisé :
L’ID de modèle ci-dessus, gpt-4.1-mini, est un exemple de choix à faible latence ; claude-haiku-4-5 est une option comparable chez Anthropic. Les deux fournisseurs peuvent être utilisés pour le même contrat llmReply ; il suffit de changer le corps de la fonction, le reste de l’agent ne change pas.
Le prompt système limite la longueur de la réponse, ce qui est important au téléphone : des réponses longues paraissent lentes et sont difficiles à interrompre naturellement.
Étape 5 : Synthétiser avec Flash TTS en ulaw_8000
Le texte doit maintenant devenir un audio que Twilio peut lire. Demandez Flash v2.5 avec outputFormat : "ulaw_8000" pour que les octets correspondent à l’encodage attendu par Twilio, puis diffusez l’audio et renvoyez chaque morceau sur le WebSocket comme événement media.
Regroupez les tokens LLM en fragments de taille phrase et synthétisez chaque fragment dès qu’il est prêt, au lieu d’attendre toute la réponse. Cela réduit le temps avant la première réponse audio, car l’appelant entend la première phrase pendant que le modèle génère la suivante. Pour un contrôle plus fin de la synthèse incrémentale, le guide TTS temps réel WebSocket montre comment envoyer du texte dans une seule socket de synthèse ouverte ; l’approche HTTP streaming ci-dessous est plus simple et suffisante pour des échanges courts.
Renforcer votre agent vocal IA pour la production
Après ces cinq étapes, vous avez un agent fonctionnel. Ce n’est pas encore un déploiement en production.
Il y a plusieurs points à surveiller avant de mettre l’agent sur une vraie ligne téléphonique.
Validez les signatures webhook Twilio
Toute personne connaissant votre URL webhook peut y faire un POST, donc la première étape est de vérifier que la requête vient bien de Twilio. Twilio signe chaque requête avec votre Auth Token dans l’en-tête X-Twilio-Signature, et vous devez rejeter toute requête qui échoue à la validation. La signature est calculée sur l’URL complète et les paramètres POST, donc vous devez la calculer comme Twilio.
L’outil Twilio s’en charge pour vous :
Gérez correctement les secrets
Gardez ELEVENLABS_API_KEY, la clé LLM et TWILIO_AUTH_TOKEN dans un gestionnaire de secrets, pas dans le code source ni dans des fichiers env en clair dans un dépôt. Limitez la clé ElevenLabs aux endpoints nécessaires et fixez un quota de crédits pour limiter l’impact en cas de fuite.
Les offres Entreprise peuvent aussi restreindre une clé à certaines plages IP grâce au whitelisting. Ce serveur utilise la clé API directement car elle ne quitte jamais votre backend ; si une partie de la logique audio passait côté navigateur ou mobile, il faudrait passer à des tokens à usage unique pour ne jamais exposer la clé côté client.
Comprenez la limite de concurrence
Chaque offre a une limite de concurrence qui varie selon la famille de modèles, et cette limite compte le nombre de requêtes générant de l’audio en même temps.
Pour un agent téléphonique, ce calcul joue en votre faveur. La génération audio est plus rapide que la lecture, donc chaque appel ne consomme la concurrence TTS que pendant les brefs moments où une réponse est synthétisée, pas pendant toute la durée de l’appel. En règle générale, une limite de concurrence d’environ cinq permet de gérer une centaine de conversations simultanées, car la génération se termine bien avant la lecture.
Malgré tout, surveillez la marge plutôt que de deviner. Les réponses ElevenLabs incluent les en-têtes current-concurrent-requests et maximum-concurrent-requests ; enregistrez-les et déclenchez une alerte à l’approche du maximum. Si vous dépassez la limite, les requêtes sont mises en file d’attente par priorité, ce qui ajoute généralement environ 50 ms, et une surcharge prolongée renvoie une erreur HTTP 429.
Gérez les réponses HTTP 429 avec un court délai avant de réessayer. Si elles persistent, augmentez la limite sur la page de tarification ou, pour les clients Entreprise, via votre gestionnaire de compte.
Gérez le barge-in et les interruptions
Un appelant qui commence à parler pendant que l’agent parle s’attend à ce que l’agent s’arrête. C’est le barge-in, et bien le gérer est essentiel pour que l’agent paraisse naturel et non scripté.
Détectez la parole de l’appelant pendant la lecture de l’agent grâce au signal VAD du STT. Quand c’est le cas, faites deux choses : arrêtez d’envoyer les morceaux TTS (le flag agentSpeaking dans speak gère déjà cela en interrompant la boucle), puis envoyez à Twilio un message clear pour vider l’audio déjà en file d’attente.
Si vous sautez le clear, Twilio continue de lire l’audio mis en tampon après que vous avez arrêté d’envoyer, donc l’agent semble parler par-dessus l’appelant.
Journalisez, surveillez et gérez les erreurs proprement
Instrumentez chaque étape pour pouvoir attribuer la latence si un appel paraît lent. Mesurez l’écart entre la transcription finale et le premier token LLM, entre le premier token LLM et le premier octet TTS, puis entre le premier octet TTS et la trame envoyée à Twilio. Vous verrez que la plus grande variabilité de latence se situe au niveau du LLM ; les étapes STT et TTS sont plus stables.
Prévoyez ensuite les échecs partiels. Le LLM peut expirer, le flux STT peut se couper, et le temps de trajet réseau vers ElevenLabs varie de 20 à 200 ms selon la géographie. Placez votre serveur près de vos appelants, pas seulement près d’ElevenLabs, car ElevenLabs route déjà vers le cluster le plus proche (Amérique du Nord, Europe, Asie du Sud-Est).
Si une étape échoue, ne laissez pas l’appelant dans le silence : synthétisez une courte phrase de secours (« Désolé, pouvez-vous répéter ? ») et gardez l’appel actif. Encapsulez chaque étape dans un timeout et un try/catch pour qu’un échec ne coupe pas tout le WebSocket.
Quelques autres paramètres valent la peine d’être définis avant de passer en production :
- Limitez la longueur des réponses dans le prompt système, comme montré, pour garder les échanges courts et faciles à interrompre.
- Limitez l’historique de la conversation pour éviter que les longs appels n’augmentent indéfiniment le contexte du LLM.
- Définissez une durée maximale d’appel pour éviter que des sessions bloquées ne consomment la concurrence en silence.
Pour continuer à optimiser ce que vous contrôlez, le document sur la latence explique d’où vient le temps avant la première réponse audio, la présentation des modèles détaille les compromis vitesse/qualité, et le guide TTS temps réel WebSocket montre comment réduire la latence de synthèse avec un texte incrémental.
Créez des agents vocaux prêts pour la production avec ElevenAPI
Après ces 20 minutes, vous avez toutes les couches d’un agent vocal prêt pour la production. Twilio gère la téléphonie, Scribe v2 Realtime transcrit, un LLM génère les réponses, et Flash v2.5 répond sur le même WebSocket.
Si vous ne souhaitez pas gérer la cascade vous-même, ElevenAgents propose la gestion des tours de parole, des interruptions et l’intégration téléphonie en service géré, basé sur les mêmes modèles que vous venez de connecter à la main.
Pour continuer à optimiser une stack que vous contrôlez, explorez la page produit ElevenAPI pour les offres, les limites de concurrence et la voice library. Sinon, inscrivez-vous et lancez-vous dès aujourd’hui pour passer votre premier appel.
.webp&w=3840&q=80)
.webp&w=3840&q=80)

.webp&w=3840&q=80)
