ai-machine-learning

Voice Agent mit der OpenAI Realtime API bauen: Das 7-Schritte-Tutorial für die Produktion (2026)

Geschrieben von Mert Batur
Jun 6, 2026
10 Lesezeit
Voice Agent mit der OpenAI Realtime API bauen: Das 7-Schritte-Tutorial für die Produktion (2026)

Voice Agent mit der OpenAI Realtime API bauen: Das 7-Schritte-Tutorial für die Produktion (2026)

Unser Testagent nahm einen Twilio-Anruf entgegen und sprach sein erstes Wort 1,1 Sekunden, nachdem der Anrufer aufgehört hatte zu reden. Das ist die p50-Round-Trip-Zeit, gemessen über 40 Anrufe mit gpt-realtime-2 und semantic_vad. Keine Magie. Die OpenAI Realtime API erledigt Speech-to-Speech in einem einzigen Socket, sodass du die STT → LLM → TTS-Kette umgehst, die rund 600 ms an Klebecode draufpackt. Aber mit den Standardwerten kommst du nicht auf eine Sekunde. Das ist der 7-Schritte-Build, den wir ausgeliefert haben, mit dem Code und der Latenztabelle.

Das ist ein Build-Tutorial, kein Konzept-Erklärstück. Wenn du zuerst die geschichtete Aufschlüsselung willst, lies was ein KI-Voice-Agent eigentlich ist und komm dann zurück. Alles Folgende setzt voraus, dass du einen OpenAI-API-Schlüssel und eine Node-Laufzeit hast.

Die wichtigsten Erkenntnisse:

  • gpt-realtime-2 macht Speech-to-Speech in einem Socket, keine STT/LLM/TTS-Kette, ~600 ms gespart.
  • Erzeuge ephemere Schlüssel serverseitig; gib deinen Standard-API-Schlüssel nie an einen Browser weiter.
  • Twilios Media-Stream ist 8 kHz μ-law; resample auf 24 kHz PCM16 für die Realtime API.
  • Wir maßen p50 1,1 s / p95 1,9 s Round-Trip. Barge-in läuft über response.cancel.

Was du in 7 Schritten baust

Dieses Tutorial baut einen telefonbeantwortenden OpenAI Realtime API Voice Agent, der in unter 1,5 Sekunden antwortet, mitten im Gespräch eine echte Funktion aufruft und dem Anrufer erlaubt, ihn zu unterbrechen. Der Ablauf ist kurz: Ein Anrufer wählt eine Telefonnummer, Audio streamt zu deinem Server, dein Server brückt es über einen einzigen Socket zu gpt-realtime-2, das Modell spricht und kann Tool-Aufrufe auslösen, und Audio streamt zurück.

Hier ist der Pfad, und du kannst bei jedem Schritt aufhören, der zu deinem Anwendungsfall passt:

  1. Ephemeren Schlüssel erzeugen (Server-Route)
  2. Session öffnen und konfigurieren
  3. Function Calling hinzufügen
  4. Mit Twilio an eine Telefonnummer brücken
  5. Barge-in und Unterbrechungen behandeln
  6. Latenz auf unter eine Sekunde tunen
  7. Deployen und für die Produktion härten

Drei Transports tragen das Audio, und deine Wahl hängt davon ab, woher das Audio kommt. Ein Browser erfasst es direkt (WebRTC), dein Server hat bereits einen rohen Stream (WebSocket), oder ein Telefonnetz liefert es (SIP). Wir nutzen WebSocket für die Twilio-Brücke und merken die anderen an, wo sie passen.

Schritt 1: Ephemeren Schlüssel erzeugen (die Route, die du nicht überspringen darfst)

Gib deinen Standard-OpenAI-API-Schlüssel niemals an einen Browser oder ein Client-Gerät weiter. Die Realtime API gibt kurzlebige ephemere Schlüssel genau dafür aus. Dein Server ruft POST /v1/realtime/client_secrets mit deinem echten Schlüssel auf, übergibt dem Client einen Token, der in etwa einer Minute abläuft, und der Client verbindet sich stattdessen damit.

Hier ist eine minimale Express-Route, die einen erzeugt:

javascript
// server.js
import express from "express";
const app = express();

app.get("/session", async (req, res) => {
  const r = await fetch("https://api.openai.com/v1/realtime/client_secrets", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.OPENAI_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      session: { type: "realtime", model: "gpt-realtime-2" },
    }),
  });
  const data = await r.json();
  res.json({ client_secret: data.value, expires_at: data.expires_at });
});

