Construirea unor pipeline-uri audio cu latență extrem de redusă folosind noul OpenAI Realtime API le permite dezvoltatorilor să lanseze în producție agenți vocali conversaționali cu comportament uman. În mod tradițional, construirea unei interfețe vocale presupunea înlănțuirea a trei straturi de modele separate: recunoașterea automată a vorbirii (ASR), un strat logic LLM bazat pe text și sinteza text-to-speech (TTS). Acel flux de lucru în mai mulți pași introducea întârzieri semnificative de rețea, făcând conversația naturală imposibilă. Procesarea audio nativă printr-o conexiune WebSocket persistentă schimbă acest lucru, reducând latența rețelei sub 300 de milisecunde. Acest ghid explică cum să stabiliți stările conexiunii, să transmiteți buffere audio brute și să optimizați configurațiile sesiunii.

[!IMPORTANT] Avertisment de securitate API: Nu expuneți niciodată cheia API OpenAI direct în scripturile de browser de pe partea de client. Utilizați întotdeauna un proxy pentru conexiunea WebSocket printr-un middleware securizat pe edge (cum ar fi un Cloudflare Worker) care adaugă antetele de autorizare înainte de a redirecționa pachetele către OpenAI.

Aspecte cheie:

  • Conexiune WebSocket: Conectați-vă direct la gateway-ul realtime WebSocket al OpenAI folosind proxy-uri edge.
  • Modalități native: Specificați atât text, cât și audio în payload-ul inițial de configurare pentru actualizarea sesiunii.
  • Format audio: Transmiteți vorbirea utilizatorului sub formă de porțiuni PCM16 mono codificate base64 la 24kHz.
  • Întreruperea vorbirii: Monitorizați semnalele de începere a vorbirii de pe server pentru a opri instantaneu redarea pe client.

Arhitectura LLM-urilor audio native

Stivele vocale tradiționale tratează atât modelele de recunoaștere a vorbirii, cât și cele de sinteza vorbirii ca pe niște wrapper-e externe în jurul unui model central de text. Modelele audio native elimină acel overhead prin procesarea directă a vorbirii.

Cu modelele realtime OpenAI, rețeaua procesează intrările și ieșirile audio în mod nativ. Modelul primește forme de undă brute, analizează tonul, inflexiunea și conținutul și generează direct o ieșire vocală naturală. Ca rezultat, fluxul elimină erorile de transcriere ASR și blocajele de sinteză TTS.

Gestionarea acestei conexiuni persistente se bazează pe WebSockets. Conexiunea rămâne deschisă pe tot parcursul apelului, permițându-i agentului să își întrerupă răspunsul dacă detectează vorbirea utilizatorului, astfel încât experiența să corespundă unui apel telefonic real.

Obțineți servicii de integrare IA

Cerințe preliminare

Înainte de a scrie cod, asigurați-vă că mediul dumneavoastră este pregătit. Acest ghid presupune că sunteți familiarizat cu JavaScript-ul asincron și aveți următoarele elemente configurate:

  • Un cont OpenAI cu acces Realtime și o cheie API activă, alimentată cu fonduri.
  • Node.js 20 sau o versiune mai nouă, sau un proiect Cloudflare Workers creat cu npm create cloudflare@latest.
  • Un client WebSocket — pachetul ws pentru un gateway Node.js sau obiectul global nativ WebSocket disponibil în Workers.
  • Un frontend de browser care poate capta audio de la microfon prin Web Audio API (getUserMedia plus un AudioWorklet pentru resantionare).
  • Cunoștințe practice despre base64 și audio PCM, deoarece fiecare cadru pe care îl trimiteți sau îl primiți este un payload PCM16 codificat base64.

Alocați puțin timp și pentru configurarea contului: accesul Realtime și facturarea trebuie să fie activate pe organizația dumneavoastră înainte ca gateway-ul să accepte o sesiune.


Stabilirea conexiunilor WebSocket

Pentru început, deschideți o conexiune la gateway-ul OpenAI Realtime, specificând modelul realtime în anteturi.

