Wie man einen KI-Sprachagenten mit Vonage Voice API und Deepgram erstellt
Einführung
Dieser Leitfaden beschreibt den Prozess der Erstellung eines Echtzeit-KI-Sprachagenten mit der Voice API von Vonage und der Voice Agent-Plattform von Deepgram. Sie werden einen intelligenten Sprachassistenten erstellen, der Telefonanrufe beantwortet, Benutzern über automatische Spracherkennung (ASR) zuhört, Anfragen mit einem Large Language Model (LLM) verarbeitet und mit natürlich klingender Text-to-Speech-Sprache antwortet - alles in Echtzeit. Außerdem unterstützt das Setup die Unterbrechung von Gesprächen, auch bekannt als Barge-in.
Voraussetzungen
Bevor Sie beginnen, vergewissern Sie sich, dass Sie alles haben:
- Ein Vonage API-Konto. Kostenlos anmelden.
- Node.js Version 18 oder höher auf Ihrem Rechner installiert haben.
- A Deepgram-Konto mit einem API-Schlüssel.
- ngrok auf Ihrem Rechner installiert.
Einrichten Ihrer lokalen Umgebung
Erstellen Sie ein neues Verzeichnis für Ihr Projekt und installieren Sie die erforderlichen Abhängigkeiten:
Lokalen Server freilegen
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.
Bereitstellung Ihrer Vonage-Ressourcen
Melden Sie sich bei der Vonage Dashboard zu beginnen.
Erstellen einer Vonage-Applikation
Generieren Sie Ihre Anmeldedaten über das Dashboard und speichern Sie sie in dem soeben erstellten Ordner.
- Gehe zu Applications > Erstellen Sie eine neue Anwendung.
- Geben Sie Ihrer Anwendung einen Namen.
- Authentifizierung: Klicken Sie auf Öffentliche und private Schlüssel generieren.
- Eine Datei namens
private.keyherunterladen wird. - Verschieben Sie diese
private.keyvonage-deepgram-voice-agentOrdner.
- Eine Datei namens
- Unter Fähigkeitenaktivieren Stimme.
- Legen Sie in den Spracheinstellungen die folgenden Webhooks fest:
- Antwort-URL:
https://{ngrok-url}/answer(Methode:GET) - URL der Veranstaltung:
https://{ngrok-url}/event(Methode:POST)
- Antwort-URL:
- Klicken Sie auf Neue Anwendung generieren am unteren Ende.
Verknüpfung einer Number
- Gehe zu Phone Numbers > Numbers kaufen und kaufen Sie eine sprachaktivierte Nummer.
- Gehe zu Applications, wählen Sie Ihre Bot-Anwendung aus, und klicken Sie auf bearbeiten.
- Im Rahmen der Numbers Registerkarte, klicken Sie auf Link neben Ihrer neu erworbenen Nummer.
Umgebungsvariablen konfigurieren
Erstellen einer .env Datei in Ihrem Projektverzeichnis mit den folgenden Variablen:
#==== 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
Wichtig: Speichern Sie Ihre API-Schlüssel in Umgebungsvariablen, anstatt sie aus Sicherheitsgründen in Ihrem Quellcode zu kodieren.
Erstellen Sie den Voice Agent Connector
Erstellen Sie eine Datei mit dem Namen server.js und fügen Sie den folgenden Code hinzu. Diese Anwendung fungiert als Konnektor zwischen Vonage Voice API und 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!`);
});
Hinweis: Wenn man lokal mit ngrok arbeitet, wird die req.hostname nicht mit Ihrer öffentlichen Tunnel-URL übereinstimmen. Wenn Webhooks fehlschlagen, setzen Sie Ihre ngrok-Basis-URL als Umgebungsvariable und verwenden Sie sie, um die eventUrl und wsUri stattdessen.
Hinzufügen der WebSocket-Connector-Logik
Fügen Sie nun die zentrale Verbindungslogik hinzu, die die Vonage Voice API mit dem Deepgram Voice Agent verbindet. Hängen Sie dies an Ihre 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");
});
});
Wie es funktioniert
Vereinfachtes Audio-Streaming: Audio von Deepgram wird direkt als Binärnachricht an Vonage gesendet. Es ist keine manuelle Pufferung oder Zeitsteuerung erforderlich - Vonage übernimmt die interne Pufferung automatisch.
Puffer löschen Steuermeldung: Wenn Deepgram erkennt, dass der Benutzer zu sprechen begonnen hat (UserStartedSpeaking Ereignis), sendet die Anwendung eine CLEAR-Kontrollnachricht an Vonage: {"action": "clear"}. Dadurch wird die Vonage Voice API angewiesen, alle gepufferten Audio-Frames sofort zu verwerfen, wodurch eine sofortige Barge-in-Funktionalität ohne manuelle Pufferverwaltung geschaffen wird.
Event-Bestätigung: Vonage antwortet mit einer websocket:cleared Ereignis, um zu bestätigen, dass der Puffer erfolgreich geleert wurde. So können Sie verfolgen, wann Unterbrechungen auftreten.
Bidirektionale Kommunikation: Benutzer-Audio fließt von Vonage → Deepgram als binäre WebSocket-Nachrichten, während Agenten-Audio und Transkripte von Deepgram → Vonage in Echtzeit fließen.
Abschriften in Echtzeit: Deepgram sendet JSON-Nachrichten, die Transkripte sowohl der Benutzerrede als auch der Agentenantworten enthalten, die Sie protokollieren oder zu Analyse- und Qualitätssicherungszwecken verarbeiten können.
Testen Sie die Applikation
- Stellen Sie sicher, dass Ihr
private.keyDatei befindet sich im Projektverzeichnis. - Starten Sie ngrok in einem Terminal:
- Starten Sie Ihren Server in einem anderen Terminal:
- Rufen Sie Ihre Vonage-Telefonnummer von Ihrem Mobiltelefon aus an.
- Der Voice-Agent begrüßt Sie und beantwortet Ihre Fragen mithilfe von KI-gestützter Konversation.
Ausgehende Anrufe hinzufügen
Damit Ihre Anwendung ausgehende Anrufe tätigen kann, fügen Sie diesen Endpunkt zu Ihrem 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));
}
});
Um einen abgehenden Anruf auszulösen, öffnen Sie Ihren Browser und navigieren Sie zu:
https://your-ngrok-url.ngrok.io/call?callee=15551234567
Ersetzen Sie 15551234567 mit der Rufnummer, die Sie anrufen möchten (im E.164-Format ohne die + Zeichen).
Passen Sie Ihren Sprachagenten an
Sie können verschiedene Aspekte des Sprachagenten anpassen, indem Sie die dgVoiceAgentSettings Objekt:
Ändern Sie das AI-Modell
"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."
}
Ändern Sie die Stimme
Aktualisieren Sie die DEEPGRAM_AGENT_SPEAK Variable in Ihrer .env Datei. Siehe Dokumentation der TTS-Modelle von Deepgram für die verfügbaren Sprachoptionen.
Anpassen der System-Eingabeaufforderung
Ändern Sie die prompt Feld in der think Abschnitt, um die Persönlichkeit und das Verhalten Ihres Agenten zu ändern:
"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."
Nächste Schritte
- Erkunden Sie WebSocket-Dokumentation für erweiterte Audio-Streaming-Muster.
- hinzufügen Gesprächsaufzeichnung und -transkription für Audit-Zwecke und zur Qualitätskontrolle.