app.listen(3000);

Der Browser ruft /session ab, liest das kurzlebige Secret und öffnet die Realtime-Verbindung damit. Wenn dein Agent reiner Server ist (der Twilio-Fall in Schritt 4), kannst du die Client-Übergabe überspringen und den Socket vom Backend direkt mit dem Standardschlüssel öffnen. Der ephemere Ablauf existiert, um nicht vertrauenswürdige Clients zu schützen.

Schritt 2: Session öffnen und gpt-realtime-2 konfigurieren

Öffne eine Verbindung und sende dann ein session.update, das Modell, Audioformat, Stimme und Turn Detection setzt. Die OpenAI-Dokumentation empfiehlt, mit reasoning.effort auf low zu starten und es nur zu erhöhen, wenn deine Tool-Logik mehr Genauigkeit braucht, da höherer Effort dich Latenz kostet. Audio läuft als 24 kHz PCM16 in beide Richtungen.

javascript
ws.send(JSON.stringify({
  type: "session.update",
  session: {
    type: "realtime",
    model: "gpt-realtime-2",
    output_modalities: ["audio"],
    audio: {
      input:  { format: "pcm16", sample_rate: 24000 },
      output: { format: "pcm16", sample_rate: 24000, voice: "marin" },
    },
    instructions: "Du bist ein Reservierungsagent für ein Restaurant. Sei kurz.",
    reasoning: { effort: "low" },
    turn_detection: { type: "semantic_vad" },
  },
}));

Welchen Transport du um diesen Socket legst, hängt von der Audioquelle ab:

TransportVerwenden wennAudioquelle
WebRTCBrowser oder mobile App erfasst Mikrofon direktClient-Gerät
WebSocketDein Server hält bereits einen rohen AudiostreamServer-Pipeline
SIPDu willst, dass OpenAI die Telefonstrecke übernimmtPSTN / Telefonie

Für die vollständige Feldliste der Session und den GA-Funktionsumfang sind die OpenAI Realtime API Docs die maßgebliche Quelle. Wir nutzen WebSocket, weil Twilio uns in Schritt 4 rohes Audio liefert.

Schritt 3: Function Calling hinzufügen (damit der Agent wirklich etwas tun kann)

Ein Voice Agent, der nicht handeln kann, ist ein Voice-over. Function Calling lässt gpt-realtime-2 mitten im Gespräch pausieren, deinen Code etwas ausführen lassen und mit dem Ergebnis weitersprechen. Du deklarierst ein Tool in der Session, das Modell emittiert ein function_call_arguments.done-Event, wenn es das Tool will, du führst die Arbeit aus und sendest die Ausgabe zurück.

Deklariere das Tool und behandle dann das Event:

javascript
// in session.update -> session.tools:
tools: [{
  type: "function",
  name: "book_reservation",
  description: "Bucht einen Tisch für eine Personenzahl und Uhrzeit.",
  parameters: {
    type: "object",
    properties: {
      party_size: { type: "integer" },
      time: { type: "string", description: "ISO 8601 Datum/Zeit" },
    },
    required: ["party_size", "time"],
  },
}]

// Behandlung des Aufrufs:
if (event.type === "response.function_call_arguments.done") {
  const args = JSON.parse(event.arguments);
  const result = await bookTable(args);            // deine echte Logik
  ws.send(JSON.stringify({
    type: "conversation.item.create",
    item: {
      type: "function_call_output",
      call_id: event.call_id,
      output: JSON.stringify(result),
    },
  }));
  ws.send(JSON.stringify({ type: "response.create" }));  // lass es das Ergebnis sprechen
}

Der häufigste Grund, warum Tools stumm nie auslösen: nicht auf function_call_arguments.done zu hören und danach kein response.create zu senden. Das Modell erzeugte den Aufruf, du ignoriertest ihn, der Anrufer hört Funkstille.

Wenn dein Agent viele Tools jongliert, hat das OpenAI Agents SDK die Rechnung hier verändert. Sein Überholungs-Update vom 15. April 2026 machte das Model Context Protocol (MCP) erstklassig und verwandelte Sub-Agent-Handoffs in ein Laufzeit-Primitiv. So kann statt jedes Tool in einen Prompt zu stopfen ein Router-Agent eine Buchung an einen Reservierungs-Sub-Agenten und eine Abrechnungsfrage an einen anderen übergeben. Der Agents-SDK-Voice-Quickstart verpackt dieselbe Realtime-Session in einen RealtimeAgent und gibt dir Handoffs, ohne dass du deine eigene Orchestrierungsschleife schreibst.

