Crie um agente de voz em 20 minutos com ElevenLabs e Twilio
- Publicado
- Última atualização
OuvirOuça este artigo
Um agente de voz pode atender chamadas recebidas, transcrever o que o usuário fala em tempo real com Speech to Text (STT), gerar respostas com um modelo de linguagem (LLM) e responder usando um módulo de Transformar Texto em Áudio (TTS). Com ElevenLabs e Twilio, você consegue colocar um agente funcionando em um número real em cerca de 20 minutos.
Para desenvolvedores, a stack completa que você vai usar inclui ElevenLabs para síntese de voz (Flash v2.5) e transcrição (Scribe v2 Realtime), Twilio para telefonia e OpenAI ou Anthropic como LLM. Todos esses componentes podem ser trocados, então você pode escolher os que já conhece e usar no seu projeto.
Este artigo mostra como criar um agente de voz em 20 minutos, usando Node.js e Typescript. Se você prefere uma solução gerenciada que cuida de turnos de fala, interrupções e integração com telefonia sem precisar montar tudo do zero, acesse o ElevenAgents.
Como funciona a arquitetura de um agente de voz
Antes de começar a programar, é importante entender como os três serviços da sua stack se conectam.
- Twilio: cuida da ligação telefônica e do transporte do áudio.
- ElevenLabs: faz a transcrição com Scribe v2 Realtime e a síntese de voz com Flash v2.5.
- LLM: gera a resposta e executa chamadas de ferramentas.
Cada etapa é um adaptador simples, por isso você pode trocar um serviço por outro sem mexer no restante. Por exemplo, dá para trocar o LLM da OpenAI pelo Anthropic sem precisar reescrever os outros componentes.
Uma ligação chega ao seu servidor via Twilio. O Twilio atende a chamada PSTN, abre um WebSocket para o seu servidor e encaminha o áudio do usuário como um fluxo de frames mu-law codificados em base64. Seu servidor executa a cadeia de processamento e envia o áudio sintetizado de volta pelo mesmo WebSocket, e o Twilio reproduz para quem ligou.

