Como criar um agente de voz com IA usando a Voice API da Vonage e o Deepgram
Introdução
Este guia descreve o processo de criação de um agente de voz com IA em tempo real usando a Voice API da Vonage e a plataforma Voice Agent da Deepgram. Você criará um assistente de voz inteligente que atende chamadas telefônicas, escuta os usuários por meio do Reconhecimento Automático de Fala (ASR), processa solicitações com um Modelo de Linguagem de Grande Escala (LLM) e responde com uma síntese de voz que soa natural, tudo em tempo real. Além disso, a configuração suporta a interrupção da conversa, também conhecida como “barge-in”.
Para uma visão geral dos conceitos de automação de voz e uma comparação entre as três abordagens de implementação, consulte Entendendo a automação por voz.
Pré-requisitos
Antes de começar, certifique-se de que você tenha:
- Um account da API da Vonage. Cadastre-se gratuitamente.
- Node.js versão 18 ou superior instalada no seu computador.
- A Account no Deepgram com uma chave de API.
- ngrok instalado no seu computador.
Configure seu ambiente local
Crie um novo diretório para o seu projeto e instale as dependências necessárias:
Exponha seu servidor local
Vonage needs to send webhooks to your local machine. Use ngrok to expose your server:
Note: Keep this terminal open and copy your ngrok URL. You'll need it in the next steps.
Configure seus recursos da Vonage
Faça login no Painel do Vonage para começar.
Criar uma aplicação da Vonage
Gere suas credenciais pelo Painel e salve-as na pasta que você acabou de criar.
- Acesse Applications > Criar um novo aplicativo.
- Dê um nome ao seu aplicativo.
- Autenticação: Clique Gerar chaves públicas e privadas.
- Um arquivo chamado
private.keyvai baixar. - Mova isso
private.keyarquivo da pasta “Downloads” para a suavonage-deepgram-voice-agentpasta.
- Um arquivo chamado
- Sob Recursos, ativar Voz.
- Nas configurações do Voice, defina os seguintes webhooks:
- URL da resposta:
https://{ngrok-url}/answer(Método: GET) - URL do evento:
https://{ngrok-url}/event(Método: POST)
- URL da resposta:
- Clique Criar um novo pedido na parte de baixo.
Vincular um número
- Acesse Números de telefone > Números para compra e adquirir um número com serviço de voz.
- Acesse Applications, selecione seu aplicativo de bot e clique em Editar.
- De acordo com o Numbers aba, clique em Link ao lado do número que você acabou de adquirir.
Configurar variáveis de ambiente
Criar um .env arquivo no diretório do seu projeto com as seguintes variáveis:
#==== Vonage Voice API ====
API_KEY=your_vonage_api_key
API_SECRET=your_vonage_api_secret
APP_ID=your_application_id
SERVICE_PHONE_NUMBER=your_vonage_number
#==== Deepgram Voice Agent API ====
DEEPGRAM_API_KEY=your_deepgram_api_key
DEEPGRAM_VOICE_AGENT_ENDPOINT=agent.deepgram.com/v1/agent/converse
DEEPGRAM_AGENT_SPEAK=aura-orion-en
#==== Other custom parameters ====
MAX_CALL_DURATION=300
Importante: Por motivos de segurança, armazene suas chaves de API em variáveis de ambiente, em vez de inseri-las diretamente no código-fonte.
Criar o conector do agente de voz
Crie um arquivo chamado server.js e adicione o código a seguir. Este aplicativo atua como um conector entre a Voice API da Vonage e o Deepgram Voice Agent.
'use strict'
require('dotenv').config();
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
require('express-ws')(app);
const webSocket = require('ws');
app.use(bodyParser.json());
//---- CORS policy ----
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.header("Access-Control-Allow-Methods", "OPTIONS,GET,POST,PUT,DELETE");
next();
});
//---- Configuration ----
const servicePhoneNumber = process.env.SERVICE_PHONE_NUMBER;
//---- Vonage API Setup ----
const { Auth } = require('@vonage/auth');
const credentials = new Auth({
apiKey: process.env.API_KEY,
apiSecret: process.env.API_SECRET,
applicationId: process.env.APP_ID,
privateKey: './private.key'
});
const apiBaseUrl = "https://api.nexmo.com";
const options = { apiHost: apiBaseUrl };
const { Vonage } = require('@vonage/server-sdk');
const vonage = new Vonage(credentials, options);
//---- Deepgram Voice Agent Configuration ----
const dgApiKey = process.env.DEEPGRAM_API_KEY;
const dgVoiceAgentEndpoint = process.env.DEEPGRAM_VOICE_AGENT_ENDPOINT;
const dgVoiceAgentSettings = {
"type": "Settings",
"audio": {
"input": { "encoding": "linear16", "sample_rate": 8000 },
"output": { "encoding": "linear16", "sample_rate": 8000, "container": "none" }
},
"agent": {
"listen": { "provider": { "type": "deepgram", "model": "nova-3" } },
"think": {
"provider": { "type": "anthropic", "model": "claude-sonnet-4-20250514" },
"prompt": "You are a helpful AI assistant on a live phone call. Keep responses concise and natural for spoken conversation."
},
"speak": {
"provider": {
"type": "deepgram",
"model": process.env.DEEPGRAM_AGENT_SPEAK
}
}
}
};
//---- Handle incoming PSTN calls ----
app.get('/answer', async (req, res) => {
const hostName = req.hostname;
const uuid = req.query.uuid;
// For local development with ngrok, use your ngrok URL directly
// const publicUrl = 'https://your-ngrok-url.ngrok.io';
const wsUri = `wss://${hostName}/socket?original_uuid=${uuid}`;
const nccoResponse = [
{
"action": "talk",
"text": "Hello, please wait while we're connecting your call!",
"language": "en-US",
"style": 11
},
{
"action": "connect",
"eventType": "synchronous",
"eventUrl": [`https://${hostName}/ws_event`],
"from": req.query.from,
"endpoint": [
{
"type": "websocket",
"uri": wsUri,
"content-type": "audio/l16;rate=8000",
"headers": {}
}
]
}
];
res.status(200).json(nccoResponse);
});
//---- Event webhook for call status ----
app.post('/event', async (req, res) => {
res.status(200).send('Ok');
});
//---- WebSocket event handler ----
app.post('/ws_event', async (req, res) => {
res.status(200).send('Ok');
// Trigger a greeting when WebSocket is connected
setTimeout(() => {
if (req.body.status === 'answered') {
vonage.voice.playTTS(req.body.uuid, {
text: "Hello",
language: 'en-US',
style: 11
})
.then(res => console.log("Initial greeting sent"))
.catch(err => console.error("Failed to play TTS:", err));
}
}, 1500);
});
//---- Start server ----
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Voice Agent application listening on port ${port}`);
console.log(`Make sure ngrok is forwarding to this port!`);
});
Nota: Ao executar localmente com o ngrok, o req.hostname pode não corresponder à URL do seu túnel público. Se os webhooks falharem, defina a URL base do ngrok como uma variável de ambiente e use-a para criar o eventUrl e wsUri em vez disso.
Adicionar a lógica do conector WebSocket
Agora adicione a lógica central do conector que faz a ponte entre a Voice API da Vonage e o Deepgram Voice Agent. Acrescente isso ao seu server.js:
//---- WebSocket Connector ----
app.ws('/socket', async (ws, req) => {
let wsDgOpen = false; // Deepgram WebSocket ready?
const originalUuid = req.query.original_uuid;
console.log('WebSocket connected for call UUID:', originalUuid);
//---- Connect to Deepgram Voice Agent ----
console.log('Opening connection to Deepgram Voice Agent');
const wsDg = new webSocket(`wss://${dgVoiceAgentEndpoint}`, {
headers: { authorization: `token ${dgApiKey}` }
});
wsDg.on('error', async (event) => {
console.log('WebSocket to Deepgram error:', event);
});
wsDg.on('open', () => {
console.log('WebSocket to Deepgram opened');
// Send configuration to Deepgram Voice Agent
wsDg.send(JSON.stringify(dgVoiceAgentSettings));
wsDgOpen = true;
});
//---- Handle messages from Deepgram ----
wsDg.on('message', async (msg, isBinary) => {
if (isBinary) {
// Audio data from agent - send directly to Vonage
ws.send(msg);
} else {
// Text messages (transcripts, events, etc.)
const message = JSON.parse(msg.toString('utf8'));
console.log(`Message from Deepgram:`, message);
// Handle barge-in: clear Vonage's audio buffer when user starts speaking
if (message.type === "UserStartedSpeaking") {
ws.send(JSON.stringify({ action: "clear" }));
console.log('Sent CLEAR command to Vonage');
}
}
});
wsDg.on('close', async () => {
wsDgOpen = false;
console.log("Deepgram WebSocket closed");
});
//---- Handle messages from Vonage (user audio) ----
ws.on('message', async (msg) => {
if (typeof msg === "string") {
const event = JSON.parse(msg);
console.log("Vonage event:", event.event);
// The first message from Vonage is always websocket:connected
if (event.event === "websocket:connected") {
console.log('Vonage WebSocket established:', event['content-type']);
}
// Handle Vonage control message confirmations
if (event.event === "websocket:cleared") {
console.log('Vonage audio buffer cleared');
}
} else {
// Binary audio data from caller - forward to Deepgram
if (wsDgOpen) {
wsDg.send(msg);
}
}
});
//---- Clean up on disconnect ----
ws.on('close', async () => {
wsDgOpen = false;
wsDg.close();
console.log("Vonage WebSocket closed");
});
});
Como funciona
Streaming de áudio simplificado: O áudio do Deepgram é enviado diretamente para a Vonage na forma de mensagens binárias. Não é necessário nenhum buffer manual nem sincronização — a Vonage cuida do buffer interno automaticamente.
Limpar mensagem de controle do buffer: Quando o Deepgram detecta que o usuário começou a falar (UserStartedSpeaking (evento), o aplicativo envia uma mensagem de controle CLEAR para a Vonage: {"action": "clear"}. Isso instrui a Voice API da Vonage a descartar imediatamente quaisquer quadros de áudio armazenados no buffer, criando uma funcionalidade de interrupção instantânea sem a necessidade de gerenciamento manual do buffer.
Confirmação do evento: A Vonage responde com um websocket:cleared evento para confirmar que o buffer foi esvaziado com sucesso. Isso permite que você acompanhe quando ocorrem interrupções.
Comunicação bidirecional: O áudio do usuário é transmitido da Vonage para a Deepgram na forma de mensagens binárias do WebSocket, enquanto o áudio do agente e as transcrições são transmitidos da Deepgram para a Vonage em tempo real.
Transcrições em tempo real: O Deepgram envia mensagens JSON contendo transcrições tanto da fala do usuário quanto das respostas do agente, que você pode registrar ou processar para fins de análise e garantia de qualidade.
Teste o aplicativo
- Certifique-se de que seu
private.keyO arquivo está no diretório do projeto. - Inicie o ngrok em um terminal:
- Execute seu servidor em outro terminal:
- Ligue para o seu número da Vonage usando seu celular.
- O assistente de voz irá cumprimentá-lo e responder às suas perguntas por meio de uma conversa baseada em inteligência artificial.
Adicionar recurso de chamadas de saída
Para permitir que seu aplicativo faça chamadas de saída, adicione este endpoint ao seu server.js:
//---- Trigger outbound PSTN calls ----
app.get('/call', async (req, res) => {
if (req.query.callee == null) {
res.status(400).send('"callee" number missing as query parameter');
} else {
res.status(200).send('Ok');
const hostName = req.hostname;
vonage.voice.createOutboundCall({
to: [{
type: 'phone',
number: req.query.callee
}],
from: {
type: 'phone',
number: servicePhoneNumber
},
limit: process.env.MAX_CALL_DURATION,
answer_url: [`https://${hostName}/answer`],
answer_method: 'GET',
event_url: [`https://${hostName}/event`],
event_method: 'POST'
})
.then(res => console.log("Outgoing PSTN call status:", res))
.catch(err => console.error("Outgoing PSTN call error:", err));
}
});
Para iniciar uma chamada de saída, abra seu navegador e acesse:
https://your-ngrok-url.ngrok.io/call?callee=15551234567
Substituir 15551234567 com o número de telefone para o qual você deseja ligar (no formato E.164, sem o + (sinal).
Personalize seu agente de voz
Você pode personalizar vários aspectos do agente de voz modificando o dgVoiceAgentSettings objeto:
Alterar o modelo de IA
"think": {
"provider": { "type": "open_ai", "model": "gpt-4o-mini" },
"prompt": "You are a helpful AI assistant on a live phone call. Keep responses concise and natural for spoken conversation."
}
Alterar a voz
Atualize o DEEPGRAM_AGENT_SPEAK variável no seu .env arquivo. Veja Documentação dos modelos de TTS do Deepgram para ver as opções de voz disponíveis.
Personalizar o aviso do sistema
Modificar o prompt campo no think seção para alterar a personalidade e o comportamento do seu agente:
"prompt": "You are a friendly customer service representative for Acme Corp. Help users with their inquiries about our products and services. Be professional but warm."
Próximos passos
- Explore Documentação do WebSocket para padrões avançados de streaming de áudio.
- Adicionar gravação e transcrição de chamadas para fins de Audit e controle de qualidade.