Codul JavaScript de mai jos demonstrează cum să inițializați conexiunea, să configurați modalitățile sesiunii și să gestionați bufferele de intrare și ieșire audio transmise prin streaming:

 1import WebSocket from "ws";
 2
 3export async function startVoiceAgent(env) {
 4  // Connect to the OpenAI Realtime WebSocket gateway
 5  const url = "wss://api.openai.com/v1/realtime?model=gpt-realtime";
 6  const ws = new WebSocket(url, {
 7    headers: {
 8      "Authorization": `Bearer ${env.OPENAI_API_KEY}`,
 9      "OpenAI-Beta": "realtime=v1"
10    }
11  });
12
13  ws.on("open", () => {
14    console.log("WebSocket connection established with OpenAI Realtime API");
15    
16    // Configure session modalities and voice parameters
17    const sessionConfig = {
18      type: "session.update",
19      session: {
20        modalities: ["text", "audio"],
21        instructions: "You are a helpful customer service assistant for Mecanik.",
22        voice: "alloy",
23        input_audio_format: "pcm16",
24        output_audio_format: "pcm16",
25        temperature: 0.7
26      }
27    };
28    ws.send(JSON.stringify(sessionConfig));
29  });
30
31  ws.on("message", (data) => {
32    const event = JSON.parse(data);
33    
34    // Handle incoming audio content from the server
35    if (event.type === "response.audio.delta") {
36      const audioBuffer = Buffer.from(event.delta, "base64");
37      // Output buffer to client audio player
38      playAudioChunk(audioBuffer);
39    }
40  });
41}

Când construiți acest handler, asigurați-vă că cheia dumneavoastră API rămâne ascunsă de browserul clientului. Mai exact, stabiliți un middleware pe edge pe serverul dumneavoastră pentru a gestiona autorizarea înainte de a direcționa conexiunea WebSocket prin proxy. Pentru a afla mai multe despre configurațiile API-urilor pe edge, citiți ghidul nostru despre construirea unui API serverless cu Cloudflare Workers .


Construirea proxy-ului securizat pe edge

Fragmentul de cod de mai sus se conectează de pe un server de încredere, dar nu a arătat piesa care vă păstrează cheia în siguranță: proxy-ul în sine. Pe Cloudflare Workers nu puteți atașa antete personalizate la constructorul new WebSocket(), așa că deschideți conexiunea upstream cu fetch și un antet Upgrade. Worker-ul acceptă socketul browserului, apelează OpenAI cu cheia secretă atașată, apoi direcționează cadrele între cele două conexiuni.

 1export default {
 2  async fetch(request, env) {
 3    if (request.headers.get("Upgrade") !== "websocket") {
 4      return new Response("Expected a WebSocket upgrade", { status: 426 });
 5    }
 6
 7    // 1. Accept the browser <-> Worker socket
 8    const [client, server] = Object.values(new WebSocketPair());
 9    server.accept();
10
11    // 2. Open the Worker <-> OpenAI socket with the secret key attached
12    const upstreamResponse = await fetch(
13      "https://api.openai.com/v1/realtime?model=gpt-realtime",
14      {
15        headers: {
16          Upgrade: "websocket",
17          Authorization: `Bearer ${env.OPENAI_API_KEY}`,
18          "OpenAI-Beta": "realtime=v1"
19        }
20      }
21    );
22
23    const upstream = upstreamResponse.webSocket;
24    if (!upstream) {
25      return new Response("Upstream refused the upgrade", { status: 502 });
26    }
27    upstream.accept();
28
29    // 3. Pipe frames in both directions
30    server.addEventListener("message", (e) => upstream.send(e.data));
31    upstream.addEventListener("message", (e) => server.send(e.data));
32
33    const close = () => { try { server.close(); upstream.close(); } catch {} };
34    server.addEventListener("close", close);
35    upstream.addEventListener("close", close);
36
37    return new Response(null, { status: 101, webSocket: client });
38  }
39};

Stocați cheia ca secret criptat cu wrangler secret put OPENAI_API_KEY în loc să o plasați în wrangler.toml, astfel încât să nu ajungă niciodată în depozitul dumneavoastră de cod. Browserul se conectează acum la wss://your-worker.workers.dev și nu vede niciodată acreditările. Cloudflare documentează acest model bidirecțional în referința lor pentru Workers WebSockets .


Gestionarea fluxului audio al utilizatorului

Odată ce actualizarea sesiunii este acceptată, clientul dumneavoastră trebuie să capteze intrarea de la microfon, să o comprime în date mono PCM16 la 24kHz și să o transmită ca segmente base64. Deoarece conexiunea rămâne persistentă, gestionarea întreruperilor de rețea este critică. Un buffer local de porțiuni audio garantează că scurte întreruperi ale conexiunii celulare nu duc la pierderi de pachete audio sau la răspunsuri sacadate ale agentului: clientul păstrează bufferul și îl reia imediat după reconectare.

1// Example of streaming user microphone data
2function streamMicrophoneChunk(ws, base64AudioChunk) {
3  const audioEvent = {
4    type: "input_audio_buffer.append",
5    audio: base64AudioChunk
6  };
7  ws.send(JSON.stringify(audioEvent));
8}

Ori de câte ori utilizatorul se oprește din vorbit, serverul procesează automat bufferul audio acumulat și declanșează un răspuns de la model. Pentru o listă completă de evenimente API, consultați OpenAI Realtime Guide .

