Bygg en röstagent på 20 minuter med ElevenLabs och Twilio
- Publicerad
- Senast uppdaterad
LyssnaLyssna på den här artikeln
En röstagent kan svara på inkommande telefonsamtal, transkribera uppringare i realtid med tal till text (STT), generera ett svar med en stor språkmodell (LLM) och svara med tal via en text till tal-modul (TTS). Med ElevenLabs och Twilio kan du ha en fungerande agent på ett riktigt telefonnummer på ungefär 20 minuter.
För utvecklare består stacken av ElevenLabs för talsyntes (Flash v2.5) och transkribering (Scribe v2 Realtime), Twilio för telefoni samt OpenAI eller Anthropic som LLM. Alla dessa delar kan dock bytas ut, så du kan välja de komponenter du känner bäst till och använda dem i stället.
Den här artikeln visar hur du bygger en röstagent på 20 minuter med Node.js och Typescript. Om du vill ha ett hanterat alternativ som sköter turordning, avbrott och telefoni utan att du själv behöver underhålla kedjan, gå till ElevenAgents.
Så fungerar arkitekturen för en röstagent
Innan du skriver kod är det bra att förstå hur de tre tjänsterna i din teknikstack kopplas samman.
- Twilio: Hanterar telefonsamtalet och ljudöverföringen.
- ElevenLabs: Hanterar STT via Scribe v2 Realtime och TTS via Flash v2.5.
- LLM: Hanterar verktygsanrop och skriver svaret.
Varje steg är en enkel adapter, vilket gör att du kan byta ut en del mot en annan tjänst utan att röra resten. Du kan till exempel byta OpenAI som LLM mot Anthropic utan att skriva om dina andra komponenter.
Ett telefonsamtal når din server via Twilio. Twilio svarar på PSTN-samtalet, öppnar en WebSocket tillbaka till din server och vidarebefordrar uppringarens ljud som en ström av base64-kodade mu-law-ramar. Din server kör kedjan och strömmar syntetiserat ljud tillbaka över samma WebSocket, som Twilio spelar upp för uppringaren.

