Sari la conținutul principal
OpenAI

23 ianuarie 2026

Inginerie

Desfășurarea buclei agentului Codex

De Michael Bolin, membru al personalului tehnic

Se încarcă…

Codex CLI(se deschide într-o fereastră nouă) este agentul nostru software local pentru toate platformele, conceput pentru a produce modificări de software fiabile și de înaltă calitate, funcționând în siguranță și eficient pe computerul tău. Am învățat foarte multe despre cum să construim un agent software de talie mondială de când am lansat inițial CLI în aprilie. Pentru a detalia aceste perspective, aceasta este prima postare dintr-o serie continuă în care vom explora diverse aspecte ale modului în care funcționează Codex, precum și lecții învățate din greșeli. (Pentru o perspectivă și mai detaliată asupra modului în care este construit Codex CLI, consultă depozitul nostru open-source la https://github.com/openai/codex(se deschide într-o fereastră nouă). Multe dintre detaliile mai fine ale deciziilor noastre de design sunt consemnate în problemele și cererile de extragere de pe GitHub (dacă vrei să afli mai multe).

Pentru început, ne vom concentra asupra buclei agentului, care este logica de bază din Codex CLI, responsabilă de orchestrarea interacțiunii dintre utilizator, model și instrumentele pe care modelul le invocă pentru a efectua activități software semnificative. Sperăm că această postare îți va oferi o imagine clară asupra rolului pe care agentul nostru (sistemul nostru) îl deține în utilizarea unui LLM.

Înainte de a intra în detalii, o scurtă observație despre terminologie: la OpenAI, „Codex” cuprinde o suită de oferte de agenți software, inclusiv Codex CLI, Codex Cloud și extensia Codex VS Code. Această postare se concentrează pe sistemul Codex, care furnizează bucla de bază a agentului și logica de execuție ale tuturor experiențelor Codex și care sunt afișate prin Codex CLI. Pentru ușurință, vom folosi termenii „Codex” și „Codex CLI” interschimbabil.

Bucla agentului

În centrul fiecărui agent de inteligență artificială se află ceva numit „bucla agentului”. O ilustrație simplificată a buclei agentului arată astfel:

Diagramă intitulată „Bucla agentului” care ilustrează modul în care un sistem de inteligență artificială procesează o solicitare a utilizatorului, apelează instrumente, observă rezultatele, își actualizează planul și returnează ieșiri. Săgețile conectează pași precum intrarea utilizatorului, raționamentul modelului, acțiunile instrumentului și răspunsul final.

Pentru început, agentul preia date de intrare de la utilizator pentru a le include în setul de instrucțiuni textuale pe care le pregătește pentru model, cunoscut sub numele de solicitare.

Următorul pas este să interogăm modelul trimițându-i instrucțiunile noastre și solicitându-i să genereze un răspuns, un proces cunoscut sub numele de inferență. În timpul inferenței, solicitarea textuală este mai întâi transformată într-o secvență de tokenuri de intrare(se deschide într-o fereastră nouă) — numere întregi care se indexează în vocabularul modelului. Aceste tokenuri sunt apoi utilizate pentru a eșantiona modelul, producând o nouă secvență de tokenuri de ieșire.

Tokenurile de ieșire sunt traduse la loc în text, devenind răspunsul modelului. Deoarece tokenurile sunt produse incremental, traducerea poate avea loc pe măsură ce modelul rulează, motiv pentru care multe aplicații bazate pe LLM afișează ieșire în flux. În practică, inferența este de obicei ascunsă în spatele unui API care lucrează cu text, eliminând detaliile tokenizării.

Ca rezultat al etapei de inferență, modelul fie (1) produce un răspuns final la intrarea originală a utilizatorului, fie (2) solicită o apelare de instrument pe care agentul trebuie s-o efectueze (de exemplu, „rulează ls și raportează ieșirea”). În cazul (2), agentul execută apelarea de instrument și adaugă ieșirea sa la solicitarea originală. Această ieșire este folosită pentru a genera o nouă intrare care este utilizată pentru a interoga din nou modelul; agentul poate apoi să țină cont de aceste informații noi și să încerce din nou.

Acest proces se repetă până când modelul încetează să mai emită apelări de instrumente și, în schimb, produce un mesaj pentru utilizator (numit mesajul asistentului în modelele OpenAI). În multe cazuri, acest mesaj răspunde direct solicitării inițiale a utilizatorului, dar poate fi și o întrebare ulterioară pentru utilizator.

Deoarece agentul poate executa apelări de instrumente care modifică mediul local, „ieșirea” sa nu se limitează la mesajul asistentului. În multe cazuri, ieșirea principală a unui agent software este codul pe care îl scrie sau îl editează pe computerul tău. Cu toate acestea, fiecare rând se termină întotdeauna cu un mesaj al asistentului — cum ar fi „Am adăugat fișierul architecture.md pe care l-ai solicitat” — care semnalează o stare de terminare în bucla agentului. Din perspectiva agentului, treaba sa s-a încheiat și controlul îi revine utilizatorului.

Traseul de la datele introduse de utilizator la răspunsul agentului, prezentat în diagramă, se numește rândul unei conversații (un fir de conversație în Codex). Deși acest rând de conversație poate include multe iterații între inferența modelului și apelurile de instrumente. De fiecare dată când trimiți un mesaj nou într-o conversație existentă, istoricul conversației este inclus ca parte a solicitării pentru noua rundă, care include mesajele și apelările de instrumente din rândurile anterioare:

Diagrama intitulată „Bucla agentului cu mai multe rânduri” arată modul în care un agent de inteligență artificială preia iterativ intrarea utilizatorului, generează acțiuni, consultă instrumente, actualizează starea și returnează rezultate. Include pași etichetați, săgeți și exemple de ieșiri ale instrumentului care ilustrează ciclul de raționament al agentului.

Asta înseamnă că, pe măsură ce conversația se dezvoltă, crește și lungimea solicitării folosite pentru a eșantiona modelul. Această lungime contează deoarece fiecare model are o fereastră contextuală, care este numărul maxim de tokenuri pe care le poate folosi pentru o apelare de inferență. Reține că această fereastră include atât tokenuri de intrare, cât și tokenuri de ieșire. După cum îți poți imagina, un agent ar putea decide să facă sute de apelări de instrumente într-un singur rând, epuizând potențial fereastra contextuală. Din acest motiv, gestionarea ferestrei contextuale este una dintre numeroasele responsabilități ale agentului. Acum, să vedem cum Codex rulează bucla agentului.

Inferența modelului

Codex CLI trimite cereri HTTP către API-ul Responses(se deschide într-o fereastră nouă) pentru a efectua inferența modelului. Vom examina cum circulă informațiile prin Codex, care folosește API-ul Responses pentru a gestiona bucla agentului.

Punctul final al API-ului Responses utilizat de Codex CLI este configurabil(se deschide într-o fereastră nouă), deci poate fi utilizat cu orice punct final care implementează API-ul Responses(se deschide într-o fereastră nouă):

Hai să explorăm cum creează Codex solicitarea pentru prima apelare de inferență dintr-o conversație.

Crearea solicitării inițiale

Ca utilizator final, nu specifici textual solicitarea folosită pentru a eșantiona modelul atunci când interoghezi API-ul Responses. În schimb, specifici diverse tipuri de intrare ca parte a interogării tale, iar serverul API-ului Responses decide cum să structureze aceste informații într-o solicitare pe care modelul este conceput să o consume. Îți poți imagina solicitarea ca o „listă de elemente”; această secțiune va explica modul în care interogarea ta este transformată în acea listă.

În solicitarea inițială, fiecare element din listă este asociat cu un rol. Rolul indică ce importanță ar trebui să aibă conținutul asociat și este una dintre următoarele valori (în ordine descrescătoare a priorității): sistem, dezvoltator, utilizator, asistent.

API-ul Responses(se deschide într-o fereastră nouă) primește o sarcină utilă JSON cu mulți parametri. Ne vom concentra pe aceste trei aspecte:

În Codex, câmpul instrucțiuni este citit din model_instructions_file(se deschide într-o fereastră nouă) din ~/.codex/config.toml, dacă este specificat; altfel, sunt utilizate base_instructions asociate cu un model(se deschide într-o fereastră nouă). Instrucțiunile specifice modelului se află în depozitul Codex și sunt incluse în CLI (de exemplu, gpt-5.2-codex_prompt.md(se deschide într-o fereastră nouă)).

Câmpul instrumente este o listă de definiții de instrumente care respectă o schemă definită de API-ul Responses. Pentru Codex, include instrumente furnizate de Codex CLI, instrumente furnizate de API-ul Responses care ar trebui să fie disponibile pentru Codex, precum și instrumente furnizate de utilizator, de obicei prin servere 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
]

