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 telefônicas recebidas, transcrever os interlocutores em tempo real com Speech to Text (STT), gerar uma resposta com um modelo de linguagem grande (LLM) e responder por voz com um módulo de Text to Speech (TTS). Com ElevenLabs e Twilio, você pode ter um agente funcional em um número de telefone real em cerca de 20 minutos.
Para desenvolvedores, a stack completa que você usará inclui a ElevenLabs para síntese de voz (Flash v2.5) e transcrição (Scribe v2 Realtime), a Twilio para telefonia e OpenAI ou Anthropic como LLM. Ainda assim, todos esses componentes são intercambiáveis, ou seja, você pode escolher os que conhece melhor e usá-los no lugar.
Este artigo mostra como criar um agente de voz em 20 minutos usando Node.js e Typescript. Se quiser uma alternativa gerenciada que lide com alternância de turnos, interrupções e telefonia sem que você precise manter a cascata, conheça o ElevenAgents.
Como funciona a arquitetura de um agente de voz
Antes de escrever qualquer código, é útil entender como os três serviços da sua stack se conectam.
- Twilio: gerencia a chamada telefônica e o transporte de áudio.
- ElevenLabs: gerencia STT pelo Scribe v2 Realtime e TTS pelo Flash v2.5.
- LLM: gerencia chamadas de ferramentas e a criação da resposta.
Cada etapa é um adaptador simples, por isso você pode trocar uma por outro serviço sem alterar o restante. Por exemplo, pode substituir o LLM da OpenAI pelo Anthropic sem precisar reescrever os outros componentes.
Uma chamada telefônica chega ao seu servidor pela Twilio. A Twilio atende a chamada PSTN, abre um WebSocket de volta para seu servidor e encaminha o áudio do interlocutor como um stream de frames mu-law codificados em base64. Seu servidor executa a cascata e transmite o áudio sintetizado de volta pelo mesmo WebSocket, e a Twilio o reproduz para o interlocutor.

