Ang Codex CLI(magbubukas sa bagong window) ay ang aming cross-platform na lokal na software agent, na idinisenyo upang makagawa ng mataas na kalidad at maaasahang mga pagbabago sa software habang ligtas at mahusay na tumatakbo sa inyong machine. Napakarami na naming natutunan tungkol sa kung paano bumuo ng world-class na software agent mula nang una naming inilunsad ang CLI noong Abril. Para himayin ang mga insight na iyon, ito ang unang post sa isang tuloy-tuloy na serye kung saan tatalakayin namin ang iba't ibang aspeto ng kung paano gumagana ang Codex, pati na rin ang mga aral mula sa mga hamong kinaharap namin. (Para sa mas detalyadong view kung paano binuo ang Codex CLI, tingnan ang aming open source repository sa https://github.com/openai/codex(magbubukas sa bagong window). Marami sa mas maliliit na detalye ng aming mga desisyon sa disenyo ay naitala sa mga isyu sa GitHub at mga pull request kung nais ninyong matuto pa.)
Para magsimula, magfo-focus tayo sa agent loop, na siyang pangunahing lohika sa Codex CLI na responsable sa pag-orchestrate ng interaksyon sa pagitan ng user, ng modelo, at ng mga tool na ini-invoke ng modelo para magsagawa ng makabuluhang gawain sa software. Inaasahan namin na ang post na ito ay magbibigay sa inyo ng magandang view sa papel na ginagampanan ng aming agent (o “harness”) sa paggamit ng isang LLM.
Bago tayo sumabak sa pagtalakay, isang maikling note tungkol sa terminolohiya: sa OpenAI, ang “Codex” ay sumasaklaw sa isang suite ng mga iniaalok na software agent, kabilang ang Codex CLI, Codex Cloud, at ang Codex VS Code extension. Ang post na ito ay nakatuon sa Codex harness, na nagbibigay ng pangunahing agent loop at lohika ng pagpapatupad na nagsisilbing pundasyon ng lahat ng karanasan sa Codex at inilalantad sa pamamagitan ng Codex CLI. Para maging mas madali rito, gagamitin natin ang mga terminong “Codex” at “Codex CLI” nang salit-salitan.
Sa sentro ng bawat AI agent ay may tinatawag na “agent loop.” Ganito ang hitsura ng isang pinasimpleng ilustrasyon ng agent loop:
Para magsimula, kinukuha ng agent ang input mula sa user upang isama sa hanay ng mga tekstwal na tagubilin na inihahanda nito para sa modelo na tinatawag na isang prompt.
Ang susunod na hakbang ay i-query ang modelo sa pamamagitan ng pagpapadala ng ating mga tagubilin at paghingi na mag-generate ito ng tugon, isang prosesong kilala bilang inference. Sa panahon ng inference, ang tekstong prompt ay unang isinasalin sa isang sequence ng input na mga token(magbubukas sa bagong window)—mga integer na nai-index sa bokabularyo ng modelo. Pagkatapos, ang mga token na ito ay ginagamit upang i-sample ang modelo, na nagreresulta sa isang bagong hanay ng mga output token.
Ang mga output token ay isinasalin pabalik sa teksto, na siyang nagiging tugon ng modelo. Dahil ang mga token ay nabubuo nang paunti-unti, maaaring mangyari ang pagsasaling ito habang tumatakbo ang modelo, kaya't maraming mga application na nakabatay sa LLM ang nagpapakita ng streaming output. Sa praktika, ang inference ay karaniwang nakapaloob sa likod ng isang API na gumagana sa text, na iniaalis ang mga detalye ng pag-tokenize.
Bilang resulta ng hakbang na inference, ang modelo ay maaaring (1) bumuo ng panghuling tugon sa orihinal na input ng user, o (2) humiling ng isang tool call na inaasahang isasagawa ng agent (hal., “patakbuhin ang ls at iulat ang output”). Sa kaso ng (2), isasagawa ng agent ang tool call at idaragdag ang output nito sa orihinal na prompt. Ginagamit ang output na ito upang mag-generate ng bagong input na ginagamit upang muling i-query ang modelo; pagkatapos, maaari nang isaalang-alang ng agent ang bagong impormasyong ito at subukang muli.
Umuulit ang prosesong ito hanggang sa tumigil ang modelo sa paglalabas ng mga tool call at sa halip ay gumawa ng mensahe para sa user (tinutukoy bilang isang assistant message sa mga modelo ng OpenAI). Sa maraming sitwasyon, direktang sinasagot ng mensaheng ito ang orihinal na hiling ng user, ngunit maaari din itong maging follow-up na tanong para sa user.
Dahil ang agent ay maaaring magsagawa ng mga tool call na nagpapabago sa lokal na environment, ang “output” nito ay hindi limitado sa mensahe ng assistant. Sa maraming sitwasyon, ang pangunahing output ng isang software agent ay ang code na isinusulat o ine-edit nito sa inyong machine. Gayunpaman, ang bawat turn ay palaging nagtatapos sa isang mensahe ng assistant—gaya ng “Idinagdag ko ang architecture.md na hiniling ninyo”—na nagsasaad ng isang estado ng pagtatapos sa agent loop. Mula sa pananaw ng agent, kumpleto na ang trabaho nito at bumabalik ang kontrol sa user.
Ang paglalakbay mula sa input ng user hanggang sa tugon ng agent na ipinapakita sa diagram ay tinutukoy bilang isang ikot ng pag-uusap (thread sa Codex). Bagama't ang ikot ng pag-uusap na ito ay maaaring may kasamang maraming pag-uulit sa pagitan ng inference ng modelo at mga tool call. Sa tuwing magpapadala ka ng bagong mensahe sa umiiral na pag-uusap, isinasama ang kasaysayan ng pag-uusap bilang bahagi ng prompt para sa bagong ikot, na kinabibilangan ng mga mensahe at mga tawag sa tool mula sa mga naunang ikot:
Ibig sabihin nito, habang humahaba ang pag-uusap, humahaba rin ang prompt na ginagamit upang kumuha ng sample mula sa modelo. Mahalaga ang habang ito dahil ang bawat modelo ay may context window, na siyang pinakamataas na bilang ng mga token na magagamit nito para sa isang inference call. Tandaan na kasama sa window na ito ang parehong input at output token. Gaya ng maaari ninyong maisip, maaaring magpasya ang isang agent na gumawa ng daan-daang tool call sa iisang turn, na posibleng makaubos sa context window. Dahil dito, ang pamamahala sa context window ay isa sa maraming responsibilidad ng agent. Ngayon, sumisid tayo sa talakayan upang makita kung paano pinapatakbo ng Codex ang agent loop.
Nagpapadala ang Codex CLI ng mga HTTP request sa Responses API(magbubukas sa bagong window) upang magsagawa ng inference ng modelo. Susuriin natin kung paano dumadaloy ang impormasyon sa Codex, na gumagamit ng Responses API upang patakbuhin ang agent loop.
Ang endpoint ng Responses API na ginagamit ng Codex CLI ay maaaring i-configure(magbubukas sa bagong window), kaya maaari itong gamitin sa anumang endpoint na nagpapatupad ng Responses API(magbubukas sa bagong window):
- Kapag gumagamit ng ChatGPT login(magbubukas sa bagong window) gamit ang Codex CLI, ginagamit nito ang
https://chatgpt.com/backend-api/codex/responsesbilang endpoint - Kapag gumagamit ng API-key authentication(magbubukas sa bagong window) sa mga modelo na naka-host sa OpenAI, ginagamit nito ang
https://api.openai.com/v1/responsesbilang endpoint - Kapag pinapatakbo ang Codex CLI gamit ang
--osspara gamitin ang gpt-oss kasama ang ollama 0.13.4+(magbubukas sa bagong window) o LM Studio 0.3.39+(magbubukas sa bagong window), nagde-default ito sahttp://localhost:11434/v1/responsesna tumatakbo nang lokal sa inyong computer - Maaaring gamitin ang Codex CLI kasama ng Responses API na hino-host ng isang cloud provider tulad ng Azure
I-explore natin kung paano nililikha ng Codex ang prompt para sa unang inference call sa isang pag-uusap.
Bilang isang end user, hindi ninyo tutukuyin ang prompt na ginagamit para i-sample ang modelo nang eksakto kapag nag-query kayo sa Responses API. Sa halip, tutukuyin ninyo ang iba't ibang uri ng input bilang bahagi ng inyong query, at ang server ng Responses API ang magpapasya kung paano iistruktura ang impormasyong ito sa isang prompt na idinisenyong gamitin ng modelo. Maaari ninyong isipin ang prompt bilang isang “listahan ng mga item”; ipapaliwanag ng seksyong ito kung paano nagiging ganoong listahan ang inyong query.
Sa paunang prompt, ang bawat item sa listahan ay may kaugnay na role. Ipinapahiwatig ng role kung gaano dapat kabigat ang kaugnay na nilalaman at isa ito sa mga sumusunod na value (sa pababang pagkakasunod-sunod ng priyoridad): system, developer, user, assistant.
Ang Responses API(magbubukas sa bagong window) ay tumatanggap ng JSON payload na may maraming parameter. Magfo-focus tayo sa tatlong ito:
instructions(magbubukas sa bagong window): mensahe mula sa system (o developer) na ipinasok sa konteksto ng modelotools(magbubukas sa bagong window): isang listahan ng tools na maaaring tawagin ng modelo habang bumubuo ng tugoninput(magbubukas sa bagong window): isang listahan ng mga input na teksto, larawan, o file sa modelo
Sa Codex, ang field na instructions ay binabasa mula sa model_instructions_file(magbubukas sa bagong window) sa ~/.codex/config.toml, kung tinukoy; kung hindi, ginagamit ang base_instructions na nauugnay sa isang modelo(magbubukas sa bagong window). Ang mga tagubiling partikular sa modelo ay matatagpuan sa Codex repo at isinama sa CLI (hal., gpt-5.2-codex_prompt.md(magbubukas sa bagong window)).
Ang field na tools ay isang listahan ng mga depinisyon ng tool na sumusunod sa isang schema na itinatakda ng Responses API. Para sa Codex, kabilang dito ang mga tool na ibinibigay ng Codex CLI, mga tool na ibinibigay ng Responses API na dapat gawing available sa Codex, pati na rin ang mga tool na ibinibigay ng user, karaniwang sa pamamagitan ng mga MCP server:
Panghuli, ang input na field ng JSON payload ay isang listahan ng mga item. Ini-insert ng Codex ang mga sumusunod na item(magbubukas sa bagong window) sa input bago idagdag ang mensahe ng user:
1. Isang mensahe na may role=developer na naglalarawan sa sandbox na naaangkop lamang sa Codex-provided shell tool na tinukoy sa seksyong tools. Ibig sabihin, ang iba pang mga tool, gaya ng mga ibinibigay mula sa mga MCP server, ay hindi na-sandbox ng Codex at responsable sa pagpapatupad ng sarili nilang mga guardrail.
Ang mensahe ay binubuo mula sa isang template kung saan ang mga pangunahing bahagi ng nilalaman ay nagmumula sa mga snippet ng Markdown na naka-bundle sa Codex CLI, tulad ng workspace_write.md(magbubukas sa bagong window) at on_request.md(magbubukas sa bagong window):
2. (Opsyonal) Isang mensahe na may role=developer na ang nilalaman ay ang value na developer_instructions na binasa mula sa config.toml file ng user.
3. (Opsyonal) Isang mensahe na may role=user na ang nilalaman ay ang “mga tagubilin ng user,” na hindi nagmumula sa iisang file kundi pinagsama-sama mula sa iba't ibang source(magbubukas sa bagong window). Sa pangkalahatan, mas tiyak na mga tagubilin ang lumilitaw sa bandang huli:
- Mga nilalaman ng
AGENTS.override.mdatAGENTS.mdsa$CODEX_HOME - Napapailalim sa limitasyon (32 KiB, bilang default), tingnan ang bawat folder mula sa Git/project root ng
cwd(kung umiiral ito) hanggang sa mismongcwd: idagdag ang mga nilalaman ng alinman saAGENTS.override.md,AGENTS.md, o anumang filename na tinukoy ngproject_doc_fallback_filenames sa config.toml - Kung may anumang kasanayan(magbubukas sa bagong window) na na-configure:
- isang maikling preamble tungkol sa mga kasanayan
- ang metadata ng kasanayan(magbubukas sa bagong window) para sa bawat kasanayan
- isang seksyon tungkol sa kung paano gamitin ang mga kasanayan(magbubukas sa bagong window)
4. Isang mensahe na may role=user na naglalarawan sa lokal na environment kung saan kasalukuyang nagpapatakbo ang agent. Ito ay tumutukoy sa kasalukuyang working directory at sa shell ng user(magbubukas sa bagong window):
Kapag natapos na ng Codex ang lahat ng nabanggit na komputasyon para i-initialize ang input, idaragdag nito ang mensahe ng user upang simulan ang pag-uusap.
Nakatuon ang mga naunang halimbawa sa nilalaman ng bawat mensahe, ngunit tandaan na ang bawat elemento ng input ay isang JSON object na may type, role(magbubukas sa bagong window), at content tulad ng sumusunod:
Kapag nabuo na ng Codex ang buong JSON payload na ipapadala sa Responses API, isasagawa nito ang HTTP POST request na may Authorization header depende sa kung paano naka-configure ang Responses API endpoint sa ~/.codex/config.toml (idinadagdag ang mga karagdagang HTTP header at query parameter kung tinukoy).
Kapag natanggap ng isang OpenAI Responses API server ang kahilingan, ginagamit nito ang JSON upang makuha ang prompt para sa modelo tulad ng sumusunod (upang makasiguro, maaaring gumawa ng ibang pagpili ang isang custom na implementasyon ng Responses API):
Gaya ng inyong nakikita, ang pagkakasunod-sunod ng unang tatlong item sa prompt ay itinatakda ng server, hindi ng client. Gayunpaman, sa tatlong item na iyon, tanging ang nilalaman ng system message ang kontrolado rin ng server, dahil ang tools at instructions ay tinutukoy ng client. Ang mga ito ay sinusundan ng input mula sa JSON payload upang makumpleto ang prompt.
Ngayong mayroon na tayong prompt, handa na tayong kumuha ng sample mula sa modelo.
Ang HTTP request na ito sa Responses API ay nagpapasimula ng unang “turn” ng isang pag-uusap sa Codex. Ang server ay sumasagot gamit ang isang Server-Sent Events (SSE(magbubukas sa bagong window)) stream. Ang data ng bawat event ay isang JSON payload na may "type" na nagsisimula sa "response", na maaaring ganito ang hitsura (ang kumpletong listahan ng mga event ay makikita sa aming API docs(magbubukas sa bagong window)):
Kinokonsumo ng Codex ang daloy ng mga kaganapan(magbubukas sa bagong window) at muling inilalathala ang mga ito bilang mga object ng internal na event na maaaring gamitin ng isang client. Ang mga event tulad ng response.output_text.delta ay ginagamit upang suportahan ang streaming sa UI, samantalang ang iba pang mga event tulad ng response.output_item.added ay ginagawang mga object na idinadagdag sa input para sa mga susunod na Responses API call.
Ipagpalagay na ang unang request sa Responses API ay may kasamang dalawang response.output_item.done na mga event: isa na may type=pangangatwiran at isa na may type=function_call. Ang mga event na ito ay dapat na irepresenta sa input field ng JSON kapag nag-query kaming muli sa modelo gamit ang tugon sa tool call:
Ang resultang prompt na ginamit upang i-sample ang modelo bilang bahagi ng kasunod na query ay magiging ganito:
Sa partikular, pansinin kung paanong ang lumang prompt ay eksaktong prefix ng bagong prompt. Sinadya ito, dahil ginagawa nitong mas mahusay ang mga kasunod na kahilingan dahil nagbibigay-daan ito sa aming samantalahin ang pag-cache ng prompt (na tatalakayin natin sa susunod na seksyon tungkol sa performance).
Sa pagbabalik-tanaw sa aming unang diagram ng agent loop, nakikita namin na maaaring magkaroon ng maraming pag-uulit sa pagitan ng inference at tool calling. Maaaring patuloy na humaba ang prompt hanggang sa wakas ay makatanggap kami ng mensahe mula sa assistant na nagpapahiwatig ng pagtatapos ng turn:
Sa Codex CLI, ipinapakita namin ang mensahe ng assistant sa user at itinututok ang composer upang ipahiwatig sa user na “turn” na nila para ipagpatuloy ang pag-uusap. Kung tumugon ang user, parehong ang mensahe ng assistant mula sa nakaraang turn at ang bagong mensahe ng user ang dapat idagdag sa input sa kahilingan ng Responses API upang simulan ang bagong turn:
Muli, dahil ipinagpapatuloy natin ang isang pag-uusap, patuloy na humahaba ang input na ipinapadala natin sa Responses API:
Suriin natin ang epekto ng patuloy na humahabang prompt na ito sa performance.
Maaaring tinatanong ninyo ang inyong sarili, “Sandali, hindi ba quadratic ang agent loop pagdating sa dami ng JSON na ipinapadala sa Responses API sa kabuuan ng pag-uusap?” At tama kayo. Bagama't sinusuportahan ng Responses API ang isang opsyonal na parameter na previous_response_id(magbubukas sa bagong window) upang mabawasan ang isyung ito, hindi ito ginagamit ng Codex sa kasalukuyan, pangunahin upang panatilihing ganap na stateless ang mga kahilingan at upang suportahan ang mga configuration ng Zero na Pagpapanatili ng Data (ZDR).
Ang pag-iwas sa previous_response_id ay nagpapasimple ng mga bagay para sa provider ng Responses API dahil tinitiyak nito na ang bawat kahilingan ay walang estado. Ginagawa rin nitong madali ang pagsuporta sa mga customer na nag-opt in sa Zero na Pagpapanatili ng Data (ZDR)(magbubukas sa bagong window), dahil ang pagso-store ng data na kinakailangan para masuportahan ang previous_response_id ay magiging salungat sa ZDR. Tandaan na hindi isinusuko ng mga customer ng ZDR ang kakayahang makinabang mula sa mga proprietary na mensahe ng pangangatwiran mula sa mga naunang turn, dahil ang kaugnay na encrypted_content ay maaaring i-decrypt sa server. (Pinananatili ng OpenAI ang decryption key ng isang ZDR customer, ngunit hindi ang kanilang data.) Tingnan ang mga PR #642(magbubukas sa bagong window) at #1641(magbubukas sa bagong window) para sa mga kaugnay na pagbabago sa Codex upang suportahan ang ZDR.
Sa pangkalahatan, ang gastos sa pag-sample ng modelo ay mas mataas kaysa sa gastos ng network traffic, kaya't ang pag-sample ang pangunahing target ng aming mga pagsisikap sa kahusayan. Ito ang dahilan kung bakit napakahalaga ng pag-cache ng prompt, dahil nagbibigay-daan ito sa amin na muling magamit ang computation mula sa nakaraang inference call. Kapag nakakakuha tayo ng mga hit sa cache, ang pag-sample sa modelo ay linear sa halip na quadratic. Ipinapaliwanag ito nang mas detalyado sa aming dokumentasyon sa pag-cache ng prompt(magbubukas sa bagong window):
Ang mga hit sa cache ay posible lamang para sa mga eksaktong tugma sa prefix sa isang prompt. Para mapakinabangan ang mga benepisyo ng pag-cache, ilagay ang static na nilalaman tulad ng mga tagubilin at halimbawa sa simula ng inyong prompt, at ilagay ang variable na nilalaman, tulad ng impormasyong partikular sa user, sa dulo. Nalalapat din ito sa mga imahe at mga tool, na kailangang magkapareho sa pagitan ng mga kahilingan.
Bilang alam na natin ito, isaalang-alang natin kung anong mga uri ng operasyon ang maaaring magdulot ng “cache miss” sa Codex:
- Pagbabago ng
toolsna available sa modelo sa kalagitnaan ng pag-uusap. - Pagbabago ng
modelona target ng kahilingan sa Responses API (sa praktika, binabago nito ang ikatlong item sa orihinal na prompt, dahil naglalaman ito ng mga tagubilin na partikular sa modelo). - Pagbabago sa configuration ng sandbox, mode ng pag-apruba, o kasalukuyang direktoryo ng trabaho.
Dapat maging masipag ang Codex team kapag nagpapakilala ng mga bagong feature sa Codex CLI na maaaring makakompromiso sa pag-cache ng prompt. Bilang halimbawa, ang aming paunang suporta para sa mga tool ng MCP ay nagpakilala ng isang bug kung saan nabigo kaming i-enumerate ang mga tool sa pare-parehong pagkakasunod-sunod(magbubukas sa bagong window), na nagdulot ng mga cache miss. Tandaan na ang mga tool ng MCP ay maaaring maging partikular na mahirap dahil maaaring baguhin ng mga server ng MCP ang listahan ng mga tool na kanilang ibinibigay nang biglaan sa pamamagitan ng isang notifications/tools/list_changed(magbubukas sa bagong window) na abiso. Ang paggalang sa abisong ito sa kalagitnaan ng mahabang pag-uusap ay maaaring magdulot ng magastos na cache miss.
Kapag posible, pinapangasiwaan namin ang mga pagbabago sa configuration na nangyayari sa kalagitnaan ng pag-uusap sa pamamagitan ng pagdaragdag ng bagong mensahe sa input upang ipakita ang pagbabago sa halip na baguhin ang mas naunang mensahe:
- Kung magbago ang configuration ng sandbox o approval mode, mag-i-insert(magbubukas sa bagong window) kami ng bagong mensaheng
role=developerna may kaparehong format gaya ng orihinal na item na<permissions instructions>. - Kung magbago ang kasalukuyang working directory, mag-i-insert(magbubukas sa bagong window) kami ng bagong mensaheng
role=userna may parehong format gaya ng orihinal na<environment_context>.
Lubos kaming nagsusumikap upang matiyak ang mga cache hit para sa performance. May isa pang mahalagang resource na kailangan naming pamahalaan: ang context window.
Ang aming pangkalahatang estratehiya upang maiwasan ang pagkaubos ng context window ay i-compact ang pag-uusap kapag lumampas na ang bilang ng mga token sa isang tiyak na threshold. Partikular na, pinapalitan namin ang input ng isang bago at mas maliit na listahan ng mga item na kumakatawan sa pag-uusap, na nagbibigay-daan sa agent na magpatuloy nang may pag-unawa sa kung ano ang nangyari hanggang sa puntong ito. Ang isang maagang implementasyon ng compaction(magbubukas sa bagong window) ay nangangailangan na mano-manong i-invoke ng user ang /compact na command, na magku-query sa Responses API gamit ang kasalukuyang pag-uusap kasama ang mga custom na tagubilin para sa pagbubuod(magbubukas sa bagong window). Ginamit ng Codex ang nagreresultang mensahe ng assistant na naglalaman ng buod bilang bagong input(magbubukas sa bagong window) para sa mga susunod na pag-uusap.
Mula noon, umunlad ang Responses API upang suportahan ang isang espesyal na /responses/compact endpoint(magbubukas sa bagong window) na nagsasagawa ng mas mahusay na compaction. Nagbabalik ito ng isang listahan ng mga item(magbubukas sa bagong window) na maaaring gamitin kapalit ng naunang input upang ipagpatuloy ang pag-uusap habang binabakante ang context window. Kasama sa listahang ito ang isang espesyal na type=compaction item na may opaque na encrypted_content item na pinapanatili ang latent na pag-unawa ng modelo sa orihinal na pag-uusap. Ngayon, awtomatikong ginagamit ng Codex ang endpoint na ito upang i-compact ang pag-uusap kapag nalampasan ang auto_compact_limit(magbubukas sa bagong window).
Ipinakikilala namin ang Codex agent loop at tinalakay kung paano binubuo at pinamamahalaan ng Codex ang konteksto nito kapag nagku-query ng isang modelo. Sa kalagitnaan nito, na-highlight namin ang mga praktikal na pagsasaalang-alang at pinakamahuhusay na kagawian na naaangkop sa sinumang bumubuo ng agent loop batay sa Responses API.
Bagama’t nagbibigay ang agent loop ng pundasyon para sa Codex, simula pa lamang ito. Sa mga darating na post, susuriin natin ang arkitektura ng CLI, ie-explore kung paano ipinatutupad ang paggamit ng mga tool, at masusing titingnan ang modelo ng sandboxing ng Codex.


