Codex CLI(odpre se v novem oknu) je naš večplatformni lokalni programski agent, zasnovan za ustvarjanje visokokakovostnih in zanesljivih sprememb programske opreme, pri čemer na vaši napravi deluje varno in učinkovito. Od prvega lansiranja CLI-ja aprila smo pridobili izjemno veliko znanja o tem, kako zasnovati programski agent najvišje ravni. Da bi te vpoglede razčlenili, je ta prispevek prvi v tekoči seriji, v kateri bomo raziskovali različne vidike delovanja Codexa ter predstavljali izkušnje, pridobljene skozi prakso. (Za še bolj podroben vpogled v zasnovo Codex CLI si lahko ogledate naš odprtokodni repozitorij na naslovu https://github.com/openai/codex(odpre se v novem oknu). Številne podrobnosti naših oblikovalskih odločitev so zabeležene v GitHub zahtevkih in zahtevkih za združitev sprememb, če želite izvedeti več.)
Za začetek se bomo osredotočili na zanko agenta, ki predstavlja osrednjo logiko v Codex CLI in je odgovorna za orkestracijo interakcije med uporabnikom, modelom in orodji, ki jih model prikliče za izvajanje smiselnega dela na programski opremi. Upamo, da vam bo ta prispevek ponudil jasen vpogled v vlogo, ki jo ima naš agent (»ogrodje«), pri učinkoviti uporabi velikega jezikovnega modela (LLM).
Preden se poglobimo, še kratka opomba o terminologiji. Pri OpenAI izraz »Codex« označuje nabor ponudb programskih agentov, med katerimi so Codex CLI, Codex Cloud in razširitev Codex za VS Code. Ta prispevek se osredotoča na ogrodje Codex, ki zagotavlja osnovno zanko agenta in izvršilno logiko, na kateri temeljijo vse izkušnje s Codexom in ki so uporabnikom dostopne prek Codex CLI. Zaradi preglednosti bomo izraza »Codex« in »Codex CLI« uporabljali izmenično. Za lažjo uporabo bomo tukaj izraza “Codex” in “Codex CLI” uporabljali izmenično.
V središču vsakega agenta, ki ga poganja umetna inteligenca, je tako imenovana 'zanka agenta'. Poenostavljen prikaz zanke agenta je naslednji:
Najprej agent prejme uporabnikov vnosin ga vključi v nabor besedilnih navodil, ki jih pripravi za model. Ta nabor navodil imenujemo poziv.
Naslednji korak je poizvedba modela, pri kateri mu agent posreduje navodila in zahteva generiranje odziva. Ta postopek imenujemo inferenca. Med inferenco se besedilni poziv najprej pretvori v zaporedje vhodnih žetonov(odpre se v novem oknu), to so cela števila, ki se nanašajo na vnose v besedišču modela. Ti žetoni se nato uporabijo za vzorčenje modela, kar povzroči nastanek novega zaporedja izhodnih žetonov.
Izhodni žetoni se pretvorijo nazaj v besedilo, ki predstavlja odziv modela. Ker se žetoni ustvarjajo postopoma, se lahko ta pretvorba izvaja sproti med delovanjem modela, zato številne aplikacije, ki temeljijo na velikih jezikovnih modelih, prikazujejo pretočni izhod. V praksi je inference običajno skrita za aplikacijskim programskim vmesnikom (API), ki deluje na ravni besedila in s tem abstrahira podrobnosti tokenizacije.
Kot rezultat koraka sklepanja model bodisi (1) pripravi končni odgovor na uporabnikov izvirni vnos ali (2) zahteva priklic orodja, ki ga naj bi agent izvedel (npr. »zaženi ls in poročaj o izhodu«). V primeru (2) agent izvede priklic orodja in njegov izhod doda k izvirnemu pozivu. Ta izhod se uporabi za oblikovanje novega vhoda, s katerim se model ponovno poizve, pri čemer agent nove informacije upošteva in postopek ponovi.
Ta postopek se ponavlja, dokler model ne preneha izdajati klicev orodij in namesto tega pripravi sporočilo za uporabnika (v modelih OpenAI imenovano sporočilo asistenta). V številnih primerih to sporočilo neposredno odgovori na uporabnikovo izvirno zahtevo, lahko pa predstavlja tudi dodatno vprašanje za uporabnika.
Ker agent lahko izvaja priklice orodij, ki spreminjajo lokalno okolje, njegov »izhod« ni omejen zgolj na sporočilo asistenta. Pogosto je glavni izhod programskega agenta koda, ki jo zapiše ali uredi na vaši napravi. Kljub temu se vsak korak vedno konča s sporočilom asistenta—na primer »Dodal sem architecture.md, ki ste ga zahtevali«—kar signalizira stanje zaključka v zanki agenta. Z vidika agenta je delo opravljeno in nadzor se vrne uporabniku.
Pot od uporabniškega vnosa do odziva agenta, prikazana v diagramu, se imenuje en korak (ang. turn) pogovora (pogovorni niz (ang. thread) v Codexu). Čeprav lahko ta pogovorni korak vključuje veliko iteracij med modelom in priklici orodij. Vsakič, ko v obstoječem pogovoru pošljete novo sporočilo, se celotna zgodovina pogovora vključi v poziv za novi korak, ki vključuje sporočila in priklice orodij iz prejšnjih korakov:
To pomeni, da se z rastjo pogovora povečuje tudi dolžina poziva, ki se uporablja za vzorčenje modela. Ta dolžina je pomembna, ker ima vsak model kontekstno okno, kar je največje število žetonov, ki jih lahko uporabi za en klic sklepanja. Upoštevajte, da to okno vključuje tako vhodne kot izhodne žetone. Kot si lahko predstavljate, se lahko agent v enem samem koraku odloči za več sto priklicev orodij, kar lahko privede do izčrpanja kontekstnega okna. Zaradi tega je upravljanje kontekstnega okna ena izmed številnih odgovornosti agenta. Zdaj si oglejmo, kako Codex izvaja zanko agenta.
Codex CLI pošilja HTTP zahteve na Responses API(odpre se v novem oknu) za izvajanje sklepanja modela. V nadaljevanju bomo prikazali, kako informacije tečejo skozi Codex, ki za poganjanje zanke agenta uporablja Responses API.
Končna točka Responses API, ki jo uporablja Codex CLI, je nastavljiva(odpre se v novem oknu), zato jo je mogoče uporabiti s katero koli končno točko, ki implementira Responses API(odpre se v novem oknu):
- Pri uporabi prijave v ChatGPT(odpre se v novem oknu) s Codex CLI se uporablja
https://chatgpt.com/backend-api/codex/responseskot končna točka - Pri uporabi preverjanja pristnosti z API-ključem(odpre se v novem oknu) z modeli, ki jih gosti OpenAI, se uporablja
https://api.openai.com/v1/responseskot končna točka - Ko zaženete Codex CLI z
--ossza uporabo gpt-oss z ollama 0.13.4+(odpre se v novem oknu) ali LM Studio 0.3.39+(odpre se v novem oknu), se privzeto poveže nahttp://localhost:11434/v1/responses, ki se izvaja lokalno na vašem računalniku - Codex CLI se lahko uporablja z Responses API-jem, ki ga gosti ponudnik oblaka, kot je Azure
Raziščimo, kako Codex ustvari poziv za prvi sklepni klic v pogovoru.
Kot končni uporabnik ne določite poziva, uporabljenega za dobesedno vzorčenje modela, ko poizvedujete po API-ju Responses. Namesto tega določite različne vrste vnosa kot del svoje poizvedbe, strežnik API za odgovore pa se odloči, kako te informacije strukturirati v poziv, ki ga je model zasnovan za uporabo. Na poziv lahko gledate kot na »seznam elementov«; ta razdelek bo pojasnil, kako se vaša poizvedba pretvori v ta seznam.
V začetnem pozivu je vsaka postavka na seznamu povezana z določeno vlogo. Vloga označuje, koliko teže naj bi imela povezana vsebina, in je ena od naslednjih vrednosti (v padajočem vrstnem redu prioritete): system, developer, user, assistant.
Responses API(odpre se v novem oknu) sprejme JSON obremenitev z mnogimi parametri. Osredotočili se bomo na te tri:
navodila(odpre se v novem oknu): sistemsko (ali razvijalčevo) sporočilo, vstavljeno v kontekst modelatools(odpre se v novem oknu): seznam orodij, ki jih lahko model uporabi med generiranjem odgovorainput(odpre se v novem oknu): seznam besedilnih, slikovnih ali datotečnih vnosov v model
V Codexu se polje instructions prebere iz model_instructions_file(odpre se v novem oknu) v ~/.codex/config.toml, če je določeno; sicer se uporabijo base_instructions, povezane z modelom(odpre se v novem oknu). Navodila, specifična za model, se nahajajo v repozitoriju Codex in so vključena v CLI (npr. gpt-5.2-codex_prompt.md(odpre se v novem oknu)).
Polje tools je seznam definicij orodij, ki ustrezajo shemi, ki jo določa Responses API. Za Codex to vključuje orodja, ki jih zagotavlja Codex CLI, orodja, ki jih zagotavlja Responses API, ki bi morala biti na voljo za Codex, ter orodja, ki jih zagotovi uporabnik, običajno prek strežnikov MCP:
Nazadnje je polje input v JSON tovoru seznam elementov. Codex vstavi naslednje elemente(odpre se v novem oknu) v input pred dodajanjem uporabniškega sporočila:
1. Sporočilo z role=developer, ki opisuje peskovnik, ki velja samo za Codex-om zagotovljeno shell orodje, opredeljeno v razdelku tools. To pomeni, da druga orodja, kot so tista, ki jih zagotavljajo strežniki MCP, niso v peskovniku Codex in so odgovorna za uveljavljanje svojih lastnih varovalk.
Sporočilo je zgrajeno iz predloge, kjer ključni deli vsebine izvirajo iz izrezkov Markdowna, vključenih v Codex CLI, kot sta workspace_write.md(odpre se v novem oknu) in on_request.md(odpre se v novem oknu):
2. (Neobvezno) Sporočilo z role=developer, katerega vsebina je vrednost developer_instructions, prebrana iz uporabnikove datoteke config.toml.
3. (Neobvezno) Sporočilo z role=user, katerega vsebina so »uporabniška navodila«, ki ne izvirajo iz ene same datoteke, temveč so zbrana iz več virov(odpre se v novem oknu). Na splošno se podrobnejša navodila pojavijo pozneje:
- Vsebina datotek
AGENTS.override.mdinAGENTS.mdv$CODEX_HOME - Ob upoštevanju omejitve (32 KiB, privzeto) preglejte vsako mapo od korena Git/projekta
cwd(če obstaja) do samegacwd: dodajte vsebino katere koli datotekeAGENTS.override.md,AGENTS.mdali katero koli ime datoteke, določeno zproject_doc_fallback_filenames v config.toml - Če so bile konfigurirane kakšne veščine(odpre se v novem oknu):
- kratek uvod o veščinah
- metapodatki veščin(odpre se v novem oknu) za vsako veščino
- oddelek o kako uporabljati veščine(odpre se v novem oknu)
4. Sporočilo z role=user, ki opisuje lokalno okolje, v katerem agent trenutno deluje. To določa trenutni delovni imenik in uporabnikovo lupino(odpre se v novem oknu):
Ko Codex opravi vse zgornje izračune za inicializacijo input, doda uporabniško sporočilo za začetek pogovora.
Prejšnji primeri so se osredotočali na vsebino vsakega sporočila, vendar upoštevajte, da je vsak element input JSON objekt s type, role(odpre se v novem oknu) in content, kot sledi:
Ko Codex sestavi celoten JSON tovor za pošiljanje v Responses API, nato izvede HTTP POST zahtevo z glavo Authorization, odvisno od tega, kako je končna točka Responses API konfigurirana v ~/.codex/config.toml (dodatne HTTP glave in parametri poizvedbe se dodajo, če so navedeni).
Ko strežnik OpenAI Responses API prejme zahtevo, uporabi JSON za izpeljavo poziva za model, kot sledi (za vsak primer, implementacija Responses API po meri bi se lahko odločila drugače):
Kot lahko vidite, vrstni red prvih treh elementov v pozivu določa strežnik, ne odjemalec. Kljub temu je od teh treh elementov samo vsebina sistemskega sporočila tudi pod nadzorom strežnika, saj orodja in navodila določa odjemalec. Temu sledi input iz JSON obremenitve za dokončanje poziva.
Zdaj, ko imamo naš poziv, smo pripravljeni vzorčiti model.
Ta zahteva HTTP za Responses API sproži prvi “korak” pogovora v Codexu. Strežnik odgovori s tokom dogodkov, poslanih s strežnika (SSE(odpre se v novem oknu)). data vsakega dogodka je JSON-podatkovni tovor z "type", ki se začne z "response", kar bi lahko bilo na primer takole (celoten seznam dogodkov je na voljo v naši API dokumentaciji(odpre se v novem oknu)):
Codex porablja tok dogodkov(odpre se v novem oknu) in jih ponovno objavi kot interne objekte dogodkov, ki jih lahko uporabi naročnik. Dogodki, kot je response.output_text.delta, se uporabljajo za podporo pretakanju v uporabniškem vmesniku, medtem ko se drugi dogodki, kot je response.output_item.added, pretvorijo v objekte, ki se dodajo v input za nadaljnje klice API-ja Responses.
Predpostavimo, da prva zahteva za Responses API vključuje dva dogodka response.output_item.done: enega z type=reasoning in enega z type=function_call. Ti dogodki morajo biti predstavljeni v polju input v JSON-u, ko model ponovno poizvedujemo z odzivom na klic orodja:
Rezultirajoči poziv, uporabljen za vzorčenje modela kot del nadaljnje poizvedbe, bi izgledal takole:
Zlasti bodite pozorni na to, kako je stari poziv natančna predpona novega poziva. To je namerno, saj to naredi nadaljnje zahteve veliko učinkovitejše, ker nam omogoča, da izkoristimo predpomnjenje pozivov (o čemer bomo razpravljali v naslednjem razdelku o zmogljivosti).
Ko se ozremo nazaj na naš prvi diagram zanke agenta, opazimo, da bi lahko bilo veliko iteracij med sklepanjem in klicanjem orodij. Poziv se lahko še naprej povečuje, dokler končno ne prejmemo sporočila asistenta, ki označuje konec izmenjave:
V Codex CLI uporabniku prikažemo sporočilo asistenta in usmerimo urejevalnik sporočil tako, da uporabniku nakaže, da je zdaj na vrsti za nadaljevanje pogovora. Če se uporabnik odzove, je treba tako sporočilo asistenta iz prejšnje izmenjave kot tudi uporabnikovo novo sporočilo dodati v input (vhod) v zahtevi API za odgovore, da se začne nova izmenjava:
Še enkrat, ker nadaljujemo pogovor, se dolžina za input, ki ga pošljemo v API za odgovore, še naprej povečuje:
Poglejmo, kaj ta vedno večji poziv pomeni za zmogljivost.
Morda se sprašujete: »Ali ni zanka agenta kvadratna glede na količino podatkov JSON, ki se med pogovorom pošiljajo v Responses API?« Odgovor bi bil pritrdilen. Čeprav API Responses podpira izbirni parameter previous_response_id(odpre se v novem oknu) za ublažitev te težave, ga Codex danes ne uporablja, predvsem zato, da ohrani zahteve popolnoma brez stanja in da podpira konfiguracije ničelne hrambe podatkov (ZDR).
Izogibanje previous_response_id poenostavi stvari za ponudnika Responses API, ker zagotavlja, da je vsaka zahteva brez stanja. To tudi olajša podporo strankam, ki so se odločile za ničelno hrambo podatkov (ZDR)(odpre se v novem oknu), saj bi bilo shranjevanje podatkov, potrebnih za podporo previous_response_id, v nasprotju z ZDR. Upoštevajte, da stranke ZDR ne žrtvujejo zmožnosti, da bi imele koristi od lastniških sporočil o sklepanju iz prejšnjih potez, saj je povezano encrypted_content mogoče dešifrirati na strežniku. (OpenAI hrani dešifrirni ključ stranke ZDR, vendar ne njenih podatkov.) Oglejte si zahtevke pridružitve sprememb #642(odpre se v novem oknu) in #1641(odpre se v novem oknu) za povezane spremembe v Codexu za podporo ZDR.
Na splošno strošek vzorčenja modela prevlada nad stroškom omrežnega prometa, zato je prav vzorčenje glavni cilj naših prizadevanj za učinkovitost. Zato je predpomnjenje pozivov tako pomembno, saj nam omogoča ponovno uporabo izračunov iz prejšnjega klica inference. Ko dobimo zadetke predpomnilnika, je vzorčenje modela linearno namesto kvadratnega. Naša dokumentacija o predpomnjenju pozivov (odpre se v novem oknu)to podrobneje pojasnjuje:
Zadetki v predpomnilniku so mogoči le pri natančnih ujemanjih predpone v pozivu. Da bi dosegli koristi predpomnjenja, je treba statično vsebino, kot so navodila in primeri, postaviti na začetek poziva, spremenljivo vsebino, kot so uporabniško specifične informacije, pa na konec. To velja tudi za slike in orodja, ki morajo biti med zahtevami enaki.
S tem v mislih razmislimo, katere vrste operacij bi lahko povzročile »zgrešitev predpomnilnika« v Codexu:
- Spreminjanje
orodij, ki so na voljo modelu, med pogovorom. - Spreminjanje
modela, ki je cilj zahteve Responses API (v praksi to spremeni tretji element v izvirnem pozivu, saj vsebuje navodila, specifična za model). - Spreminjanje konfiguracije peskovnika, načina odobritve ali trenutnega delovnega imenika.
Ekipa Codex mora biti pri uvajanju novih funkcionalnosti v Codex CLI zelo previdna, saj lahko te ogrozijo predpomnjenje pozivov. Kot primer je naša začetna podpora za orodja MCP uvedla napako, pri kateri nismo uspeli našteti orodij v doslednem vrstnem redu(odpre se v novem oknu), kar je povzročalo zgrešene zadetke predpomnilnika. Upoštevajte, da so orodja MCP lahko še posebej zahtevna, ker lahko strežniki MCP sproti spreminjajo seznam orodij, ki jih zagotavljajo, prek obvestila notifications/tools/list_changed(odpre se v novem oknu). Upoštevanje takega obvestila sredi dolgega pogovora lahko povzroči drago zgrešitev predpomnilnika.
Kadar je mogoče, spremembe konfiguracije, ki se zgodijo sredi pogovora, obravnavamo tako, da novo sporočilo dodamo k input, da odraža spremembo, namesto da bi spreminjali prejšnje sporočilo:
- Če se konfiguracija peskovnika ali način odobritve spremeni, vstavimo(odpre se v novem oknu) novo sporočilo
role=developerv enaki obliki kot izvirni element<permissions instructions>. - Če se trenutni delovni imenik spremeni, vstavimo(odpre se v novem oknu) novo sporočilo
role=userz enakim formatom kot izvirni<environment_context>.
Zelo se trudimo, da zagotovimo zadetke predpomnilnika za boljšo učinkovitost. Obstaja še en ključni vir, ki ga moramo upravljati: okno za kontekst.
Naša splošna strategija, da se izognemo temu, da bi nam zmanjkalo okna konteksta, je, da pogovor zbije, ko število žetonov preseže določen prag. Natančneje, input zamenjamo z novim, manjšim seznamom elementov, ki je reprezentativen za pogovor, kar agentu omogoča, da nadaljuje z razumevanjem dogajanja do tega trenutka. Zgodnja implementacija kompakcije(odpre se v novem oknu) je od uporabnika zahtevala, da ročno sproži ukaz /compact, ki bi poizvedel Responses API z uporabo obstoječega pogovora in prilagojenih navodil za povzetek(odpre se v novem oknu). Codex je uporabil nastalo sporočilo asistenta, ki je vsebovalo povzetek kot nov vnos(odpre se v novem oknu) za nadaljnje korake pogovora.
Od takrat se je Responses API razvil, da podpira posebno /responses/compact končno točko(odpre se v novem oknu), ki izvaja zgoščevanje bolj učinkovito. Vrne seznam elementov(odpre se v novem oknu), ki jih je mogoče uporabiti namesto prejšnjega input za nadaljevanje pogovora, hkrati pa sprosti okno konteksta. Ta seznam vključuje poseben element type=compaction z neprozornim elementom encrypted_content, ki ohranja latentno razumevanje modela o izvirnem pogovoru. Zdaj Codex samodejno uporablja to končno točko za stiskanje pogovora, ko je presežena auto_compact_limit(odpre se v novem oknu).
Predstavili smo zanko agenta Codex in pojasnili, kako Codex pri poizvedovanju modela oblikuje in upravlja svoj kontekst. Ob tem smo izpostavili praktične vidike in najboljše prakse, ki veljajo za vsakogar, ki gradi zanko agenta na podlagi Responses API.
Čeprav zanka agenta predstavlja temelj Codexa, je to šele začetek. V prihodnjih prispevkih se bomo poglobili v arhitekturo CLI, raziskali, kako je udejanjena uporaba orodij, in si podrobneje ogledali model peskovniškega delovanja v Codexu.