În plus, implementați anularea ecoului în playerul frontend. Dacă microfonul captează sunetul din difuzor, agentul își va interpreta propria voce ca pe o întrerupere din partea utilizatorului, cauzând eșecul buclei sesiunii. Pentru a afla mai multe despre optimizarea frontend-ului, consultați ghidul nostru despre WordPress vs dezvoltarea web personalizată .


Gestionarea detectării vorbirii și a întreruperilor

În mod implicit, trebuie să îi comunicați modelului când se termină o replică. Activarea detectării activității vocale (VAD) pe partea de server deleagă această sarcină către OpenAI: gateway-ul urmărește bufferul de intrare, decide când utilizatorul s-a oprit din vorbit și declanșează automat un răspuns. Adăugați un bloc turn_detection la actualizarea sesiunii pe care o trimiteți în timpul handshake-ului.

 1const sessionConfig = {
 2  type: "session.update",
 3  session: {
 4    modalities: ["text", "audio"],
 5    voice: "alloy",
 6    input_audio_format: "pcm16",
 7    output_audio_format: "pcm16",
 8    turn_detection: {
 9      type: "server_vad",
10      threshold: 0.5,
11      prefix_padding_ms: 300,
12      silence_duration_ms: 500
13    }
14  }
15};

Cu VAD activ, serverul emite input_audio_buffer.speech_started în momentul în care aude utilizatorul vorbind peste agent. Tratați acel eveniment ca pe o oprire bruscă: goliți toate porțiunile audio aflate în coadă în playerul dumneavoastră, altfel răspunsul anterior va continua să ruleze în timp ce noul răspuns sosește.

 1let playbackQueue = [];
 2
 3ws.on("message", (raw) => {
 4  const event = JSON.parse(raw);
 5  if (event.type === "input_audio_buffer.speech_started") {
 6    // User is interrupting: drop everything still buffered
 7    playbackQueue = [];
 8    stopSpeaker();
 9  }
10});

Mărirea valorii silence_duration_ms determină agentul să aștepte mai mult înainte de a răspunde, ceea ce se potrivește persoanelor care vorbesc mai lent sau ezită; reducerea acesteia face ca schimbul de replici să fie mai dinamic, dar riscă să întrerupă oamenii în mijlocul frazei.


Implementarea pas cu pas a agentului vocal

Pentru a implementa middleware-ul agentului vocal, începeți prin configurarea unui gateway de rutare WebSocket securizat pe nodurile de edge serverless pentru a vă proteja acreditările OpenAI. Acest lucru previne ca roboții de tip scraper să vă descopere cheile.

Apoi, redactați cu atenție prompturile sistemului și regulile de comportament. Setați tonul, vocabularul și instrucțiunile de răspuns în cadrul evenimentului inițial de actualizare a sesiunii. Prin urmare, acest lucru stabilește un cadru clar pentru fluxurile conversaționale.

Implementați apoi o logică robustă de întrerupere a redării. Ar trebui să ascultați evenimentul input_audio_buffer.speech_started și să opriți imediat redarea audio în playerul frontend. În cele din urmă, optimizați latența globală prin implementarea Workers în proximitate regională strânsă cu utilizatorii dumneavoastră pentru a reduce salturile de rutare. Pentru a explora opțiunile de găzduire pe edge, citiți tutorialul nostru Cloudflare Workers AI .


Probleme comune și depanare

Cele mai multe defecțiuni la prima rulare provin dintr-o mână de greșeli previzibile. Tabelul de mai jos mapează simptomul pe care îl veți vedea cu cauza sa probabilă și remedierea corespunzătoare.

SimptomCauză probabilăRemediu
Conexiunea se închide imediat cu 401Antet Authorization lipsă sau malformatConfirmați că proxy-ul atașează Bearer <key> și că acea cheie are acces Realtime
Agentul aude doar liniște sau vorbire distorsionatăAudio trimis la o rată de eșantionare sau adâncime de biți greșităRe-eșantionați la 24kHz mono PCM16 înainte de codificarea base64
Modelul nu răspunde niciodată după ce utilizatorul vorbeșteturn_detection este dezactivat și nu se trimite niciun commit manualActivați server_vad sau trimiteți input_audio_buffer.commit apoi response.create
Agentul vorbește peste utilizatorHandlerul speech_started nu golește coadaGoliți bufferul de redare și opriți difuzorul la acel eveniment
Răspunsurile se întrerup în mijlocul frazeimax_response_output_tokens este setat prea micMăriți limita sau lăsați-o ca inf

Două probleme mai subtile merită atenție. Un eveniment rate_limits.updated care sosește cu o balanță rămasă scăzută este avertismentul dumneavoastră timpuriu că sesiunile concurente urmează să fie limitate; înregistrați-l și reduceți frecvența cererilor în loc să forțați reconectări. Și dacă agentul își răspunde ocazional la propria ultimă propoziție, înseamnă că microfonul captează sunetul din difuzor, așa că îmbunătățiți anularea ecoului sau mutați testerii pe căști.


