Vai al contenuto principale
OpenAI

23 gennaio 2026

Ingegneria

Analisi dettagliata del ciclo operativo dell'agente Codex

Di Michael Bolin, Membro del personale tecnico

Caricamento in corso...

Codex CLI(si apre in una nuova finestra) è il nostro agente software locale multipiattaforma, progettato per apportare modifiche software di alta qualità e affidabili, operando in modo sicuro ed efficiente sul tuo computer. Dal primo lancio della CLI ad aprile, abbiamo appreso moltissimo su come sviluppare un agente software di eccellenza. Per approfondire queste idee, questo sarà il primo post di una serie in cui continueremo ad esplorare i vari aspetti del funzionamento di Codex, oltre a tutto ciò che abbiamo appreso dalla nostra esperienza. (Per una visione ancora più dettagliata sulla struttura di Codex CLI, visita il nostro repository open source su https://github.com/openai/codex(si apre in una nuova finestra). Se vuoi saperne di più, i dettagli più specifici delle nostre decisioni di progettazione sono documentati in gran parte nelle issue e nelle pull request di GitHub.)

Per iniziare, ci concentreremo sul loop agente, ossia la logica centrale in Codex CLI responsabile dell'orchestrazione dell'interazione tra l'utente, il modello e gli strumenti che il modello invoca per eseguire un lavoro software significativo. Ci auguriamo che questo post ti offra una buona panoramica del ruolo che il nostro agente (o “infrastruttura“) svolge nell'utilizzo di un LLM.

Prima di entrare nel vivo, una breve nota sulla terminologia: in OpenAI, “Codex” comprende una suite di offerte di agenti software, tra cui Codex CLI, Codex Cloud e l'estensione Codex per VS Code. Questo post si concentra sull'infrastruttura di Codex, che fornisce il ciclo principale dell'agente e la logica di esecuzione alla base di tutte le esperienze Codex, resa disponibile tramite Codex CLI. Per semplicità, qui useremo i termini “Codex“ e “Codex CLI“ in modo intercambiabile.

Il ciclo dell'agente

Al cuore di ogni agente IA c'è il cosiddetto “ciclo dell'agente“. Un'illustrazione semplificata del ciclo dell'agente appare così:

Diagramma intitolato “Loop dell'agente” che illustra come un sistema di IA elabora una richiesta dell'utente, chiama gli strumenti, osserva i risultati, aggiorna il piano e restituisce gli output. Le frecce collegano passaggi come l'input dell'utente, il ragionamento del modello, le azioni degli strumenti e la risposta finale.

Per iniziare, l'agente riceve input dall'utente da includere nell'insieme di istruzioni testuali che prepara per il modello noto come prompt.

Il passo successivo è interrogare il modello inviandogli le nostre istruzioni e chiedendogli di generare una risposta, un processo noto come inferenza. Durante l'inferenza, il prompt testuale viene prima tradotto in una sequenza di token(si apre in una nuova finestra) di input: interi che indicizzano il vocabolario del modello. Questi token vengono quindi utilizzati per campionare il modello, generando una nuova sequenza di token di output.

I token di output vengono convertiti nuovamente in testo, che diventa la risposta del modello. Poiché i token vengono prodotti in modo incrementale, la traduzione può avvenire mentre il modello è in esecuzione, ed è per questo che molte applicazioni basate su LLM mostrano un output in streaming. In pratica, l'inferenza è solitamente incapsulata dietro un'API che opera sul testo, nascondendo i dettagli della tokenizzazione.

Come risultato del passaggio di inferenza, il modello (1) produce una risposta finale all'input originale dell'utente oppure (2) richiede una chiamata a uno strumento che l'agente deve eseguire (ad esempio, “esegui ls e riportane l'output“). Nel caso (2), l'agente esegue la chiamata allo strumento e aggiunge il suo output al prompt originale. Questo output viene usato per generare un nuovo input che serve per interrogare di nuovo il modello; l'agente può quindi considerare queste nuove informazioni e riprovare.