Schritt 4: An eine Telefonnummer brücken (Twilio)

Um echte Anrufe entgegenzunehmen, brückst du einen Telefonie-Anbieter in den Socket. Mit Twilio richtest du einen eingehenden Anruf auf ein TwiML <Connect><Stream>, das einen WebSocket zu deinem Server öffnet, und du leitest Audio-Frames zwischen Twilio und der Realtime API weiter. SIP ist die Alternative. OpenAI Realtime akzeptiert SIP direkt, was deinen Media-Relay ganz entfernt, wenn du das Audio nicht anfassen musst.

Das TwiML, das den Stream startet:

xml
<Response>
  <Connect>
    <Stream url="wss://your-server.com/twilio-stream" />
  </Connect>
</Response>

Hier ist der Haken, der einen Tag frisst, wenn du ihn übersiehst: Twilios Media-Stream ist 8 kHz μ-law, und die Realtime API will 24 kHz PCM16. Du resamplest in beide Richtungen, oder du bekommst verzerrtes Chipmunk-Audio.

javascript
// eingehend: Twilio (8kHz μ-law base64) -> Realtime (24kHz PCM16)
const pcm16 = upsample(muLawDecode(Buffer.from(msg.media.payload, "base64")), 8000, 24000);
realtime.send(JSON.stringify({
  type: "input_audio_buffer.append",
  audio: pcm16.toString("base64"),
}));

// ausgehend: Realtime (24kHz PCM16) -> Twilio (8kHz μ-law)
const ulaw = muLawEncode(downsample(modelPcm16, 24000, 8000));
twilioWs.send(JSON.stringify({
  event: "media",
  media: { payload: ulaw.toString("base64") },
}));

Das vollständige Frame-Format steht in den Twilio-Media-Streams-Docs. Halte das Resampling günstig, denn eine schwere Bibliothek hier fügt Latenz hinzu, die du bei jedem Frame bezahlst.

Schritt 5: Barge-in und Unterbrechungen behandeln

Ein Produktionsagent lässt den Anrufer dazwischenreden. Barge-in bedeutet zu erkennen, dass der Anrufer zu sprechen begann, während der Agent mitten im Satz ist, und den Agenten dann sauber abzuschneiden. Die Realtime API handhabt das mit response.cancel: Wenn Turn Detection meldet, dass während der Wiedergabe Sprache begonnen hat, brichst du die aktive Antwort ab und leerst das bereits Richtung Anrufer gepufferte Audio.

javascript
if (event.type === "input_audio_buffer.speech_started") {
  realtime.send(JSON.stringify({ type: "response.cancel" }));
  twilioWs.send(JSON.stringify({ event: "clear" }));   // gepufferte Wiedergabe verwerfen
}

Turn Detection hat zwei Modi, und die Wahl zählt. server_vad löst bei rohen Stille-Schwellen aus und neigt dazu, den Anrufer bei natürlichen Pausen abzuschneiden. semantic_vad wartet, bis das Modell denkt, der Anrufer habe tatsächlich einen Gedanken beendet, und erzeugt so weit weniger falsche Unterbrechungen bei einer Denkpause. Für Telefonanrufe ist semantisches VAD das, was menschlich wirkt.

Schritt 6: Latenz auf unter eine Sekunde tunen

Hier wird aus einer Demo ein Produkt, also hier die Zahlen aus unserem eigenen Build, kein theoretisches Budget. Wir liefen denselben Restaurant-Agenten über 40 Testanrufe im Mai 2026, auf einem einzigen kleinen Server nahe der OpenAI-Region, und tauschten nur die Turn-Detection- und Reasoning-Einstellungen.

Konfigurationp50 Round-Tripp95Hinweise
server_vad, reasoning low~1,4 s~2,3 smehr falsche Barge-ins bei Pausen
semantic_vad, reasoning low~1,1 s~1,9 sunser Produktionsstandard
semantic_vad, reasoning medium~1,8 s~3,1 sbessere Tool-Genauigkeit, langsamer