Considerații privind testarea și producția

Testarea locală este destul de dificilă deoarece aveți nevoie de un flux audio real în ambele direcții. Cea mai rapidă modalitate este să rulați Worker-ul cu wrangler dev, să îndreptați o pagină simplă de browser către acesta și să urmăriți fluxul de evenimente în consolă înainte de a vă face griji cu privire la calitatea audio. Înregistrați fiecare tip de eveniment primit în timpul dezvoltării; odată ce secvența de session.updated, speech_started, response.audio.delta și response.done arată corect, știți că structura de bază este funcțională.

Înainte de a lansa în producție, luați în considerare câteva realități operaționale:

  • Cost. Audio în timp real este facturat pe minut de tokens audio de intrare și ieșire, ceea ce este mult mai costisitor decât tokens simpli de text. Plafonați lungimea sesiunii și luați în considerare un fallback pe text pentru răspunsurile informative lungi.
  • Latență. Implementați proxy-ul aproape de utilizatori și păstrați-l cât mai simplu. Fiecare salt suplimentar între browser, edge și OpenAI mărește timpul total pe care ați încercat atât de mult să îl reduceți.
  • Reconectare. Conexiunile de socket se întrerup frecvent pe rețelele mobile. Păstrați un buffer scurt pe client și reulați-l la reconectare, astfel încât un cadru pierdut să nu deraieze conversația.
  • Observabilitate. Emiteți loguri structurate pentru pornirea sesiunii, numărul de întreruperi și evenimentele de eroare pentru a detecta degradarea performanței înainte ca utilizatorii să raporteze probleme.
  • Siguranță și confidențialitate. Vocea reprezintă date cu caracter personal. Stabiliți o politică clară de păstrare a datelor și adăugați moderare pe partea de server pentru transcrieri dacă agentul dumneavoastră gestionează fluxuri de lucru sensibile.

Un scurt proiect pilot cu apeluri reale va scoate la iveală cazuri speciale legate de accent, zgomot de fundal și întreruperi pe care niciun test automatizat nu le poate acoperi, așa că rulați unul înainte de lansarea generală.


Concluzii principale

  • OpenAI Realtime API procesează audio în mod nativ prin WebSockets persistenți, menținând latența sub 300 ms.
  • Configurați întotdeauna modalitățile, formatele și prompturile în timpul handshake-ului inițial WebSocket.
  • Transmiteți audio de la utilizator sub formă de segmente mono PCM16 base64 în timp real.
  • Gestionați evenimentele de întrerupere a vorbirii prin monitorizarea rezultatelor evenimentului de început de vorbire.
  • Mențineți securitatea acreditărilor API prin proxy-ul conexiunii prin routere edge worker.

Întrebări frecvente (FAQ)

Ce este OpenAI Realtime API? OpenAI Realtime API este o interfață WebSocket care le permite dezvoltatorilor să transmită audio brut în și din model, ocolind modulele separate ASR/TTS. Procesând audio în mod nativ, modelul păstrează tonul emoțional, inflexiunile de accent și nuanțele vorbirii, obținând latențe sub 300 de milisecunde.

Cum gestionez întreruperile vocale? Ascultați evenimentul de server input_audio_buffer.speech_started. Când este primit, goliți bufferele audio frontend și opriți imediat redarea în difuzor. Acest lucru creează un flux conversațional natural, permițând agentului de IA să se oprească din vorbit instantaneu când utilizatorul începe să vorbească.

Ce formate audio sunt acceptate de API? API-ul acceptă în mod nativ formatele audio mono PCM16 la 24kHz (date brute pe întregi cu semn de 16 biți) și G.711 (u-law și a-law). Dezvoltatorii trebuie să capteze datele de la microfon, să le convertească în aceste formate specifice pe partea de client și să le transmită sub formă de șiruri codificate base64 în interiorul cadrelor JSON WebSocket.

Am nevoie de un server separat pentru a coordona traficul WebSocket? Da. Se recomandă rularea unui coordonator pe edge serverless (cum ar fi Cloudflare Workers sau un gateway Node.js simplu). Proxy-ul primește intrările de la microfon de la browserele client, atașează antete de autorizare securizate și redirecționează fluxul binar către gateway-ul OpenAI.

Cum previn buclele de ecou la agenții vocali în timp real? Buclele de ecou apar atunci când sunetul din difuzor este captat din nou de microfonul clientului, declanșând evenimente false de întrerupere a vorbirii. Dezvoltatorii trebuie să implementeze algoritmi de anulare a ecoului pe partea de client sau să folosească căști în timpul testelor pentru a preveni perturbarea sesiunilor.