Corso
Il nostro articolo introduttivo su GPT-Realtime-2 ha coperto il lancio, le affermazioni sui benchmark e perché OpenAI ha suddiviso la voce in tempo reale in una piccola famiglia di modelli. Questo tutorial riparte da lì: collegarsi all'API, inviare audio e vedere cosa cambia nel codice.
La suddivisione conta nella pratica. Il Test 1 usa gpt-realtime-whisper per la trascrizione, il Test 2 usa gpt-realtime-translate per la traduzione in diretta e il Test 3 usa gpt-realtime-2 per un assistente vocale. Il modello principale funziona anche per attività più semplici come traduzione o trascrizione, ma pagheresti per un livello di ragionamento e modalità di risposta non necessari: sarebbe eccessivo.
Configurare l'API GPT-Realtime-2 in Python
Prima dei test, è utile separare trasporto, autenticazione e formato audio. Questi dettagli restano quasi sempre uguali negli esempi. Quello che cambia, man mano che si passa dall'output testuale al parlato tradotto e poi a un ciclo vocale completo, sono gli endpoint del modello e i nomi degli eventi.
Scegliere tra WebRTC e WebSocket
La documentazione OpenAI dà una regola semplice: usa WebRTC per client browser e mobile, e usa WebSocket per applicazioni lato server. WebRTC gestisce jitter buffering e trasporto audio. I WebSocket hanno senso quando il backend riceve già audio grezzo da un fornitore di telefonia o da una pipeline multimediale.

Due percorsi di trasporto per la Realtime API. Immagine dell'autore.
Per questo motivo, tutti e tre i test in Python usano i WebSocket. Questo percorso mostra direttamente i nomi degli eventi, così le differenze tra modelli sono visibili nel codice. Per una build browser, usa secret effimeri lato client in modo che la tua chiave API non arrivi mai al frontend.
Ambiente e pacchetti Python
Usa Python 3.9 o versioni successive. Il codice completo per tutti e quattro gli script è disponibile su github.com/KhalidAbdelaty/gpt-realtime-api. Clonalo per primo, poi installa le dipendenze:
git clone https://github.com/KhalidAbdelaty/gpt-realtime-api.git
cd gpt-realtime-api
pip install websocket-client sounddevice numpy python-dotenv
websocket-client gestisce il socket. sounddevice cattura l'audio del microfono, numpy converte il buffer e python-dotenv carica la chiave API. Su macOS, potresti dover eseguire brew install portaudio prima che sounddevice funzioni. Su Linux, installa portaudio19-dev.
Crea un file .env alla radice del progetto:
OPENAI_API_KEY=sk-...
Poi caricalo in ogni script:
import os
from dotenv import load_dotenv
load_dotenv()
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
Autenticazione e URL del WebSocket
Le connessioni lato server usano un'intestazione Authorization: Bearer durante l'handshake del WebSocket. Aggiungi OpenAI-Safety-Identifier se l'app traccia utenti individuali. Questi sono i percorsi usati più avanti nei test:
# Voice agent
wss://api.openai.com/v1/realtime?model=gpt-realtime-2
# Translation
wss://api.openai.com/v1/realtime/translations?model=gpt-realtime-translate
# Transcription
wss://api.openai.com/v1/realtime?intent=transcription
Quel percorso di traduzione è importante più avanti nel Test 2, perché è l'unico endpoint che non usa direttamente /v1/realtime.
Test 1: Trascrizione audio in tempo reale con GPT-Realtime-Whisper
La trascrizione è un'area in cui è facile complicare troppo. Se l'output è solo testo, il modello di trascrizione è sufficiente.
Cosa stiamo testando
gpt-realtime-whisper riceve audio ed emette delta di trascrizione. Non ragiona, non chiama tool e non risponde a voce. Questo compito più ristretto è anche il motivo per cui viene fatturato in base alla durata dell'audio, invece che con lo stesso modello a token usato da gpt-realtime-2. La sezione sui costi torna su queste tariffe.
Guida al codice
Il campo chiave è session.type: "transcription". Dice all'API di saltare le risposte dell'assistente ed emettere solo eventi di trascrizione. Lo script completo gestisce anche la cattura dal microfono e il threading. Questa è la parte che modifica il comportamento della sessione Realtime:
session_config = {
"type": "session.update",
"session": {
"type": "transcription",
"audio": {
"input": {
"format": {"type": "audio/pcm", "rate": 24000},
"transcription": {
"model": "gpt-realtime-whisper",
"language": "en"
},
"turn_detection": None
}
}
}
}
ws.send(json.dumps(session_config))
ws.send(json.dumps({
"type": "input_audio_buffer.append",
"audio": audio_b64
}))
ws.send(json.dumps({"type": "input_audio_buffer.commit"}))
Usa audio PCM16 mono a 24 kHz, codificato in base64. A differenza della sessione dell'agente vocale, lo script esegue il commit del buffer di input manualmente a intervalli, invece di usare il rilevamento dei turni server_vad. I commit vuoti generano input_audio_buffer_commit_empty, motivo per cui lo script completo esegue il commit solo dopo aver inviato audio reale.