În cele din urmă, câmpul intrare al sarcinii utile JSON este o listă de elemente. Codex inserează următoarele elemente(se deschide într-o fereastră nouă) în intrare înainte de a adăuga mesajul utilizatorului:

1. Un mesaj cu role=developer care descrie sandboxul care se aplică doar instrumentului shell furnizat de Codex definit în secțiunea tools. Adică, alte instrumente, cum ar fi cele furnizate de serverele MCP, nu sunt izolate de Codex și sunt responsabile pentru aplicarea propriilor măsuri de protecție.

Mesajul este construit dintr-un șablon în care elementele-cheie de conținut provin din fragmente de Markdown incluse în Codex CLI, cum ar fi workspace_write.md(se deschide într-o fereastră nouă) și on_request.md(se deschide într-o fereastră nouă):

Text simplu

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. (Opțional) Un mesaj cu role=developer al cărui conținut este valoarea developer_instructions citită din fișierul config.toml al utilizatorului.

3. (Opțional) Un mesaj cu role=user al cărui conținut sunt „instrucțiunile utilizatorului”, care nu provin dintr-un singur fișier, ci sunt agregate din mai multe surse(se deschide într-o fereastră nouă). În general, instrucțiuni mai detaliate apar ulterior:

4. Un mesaj cu role=user care descrie mediul local în care agentul funcționează în prezent. Aceasta specifică directorul de lucru curent și shell-ul utilizatorului(se deschide într-o fereastră nouă):

