La creazione di pipeline audio a bassissima latenza con la nuova OpenAI Realtime API consente agli sviluppatori di lanciare in produzione agenti vocali conversazionali simili a quelli umani. Tradizionalmente, la creazione di un’interfaccia vocale comportava il concatenamento di tre livelli di modelli separati: il riconoscimento vocale automatico (ASR), un livello logico LLM basato su testo e la sintesi vocale (TTS). Questa pipeline a più fasi introduceva significativi ritardi di rete, rendendo impossibile una conversazione naturale. L’elaborazione audio nativa tramite una connessione WebSocket persistente cambia questa situazione, riducendo la latenza di rete al di sotto dei 300 millisecondi. Questa guida spiega come stabilire gli stati di connessione, trasmettere buffer audio grezzi e ottimizzare le configurazioni di sessione.

[!IMPORTANT] Avviso di sicurezza dell’API: Non esporre mai la chiave API OpenAI direttamente negli script del browser lato client. Proxy sempre la connessione WebSocket attraverso un middleware edge sicuro (come un Cloudflare Worker) che aggiunga gli header di autorizzazione prima di inoltrare i pacchetti a OpenAI.

Punti chiave:

  • Connessione WebSocket: Connettiti direttamente al gateway WebSocket in tempo reale di OpenAI utilizzando proxy edge.
  • Modalità native: Specifica sia text che audio nel payload iniziale di configurazione dell’aggiornamento della sessione.
  • Formato audio: Trasmetti la voce dell’utente come blocchi PCM16 mono codificati in base64 a 24kHz.
  • Interruzione del parlato: Monitora i segnali di inizio del parlato del server per interrompere istantaneamente la riproduzione del client.

L’architettura degli LLM audio nativi

Gli stack vocali tradizionali trattano sia i modelli di riconoscimento che quelli di sintesi vocale come wrapper esterni attorno a un modello di testo centrale. I modelli audio nativi eliminano questo sovraccarico gestendo direttamente il parlato.

Con i modelli in tempo reale di OpenAI, la rete elabora gli input e gli output audio in modo nativo. Il modello riceve forme d’onda audio grezze, analizza il tono, l’inflessione e il contenuto e genera direttamente un output vocale naturale. Di conseguenza, la pipeline elimina gli errori di trascrizione ASR e i colli di bottiglia della sintesi TTS.

La gestione di questa connessione persistente si basa sui WebSocket. La connessione rimane aperta durante tutta la chiamata, consentendo all’agente di interrompere il proprio output se rileva il parlato dell’utente, in modo che l’esperienza corrisponda a una vera telefonata.

Ottieni servizi di integrazione dell'IA

Prerequisiti

Prima di scrivere codice, assicurati che il tuo ambiente sia pronto. Questo build presuppone che tu abbia familiarità con JavaScript asincrono e che disponga di quanto segue:

  • Un account OpenAI con accesso Realtime e una chiave API finanziata e attiva.
  • Node.js 20 o successivo, o un progetto Cloudflare Workers creato con npm create cloudflare@latest.
  • Un client WebSocket — il pacchetto ws per un gateway Node.js, o il WebSocket nativo disponibile all’interno dei Workers.
  • Un frontend del browser in grado di catturare l’audio del microfono tramite la Web Audio API (getUserMedia più un AudioWorklet per il ricampionamento).
  • Conoscenza pratica dell’audio base64 e PCM, poiché ogni frame inviato o ricevuto è un payload PCM16 codificato in base64.

Prevedete un po’ di tempo anche per la configurazione dell’account: l’accesso in tempo reale e la fatturazione devono essere abilitati sulla vostra organizzazione prima che il gateway accetti una sessione.


Stabilire connessioni WebSocket

Per iniziare, apri una connessione al gateway OpenAI Realtime, specificando il modello realtime nei tuoi header.

Il codice JavaScript seguente dimostra come inizializzare la connessione, configurare le modalità di sessione e gestire i buffer audio di input e output in 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}

Quando crei questo gestore, assicurati che la tua chiave API rimanga nascosta al browser del client. In particolare, stabilisci un middleware edge sul tuo server per gestire l’autorizzazione prima di eseguire il proxy della connessione WebSocket. Per informazioni sulle configurazioni delle API edge, leggi la nostra guida su come creare un’API serverless con Cloudflare Workers .


Creazione del Proxy Edge sicuro

Lo snippet sopra si connette da un server affidabile, ma non ha mai mostrato la parte che protegge la tua chiave: il proxy stesso. Su Cloudflare Workers non è possibile allegare header personalizzati al costruttore new WebSocket(), quindi si apre la connessione upstream con fetch e un header Upgrade. Il Worker accetta il socket del browser, compone OpenAI con la tua chiave segreta allegata, quindi convoglia i frame tra i due.

 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};

