Preskočite na glavni sadržaj
OpenAI

23. siječnja 2026.

Inženjerstvo

Razmotavanje petlje agenta Codex

Autor: Michael Bolin, član tehničkog osoblja

Učitavanje…

Codex CLI(otvara se u novom prozoru) je naš lokalni softverski agent za više platformi, osmišljen za izradu visokokvalitetnih i pouzdanih promjena softvera, uz siguran i učinkovit rad na vašem računalu. Naučili smo mnogo o tome kako izgraditi prvorazrednog softverskog agenta otkako smo prvi put lansirali CLI u travnju. Kako bismo razjasnili te uvide, ovo je prva objava u kontinuiranom serijalu u kojem ćemo istražiti različite aspekte funkcioniranja Codexa, kao i teško stečene lekcije. (Za još detaljniji uvid u to kako je izgrađen Codex CLI, pogledajte naš repozitorij otvorenog koda na https://github.com/openai/codex(otvara se u novom prozoru). Mnogi finiji detalji naših dizajnerskih odluka zabilježeni su u GitHub issueovima i pull requestovima ako želite saznati više.)

Za početak ćemo se usredotočiti na petlju agenta, koja je temeljna logika u Codex CLI-ju odgovorna za orkestriranje interakcije između korisnika, modela i alata koje model poziva kako bi obavio smislen softverski rad. Nadamo se da vam ovaj post pruža dobar uvid u ulogu koju naš agent (ili „harness”) ima u korištenju LLM-a.

Prije nego što krenemo, kratka napomena o terminologiji: u OpenAI-u, „Codex“ obuhvaća skup ponuda softverskih agenata, uključujući Codex CLI, Codex Cloud i proširenje Codex VS Code. Ova objava usredotočuje se na Codex harness, koji pruža osnovnu petlju agenta i logiku izvršavanja koja je temelj svih Codex iskustava i dostupna je putem Codex CLI-ja. Radi lakšeg razumijevanja, ovdje ćemo pojmove „Codex“ i „Codex CLI“ koristiti naizmjenično.

Petlja agenta

U središtu svakog AI agenta nalazi se nešto što se naziva „petlja agenta“. Pojednostavljeni prikaz petlje agenta izgleda ovako:

Dijagram pod nazivom „Agent loop“ koji prikazuje kako AI sustav obrađuje korisnički zahtjev, poziva alate, promatra rezultate, ažurira svoj plan te vraća rezultate. Strelice povezuju korake poput korisničkog unosa, rasuđivanja modela, radnji alata i konačnog odgovora.

Za početak, agent uzima unos od korisnika kako bi ga uključio u skup tekstualnih uputa koje priprema za model poznat kao upit.

Sljedeći korak je upit modelu slanjem uputa i traženjem da generira odgovor, proces poznat kao inferencija. Tijekom izvođenja zaključivanja, tekstualni upit se najprije prevodi u niz ulaznih tokena(otvara se u novom prozoru)—cijelih brojeva koji indeksiraju u vokabular modela. Ti se tokeni zatim koriste za uzorkovanje modela, stvarajući novi niz izlaznih tokena.

Izlazni tokeni prevode se natrag u tekst, što postaje odgovor modela. Budući da se tokeni generiraju postupno, prijevod se može odvijati dok model radi, što je razlog zašto mnoge aplikacije temeljene na LLM-u prikazuju izlaz u stvarnom vremenu. U praksi se zaključivanje obično skriva iza API-ja koji obrađuje tekst, apstrahirajući detalje tokenizacije.

Kao rezultat koraka izvođenja zaključaka, model ili (1) daje konačan odgovor na korisnikov izvorni unos ili (2) zatraži poziv alata koji agent treba izvršiti (npr. „pokreni ls i prijavi izlaz“). U slučaju (2), agent izvršava poziv alata i dodaje njegov izlaz izvornom upitu. Ovaj izlaz koristi se za stvaranje novog unosa koji se ponovno šalje modelu; agent tada može uzeti u obzir te nove informacije i pokušati ponovno.

Taj se proces ponavlja dok model ne prestane slati pozive alata i umjesto toga ne generira poruku za korisnika (u OpenAI modelima naziva se poruka asistenta). U mnogim slučajevima, ta poruka izravno odgovara na izvorni zahtjev korisnika, ali može biti i dodatno pitanje za korisnika.

Budući da agent može izvršavati pozive alata koji mijenjaju lokalno okruženje, njegov „izlaz“ nije ograničen samo na poruku asistenta. U mnogim slučajevima, glavni rezultat rada softverskog agenta je kôd koji piše ili uređuje na vašem računalu. Ipak, svaki se korak uvijek završava porukom asistenta—poput „Dodao sam architecture.md koji ste tražili”—koja signalizira stanje završetka u petlji agenta. Iz perspektive agenta, njegov je posao završen i kontrola se vraća korisniku.

Put od korisničkog unosa do odgovora agenta prikazan na dijagramu naziva se jednim krugom razgovora (jednom niti u Codexu). Iako ovaj razgovor može uključivati mnogo iteracija između modela zaključivanja i poziva alata. Svaki put kad pošaljete novu poruku u postojeći razgovor, povijest razgovora uključuje se kao dio upita za novi korak, uključujući poruke i pozive alata iz prethodnih koraka:

Dijagram pod naslovom „Višekoračna petlja agenta“ koji prikazuje kako AI agent iterativno prima korisnički unos, generira radnje, konzultira alate, ažurira stanje i vraća rezultate. Uključuje označene korake, strelice i primjere izlaza alata koji prikazuju ciklus rasuđivanja agenta.

To znači da kako razgovor raste, tako se povećava i duljina upita koji se upotrebljava za uzorkovanje modela. Ova duljina je važna jer svaki model ima kontekstni prozor, što je maksimalan broj tokena koje može koristiti za jedan poziv inferencije. Napomena: ovaj prozor uključuje ulazne i izlazne tokene. Kao što možete zamisliti, agent bi mogao odlučiti napraviti stotine poziva alata u jednom potezu, što bi moglo iscrpiti kontekstualni prozor. Iz tog razloga, upravljanje kontekstualnim prozorom jedna je od mnogih odgovornosti agenta. Sada zaronimo kako bismo vidjeli kako Codex pokreće petlju agenta.

Inferencija modela

Codex CLI šalje HTTP zahtjeve Responses API-ju(otvara se u novom prozoru) za pokretanje inferencije modela. Ispitat ćemo kako informacije teku kroz Codex, koji upotrebljava Responses API za pokretanje petlje agenta.

Krajnja točka Responses API-ja koju upotrebljava Codex CLI je podesiva(otvara se u novom prozoru), tako da se može upotrebljavati s bilo kojom krajnjom točkom koja implementira Responses API(otvara se u novom prozoru):

Istražimo kako Codex kreira upit za prvi poziv inferencije u razgovoru.

Izrada početnog upita

Kao krajnji korisnik, ne specificirate upit koji se koristi za doslovno uzorkovanje modela kada šaljete upit prema Responses API-ju. Umjesto toga, vi navodite različite vrste unosa kao dio svog upita, a poslužitelj Responses API-ja odlučuje kako strukturirati te informacije u upit koji je model osmišljen za obradu. Možete razmišljati o upitu kao o „popisu stavki”; ovaj će odjeljak objasniti kako se vaš upit pretvara u taj popis.

U početnom upitu svaka stavka na popisu povezana je s ulogom. Uloga označava koliku važnost treba imati povezani sadržaj i jedna je od sljedećih vrijednosti (prema opadajućem redoslijedu prioriteta): sustav, razvojni inženjer, korisnik, asistent.

Responses API(otvara se u novom prozoru) prima JSON payload s mnogim parametrima. Usredotočit ćemo se na ta tri područja:

U Codexu se polje instructions čita iz model_instructions_file(otvara se u novom prozoru) u ~/.codex/config.toml, ako je navedeno; u suprotnom se koriste base_instructions povezane s modelom(otvara se u novom prozoru). Upute specifične za model nalaze se u repozitoriju Codex i uključene su u CLI (npr. gpt-5.2-codex_prompt.md(otvara se u novom prozoru)).

Polje tools je popis definicija alata koji su u skladu sa shemom koju definira Responses API. Za Codex, to uključuje alate koje pruža Codex CLI, alate koje pruža Responses API i koji bi trebali biti dostupni Codexu, kao i alate koje pruža korisnik, obično putem MCP poslužitelja:

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
]