I delta di trascrizione arrivano parola per parola in tempo reale. Immagine dell'autore.
Comportamento osservato
Nei test locali, i risultati di trascrizione sono apparsi entro la finestra di commit, circa 3-4 secondi dopo l'inizio del parlato. Poiché questa configurazione si basa su commit manuali invece che sul VAD, la latenza è legata all'intervallo di commit.
Fai anche attenzione all'ordinamento: gli eventi di completamento da turni sovrapposti possono arrivare fuori ordine, quindi riconcilia tramite item_id se costruisci un'interfaccia intorno allo stream.

La trascrizione è avviata da un commit manuale periodico, non dal rilevamento del silenzio. Immagine dell'autore.
Verdetto: superato. Per la trascrizione live lato server, gpt-realtime-whisper ha fatto ciò che serviva a questo test. Testerei comunque con microfoni reali, accenti e rumore ambientale prima di fissare un obiettivo di latenza.
Test 2: Traduzione in tempo reale con GPT-Realtime-Translate
La traduzione del parlato dal vivo inizialmente somiglia alla trascrizione, ma il ciclo di vita della sessione è diverso. L'endpoint di traduzione rimuove il loop di risposta dell'assistente, rendendo l'esempio più breve.
Cosa stiamo testando
Le sessioni di traduzione non hanno loop di turni dell'assistente e niente response.create. Il modello funziona come un interprete in diretta, non come un agente conversazionale. Per Q&A, strumenti o stato della conversazione, nell'articolo si passa a gpt-realtime-2 nel Test 3.