Memorizza la chiave come segreto crittografato con wrangler secret put OPENAI_API_KEY invece che in wrangler.toml, in modo che non finisca mai nel tuo repository. Il browser ora si connette a wss://your-worker.workers.dev e non vede mai le credenziali. Cloudflare documenta questo modello bidirezionale nel suo riferimento Workers WebSockets .


Gestione dello streaming audio dell’utente

Una volta accettato l’aggiornamento della sessione, il client deve catturare l’input del microfono, comprimerlo in dati PCM16 mono a 24kHz e trasmetterlo come segmenti base64. Poiché la connessione rimane persistente, la gestione delle interruzioni di rete è fondamentale. Un buffer locale di blocchi garantisce che brevi interruzioni della connessione cellulare non provochino la perdita di pacchetti audio o risposte instabili dell’agente: il client conserva il buffer e lo riproduce immediatamente al momento della riconnessione.

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}

Ogni volta che l’utente smette di parlare, il server elabora automaticamente il buffer audio accumulato e attiva una risposta del modello. Per un elenco completo degli eventi API, consulta la Guida all’OpenAI Realtime .

Inoltre, implementa la cancellazione dell’eco nel tuo lettore frontend. Se il microfono cattura l’output dell’altoparlante, l’agente interpreterà la propria voce come un’interruzione dell’utente, causando il fallimento del ciclo di sessione. Per saperne di più sull’ottimizzazione del frontend, consulta la nostra guida su WordPress vs sviluppo web personalizzato .


Gestione del Turn Detection e delle interruzioni

Per impostazione predefinita, devi dire al modello quando termina un turno. L’abilitazione della rilevazione dell’attività vocale (VAD) lato server affida questo compito a OpenAI: il gateway monitora il buffer in arrivo, decide quando l’utente ha smesso di parlare e attiva automaticamente una risposta. Aggiungi un blocco turn_detection all’aggiornamento della sessione che invii durante l’handshake.

 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};

Con il VAD attivo, il server emette input_audio_buffer.speech_started nell’istante in cui sente l’utente parlare sopra l’agente. Tratta quell’evento come un arresto forzato: svuota tutti i blocchi audio in coda nel tuo lettore, altrimenti la risposta precedente continuerà a essere riprodotta mentre arriva quella nuova.

 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});

Aumentando silence_duration_ms, l’agente attende più a lungo prima di rispondere, il che si adatta a parlanti lenti o esitanti; diminuendolo, lo scambio risulta più rapido, ma si rischia di interrompere le persone a metà frase.


Installazione dell’agente vocale passo dopo passo

Per distribuire il middleware del tuo agente vocale, inizia configurando un gateway di routing WebSocket sicuro sui nodi edge serverless per proteggere le tue credenziali OpenAI. Ciò impedisce agli scraper di scoprire le tue chiavi.

Quindi, redigi con cura i tuoi prompt e le tue regole di sistema. Imposta il tono, il vocabolario e le istruzioni di risposta all’interno del payload dell’evento di aggiornamento iniziale della sessione. Di conseguenza, questo stabilisce un ambito chiaro per i flussi conversazionali.

Implementa quindi una solida logica di interruzione del parlato. Dovresti ascoltare l’evento input_audio_buffer.speech_started e interrompere immediatamente la riproduzione audio nel tuo lettore frontend. Infine, ottimizza la latenza globale distribuendo i tuoi Workers in stretta vicinanza regionale ai tuoi utenti per ridurre gli hop di routing. Per esplorare le opzioni di hosting edge, leggi il nostro tutorial Cloudflare Workers AI .


Errori comuni e risoluzione dei problemi

La maggior parte dei guasti al primo avvio deriva da una manciata di errori prevedibili. La tabella seguente mappa il sintomo visualizzato con la causa e la soluzione consuete.

SintomoCausa probabileSoluzione
La connessione si chiude immediatamente con 401Intestazione Authorization mancante o non correttaConferma che il proxy alleghi Bearer <key> e che la chiave abbia l’accesso Realtime
L’agente sente solo silenzio o parlato distortoAudio inviato con frequenza di campionamento o profondità di bit errateEsegui il ricampionamento a 24kHz mono PCM16 prima della codifica base64
Il modello non risponde mai dopo che l’utente ha parlatoturn_detection è disabilitato e non viene inviato alcun commit manualeAbilita server_vad, o invia input_audio_buffer.commit quindi response.create
L’agente parla sopra l’utenteIl gestore speech_started non svuota la codaSvuota il buffer di riproduzione e arresta l’altoparlante in corrispondenza di tale evento
Le risposte si interrompono a metà frasemax_response_output_tokens impostato troppo bassoAumenta il limite o lascialo su inf