Na kraju, polje unos u JSON payloadu je popis stavki. Codex umeće sljedeće stavke(otvara se u novom prozoru) u unos prije dodavanja korisničke poruke:

1. Poruka s role=developer koja opisuje sandbox koji se primjenjuje samo na Codexov shell alat definiran u odjeljku tools. To jest, ostali alati, poput onih koje pružaju MCP poslužitelji, nisu u Codexovom sandboxu i odgovorni su za provedbu vlastitih zaštitnih mjera.

Poruka je sastavljena iz predloška gdje ključni dijelovi sadržaja dolaze iz isječaka Markdowna uključenih u Codex CLI, kao što su workspace_write.md(otvara se u novom prozoru) i on_request.md(otvara se u novom prozoru):

Običan tekst

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. (Neobavezno) Poruka s role=developer čiji sadržaj predstavlja vrijednost developer_instructions pročitanu iz korisnikove datoteke config.toml.

3. (Neobavezno) Poruka s role=user čiji sadržaj su „korisničke upute”, koje ne potječu iz jedne datoteke, već su prikupljene iz više izvora(otvara se u novom prozoru). Općenito, detaljnije upute pojavljuju se kasnije:

4. Poruka s role=user koja opisuje lokalno okruženje u kojem agent trenutno radi. Ovo određuje trenutni radni direktorij i korisničku ljusku(otvara se u novom prozoru):