Questo processo si ripete finché il modello non smette di emettere chiamate agli strumenti e produce invece un messaggio per l'utente (detto messaggio dell'assistente nei modelli OpenAI). In molti casi, questo messaggio risponde direttamente alla richiesta originale dell'utente, ma può anche essere una domanda di follow-up per l'utente.

Poiché l'agente può eseguire chiamate agli strumenti che modificano l'ambiente locale, il suo “output“ non è limitato al messaggio dell'assistente. In molti casi, l'output principale di un agente software è il codice che scrive o modifica sul dispositivo. Tuttavia, ogni turno termina sempre con un messaggio dell'assistente, come «Ho aggiunto architecture.md come hai chiesto», che segnala uno stato di terminazione nel ciclo dell'agente. Dal punto di vista dell'agente, il lavoro è completato e il controllo torna all'utente.

Il percorso da input dell'utente a risposta dell'agente mostrato nel diagramma è definito come un turno di una conversazione (un thread in Codex). Anche questo turno di conversazione può includere molte iterazioni tra l'inferenza del modello e le chiamate agli strumenti. Ogni volta che invii un nuovo messaggio in una conversazione esistente, la cronologia della conversazione viene inclusa come parte del prompt per il nuovo turno, che comprende i messaggi e le chiamate agli strumenti dei turni precedenti:

Diagramma intitolato “Ciclo di agente a più turni“ che mostra come un agente IA acquisisce iterativamente l'input dell'utente, genera azioni, consulta strumenti, aggiorna lo stato e restituisce i risultati. Include passaggi etichettati, frecce e output di strumenti esemplificativi che illustrano il ciclo di ragionamento dell'agente.

Questo significa che, man mano che la conversazione si sviluppa, aumenta anche la lunghezza del prompt usato per campionare il modello. Questa lunghezza è importante perché ogni modello ha una finestra di contesto, che rappresenta il numero massimo di token che può utilizzare per una singola chiamata di inferenza. Nota che questa finestra include sia i token di input che quelli di output. Come puoi immaginare, un agente potrebbe decidere di effettuare centinaia di chiamate a strumenti in un singolo turno, esaurendo potenzialmente la finestra di contesto. Per questo motivo, la gestione della finestra di contesto è una delle molte responsabilità dell'agente. Ora, approfondiamo come Codex esegue il ciclo dell'agente.

Inferenza del modello

Codex CLI invia richieste HTTP alla API Risposte(si apre in una nuova finestra) per eseguire l'inferenza del modello. Esamineremo come le informazioni fluiscono attraverso Codex, che utilizza l'API Risposte per gestire il ciclo dell'agente.

L'endpoint dell'API Risposte utilizzato da Codex CLI è configurabile(si apre in una nuova finestra), quindi può essere utilizzato con qualsiasi endpoint che implementi l'API Risposte(si apre in una nuova finestra):

Vediamo come Codex crea il prompt per la prima chiamata di inferenza in una conversazione.

Costruzione del prompt iniziale

Come utente finale, non specifichi il prompt utilizzato per campionare il modello parola per parola quando interroghi l'API Risposte. Invece, specifichi vari tipi di input all'interno della tua query e il server dell'API Risposte decide come strutturare tali informazioni in un prompt che il modello è progettato per elaborare. Puoi considerare il prompt come un “elenco di elementi“; questa sezione spiegherà come la tua query viene trasformata in quell'elenco.

Nel prompt iniziale, ogni elemento della lista è associato a un ruolo. Il ruolo indica il peso che dovrebbe avere il contenuto associato ed è uno dei seguenti valori (in ordine decrescente di priorità): sistema, sviluppatore, utente, assistente.

L'API Risposte(si apre in una nuova finestra) accetta un payload JSON con molti parametri. Ci concentreremo su questi tre punti:

In Codex, il campo istruzioni viene letto dal file model_instructions_file(si apre in una nuova finestra) in ~/.codex/config.toml, se specificato; altrimenti, vengono utilizzate le base_instructions associate a un modello(si apre in una nuova finestra). Le istruzioni specifiche del modello si trovano nel repository Codex e sono incluse nella CLI (ad esempio, gpt-5.2-codex_prompt.md(si apre in una nuova finestra)).