Die Hebel, die wirklich etwas bewegten, in der Reihenfolge ihrer Wirkung:

  • Halte reasoning.effort auf low, außer ein bestimmtes Tool braucht die Genauigkeit wirklich. Medium verdoppelte unseren p50 fast.
  • Schiebe Audio nicht schneller als in Echtzeit. input_audio_buffer.append zu fluten überläuft den Puffer und verursacht Drift; takte Frames auf die Wanduhrzeit.
  • Halte den Socket warm. Eine Verbindung pro Anruf kalt zu öffnen fügt den Handshake zu deiner First-Word-Latenz hinzu. Poole Verbindungen, wo das Anrufvolumen es erlaubt.
  • Resample effizient. Ein naiver Resampler im heißen Pfad fügte bei uns ~80 ms pro Turn hinzu.

Was kostet der Betrieb pro Minute, sobald es live ist? Wir haben die Bring-your-own-Key-Rechnung separat gemacht, siehe was ein BYOK-Voice-Agent pro Minute kostet, statt sie hier neu herzuleiten.

Schritt 7: Deployen und für die Produktion härten

Die Lücke zwischen "es lief auf meinem Laptop" und "es übersteht 500 Anrufe am Tag" ist eine Handvoll bekannter Fehler. Hier die Härtungs-Checkliste, gezogen aus den Fehlern, die Realtime-Agenten tatsächlich brechen:

FalleSymptomLösung
Falsche Sample-Rateverzerrtes / Chipmunk-Audio24 kHz PCM16 in beide Richtungen
function_call_arguments.done ignoriertTools lösen nie aushören und response.create senden
Audio schneller als Echtzeit schiebenPufferüberlauf, DriftFrames auf Echtzeit takten
Keine Reconnect-LogikAnrufe brechen bei Socket-Aussetzer abAuto-Reconnect + Session fortsetzen
Kein response.done-Handlingüberlappende Turnsnächsten Turn auf response.done gaten

Zwei weitere Dinge für echten Verkehr. Bei langen Anrufen rotiere oder reseede die Session alle paar Turns, damit der Kontext nicht driftet, denn ein 20-minütiger Anruf sammelt Zustand an, über den das Modell zu stolpern beginnt. Und logge jeden Tool-Aufruf mit Argumenten und Ergebnis; wenn ein Anrufer sagt "der Agent buchte die falsche Zeit", verrät dir das Transkript allein nicht, ob das Modell oder dein Code falsch lag.

Wenn du den Agents-SDK-Weg aus Schritt 3 wählst, lässt seine neue Container-Sandbox Tool-Code isoliert laufen, was wichtig wird, sobald deine Tools ein Dateisystem oder eine Shell statt nur einer API berühren.

Wann du stattdessen eine Managed-Plattform kaufen solltest

Direkt auf der Realtime API zu bauen gibt dir die meiste Kontrolle und die niedrigsten Kosten pro Minute, aber du besitzt die Reconnect-Logik, die Telefonbrücke, Compliance und Observability, all die unglamourösen Teile der Schritte 4 bis 7. Wenn du diese Woche einen Telefonagenten live brauchst und keinen Media-Relay pflegen willst, ist eine Managed-Plattform die schnellere Wahl.

Wir bauten denselben Agenten auf den drei großen und verglichen sie ehrlich: Retell, Vapi oder Bland. Wenn du noch entscheidest, auf welcher Seite der Linie du stehst, geh das vollständige Build-vs-Buy-Entscheidungsframework durch, bevor du Engineering-Zeit festlegst.

Wenn Teams die Kontrolle eines individuellen Realtime-Builds wollen, ohne ihn personell zu besetzen, ist das die Arbeit, die wir machen: Voice-Agent-Entwicklung für die Produktion, von der Telefonbrücke bis zum Latenz-Tuning oben. Schauen uns gern deinen Anwendungsfall an, wenn du ihn abwägst.

Über den Autor — Mert Batur ist Mitgründer von Techsy.io, wo das Team KI-Agenten, Automatisierungssysteme und Voice/SDR-Pipelines für B2B-Kunden ausliefert. Er schreibt über den LLM-Tooling-Stack, den das Techsy-Team tatsächlich in der Produktion einsetzt. LinkedIn

Häufig gestellte Fragen

Wie hoch ist die Latenz eines OpenAI Realtime API Voice Agents?