Le sessioni di traduzione usano un endpoint dedicato e separato. Immagine dell'autore.
Il modello supporta più di 70 lingue in input e 13 in output. Imposti la lingua di destinazione con session.audio.output.language; il rilevamento della lingua sorgente è automatico. I limiti sono chiari: niente prompt personalizzati, nessuna selezione della voce e nessun glossario di dominio.
Guida al codice
Come detto prima, la traduzione usa l'URL WebSocket /translations. Cambiano anche altri due dettagli: il campo di destinazione session.audio.output.language e il nome dell'evento session.input_audio_buffer.append. Nota il prefisso session.. Le sessioni di traduzione lo usano qui.
url = "wss://api.openai.com/v1/realtime/translations?model=gpt-realtime-translate"
session_config = {
"type": "session.update",
"session": {
"audio": {
"output": {"language": "es"},
"input": {
"transcription": {"model": "gpt-realtime-whisper"},
"noise_reduction": {"type": "near_field"}
}
}
}
}
ws.send(json.dumps(session_config))
ws.send(json.dumps({
"type": "session.input_audio_buffer.append",
"audio": audio_b64
}))
L'audio tradotto arriva su session.output_audio.delta e i byte audio sono in event["delta"], non in event["audio"]. Le trascrizioni sorgente e tradotta arrivano separatamente:
if event_type == "session.output_audio.delta":
audio_out_queue.put(base64.b64decode(event["delta"]))
elif event_type == "session.input_transcript.delta":
print("[EN]", event.get("delta", ""), end="")
elif event_type == "session.output_transcript.delta":
print("[ES]", event.get("delta", ""), end="")
Un caso limite: se l'audio sorgente è già nella lingua di destinazione, il modello può produrre silenzio anziché passarlo così com'è.
Comportamento osservato
Per frasi brevi dall'inglese allo spagnolo, l'audio tradotto iniziava prima che l'utterance sorgente finisse. Il terminale mostrava righe [EN] e [ES] intercalate mentre i delta arrivavano in streaming. Coppie linguistiche più distanti possono attendere più a lungo per il contesto. Riuscivo a seguire la voce tradotta senza problemi, ma la selezione personalizzata della voce non è disponibile.
Verdetto: superato, con una riserva. gpt-realtime-translate ha funzionato per la traduzione diretta in tempo reale. È meno utile quando contano il controllo della terminologia o il branding della voce.
Test 3: Creare un assistente vocale con turni e interruzioni
Questo è il test di gpt-realtime-2: un agente vocale che ascolta, parla, mantiene il contesto e può chiamare tool. È anche il punto in cui il codice client inizia a contare di più, perché riproduzione e stato dei turni possono andare fuori sincronia.
Cosa stiamo testando
gpt-realtime-2 è un modello speech-to-speech con capacità di ragionamento. Evita una pipeline separata STT-to-LLM-to-TTS e la sua finestra di contesto da 128K offre più spazio a sessioni lunghe. Il ragionamento è controllato con reasoning.effort; parti da low a meno che l'attività non richieda più ragionamento, perché impostazioni più alte aggiungono latenza.
Guida al codice
La configurazione seguente usa semantic_vad, che osserva indizi del parlato invece del solo silenzio. eagerness controlla la rapidità con cui il modello decide che l'utente ha finito. Le parti da notare sono il nome del modello, le impostazioni dell'output audio, il response.create manuale e il nome dell'evento audio dell'assistente:
session_config = {
"type": "session.update",
"session": {
"type": "realtime",
"model": "gpt-realtime-2",
"output_modalities": ["audio"],
"audio": {
"input": {
"format": {"type": "audio/pcm", "rate": 24000},
"transcription": {"model": "gpt-realtime-whisper", "language": "en"},
"turn_detection": {
"type": "semantic_vad",
"eagerness": "medium",
"create_response": False,
"interrupt_response": True
}
},
"output": {
"format": {"type": "audio/pcm", "rate": 24000},
"voice": "marin"
}
},
"instructions": "You are a helpful voice assistant. Keep answers short.",
"reasoning": {"effort": "low"}
}
}
Quando la trascrizione dell'utente si completa, il client crea la risposta dell'assistente. L'audio dell'assistente arriva quindi come response.output_audio.delta, non come response.audio.delta.
if event_type == "conversation.item.input_audio_transcription.completed":
ws.send(json.dumps({"type": "response.create"}))
elif event_type == "response.output_audio.delta":
audio_out_queue.put(base64.b64decode(event["delta"]))
Gestire le interruzioni (barge-in)
La sequenza di interruzione è facile da sbagliare. Quando l'utente parla sopra l'assistente, il server invia input_audio_buffer.speech_started. Il client interrompe la riproduzione, registra quanta parte di audio è stata riprodotta e invia conversation.item.truncate con audio_end_ms per tenere traccia del punto in cui è stato interrotto. Altrimenti, il server continua a trascrivere testo che l'utente non ha mai sentito e il turno successivo può risultare fuori fase.
if current_response_item_id and playback_position_ms > 0:
ws.send(json.dumps({
"type": "conversation.item.truncate",
"item_id": current_response_item_id,
"content_index": 0,
"audio_end_ms": playback_position_ms
}))
Un problema pratico con gli altoparlanti del laptop: il microfono può captare l'audio dell'assistente e rimandarlo al modello. Lo script di esempio usa MUTE_MIC_DURING_ASSISTANT = True per silenziare lo stream di input mentre l'assistente parla e per un breve periodo di raffreddamento dopo. Impostalo su False solo se usi le cuffie e vuoi il supporto alle interruzioni.

Il troncamento mantiene server e client sincronizzati. Immagine dell'autore.
WebRTC e SIP gestiscono più buffering. Con il percorso WebSocket usato in questo tutorial, è il client a farsene carico. Il contatore nello script di esempio basta per una demo; in produzione il codice dovrebbe tracciare i timestamp dallo stream di output audio.
Configurare i preamboli per la latenza di ragionamento
Quando reasoning.effort è sopra low, il silenzio diventa evidente. Si possono aggiungere brevi preamboli parlati nel prompt di sistema:
# Preambles
Use a short spoken update before longer tasks.
Keep preambles under five seconds.
Skip preambles for short factual questions.
Questo comportamento è documentato per gpt-realtime-2.
Comportamento osservato
La gestione dei turni ha funzionato con le impostazioni predefinite in una stanza silenziosa. Con gli altoparlanti del laptop, ho avuto bisogno di silenziare il microfono; senza, il modello ascoltava il proprio output e iniziava un loop di eco.
In ambienti più rumorosi, le impostazioni del VAD e il posizionamento del microfono hanno avuto un impatto maggiore. La memoria della conversazione è rimasta coerente per un test di dieci minuti, ma non distribuirei un'app più lunga senza un piano di riconnessione.
Verdetto: superato per il loop vocale di base. gpt-realtime-2 ha gestito un assistente a basso sforzo di ragionamento nel mio test. Il lavoro extra è lato client: riproduzione, gestione delle interruzioni, riconnessioni e chiamate a tool se l'app ne ha bisogno.
Demo Streamlit: tre modelli in un'unica interfaccia
L'app Streamlit mette i test dietro un selettore a schede. Ti permette di registrare audio, scegliere una lingua di destinazione e confrontare i percorsi dei modelli senza modificare gli script. L'ho tenuta come app demo invece che come percorso didattico principale, perché gli script da terminale mostrano gli eventi in modo più diretto.

