A Codex CLI(új ablakban nyílik meg) egy többplatformos, helyben futó szoftverügynökünk, amelyet arra terveztünk, hogy kiváló minőségű, megbízható szoftvermódosításokat hozzon létre, miközben biztonságosan és hatékonyan működik a számítógépeden. Rengeteget tanultunk arról, hogyan lehet világszínvonalú szoftverügynököt építeni, amióta áprilisban elindítottuk a CLI-t. Ez az első bejegyzés annak a sorozatnak az első része, amelyben a Codex működésének különböző aspektusait, valamint a nehezen megszerzett tanulságokat fogjuk feltárni. (A Codex CLI felépítésének még részletesebb áttekintéséhez nézd meg a nyílt forráskódú adattárunkat a https://github.com/openai/codex(új ablakban nyílik meg) oldalon. Tervezési döntéseink számos finomabb részletét a GitHub-issue-kban és a lekéréses kérelmekben dokumentáljuk, ha még többet szeretnél megtudni.)
Kezdésként az ügynökciklusra fogunk összpontosítani, amely a Codex CLI alapvető logikája, és a felhasználó, a modell, valamint a modell által a tényleges szoftveres munka elvégzéséhez meghívott eszközök közötti interakció összehangolásáért felel. Reméljük, hogy ez a bejegyzés jó betekintést nyújt abba, milyen szerepet játszik az ügynökünk (vagy „keretrendszer”) az LLM használatában.
Mielőtt belevágnánk, egy gyors megjegyzés a terminológiáról: az OpenAI-nál a „Codex” egy szoftverügynök-ajánlatokból álló csomagot jelent, amely tartalmazza a Codex CLI-t, a Codex Cloudot és a Codex VS Code-bővítményt. Ez a bejegyzés a Codex keretrendszerre fókuszál, amely az alapvető ügynökciklust és a végrehajtási logikát biztosítja minden Codex-élmény mögött, és a Codex CLI-n keresztül érhető el. Az egyszerűség kedvéért itt a „Codex” és a „Codex CLI” kifejezéseket felváltva fogjuk használni.
Minden AI-ügynök középpontjában egy úgynevezett „ügynökciklus” áll. Az ügynökciklus egyszerűsített ábrája így néz ki:
Kezdésként az ügynök a felhasználótól kapott bemenetet beépíti abba a szöveges utasításkészletbe, amelyet a modell számára készít. Ezt nevezzük utasításnak.
A következő lépésben az ügynök lekérdezi a modellt: elküldi neki az utasításokat, és megkéri, hogy generáljon választ. Ezt a folyamatot inferenciának nevezzük. Az inferencia során a szöveges utasítás először bemeneti tokenek(új ablakban nyílik meg) sorozatává alakul – egész számokká, amelyek a modell szókészletében elfoglalt pozíciókra hivatkoznak. Ezeket a tokeneket a modell mintavételezésére használjuk, amely eredményeként egy új kimeneti tokensorozat jön létre.
A kimeneti tokeneket ezután visszaalakítjuk szöveggé, és ez lesz modell válasza. Mivel a tokenek fokozatosan jönnek létre, a visszaalakítás a modell futása közben is megtörténhet. Ez az oka annak, hogy sok LLM-alapú alkalmazás folyamatosan frissülő kimenetet jelenít meg. A gyakorlatban az inferencia általában egy szövegalapú API mögé van elrejtve, amely elvonatkoztat a tokenizálás részleteitől.
Az inferencia lépés eredményeként a modell vagy (1) végső választ ad a felhasználó eredeti bemenetére, vagy (2) egy eszközhívást kér, amelyet az ügynöknek végre kell hajtania (például „futtasd az ls parancsot, és jelentsd az eredményt”). A (2) esetben az ügynök végrehajtja az eszközhívást, és a kimenetét hozzáfűzi az eredeti utasításhoz. Ezt a kimenetet egy új bemenet létrehozására használjuk, amellyel ismét lekérdezzük a modellt. Az ügynök ezután már figyelembe tudja venni ezt az új információt, és újra próbálkozhat.
Ez a folyamat addig ismétlődik, amíg a modell abba nem hagyja az eszközhívások kibocsátását, és ehelyett egy üzenetet nem állít elő a felhasználó számára (az OpenAI modellekben ezt asszisztensüzenetnek nevezzük). Sok esetben ez az üzenet közvetlenül megválaszolja a felhasználó eredeti kérését, de előfordulhat, hogy egy következő kérdés a felhasználó felé.
Mivel az ügynök képes olyan eszközhívásokat végrehajtani, amelyek módosítják a helyi környezetet, a „kimenete” nem korlátozódik csupán az asszisztensüzenetre. Gyakran egy szoftverügynök elsődleges kimenete maga az a kód, amelyet a számítógépeden ír vagy szerkeszt. Mindazonáltal minden forduló mindig egy asszisztensüzenettel zárul – például: „Hozzáadtam a kért architecture.md fájlt” –, amely az ügynökciklusban lezárási állapotot jelez. Az ügynök szemszögéből nézve a munkája ezzel befejeződött, és az irányítás visszakerül a felhasználóhoz.
Az ábrán bemutatott, a felhasználói bemenettől az ügynök válaszáig tartó utat a beszélgetés egy fordulójának (a Codexben témaszálnak ) nevezzük. Bár ez a beszélgetési forduló több iterációt is tartalmazhat a modell következtetése és az eszközhívások között. Minden alkalommal, amikor új üzenetet küldesz egy meglévő beszélgetésben, a teljes beszélgetési előzmény bekerül az új forduló utasításába, ami tartalmazza az előző fordulók üzeneteit és eszközhívásait is.
Ez azt jelenti, hogy ahogy a beszélgetés bővül, úgy nő a modell mintavételezéséhez használt utasítás hossza is. Ez a hossz azért fontos, mert minden modellnek van egy kontextusablaka, amely az egyetlen inferenciahívás során maximálisan felhasználható tokenek számát jelenti. Fontos megjegyezni, hogy ez az ablak a bemeneti és a kimeneti tokeneket egyaránt tartalmazza. Egy ügynök dönthet úgy, hogy egyetlen körben több száz eszközhívást hajt végre, ami esetleg kimerítheti a kontextusablakot. Ezért a kontextusablak-kezelés is az ügynök számos feladata közé tartozik. Most nézzük meg közelebbről, hogyan működteti a Codex az ügynökciklust.
A Codex CLI HTTP-kéréseket küld a Responses API-hoz(új ablakban nyílik meg) a modell-inferencia futtatására. Megvizsgáljuk, hogyan áramlik az információ a Codexen keresztül, amely a Responses API-t használja az ügynökciklus működtetésére.
A Codex CLI által használt Responses API-végpont konfigurálható(új ablakban nyílik meg), így bármely olyan végponttal használható, amely megvalósítja a Responses API-t(új ablakban nyílik meg):
- Amikor a ChatGPT‑bejelentkezést használod(új ablakban nyílik meg) a Codex CLI-vel, akkor az a
https://chatgpt.com/backend-api/codex/responsescímet használja végpontként - API-kulcsos hitelesítés használatakor(új ablakban nyílik meg) az OpenAI által hosztolt modellek a
https://api.openai.com/v1/responsescímet használják végpontként - Amikor a Codex CLI-t
--osskapcsolóval futtatod, hogy a gpt-oss modellt az ollama 0.13.4+(új ablakban nyílik meg) vagy az LM Studio 0.3.39+(új ablakban nyílik meg) segítségével használd, alapértelmezés szerint ahttp://localhost:11434/v1/responsesvégpontot használja, amely helyben fut a számítógépeden - A Codex CLI használható felhőszolgáltatók, például az Azure által üzemeltetett Responses API-val is
Vizsgáljuk meg, hogyan hozza létre a Codex az első inferenciahíváshoz szükséges utasítást egy beszélgetésben.
Végfelhasználóként a Responses API lekérdezésekor nem szó szerint kell megadni a modell mintavételezéséhez használt utasítást. Ehelyett a lekérdezés részeként különféle beviteltípusokat kell meghatározni, és a Responses API szerver dönti el, hogyan strukturálja ezt az információt olyan utasítássá, amelyet a modell képes feldolgozni. Az utasítást tekintheted „elemlistának”. Ez a szakasz elmagyarázza, hogyan alakul át a lekérdezésed ezzé a listává.
A kezdeti promptban a lista minden eleme egy szerepkörhöz van rendelve. A szerepkör azt jelzi, hogy mekkora súlya van a kapcsolódó tartalomnak, és az alábbi értékek egyikét veheti fel (csökkenő prioritási sorrendben): system, developer, user, assistant.
A Responses API(új ablakban nyílik meg) JSON formátumú adatcsomagot fogad, amely számos paramétert tartalmaz. Mi most ezek közül a következő háromra fogunk koncentrálni:
instructions(új ablakban nyílik meg): a modell kontextusába beszúrt rendszer- (vagy fejlesztői) üzenettools(új ablakban nyílik meg): a modell által a válasz generálása során meghívható eszközök listájainput(új ablakban nyílik meg): a modell számára megadott szöveg-, kép- vagy fájlalapú bevitelek listája
A Codexben az instructions mezőt a model_instructions_file(új ablakban nyílik meg) fájlból olvassák be a ~/.codex/config.toml fájlban, ha meg van adva; ellenkező esetben a modellhez társított base_instructions kerül felhasználásra(új ablakban nyílik meg). A modellspecifikus utasítások a Codex repóban találhatók, és a CLI-be vannak csomagolva (például gpt-5.2-codex_prompt.md(új ablakban nyílik meg)).
A tools mező a Responses API által meghatározott sémának megfelelő eszközdefiníciók listája. A Codex esetében ez magában foglalja a Codex CLI által biztosított eszközöket, a Responses API által biztosított, a Codex számára elérhetővé tett eszközöket, valamint a felhasználó által biztosított eszközöket, általában MCP szervereken keresztül:
Végül, a JSON payload input mezője egy elemlista. A Codex a következő elemeket szúrja be(új ablakban nyílik meg) az input mezőbe, mielőtt hozzáadná a felhasználói üzenetet:
1. Egy role=developer szerepű üzenet, amely leírja azt a sandboxot, amely kizárólag a Codex által biztosított shell eszközre vonatkozik, és amely a tools szakaszban van meghatározva. Más eszközök – például az MCP-szerverek által biztosítottak – nem tartoznak a Codex sandboxába, és saját védelmi korlátaik érvényesítéséért maguk felelnek.
Az üzenet egy sablon alapján épül fel, amelyben a tartalom kulcselemei a Codex CLI-be csomagolt Markdown-részletekből származnak, például a workspace_write.md(új ablakban nyílik meg) és az on_request.md(új ablakban nyílik meg) fájlokból:
2. (Opcionális) Egy role=developer szerepű üzenet, amelynek tartalma a felhasználó config.toml fájljából beolvasott developer_instructions érték.
3. (Opcionális) Egy role=user szerepű üzenet, amely a „felhasználói utasításokat” tartalmazza. Ezek nem egyetlen fájlból származnak, hanem több forrásból vannak összesítve(új ablakban nyílik meg). Általánosságban elmondható, hogy a konkrétabb utasítások később jelennek meg:
- A
$CODEX_HOMEkönyvtárban találhatóAGENTS.override.mdésAGENTS.mdfájlok tartalma - Egy meghatározott korlát (alapértelmezés szerint 32 KiB) figyelembevételével nézd meg minden mappában, a
cwdGit-/projektgyökértől kezdve (ha létezik) egészen magáig acwd-ig: add hozzá az alábbi fájlok tartalmát:AGENTS.override.md,AGENTS.md, vagy bármely olyan fájlnév, amelyet aconfig.toml fájlban a project_doc_fallback_filenameshatároz meg. - Ha bármelyik készség(új ablakban nyílik meg) konfigurálva van:
- egy rövid bevezető a készségekről
- az egyes készségek készségmetaadatai(új ablakban nyílik meg)
- egy szakasz arról, hogyan kell használni a készségeket(új ablakban nyílik meg)
4. Egy role=user üzenet, amely leírja azt a helyi környezetet, ahol az ügynök éppen működik. Ez meghatározza az aktuális munkakönyvtárat és a felhasználó shelljét(új ablakban nyílik meg):
Miután a Codex elvégezte az input inicializálásához szükséges fenti számításokat, hozzáfűzi a felhasználói üzenetet, és ezzel elindítja a beszélgetést.
Az előző példák az egyes üzenetek tartalmára összpontosítottak, de fontos megjegyezni, hogy az input minden eleme egy JSON-objektum, amely tartalmazza a type, a role(új ablakban nyílik meg) és a content mezőt a következők szerint:
Miután a Codex összeállította a Responses API-nak küldendő teljes JSON payloadot, Authorization fejléccel HTTP POST kérést küld, amely tartalma attól függ, hogy a Responses API végpont hogyan van konfigurálva a ~/.codex/config.toml fájlban (további HTTP fejlécek és lekérdezési paraméterek is hozzáadódnak, amennyiben meg vannak adva).
Amikor az OpenAI Responses API szervere megkapja a kérést, a JSON-t használja arra, hogy az alábbiak szerint levezesse a modell számára az utasítást (természetesen egy egyedi Responses API-implementáció ettől eltérően is dönthet):
Amint látható, az utasítás első három elemének sorrendjét a szerver határozza meg, nem a kliens. Ennek ellenére a három elem közül kizárólag a rendszerüzenet tartalmát a szerver is vezérli, mivel a tools és az instructions tartalmát a kliens határozza meg. Ezeket követik a JSON-payload input mezőjéből származó elemek, amelyekkel teljessé válik az utasítás.
Most, hogy megvan az utasításunk, készen állunk a modell mintavételezésére.
Ez a Responses API-hoz intézett HTTP-kérés a Codexben elindítja egy beszélgetés első „fordulóját”. A szerver egy Server-Sent Events (SSE(új ablakban nyílik meg)) adatfolyammal válaszol. Az egyes események adatmezője egy JSON payload, amelynek "type" mezője "response"-szal kezdődik, ami valami ilyesmi lehet (az események teljes listája az API dokumentációban(új ablakban nyílik meg) található):
A Codex feldolgozza az eseményfolyamot(új ablakban nyílik meg), majd belső eseményobjektumokként újra közzéteszi azokat az eseményeket, amelyeket a kliens használhat. Az olyan események, mint a response.output_text.delta, a felhasználói felületen a streaming támogatására szolgálnak, míg más események, például a response.output_item.added, objektumokká alakulnak, amelyeket a későbbi Responses API-hívásokhoz használt inputhoz fűznek.
Tegyük fel, hogy a Responses API-hoz intézett első kérés két response.output_item.done eseményt tartalmaz: az egyik type=reasoning, a másik pedig type=function_call. Ezeket az eseményeket a JSON input mezőjében kell feltüntetni, amikor újra lekérdezzük a modellt az eszközhívásra adott válasz alapján:
A kapott utasítás, amelyet a következő lekérdezés során a modell mintavételezésére használunk, nagyjából így néz ki:
Különösen fontos megfigyelni, hogy a korábbi utasítás pontosan megegyezik az új utasítás előtagjával. Ez szándékos, mivel ez a későbbi kéréseket sokkal hatékonyabbá teszi, hiszen lehetővé teszi az utasítás gyorsítótárazás kihasználását (amelyről a teljesítményről szóló következő szakaszban lesz szó).
Ha visszatekintünk az ügynökciklus első ábrájára, láthatjuk, hogy az inferencia és az eszközhívás között számos iteráció fordulhat elő. Az utasítás addig növekedhet, amíg végül meg nem kapjuk a forduló végét jelző asszisztensüzenetet:
A Codex CLI-ben ezt az asszisztensüzenetet megjelenítjük a felhasználónak, és a szerkesztőmező fókuszba kerül, jelezve a felhasználónak, hogy most rajta a sor a beszélgetés folytatásában. Ha a felhasználó válaszol, akkor az előző forduló asszisztensüzenetét és a felhasználó új üzenetét is hozzá kell fűzni a Responses API-kérés input mezőjéhez az új forduló elindításához:
Mivel folytatjuk a beszélgetést, a Responses API-hoz küldött input hossza tovább nő:
Vizsgáljuk meg, mit jelent ez az egyre hosszabb utasítás a teljesítményre nézve.
Felmerülhet benned a kérdés: „Várjunk csak, nem kvadratikus az ügynökciklus a beszélgetés során a Responses API-nak küldött JSON mennyiségét tekintve?” És milyen igazad van! Bár a Responses API támogatja az opcionális previous_response_id(új ablakban nyílik meg) paramétert, hogy enyhítse ezt a problémát, a Codex jelenleg nem használja ezt, elsősorban azért, hogy a kérések teljesen állapotmentesek maradjanak, és támogathatók legyenek a zéró adatmegőrzés (ZDR) konfigurációk.
A previous_response_id elkerülése leegyszerűsíti a Responses API szolgáltatója számára a működést, mert biztosítja, hogy minden kérés állapotmentes legyen. Ez egyben megkönnyíti azoknak az ügyfeleknek a támogatását is, akik a zéró adatmegőrzés (ZDR)(új ablakban nyílik meg) mellett döntöttek, mivel a previous_response_id támogatásához szükséges adatok tárolása ellentmondana a ZDR elveinek. Fontos megjegyezni, hogy a ZDR-t választó ügyfelek nem esnek el attól a lehetőségtől, hogy a korábbi fordulókból származó tulajdonosi érvelési üzenetek előnyeit kihasználják, mivel a kapcsolódó encrypted_content a szerveren visszafejthető. (Az OpenAI megőrzi a ZDR-t választó ügyfelek visszafejtési kulcsát, de az adataikat nem.) A ZDR támogatásához kapcsolódó Codex-módosítások a #642(új ablakban nyílik meg) és a #1641(új ablakban nyílik meg) PR-ekben tekinthetők meg.
Általában a modell mintavételezésének költsége messze meghaladja a hálózati forgalom költségét, így a mintavételezés válik a hatékonysági erőfeszítéseink elsődleges célpontjává. Ezért olyan fontos az azonnali gyorsítótárazás, mivel lehetővé teszi, hogy egy korábbi inferenciahívás számításait újra felhasználjuk. Amikor gyorsítótár-találatokat kapunk, a modell mintavételezése lineáris, nem pedig kvadratikus. Az utasítás gyorsítótárazásáról (új ablakban nyílik meg)szóló dokumentációnk részletesebben is elmagyarázza mindezt:
Gyorsítótár-találat csak akkor lehetséges, ha az utasításon belül pontos előtagegyezés van. A gyorsítótárazás előnyeinek kihasználásához a statikus tartalmakat – például az utasításokat és a példákat – a prompt elejére kell helyezni, míg a változó tartalmakat – például a felhasználóspecifikus információkat – pedig a végére. Ez a képekre és az eszközökre is vonatkozik, amelyeknek az egyes kérések között változatlanoknak kell maradniuk.
Ezt szem előtt tartva nézzük meg, milyen típusú műveletek okozhatnak „gyorsítótárhibát” a Codexben:
- A modell számára elérhető
toolsmegváltoztatása a beszélgetés közben. - A Responses API-kérés célját képező
modellmódosítása (a gyakorlatban ez az eredeti utasítás harmadik elemét változtatja meg, mivel az modellspecifikus instrukciókat tartalmaz). - A sandbox konfigurációjának, a jóváhagyási módnak vagy az aktuális munkakönyvtárnak a megváltoztatása.
A Codex csapatának különösen körültekintően kell eljárnia, amikor olyan új funkciókat vezet be a Codex CLI-ben, amelyek veszélyeztethetik a gyorsítótárazást. Például az MCP-eszközök kezdeti támogatása során egy olyan hiba került a rendszerbe, ami miatt az eszközök felsorolása nem következetes sorrendben történt (új ablakban nyílik meg), és ez gyorsítótárhibákat eredményezett. Érdemes figyelembe venni, hogy az MCP-eszközök különösen trükkösek lehetnek, mert az MCP-szerverek menet közben is megváltoztathatják az általuk biztosított eszközök listáját egy notifications/tools/list_changed(új ablakban nyílik meg) értesítésen keresztül. Ha egy hosszú beszélgetés közepén figyelembe veszed ezt az értesítést, az költséges gyorsítótárhibát okozhat.
Amikor lehetséges, a beszélgetés közben bekövetkező konfigurációs változásokat úgy kezeljük, hogy egy új üzenetet fűzünk az inputhoz, amely tükrözi a változást, ahelyett hogy egy korábbi üzenetet módosítanánk:
- Ha a sandbox konfigurációja vagy a jóváhagyási mód megváltozik, beszúrunk(új ablakban nyílik meg) egy új, az eredeti
<permissions instructions>elem formátumával megegyező formátumúrole=developerüzenetet . - Ha az aktuális munkakönyvtár változik, beszúrunk(új ablakban nyílik meg) egy új
role=userüzenetet, melynek a formátuma megegyezik az eredeti<environment_context>formátummal.
Mindent megteszünk azért, hogy a teljesítményjavítás érdekében biztosítsuk a gyorsítótár-találatokat. Van még egy másik kulcsfontosságú erőforrás is, amelyet kezelnünk kell. Ez pedig a kontextusablak.
Általános stratégiánk a kontextusablak kimerülésének elkerülésére az, hogy amikor a tokenek száma meghalad egy bizonyos küszöbértéket, tömörítjük a beszélgetést. Pontosabban: az input tartalmát egy új, kisebb elemlistára cseréljük, amely reprezentálja az addigi beszélgetést, így az ügynök úgy folytathatja a munkáját, hogy érti, mi történt korábban. A tömörítés egy korai megvalósítása(új ablakban nyílik meg) megkövetelte, hogy a felhasználó manuálisan indítsa el a /compact parancsot, amely a meglévő beszélgetést és az összefoglaláshoz(új ablakban nyílik meg) tartozó egyéni utasításokat felhasználva kérdezi le a Responses API-t. A Codex a kapott, összefoglalót tartalmazó asszisztensüzenetet használta új bemenetként(új ablakban nyílik meg) a beszélgetés későbbi fordulóihoz.
Azóta a Responses API kibővült egy speciális /responses/compact végponttal(új ablakban nyílik meg), amely hatékonyabban végzi el a tömörítést. Ez a végpont egy elemlistát(új ablakban nyílik meg) ad vissza, amely a korábbi input helyett használható a beszélgetés folytatásához, miközben felszabadítja a kontextusablakot. Az elemlista tartalmaz egy speciális type=compaction elemet is, amely egy átláthatatlan encrypted_content elemet hordoz, és megőrzi a modell eredeti beszélgetésről alkotott látens megértését. A Codex automatikusan ezt a végpontot használja a beszélgetés tömörítésére, amikor az auto_compact_limit(új ablakban nyílik meg) értéke túllépésre kerül.
Bemutattuk a Codex ügynökciklusát, és áttekintettük, hogyan alakítja ki és kezeli a Codex a kontextusát akkor, amikor egy modellt kérdez le. Mindeközben rávilágítottunk azokra a gyakorlati szempontokra és bevált megoldásokra is, amelyek mindenkire érvényesek, aki a Responses API-ra építve ügynökciklust valósít meg.
Bár az ügynökciklus adja a Codex alapját, ez még csak a kezdet. A következő bejegyzésekben részletesen bemutatjuk a CLI architektúráját, megvizsgáljuk, hogyan valósul meg az eszközhasználat, és közelebbről is szemügyre vesszük a Codex sandbox modelljét.


