Zbuduj agenta głosowego w 20 minut z ElevenLabs i Twilio
- Opublikowano
- Ostatnia aktualizacja
PosłuchajPosłuchaj tego artykułu
Agent głosowy może odbierać połączenia przychodzące, transkrybować rozmówców w czasie rzeczywistym za pomocą zamiany mowy na tekst (STT), generować odpowiedzi za pomocą dużego modelu językowego (LLM) i odpowiadać głosem dzięki modułowi zamiany tekstu na mowę (TTS). Z ElevenLabs i Twilio uruchomisz działającego agenta pod prawdziwym numerem telefonu w około 20 minut.
Dla deweloperów: cały stos obejmuje ElevenLabs do syntezy mowy (Flash v2.5) i transkrypcji (Scribe v2 Realtime), Twilio do telefonii oraz OpenAI lub Anthropic jako LLM. Każdy z tych elementów można jednak wymienić, więc możesz wybrać komponenty, które znasz najlepiej.
Ten artykuł pokazuje, jak zbudować agenta głosowego w 20 minut, używając Node.js i Typescript. Jeśli wolisz zarządzaną alternatywę, która obsługuje zmianę tur, przerwania i telefonię bez samodzielnego utrzymywania całego łańcucha, przejdź do ElevenAgents.
Jak działa architektura agenta głosowego
Zanim napiszesz kod, warto zrozumieć, jak łączą się trzy usługi w twoim stacku technologicznym.
- Twilio: Obsługuje połączenie telefoniczne i przesyłanie audio.
- ElevenLabs: Obsługuje STT przez Scribe v2 Realtime i TTS przez Flash v2.5.
- LLM: Obsługuje wywoływanie narzędzi i tworzenie odpowiedzi.
Każdy etap to prosty adapter, dlatego możesz zamienić jedną usługę na inną bez ruszania reszty. Na przykład możesz zastąpić LLM OpenAI rozwiązaniem Anthropic bez przepisywania pozostałych komponentów.
Połączenie telefoniczne trafia na twój serwer przez Twilio. Twilio odbiera połączenie PSTN, otwiera WebSocket do twojego serwera i przekazuje audio rozmówcy jako strumień ramek mu-law zakodowanych w base64. Twój serwer uruchamia łańcuch i przesyła zsyntetyzowane audio z powrotem przez ten sam WebSocket, a Twilio odtwarza je rozmówcy.