Il campo strumenti è un elenco di definizioni di strumenti che rispettano uno schema definito dall'API Risposte. Per Codex, questo include strumenti forniti da Codex CLI, strumenti forniti dall'API Risposte che dovrebbero essere resi disponibili a Codex, così come strumenti forniti dall'utente, di solito tramite i server MCP:

JavaScript

1
[
2
// Codex's default shell tool for spawning new processes locally.
3
{
4
"type": "function",
5
"name": "shell",
6
"description": "Runs a shell command and returns its output...",
7
"strict": false,
8
"parameters": {
9
"type": "object",
10
"properties": {
11
"command": {"type": "array", "description": "The command to execute", ...},
12
"workdir": {"description": "The working directory...", ...},
13
"timeout_ms": {"description": "The timeout for the command...", ...},
14
...
15
},
16
"required": ["command"],
17
}
18
}
19

20
// Codex's built-in plan tool.
21
{
22
"type": "function",
23
"name": "update_plan",
24
"description": "Updates the task plan...",
25
"strict": false,
26
"parameters": {
27
"type": "object",
28
"properties": {"plan":..., "explanation":...},
29
"required": ["plan"]
30
}
31
},
32

33
// Web search tool provided by the Responses API.
34
{
35
"type": "web_search",
36
"external_web_access": false
37
},
38

39
// MCP server for getting weather as configured in the
40
// user's ~/.codex/config.toml.
41
{
42
"type": "function",
43
"name": "mcp__weather__get-forecast",
44
"description": "Get weather alerts for a US state",
45
"strict": false,
46
"parameters": {
47
"type": "object",
48
"properties": {"latitude": {...}, "longitude": {...}},
49
"required": ["latitude", "longitude"]
50
}
51
}
52
]

Infine, il campo input del payload JSON è un elenco di elementi. Codex inserisce i seguenti elementi(si apre in una nuova finestra) nell'input prima di aggiungere il messaggio dell'utente:

1. Un messaggio con ruolo=sviluppatore che descrive la sandbox che si applica solo allo strumento shell fornito da Codex definito nella sezione strumenti. Quindi, altri strumenti, come quelli forniti dai server MCP, non sono isolati da Codex e sono responsabili dell'applicazione delle rispettive misure di sicurezza.

Il messaggio è creato da un modello in cui i pezzi chiave di contenuto provengono da frammenti di Markdown inclusi in Codex CLI, come workspace_write.md(si apre in una nuova finestra) e on_request.md(si apre in una nuova finestra):

Testo semplice

1
<permissions instructions>
2
- description of the sandbox explaining file permissions and network access
3
- instructions for when to ask the user for permissions to run a shell command
4
- list of folders writable by Codex, if any
5
</permissions instructions>

2. (Facoltativo) Un messaggio con ruolo=sviluppatore il cui contenuto è il valore developer_instructions letto dal file config.toml dell'utente.

3. (Facoltativo) Un messaggio con ruolo=utente i cui contenuti sono le “istruzioni utente”, che non provengono da un singolo file ma sono aggregati da più fonti(si apre in una nuova finestra). In generale, le istruzioni più specifiche appaiono più avanti:

4. Un messaggio con ruolo=utente che descrive l'ambiente locale in cui l'agente sta operando attualmente. Questo indica la directory di lavoro attuale e la shell dell'utente(si apre in una nuova finestra):

Testo semplice

1
<environment_context>
2
<cwd>/Users/mbolin/code/codex5</cwd>
3
<shell>zsh</shell>
4
</environment_context>

Una volta che Codex ha eseguito tutti i calcoli sopra indicati per inizializzare l'input, aggiunge il messaggio dell'utente per iniziare la conversazione.

Gli esempi precedenti si sono concentrati sul contenuto di ciascun messaggio, ma tieni presente che ogni elemento di input è un oggetto JSON con type, role(si apre in una nuova finestra) e content come segue:

JSON

1
{
2
"type": "message",
3
"role": "user",
4
"content": [
5
{
6
"type": "input_text",
7
"text": "Add an architecture diagram to the README.md"
8
}
9
]
10
}

