Codex CLI(hapet në një dritare të re) është agjenti ynë lokal i softuerit ndër-platformor, i projektuar për të prodhuar ndryshime softuerike me cilësi të lartë dhe të besueshme, duke operuar në mënyrë të sigurt dhe efikase në pajisjen tënde. Kemi mësuar jashtëzakonisht shumë rreth mënyrës se si të ndërtojmë një agjent softuerik të nivelit botëror që nga hera e parë kur lançuam CLI-në në prill. Për t’i shpalosur ato njohuri, ky është postimi i parë në një seri të vazhdueshme ku do të eksplorojmë aspekte të ndryshme të funksionimit të Codex-it, si edhe mësime të fituara me shumë mund. (Për një pamje edhe më të detajuar se si ndërtohet Codex CLI, shiko depon tonë me burim të hapur në https://github.com/openai/codex(hapet në një dritare të re). Shumë nga detajet më të vogla të vendimeve tona të dizajnit janë dokumentuar në çështje dhe kërkesë pull në GitHub, nëse dëshiron të mësosh më shumë.)
Për të filluar, do të fokusohemi te cikli i agjentit, i cili është logjika kryesore në Codex CLI që orkestron ndërveprimin midis përdoruesit, modelit dhe mjeteve që modeli përdor për të kryer punë kuptimplota softuerike. Shpresojmë që ky postim të të japë një pamje të qartë mbi rolin që luan agjenti ynë (ose “harness”) në përdorimin e një LLM.
Para se t'i analizojmë, një shënim i shpejtë mbi terminologjinë: te OpenAI, “Codex” përfshin një suitë ofertash të agjentëve softuerikë, duke përfshirë Codex CLI, Codex Cloud dhe zgjerimin Codex për VS Code. Ky post përqendrohet te harness i Codex, që ofron ciklin bazë të agjentit dhe logjikën e ekzekutimit që qëndron në themel të të gjitha përvojave të Codex dhe shfaqet përmes Codex CLI. Për lehtësi, do t'i përdorim termat “Codex” dhe “Codex CLI” në mënyrë të ndërrueshme.
Në zemër të çdo agjenti AI është diçka që quhet “loop-i i agjentit.” Një ilustrim i thjeshtuar i ciklit të agjentit duket kështu:
Për të filluar, agjenti merr input nga përdoruesi për ta përfshirë në grupin e udhëzimeve tekstuale që përgatit për modelin, të njohur si një kërkesë.
Hapi tjetër është të pyesim modelin duke i dërguar udhëzimet tona dhe duke i kërkuar të gjenerojë një përgjigje, një proces i njohur si inferencë. Gjatë inferencës, kërkesa tekstuale fillimisht përkthehet në një sekuencë tokenësh hyrës tokenësh(hapet në një dritare të re)—numra të plotë që indeksojnë në fjalorin e modelit. Këta tokenë më pas përdoren për të mostruar modelin, duke prodhuar një sekuencë të re tokenësh dalës.
Tokenët e daljes përkthehen përsëri në tekst, që bëhet përgjigjja e modelit. Meqenëse tokenët prodhohen në mënyrë inkrementale, ky përkthim mund të ndodhë ndërsa modeli po ekzekutohet, prandaj shumë aplikacione të bazuara në LLM shfaqin dalje në transmetim. Në praktikë, inferenca zakonisht është e fshehur pas një API-je që punon me tekst, duke fshehur detajet e tokenizimit.
Si rezultat i hapit të inferencës, modeli ose (1) prodhon një përgjigje përfundimtare ndaj inputit origjinal të përdoruesit, ose (2) kërkon një thirrje mjeti që agjenti pritet ta kryejë (p.sh., “ekzekuto ls dhe raporto rezultatin”). Në rastin e (2), agjenti ekzekuton thirrjen e mjetit dhe i shton rezultatin e tij kërkesës origjinale. Ky rezultat përdoret për të krijuar një hyrje të re që përdoret për të ri-kërkuar modelin; agjenti më pas mund ta marrë parasysh këtë informacion të ri dhe të provojë sërish.
Ky proces përsëritet derisa modeli të ndalojë së lëshuari thirrje mjetesh dhe në vend të kësaj të prodhojë një mesazh për përdoruesin (i quajtur mesazh asistenti në modelet e OpenAI). Në shumë raste, ky mesazh i përgjigjet drejtpërdrejt kërkesës origjinale të përdoruesit, por mund të jetë gjithashtu një pyetje pasuese për përdoruesin.
Për shkak se agjenti mund të ekzekutojë thirrje të mjeteve që modifikojnë mjedisin lokal, “output”-i i tij nuk kufizohet vetëm te mesazhi i asistentit. Në shumë raste, prodhimi kryesor i një agjenti softuerik është kodi që ai shkruan ose redakton në kompjuterin tënd. Megjithatë, çdo kthesë përfundon gjithmonë me një mesazh nga asistenti—si p.sh. “Shtova architecture.md që kërkove”—që sinjalizon një gjendje përfundimi në ciklin e agjentit. Nga këndvështrimi i agjentit, puna e tij është përfunduar dhe kontrolli i kthehet përdoruesit.
Udhëtimi nga hyrja e përdoruesit te përgjigjja e agjentit i paraqitur në diagram quhet një turn i një bisede (një temë në Codex). Megjithëse ky ndërveprim i bisedës mund të përfshijë shumë iteracione midis inferencës së modelit dhe thirrjeve të mjeteve. Sa herë që dërgon një mesazh të ri në një bisedë ekzistuese, historiku i bisedës përfshihet si pjesë e kërkesës për kthesën e re, që përfshin mesazhet dhe thirrjet e mjeteve nga kthesat e mëparshme:
Kjo do të thotë se, ndërsa biseda zgjerohet, zgjatet edhe gjatësia e kërkesës që përdoret për të marrë një mostër nga modeli. Kjo gjatësi ka rëndësi sepse çdo model ka një dritare konteksti, e cila është numri maksimal i tokenëve që mund të përdoren për një thirrje të vetme inferimi. Vini re se kjo dritare përfshin si tokenët e hyrjes dhe të daljes. Siç mund ta imagjinosh, një agjent mund të vendosë të bëjë qindra thirrje mjetesh në një kthesë të vetme, duke e shteruar potencialisht dritaren e kontekstit. Për këtë arsye, menaxhimi i dritares së kontekstit është një nga shumë përgjegjësitë e agjentit. Tani, le të analizojmë për të parë se si Codex ekzekuton ciklin e agjentit.
Codex CLI dërgon kërkesa HTTP te Responses API(hapet në një dritare të re) për të kryer inferencën e modelit. Do të shqyrtojmë se si rrjedh informacioni përmes Codex, që përdor API-në e përgjigjeve për të drejtuar ciklin e agjentit.
Pika fundore e API-së së Përgjigjeve që përdor Codex CLI është e konfigurueshme(hapet në një dritare të re), kështu që mund të përdoret me çdo pikë fundore që zbaton API-në e Përgjigjeve(hapet në një dritare të re):
- Kur përdor hyrjen në ChatGPT(hapet në një dritare të re) me Codex CLI, përdor
https://chatgpt.com/backend-api/codex/responsessi pikë fundore - Kur përdor autentifikimin me çelësin API(hapet në një dritare të re) me modelet e hostuara nga OpenAI, përdoret
https://api.openai.com/v1/responsessi pikë fundore - Kur ekzekuton Codex CLI me
--osspër të përdorur gpt-oss me ollama 0.13.4+(hapet në një dritare të re) ose LM Studio 0.3.39+(hapet në një dritare të re), ai si parazgjedhje përdorhttp://localhost:11434/v1/responsesqë ekzekutohet lokalisht në kompjuterin tënd - Codex CLI mund të përdoret me Responses API të hostuar nga një ofrues cloud si Azure
Le të shqyrtojmë se si Codex krijon kërkesën për thirrjen e parë të inferencës në një bisedë.
Si përdorues përfundimtar, ti nuk e specifikon kërkesën e përdorur për të mostruar modelin fjalë për fjalë kur bën një kërkesë te API-ja e Përgjigjeve. Në vend të kësaj, ti specifikon lloje të ndryshme të dhënash hyrëse si pjesë e kërkesës tënde, dhe serveri i Responses API vendos se si ta strukturojë këtë informacion në një kërkesë që modeli është projektuar ta konsumojë. Mund ta mendosh kërkesën si një “listë sendesh”; ky seksion do të shpjegojë se si pyetja jote shndërrohet në atë listë.
Në kërkesën fillestare, çdo element në listë lidhet me një rol. Roli tregon se sa peshë duhet të ketë përmbajtja e lidhur dhe është një nga vlerat e mëposhtme (në rend zbritës të përparësisë): sistemi, zhvilluesi, përdoruesi, asistenti.
Responses API(hapet në një dritare të re) pranon një ngarkesë JSON me shumë parametra. Ne do të përqendrohemi te këto tre:
udhëzime(hapet në një dritare të re): mesazh sistemi (ose mesazh zhvilluesi) i futur në kontekstin e modelitmjete(hapet në një dritare të re): një listë mjetesh që modeli mund t'i përdorë gjatë krijimit të një përgjigjejetë dhëna hyrëse(hapet në një dritare të re): një listë me të dhëna me tekst, imazhe ose skedar për modelin
Në Codex, fusha instructions lexohet nga model_instructions_file(hapet në një dritare të re) në ~/.codex/config.toml, nëse specifikohet; përndryshe, përdoren base_instructions të lidhura me një model(hapet në një dritare të re). Udhëzimet specifike për modelin ndodhen në repo-n Codex dhe janë të përfshira në CLI (p.sh., gpt-5.2-codex_prompt.md(hapet në një dritare të re)).
Fusha tools është një listë përkufizimesh mjetesh që përputhen me një skemë të përcaktuar nga API-ja e Përgjigjeve. Për Codex, kjo përfshin mjetet që ofrohen nga Codex CLI, mjetet që ofrohen nga Responses API që duhet të jenë të disponueshme për Codex, si dhe mjetet e ofruara nga përdoruesi, zakonisht përmes serverëve MCP:
Së fundmi, fusha e të dhënave hyrëse e payload-it JSON është një listë artikujsh. Codex fut artikujt e mëposhtëm(hapet në një dritare të re) në të dhënat hyrëse përpara se të shtojë mesazhin e përdoruesit:
1. Një mesazh me role=developer që përshkruan sandbox-in që zbatohet vetëm për mjetin shell të ofruar nga Codex, i përcaktuar në seksionin tools. Kjo do të thotë se mjetet e tjera, si ato të ofruara nga serverët MCP, nuk janë të izoluara nga Codex dhe janë përgjegjëse për zbatimin e masave të tyre mbrojtëse.
Mesazhi ndërtohet nga një shabllon ku pjesët kyçe të përmbajtjes vijnë nga fragmente të Markdown të paketuara në Codex CLI, si workspace_write.md(hapet në një dritare të re) dhe on_request.md(hapet në një dritare të re):
2. (Opsionale) Një mesazh me role=developer përmbajtja e të cilit është vlera developer_instructions e lexuar nga skedari config.toml i përdoruesit.
3. (Opsionale) Një mesazh me role=user përmbajtja e të cilit janë “udhëzimet e përdoruesit”, të cilat nuk merren nga një skedar i vetëm, por janë mbledhur nga burime të shumta(hapet në një dritare të re). Në përgjithësi, udhëzimet më specifike shfaqen më vonë:
- Përmbajtja e
AGENTS.override.mddheAGENTS.mdnë$CODEX_HOME - I nënshtrohet një kufiri (32 KiB, si parazgjedhje), shiko në secilën dosje nga rrënja e Git/projektit të
cwd(nëse ekziston) deri te vetëcwd: shto përmbajtjen e cilësdo prejAGENTS.override.md,AGENTS.md, ose çdo emër skedari i specifikuar ngaproject_doc_fallback_filenames në config.toml - Nëse janë konfiguruar ndonjë aftësi(hapet në një dritare të re):
- një hyrje e shkurtër rreth aftësive
- metadatat e aftësive(hapet në një dritare të re) për çdo aftësi
- një seksion mbi si të përdoren aftësitë(hapet në një dritare të re)
4. Një mesazh me role=user që përshkruan mjedisin lokal ku agjenti po operon aktualisht. Kjo përcakton direktorinë aktuale të punës dhe shell-in e përdoruesit(hapet në një dritare të re):
Pasi Codex ka kryer të gjitha llogaritjet e mësipërme për të inicializuar input, ai shton mesazhin e përdoruesit për të nisur bisedën.
Shembujt e mëparshëm u përqendruan te përmbajtja e secilit mesazh, por vini re se secili element i input është një objekt JSON me type, role(hapet në një dritare të re) dhe content si më poshtë:
Pasi Codex ndërton payload-in e plotë JSON për ta dërguar te Responses API, ai më pas bën kërkesën HTTP POST me një header Authorization në varësi të mënyrës se si është konfiguruar pika fundore e Responses API në ~/.codex/config.toml (header-a shtesë HTTP dhe parametra query shtohen nëse specifikohen).
Kur një server i OpenAI Responses API merr kërkesën, ai përdor JSON-in për të nxjerrë kërkesë për modelin si më poshtë (për t'u siguruar, një implementim i personalizuar i Responses API mund të bëjë një zgjedhje tjetër):
Siç mund ta shohësh, renditja e tre elementeve të para në kërkesë përcaktohet nga serveri, jo nga klienti. Megjithatë, nga ato tre elemente, vetëm përmbajtja e mesazhit të sistemit kontrollohet gjithashtu nga serveri, pasi tools dhe instructions përcaktohen nga klienti. Këto ndiqen nga të dhënat hyrëse të payload-it JSON për të plotësuar kërkesën.
Tani që kemi kërkesën tonë, jemi gati të provojmë modelin.
Kjo kërkesë HTTP ndaj API-së së Përgjigjeve nis “turnin” e parë të një bisede në Codex. Serveri përgjigjet me një rrjedhë të ngjarjeve të dërguara nga serveri (SSE(hapet në një dritare të re)). Të dhënat data të çdo ngjarjeje janë një payload JSON me një "type" që fillon me "response", që mund të jetë diçka e tillë (një listë e plotë e ngjarjeve mund të gjendet në dokumentacionin tonë të API(hapet në një dritare të re)):
Codex përthith rrjedhën e ngjarjeve(hapet në një dritare të re) dhe i riboton ato si objekte të brendshme ngjarjesh që mund të përdoren nga një klient. Ngjarje si response.output_text.delta përdoren për të mbështetur transmetimin në UI, ndërsa ngjarje të tjera si response.output_item.added transformohen në objekte që shtohen në input për thirrjet pasuese të API-së së Përgjigjeve.
Supozoni që kërkesa e parë për API-në e Përgjigjeve përfshin dy ngjarje response.output_item.done: një me type=arsyetim dhe një me type=function_call. Këto ngjarje duhet të përfaqësohen në fushën input të JSON kur e pyesim modelin përsëri me përgjigjen ndaj thirrjes së mjetit:
Kërkesa e krijuar për të marrë mostra nga modeli si pjesë e pyetjes pasuese do të duket kështu:
Në veçanti, vëre se si kërkesa e vjetër është një parashtesë e saktë e kërkesës së re. Kjo është e qëllimshme, pasi kjo i bën kërkesat pasuese shumë më efikase sepse na mundëson të përfitojmë nga memorizimi i kërkesës (për të cilin do të flasim në seksionin tjetër mbi performancën).
Duke iu kthyer diagramit tonë të parë të ciklit të agjentit, shohim se mund të ketë shumë përsëritje midis inferencës dhe thirrjes së mjeteve. Kërkesa mund të vazhdojë të rritet derisa më në fund të marrim një mesazh nga asistenti, që tregon fundin e radhës:
Në Codex CLI, ne i paraqesim përdoruesit mesazhin e asistentit dhe e përqendrojmë kompozuesin për t'i treguar përdoruesit se është “radha” e tij për të vazhduar bisedën. Nëse përdoruesi përgjigjet, si mesazhi i asistentit nga radha e mëparshme, ashtu edhe mesazhi i ri i përdoruesit, duhet të shtohen në input në kërkesën e Responses API për të nisur radhën e re:
Edhe një herë, meqenëse po vazhdojmë një bisedë, gjatësia e input që dërgojmë te API-ja e Përgjigjeve vazhdon të rritet:
Le të shqyrtojmë se çfarë nënkupton kjo kërkesë gjithnjë në rritje për performancën.
Mund të pyesësh veten, “Prit, a nuk është cikli i agjentit kuadratik për sa i përket sasisë së JSON-it të dërguar te Responses API gjatë rrjedhës së bisedës?” Dhe do të kishe të drejtë. Ndërsa API-ja Responses mbështet një parametër opsional previous_response_id(hapet në një dritare të re) për të zbutur këtë problem, Codex nuk e përdor atë sot, kryesisht për të mbajtur kërkesat plotësisht pa gjendje dhe për të mbështetur konfigurimet e pa mbajtje të dhënash (ZDR).
Shmangia e previous_response_id e thjeshton punën për ofruesin e API-së së përgjigjeve sepse siguron që çdo kërkesë të jetë pa gjendje. Kjo gjithashtu e bën të lehtë mbështetjen e klientëve që kanë zgjedhur pa mbajtje të dhënash (ZDR)(hapet në një dritare të re), pasi ruajtja e të dhënave të nevojshme për të mbështetur previous_response_id do të ishte në kundërshtim me ZDR. Vini re se klientët e ZDR nuk humbasin aftësinë për të përfituar nga mesazhet e arsyetimit të pronësisë nga kthesat e mëparshme, pasi encrypted_content i lidhur mund të deshifrohet në server. (OpenAI ruan çelësin e deshifrimit të një klienti ZDR, por jo të dhënat e tij.) Shiko PR-të #642(hapet në një dritare të re) dhe #1641(hapet në një dritare të re) për ndryshimet përkatëse në Codex për të mbështetur ZDR.
Në përgjithësi, kostoja e marrjes së mostrave nga modeli është më e lartë se kostoja e trafikut të rrjetit, duke e bërë marrjen e mostrave objektivin kryesor të përpjekjeve tona për efikasitet. Kjo është arsyeja pse memorizimi i kërkesës është kaq i rëndësishëm, pasi na lejon të ripërdorim llogaritjet nga një thirrje e mëparshme inferencash. Kur kemi goditje të cache, mostrimi i modelit është linear në vend që të jetë kuadratik. Dokumentacioni ynë për ruajtjen e kërkesave (hapet në një dritare të re)e shpjegon këtë më në detaje:
Goditjet e cache janë të mundshme vetëm për përputhje të sakta të parashtesës brenda një kërkese. Për të përfituar nga memorizimi në cache, vendos përmbajtjen statike si udhëzimet dhe shembujt në fillim të kërkesës, dhe përmbajtjen e ndryshueshme, si informacioni specifik për përdoruesin, në fund. Kjo vlen gjithashtu për imazhet dhe mjetet, të cilat duhet të jenë identike ndërmjet kërkesave.
Duke pasur këtë parasysh, le të shqyrtojmë se cilat lloje operacionesh mund të shkaktojnë një “cache miss” në Codex:
- Ndryshimi i
mjetevdtë disponueshme për modelin në mes të bisedës. - Ndryshimi i
modeleveqë është objektivi i kërkesës së Responses API (në praktikë, kjo ndryshon elementin e tretë në kërkesën origjinale, pasi përmban udhëzime specifike për modelin). - Ndryshimi i konfigurimit të sandbox-it, mënyrës së miratimit ose direktorisë aktuale të punës.
Ekipi i Codex duhet të jetë i kujdesshëm kur prezanton veçori të reja në Codex CLI që mund të komprometojnë ruajtjen e kërkesave në cache. Si shembull, mbështetja jonë fillestare për mjetet MCP prezantoi një gabim ku nuk arritëm t’i numëronim mjetet në një rend të qëndrueshëm(hapet në një dritare të re), duke shkaktuar dështime të cache-it. Vini re se mjetet MCP mund të jenë veçanërisht të ndërlikuara, sepse serverët MCP mund të ndryshojnë listën e mjeteve që ofrojnë në çast përmes një njoftimi notifications/tools/list_changed(hapet në një dritare të re). Respektimi i këtij njoftimi në mes të një bisede të gjatë mund të shkaktojë një dështim të kushtueshëm të cache-it.
Kur është e mundur, ne i trajtojmë ndryshimet e konfigurimit që ndodhin në mes të bisedës duke shtuar një mesazh të ri te të dhënave hyrëse për të pasqyruar ndryshimin, në vend që të modifikojmë një mesazh të mëparshëm:
- Nëse konfigurimi i sandbox-it ose mënyra e miratimit ndryshon, ne shtojmë(hapet në një dritare të re) një mesazh të ri
role=developerme të njëjtin format si elementi origjinal<permissions instructions>. - Nëse drejtoria aktuale e punës ndryshon, ne futim(hapet në një dritare të re) një mesazh të ri
role=userme të njëjtin format si origjinali<environment_context>.
Ne bëjmë çmos për të siguruar goditje të cache për performancën. Ka edhe një burim tjetër të rëndësishëm që duhet ta menaxhojmë: dritarja e kontekstit.
Strategjia jonë e përgjithshme për të shmangur mbarimin e dritares së kontekstit është të kompaktojmë bisedën sapo numri i tokenëve të kalojë një prag të caktuar. Konkretisht, ne zëvendësojmë të dhënat hyrëse me një listë të re, më të vogël artikujsh që përfaqëson bisedën, duke i mundësuar agjentit të vazhdojë me një kuptim të asaj që ka ndodhur deri tani. Një zbatim i hershëm i kompaktimit(hapet në një dritare të re) kërkonte që përdoruesi të thërriste manualisht komandën /compact, e cila do të bënte një kërkesë te API-ja e Përgjigjeve duke përdorur bisedën ekzistuese plus udhëzime të personalizuara për përmbledhje(hapet në një dritare të re). Codex përdori mesazhin e asistentit që rezultoi, i cili përmbante përmbledhjen si të dhënat hyrëse(hapet në një dritare të re) për kthesat pasuese të bisedës.
Që atëherë, API-ja e Përgjigjeve ka evoluar për të mbështetur një /responses/compact pikë fundore(hapet në një dritare të re) të veçantë që kryen kompaktimin në mënyrë më efikase. Kthen një listë artikujsh(hapet në një dritare të re) që mund të përdoret në vend të input të mëparshëm për të vazhduar bisedën, duke liruar dritaren e kontekstit. Kjo listë përfshin një artikull të veçantë type=compaction me një artikull të errët encrypted_content që ruan kuptimin latent të modelit për bisedën origjinale. Tani, Codex përdor automatikisht këtë pikë fundore për të kompaktuar bisedën kur tejkalohet auto_compact_limit(hapet në një dritare të re).
Kemi prezantuar ciklin e agjentit Codex dhe kemi shpjeguar se si Codex krijon dhe menaxhon kontekstin e tij kur pyet një model. Gjatë rrugës, theksuam konsiderata praktike dhe praktikat më të mira që zbatohen për këdo që po ndërton një cikël agjent mbi API-në e Përgjigjeve.
Ndërsa cikli i agjentit ofron bazën për Agjent Codex, është vetëm fillimi. Në postimet e ardhshme, do të thellohemi në arkitekturën e CLI-së, do të eksplorojmë se si zbatohet përdorimi i mjeteve dhe do të shqyrtojmë më nga afër modelin e sandboxing të Codex.