Text simplu

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

Odată ce Codex a realizat toate calculele de mai sus pentru a inițializa intrarea, adaugă mesajul utilizatorului pentru a începe conversația.

Exemplele anterioare s-au concentrat pe conținutul fiecărui mesaj, dar reține că fiecare element din intrare este un obiect JSON cu tip, rol(se deschide într-o fereastră nouă) și conținut, după cum urmează:

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
}

După ce Codex construiește întreaga sarcină utilă JSON pentru a o trimite către API-ul Responses, acesta creează cererea HTTP POST cu un antet de Autorizare, în funcție de modul în care este configurat punctul final al API-ului Responses în ~/.codex/config.toml (se adaugă anteturi HTTP și parametri de interogare suplimentari, dacă sunt specificați).

Când un server al API-ului Responses al OpenAI primește cererea, utilizează JSON pentru a deriva solicitarea pentru model după cum urmează (desigur, o implementare personalizată a API-ului Responses ar putea face o alegere diferită):

Diagrama instantanee care arată o singură etapă dintr-o buclă a unui agent de inteligență artificială. O solicitare a utilizatorului intră în model, care generează o idee, o acțiune cu un nume de instrument și o intrare de instrument. Diagrama evidențiază acest pas intermediar de raționament înainte de apelarea instrumentului.

După cum poți vedea, ordinea primelor trei elemente din solicitare este determinată de server, nu de client. Așadar, dintre aceste trei elemente, doar conținutul mesajului de sistem este controlat și de server, deoarece instrumentele și instrucțiunile sunt determinate de client. Acestea sunt urmate de intrarea din sarcina utilă JSON pentru a completa solicitarea.

Acum că avem solicitarea, suntem gata să eșantionăm modelul.

Primul rând

Această cerere HTTP către API-ul Responses inițiază primul „rând” al unei conversații în Codex. Serverul răspunde cu un flux Server-Sent Events (SSE(se deschide într-o fereastră nouă)). Datele fiecărui eveniment sunt o sarcină utilă JSON cu un "type" care începe cu "response", care ar putea fi ceva de genul acesta (o listă completă de evenimente poate fi găsită în documentația API(se deschide într-o fereastră nouă)):

Text simplu

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 consumă fluxul de evenimente(se deschide într-o fereastră nouă) și le republică sub formă de obiecte interne de eveniment care pot fi folosite de un client. Evenimentele precum response.output_text.delta sunt utilizate pentru a sprijini transmiterea în flux în interfața cu utilizatorul, în timp ce alte evenimente precum response.output_item.added sunt transformate în obiecte care sunt adăugate la intrare pentru apelări ulterioare ale API-ului Responses.