Una volta che Codex ha costruito il payload JSON completo da inviare all'API Risposte, effettua la richiesta HTTP POST con un'intestazione Autorizzazione a seconda di come è configurato l'endpoint dell'API Risposte in ~/.codex/config.toml (con inserimento di intestazioni HTTP e parametri di query aggiuntivi, se specificati).

Quando un server dell'API Risposte di OpenAI riceve la richiesta, utilizza il JSON per derivare il prompt per il modello come segue (per chiarezza, un'implementazione personalizzata dell'API Risposte potrebbe fare una scelta diversa):

Diagramma istantaneo che mostra un singolo passaggio in un ciclo di un agente IA. Una richiesta dell'utente entra nel modello, che genera un pensiero, un'azione con il nome di uno strumento e un input per lo strumento. Il diagramma evidenzia questo passaggio intermedio di ragionamento prima che lo strumento venga chiamato.

Come puoi vedere, l'ordine dei primi tre elementi del prompt è determinato dal server, non dal client. Detto ciò, di questi tre elementi, solo il contenuto del messaggio di sistema è controllato anche dal server, mentre gli strumenti e le istruzioni sono determinati dal client. Questi sono seguiti dall'input del payload JSON per completare il prompt.

Ora che abbiamo il nostro prompt, siamo pronti a eseguire il campionamento del modello.

La prima svolta

Questa richiesta HTTP all'API Risposte avvia il primo “turno“ di una conversazione in Codex. Il server risponde con un flusso di eventi inviati dal server (SSE(si apre in una nuova finestra)). I dati di ogni evento sono un payload JSON con un "type" che inizia con "response", che potrebbe essere come qui di seguito (l'elenco completo degli eventi è disponibile nella nostra documentazione API(si apre in una nuova finestra)):

Testo semplice

1
data: {"type":"response.reasoning_summary_text.delta","delta":"ah ", ...}
2
data: {"type":"response.reasoning_summary_text.delta","delta":"ha!", ...}
3
data: {"type":"response.reasoning_summary_text.done", "item_id":...}
4
data: {"type":"response.output_item.added", "item":{...}}
5
data: {"type":"response.output_text.delta", "delta":"forty-", ...}
6
data: {"type":"response.output_text.delta", "delta":"two!", ...}
7
data: {"type":"response.completed","response":{...}}

Codex consuma il flusso di eventi(si apre in una nuova finestra) e li ripubblica come oggetti evento interni utilizzabili da un client. Eventi come response.output_text.delta vengono utilizzati per supportare lo streaming nell'interfaccia utente, mentre altri eventi come response.output_item.added vengono trasformati in oggetti che vengono aggiunti all'input per le chiamate successive all'API Risposte.

Supponiamo che la prima richiesta all'API Risposte includa due eventi response.output_item.done: uno con type=reasoning e uno con type=function_call. Questi eventi devono essere rappresentati nel campo input del JSON quando interroghiamo nuovamente il modello con la risposta alla chiamata dello strumento: 

JavaScript

1
[
2
/* ... original 5 items from the input array ... */
3
{
4
"type": "reasoning",
5
"summary": [
6
"type": "summary_text",
7
"text": "**Adding an architecture diagram for README.md**\n\nI need to..."
8
],
9
"encrypted_content": "gAAAAABpaDWNMxMeLw..."
10
},
11
{
12
"type": "function_call",
13
"name": "shell",
14
"arguments": "{\"command\":\"cat README.md\",\"workdir\":\"/Users/mbolin/code/codex5\"}",
15
"call_id": "call_8675309..."
16
},
17
{
18
"type": "function_call_output",
19
"call_id": "call_8675309...",
20
"output": "<p align=\"center\"><code>npm i -g @openai/codex</code>..."
21
}
22
]

Il prompt risultante usato per campionare il modello nella query successiva apparirebbe così:

Diagramma etichettato "Istantanea 2" che mostra un agente IA dopo una chiamata a uno strumento. Il modello riceve un'osservazione proveniente da uno strumento e genera un nuovo pensiero e un'azione. Le frecce collegano gli input, le osservazioni e gli output per illustrare come l'agente ripete il suo ciclo di ragionamento.

