OpenAI Realtime API: Echtzeit-Unterhaltungen mit Sprachmodellen
Seite 2: Tutorial: Die Echtzeit-KI per Telefon anrufen
Es folgt ein vollständiges Beispiel für die Nutzung der OpenAI Realtime API mit Node.js. Die API wird mit einer Kommunikationsplattform, auch Communication-Platform-as-a-Service (CPaas) genannt, integriert, um eingehende Anrufe in Echtzeit von einer KI beantworten zu lassen. Solche Anbieter sind beispielsweise Twilio, Vonage oder Infobip. Dieses praktische Beispiel zeigt, wie Sprache als Schnittstelle genutzt werden kann, um intelligente KI-Systeme mit bewährten Kommunikationsmitteln wie Telefonie zu kombinieren.
Traditionelle APIs basieren auf HTTP-Anfragen, die eine Anfrage senden und auf eine Antwort warten. Für Echtzeitanwendungen wie Telefonie ist diese Methode jedoch ungeeignet, da sie Latenzzeiten erzeugt und keine kontinuierliche Datenübertragung unterstützt. Hier kommen WebSockets ins Spiel.
WebSockets sind bidirektionale Kommunikationskanäle, die es ermöglichen, Datenströme zwischen einem Server und einem Client offenzuhalten. Dadurch lassen sich kontinuierlich Daten – wie Audio- oder Textinhalte – in Echtzeit übertragen. Das vorliegende Beispiel verbindet zwei WebSocket-Kanäle miteinander: einen zwischen Twilio und Server und einen zwischen Server und OpenAI-API. Diese Struktur sorgt für eine nahtlose, latenzarme Kommunikation.
1. Einrichtung und Abhängigkeiten
Im ersten Abschnitt des Codes werden die benötigten Abhängigkeiten importiert. Hierbei handelt es sich um:
- Fastify als Webframework
- Zusätzliche Fastify-Plug-ins für Formulardaten und WebSocket-Funktionalität
- WebSocket für die bidirektionale Kommunikation
- dotenv zum Laden von Umgebungsvariablen
import Fastify from 'fastify';
import fastifyFormBody from '@fastify/formbody';
import fastifyWs from '@fastify/websocket';
import WebSocket from 'ws';
import dotenv from 'dotenv';
2. Laden der Umgebungsvariablen
Der API-Schlüssel für OpenAI wird als Umgebungsvariable aus einer .env-Datei geladen. Falls sie fehlt, wird das Programm mit einer Fehlermeldung beendet. Dies stellt sicher, dass der Server nur mit den korrekten Konfigurationsdaten startet.
// Lese Umgebungsvariablen aus einer .env-Datei
dotenv.config();
const { OPENAI_API_KEY } = process.env;
if (!OPENAI_API_KEY) {
console.error('Fehlende Umgebungsvariable. Bitte definieren Sie diese in einer .env-Datei.');
process.exit(1);
}
3. Verarbeiten eingehender Anrufe
Die Route /incoming-call wird bei einem eingehenden Anruf an die verwendete Nummer angerufen. Die verwendete Kommunikationsplattform sendet Anrufinformationen an diese URL. Der Server antwortet mit einem TwiML-Dokument, das den Anruf steuert:
- Eine Begrüßungsnachricht wird über
<Say>abgespielt. - Der Anruf wird an einen WebSocket-Stream weitergeleitet, der an die Route
/media-streamangebunden ist.
Die Konfiguration des Streams erfolgt über <Stream>-Tags und kann optionale Parameter wie die Anrufer-ID enthalten.
// Initisalisiere Fastify-Server
const fastify = Fastify();
fastify.register(fastifyFormBody);
fastify.register(fastifyWs);
fastify.all('/incoming-call', async (request, reply) => {
const twimlResponse = `<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say language="de-De">Viel Spaß beim Testen des Sprachassistenten!</Say>
<Connect>
<Stream url="wss://${request.headers.host}/media-stream"/>
</Connect>
</Response>`;
reply.type('text/xml').send(twimlResponse);
});
4. WebSocket-Verbindung zur CPaaS
Die Route /media-stream ist das Herzstück des Programms. Sie verarbeitet die WebSocket-Verbindung zwischen Twilio und dem Server und leitet Daten von Twilio zu OpenAI weiter. Beim Aufbau dieser Verbindung geschieht Folgendes:
Nachdem die Variablen für die Sitzungsdefinition definiert sind, initialisiert das Programm eine zweite WebSocket-Verbindung zur OpenAI Realtime API. Anschließend erfolgt die Konfiguration der WebSocket-Verbindungen zu OpenAI und Twilio. Dabei wird sichergestellt, dass Sitzungen beim Aufbau der Verbindung initialisiert und eingehende Nachrichten aus beiden Quellen korrekt verarbeitet werden.
Zusätzlich protokollieren die eingerichteten Handler Verbindungsabbrüche oder Fehler und schließen bei Bedarf die jeweilige Verbindung. Da sich die API noch im Beta-Status befindet, wird direkt mit WebSockets gearbeitet, um Nachrichten zu senden und zu empfangen. OpenAI arbeitet bereits an einem SDK, das die Verarbeitung der Events abstrahieren soll. Dieses SDK ist jedoch derzeit noch nicht über npm verfügbar und lässt sich ausschließlich via GitHub herunterladen.
// WebSocket-Route für die WebSocket-Verbindung zwischen Twilio und OpenAI
fastify.register(async (fastify) => {
fastify.get('/media-stream', { websocket: true }, (twilioWs, req) => {
console.log('Anrufer verbunden');
// Zustandsvariablen
let streamSid = null, lastAssistantItem = null,
responseStartTimestampTwilio = null,
latestMediaTimestamp = 0, markQueue = [];
const openAiWs = new WebSocket('wss://api.openai.com/v1/realtime?model=gpt-4o-mini-realtime-preview-2024-12-17', {
headers: {
Authorization: `Bearer ${OPENAI_API_KEY}`,
"OpenAI-Beta": "realtime=v1"
}
});
openAiWs.on('open', initialisiereOpenAiSession);
openAiWs.on('message', verarbeiteOpenAiNachrichten);
openAiWs.on('close', () => {
console.log('Disconnected from the OpenAI Realtime API');
});
openAiWs.on('error', (error) => {
console.error('Error in the OpenAI WebSocket:', error);
});
twilioWs.on('message', verarbeiteAnruferNachrichten);
twilioWs.on('close', () => {
if (openAiWs.readyState === WebSocket.OPEN) openAiWs.close();
console.log('Client disconnected.');
});
function initialisiereOpenAiSession() {
console.log('Verbunden mit der OpenAI Realtime API');
…
};
function verarbeiteAnruferNachrichten(message) {
…
};
// Liste der Ereignistypen, die protokolliert werden sollen. Vergleiche mit der OpenAI-Dokumentation https://platform.openai.com/docs/api-reference/realtime
const LOG_EVENT_TYPES = [
'error',
"conversation.item.created",
"response.function_call_arguments.done",
];
function verarbeiteOpenAiNachrichten(data) {
…
};
});
});