Să presupunem că prima solicitare către API-ul Responses include două evenimente response.output_item.done: unul cu type=reasoning și unul cu type=function_call. Aceste evenimente trebuie să fie reprezentate în câmpul intrare al JSON-ului atunci când interogăm din nou modelul cu răspunsul la apelarea de instrument: 

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
]

Solicitarea rezultată folosită pentru a eșantiona modelul ca parte a interogării ulterioare ar arăta astfel:

Diagrama intitulată „Instantaneu 2” care prezintă un agent de inteligență artificială după o apelare de instrument. Modelul primește o observație a unui instrument și generează un nou gând și o nouă acțiune. Săgețile conectează intrările, observațiile și ieșirile pentru a ilustra modul în care agentul își iterează bucla de raționament.

De remarcat cum vechea solicitare este un prefix exact al noii solicitări. Acest lucru este intenționat, deoarece face ca solicitările ulterioare să fie mult mai eficiente, deoarece ne permite să profităm de memorarea în cache a solicitărilor (pe care o vom discuta în secțiunea următoare despre performanță).

Revenind la prima noastră diagramă a buclei agentului, observăm că ar putea exista multe iterații între inferență și apelarea de instrumente. Solicitarea poate continua să crească până când primim în cele din urmă un mesaj al asistentului, indicând sfârșitul rândului:

Text simplu

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

În Codex CLI, îi prezentăm utilizatorului mesajul asistentului și focalizăm compozitorul pentru a-i indica utilizatorului că este „rândul” său să continue conversația. Dacă utilizatorul răspunde, atât mesajul asistentului din runda anterioară, cât și noul mesaj al utilizatorului trebuie să fie adăugate la intrare în cererea API-ului Responses pentru a începe noul rând:

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
]

Din nou, deoarece continuăm o conversație, lungimea intrării pe care o trimitem către API-ul Responses continuă să crească:

Diagramă etichetată „Instantaneu 3” care prezintă etapa finală a buclei unui agent de inteligență artificială. După ce primești rezultatele instrumentului, modelul generează o concluzie și un răspuns final care-i este returnat utilizatorului. Săgețile arată trecerea de la ieșirea instrumentului la răspunsul complet.

Să vedem ce înseamnă această solicitare în continuă creștere pentru performanță.

Considerații privind performanța

S-ar putea să te întrebi: „Stai puțin, bucla agentului nu este cvadratică în ceea ce privește cantitatea de JSON trimisă către API-ul Responses pe parcursul conversației?” Și ai avea dreptate. Deși API-ul Responses acceptă un parametru opțional previous_response_id(se deschide într-o fereastră nouă) pentru a atenua această problemă, Codex nu-l folosește în prezent, în principal pentru a menține solicitările complet fără stare și pentru a susține configurațiile de Zero date păstrate (ZDR).

Evitarea previous_response_id simplifică lucrurile pentru furnizorul API-ului Responses, deoarece asigură că fiecare cerere este fără stare. Acest lucru simplifică și compatibilitatea cu clienții care au optat pentru Zero date păstrate (ZDR)(se deschide într-o fereastră nouă), deoarece stocarea datelor necesare pentru a accepta previous_response_id ar fi incompatibilă cu ZDR. Reține că utilizatorii ZDR nu pierd capacitatea de a beneficia de mesajele de raţionament proprietare din rândurile anterioare, deoarece encrypted_content asociat poate fi decriptat pe server. (OpenAI păstrează cheia de decriptare a unui client ZDR, dar nu și datele sale.) Vezi cererile de extragere #642(se deschide într-o fereastră nouă) și #1641(se deschide într-o fereastră nouă) pentru modificările asociate aduse la Codex pentru a accepta ZDR.

În general, costul eșantionării modelului depășește costul traficului de rețea, ceea ce face ca eșantionarea să fie ținta principală a eforturilor noastre de eficiență. De aceea memorarea în cache a solicitărilor este atât de importantă, deoarece ne permite să reutilizăm calculul dintr-o apelare de inferență anterioară. Când obținem rezultate din memoria cache, eșantionarea modelului este liniară, nu pătratică. Documentația noastră despre memorarea în cache a solicitărilor (se deschide într-o fereastră nouă)explică acest lucru în detaliu:

Rezultatele din memoria cache sunt posibile numai pentru potriviri exacte ale prefixului din cadrul unei solicitări. Pentru a beneficia de avantajele memorării în cache, pune conținutul static, cum ar fi instrucțiunile și exemplele, la începutul solicitării și pune conținutul variabil, cum ar fi informațiile specifice utilizatorului, la final. Acest lucru se aplică și imaginilor și instrumentelor, care trebuie să fie identice între cereri.

Având în vedere acest lucru, să analizăm ce tipuri de operațiuni ar putea cauza o „eroare de cache” în Codex:

  • Schimbarea instrumentelor disponibile pentru model în mijlocul conversației.
  • Schimbarea modelului care este ținta cererii API-ului Responses (în practică, acest lucru schimbă al treilea element din solicitarea originală, deoarece conține instrucțiuni specifice modelului).
  • Schimbarea configurației sandbox, a modului de aprobare sau a directorului de lucru curent.

Echipa Codex trebuie să fie atentă când introduce funcționalități noi în CLI Codex care ar putea compromite memorarea în cache a solicitărilor. De exemplu, compatibilitatea noastră inițială cu instrumentele MCP a introdus un bug din cauza căruia nu am reușit să enumerăm instrumentele într-o ordine consecventă(se deschide într-o fereastră nouă), cauzând erori de cache. Reține că instrumentele MCP pot fi deosebit de complicate, deoarece serverele MCP pot modifica din mers lista de instrumente pe care le oferă printr-o notificare notifications/tools/list_changed(se deschide într-o fereastră nouă). Onorarea acestei notificări în mijlocul unei conversații lungi poate cauza o eroare de cache costisitoare.

Când este posibil, gestionăm modificările de configurație care au loc în timpul conversației prin adăugarea unui mesaj nou la intrare pentru a reflecta modificarea, în loc să modificăm un mesaj anterior:

Depunem eforturi considerabile pentru a asigura accesări ale memoriei cache pentru performanță. Există o altă resursă cheie pe care trebuie să o gestionăm: fereastra contextuală.

Strategia noastră generală pentru a evita epuizarea ferestrei contextuale este de a compacta conversația odată ce numărul de tokenuri depășește un anumit prag. Mai exact, înlocuim intrarea cu o listă nouă, mai mică, de elemente care este reprezentativă pentru conversație, permițându-i agentului să continue înțelegând ceea ce s-a întâmplat până acum. O implementare timpurie a compactării(se deschide într-o fereastră nouă) necesita ca utilizatorul să invoce manual comanda /compact , care interoga API-ul Responses folosind conversația existentă, plus instrucțiuni personalizate pentru rezumare(se deschide într-o fereastră nouă). Codex a folosit mesajul rezultat al asistentului, care conținea rezumatul ca noua intrare(se deschide într-o fereastră nouă) pentru rândurile ulterioare ale conversației.

De atunci, API-ul Responses a evoluat, acceptând un punct final special /responses/compact (se deschide într-o fereastră nouă) care efectuează compactarea mai eficient. Returnează o listă de elemente(se deschide într-o fereastră nouă) care pot fi utilizate în locul intrării anterioare pentru a continua conversația, eliberând totodată fereastra contextuală. Această listă include un element special type=compaction cu un element opac encrypted_content care păstrează înțelegerea latentă a modelului despre conversația originală. Acum, Codex folosește automat acest punct final pentru a compacta conversația când auto_compact_limit(se deschide într-o fereastră nouă) este depășit.

Ce urmează

Am introdus bucla agentului Codex și am explicat cum Codex își construiește și gestionează contextul atunci când interoghează un model. Pe parcurs, am evidențiat considerații practice și cele mai bune practici care se aplică oricui construiește o buclă de agent pe baza API-ului Responses.

Deși bucla agentului asigură fundația pentru Codex, este doar începutul. În postările viitoare, vom aprofunda arhitectura CLI, vom explora cum este implementată utilizarea instrumentelor și vom analiza mai detaliat modelul de sandboxing al Codex.

Autor

Michael Bolin

Mulțumiri

Mulțumiri speciale întregii echipe care a dezvoltat Codex CLI.