In particolare, si noti come il vecchio prompt sia un prefisso esatto del nuovo prompt. Questo è intenzionale, poiché rende le richieste successive molto più efficienti dato che ci permette di sfruttare la cache del prompt (di cui parleremo nella prossima sezione sulle prestazioni).

Ripensando al nostro primo diagramma del ciclo dell'agente, vediamo che potrebbero esservi molte iterazioni tra l'inferenza e le chiamate agli strumenti. Il prompt potrebbe continuare a crescere finché non riceviamo finalmente un messaggio dell'assistente, che indica la fine del turno:

Testo semplice

1
data: {"type":"response.output_text.done","text": "I added a diagram to explain...", ...}
2
data: {"type":"response.completed","response":{...}}

In Codex CLI, presentiamo all'utente il messaggio dell'assistente e ci concentriamo sull'area di composizione per indicare all'utente che è il suo “turno“ di continuare la conversazione. Se l'utente risponde, sia il messaggio dell'assistente del turno precedente sia il nuovo messaggio dell'utente devono essere aggiunti all'input nella richiesta di API Risposte per avviare il nuovo turno:

JavaScript

1
[
2
/* ... all items from the last Responses API request ... */
3
{
4
"type": "message",
5
"role": "assistant",
6
"content": [
7
{
8
"type": "output_text",
9
"text": "I added a diagram to explain the client/server architecture."
10
}
11
]
12
},
13
{
14
"type": "message",
15
"role": "user",
16
"content": [
17
{
18
"type": "input_text",
19
"text": "That's not bad, but the diagram is missing the bike shed."
20
}
21
]
22
}
23
]

Ancora una volta, poiché stiamo continuando una conversazione, la lunghezza dell'input che inviamo all'API Risposte continua ad aumentare:

Diagramma etichettato “Snapshot 3“ che mostra la fase finale di un ciclo di un agente IA. Dopo aver ricevuto i risultati degli strumenti, il modello genera un pensiero conclusivo e restituisce una risposta finale all'utente. Le frecce illustrano la transizione dall'output dello strumento alla risposta completata.

Vediamo cosa implica questo prompt in continua crescita per le prestazioni.

Considerazioni sulle prestazioni

Potresti pensare: ”Un attimo, il loop dell'agente non cresce in modo quadratico rispetto alla quantità di JSON inviato all'API Risposte nel corso della conversazione?" E avresti ragione. Sebbene l'API Risposte supporti un parametro facoltativo previous_response_id(si apre in una nuova finestra) per mitigare questo problema, Codex non lo utilizza oggi, principalmente per mantenere le richieste completamente senza stato e per supportare configurazioni Zero Data Retention (ZDR).

Evitare previous_response_id semplifica il processo per il fornitore dell'API Risposte perché garantisce che ogni richiesta sia stateless. Questo inoltre semplifica il supporto ai clienti che hanno optato per l'assenza di conservazione dei dati (ZDR)(si apre in una nuova finestra), poiché l'archiviazione dei dati necessari per supportare previous_response_id sarebbe in contrasto con l'opzione ZDR. Nota che i clienti ZDR non rinunciano alla possibilità di beneficiare dei messaggi di ragionamento proprietari dei turni precedenti, poiché i contenuti encrypted_content associati possono essere decrittati sul server. (OpenAI conserva la chiave di decrittografia di un cliente ZDR, ma non i dati del cliente.) Consulta le PR n. 642(si apre in una nuova finestra) e n. 1641(si apre in una nuova finestra) per conoscere le modifiche correlate a Codex a supporto dell'opzione ZDR.

In generale, il costo del campionamento del modello supera quello del traffico di rete, pertanto il campionamento è il principale obiettivo delle nostre iniziative di efficienza. Il caching del prompt è così importante proprio perché ci permette di riutilizzare i calcoli di una precedente chiamata di inferenza. Quando otteniamo hit della cache, il campionamento del modello diventa lineare anziché quadratico. La nostra documentazione sul caching dei prompt (si apre in una nuova finestra)spiega tutto in modo più dettagliato:

Gli hit della cache sono possibili solo per corrispondenze esatte del prefisso all'interno di un prompt. Per sfruttare i vantaggi della cache, posiziona i contenuti statici, come istruzioni ed esempi, all'inizio del tuo prompt e colloca i contenuti variabili, come le informazioni specifiche dell'utente, alla fine. Questo vale anche per immagini e strumenti, che devono essere identici tra le richieste.

Tenendo presente questo, consideriamo quali tipi di operazioni potrebbero causare un “miss della cache“ in Codex:

  • Variazione degli strumenti disponibili per il modello a metà della conversazione.
  • Variazione del modello che è l'obiettivo della richiesta dell'API Risposte (in pratica, ciò modifica il terzo elemento nel prompt originale, poiché contiene istruzioni specifiche del modello).
  • Variazione della configurazione della sandbox, della modalità di approvazione o della cartella di lavoro attuale.

Il team di Codex deve essere diligente nell'introdurre in Codex CLI nuove funzionalità che potrebbero compromettere la cache dei prompt. A titolo esemplificativo, il nostro supporto iniziale per gli strumenti MCP ha introdotto un bug che ci ha impedito di elencare gli strumenti in un ordine coerente(si apre in una nuova finestra), causando miss della cache. Nota che gli strumenti MCP possono essere particolarmente complessi perché i server MCP possono modificare al volo l'elenco degli strumenti che forniscono tramite una notifica notifications/tools/list_changed(si apre in una nuova finestra). Rispettare questa notifica nel mezzo di una lunga conversazione può causare un costoso miss della cache.

Ove possibile, gestiamo le modifiche di configurazione che avvengono a metà conversazione aggiungendo un nuovo messaggio all'input per riflettere la modifica anziché modificare un messaggio precedente:

Ci adoperiamo in ogni modo per garantire hit della cache e migliorare le prestazioni. L'altra risorsa chiave che dobbiamo gestire è la finestra di contesto.

La nostra strategia generale per evitare di esaurire la finestra di contesto è compattare la conversazione quando il numero di token supera una certa soglia. Nello specifico, sostituiamo l'input con un nuovo elenco più piccolo di elementi rappresentativi della conversazione, permettendo all'agente di continuare, avendo compreso quanto accaduto finora. Una prima implementazione della compattazione(si apre in una nuova finestra) richiedeva all'utente di invocare manualmente il comando /compact, che avrebbe interrogato l'API Risposte utilizzando la conversazione esistente oltre a istruzioni personalizzate per il riepilogo(si apre in una nuova finestra). Codex ha usato il messaggio prodotto dall'assistente e contenente il riepilogo come nuovo input(si apre in una nuova finestra) per i turni di conversazione successivi.

Da allora, l'API Risposte si è evoluta per supportare un endpoint speciale /responses/compact (si apre in una nuova finestra) che esegue la compattazione in modo più efficiente. Restituisce un elenco di elementi(si apre in una nuova finestra) che possono essere utilizzati al posto del precedente input per continuare la conversazione liberando la finestra di contesto. Questo elenco include un elemento speciale type=compaction con un elemento opaco encrypted_content che preserva la comprensione latente del modello della conversazione originale. Ora, Codex utilizza automaticamente questo endpoint per compattare la conversazione al superamento del limite auto_compact_limit(si apre in una nuova finestra).

Prossimamente

Abbiamo introdotto il ciclo dell'agente Codex e spiegato come Codex crea e gestisce il proprio contesto quando interroga un modello. Nel frattempo, abbiamo evidenziato considerazioni pratiche e best practice che si applicano a chiunque stia costruendo un loop di agente basato sull'API Risposte.

Sebbene il ciclo dell'agente fornisca le basi per Codex, è solo l'inizio. Nei prossimi post, esamineremo l'architettura della CLI, esploreremo come viene implementato l'uso degli strumenti e analizzeremo più da vicino il modello di sandboxing di Codex.

Autore

Michael Bolin

Ringraziamenti

Un ringraziamento speciale a tutto il team che ha costruito Codex CLI.