In unserem Build mit gpt-realtime-2, semantic_vad und niedrigem Reasoning-Effort lag die Round-Trip-Latenz bei p50 1,1 s und p95 1,9 s über 40 Testanrufe. Speech-to-Speech in einem Socket vermeidet die STT/LLM/TTS-Kette, was Antworten unter einer Sekunde überhaupt erst möglich macht.

Brauche ich WebRTC, WebSocket oder SIP für meinen Voice Agent?

Nutze WebRTC, wenn ein Browser oder eine mobile App das Mikrofon direkt erfasst, WebSocket, wenn dein Server bereits einen rohen Audiostream hält (der Twilio-Brücken-Fall), und SIP, wenn OpenAI die Telefonstrecke ohne deinen eigenen Media-Relay übernehmen soll. Die meisten Telefonagenten nutzen WebSocket oder SIP.

Wie verbinde ich die OpenAI Realtime API mit Twilio?

Richte einen eingehenden Twilio-Anruf auf ein TwiML <Connect><Stream>, das einen WebSocket zu deinem Server öffnet, und leite dann Audio zwischen Twilio und dem Realtime-Socket weiter. Resample Twilios 8 kHz μ-law auf die 24 kHz PCM16 der API in beide Richtungen, sonst kommt das Audio verzerrt heraus.

Wie funktioniert Function Calling in der Realtime API?

Du deklarierst Tools in der Session-Konfiguration. Wenn das Modell eines will, emittiert es ein function_call_arguments.done-Event. Du führst die Arbeit aus, sendest das Ergebnis als function_call_output-Konversationselement zurück und sendest dann response.create, damit der Agent das Ergebnis spricht. Den letzten Schritt zu vergessen ist der Grund, warum Tools oft "stumm" versagen.

Wie behandelt man Unterbrechungen (Barge-in) in der Realtime API?

Wenn Turn Detection während der Wiedergabe input_audio_buffer.speech_started meldet, sende response.cancel, um die aktive Antwort zu stoppen, und leere jegliches gepufferte Ausgabe-Audio Richtung Anrufer. Paare es mit semantic_vad, damit natürliche Pausen keine falschen Unterbrechungen mitten im Satz auslösen.

Welche Audio-Sample-Rate nutzt die OpenAI Realtime API?

Die Realtime API nutzt 24 kHz PCM16-Audio in beide Richtungen. Telefonie-Anbieter wie Twilio liefern 8 kHz μ-law, also muss eine Telefonbrücke beim Eingang hochresamplen und beim Ausgang herunter. Nicht übereinstimmende Sample-Raten sind die häufigste Ursache für verzerrtes Audio.

Wie viel kostet der Betrieb eines Voice Agents auf der Realtime API?

Die Kosten werden durch Audio-Eingabe- und Ausgabe-Minuten auf gpt-realtime-2 getrieben, und die Bring-your-own-Key-Ökonomie unterscheidet sich stark von einer Managed-Per-Minute-Plattform. Wir haben die vollständige Rechnung in unserer Voice-Agent-Preisanalyse gemacht, statt sie hier zu schätzen.

Sollte ich auf der Realtime API bauen oder Retell, Vapi oder Bland nutzen?

Baue direkt, wenn du maximale Kontrolle und die niedrigsten Kosten pro Minute willst und Reconnects, Telefonie und Compliance besitzen kannst. Kaufe eine Managed-Plattform, wenn die Geschwindigkeit zum Launch wichtiger ist. Unser Retell-vs-Vapi-vs-Bland-Vergleich und das Build-vs-Buy-Framework decken die Abwägungen ab.

Was änderte das OpenAI-Agents-SDK-Update vom April 2026 für Voice Agents?

Die Überholung vom 15. April 2026 machte das Model Context Protocol erstklassig, fügte eine Container-Sandbox für Tool-Code hinzu und verwandelte Sub-Agent-Handoffs in ein Laufzeit-Primitiv. Für Voice Agents bedeutet das, dass ein Router-Agent an spezialisierte Sub-Agenten übergeben kann, statt jedes Tool in einen Prompt zu stopfen.

Tags

openai realtime api voice agentgpt-realtime-2function callingtwilio voice agentopenai agents sdkvoice ai tutorial

Diesen Artikel teilen

Ihr Projekt starten

Bereit, etwas Außergewöhnliches zu bauen?

Machen wir aus Ihrer Vision ein fertiges Produkt. Unser Team baut mit Ihnen Software, die spürbar etwas bewegt.