Tre modelli in un'unica interfaccia a schede. Immagine dell'autore.
Il video demo qui sotto mostra le schede con una chiave API live. Ogni scheda usa vere chiamate Realtime WebSocket.
Esegui l'app dalla stessa cartella degli script:
streamlit run demo_app.py
La tua chiave API va nella sidebar e non viene salvata da nessuna parte. Per un'app pubblica, mettila invece in Streamlit Secrets.
Costi e latenza di GPT-Realtime-2 nella pratica
Come menzionato nel Test 1, i prezzi si dividono in due gruppi: gpt-realtime-2 è fatturato a token, mentre traduzione e trascrizione sono fatturate al minuto.

Fatturazione a token e al minuto per modello. Immagine dell'autore.
Per trascrizione e traduzione, il costo scala con la durata. Al momento della scrittura, trenta minuti costano circa $0,51 su gpt-realtime-whisper e circa $1,02 su gpt-realtime-translate.
Gli agenti vocali sono più difficili da stimare perché i token audio si accumulano su entrambi i lati della conversazione. Durata della sessione, rapporto di parlato, sforzo di ragionamento e dimensione del contesto contano tutti. Il caching del prompt può ridurre i costi quando i turni precedenti restano stabili.
Una chiamata REST di trascrizione più TTS è un confronto diverso, a meno che non sia richiesta l'interazione live. whisper-1 è più economico per i file, ma non è lo stesso tipo di API.
Limiti dell'API GPT-Realtime-2, requisiti audio e troubleshooting
Questi sono i limiti che hanno influito sui miei primi test. La maggior parte dei fallimenti derivava da errori nel formato audio o nel ciclo di vita della sessione, non dal modello in sé.
Vincoli di trasporto e audio
Come notato nel primo test, l'audio via WebSocket deve essere PCM16 a 24 kHz, mono e codificato in base64. Ogni evento input_audio_buffer.append è limitato a 15 MB, quindi chunk da 50 millisecondi restano ben al di sotto del limite. È supportato anche G.711 per la telefonia.
Le sessioni Realtime terminano dopo 60 minuti su OpenAI e 30 minuti su Azure OpenAI. Le app più lunghe necessitano di un piano di riconnessione e di un modo per ricostruire lo stato. La voce deve anche essere scelta prima del primo output audio; non può essere cambiata a sessione in corso.
Rate limit e quote
I rate limit sono basati su tier e specifici per progetto. Il Tier 1 attualmente elenca 200 richieste al minuto e 40.000 token al minuto per gpt-realtime-2. Il piano Free non è supportato.
Errori comuni e spigolosità
Gli errori che ho incontrato più spesso sono stati commit di buffer vuoti e formattazione audio errata. Per gli agenti vocali, attenzione anche ai loop di feedback in cui il microfono sente l'output degli altoparlanti dell'assistente. Usa cuffie, cancellazione dell'eco o silenziamento del microfono.
Per sessioni lunghe, riconnettiti intorno ai 55 minuti invece di aspettare la scadenza. Una piccola incongruenza nella documentazione: la pagina del modello gpt-realtime-2 ha una riga generica "Streaming: non supportato", mentre le guide Realtime documentano l'uso di /v1/realtime. Quella riga riguarda lo streaming delle Chat Completions, non il comportamento della Realtime API.
Verdetto finale
Lo stesso schema si ripete nei tre test: a ogni compito corrispondono un proprio modello e un proprio endpoint. Questa suddivisione incide su ciò che il modello può fare, su come viene fatturato e su quanta parte del codice client devi gestire.
Come visto sopra, gpt-realtime-whisper copre il testo live, gpt-realtime-translate copre la traduzione diretta del parlato e gpt-realtime-2 copre il comportamento da assistente con voce, ragionamento e contesto.
Il codice non mostra un modello che sostituisce gli altri. Mostra che le app vocali in tempo reale dipendono dal design della sessione. Il mio punto di partenza sarebbe il modello più piccolo che corrisponde al compito, con il tempo di sviluppo restante dedicato a qualità dell'audio, gestione dei turni, riconnessioni e stato del client.
Per approfondire, i nostri tutorial trattano argomenti correlati su audio e API realtime:
Sono un data engineer e community builder: lavoro su pipeline dati, cloud e strumenti di AI, e scrivo tutorial pratici e ad alto impatto per DataCamp e per sviluppatori alle prime armi.


