Codex CLI(otvorí sa v novom okne) je náš multiplatformový lokálny softvérový agent, navrhnutý na vytváranie vysokokvalitných a spoľahlivých zmien softvéru, pričom na tvojom zariadení funguje bezpečne a efektívne. Naučili sme sa veľa o tom, ako vytvoriť softvérového agenta svetovej triedy od nášho prvého spustenia CLI v apríli. Aby sme odhalili tieto poznatky, toto je prvý príspevok v prebiehajúcej sérii, v ktorom preskúmame rôzne aspekty fungovania Codexu a ťažko získané lekcie. (Pre ešte podrobnejší pohľad na to, ako je Codex CLI zostavený, pozri si náš repozitár s otvoreným zdrojovým kódom na https://github.com/openai/codex(otvorí sa v novom okne). Al sa chceš dozvedieť viac, mnohé z detailnejších údajov o našich dizajnových rozhodnutiach sú zdokumentované v GitHub issues a žiadostiach o zlúčenie.)
Na úvod sa zameriame na slučku agenta, čo je hlavná logika v Codex CLI, ktorá je zodpovedná za riadenie interakcie medzi používateľom, modelom a nástrojmi, ktoré model vyvoláva na vykonávanie zmysluplnej práce so softvérom. Dúfame, že vám tento príspevok poskytne dobrý pohľad na úlohu, ktorú náš agent (alebo „harness“) zohráva pri využívaní LLM.
Skôr než sa do toho pustíme, krátka poznámka k terminológii: v OpenAI „Codex“ zahŕňa súbor ponúk softvérových agentov, vrátane Codex CLI, Codex Cloud a rozšírenia Codex VS Code. Tento príspevok sa zameriava na Codex harness, ktorý poskytuje základnú slučku agenta a logiku vykonávania, na ktorej sú postavené všetky skúsenosti s Codexom a ktorá je sprístupnená prostredníctvom Codex CLI. Pre zjednodušenie budeme používať pojmy „Codex“ a „Codex CLI“ zameniteľne.
V jadre každého AI agenta je niečo, čo sa nazýva „slučka agenta“. Zjednodušená ilustrácia cyklu agenta vyzerá takto:
Na začiatok agent prevezme od používateľa input, ktorý zahrnie do súboru textových pokynov, ktoré pripraví pre model známy ako príkaz.
Ďalším krokom je dotazovať model zaslaním našich pokynov a požiadaním o vygenerovanie odpovede, čo je proces známy ako inferencia. Počas inferencie sa textový príkaz najprv preloží na postupnosť vstupných tokenov(otvorí sa v novom okne) – celých čísel, ktoré sa indexujú do slovníka modelu. Tieto tokeny sa potom použijú na vzorkovanie modelu, čím sa vytvorí nová sekvencia výstupných tokenov.
Výstupné tokeny sa preložia späť na text, ktorý sa stáva odpoveďou modelu. Keďže sa tokeny vytvárajú postupne, preklad môže prebiehať súčasne s behom modelu, čo je dôvod, prečo mnohé aplikácie založené na LLM zobrazujú prúdový výstup. V praxi je inferencia zvyčajne zapuzdrená za API, ktoré pracuje s textom a abstrahuje detaily tokenizácie.
V dôsledku kroku inferencie model buď 1) vytvorí konečnú odpoveď na pôvodný vstup používateľa, alebo 2) požiada o volanie nástroja, ktoré má agent vykonať (napr. „spustiť ls a nahlásiť výstup“). V prípade 2) agent vykoná volanie nástroja a pripojí jeho výstup k pôvodnému príkazu. Tento výstup sa používa na vytvorenie nového vstupu, ktorý sa použije na opätovné dotazovanie modelu. Agent potom môže zohľadniť tieto nové informácie a skúsiť to znova.
Tento proces sa opakuje, kým model neprestane generovať volania nástrojov a namiesto toho nevytvorí správu pre používateľa (v modeloch OpenAI označovanú ako správa asistenta). V mnohých prípadoch táto správa priamo odpovedá na pôvodnú žiadosť používateľa, ale môže byť aj následnou otázkou pre používateľa.
Keďže agent môže vykonávať volania nástrojov, ktoré upravujú lokálne prostredie, jeho „výstup“ nie je obmedzený len na správu asistenta. V mnohých prípadoch je hlavným výstupom softvérového agenta kód, ktorý píše alebo upravuje na vašom počítači. Napriek tomu sa každý ťah vždy končí správou asistenta – napríklad „Pridal som architecture.md, o ktorý si požiadal“ – ktorá signalizuje ukončenie v slučke agenta. Z pohľadu agenta je jeho práca dokončená a kontrola sa vracia používateľovi.
Cesta od vstupu používateľa po odpoveď agenta zobrazená v diagrame sa označuje ako jeden ťah konverzácie (vlákno v Codexe). Prostredníctvom tohto ťahu v konverzácii môže zahŕňať mnoho iterácií medzi modelovou inferenciou a volaniami nástrojov. Zakaždým, keď pošleš novú správu do existujúcej konverzácie, história konverzácie sa zahrnie ako súčasť príkazu pre nový ťah, ktorý obsahuje správy a volania nástrojov z predchádzajúcich ťahov:
To znamená, že ako sa konverzácia rozširuje, predlžuje sa aj dĺžka príkazu na vzorkovanie modelu. Táto dĺžka je dôležitá, pretože každý model má kontextové okno, čo je maximálny počet tokenov, ktoré môže použiť na jedno volanie inferencie. Všimni si, že toto okno obsahuje vstupné a výstupné tokeny. Ako si vieš predstaviť, agent by sa mohol rozhodnúť vykonať stovky volaní nástrojov v jednom ťahu, čím by potenciálne vyčerpal kontextové okno. Z tohto dôvodu je správa kontextového okna jednou z mnohých zodpovedností agenta. Teraz sa poďme ponoriť do toho, ako Codex spúšťa slučku agenta.
Codex CLI odosiela HTTP požiadavky na Responses API(otvorí sa v novom okne) na vykonanie inferencie modelu. Preskúmame, ako informácie prúdia cez Codex, ktorý používa API Responses na riadenie slučky agenta.
Koncový bod rozhrania Responses API, ktorý používa Codex CLI, je konfigurovateľný(otvorí sa v novom okne), takže ho možno použiť s akýmkoľvek koncovým bodom, ktorý implementuje rozhranie Responses API(otvorí sa v novom okne):
- Pri používaní prihlásenia do ChatGPT(otvorí sa v novom okne) s rozhraním príkazového riadku Codex používa
https://chatgpt.com/backend-api/codex/responsesako koncový bod - Pri používaní autentifikácie pomocou API kľúča(otvorí sa v novom okne) s modelmi hostovanými OpenAI sa používa
https://api.openai.com/v1/responsesako koncový bod - Pri spustení Codex CLI s
--ossna použitie gpt-oss s ollama 0.13.4+(otvorí sa v novom okne) alebo LM Studio 0.3.39+(otvorí sa v novom okne) sa predvolene nastavíhttp://localhost:11434/v1/responsesbežiace lokálne na tvojom počítači - Codex CLI sa dá používať s rozhraním Responses API, ktoré je hostované poskytovateľom cloudu, ako je Azure.
Poďme preskúmať, ako Codex vytvára príkaz pre prvé vyvolanie inferencie v konverzácii.
Ako koncový používateľ pri dopytovaní rozhrania Responses API neuvádzate doslovne príkaz použitý na vzorkovanie modelu. Namiesto toho zadáš rôzne typy vstupov ako súčasť svojho dopytu a server API Responses rozhodne, ako tieto informácie štruktúrovať do príkazu, ktorý je model navrhnutý spracovať. Príkaz si môžeš predstaviť ako „zoznam položiek“. Táto časť vysvetlí, ako sa tvoja požiadavka transformuje na tento zoznam.
V úvodnom príkaze je každá položka v zozname priradená k úlohe. role určuje, akú dôležitosť má priradený obsah, a je jednou z nasledujúcich hodnôt (v klesajúcom poradí priority): system, developer, user, assistant.
API Responses(otvorí sa v novom okne) prijíma JSON payload s mnohými parametrami. Zameriame sa na tieto tri oblasti:
pokyny(otvorí sa v novom okne): systémová (alebo vývojárska) správa vložená do kontextu modelutools(otvorí sa v novom okne): zoznam nástrojov, ktoré môže model volať počas generovania odpovedeinput(otvorí sa v novom okne): zoznam textových, obrázkových alebo súborových vstupov pre model
V Codexe sa pole instructions načítava zo súboru model_instructions_file(otvorí sa v novom okne) v ~/.codex/config.toml, ak je zadaný. Inak sa použijú base_instructions priradené k modelu(otvorí sa v novom okne). Pokyny špecifické pre model sa nachádzajú v repozitári Codex a sú zahrnuté v CLI (napr., gpt-5.2-codex_prompt.md(otvorí sa v novom okne)).
Pole tools obsahuje zoznam definícií nástrojov, ktoré zodpovedajú schéme definovanej rozhraním Responses API. Pre Codex to zahŕňa nástroje poskytované Codex CLI, nástroje poskytované Responses API, ktoré by mali byť sprístupnené Codexu, ako aj nástroje poskytované používateľom, zvyčajne prostredníctvom serverov MCP:
Nakoniec je pole input v JSON náklade zoznamom položiek. Codex vkladá nasledujúce položky(otvorí sa v novom okne) do vstupu pred pridaním používateľskej správy:
1. Správa s role=developer, ktorá opisuje sandbox, ktorý sa vzťahuje iba na Codexom poskytovaný shell nástroj, definovaný v časti tools. To znamená, že iné nástroje, ako napríklad tie poskytované zo serverov MCP, nie sú izolované nástrojom Codex a sú zodpovedné za presadzovanie vlastných ochranných opatrení.
Správa je zostavená zo šablóny, kde kľúčové časti obsahu pochádzajú z úryvkov Markdownu zabalených do Codex CLI, ako napríklad workspace_write.md(otvorí sa v novom okne) a on_request.md(otvorí sa v novom okne):
2. (Voliteľné) Správa s role=developer, ktorej obsahom je hodnota developer_instructions prečítaná zo súboru config.toml používateľa.
3. (Voliteľné) Správa s role=user, ktorej obsahom sú „používateľské pokyny“, ktoré nepochádzajú z jedného súboru, ale sú zhromaždené z viacerých zdrojov(otvorí sa v novom okne). Vo všeobecnosti sa konkrétnejšie pokyny objavia neskôr:
- Obsah súborov
AGENTS.override.mdaAGENTS.mdv$CODEX_HOME - S výhradou limitu (predvolene 32 KiB) prehľadaj každý priečinok od koreňa Git/projektu
cwd(ak existuje) až po samotnýcwd: pridaj obsah ktoréhokoľvek súboruAGENTS.override.md,AGENTS.mdalebo akýkoľvek názov súboru určený vproject_doc_fallback_filenames v config.toml - Ak boli nakonfigurované nejaké zručnosti(otvorí sa v novom okne):
- krátky úvod o zručnostiach
- metadáta zručností(otvorí sa v novom okne) pre každú zručnosť
- sekcia o používaní zručností(otvorí sa v novom okne)
4. Správa s role=user, ktorá opisuje miestne prostredie, v ktorom agent práve pracuje. Toto určuje aktuálny pracovný adresár a používateľský shell(otvorí sa v novom okne):
Po tom, ke´d Codex dokončí všetky vyššie uvedené výpočty na inicializáciu vstupu, pripojí správu používateľa na začatie konverzácie.
Predchádzajúce príklady sa zameriavali na obsah každej správy, ale všimni si, že každý prvok vstupu je objekt JSON s type, role(otvorí sa v novom okne) a content takto:
Keď Codex zostaví kompletný JSON payload na odoslanie do Responses API, vykoná HTTP POST požiadavku s hlavičkou Authorization podľa toho, ako je koncový bod Responses API nakonfigurovaný v ~/.codex/config.toml (ak sú zadané, pridajú sa aj ďalšie HTTP hlavičky a parametre dotazu).
Keď server OpenAI Responses API prijme požiadavku, použije JSON na odvodenie príkazu pre model nasledovne (pre istotu, vlastná implementácia Responses API by sa mohla rozhodnúť inak):
Ako vidíš, poradie prvých troch položiek v príkaze určuje server, nie klient. To znamená, že z týchto troch položiek je serverom riadený iba obsah system message, keďže tools a instructions určuje klient. Nasleduje input z JSON payloadu na dokončenie príkazu.
Teraz, keď máme náš príkaz, sme pripravení otestovať model.
Táto požiadavka HTTP na rozhranie API Responses iniciuje prvé „kolo“ konverzácie v Codex. Server odpovedá prúdom udalostí odosielaných serverom (SSE(otvorí sa v novom okne)). Údaje data každej udalosti sú JSON payload s "type", ktorý začína s "response", čo môže vyzerať napríklad takto (úplný zoznam udalostí nájdeš v našich dokumentoch k API(otvorí sa v novom okne)):
Codex spracúva tok udalostí(otvorí sa v novom okne) a znovu ich publikuje ako interné objekty udalostí, ktoré môže využiť klient. Udalosti ako response.output_text.delta sa používajú na podporu streamovania v používateľskom rozhraní, zatiaľ čo iné udalosti ako response.output_item.added sa transformujú na objekty, ktoré sa pridávajú k input pre následné volania API Responses.
Predpokladajme, že prvá požiadavka na API Responses obsahuje dve udalosti response.output_item.done: jednu s type=uvažovanie a jednu s type=function_call. Tieto udalosti musia byť reprezentované v poli input JSON, keď model znova dotazujeme s odpoveďou na volanie nástroja:
Výsledný príkaz na vzorkovanie modelu v rámci následného dopytu by vyzeral takto:
Najmä si všimni, že starý príkaz je presnou predponou nového príkazu. Je to zámerné, pretože to robí následné požiadavky oveľa efektívnejšími, pretože nám to umožňuje využiť caching príkazu (o čom budeme hovoriť v ďalšej časti o výkone).
Keď sa pozrieme späť na náš prvý diagram agentovej slučky, vidíme, že medzi inferenciou a volaním nástrojov môže byť veľa iterácií. Príkaz môže pokračovať v raste, až kým napokon nedostaneme správu asistenta, ktorá naznačí koniec ťahu:
V rozhraní Codex CLI zobrazujeme používateľovi správu asistenta a zameriavame sa na kompozitora, aby sme používateľovi naznačili, že je na „ťahu“, aby pokračoval v konverzácii. Ak používateľ odpovie, správa asistenta z predchádzajúceho ťahu a nová správa používateľa sa musia pripojiť k inputu v požiadavke rozhrania Responses API, aby sa začal nový ťah:
Opäť, pretože pokračujeme v konverzácii, dĺžka inputu, ktorý posielame do Responses API, sa neustále zvyšuje:
Pozrime sa, čo tento neustále sa rozrastajúci príkaz znamená pre výkon.
Možno sa pýtaš sám seba: „Počkať, nie je slučka agenta kvadratická z hľadiska množstva JSON odoslaného do Responses API počas konverzácie?“ A mal by si pravdu. Hoci API Responses API podporuje voliteľný parameter previous_response_id(otvorí sa v novom okne) na zmiernenie tohto problému ho dnes Codex nepoužíva predovšetkým preto, aby zachoval požiadavky úplne bezstavové a aby podporoval konfigurácie nulového uchovávania údajov (ZDR).
Vyhnutím sa používaniu previous_response_id zjednodušuje prácu poskytovateľovi rozhrania Responses API, pretože zabezpečuje, že každá požiadavka je bezstavová. To tiež uľahčuje poskytovanie podpory zákazníkom, ktorí sa rozhodli pre nulové uchovávanie údajov (ZDR)(otvorí sa v novom okne), pretože ukladanie údajov potrebných na podporu previous_response_id by bolo v rozpore so ZDR. Upozorňujeme, že zákazníci ZDR nemusia obetovať možnosť využívať proprietárne správy o uvažovaní z predchádzajúcich ťahov, pretože súvisiaci encrypted_content je možné dešifrovať na serveri. (OpenAI uchováva dešifrovací kľúč zákazníka ZDR, ale nie jeho dáta.) Pozrite si PR #642(otvorí sa v novom okne) a #1641(otvorí sa v novom okne) pre súvisiace zmeny v Codexe na podporu ZDR.
Vo všeobecnosti náklady na vzorkovanie modelu prevyšujú náklady na sieťovú prevádzku, čím sa vzorkovanie stáva hlavným cieľom našich snáh o efektívnosť. Preto je ukladanie príkazov do vyrovnávacej pamäte také dôležité, pretože umožňuje opätovné použitie výpočtov z predchádzajúceho volania inferencie. Keď máme zásahy do vyrovnávacej pamäte, vzorkovanie modelu je lineárne namiesto kvadratického. Naša dokumentácia k ukladaniu príkazov do vyrovnávacej pamäte (otvorí sa v novom okne)to vysvetľuje podrobnejšie:
Zásahy do vyrovnávacej pamäte sú možné iba pri presných zhodách prefixu v rámci príkazu. Aby si využil výhody ukladania do vyrovnávacej pamäte, umiestni statický obsah, ako sú inštrukcie a príklady, na začiatok svojho príkazu, a variabilný obsah, ako sú informácie špecifické pre používateľa, na koniec. To sa vzťahuje aj na obrázky a nástroje, ktoré musia byť medzi jednotlivými požiadavkami identické.
S ohľadom na to sa pozrime, aké typy operácií by mohli v Codexe spôsobiť „vynechanie vyrovnávacej pamäte“:
- Zmena dostupných
nástrojovpre model uprostred konverzácie. - Zmena
modelu, ktorý je cieľom požiadavky na Responses API (v praxi to zmení tretiu položku v pôvodnom príkaze, pretože obsahuje inštrukcie špecifické pre model). - Zmena konfigurácie sandboxu, režimu schvaľovania alebo aktuálneho pracovného adresára.
Codex tím musí byť dôsledný pri zavádzaní nových funkcií v Codex CLI, ktoré by mohli ohroziť ukladanie príkazov do vyrovnávacej pamäte. Napríklad naša počiatočná podpora pre nástroje MCP zaviedla chybu, pri ktorej sme nedokázali vymenovať nástroje v konzistentnom poradí(otvorí sa v novom okne), čo spôsobovalo vynechania vyrovnávacej pamäte. Všimni si, že nástroje MCP môžu byť obzvlášť zložité, pretože servery MCP môžu za chodu meniť zoznam nástrojov, ktoré poskytujú, prostredníctvom oznámenia notifications/tools/list_changed(otvorí sa v novom okne). Rešpektovanie tohto oznámenia uprostred dlhej konverzácie môže spôsobiť nákladný výpadok vyrovnávacej pamäte.
Ak je to možné, zmeny konfigurácie, ktoré nastanú uprostred konverzácie, riešime pripojením novej správy k inputu, aby odrážala zmenu, namiesto úpravy predchádzajúcej správy:
- Ak sa zmení konfigurácia sandboxu alebo režim schvaľovania, vložíme(otvorí sa v novom okne) novú správu
role=developers rovnakým formátom ako pôvodná položka<permissions instructions>. - Ak sa zmení aktuálny pracovný adresár, vložíme(otvorí sa v novom okne) novú správu
role=users rovnakým formátom ako pôvodný<environment_context>.
Vynakladáme veľké úsilie, aby sme zabezpečili zásahy do vyrovnávacej pamäte kvôli zlepšeniu výkonu. Musíme spravovať ešte jeden kľúčový zdroj: kontextové okno.
Našou všeobecnou stratégiou, ako sa vyhnúť vyčerpaniu kontextového okna, je stlačiť konverzáciu, keď počet tokenov prekročí určitú prahovú hodnotu. Konkrétne nahrádzame input novým, menším zoznamom položiek, ktorý reprezentuje konverzáciu, čo agentovi umožňuje pokračovať s pochopením toho, čo sa doteraz stalo. Skorá implementácia kompakcie(otvorí sa v novom okne) vyžadovala, aby používateľ manuálne spustil príkaz /compact, ktorý by poslal dopyt do rozhrania Responses API s použitím existujúcej konverzácie a vlastných pokynov na zhrnutie(otvorí sa v novom okne). Codex použil výslednú správu asistenta obsahujúcu súhrn ako nový input(otvorí sa v novom okne) pre ďalšie kolá konverzácie.
Odvtedy sa rozhranie Responses API vyvinulo tak, aby podporovalo špeciálny /responses/compact koncový bod(otvorí sa v novom okne), ktorý efektívnejšie vykonáva kompakciu. Vráti zoznam položiek(otvorí sa v novom okne), ktoré môžeš použiť namiesto predchádzajúceho inputu na pokračovanie v konverzácii a zároveň uvoľniť kontextové okno. Tento zoznam obsahuje špeciálnu položku type=compaction s nepriehľadnou položkou encrypted_content, ktorá uchováva latentné porozumenie modelu pôvodnej konverzácie. Teraz Codex automaticky používa tento koncový bod na zhutnenie konverzácie, keď sa prekročí auto_compact_limit(otvorí sa v novom okne).
Predstavili sme slučku agenta Codex a prešli sme si, ako Codex vytvára a spravuje svoj kontext pri dopytovaní modelu. Počas procesu sme zdôraznili praktické aspekty a osvedčené postupy, ktoré sú relevantné pre každého, kto vytvára slučku agenta nad rozhraním Responses API.
Hoci slučka agenta poskytuje základ pre Codex, je to len začiatok. V nadchádzajúcich príspevkoch sa ponoríme do architektúry CLI. Preskúmame, ako je implementované používanie nástrojov a podrobnejšie sa pozrieme na model sandboxovania Codexu.


