
Construiește un Agent Vocal pe OpenAI Realtime API: Tutorial de Producție în 7 Pași (2026)
Agentul nostru de test a răspuns la un apel Twilio și a rostit primul său cuvânt la 1,1 secunde după ce apelantul a încetat să vorbească. Aceasta este valoarea p50 pentru dus-întors, măsurată pe 40 de apeluri folosind gpt-realtime-2 cu semantic_vad. Nu este magie. OpenAI Realtime API realizează conversia vorbire-la-vorbire într-un singur socket, astfel încât eviți releul STT → LLM → TTS care adaugă aproximativ 600 ms de overhead. Dar setările implicite nu te vor duce sub o secundă. Aceasta este implementarea în 7 pași pe care am lansat-o, inclusiv codul și tabelul de latență.
Acesta este un tutorial de construcție, nu o explicație conceptuală. Dacă dorești mai întâi o defalcare detaliată a conceptelor, citește ce este de fapt un agent vocal AI, apoi revino aici. Tot ceea ce urmează presupune că ai o cheie API OpenAI și un runtime Node.
Puncte cheie:
- gpt-realtime-2 realizează conversia vorbire-la-vorbire într-un singur socket — fără releu STT/LLM/TTS, economisind ~600 ms.
- Generează chei efemere pe server; nu expune niciodată cheia ta API standard unui browser.
- Fluxul media Twilio este de 8kHz μ-law; reeșantionează la 24kHz PCM16 pentru Realtime API.
- Am măsurat p50 1,1s / p95 1,9s pentru dus-întors. Întreruperea (barge-in) se declanșează prin
response.cancel.
Ce vei construi în 7 pași
Acest tutorial construiește un agent vocal OpenAI Realtime API care răspunde la telefon în mai puțin de 1,5 secunde, apelează o funcție reală în mijlocul conversației și permite apelantului să îl întrerupă. Fluxul este scurt: un apelant sună la un număr de telefon, audio-ul este transmis către serverul tău, serverul face puntea către gpt-realtime-2 printr-un singur socket, modelul vorbește și poate declanșa apeluri de instrumente (tool calls), iar audio-ul este transmis înapoi.
Iată parcursul, iar tu te poți opri la orice pas care se potrivește cazului tău de utilizare:
- Generează o cheie efemeră (rută server)
- Deschide și configurează sesiunea
- Adaugă apelarea de funcții (function calling)
- Conectează la un număr de telefon cu Twilio
- Gestionează întreruperile (barge-in)
- Optimizează latența pentru sub o secundă
- Implementează și securizează
Trei tipuri de transport transmit audio-ul, iar alegerea depinde de sursa audio-ului. Un browser îl captează direct (WebRTC), serverul tău deține deja un flux brut (WebSocket) sau o rețea telefonică îl livrează (SIP). Vom folosi WebSocket pentru puntea Twilio și vom menționa celelalte opțiuni acolo unde se potrivesc.
Pasul 1: Generează o cheie efemeră (ruta pe care nu o poți omite)
Nu expune niciodată cheia ta standard OpenAI API unui browser sau unui dispozitiv client. Realtime API emite chei efemere cu durată scurtă de viață exact pentru acest scop. Serverul tău apelează POST /v1/realtime/client_secrets cu cheia ta reală, oferă clientului un token care expiră în aproximativ un minut, iar clientul se conectează folosindu-l pe acesta.
Iată o rută Express minimală care generează una:
// 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);Browserul preia /session, citește secretul cu durată scurtă de viață și deschide conexiunea Realtime cu acesta. Dacă agentul tău rulează doar pe server (cazul Twilio din Pasul 4), poți omite handoff-ul către client și poți deschide socket-ul direct din backend-ul tău cu cheia standard. Fluxul efemer există pentru a proteja clienții neîncrezători.
Pasul 2: Deschide Sesiunea și Configurează gpt-realtime-2
Deschide o conexiune, apoi trimite un session.update care setează modelul, formatul audio, vocea și detectarea schimbării de tură. Documentația OpenAI recomandă să începi cu reasoning.effort setat la low și să îl crești doar dacă logica instrumentelor tale necesită mai multă precizie, deoarece un efort mai mare costă latență. Audio-ul rulează ca 24kHz PCM16 în ambele direcții.
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: "You are a reservations agent for a restaurant. Be brief.",
reasoning: { effort: "low" },
turn_detection: { type: "semantic_vad" },
},
}));Transportul în care împachetezi acel socket depinde de sursa audio:
| Transport | Folosește când | Sursă audio |
|---|---|---|
| WebRTC | Browserul sau aplicația mobilă capturează microfonul direct | Dispozitiv client |
| WebSocket | Serverul tău deține deja un flux audio brut | Pipeline server |
| SIP | Dorești ca OpenAI să gestioneze partea telefonică | PSTN / telefonie |
Pentru lista completă a câmpurilor sesiunii și setul de funcții GA, documentația OpenAI Realtime API este sursa de adevăr. Vom folosi WebSocket deoarece Twilio ne oferă audio brut în Pasul 4.
Pasul 3: Adaugă Apelarea de Funcții (pentru ca agentul să poată face ceva)
Un agent vocal care nu poate acționa este doar o naratiune. Apelarea de funcții (Function calling) permite lui gpt-realtime-2 să facă o pauză în mijlocul conversației, să ceară codului tău să execute ceva și să continue discuția cu rezultatul. Declari un instrument în sesiune, modelul emite un eveniment function_call_arguments.done când îl dorește, tu execuți lucrarea și trimiți output-ul înapoi.
Declară instrumentul, apoi gestionează evenimentul:
// in session.update -> session.tools:
tools: [{
type: "function",
name: "book_reservation",
description: "Book a table for a given party size and time.",
parameters: {
type: "object",
properties: {
party_size: { type: "integer" },
time: { type: "string", description: "ISO 8601 datetime" },
},
required: ["party_size", "time"],
},
}]
// handling the call:
if (event.type === "response.function_call_arguments.done") {
const args = JSON.parse(event.arguments);
const result = await bookTable(args); // your real logic
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" })); // let it speak the result
}Cel mai frecvent motiv pentru care instrumentele nu se declanșează niciodată în mod silențios: nu asculți după function_call_arguments.done și nu trimiți response.create ulterior. Modelul a produs apelul, tu l-ai ignorat, iar apelantul aude liniște totală.
Dacă agentul tău gestionează multe instrumente, OpenAI Agents SDK a schimbat ecuația aici. Actualizarea din 15 aprilie 2026 a făcut ca Model Context Protocol (MCP) să fie cetățean de primă clasă și a transformat predările între sub-agenți într-o primitivă de runtime. Astfel, în loc să înghesui fiecare instrument într-un singur prompt, un agent router poate preda o rezervare unui sub-agent de rezervări și o întrebare de facturare altuia. Ghidul rapid pentru voce al Agents SDK împachetează aceeași sesiune Realtime într-un RealtimeAgent și îți oferă predări fără a scrie propria buclă de orchestrare.
Pasul 4: Conectează-l la un Număr de Telefon (Twilio)
Pentru a răspunde la apeluri reale, conectezi un furnizor de telefonie la socket. Cu Twilio, direcționezi un apel incoming către un TwiML <Connect><Stream> care deschide un WebSocket către serverul tău, iar tu retransmiți cadrele audio între Twilio și Realtime API. SIP este alternativa — OpenAI Realtime acceptă SIP direct, ceea elimină complet releul tău media dacă nu ai nevoie să atingi audio-ul.
TwiML-ul care pornește fluxul:
<Response>
<Connect>
<Stream url="wss://your-server.com/twilio-stream" />
</Connect>
</Response>Iată capcana care îți poate consuma o zi dacă o ratezi: fluxul media Twilio este de 8kHz μ-law, iar Realtime API dorește 24kHz PCM16. Trebuie să reeșantionezi în ambele direcții, altfel vei obține un audio distorsionat, de tip „voci de veveriță”.
// inbound: 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"),
}));
// outbound: Realtime (24kHz PCM16) -> Twilio (8kHz μ-law)
const ulaw = muLawEncode(downsample(modelPcm16, 24000, 8000));
twilioWs.send(JSON.stringify({
event: "media",
media: { payload: ulaw.toString("base64") },
}));Formatul complet al cadrului se găsește în documentația Twilio Media Streams. Menține reeșantionarea eficientă, deoarece o librărie greoaie aici adaugă latență pe care o vei plăti la fiecare cadru.
Pasul 5: Gestionează Întreruperile (Barge-in)
Un agent de producție permite apelantului să vorbească peste el. Barge-in înseamnă detectarea faptului că apelantul a început să vorbească în timp ce agentul este la mijlocul unei propoziții, apoi întreruperea curată a agentului. Realtime API gestionează acest lucru cu response.cancel: atunci când detectarea turei raportează că a început vorbirea în timpul redării, anulezi răspunsul activ și golești orice audio pe care l-ai bufferat deja către apelant.
if (event.type === "input_audio_buffer.speech_started") {
realtime.send(JSON.stringify({ type: "response.cancel" }));
twilioWs.send(JSON.stringify({ event: "clear" })); // drop queued playback
}Detectarea turei are două moduri, iar alegerea contează. server_vad se declanșează pe praguri brute de silențiu și tinde să întrerupă apelantul la pauze naturale. semantic_vad așteaptă până când modelul consideră că apelantul a terminat efectiv un gând, astfel încât produce mult mai puține întreruperi false în timpul unei pauze de gândire. Pentru apelurile telefonice, semantic VAD este cel care pare uman.
Pasul 6: Optimizează Latența pentru Sub o Secundă
Aici un demo devine un produs, așa că iată cifrele din propria noastră implementare, nu un buget teoretic. Am rulat același agent de restaurant pe 40 de apeluri de test în mai 2026, pe un singur server mic colocat lângă regiunea OpenAI, schimbând doar setările de detectare a turei și de raționament.
| Configurație | p50 dus-întors | p95 | Note |
|---|---|---|---|
| server_vad, reasoning low | ~1,4s | ~2,3s | mai multe întreruperi false la pauze |
| semantic_vad, reasoning low | ~1,1s | ~1,9s | default-ul nostru de producție |
| semantic_vad, reasoning medium | ~1,8s | ~3,1s | precizie mai bună a instrumentelor, mai lent |
Pârghiile care au mutat realmente acul, în ordinea impactului:
- Menține
reasoning.effortla low decât dacă un instrument specific are nevoie neapărat de precizie. Medium ne-a dublat aproape p50. - Nu împinge audio mai rapid decât timpul real. Supraîncărcarea
input_audio_buffer.appenddepășește buffer-ul și provoacă drift; sincronizează cadrele cu timpul real (wall-clock). - Menține socket-ul cald. Deschiderea la rece a unei conexiuni per apel adaugă handshake-ul la latența primului cuvânt. Pune în pool conexiunile acolo unde volumul de apeluri permite.
- Reeșantionează eficient. Un reeșantionator naiv în calea critică a adăugat ~80 ms per tură pentru noi.
Cât costă rularea acestui sistem pe minut odată ce este live? Am calculat separat matematica bring-your-own-key — vezi cât costă un agent vocal BYOK pe minut în loc să o derivăm din nou aici.
Pasul 7: Implementează și Securizează pentru Producție
Distanța dintre „a funcționat pe laptopul meu” și „rezistă la 500 de apeluri pe zi” este reprezentată de câteva eșecuri bine cunoscute. Iată lista de verificare pentru securizare, extrasă din greșelile care strică efectiv agenții Realtime:
| Capcană | Simptom | Remediere |
|---|---|---|
| Rata de eșantionare greșită | audio distorsionat / voci de veveriță | 24kHz PCM16 în ambele sensuri |
Ignorarea function_call_arguments.done | instrumentele nu se declanșează | ascultă și trimite response.create |
| Împingerea audio mai rapid decât timpul real | depășire buffer, drift | sincronizează cadrele cu timpul real |
| Fără logică de reconectare | apelurile cad la o fluctuație a socket-ului | auto-reconectare + reluarea sesiunii |
Fără gestionarea response.done | ture suprapuse | condiționează următoarea tură de response.done |
Încă două aspecte pentru traficul real. La apelurile lungi, rotește sau reseedează sesiunea la fiecare câteva ture astfel încât contextul să nu derive, deoarece un apel de 20 de minute acumulează stare peste care modelul începe să se poticnească. Și loghează fiecare apel de instrument cu argumentele și rezultatul său; când un apelant spune „agentul a rezervat ora greșită”, transcrierea singură nu îți va spune dacă modelul sau codul tău a greșit.
Dacă adopți ruta Agents SDK din Pasul 3, noul său sandbox de containere rulează codul instrumentelor izolat, ceea ce contează odată ce instrumentele tale ating un sistem de fișiere sau shell în loc de doar un API.
Când ar trebui să cumperi o Platformă Gestionată în schimb
Construirea direct pe Realtime API îți oferă cel mai mult control și cel mai mic cost pe minut, dar tu deții logica de reconectare, puntea telefonică, conformitatea și observabilitatea — toate părțile neglamuroase din Pașii 4 până la 7. Dacă ai nevoie de un agent telefonic live săptămâna aceasta și nu vrei să întreții un releu media, o platformă gestionată este decizia mai rapidă.
Am construit același agent pe cele trei mari platforme și le-am comparat onest: Retell, Vapi sau Bland. Dacă încă te decizi pe care parte a liniei te afli, parcurge cadrul complet de decizie build-vs-buy înainte de a angaja timp de inginerie.
Când echipele doresc controlul unei construcții Realtime personalizate fără a aloca personal pentru ea, aceasta este munca pe care o facem: dezvoltare agenți vocali de producție, de la puntea telefonică la optimizarea latenței descrisă mai sus. Suntem dispuși să analizăm cazul tău de utilizare dacă îl cântărești.
Despre autor — Mert Batur Gurbuz este Co-Fondator al Techsy.io, unde echipa livrează agenți AI, sisteme de automatizare și pipeline-uri voice/SDR pentru clienți B2B. El studiază la University of Birmingham și scrie despre stiva de tooling LLM pe care echipa Techsy o folosește efectiv în producție. LinkedIn
Întrebări Frecvente
Care este latența agentului vocal OpenAI Realtime API?
În implementarea noastră pe gpt-realtime-2 cu semantic_vad și efort de raționament low, latența dus-întors măsurată a fost p50 1,1s și p95 1,9s pe 40 de apeluri de test. Conversia vorbire-la-vorbire într-un singur socket evită releul STT/LLM/TTS, ceea ce face posibil răspunsurile sub o secundă.
Am nevoie de WebRTC, WebSocket sau SIP pentru agentul meu vocal?
Folosește WebRTC când un browser sau o aplicație mobilă capturează microfonul direct, WebSocket când serverul tău deține deja un flux audio brut (cazul punții Twilio) și SIP când dorești ca OpenAI să gestioneze partea telefonică fără propriul tău releu media. Majoritatea agenților telefonici folosesc WebSocket sau SIP.
Cum conectez OpenAI Realtime API la Twilio?
Direcționează un apel incoming Twilio către un TwiML <Connect><Stream> care deschide un WebSocket către serverul tău, apoi retransmite audio între Twilio și socket-ul Realtime. Reeșantionează 8kHz μ-law de la Twilio la 24kHz PCM16 al API-ului în ambele direcții, altfel audio-ul va ieși distorsionat.
Cum funcționează apelarea de funcții în Realtime API?
Declari instrumentele în configurația sesiunii. Când modelul dorește unul, emite un eveniment function_call_arguments.done. Tu execuți lucrarea, trimiți rezultatul înapoi ca element de conversație function_call_output, apoi trimiți response.create pentru ca agentul să rostească rezultatul. Uitarea acestui ultim pas este motivul pentru care instrumentele „eșuează” adesea în mod silențios.
Cum gestionezi întreruperile (barge-in) în Realtime API?
Când detectarea turei raportează input_audio_buffer.speech_started în timpul redării, trimite response.cancel pentru a opri răspunsul activ și a goli orice audio de ieșire pus în coadă către apelant. Asociază-l cu semantic_vad astfel încât pauzele naturale să nu declanșeze întreruperi false la mijlocul propoziției.
Ce rată de eșantionare audio folosește OpenAI Realtime API?
Realtime API folosește audio 24kHz PCM16 în ambele direcții. Furnizorii de telefonie precum Twilio livrează 8kHz μ-law, deci o punte telefonică trebuie să reeșantioneze în sus la intrare și în jos la ieșire. Ratele de eșantionare nepotrivite sunt cea mai frecventă cauză a audio-ului distorsionat.
Cât costă rularea unui agent vocal pe Realtime API?
Costul este determinat de minutele de input și output audio pe gpt-realtime-2, iar economia bring-your-own-key diferă semnificativ de o platformă gestionată cu tarif pe minut. Am calculat matematica completă în defalcarea prețurilor pentru agenți vocali în loc să o estimăm aici.
Ar trebui să construiesc pe Realtime API sau să folosesc Retell, Vapi sau Bland?
Construiește direct când dorești control maxim și cel mai mic cost pe minut și poți gestiona reconectările, telefonía și conformitatea. Cumpără o platformă gestionată când viteza de lansare contează mai mult. Comparația noastră Retell vs Vapi vs Bland și cadrul build-vs-buy acoperă compromisurile.
Ce a schimbat actualizarea OpenAI Agents SDK din aprilie 2026 pentru agenții vocali?
Revizuirea din 15 aprilie 2026 a făcut ca Model Context Protocol să fie cetățean de primă clasă, a adăugat un sandbox de containere pentru codul instrumentelor și a transformat predările între sub-agenți într-o primitivă de runtime. Pentru agenții vocali, aceasta înseamnă că un agent router poate preda sarcini către sub-agenți specialiști în loc să înghesuie fiecare instrument într-un singur prompt.