Este é o fluxo que você usará para criar um agente de voz:
Um interlocutor liga para seu número da Twilio. A Twilio busca um documento TwiML no seu webhook. O TwiML instrui a Twilio a abrir um Media Stream para seu endpoint WebSocket. A Twilio transmite o áudio de entrada como eventos JSON com payloads mu-law (ulaw_8000) em base64.
Seu servidor encaminha blocos de áudio para o Scribe v2 Realtime para transcrição em streaming. Quando o turno do interlocutor é finalizado, você envia a transcrição ao LLM e sintetiza a resposta com o Flash v2.5 em ulaw_8000. Você envia os frames mu-law sintetizados de volta à Twilio pelo WebSocket, codificados em base64, e a Twilio os reproduz para o interlocutor.
O Scribe v2 Realtime emite transcrições parciais com latência de cerca de 150 ms, e o Flash v2.5 executa a inferência do modelo em aproximadamente 75 ms, sem considerar a latência de rede e da aplicação. O LLM é o maior e menos previsível fator no tempo até o primeiro áudio, e é nele que vai a maior parte do orçamento de latência. Para manter esse intervalo curto, transmitimos a saída do LLM token a token e começamos a sintetizar antes de o modelo terminar a frase.
Para conhecer os compromissos entre os modelos por trás dessas escolhas, consulte a visão geral dos modelos e a explicação sobre como entender a latência.
O que você precisa antes de começar a criar um agente de voz
O guia pressupõe que você já tenha quatro itens prontos. Cada um é rápido de configurar, mas a ausência de qualquer um deles impedirá o servidor de executar.
Confira estes pré-requisitos:
- Um número da Twilio com capacidade de Voice: anote o número, o Account SID e o Auth Token no console da Twilio.
- Chave de API da ElevenLabs: criada no painel da ElevenLabs. A chave é enviada no cabeçalho xi-api-key e é secreta, portanto mantenha-a apenas no servidor. Consulte autenticação da API.
- Chave de API do LLM: este tutorial trata o Anthropic Claude e o OpenAI como backends intercambiáveis, então escolha um deles.
- Ngrok (ou qualquer túnel) para desenvolvimento local: a Twilio precisa alcançar seu servidor por uma URL HTTPS e WSS pública, e o ngrok oferece isso sem a necessidade de implantar nada.
Defina seus segredos como variáveis de ambiente e nunca faça commit deles.
Em seguida, inicie um túnel apontando para a porta que seu servidor usará:
Como entender o protocolo Twilio Media Streams
A Twilio não fornece um socket de áudio bruto. Em vez disso, ela encapsula tudo em um protocolo JSON estruturado via WebSocket. Entender os quatro tipos de evento e o formato de envio fará com que o manipulador de WebSocket da Etapa 2 faça total sentido antes de você escrevê-lo.
Quando a Twilio se conecta ao seu WebSocket, ela envia uma sequência de mensagens de texto JSON, que pode ter quatro tipos de evento.
O evento connected chega primeiro e confirma que o WebSocket está ativo. O evento start é enviado uma vez quando o stream de mídia começa; ele traz um streamSid que você precisa armazenar, pois precisará dele para enviar áudio de volta, e também inclui metadados da chamada em start.customParameters e start.callSid.
O evento media é recorrente: media.payload é um bloco de áudio mu-law de 8 kHz codificado em base64, com 20 ms por frame, e media.track é inbound para o áudio do interlocutor. Por fim, stop é enviado quando o stream termina, geralmente porque a chamada foi encerrada.
Para reproduzir áudio de volta, envie uma mensagem do tipo media com o mesmo streamSid e um payload mu-law em base64. Para interromper um áudio que já está na fila, envie uma mensagem clear com o streamSid, que limpa o buffer de saída da Twilio.
As codificações de entrada e saída são idênticas (ulaw_8000). Solicitamos ulaw_8000 ao Text to Speech da ElevenLabs e encaminhamos os bytes diretamente à Twilio, sem fazer reamostragem no caminho.
Etapa 1: disponibilize o webhook TwiML
Quando uma chamada chega, a Twilio faz uma solicitação HTTP ao 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: ele mantém a chamada ativa durante o stream e permite enviar áudio de volta, que é o objetivo desta configuração.
O TwiML retornado pelo webhook é:
No Express, basta um único manipulador POST que preenche o host e retorna o documento:
No console da Twilio, configure o webhook "A call comes in" do número como https://your-subdomain.ngrok.app/incoming-call usando HTTP POST.
Etapa 2: aceite o WebSocket do Media Stream
O manipulador de WebSocket lê os eventos da Twilio, executa a cascata e envia o áudio de volta.
Mantemos uma pequena quantidade de estado por chamada: o streamSid, uma conexão STT e um indicador de se o agente está falando no momento. O manipulador decodifica cada frame de mídia de entrada de base64 e encaminha os bytes mu-law brutos ao STT:
Etapa 3: transcreva com o Scribe v2 Realtime
O Scribe v2 Realtime aceita blocos de áudio em streaming e retorna transcrições parciais e finais. Ele também oferece suporte direto à codificação mu-law, por isso enviamos os frames da Twilio sem alterações.
Ele também oferece Detecção de Atividade de Voz para segmentação baseada em silêncio e controle de commit manual para finalizar um segmento. Para um agente telefônico, a segmentação orientada por VAD costuma ser a escolha certa, pois uma pausa natural é o sinal mais confiável de que o turno do interlocutor terminou.
Estas são as etapas: abra um stream STT quando a chamada começar. Envie cada bloco mu-law de entrada. Reaja às transcrições finalizadas chamando o LLM.
A interface do cliente STT em tempo real ainda está evoluindo, portanto o formato abaixo fica por trás de um pequeno adaptador TypeScript (openRealtimeStt), que você implementa com base na API atual, e não em um conjunto fixo de nomes de campos. Considere onFinal como o hook que entrega um turno concluído do interlocutor à próxima etapa.
A latência do reconhecimento em tempo real é de cerca de 150 ms para parciais, o que mantém pequeno o intervalo percebido entre o fim da fala do interlocutor e o início da fala do agente. Para a alternativa em lote e o conjunto completo de recursos, consulte a documentação de Speech to Text e a página do produto Speech to Text em tempo real.
Etapa 4: gere uma resposta com um 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 em streaming para poder iniciar a síntese já na primeira frase.
Aqui, usamos a OpenAI:
O ID de modelo acima, gpt-4.1-mini, é um exemplo de opção de baixa latência; claude-haiku-4-5 é uma opção comparável da Anthropic. Qualquer um dos provedores pode atender ao mesmo contrato llmReply; troque o corpo da função, e o restante do agente permanecerá inalterado.
O prompt de sistema limita o tamanho da resposta, algo importante ao telefone: respostas longas parecem lentas e são difíceis de interromper naturalmente.
Etapa 5: sintetize com Flash TTS em ulaw_8000
Agora o texto precisa se transformar em áudio que a Twilio possa reproduzir. Solicite o Flash v2.5 com outputFormat: "ulaw_8000" para que os bytes correspondam à codificação esperada pela Twilio. Em seguida, transmita o áudio em streaming e encaminhe cada bloco de volta pelo WebSocket como um evento media.
Acumule tokens do LLM em fragmentos do tamanho de uma frase e sintetize cada fragmento assim que ele for concluído, em vez de esperar a resposta inteira. Isso reduz o tempo até o primeiro áudio, pois o interlocutor ouve a primeira frase enquanto o modelo ainda produz a segunda. Para ter mais controle sobre a síntese incremental, o guia de WebSocket TTS em tempo real mostra como enviar texto para um único socket de síntese aberto; a abordagem de streaming HTTP abaixo é mais simples e suficiente para turnos curtos de conversa.
Prepare seu agente de voz IA para produção
Depois das cinco etapas acima, você tem um agente funcional. Mas isso não é o mesmo que uma implantação em produção.
Há vários aspectos dos quais você precisa estar ciente antes de colocar o agente em uma linha telefônica real.
Valide as assinaturas dos webhooks da Twilio
Qualquer pessoa que descubra a URL do seu webhook pode fazer POST para ela, então a primeira tarefa é confirmar que a solicitação realmente veio da Twilio. A Twilio assina cada solicitação com seu Auth Token no cabeçalho X-Twilio-Signature, e você deve rejeitar tudo que não passar na validação. A assinatura é calculada com base na URL completa e nos parâmetros POST, portanto você precisa calculá-la da mesma forma que a Twilio.
O helper da Twilio faz isso para você:
Gerencie os segredos corretamente
Mantenha ELEVENLABS_API_KEY, a chave do LLM e TWILIO_AUTH_TOKEN em um gerenciador de segredos, não no código-fonte nem em arquivos env de texto simples enviados a um repositório. Restrinja a chave da ElevenLabs apenas aos endpoints de que este serviço precisa e defina uma cota de créditos para que um vazamento tenha impacto limitado, e não indefinido.
Os planos Enterprise também podem restringir uma chave a faixas específicas de IP usando uma lista de permissões de IP. Este servidor usa a chave de API diretamente porque ela nunca sai do seu backend; se alguma lógica de áudio fosse movida para um navegador ou cliente móvel, você usaria tokens de uso único para que a chave nunca fosse exposta no lado do cliente.
Entenda o limite de simultaneidade
Cada plano tem um limite de simultaneidade que varia conforme a família de modelos, e o limite conta quantas solicitações estão gerando áudio ativamente ao mesmo tempo.
Para um agente telefônico, essa contagem trabalha a seu favor. A geração de áudio é mais rápida que a reprodução, então cada chamada consome simultaneidade de TTS apenas nas breves janelas em que uma resposta está sendo sintetizada, e não durante toda a chamada. Como estimativa, um limite de simultaneidade de cerca de cinco pode atender aproximadamente 100 chamadas conversacionais simultâneas, porque a geração termina bem antes da reprodução.
Ainda assim, monitore a margem disponível em vez de estimar. As respostas da ElevenLabs expõem os cabeçalhos current-concurrent-requests e maximum-concurrent-requests; registre-os e crie alertas ao se aproximar do máximo. Quando você excede o limite, as solicitações entram na fila por prioridade, o que normalmente adiciona cerca de 50 ms, e uma sobrecarga persistente retorna HTTP 429.
Trate respostas HTTP 429 com um breve backoff. Se elas persistirem, aumente os limites fazendo upgrade na página de preços ou, para clientes Enterprise, por meio do seu gerente de conta.
Lide com barge-in e interrupções
Um interlocutor que começa a falar enquanto o agente está falando espera que ele pare. Isso é barge-in, e lidar corretamente com isso é uma parte importante do que faz um agente parecer natural, em vez de roteirizado.
Detecte a fala do interlocutor durante a reprodução do agente usando o sinal VAD do STT. Quando detectá-la, faça duas coisas. Primeiro, pare de encaminhar blocos de TTS, o que o indicador agentSpeaking em speak já faz ao interromper o loop. Segundo, envie uma mensagem clear à Twilio para limpar o áudio que você já colocou na fila do lado dela.
Se você pular o clear, a Twilio continuará reproduzindo o áudio armazenado em buffer depois que você parar de enviar, fazendo parecer que o agente fala por cima do interlocutor.
Registre, monitore e lide bem com falhas
Instrumente cada etapa para poder atribuir a latência quando uma chamada parecer lenta. Meça o intervalo da transcrição final até o primeiro token do LLM, do primeiro token do LLM até o primeiro byte de TTS e do primeiro byte de TTS até o frame enviado à Twilio. Você verá que a maior parte da latência variável está na etapa do LLM; as etapas de STT e TTS são comparativamente estáveis.
Depois, planeje para falhas parciais. O LLM pode exceder o tempo limite, o stream STT pode cair, e o tempo de ida e volta da rede até a ElevenLabs varia de cerca de 20 a 200 ms na internet pública, dependendo da localização. Mantenha seu servidor próximo dos interlocutores, não apenas da ElevenLabs, já que a ElevenLabs encaminha para o cluster mais próximo na América do Norte, Europa e Sudeste Asiático.
Quando uma etapa falhar, não deixe o interlocutor em silêncio: sintetize uma frase curta de contingência ("Desculpe, pode repetir?") e mantenha a chamada ativa. Envolva cada etapa em um timeout e um try/catch para que um turno com falha não derrube todo o WebSocket.
Vale definir mais alguns padrões antes de lançar:
- Limite o tamanho da resposta no prompt de sistema, como mostrado, para que os turnos sejam curtos e interrompíveis.
- Limite o histórico da conversa para que chamadas longas não aumentem o contexto do LLM sem limite.
- Defina uma duração máxima para a chamada como proteção contra sessões travadas consumindo simultaneidade silenciosamente.
Para continuar ajustando os elementos que você controla, o documento sobre latência explica de onde vem o tempo até o primeiro áudio, a visão geral dos modelos aborda os compromissos entre velocidade e qualidade, e o guia de WebSocket TTS em tempo real 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 a ElevenAPI
Agora que os 20 minutos passaram, você tem todas as camadas de um agente de voz de produção. A Twilio gerencia a telefonia, o Scribe v2 Realtime transcreve, um LLM gera as respostas e o Flash v2.5 responde por voz pelo mesmo WebSocket.
Se preferir não manter a cascata por conta própria, o ElevenAgents oferece alternância de turnos, tratamento de interrupções e integração de telefonia como um serviço gerenciado, baseado nos mesmos modelos que você acabou de conectar manualmente.
Para continuar ajustando uma stack que você controla, explore a página do produto ElevenAPI para ver planos, limites de simultaneidade e a Voice Library. Ou crie sua conta e comece hoje a fazer sua primeira chamada.