Običan tekst

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

Nakon što Codex obavi sve gore navedene izračune za inicijalizaciju unosa, dodaje korisničku poruku kako bi započeli razgovor.

Prethodni primjeri bili su usmjereni na sadržaj svake poruke, ali imajte na umu da je svaki element unosa JSON objekt s vrstom, ulogom(otvara se u novom prozoru) i sadržajem kako slijedi:

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
}

Nakon što Codex izgradi puni JSON payload za slanje na Responses API, šalje HTTP POST zahtjev s autorizacijskim zaglavljem, ovisno o tome kako je krajnja točka Responses API-ja konfigurirana u ~/.codex/config.toml (dodatna HTTP zaglavlja i parametri upita dodaju se ako su navedeni).

Kada poslužitelj OpenAI Responses API-ja primi zahtjev, koristi JSON za generiranje upita za model na sljedeći način (za svaki slučaj, prilagođena implementacija Responses API-ja mogla bi donijeti drugačiji izbor):

Dijagram snimke koji prikazuje jedan korak u petlji AI agenta. Zahtjev korisnika ulazi u model, koji generira misao, radnju s nazivom alata i unos za alat. Dijagram ističe taj međukorak u rasuđivanju prije nego što se alat pozove.

Kao što možete vidjeti, redoslijed prve tri stavke u upitu određuje poslužitelj, a ne klijent. Međutim, od te tri stavke, samo je sadržaj sistemske poruke također pod kontrolom poslužitelja, dok alate i upute određuje klijent. Nakon toga slijedi unos iz JSON payloada kako bi se dovršio upit.

Sada kada imamo naš upit, spremni smo uzeti uzorak modela.

Prvi ciklus

Ovaj HTTP zahtjev prema Responses API-ju pokreće prvi „korak“ razgovora u Codexu. Poslužitelj odgovara nizom događaja poslanih od poslužitelja (SSE(otvara se u novom prozoru)). Podatak svakog događaja je JSON payload s "type" koji započinje s "response", što bi moglo izgledati ovako (cjelovit popis događaja možete pronaći u našoj API dokumentaciji(otvara se u novom prozoru)):

Običan tekst

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 konzumira niz događaja(otvara se u novom prozoru) i ponovno ih objavljuje kao interne objekte događaja koje klijent može koristiti. Događaji poput response.output_text.delta koriste se za podršku strujanju u korisničkom sučelju, dok se drugi događaji poput response.output_item.added pretvaraju u objekte koji se dodaju u unos za naknadne pozive API-ja za odgovore.