Så här ser flödet ut som du använder för att bygga en röstagent:
En uppringare ringer ditt Twilio-nummer. Twilio hämtar ett TwiML-dokument från din webhook. TwiML instruerar Twilio att öppna en Media Stream till din WebSocket-slutpunkt. Twilio strömmar inkommande ljud som JSON-händelser med base64-kodade mu-law-payloads (ulaw_8000).
Din server vidarebefordrar ljudsegment till Scribe v2 Realtime för strömmande transkribering. När en uppringares tur avslutas skickar du transkriptet till LLM:en och syntetiserar sedan svaret med Flash v2.5 i ulaw_8000. Du skickar de syntetiserade mu-law-ramarna, base64-kodade, tillbaka till Twilio via WebSocket, och Twilio spelar upp dem för uppringaren.
Scribe v2 Realtime ger partiella transkriptioner med ungefär 150 ms latens, och Flash v2.5 har cirka 75 ms modellinferens, exklusive nätverks- och applikationslatens. LLM:en bidrar mest och minst förutsägbart till tiden till första ljudet, och det är där större delen av latensbudgeten går åt. För att hålla väntan kort strömmar vi LLM-utdata token för token och börjar syntetisera innan modellen har avslutat meningen.
Information om modellavvägningarna bakom dessa val finns i modellöversikten och förklaringen om att förstå latens.
Det här behöver du innan du börjar bygga en röstagent
Guiden förutsätter att du har fyra saker på plats. Var och en går snabbt att konfigurera, men om någon saknas kan servern inte köras.
Här är förutsättningarna du behöver kontrollera:
- Ett Twilio-telefonnummer med Voice-funktion: Anteckna numret samt Account SID och Auth Token från Twilio-konsolen.
- ElevenLabs API-nyckel: Skapa den i din ElevenLabs-dashboard. Nyckeln skickas i rubriken xi-api-key och är hemlig, så förvara den bara på serversidan. Se API-autentisering.
- LLM API-nyckel: Den här handledningen behandlar Anthropic Claude och OpenAI som utbytbara backends, så välj en av dem.
- Ngrok (eller valfri tunnel) för lokal utveckling: Twilio måste kunna nå din server via en offentlig HTTPS- och WSS-URL, och ngrok ger dig det utan att du behöver driftsätta något.
Ange dina hemligheter som miljövariabler och checka aldrig in dem.
Starta sedan en tunnel till den port som servern använder:
Förstå Twilio Media Streams-protokollet
Twilio ger dig inte en rå ljudsocket. I stället paketerar tjänsten allt i ett strukturerat JSON-protokoll över WebSocket. Om du förstår de fyra händelsetyperna och sändformatet blir WebSocket-hanteraren i steg 2 helt tydlig innan du skriver den.
När Twilio har anslutit till din WebSocket skickar det en serie JSON-textmeddelanden, som kan ha fyra händelsetyper.
Händelsen connected kommer först och bekräftar att WebSocket är igång. Händelsen start skickas en gång när medieströmmen börjar. Den innehåller ett streamSid som du måste spara eftersom det krävs för att skicka tillbaka ljud, och den innehåller även samtalsmetadata under start.customParameters och start.callSid.
Händelsen media återkommer: media.payload är ett base64-kodat segment med 8 kHz mu-law-ljud, 20 ms per ram, och media.track är inbound för uppringarens ljud. Slutligen skickas stop när strömmen avslutas, vanligtvis för att samtalet har lagts på.
För att spela upp ljud skickar du ett meddelande av typen media med samma streamSid och en base64-kodad mu-law-payload. För att avbryta ljud som du redan har köat skickar du ett clear-meddelande med streamSid, vilket tömmer Twilios utgående buffert.
Kodningen för inkommande och utgående ljud är densamma (ulaw_8000). Vi begär ulaw_8000 från ElevenLabs Text to Speech och vidarebefordrar byte direkt till Twilio utan någon omsampling däremellan.
Steg 1: Hantera TwiML-webhooken
När ett samtal kommer in skickar Twilio en HTTP-begäran till din webhook, och du svarar med TwiML som ansluter samtalet till din Media Stream. Verbet <Connect><Stream> öppnar en dubbelriktad WebSocket. Använd <Connect> i stället för <Start> här: det håller samtalet vid liv under hela strömmen och låter dig skicka tillbaka ljud, vilket är syftet med den här konfigurationen.
TwiML som webhooken returnerar är:
I Express är det en enda POST-hanterare som fyller i värden för värden och returnerar dokumentet:
I Twilio-konsolen anger du numrets webhook för "A call comes in" till https://your-subdomain.ngrok.app/incoming-call med HTTP POST.
Steg 2: Ta emot Media Stream-WebSocketen
WebSocket-hanteraren läser Twilio-händelser, driver kedjan och skriver tillbaka ljud.
Vi sparar lite tillstånd per samtal: streamSid, en STT-anslutning och en flagga för om agenten talar just nu. Hanteraren avkodar varje inkommande medieram från base64 och vidarebefordrar de råa mu-law-byten till STT:
Steg 3: Transkribera med Scribe v2 Realtime
Scribe v2 Realtime tar emot strömmande ljudsegment och returnerar partiella och slutliga transkriptioner. Den har direkt stöd för mu-law-kodning, så vi skickar Twilios ramar utan ändringar.
Den erbjuder även Voice Activity Detection för tystnadsbaserad segmentering och manuell commit-kontroll för att avsluta ett segment. För en telefonagent är VAD-styrd segmentering oftast rätt val, eftersom en naturlig paus är den tillförlitligaste signalen för att uppringarens tur är slut.
Stegen är: Öppna en STT-ström när samtalet börjar. Skicka varje inkommande mu-law-segment. Reagera på slutförda transkript genom att anropa LLM:en.
Klientgränssnittet för STT i realtid utvecklas fortfarande, så strukturen nedan hålls bakom en liten TypeScript-adapter (openRealtimeStt) som du implementerar mot det aktuella API:et i stället för en fast uppsättning fältnamn. Se onFinal som hooken som skickar en färdig uppringartur till nästa steg.
Latensen för igenkänning i realtid är ungefär 150 ms för partiella resultat, vilket håller den upplevda pausen mellan att uppringaren slutar och att agenten börjar kort. Information om batchmotsvarigheten och hela funktionsuppsättningen finns i dokumentationen för Speech to Text och på produktsidan för Speech to Text i realtid.
Steg 4: Generera ett svar med en LLM
Det här är steget som skapar svaret. LLM:en tar konversationshistoriken och returnerar assistentens text. Strömma svaret så att du kan börja syntetisera vid den första meningen.
Här används OpenAI som backend:
Modell-ID:t ovan, gpt-4.1-mini, är ett exempel på ett alternativ med låg latens. claude-haiku-4-5 är ett jämförbart alternativ från Anthropic. Båda leverantörerna kan användas med samma llmReply-kontrakt. Byt ut funktionens innehåll, så förblir resten av agenten oförändrad.
Systemprompten begränsar svarsens längd, vilket är viktigt i telefon: långa svar känns långsamma och är svåra att avbryta på ett naturligt sätt.
Steg 5: Syntetisera med Flash TTS i ulaw_8000
Texten måste nu bli ljud som Twilio kan spela upp. Begär Flash v2.5 med outputFormat: "ulaw_8000" så att byten matchar Twilios förväntade kodning. Strömma sedan ljudet och vidarebefordra varje segment via WebSocket som en media-händelse.
Samla LLM-token i fragment som motsvarar meningar och syntetisera varje fragment när det är färdigt, i stället för att vänta på hela svaret. Det förkortar tiden till första ljudet eftersom uppringaren hör den första meningen medan modellen fortfarande skapar den andra. För mer detaljerad kontroll över inkrementell syntes visar guiden för TTS WebSocket i realtid hur du matar text till en enda öppen syntessocket. HTTP-strömningsmetoden nedan är enklare och räcker för korta konversationsturer.
Gör din AI-röstagent robust för produktion
Efter de fem stegen ovan har du en fungerande agent. Det är inte samma sak som en produktionsdriftsättning.
Det finns flera saker du behöver känna till innan du sätter agenten på en riktig telefonlinje.
Validera Twilio-webhooksignaturer
Alla som känner till din webhook-URL kan skicka POST-begäranden till den, så den första uppgiften är att bekräfta att begäran faktiskt kommer från Twilio. Twilio signerar varje begäran med din Auth Token i rubriken X-Twilio-Signature, och du avvisar allt som inte klarar valideringen. Signaturen beräknas utifrån hela URL:en och POST-parametrarna, så du måste beräkna den på samma sätt som Twilio gör.
Twilios hjälpfunktion gör det åt dig:
Hantera hemligheter på rätt sätt
Förvara ELEVENLABS_API_KEY, LLM-nyckeln och TWILIO_AUTH_TOKEN i en hemlighetshanterare, inte i källkoden eller i okrypterade env-filer som checkas in i ett repo. Begränsa ElevenLabs-nyckeln till bara de slutpunkter som tjänsten behöver och ge den en kreditkvot, så att konsekvenserna av en läcka begränsas.
Enterprise-planer kan dessutom begränsa en nyckel till specifika IP-intervall med IP-vitlistning. Den här servern använder API-nyckeln direkt eftersom den aldrig lämnar din backend. Om någon ljudlogik flyttades till en webbläsare eller mobilklient skulle du byta till engångstoken, så att nyckeln aldrig exponeras på klientsidan.
Förstå samtidighetsgränsen
Varje plan har en samtidighetsgräns som varierar mellan modellfamiljer, och gränsen räknar hur många begäranden som aktivt genererar ljud samtidigt.
För en telefonagent är det till din fördel. Ljudgenerering går snabbare än uppspelning, så varje samtal förbrukar bara TTS-samtidighet under de korta perioder då ett svar syntetiseras, inte under hela samtalet. Som en grov tumregel kan en samtidighetsgräns på omkring fem stödja i storleksordningen 100 samtidiga konversationssamtal, eftersom genereringen avslutas långt innan uppspelningen.
Övervaka ändå marginalen i stället för att gissa. ElevenLabs-svar innehåller rubrikerna current-concurrent-requests och maximum-concurrent-requests. Logga dem och skapa varningar när du närmar dig maxgränsen. När du överskrider gränsen köas begäranden efter prioritet, vilket vanligtvis lägger till cirka 50 ms, och vid varaktig överbelastning returneras HTTP 429.
Hantera HTTP 429-svar med en kort backoff. Om de fortsätter kan du höja gränserna genom att uppgradera på prissidan eller, för Enterprise-kunder, via din account manager.
Hantera avbrott och överlappande tal
En uppringare som börjar prata medan agenten talar förväntar sig att agenten slutar. Detta kallas barge-in, och att hantera det korrekt är en viktig del av det som får en agent att kännas naturlig snarare än skriptad.
Identifiera uppringarens tal under agentens uppspelning med STT:ns VAD-signal. När du identifierar det gör du två saker. Först slutar du vidarebefordra TTS-segment, vilket flaggan agentSpeaking i speak redan hanterar genom att avbryta loopen. Sedan skickar du Twilio ett clear-meddelande för att tömma ljudet som redan har köats hos dem.
Om du hoppar över clear fortsätter Twilio att spela buffrat ljud efter att du har slutat skicka, så det verkar som att agenten pratar över uppringaren.
Logga, övervaka och hantera fel smidigt
Instrumentera varje steg så att du kan koppla latens till rätt del när ett samtal känns långsamt. Mät tiden från slutligt transkript till första LLM-token, från första LLM-token till första TTS-byte och från första TTS-byte till ramen som skickas till Twilio. Du kommer att se att större delen av den varierande latensen finns i LLM-steget. STT- och TTS-stegen är jämförelsevis stabila.
Planera sedan för partiella fel. LLM:en kan få timeout, STT-strömmen kan brytas och tur- och returresan i nätverket till ElevenLabs varierar från ungefär 20 till 200 ms över det publika internet beroende på geografi. Placera servern nära dina uppringare, inte bara nära ElevenLabs, eftersom ElevenLabs redan dirigerar till närmaste kluster i Nordamerika, Europa och Sydostasien.
När ett steg misslyckas ska du inte lämna uppringaren i tystnad: syntetisera en kort reservreplik ("Förlåt, kan du säga det igen?") och håll samtalet vid liv. Omslut varje steg med en timeout och try/catch så att en misslyckad tur inte stänger hela WebSocket-anslutningen.
Några ytterligare standardinställningar är värda att göra innan du lanserar:
- Begränsa svarslängden i systemprompten, som visas ovan, så att turerna förblir korta och möjliga att avbryta.
- Begränsa konversationshistoriken så att långa samtal inte får LLM-kontexten att växa utan gräns.
- Ange en maximal samtalslängd som skydd mot fastnade sessioner som i tysthet förbrukar samtidighet.
För att fortsätta justera de delar du styr över förklarar latensdokumentet var tiden till första ljudet kommer från, modellöversikten beskriver avvägningarna mellan hastighet och kvalitet, och guiden för TTS WebSocket i realtid visar hur du kan sänka synteslatensen med inkrementell textinmatning.
Bygg produktionsklara röstagentar med ElevenAPI
Nu när 20 minuter har gått har du alla lager som krävs för en produktionsklar röstagent. Twilio hanterar telefoni, Scribe v2 Realtime transkriberar, en LLM genererar svar och Flash v2.5 svarar via samma WebSocket.
Om du hellre slipper underhålla kedjan själv erbjuder ElevenAgents turordning, hantering av avbrott och telefoniintegration som en hanterad tjänst byggd på samma modeller som du just kopplade ihop för hand.
För att fortsätta justera en stack som du själv styr över kan du utforska produktsidan för ElevenAPI med planer, samtidighetsgränser och röstbiblioteket. Du kan också registrera dig och komma igång i dag med ditt första samtal.