Veja o fluxo que você vai usar para criar um agente de voz:
O usuário liga para seu número Twilio. O Twilio busca um documento TwiML no seu webhook. O TwiML instrui o Twilio a abrir um Media Stream para o seu endpoint WebSocket. O Twilio transmite o áudio recebido como eventos JSON contendo payloads mu-law base64 (ulaw_8000).
Seu servidor encaminha os pedaços de áudio para o Scribe v2 Tempo Real para transcrição em tempo real. Quando o turno do usuário termina, você envia a transcrição para o LLM e depois sintetiza a resposta com o Flash v2.5 em ulaw_8000. Você envia os frames mu-law sintetizados de volta para o Twilio pelo WebSocket, em base64, e o Twilio reproduz para o usuário.
O Scribe v2 Realtime gera transcrições parciais com cerca de 150ms de latência, e o Flash v2.5 roda com aproximadamente 75ms de inferência, sem contar a latência de rede e aplicação. O LLM é o maior e mais imprevisível fator para o tempo até o primeiro áudio, e é onde vai a maior parte da latência. Para reduzir esse tempo, transmitimos a resposta do LLM token por token e já começamos a sintetizar antes do modelo terminar a frase.
Para entender as escolhas dos modelos, veja a visão geral dos modelos e o explicativo sobre latência.
O que você precisa antes de começar a criar um agente de voz
Este guia parte do princípio que você já tem quatro itens prontos. Cada um é rápido de configurar, mas faltar qualquer um deles impede o servidor de rodar.
Confira os pré-requisitos:
- Um número Twilio com capacidade de voz: Anote o número junto com seu Account SID e Auth Token no console do Twilio.
- Chave da API ElevenLabs: criada no seu painel da ElevenLabs. A chave vai no cabeçalho xi-api-key e é secreta, então mantenha apenas no servidor. Veja
- Chave de API do LLM:Este tutorial trata Anthropic Claude e OpenAI como backends intercambiáveis, então escolha um.
- Ngrok (ou outro túnel) para desenvolvimento local: O Twilio precisa acessar seu servidor por HTTPS e WSS públicos, e o ngrok resolve isso sem precisar fazer deploy.
Defina seus segredos como variáveis de ambiente e nunca os envie para o repositório.
Depois, inicie um túnel apontando para a porta que seu servidor vai usar:
Entendendo o protocolo Twilio Media Streams
O Twilio não fornece um socket de áudio bruto. Em vez disso, ele encapsula tudo em um protocolo JSON estruturado via WebSocket. Entender os quatro tipos de eventos e o formato de envio faz com que o handler do WebSocket no Passo 2 fique claro antes mesmo de você escrever.
Assim que o Twilio conecta ao seu WebSocket, ele envia uma sequência de mensagens de texto JSON, que podem ter quatro tipos de eventos.
O evento connected chega primeiro e confirma que o WebSocket está ativo. O evento start é enviado uma vez quando o media stream começa; ele traz um streamSid que você precisa guardar, pois será usado para enviar áudio de volta, além de incluir metadados da chamada em start.customParameters e start.callSid.
O evento media é recorrente: media.payload é um pedaço de áudio mu-law 8kHz em base64, 20ms por frame, e media.track indica que é áudio do usuário. Por fim, o evento stop é enviado quando o stream termina, normalmente porque a ligação foi encerrada.
Para reproduzir áudio, envie uma mensagem do tipo media com o mesmo streamSid e um payload mu-law em base64. Para interromper o áudio já na fila, envie uma mensagem clear com o streamSid, que limpa o buffer de saída do Twilio.
As codificações de entrada e saída são idênticas (ulaw_8000). Pedimos ulaw_8000 no Transformar Texto em Áudio da ElevenLabs e encaminhamos os bytes direto para o Twilio, sem fazer resample.
Passo 1: Servir o webhook TwiML
Quando chega uma ligação, o Twilio faz uma requisição HTTP para seu webhook, e você responde com um TwiML que conecta a chamada ao seu Media Stream. O verbo <Connect><Stream> abre um WebSocket bidirecional. Use <Connect> em vez de <Start> aqui: isso mantém a chamada ativa durante todo o stream e permite enviar áudio de volta, que é o objetivo desse fluxo.
O TwiML que o webhook retorna é:
No Express, isso é um único handler POST que preenche o host e retorna o documento:
No console do Twilio, configure o webhook "Quando uma chamada chega" do número para https://your-subdomain.ngrok.app/incoming-call usando HTTP POST.
Passo 2: Aceitar o WebSocket do Media Stream
O handler do WebSocket lê os eventos do Twilio, executa a cadeia de processamento e envia o áudio de volta.
Mantemos um pequeno estado por chamada: o streamSid, uma conexão STT e um flag para saber se o agente está falando. O handler decodifica cada frame de mídia recebido de base64 e encaminha os bytes mu-law brutos para o STT:
Passo 3: Transcrever com Scribe v2 Realtime
O Scribe v2 Realtime aceita pedaços de áudio em streaming e retorna transcrições parciais e finais, além de suportar codificação mu-law diretamente, então enviamos os frames do Twilio sem alterações.
Ele também oferece Detecção de Atividade de Voz (VAD) para segmentação por silêncio e controle manual para finalizar um segmento. Para um agente telefônico, a segmentação guiada por VAD costuma ser a melhor escolha, pois uma pausa natural é o sinal mais confiável de que o turno do usuário terminou.
Veja os passos: Abra um stream STT quando a chamada começar. Envie cada pedaço mu-law recebido. Reaja às transcrições finalizadas chamando o LLM.
O cliente STT em tempo real ainda está evoluindo, então o exemplo abaixo fica atrás de um pequeno adaptador TypeScript (openRealtimeStt) que você implementa usando a API ao vivo, não um conjunto fixo de campos. Considere o onFinal como o gancho que entrega o turno finalizado do usuário para a próxima etapa.
A latência de reconhecimento em tempo real é de cerca de 150ms para parciais, o que mantém o intervalo entre o usuário terminar de falar e o agente começar bem curto. Para a versão batch e todos os recursos, veja a documentação do Speech to Text e a página do produto Speech to Text em tempo real.
Passo 4: Gerar uma resposta com o LLM
Esta é a etapa que produz a resposta. O LLM recebe o histórico da conversa e retorna o texto do assistente. Transmita a resposta para já começar a síntese assim que sair a primeira frase.
Aqui, usamos o OpenAI:
O modelo acima, gpt-4.1-mini, é uma opção de baixa latência; o claude-haiku-4-5 é uma alternativa similar da Anthropic. Qualquer um pode ser usado no mesmo contrato llmReply; basta trocar o corpo da função e o restante do agente segue igual.
O prompt do sistema limita o tamanho da resposta, o que é importante no telefone: respostas longas parecem lentas e são difíceis de interromper naturalmente.
Passo 5: Sintetizar com Flash TTS em ulaw_8000
Agora o texto precisa virar áudio para o Twilio reproduzir. Solicite o Flash v2.5 com outputFormat: "ulaw_8000" para que os bytes estejam no formato esperado pelo Twilio, depois transmita o áudio e envie cada pedaço de volta pelo WebSocket como um evento media.
Junte os tokens do LLM em fragmentos do tamanho de frases e sintetize cada um assim que estiver pronto, em vez de esperar a resposta inteira. Isso reduz o tempo até o primeiro áudio, pois o usuário já ouve a primeira frase enquanto o modelo ainda está gerando a segunda. Para mais controle sobre a síntese incremental, o guia de TTS em tempo real via WebSocket mostra como enviar texto para um único socket de síntese aberto; o método HTTP streaming abaixo é mais simples e suficiente para conversas curtas.
Deixando seu agente de voz com IA pronto para produção
Depois dos cinco passos acima, você já tem um agente funcional. Mas isso não é o mesmo que um ambiente de produção.
Existem alguns pontos importantes para considerar antes de colocar o agente em um número real.
Valide as assinaturas do webhook do Twilio
Qualquer pessoa que descobrir a URL do seu webhook pode fazer POST nela, então o primeiro passo é confirmar que a requisição veio mesmo do Twilio. O Twilio assina cada requisição com seu Auth Token no header X-Twilio-Signature, e você deve rejeitar qualquer uma que não passe na validação. A assinatura é calculada sobre a URL completa e os parâmetros do POST, então você precisa calcular igual ao Twilio.
O helper do Twilio faz isso para você:
Gerencie segredos corretamente
Guarde o ELEVENLABS_API_KEY, a chave do LLM e o TWILIO_AUTH_TOKEN em um gerenciador de segredos, nunca no código-fonte ou em arquivos .env em texto aberto no repositório. Restrinja a chave da ElevenLabs apenas aos endpoints que este serviço precisa e defina um limite de crédito, assim, se vazar, o impacto é limitado.
Planos Enterprise podem ainda restringir a chave a faixas de IP específicas com whitelist. Este servidor usa a chave da API diretamente porque ela nunca sai do backend; se alguma lógica de áudio for para o navegador ou app móvel, use tokens de uso único para que a chave nunca fique exposta no cliente.
Entenda o limite de concorrência
Cada plano tem um limite de concorrência que varia conforme a família de modelos, e o limite conta quantas requisições estão gerando áudio ao mesmo tempo.
Para um agente telefônico, esse controle joga a seu favor. A geração de áudio é mais rápida que a reprodução, então cada chamada só consome concorrência de TTS durante os breves momentos em que a resposta está sendo sintetizada, não durante toda a ligação. Como referência, um limite de concorrência de cinco pode suportar cerca de 100 transmissões simultâneas, pois a geração termina bem antes da reprodução.
Mesmo assim, monitore o uso em vez de adivinhar. As respostas da ElevenLabs mostram os headers current-concurrent-requests e maximum-concurrent-requests; registre e crie alertas quando estiver perto do máximo. Se passar do limite, as requisições entram em fila por prioridade, o que normalmente adiciona cerca de 50ms, e sobrecarga contínua retorna HTTP 429.
Trate respostas HTTP 429 com um pequeno backoff. Se persistirem, aumente o limite na página de preços ou, para clientes Enterprise, com seu gerente de conta.
Lide com barge-in e interrupções
Quando o usuário começa a falar enquanto o agente está falando, espera que o agente pare. Isso é barge-in, e lidar bem com isso faz o agente parecer natural, não robótico.
Detecte a fala do usuário durante a reprodução do agente usando o sinal VAD do STT. Quando detectar, faça duas coisas: pare de enviar pedaços de TTS (o flag agentSpeaking já cuida disso interrompendo o loop) e envie uma mensagem clear para o Twilio, limpando o áudio já na fila.
Se você não enviar o clear, o Twilio continua tocando o áudio em buffer mesmo depois de você parar de enviar, e o agente parece falar por cima do usuário.
Registre, monitore e trate falhas de forma elegante
Instrumente cada etapa para identificar onde está a latência quando uma chamada parecer lenta. Meça o tempo do transcript final até o primeiro token do LLM, do primeiro token do LLM até o primeiro byte do TTS, e do primeiro byte do TTS até o frame enviado ao Twilio. Você vai perceber que a maior variação está no LLM; as etapas de STT e TTS são mais estáveis.
Planeje também para falhas parciais. O LLM pode dar timeout, o stream STT pode cair e a latência de rede até a ElevenLabs varia de 20 a 200ms dependendo da região. Coloque seu servidor perto dos usuários, não só da ElevenLabs, pois a ElevenLabs já direciona para o cluster mais próximo (América do Norte, Europa ou Sudeste Asiático).
Quando uma etapa falhar, não deixe o usuário no silêncio: sintetize uma frase curta de fallback ("Desculpe, pode repetir?") e mantenha a ligação ativa. Envolva cada etapa em timeout e try/catch para que uma falha não derrube todo o WebSocket.
Alguns padrões ainda valem a pena definir antes de colocar em produção:
- Limite o tamanho das respostas no prompt do sistema, como mostrado, para que os turnos fiquem curtos e fáceis de interromper.
- Limite o histórico da conversa para que chamadas longas não aumentem o contexto do LLM sem controle.
- Defina uma duração máxima de chamada para evitar sessões travadas consumindo concorrência.
Para continuar ajustando o que está sob seu controle, o documento de latência explica de onde vem o tempo até o primeiro áudio, a visão geral dos modelos mostra os tradeoffs de velocidade e qualidade, e o guia de TTS em tempo real via WebSocket mostra como reduzir ainda mais a latência de síntese com entrada de texto incremental.
Crie agentes de voz prontos para produção com ElevenAPI
Agora que se passaram 20 minutos, você já tem todas as camadas de um agente de voz para produção. O Twilio cuida da telefonia, o Scribe v2 Realtime faz a transcrição, o LLM gera as respostas e o Flash v2.5 fala de volta pelo mesmo WebSocket.
Se você prefere não manter toda a cadeia por conta própria, o ElevenAgents oferece turnos de fala, tratamento de interrupções e integração com telefonia como serviço gerenciado, usando os mesmos modelos que você acabou de conectar manualmente.
Para continuar ajustando sua stack, explore a página do produto ElevenAPI para ver planos, limites de concorrência e a voice library. Ou então, cadastre-se e comece hoje mesmo a fazer sua primeira ligação.
.webp&w=3840&q=80)
.webp&w=3840&q=80)

.webp&w=3840&q=80)