Pretpostavimo da prvi zahtjev prema Responses API-ju uključuje dva događaja response.output_item.done: jedan s type=reasoning i jedan s type=function_call. Ovi događaji moraju biti predstavljeni u polju unos JSON-a kada ponovno upitamo model s odgovorom na poziv alata: 

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
]

Rezultirajući upit korišten za uzorkovanje modela kao dio sljedećeg upita izgledao bi ovako:

Dijagram s oznakom „Snapshot 2” koji prikazuje AI agenta nakon poziva alata. Model prima opažanje alata i generira novu misao i akciju. Strelice povezuju unose, opažanja i izlaze kako bi ilustrirale kako agent ponavlja svoju petlju rasuđivanja.

Posebno obratite pozornost na to kako je stari upit točan prefiks novog upita. To je namjerno tako, jer čini sljedeće zahtjeve mnogo učinkovitijima omogućujući nam da iskoristimo predmemoriranje upita (o čemu ćemo govoriti u sljedećem odjeljku o performansama).

Osvrćući se na naš prvi dijagram petlje agenta, vidimo da može biti mnogo iteracija između zaključivanja i pozivanja alata. Upit može nastaviti rasti sve dok napokon ne primimo poruku asistenta, što označava kraj poteza:

Običan tekst

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

U Codex CLI-ju korisniku prikazujemo poruku asistenta i usmjeravamo uređivač kako bismo korisniku naznačili da je njihov red da nastave razgovor. Ako korisnik odgovori, poruka asistenta iz prethodnog koraka i nova poruka korisnika moraju se dodati u unos u zahtjevu za Responses API kako bi se započeo novi korak:

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
]

Još jednom, budući da nastavljamo razgovor, duljina unosa koju šaljemo u Responses API stalno se povećava:

Dijagram s oznakom „Snapshot 3” koji prikazuje završnu fazu petlje AI agenta. Nakon što primi rezultate alata, model generira završnu misao i konačan odgovor koji se vraća korisniku. Strelice prikazuju prijelaz od rezultata alata do dovršenog odgovora.

Istražimo što ovaj sve veći upit znači za performanse.

Razmatranja o performansama

Možda se pitate: „Samo malo, nije li petlja agenta kvadratna u smislu količine JSON-a poslanog na Responses API tijekom razgovora?” I bili biste u pravu. Iako Responses API podržava neobavezni parametar previous_response_id(otvara se u novom prozoru) za ublažavanje tog problema, Codex ga danas ne koristi, prvenstveno kako bi zahtjevi ostali potpuno bez stanja i kako bi se podržale konfiguracije bez zadržavanja podataka (ZDR).

Izbjegavanje previous_response_id pojednostavljuje stvari za pružatelja Responses API-ja jer osigurava da svaki zahtjev bude bez stanja. To također olakšava podršku korisnicima koji su se odlučili za bez zadržavanja podataka (ZDR)(otvara se u novom prozoru), jer bi pohrana podataka potrebnih za podršku previous_response_id bila u sukobu sa ZDR-om. Imajte na umu da korisnici ZDR-a ne gube mogućnost korištenja vlasničkih poruka rasuđivanja iz prethodnih koraka, jer se povezani encrypted_content može dešifrirati na poslužitelju. (OpenAI pohranjuje ključ za dešifriranje ZDR korisnika, ali ne i njihove podatke.) Pogledajte PR-ove #642(otvara se u novom prozoru) i #1641(otvara se u novom prozoru) za povezane promjene u Codexu kako bi se podržao ZDR.

Općenito, trošak uzorkovanja modela nadmašuje trošak mrežnog prometa, zbog čega je uzorkovanje primarni cilj naših nastojanja za povećavanje učinkovitosti. Zato je predmemoriranje upita toliko važno jer omogućuje ponovnu upotrebu izračuna iz prethodnog poziva zaključivanja. Kada dobijemo pogotke predmemorije, uzorkovanje modela postaje linearno umjesto kvadratno. Naša dokumentacija o predmemoriranju upita (otvara se u novom prozoru)to objašnjava u više detalja:

Pogoci predmemorije mogući su samo za točna podudaranja prefiksa unutar upita. Kako biste ostvarili prednosti predmemoriranja, stavite statičan sadržaj poput uputa i primjera na početak upita, a promjenjiv sadržaj, kao što su informacije specifične za korisnika, na kraj. Ovo se odnosi i na slike i alate, koji moraju biti identični u svim zahtjevima.

Imajući to na umu, razmotrimo koje vrste operacija mogu uzrokovati „promašaj predmemorije” u Codexu:

  • Promjena alata dostupnih modelu tijekom razgovora.
  • Promjena modela koji je cilj zahtjeva za Responses API (u praksi, to mijenja treću stavku u izvornom upitu jer sadrži upute specifične za model).
  • Promjena konfiguracije sandboxa, načina odobravanja ili trenutnog radnog direktorija.

Tim Codex mora biti pažljiv pri uvođenju novih značajki u Codex CLI koje bi mogle ugroziti predmemoriranje upita. Na primjer, naša početna podrška za MCP alate uvela je pogrešku zbog koje nismo uspjeli nabrojati alate u dosljednom redoslijedu(otvara se u novom prozoru), što je uzrokovalo promašaje predmemorije. Imajte na umu da MCP alati mogu biti posebno zahtjevni jer MCP poslužitelji mogu u hodu promijeniti popis alata koje pružaju putem obavijesti notifications/tools/list_changed(otvara se u novom prozoru). Poštivanje te obavijesti usred dugog razgovora može uzrokovati skupi promašaj predmemorije.

Kad god je moguće, promjene konfiguracije koje se dogode usred razgovora rješavamo dodavanjem nove poruke u unos kako bismo odrazili promjenu, umjesto da mijenjamo raniju poruku:

Ulažemo velike napore kako bismo osigurali pogotke predmemorije radi poboljšavanja performansi. Postoji još jedan ključni resurs kojim trebamo upravljati: kontekstni prozor.

Naša opća strategija da izbjegnemo nestanak kontekstnog prozora jest sažimanje razgovora kada broj tokena premaši određeni prag. Konkretno, mijenjamo unos novim, manjim popisom stavki koje su reprezentativne za razgovor, omogućujući agentu da nastavi s razumijevanjem onoga što se dosad dogodilo. Rana implementacija zbijanja(otvara se u novom prozoru) zahtijevala je da korisnik ručno pokrene naredbu /compact, koja bi ispitivala Responses API upotrebom postojećeg razgovora i prilagođene upute za sažimanje(otvara se u novom prozoru). Codex je nekada koristio poruku asistenta koja sadrži sažetak kao novi unos(otvara se u novom prozoru) za sljedeće korake razgovora.

Od tada se API za odgovore razvio kako bi podržavao posebnu /responses/compact krajnju točku(otvara se u novom prozoru) koja učinkovitije obavlja zbijanje. Ono vraća popis stavki(otvara se u novom prozoru) koji se može upotrebljavati umjesto prethodnog unosa za nastavak razgovora te se pritom oslobađa kontekstualni prozor. Taj popis uključuje posebnu stavku type=compaction s neprozirnom stavkom encrypted_content koja čuva latentno razumijevanje modela izvornog razgovora. Sada, Codex automatski upotrebljava tu krajnju točku za zbijanje razgovora kada se premaši auto_compact_limit(otvara se u novom prozoru).

Slijedi

Uveli smo petlju agenta Codex i objasnili kako Codex oblikuje i upravlja svojim kontekstom prilikom upita prema modelu. Usput smo istaknuli praktična razmatranja i najbolje prakse koje se primjenjuju na svakoga tko gradi petlju agenta na temelju Responses API-ja.

Iako petlja agenta pruža temelj za Codex, to je tek početak. U nadolazećim objavama detaljnije ćemo istražiti arhitekturu CLI-ja, istražiti kako je implementirana upotreba alata i pobliže pogledati Codexov model sandboxinga.

Autor

Michael Bolin

Priznanja

Posebne se zahvaljujemo cijelom timu koji je razvio Codex CLI.