Cómo crear un IVR / Bot de voz avanzado
Esta guía explica cómo crear un agente de IA basado en voz utilizando la Voice API de Vonage y OpenAI. Crearás un Bot de voz que responde a las llamadas entrantes, escucha la pregunta del usuario mediante reconocimiento automático de voz (ASR) y responde con una respuesta inteligente generada por un LLM.
Para obtener una visión general de los conceptos de automatización de voz y una comparación de los tres enfoques de implementación, véase Comprender la automatización de voz.
Requisitos previos
Antes de empezar, asegúrate de que tienes:
- A Account de la API de Vonage.
- Node.js instalado en su máquina.
- Un Clave de la API de OpenAI.
- ngrok instalado en su máquina.
Configure su entorno local
Crea un nuevo directorio para tu proyecto e instala las dependencias necesarias:
Exponga su 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.
Configuración de tus recursos de Vonage
Inicie sesión en Panel de Vonage Para empezar.
Crear una aplicación de voz
- Vaya a Applications > Crear una nueva aplicación.
- Dale un nombre (por ejemplo, Voice AI Bot).
- Haz clic Generar clave pública y privada. Guarda el
private.keyarchivo en la carpeta de tu proyecto (aunque no lo utilizaremos para este flujo básico de ASR, es necesario para crear la aplicación). - En Capacidadesactivar Voz.
- En el Respuesta URL campo, introduce tu URL base de ngrok seguida de
/webhooks/answer(p. ej.,https://{random-id}.ngrok.app/webhooks/answer). Establezca el método en GET. - En el URL del evento campo, introduce tu URL base de ngrok seguida de
/webhooks/events(p. ej.,https://{random-id}.ngrok.app/webhooks/events). Establezca el método en POST. - Haz clic Generar nueva aplicación en la parte inferior.
Vincular un número
- Ir a Números de teléfono > Comprar Numbers y adquiera un número vocal.
- Ir a Applicationsseleccione su aplicación bot y haga clic en Editar.
- En virtud de la Numbers haga clic en Enlace junto al número que acaba de adquirir.
Crear el bot de voz
Cree un archivo llamado index.js y añada el siguiente código. Sustituya YOUR_OPENAI_API_KEY con tu clave real.
Nota: Al ejecutarlo localmente con ngrok, req.protocol/req.get('host') puede no coincidir con la URL de su túnel público. Si los webhooks fallan, establezca la URL base de su túnel en config (por ejemplo una var env) y construya eventUrl de eso en su lugar.
const express = require('express');
const { OpenAI } = require('openai');
const app = express();
app.use(express.json());
const openai = new OpenAI({ apiKey: 'YOUR_OPENAI_API_KEY' });
// 1. Handle the initial call
app.get('/webhooks/answer', (req, res) => {
const ncco = [
{
action: 'talk',
text: 'Hi, I am your AI assistant. How can I help you today?'
},
{
action: 'input',
eventUrl: [`${req.protocol}://${req.get('host')}/webhooks/asr`],
type: ['speech'],
speech: {
language: 'en-us',
endOnSilence: 1
}
}
];
res.json(ncco);
});
// 2. Process the Speech-to-Text result and query OpenAI
app.post('/webhooks/asr', async (req, res) => {
const speechResults = req.body.speech?.results;
if (!speechResults || speechResults.length === 0) {
return res.json([{ action: 'talk', text: 'I am sorry, I didn\'t catch that. Goodbye.' }]);
}
const userText = speechResults[0].text;
console.log(`User said: ${userText}`);
try {
// Request a completion from OpenAI
const completion = await openai.chat.completions.create({
model: "gpt-4o",
messages: [
{ role: "system", content: "You are a helpful assistant on a phone call. Keep answers concise." },
{ role: "user", content: userText }
],
});
const aiResponse = completion.choices[0].message.content;
// Respond back to the user
res.json([{ action: 'talk', text: aiResponse }]);
} catch (error) {
console.error("OpenAI Error:", error);
res.json([{ action: 'talk', text: 'I encountered an error processing your request.' }]);
}
});
// 3. Log call events
app.post('/webhooks/events', (req, res) => {
console.log('Event:', req.body.status);
res.sendStatus(200);
});
app.listen(3000, () => console.log('Server running on port 3000'));
Probar la aplicación
-
Ejecuta tu servidor:
node index.js -
Marca tu número de Vonage desde tu teléfono.
-
Cuando se te indique, haz una pregunta (por ejemplo, ¿Por qué el cielo es azul? o Cuéntame un chiste).
-
El bot grabará lo que digas, lo enviará a OpenAI y te leerá la respuesta utilizando Texto a voz.
Habilitar la conversación contextual
Para que la conversación resulte natural, debemos modificar la aplicación para que recuerde los intercambios anteriores y vuelva a preguntar al usuario.
Nota: Al ejecutarlo localmente con ngrok, req.get('host') puede no coincidir con su host de túnel público. Si los webhooks fallan, construya eventUrl utilizando la URL base de tu túnel público (por ejemplo desde config/env) en lugar del host de la petición.
Actualice su index.js con esta lógica con estado:
// 1. Add a Map to store conversation history by Call UUID
const sessions = new Map();
// Helper to generate a NCCO that "loops" back to ASR
const getConversationalNCCO = (text, host) => [
{ action: 'talk', text: text },
{
action: 'input',
eventUrl: [`https://${host}/webhooks/asr`],
type: ['speech'],
speech: { language: 'en-us', endOnSilence: 1 }
}
];
app.get('/webhooks/answer', (req, res) => {
const uuid = req.query.uuid;
// Initialize history for this specific caller
sessions.set(uuid, [{ role: "system", content: "You are a helpful, concise assistant." }]);
res.json(getConversationalNCCO('Hello! What is on your mind?', req.get('host')));
});
app.post('/webhooks/asr', async (req, res) => {
const { uuid, speech } = req.body;
const userText = speech?.results?.[0]?.text;
if (!userText) {
sessions.delete(uuid);
return res.json([{ action: 'talk', text: 'Goodbye!' }]);
}
// Retrieve history and append the new question
let history = sessions.get(uuid) || [];
history.push({ role: "user", content: userText });
const completion = await openai.chat.completions.create({
model: "gpt-4o",
messages: history,
});
const aiResponse = completion.choices[0].message.content;
history.push({ role: "assistant", content: aiResponse });
sessions.set(uuid, history);
// Return the AI response AND listen for the next question
res.json(getConversationalNCCO(aiResponse, req.get('host')));
});
// Clean up memory when the call ends
app.post('/webhooks/events', (req, res) => {
if (req.body.status === 'completed') sessions.delete(req.body.uuid);
res.sendStatus(200);
});
Qué ha cambiado
- El mapa de la sesión: Utilizamos el
uuidpara mantener separados los historiales de las distintas personas que llaman. - NCCO recursivo: En lugar de un simple
talkahora devolvemos untalkseguido de uninputacción. Esto mantiene la línea abierta. - Memoria: Al pasar todo el
historysegún OpenAI, el robot entiende ahora preguntas de seguimiento como Cuénteme más sobre eso.
Prueba la aplicación actualizada reiniciando el servidor y marcando el número de Vonage vinculado a tu aplicación, tal y como se indica en el Probar la aplicación paso.
Añadir la herramienta «Conectarse con una persona»
Este paso consiste en actualizar las definiciones de tus herramientas y añadir una rama a tu lógica ASR que devuelva el Vonage connect acción.
Actualización index.js
Añade la nueva definición de herramienta y modifica el asr para gestionar la transferencia:
// Define the transfer tool
const tools = [
{
type: "function",
function: {
name: "connect_to_human",
description: "Call this when the user wants to speak to a real person or a human agent.",
parameters: { type: "object", properties: {} } // No arguments needed
}
}
];
const HUMAN_AGENT_NUMBER = '15551234567'; // Replace with your phone number
app.post('/webhooks/asr', async (req, res) => {
const { uuid, speech } = req.body;
const userText = speech?.results?.[0]?.text;
if (!userText) return res.json([{ action: 'talk', text: 'Goodbye.' }]);
let history = sessions.get(uuid) || [];
history.push({ role: "user", content: userText });
const response = await openai.chat.completions.create({
model: "gpt-4o",
messages: history,
tools: tools // Provide the tool to the LLM
});
const message = response.choices[0].message;
// Check if the AI wants to transfer the call
if (message.tool_calls && message.tool_calls[0].function.name === 'connect_to_human') {
console.log(`Transferring call ${uuid} to human agent...`);
// Clean up session since the AI is leaving the call
sessions.delete(uuid);
// Return the "connect" NCCO
return res.json([
{
action: 'talk',
text: 'Please hold while I connect you to a human representative.'
},
{
action: 'connect',
from: 'YOUR_VONAGE_NUMBER', // Your linked Vonage number
endpoint: [{ type: 'phone', number: HUMAN_AGENT_NUMBER }]
}
]);
}
// Regular conversational flow
const aiResponse = message.content;
history.push({ role: "assistant", content: aiResponse });
sessions.set(uuid, history);
res.json(getConversationalNCCO(aiResponse, req.get('host')));
});
Cómo funciona
- La intención: Cuando el usuario dice Quiero hablar con un gestor o Ayúdame, esto es muy difícil El LLM reconoce la intención y activa el
connect_to_humanfunción. - El Hand-off: Su servidor detiene el bucle ASR y envía el
connectacción a Vonage. - La conexión: Vonage crea un nuevo tramo saliente hacia el
HUMAN_AGENT_NUMBERy une ambas llamadas. La IA deja de «escuchar» una vez establecida la conexión.
Reinicia tu servidor y llama al número de Vonage de tu aplicación. Cuando le pidas al bot que hable con un humano, debería decir la frase Por favor, espere un momento mientras le paso con un agente humano., y a continuación te conectará con el número de teléfono que hayas configurado como HUMAN_AGENT_NUMBER.
Próximos pasos
- Voces personalizadas: Cambiar el nombre de la voz en el
talkmedidas para ofrecer una experiencia más centrada en la marca. - Transmisión por WebSocket: Para reducir la latencia, utiliza WebSockets para transmitir audio en tiempo real.
- Puntos finales: Conéctese a su centralita o Contact Center vía SIP o crea tu propia interfaz web para un agente humano con Client SDK.
- Versión .NET: Consulta el mismo escenario de IVR/bot de voz implementado en .NET en este entrada de blog.