Todo menu telefônico é o mesmo pedido de desculpas. Digite 1 para vendas. Digite 2 para suporte. Digite 3 para ouvir estas opções novamente.
É uma tabela de roteamento, lida em voz alta, na velocidade da fala. Quem liga já sabe o que quer. Só precisa esperar enquanto recitam o seu organograma e depois adivinhar em qual ramo alguém como ele se encaixa.
A alternativa não é um menu melhor. É nenhum menu: o telefone é atendido, a pessoa diz o que precisa, e do outro lado alguma coisa entende, resolve, e coloca um humano na linha quando um humano é realmente necessário.
Isso é um agente de voz, e é mais ou menos uma tarde de trabalho.
O que a central antiga custa de verdade
Não a assinatura da linha. Outras três coisas.
Chamadas abandonadas. Cada camada de menu perde gente. Desligam e tentam um concorrente, ou escolhem o ramo errado e caem com alguém que não pode ajudar, o que é pior porque agora duas pessoas perderam tempo.
Nenhum registro do que as pessoas queriam. Uma URA sabe que digitaram 2. Não sabe que estavam perguntando sobre o reembolso de um pedido de março. Essa intenção é a coisa mais valiosa da ligação e a sua central joga fora toda vez.
Ela não consegue fazer nada. Um menu roteia. Não consulta uma reserva, não cota um preço, não atualiza um endereço. Tudo vira repasse para uma pessoa, incluindo a metade das ligações que essa pessoa vai resolver lendo um campo numa tela.
Um agente de voz resolve as três pelo mesmo motivo: é uma conversa, então ouve a intenção, e tem tools, então consegue agir sobre ela.
O que você vai construir
| O menu antigo | Um agente de voz da Zavu |
|---|---|
| Digite 1 para vendas | Quem liga diz o que quer |
| Uma árvore que você mantém num portal | Um prompt no seu repo, com version control |
| Roteia e se desliga do assunto | Consulta e responde durante a ligação |
| Transfere às cegas | Transfere quando o seu prompt manda, para o número que você definiu |
| Sabe que um dígito foi apertado | Transcrição completa, duração e custo por ligação |
O agente não é um produto separado do que atende no WhatsApp. É o mesmo agente com um bloco voice: mesmo prompt, mesmo modelo, mesmas tools, mesma knowledge base.
Antes de começar
Três pré-requisitos, e um deles leva um dia.
- Um sender com número de telefone. Esse número é para onde as pessoas ligam.
- Voice Agents habilitado para o seu team. Está em beta e é ligado por team, então os endpoints de voz retornam
403até alguém ativar. Escreva para hi@zavu.dev e peça. - Saldo positivo. As ligações são cobradas por minuto conectado. Ligações recebidas só são atendidas enquanto o seu saldo está acima de zero, que é um modo de falha que convém conhecer antes de acontecer numa segunda-feira.
Quem faz o quê
Você entra com a conta, o número e o critério sobre quando uma pessoa deve atender. Seu coding agent escreve e publica o resto.
| Você | Seu coding agent |
|---|---|
| Cria a conta Zavu | Encontra ou cria o sender |
Roda zavu login e autoriza | Escreve defineAgent com o bloco de voz |
| Pede para habilitarem Voice Agents | Escreve as tools que ele chama durante a ligação |
| Aprova a compra de um número | Define os secrets e deploya |
| Atende a primeira ligação de teste | Lê a transcrição e corrige o prompt |
A única parte que você digita
bashnpx skills add zavudev/zavu-skills npx zavudev login
O primeiro comando instala as skills da Zavu no seu coding agent. Claude Code, Cursor, Copilot, Codex e uns 40 outros são suportados; o installer pergunta quais. Uma dessas skills é voice-agent, que é o que impede o seu agente de inventar campos de configuração que não existem.
login abre o browser, você escolhe o projeto, e a key fica em ~/.zavu/credentials.json. Daqui para frente você descreve.
Depois você descreve o agente
textConstrua um voice agent Zavu para a minha clínica no meu sender atual. Que atenda em português, informe os horários, busque uma consulta por número de telefone, remarque, e transfira para +14155551234 quando pedirem uma pessoa ou soarem irritados. Teto de doze minutos. Faça o deploy.
Você não precisa conhecer os comandos abaixo. Eles estão aqui para você reconhecer o que passa na tela, e para perceber se uma etapa foi pulada.
bashnpx zavudev whoami # qual projeto eu vou mexer npx zavudev senders list # o número para onde as pessoas ligam npx zavudev fn secrets set SENDER_ID "jn76vnxet8g5nq661by3v06y1581bmmn" npx zavudev deploy
Se preferir partir de algo testado, existe um catálogo de agentes de voz prontos:
bashnpx zavudev agents catalog
textid name voice tools category fermi Fermi, Lead Qualification yes 5 sales curie Curie, Customer Support yes 5 support kepler Kepler, Video Call Booking yes 2 frontDesk hopper Hopper, Lead Capture and Booking yes 5 sales aria Aria, Multi-channel concierge yes 3 frontDesk atlas Atlas, Product Expert yes 0 support
npx zavudev agents pull curie --sender "$SENDER_ID" baixa um para o seu repo como TypeScript editável. É um ponto de partida que é seu, não uma caixa preta que você aluga.
Leia o que ele escreveu
tsimport { defineAgent, defineTool } from "@zavudev/functions" defineAgent({ senderId: process.env.SENDER_ID!, name: "Clínica Norte", provider: "zavu", model: "openai/gpt-4o-mini", channels: ["voice", "whatsapp"], voice: { enabled: true, model: "openai/gpt-4o", greeting: "Clínica Norte, bom dia. Como posso ajudar?", language: "pt-BR", interruptible: true, maxCallDurationMinutes: 12, maxIdleSeconds: 30, transferPhoneNumber: "+14155551234", voicemailAction: "hangup", }, prompt: "Você é a recepção da Clínica Norte. Responda em português, uma ou duas frases. Transfira para uma pessoa quando pedirem, ou quando estiverem irritados.", })
Cinco campos decidem se a ligação parece humana.
interruptible é barge-in. Quem liga pode cortar o agente no meio da frase e ele para para ouvir. Deixe ligado. Um agente que fala por cima das pessoas é exatamente o que todo mundo já odeia nas ligações automáticas.
maxIdleSeconds encerra a ligação depois de silêncio, para que um celular de cara para baixo numa mochila não te cobre doze minutos.
maxCallDurationMinutes é o freio duro. Existe porque uma conversa que deu errado costuma dar errado por muito tempo.
voicemailAction define o que acontece quando uma secretária eletrônica atende numa ligação de saída: hangup em silêncio, ou leave_message e falar. Deixar recado quando você queria falar com uma pessoa é como um agente útil vira um incômodo.
transferPhoneNumber é a saída de emergência, e merece a própria seção.
A transferência é o que julgam em você
Se você define transferPhoneNumber, o agente ganha uma tool de transferência. Ele também sempre ganha uma de desligar, então consegue encerrar sozinho uma ligação que já acabou.
Duas coisas para saber, e a segunda surpreende.
A transferência só existe se o sender também tiver um número para discar. Se você define o destino e esquece o número do sender, o agente fica sem com o que escalar e sem como avisar.
Uma transferência encaminha a ligação. Ela não explica nada para o humano. Seu colega atende um telefone tocando sem ideia de que quem ligou já passou noventa segundos se explicando. Se você quer que ele saiba, isso é você que constrói: a transcrição completa está no registro da ligação e no webhook call.completed, então dá para mandar um resumo para o Slack, WhatsApp ou o seu helpdesk no momento da transferência. Diga isso no prompt também, para o agente avisar o que vai acontecer em vez de ficar mudo.
Dê algo para ele fazer
Um menu roteia. Esta é a parte que faz um agente valer mais que um menu:
tsdefineTool({ name: "find_appointment", description: "Busca a próxima consulta de um paciente pelo número de telefone.", parameters: { type: "object", properties: { phone: { type: "string", description: "Número no formato E.164" } }, required: ["phone"], }, handler: async ({ phone }) => { const res = await fetch("https://api.clinica.example/consultas?phone=" + phone) if (!res.ok) return { found: false, reason: "lookup_failed" } return { found: true, ...(await res.json()) } }, })
Vale dizer com todas as letras: as tools rodam em voz, sim. No path de texto puro não rodam, o que confunde as pessoas na direção contrária. Um agente de voz consegue consultar coisas de verdade no meio da frase, e essa é toda a razão para apontar um deles para o seu número.
Teste o handler sozinho, antes de qualquer ligação:
bashnpx zavudev fn invoke --tool find_appointment --args '{"phone":"+14155551234"}'
Isso roda na sua máquina, contra código sem commit, em menos de um segundo.
Ouça antes de um cliente ouvir
Dois testes diferentes, que pegam falhas diferentes.
bashnpx zavudev agents test --sender "$SENDER_ID" --message "quero remarcar minha consulta de terça"
Isso exercita o cérebro: prompt, modelo, knowledge base. Nada é entregue, nada é cobrado, nada é registrado. Também imprime warnings sobre o que um dry run não consegue provar, e num agente de voz o mais útil é que esse path não chama tools mesmo que a ligação vá chamar.
Depois ligue para você mesmo:
bashnpx zavudev calls create --to "+55SEUPROPRIONUMERO"
Seu telefone toca e o agente fala com você. Nenhuma outra coisa vai te dizer que a saudação está três segundos longa demais, ou que a voz lê um número de telefone como um inteiro gigante. Preste atenção na interrupção: fale por cima de propósito e veja se ele para.
Depois leia a ligação:
bashnpx zavudev calls list --limit 5 npx zavudev calls get <callId>
list mostra direção, status, duração e por que cada ligação terminou. get devolve a transcrição turno a turno. Essa transcrição é o que a sua central nunca te deu, e é de onde sai a próxima versão do prompt.
Depois entregue o loop:
textLeia a transcrição das minhas últimas três ligações. Todo mundo perguntou sobre estacionamento e o agente não sabia. Adicione isso na knowledge base, encurte a saudação, e faça o deploy de novo.
Ele também liga para fora
O mesmo agente coloca ligações:
bashnpx zavudev calls create --to "+5511999999999" --language pt-BR --max-minutes 5
Lembretes de consulta, janelas de entrega, um pagamento que falhou. --language aceita auto para seguir o idioma de quem atende. voicemailAction define o que acontece quando uma máquina atende, que em campanhas de saída é a maioria das vezes.
Saída também é onde um agente deixa de ser uma comodidade e vira uma coisa capaz de incomodar milhares de pessoas rápido. Lembretes que a pessoa pediu, sim. Ligações frias, não. Consentimento e horários permitidos são responsabilidade sua, e nem o modelo nem a API vão respeitar isso por você.
O que fica de cada ligação
Quatro eventos de webhook disparam: call.initiated, call.answered, call.completed, call.failed. Todos levam o id da ligação, direção, from, to, status, duração e por que terminou.
Os dois terminais ainda levam cost, em USD, depois que a ligação foi cobrada. Esse não é o número cru da operadora: é o que você foi realmente faturado, telefonia e pipeline de voz juntos, então dá para colocar um preço real ao lado de uma conversa real.
Como isso é cobrado na Zavu
Voz é por minuto conectado. O pipeline gerenciado custa $0.0625 por minuto mais telefonia, contados de quando atendem até desligarem. Os modelos padrão estão cobertos por essa tarifa; um modelo premium soma o próprio custo de tokens. Ao colocar a chamada se reserva uma estimativa curta, liquidada contra a duração real no fim.
O plano cobre tudo em volta. Free é $0, Hobby é $25 por mês, e daí escala. Cada plano inclui uma cota de números, três no Hobby, e o primeiro número dos Estados Unidos é grátis para cada team. Números extras custam cerca de um dólar por mês cada.
Construir é grátis. Skills, CLI, SDKs, deploy e cada redeploy. agents test não cobra. fn invoke roda local. O que você paga é uma ligação conectada.
O número para guardar: uma ligação de cinco minutos no pipeline gerenciado dá uns trinta centavos mais telefonia. Compare com o que uma pessoa custa nesses mesmos cinco minutos, e depois com o que custa uma chamada abandonada, que é a ordem de grandeza que importa.
As tarifas mudam. A página de preços é a que está atualizada.
Três regras que mantêm isso honesto
Nunca finja um sucesso. Uma tool que reporta uma consulta remarcada que nunca remarcou faz o agente dizer a uma pessoa real, em voz alta e com tom confiante, que a terça dela está resolvida. Devolva a falha. O agente vai dizer, e quem ligou pode fazer alguma coisa a respeito. Isso é pior em voz do que em qualquer outro lugar, porque ninguém relê uma ligação.
Avise que vai transferir. Silêncio seguido de toque parece que desligaram na sua cara. Uma frase, depois a transferência.
Um dry run não é uma ligação. agents test prova que o prompt faz sentido. Não prova que a saudação soa bem, que a voz pronuncia o nome da sua empresa, nem que o agente para quando é interrompido. Só uma ligação real prova isso, então faça a primeira para o seu próprio telefone.
Onde isso te deixa
O seu número agora atende, entende, consulta algo real e coloca uma pessoa na linha quando é o caso. O comportamento vive num arquivo em que você consegue dar diff, com uma transcrição por ligação e um custo ao lado.
O menu nunca foi o produto. Era o acordo que você aceitou porque software não conseguia sustentar uma conversa. Agora consegue.