Accelerare i workflow agentici con i WebSockets nell'API Responses
Quando si chiede a Codex di correggere un bug, il processo prevede la scansione del codebase alla ricerca di file pertinenti, la loro lettura per costruire il contesto, l'esecuzione di modifiche e l'avvio di test per verificare la correttezza della correzione. Sotto il cofano, questo si traduce in decine di richieste API Responses avanti e indietro: determinare l'azione successiva del modello, eseguire uno strumento sul computer, inviare l'output dello strumento all'API e ripetere.
Tutte queste richieste possono sommarsi a minuti che gli utenti trascorrono ad aspettare che Codex completi attività complesse. Da una prospettiva di latenza, il ciclo agente di Codex trascorre la maggior parte del suo tempo in tre fasi principali: lavoro nei servizi API (per convalidare ed elaborare le richieste), inferenza del modello e tempo lato client (esecuzione di strumenti e costruzione del contesto del modello). L'inferenza è la fase in cui il modello viene eseguito su GPU per generare nuovi token. In passato, l'esecuzione dell'inferenza LLM su GPU era la parte più lenta del ciclo agentico, quindi l'overhead del servizio API era facile da nascondere. Man mano che l'inferenza diventa più veloce, l'overhead cumulativo dell'API derivante da un rollout agentico diventa molto più evidente.
Accelerare i workflow agentici con i WebSockets
In questo post, spiegheremo come abbiamo reso i cicli agentici che utilizzano l'API il 40% più veloci end-to-end, permettendo agli utenti di sperimentare il salto di velocità di inferenza da 65 a quasi 1.000 token al secondo. Abbiamo affrontato questo problema attraverso la memorizzazione in cache, l'eliminazione di salti di rete non necessari, il miglioramento del nostro stack di sicurezza per segnalare rapidamente i problemi e, soprattutto, la creazione di un modo per stabilire una connessione persistente all'API Responses, invece di dover effettuare una serie di chiamate API sincrone.
Nell'API Responses, i precedenti modelli di punta come GPT-5 e GPT-5.2 operavano a circa 65 token al secondo (TPS). Per il lancio di GPT-5.3-Codex-Spark, un modello di codifica veloce, il nostro obiettivo era un ordine di grandezza più veloce: oltre 1.000 TPS, reso possibile da hardware Cerebras specializzato e ottimizzato per l'inferenza LLM. Per assicurarci che gli utenti potessero sperimentare la vera velocità di questo nuovo modello, abbiamo dovuto ridurre l'overhead dell'API.
Ottimizzazioni iniziali per la latenza
Intorno a novembre 2025, abbiamo lanciato uno sprint di performance sull'API Responses, ottenendo molte ottimizzazioni per la latenza del percorso critico per una singola richiesta:
- Memorizzazione in cache di token renderizzati e configurazione del modello in memoria per saltare costose tokenizzazioni e chiamate di rete per risposte multi-turno.
- Riduzione della latenza dei salti di rete eliminando le chiamate a servizi intermedi (ad esempio, la risoluzione dell'elaborazione delle immagini) e chiamando direttamente il servizio di inferenza stesso.
- Miglioramento del nostro stack di sicurezza in modo da poter eseguire determinati classificatori per segnalare le conversazioni più velocemente.
Con questi miglioramenti, abbiamo riscontrato un miglioramento di quasi il 45% nel tempo al primo token (TTFT) – che riflette quanto l'API risulti reattiva – ma questi miglioramenti non erano ancora abbastanza veloci per GPT-5.3-Codex-Spark. Anche con questi miglioramenti, l'overhead dell'API Responses era troppo grande rispetto alla velocità del modello; ovvero, gli utenti dovevano aspettare che le CPU che eseguivano la nostra API fossero pronte prima di poter utilizzare le GPU che servivano il modello.
Il problema più profondo era strutturale: trattavamo ogni richiesta Codex come indipendente, elaborando lo stato della conversazione e altri contesti riutilizzabili in ogni richiesta successiva. Anche quando la maggior parte della conversazione non era cambiata, pagavamo comunque per il lavoro legato alla cronologia completa. Man mano che le conversazioni si allungavano, questa elaborazione ripetuta diventava più costosa.
Riprogettare il protocollo di trasporto: l'introduzione dei WebSockets
Per ottimizzare il design, abbiamo ripensato il protocollo di trasporto: potremmo mantenere una connessione persistente e uno stato in cache, anziché stabilire una nuova connessione tramite HTTP e inviare la cronologia completa della conversazione per ogni richiesta successiva? L'idea era di inviare solo le nuove informazioni che richiedevano convalida ed elaborazione e di memorizzare in cache lo stato riutilizzabile in memoria per tutta la durata della connessione. Ciò ridurrebbe l'overhead dovuto a lavori ridondanti.
Abbiamo considerato alcuni approcci diversi, inclusi WebSockets e streaming bidirezionale gRPC. Abbiamo scelto i WebSockets perché, come semplice protocollo di trasporto di messaggi, gli utenti non avrebbero dovuto modificare le forme di input e output della loro API Responses. Era facile da usare per gli sviluppatori e si adattava alla nostra architettura esistente con poca interruzione.
Il prototipo WebSocket e la sua efficacia
Il primo prototipo WebSocket ha cambiato ciò che pensavamo fosse possibile per la latenza dell'API Responses. Un ingegnere del team Codex con profonda esperienza nello stack API ha realizzato un prototipo eseguendo un agente Codex durante la notte.
In quel prototipo, i rollout agentici erano modellati come una singola Response a lunga esecuzione. Utilizzando le funzionalità di asyncio, l'API Responses si sarebbe bloccata in modo asincrono nel ciclo di campionamento dopo che una chiamata a uno strumento fosse stata campionata, e l'API Responses avrebbe inviato un evento response.done al client. Dopo aver eseguito la chiamata allo strumento, i client avrebbero inviato un evento response.append con il risultato dello strumento, che sbloccava il ciclo di campionamento e permetteva al modello di continuare.
Un'analogia qui è trattare la chiamata a uno strumento locale come una chiamata a uno strumento ospitato. Quando il modello chiama la ricerca web, il ciclo di inferenza si blocca, chiama un servizio di ricerca web e inserisce la risposta del servizio nel contesto del modello. Nel nostro design, abbiamo fatto la stessa cosa; ma invece di chiamare un servizio remoto, abbiamo inviato la chiamata allo strumento del modello al client tramite il WebSocket. Quando il client rispondeva, mettevamo la risposta della chiamata allo strumento del client nel contesto e continuavamo a campionare.
Questo design è stato estremamente efficace perché ha eliminato il lavoro API ripetuto durante un rollout agente. Potremmo eseguire il lavoro di pre-inferenza una volta, mettere in pausa per l'esecuzione dello strumento e eseguire il lavoro di post-inferenza una volta alla fine.
Rifinitura per l'implementazione pubblica
Sfortunatamente, questo è avvenuto a costo di una forma API meno familiare e più complicata. Volevamo che gli sviluppatori potessero implementare il supporto WebSocket senza dover riscrivere la loro integrazione API attorno a una nuova modalità di interazione.
Per la versione che abbiamo lanciato, siamo tornati a una forma familiare: continuare a usare response.create con lo stesso corpo e usare previous_response_id per continuare il contesto della conversazione dallo stato della risposta precedente.
Su una connessione WebSocket, il server mantiene una cache in memoria, con ambito di connessione, dello stato della risposta precedente. Quando una successiva response.create include previous_response_id, recuperiamo quello stato dalla cache invece di ricostruire l'intera conversazione da zero.
Quello stato in cache include:
- L'oggetto
responseprecedente. - Elementi di input e output precedenti.
- Definizioni e namespace degli strumenti.
- Artefatti di campionamento riutilizzabili, come i token precedentemente renderizzati.
Riutilizzando lo stato della risposta precedente in memoria, siamo stati in grado di realizzare diverse importanti ottimizzazioni:
- Fare in modo che alcuni dei nostri classificatori di sicurezza e validatori di richieste elaborino solo il nuovo input, non l'intera cronologia ogni volta.
- Mantenere una cache in memoria di token renderizzati a cui aggiungiamo, in modo da poter saltare la tokenizzazione non necessaria.
- Riutilizzare la nostra logica di risoluzione/instradamento del modello di successo tra le richieste.
- Sovrapporre il lavoro di post-inferenza non bloccante come la fatturazione con le richieste successive.
L'obiettivo era avvicinarsi il più possibile al prototipo a minimo overhead ma con una forma API che gli sviluppatori già comprendevano e attorno alla quale avevano costruito.
Risultati del lancio e impatto
Dopo uno sprint di due mesi per la costruzione della modalità WebSocket, abbiamo lanciato una versione alpha con le principali startup di agenti di codifica in modo che potessero integrarla nella loro infrastruttura e aumentare in sicurezza il traffico. Gli utenti alpha l'hanno apprezzata, segnalando fino al 40% di miglioramenti nei loro workflow agentici. Dato il feedback positivo dell'alpha, eravamo pronti per il lancio.
I risultati del lancio sono stati immediati. Codex ha rapidamente aumentato la maggior parte del proprio traffico API Responses sulla modalità WebSocket, riscontrando miglioramenti significativi della latenza. Per GPT-5.3-Codex-Spark, abbiamo raggiunto il nostro obiettivo di 1.000 TPS e abbiamo visto picchi fino a 4.000 TPS, dimostrando che l'API Responses poteva tenere il passo con un'inferenza molto più veloce nel traffico di produzione reale. L'impatto si è manifestato rapidamente anche nella comunità degli sviluppatori:
- Codex ha rapidamente spostato la maggior parte del proprio traffico su WebSockets.
- Gli utenti Codex che eseguono i modelli più recenti come GPT-5.3-Codex, GPT-5.4.