Oto przepływ, którego użyjesz do zbudowania agenta głosowego:
Rozmówca wybiera twój numer Twilio. Twilio pobiera dokument TwiML z twojego webhooka. TwiML instruuje Twilio, aby otworzył Media Stream do endpointu WebSocket. Twilio przesyła przychodzące audio jako zdarzenia JSON zawierające ładunki mu-law (ulaw_8000) zakodowane w base64.
Twój serwer przekazuje fragmenty audio do Scribe v2 Realtime w celu transkrypcji strumieniowej. Gdy wypowiedź rozmówcy zostanie zakończona, wysyłasz transkrypcję do LLM, a następnie syntezujesz odpowiedź za pomocą Flash v2.5 w formacie ulaw_8000. Wysyłasz zsyntetyzowane ramki mu-law z powrotem do Twilio przez WebSocket, zakodowane w base64, a Twilio odtwarza je rozmówcy.
Scribe v2 Realtime generuje częściowe transkrypcje z opóźnieniem około 150 ms, a Flash v2.5 działa z inferencją modelu na poziomie około 75 ms, bez opóźnień sieci i aplikacji. LLM ma największy i najmniej przewidywalny wpływ na czas do pierwszego audio, więc pochłania większość budżetu opóźnień. Aby skrócić przerwę, przesyłamy wynik LLM token po tokenie i zaczynamy syntezę, zanim model dokończy zdanie.
Więcej o kompromisach między modelami znajdziesz w przeglądzie modeli oraz w materiale o opóźnieniach.
Czego potrzebujesz, zanim zaczniesz budować agenta głosowego
Ten przewodnik zakłada, że masz przygotowane cztery rzeczy. Każdą skonfigurujesz szybko, ale brak którejkolwiek uniemożliwi uruchomienie serwera.
Sprawdź te wymagania:
- Numer Twilio z obsługą Voice: Zanotuj numer, Account SID i Auth Token z konsoli Twilio.
- Klucz API ElevenLabs: Utworzony w panelu ElevenLabs. Klucz jest przesyłany w nagłówku xi-api-key i jest tajny, więc przechowuj go wyłącznie po stronie serwera. Zobacz uwierzytelnianie API.
- Klucz API LLM: W tym poradniku Anthropic Claude i OpenAI są wymiennymi backendami, więc wybierz jeden z nich.
- Ngrok (lub dowolny tunel) do pracy lokalnej: Twilio musi dotrzeć do twojego serwera przez publiczny adres HTTPS i WSS, a ngrok zapewnia to bez wdrażania czegokolwiek.
Ustaw sekrety jako zmienne środowiskowe i nigdy ich nie commituj.
Następnie uruchom tunel wskazujący na port, którego użyje serwer:
Protokół Twilio Media Streams
Twilio nie udostępnia surowego gniazda audio. Zamiast tego opakowuje wszystko w ustrukturyzowany protokół JSON przez WebSocket. Gdy zrozumiesz cztery typy zdarzeń i format wysyłki, handler WebSocket z kroku 2 będzie w pełni jasny jeszcze przed napisaniem kodu.
Gdy Twilio połączy się z twoim WebSocketem, wysyła serię tekstowych wiadomości JSON, które mogą mieć cztery typy zdarzeń.
Zdarzenie connected przychodzi jako pierwsze i potwierdza działanie WebSocketu. Zdarzenie start jest wysyłane raz, gdy zaczyna się strumień mediów; zawiera streamSid, który musisz zapisać, aby odsyłać audio, oraz metadane połączenia w start.customParameters i start.callSid.
Zdarzenie media powtarza się: media.payload to zakodowany w base64 fragment audio mu-law 8 kHz, po 20 ms na ramkę, a media.track ma wartość inbound dla audio rozmówcy. Na końcu wysyłane jest stop, gdy strumień się kończy, zwykle po rozłączeniu rozmowy.
Aby odtworzyć audio, wysyłasz wiadomość typu media z tym samym streamSid i ładunkiem mu-law zakodowanym w base64. Aby przerwać audio, które już zakolejkowałeś, wysyłasz wiadomość clear ze streamSid, która czyści bufor wyjściowy Twilio.
Kodowanie wejściowe i wyjściowe jest identyczne (ulaw_8000). Żądamy ulaw_8000 od ElevenLabs Text to Speech i przekazujemy bajty prosto do Twilio, bez resamplingu po drodze.
Krok 1: Obsłuż webhook TwiML
Gdy nadchodzi połączenie, Twilio wysyła żądanie HTTP do twojego webhooka, a ty odpowiadasz TwiML, który łączy rozmowę z Media Stream. Czasownik <Connect><Stream> otwiera dwukierunkowy WebSocket. Użyj tu <Connect> zamiast <Start>: utrzymuje rozmowę przez cały czas trwania strumienia i pozwala odsyłać audio — to cel tej konfiguracji.
Webhook zwraca następujący TwiML:
W Express wystarczy jeden handler POST, który uzupełnia host i zwraca dokument:
W konsoli Twilio ustaw webhook „A call comes in” tego numeru na https://your-subdomain.ngrok.app/incoming-call metodą HTTP POST.
Krok 2: Odbierz WebSocket Media Stream
Handler WebSocket odczytuje zdarzenia Twilio, steruje łańcuchem i odsyła audio.
Przechowujemy niewielki stan dla każdego połączenia: streamSid, połączenie STT oraz flagę określającą, czy agent aktualnie mówi. Handler dekoduje każdą przychodzącą ramkę media z base64 i przekazuje surowe bajty mu-law do STT:
Krok 3: Transkrybuj z Scribe v2 Realtime
Scribe v2 Realtime przyjmuje strumieniowe fragmenty audio i zwraca częściowe oraz końcowe transkrypcje. Obsługuje też bezpośrednio kodowanie mu-law, więc przekazujemy mu ramki Twilio bez zmian.
Oferuje też Voice Activity Detection do segmentacji na podstawie ciszy oraz ręczne sterowanie commitowaniem, aby finalizować segment. Dla agenta telefonicznego, segmentacja oparta na VAD to zwykle najlepszy wybór, bo naturalna pauza jest najpewniejszym sygnałem końca wypowiedzi rozmówcy.
Kroki są proste: otwórz strumień STT na początku rozmowy. Wysyłaj każdy przychodzący fragment mu-law. Reaguj na finalne transkrypcje, wywołując LLM.
Interfejs klienta STT w czasie rzeczywistym nadal się zmienia, dlatego poniższy kształt ukryto za małym adapterem TypeScript (openRealtimeStt), który implementujesz względem aktualnego API, a nie stałego zestawu nazw pól. Traktuj onFinal jako hook przekazujący ukończoną wypowiedź rozmówcy do kolejnego etapu.
Opóźnienie rozpoznawania w czasie rzeczywistym dla częściowych wyników wynosi około 150 ms, dzięki czemu przerwa między zakończeniem wypowiedzi rozmówcy a początkiem odpowiedzi agenta jest krótka. Informacje o odpowiedniku wsadowym i pełnym zestawie funkcji znajdziesz w dokumentacji Speech to Text oraz na stronie produktu Speech to Text w czasie rzeczywistym.
Krok 4: Wygeneruj odpowiedź za pomocą LLM
Na tym etapie powstaje odpowiedź. LLM przyjmuje historię rozmowy i zwraca tekst asystenta. Przesyłaj odpowiedź strumieniowo, aby rozpocząć syntezę już przy pierwszym zdaniu.
Tutaj używamy OpenAI:
Powyższy identyfikator modelu, gpt-4.1-mini, to przykład modelu o niskim opóźnieniu; porównywalną opcją od Anthropic jest claude-haiku-4-5. Każdy z dostawców może obsłużyć ten sam kontrakt llmReply — zamień treść funkcji, a reszta agenta pozostanie bez zmian.
Prompt systemowy ogranicza długość odpowiedzi, co ma znaczenie w rozmowach telefonicznych: długie odpowiedzi wydają się wolne i trudno je naturalnie przerwać.
Krok 5: Syntezuj z Flash TTS w ulaw_8000
Tekst musi teraz stać się audio, które Twilio może odtworzyć. Poproś Flash v2.5 o outputFormat: "ulaw_8000", aby bajty odpowiadały oczekiwanemu przez Twilio kodowaniu, następnie przesyłaj audio strumieniowo i odsyłaj każdy fragment przez WebSocket jako zdarzenie media.
Zbieraj tokeny LLM w fragmenty wielkości zdania i syntezuj każdy, gdy będzie gotowy, zamiast czekać na całą odpowiedź. Skraca to czas do pierwszego audio, ponieważ rozmówca słyszy pierwsze zdanie, gdy model wciąż tworzy drugie. Aby dokładniej sterować syntezą przyrostową, przewodnik po WebSocket TTS w czasie rzeczywistym pokazuje, jak przekazywać tekst do jednego otwartego gniazda syntezy; poniższe podejście ze strumieniowaniem HTTP jest prostsze i wystarcza do krótkich tur rozmowy.
Przygotuj agenta głosowego AI do produkcji
Po wykonaniu powyższych pięciu kroków masz działającego agenta. To nie to samo co wdrożenie produkcyjne.
Zanim podłączysz agenta do prawdziwej linii telefonicznej, musisz uwzględnić kilka kwestii.
Weryfikuj podpisy webhooków Twilio
Każdy, kto pozna adres URL twojego webhooka, może wysłać do niego żądanie POST, więc najpierw potwierdź, że żądanie rzeczywiście pochodzi od Twilio. Twilio podpisuje każde żądanie twoim Auth Token w nagłówku X-Twilio-Signature, a ty odrzucasz wszystko, co nie przejdzie weryfikacji. Podpis jest obliczany na podstawie pełnego adresu URL i parametrów POST, więc musisz obliczać go tak samo jak Twilio.
Helper Twilio zrobi to za ciebie:
Właściwie zarządzaj sekretami
Przechowuj ELEVENLABS_API_KEY, klucz LLM i TWILIO_AUTH_TOKEN w menedżerze sekretów, a nie w kodzie źródłowym ani w plikach env w postaci zwykłego tekstu commitowanych do repozytorium. Ogranicz klucz ElevenLabs do endpointów potrzebnych tej usłudze i ustaw dla niego limit kredytów, aby wyciek miał ograniczone skutki.
Plany Enterprise pozwalają też ograniczyć klucz do konkretnych zakresów IP przez białą listę IP. Ten serwer używa klucza API bezpośrednio, ponieważ nigdy nie opuszcza on backendu; gdyby logika audio trafiła do przeglądarki lub klienta mobilnego, użyj tokenów jednorazowych, aby klucz nigdy nie został ujawniony po stronie klienta.
Poznaj limit współbieżności
Każdy plan ma limit współbieżności różny dla poszczególnych rodzin modeli. Limit określa, ile żądań aktywnie generuje audio w tym samym czasie.
W przypadku agenta telefonicznego działa to na twoją korzyść. Generowanie audio jest szybsze niż odtwarzanie, więc każde połączenie korzysta ze współbieżności TTS tylko przez krótkie chwile syntezy odpowiedzi, a nie przez całą rozmowę. Orientacyjnie limit współbieżności około pięciu może obsłużyć około 100 jednoczesnych rozmów, ponieważ generowanie kończy się dużo przed odtwarzaniem.
Mimo to monitoruj zapas zamiast zgadywać. Odpowiedzi ElevenLabs zawierają nagłówki current-concurrent-requests i maximum-concurrent-requests; zapisuj je w logach i ustaw alerty, gdy zbliżasz się do maksimum. Po przekroczeniu limitu żądania trafiają do kolejki według priorytetu, co zwykle dodaje około 50 ms, a długotrwałe przeciążenie zwraca HTTP 429.
Obsługuj odpowiedzi HTTP 429 krótkim backoffem. Jeśli się powtarzają, zwiększ limity przez zmianę planu na stronie cenowej lub, w przypadku klientów Enterprise, przez opiekuna konta.
Obsłuż wtrącenia i przerwania
Rozmówca, który zaczyna mówić, gdy agent odpowiada, oczekuje, że agent przestanie. To wtrącenie (barge-in), a jego poprawna obsługa w dużej mierze decyduje o tym, czy agent brzmi naturalnie, a nie jak skrypt.
Wykrywaj mowę rozmówcy podczas odtwarzania odpowiedzi agenta za pomocą sygnału STT VAD. Gdy ją wykryjesz, zrób dwie rzeczy. Po pierwsze, przestań przekazywać fragmenty TTS — flaga agentSpeaking w speak już to obsługuje, przerywając pętlę. Po drugie, wyślij do Twilio wiadomość clear, aby wyczyścić audio, które już zakolejkowałeś po jego stronie.
Jeśli pominiesz clear, Twilio będzie dalej odtwarzać zbuforowane audio po zatrzymaniu wysyłania, więc agent będzie sprawiał wrażenie, że mówi jednocześnie z rozmówcą.
Loguj, monitoruj i obsługuj błędy bez zakłóceń
Dodaj pomiary na każdym etapie, aby przypisać źródło opóźnienia, gdy rozmowa wydaje się wolna. Mierz czas od finalnej transkrypcji do pierwszego tokenu LLM, od pierwszego tokenu LLM do pierwszego bajtu TTS oraz od pierwszego bajtu TTS do ramki wysłanej do Twilio. Większość zmiennego opóźnienia znajdziesz na etapie LLM; etapy STT i TTS są stosunkowo stabilne.
Zaplanuj też częściowe awarie. LLM może przekroczyć limit czasu, strumień STT może się zerwać, a czas podróży sieciowej do ElevenLabs przez publiczny internet zależnie od lokalizacji waha się od około 20 do 200 ms. Umieść serwer blisko rozmówców, nie tylko blisko ElevenLabs, ponieważ ElevenLabs i tak kieruje ruch do najbliższego klastra w Ameryce Północnej, Europie lub Azji Południowo-Wschodniej.
Gdy etap zawiedzie, nie zostawiaj rozmówcy w ciszy: zsyntezuj krótką komunikat awaryjny („Przepraszam, możesz powtórzyć?”) i utrzymaj rozmowę. Owiń każdy etap w timeout i try/catch, aby jedna nieudana tura nie zamknęła całego WebSocketu.
Przed wdrożeniem warto ustawić jeszcze kilka domyślnych wartości:
- Ogranicz długość odpowiedzi w prompcie systemowym, jak pokazano wyżej, aby tury były krótkie i łatwe do przerwania.
- Ogranicz historię rozmowy, aby długie połączenia nie zwiększały bez końca kontekstu LLM.
- Ustaw maksymalny czas rozmowy jako zabezpieczenie przed zawieszonymi sesjami, które po cichu zużywają współbieżność.
Aby dalej optymalizować elementy, na które masz wpływ, dokument o opóźnieniach wyjaśnia, skąd bierze się czas do pierwszego audio, przegląd modeli omawia kompromisy między szybkością i jakością, a przewodnik po WebSocket TTS w czasie rzeczywistym pokazuje, jak jeszcze bardziej skrócić opóźnienie syntezy dzięki przyrostowemu wejściu tekstowemu.
Buduj gotowe do produkcji agenty głosowe z ElevenAPI
Po 20 minutach masz już wszystkie warstwy produkcyjnego agenta głosowego. Twilio obsługuje telefonię, Scribe v2 Realtime transkrybuje, LLM generuje odpowiedzi, a Flash v2.5 odpowiada przez ten sam WebSocket.
Jeśli nie chcesz samodzielnie utrzymywać całego łańcucha, ElevenAgents oferuje zmianę tur, obsługę przerwań i integrację telefonii jako zarządzaną usługę opartą na tych samych modelach, które właśnie połączyłeś ręcznie.
Aby dalej optymalizować stack, nad którym masz kontrolę, odwiedź stronę produktu ElevenAPI i poznaj plany, limity współbieżności oraz Voice Library. Możesz też założyć konto i zacząć już dziś, wykonując pierwsze połączenie.