Due problemi più sottili meritano attenzione. Un evento rate_limits.updated che arriva con un saldo rimanente basso è il primo avviso che le sessioni simultanee stanno per essere limitate; registralo e rallenta le riconnessioni piuttosto che insistere. E se l’agente risponde occasionalmente alla propria ultima frase, significa che il microfono sta catturando l’output dell’altoparlante, quindi migliora la cancellazione dell’eco o sposta i tester sui sistemi cuffia/microfono.


Test e considerazioni sulla produzione

I test locali sono complessi perché è necessario un flusso audio reale in entrambe le direzioni. Il ciclo più rapido consiste nell’eseguire il Worker con wrangler dev, puntare una piccola pagina del browser su di esso e guardare lo stream degli eventi nella console prima di preoccuparsi della qualità audio. Registra ogni tipo di evento in entrata durante lo sviluppo; una volta che la sequenza di session.updated, speech_started, response.audio.delta e response.done appare corretta, saprai che la struttura è solida.

Prima di spedire, valuta alcune realtà di produzione:

  • Costo. L’audio in tempo reale viene tariffato al minuto di token audio di input e output, il che è molto più costoso rispetto ai semplici token di testo. Limita la durata della sessione e valuta la possibilità di un ripiego testuale per risposte informative lunghe.
  • Latenza. Distribuisci il proxy vicino ai tuoi utenti e mantienilo leggero. Ogni hop aggiuntivo tra browser, edge e OpenAI si somma alla latenza che hai lavorato così duramente per ridurre.
  • Riconnessione. I socket cadono sulle reti mobili. Mantieni un breve buffer rotante sul client e riproducilo in caso di riconnessione in modo che un frame perso non faccia deragliare la conversazione.
  • Osservabilità. Emetti log strutturati per l’inizio della sessione, il conteggio delle interruzioni e gli eventi di errore, in modo da poter individuare comportamenti degradati prima che gli utenti si lamentino.
  • Sicurezza e privacy. La voce è un dato personale. Rendi esplicita la conservazione dei dati e aggiungi la moderazione lato server sulle trascrizioni se il tuo agente gestisce flussi di lavoro sensibili.

Un breve test pilota con chiamanti reali farà emergere casi limite di accento, rumore e interruzione che nessun test programmato può coprire, quindi eseguilo prima di qualsiasi lancio su larga scala.


Punti chiave

  • L’OpenAI Realtime API elabora l’audio in modo nativo tramite WebSocket persistenti, mantenendo la latenza al di sotto di 300 ms.
  • Configura sempre le modalità, i formati e i prompt durante l’handshake WebSocket iniziale.
  • Trasmetti l’audio dell’utente come segmenti mono PCM16 base64 in tempo reale.
  • Gestisci gli eventi di interruzione del parlato monitorando l’output dell’evento di inizio voce.
  • Mantieni la sicurezza delle credenziali API eseguendo il proxy dei dettagli di connessione attraverso i router dei workers edge.

Domande frequenti (FAQ)

Cos’è l’OpenAI Realtime API? L’OpenAI Realtime API è un’interfaccia WebSocket che consente agli sviluppatori di trasmettere audio grezzo in entrata e in uscita dal modello, bypassando i moduli ASR/TTS separati. Elaborando l’audio in modo nativo, il modello preserva il tono emotivo, le inflessioni dell’accento e le sfumature del parlato, ottenendo tempi di latenza inferiori a 300 millisecondi.

Come gestisco le interruzioni vocali? Ascolta l’evento server input_audio_buffer.speech_started. Una volta ricevuto, svuota i buffer audio del frontend e interrompi immediatamente la riproduzione dell’altoparlante. Di conseguenza, ciò crea un flusso conversazionale naturale, consentendo all’agente IA di smettere di parlare istantaneamente quando l’utente inizia a parlare.

Quali formati audio supporta l’API? L’API supporta nativamente i formati audio mono PCM16 a 24kHz (dati interi con segno a 16 bit grezzi) e G.711 (u-law e a-law). Gli sviluppatori devono acquisire i dati del microfono, convertirli in questi formati specifici sul lato client e trasmetterli in streaming come stringhe codificate in base64 all’interno di frame WebSocket JSON.

Ho bisogno di un server separato per coordinare il traffico WebSocket? Sì. L’uso di un coordinatore edge serverless (come Cloudflare Workers o un gateway Node.js leggero) è fortemente raccomandato. Il proxy riceve gli input del microfono dai browser dei client, allega header di autorizzazione sicuri e reindirizza lo stream binario al gateway di OpenAI.

Come posso evitare i cicli di eco negli agenti vocali in tempo reale? I cicli di eco si verificano quando l’audio dell’altoparlante rientra nel microfono del client, attivando falsi eventi di interruzione del parlato. Gli sviluppatori devono implementare algoritmi di cancellazione dell’eco sul lato client o utilizzare cuffie durante i test per evitare che i cicli di feedback interrompano le sessioni.