Codex CLI(åbner i et nyt vindue) er vores lokale softwareagent på tværs af platforme, designet til at producere pålidelige softwareændringer af høj kvalitet, mens den arbejder sikkert og effektivt på din maskine. Vi har lært utrolig meget om, hvordan man bygger en agent i verdensklasse, siden vi første gang lancerede CLI i april. For at udfolde disse indsigter er dette det første indlæg i en løbende serie, hvor vi vil udforske forskellige aspekter af, hvordan Codex fungerer, samt de erfaringer, vi har gjort os. (Du kan få et endnu mere detaljeret indblik i, hvordan Codex CLI er bygget, i vores open source-lager på https://github.com/openai/codex(åbner i et nyt vindue). Mange af de finere detaljer i vores designbeslutninger er dokumenteret i GitHub-issues og pull-anmodninger, hvis du gerne vil vide mere).
For at starte vil vi fokusere på agent-loopet, som er den centrale logik i Codex CLI, der er ansvarlig for at orkestrere interaktionen mellem brugeren, modellen og de værktøjer, modellen anvender for at udføre meningsfuldt softwarearbejde. Vi håber, at dette indlæg giver dig et godt indblik i den rolle, vores agent (eller “harness”) spiller i brugen af en LLM.
En hurtig bemærkning om terminologi, før vi dykker ned: hos OpenAI omfatter “Codex” en række software agent-tilbud, herunder Codex CLI, Codex Cloud og Codex VS Code-udvidelsen. Dette indlæg fokuserer på Codex harness, som leverer den centrale agent-loop og eksekveringslogik, der ligger til grund for alle Codex-oplevelser og præsenteres gennem Codex CLI. For nemheds skyld vil vi her bruge termerne “Codex” og “Codex CLI” i flæng.
I hjertet af enhver AI-agent er der noget, der kaldes “agent-loopen.” En forenklet illustration af agent-loopet ser sådan ud:
Til at begynde med tager agenten input fra brugeren for at inkludere det i det sæt tekstinstruktioner, den forbereder til modellen, kendt som en prompt.
Det næste skridt er at forespørge modellen ved at sende den vores instruktioner og bede den om at generere et svar, en proces kendt som inferens. Under inferens oversættes den tekstlige prompt først til en sekvens af inputtokens(åbner i et nyt vindue) – heltal, der indekserer i modellens ordforråd. Disse tokener bruges derefter til at sample modellen, hvilket producerer en ny sekvens af outputtokener.
Outputtokener oversættes tilbage til tekst, som bliver modellens svar. Da tokener produceres trinvis, kan denne oversættelse ske, mens modellen kører, hvilket er grunden til, at mange LLM-baserede applikationer viser streamingoutput. I praksis er inferens normalt indkapslet bag en API, der opererer på tekst og abstraherer detaljerne ved tokenisering.
Som resultat af inferenstrinnet producerer modellen enten (1) et endeligt svar på brugerens oprindelige input eller (2) anmoder om et værktøjskald, som agenten forventes at udføre (f.eks. “kør ls og rapportér outputtet”). I tilfælde af (2) udfører agenten værktøjsopkaldet og tilføjer dets output til den oprindelige prompt. Dette output bruges til at generere et nyt input, der bruges til at forespørge modellen igen; agenten kan derefter tage disse nye oplysninger i betragtning og prøve igen.
Denne proces gentages, indtil modellen stopper med at udsende værktøjskald og i stedet producerer en besked til brugeren (omtalt som en assistentbesked i OpenAI-modeller). I mange tilfælde besvarer denne besked direkte brugerens oprindelige anmodning, men den kan også være et opfølgende spørgsmål til brugeren.
Fordi agenten kan udføre værktøjskald, der ændrer det lokale miljø, er dens “output” ikke begrænset til assistentbeskeden. I mange tilfælde er det primære output fra en softwareagent den kode, den skriver eller redigerer på din maskine. Ikke desto mindre slutter hver omgang altid med en assistentbesked, såsom “Jeg tilføjede architecture.md, som du bad om”, hvilket signalerer en afslutningstilstand i agentloopet. Fra agentens perspektiv er arbejdet fuldført, og kontrollen vender tilbage til brugeren.
Rejsen fra brugerinput til agentrespons, som vises i diagrammet, omtales som ét trin i en samtale (en tråd i Codex). Selvom dette samtaletrin kan omfatte mange iterationer mellem model inference og værktøjsopkald. Hver gang du sender en ny besked til en eksisterende samtale, inkluderes samtalehistorikken som en del af prompten for det nye trin, som omfatter beskederne og værktøjskald fra tidligere ture:
Det betyder, at efterhånden som samtalen vokser, vokser også længden af den prompt, der bruges til at sample modellen. Denne længde er vigtig, fordi hver model har et kontekstvindue, som er det maksimale antal tokens, den kan bruge til en enkelt inferenskald. Bemærk, at dette vindue inkluderer både input- og outputtokens. Som du nok kan forestille dig, kunne en agent beslutte at foretage hundredvis af værktøjskald i et enkelt trin, hvilket potentielt kan udtømme kontekstvinduet. Af denne grund er håndtering af kontekstvinduer et af agentens mange ansvarsområder. Lad os nu dykke ned i, hvordan Codex kører agent-loopet.
Codex CLI sender HTTP-anmodninger til Responses API(åbner i et nyt vindue) for at udføre modelinference. Vi vil undersøge, hvordan information flyder gennem Codex, som bruger Responses API til at drive agent-loopet.
Det Responses API-endepunkt, som Codex CLI bruger, er konfigurerbart(åbner i et nyt vindue), så det kan bruges med ethvert endepunkt, der implementerer Responses API(åbner i et nyt vindue):
- Når du bruger ChatGPT‑login(åbner i et nyt vindue) med Codex CLI, bruger den
https://chatgpt.com/backend-api/codex/responsessom endepunktet - Når du bruger API-nøgleautentificering(åbner i et nyt vindue) med OpenAI-hostede modeller, anvender den
https://api.openai.com/v1/responsessom endepunktet - Når du kører Codex CLI med
--ossfor at bruge gpt-oss med ollama 0.13.4+(åbner i et nyt vindue) eller LM Studio 0.3.39+(åbner i et nyt vindue), er standardindstillingenhttp://localhost:11434/v1/responses, der kører lokalt på din computer - Codex CLI kan bruges med Responses API, der hostes af en cloududbyder som Azure
Lad os udforske, hvordan Codex skaber prompten til det første inferensopkald i en samtale.
Som slutbruger specificerer du ikke den prompt, der bruges til at sample modellen ordret, når du forespørger Responses API. I stedet specificerer du forskellige inputtyper som en del af din forespørgsel, og Responses API-serveren beslutter, hvordan disse oplysninger skal struktureres til en prompt, som modellen er designet til at forbruge. Du kan tænke på prompten som en “liste over elementer”; dette afsnit vil forklare, hvordan din forespørgsel bliver omdannet til den liste.
I den indledende prompt er hvert element på listen forbundet med en rolle. Denne rolle angiver, hvor meget vægt det tilknyttede indhold skal have, og er en af følgende værdier (i faldende rækkefølge efter prioritet): system, developer, user, assistant.
The Responses API(åbner i et nyt vindue) tager en JSON-payload med mange parametre. Vi fokuserer på disse tre:
instruction(åbner i et nyt vindue): system- (eller udvikler-)besked indsat i modellens konteksttools(åbner i et nyt vindue): en liste over værktøjer, som modellen kan kalde, mens den genererer et svarinput(åbner i et nyt vindue): en liste over tekst-, billede- eller filinput til modellen
I Codex læses feltet instructions fra model_instructions_file(åbner i et nyt vindue) i ~/.codex/config.toml, hvis det er angivet; ellers bruges de base_instructions, der er tilknyttet en model(åbner i et nyt vindue). Modelspecifikke instruktioner findes i Codex-lageret og er bundtet i CLI'en (f.eks. gpt-5.2-codex_prompt.md(åbner i et nyt vindue)).
Feltet tools er en liste over værktøjsdefinitioner, der overholder et skema defineret af Responses API. For Codex omfatter dette værktøjer, der leveres af Codex CLI, værktøjer, der leveres af Responses API, som bør gøres tilgængelige for Codex, samt værktøjer, der leveres af brugeren, typisk via MCP-servere:
Endelig er input-feltet i JSON-payload en liste over elementer. Codex indsætter følgende elementer(åbner i et nyt vindue) i input før tilføjelse af brugermeddelelsen:
1. En besked med role=developer, der beskriver sandkassen, som kun gælder for det Codex-leverede shell-værktøj, der er defineret i tools-sektionen. Det vil sige, at andre værktøjer, såsom dem, der leveres fra MCP-servere, ikke er sandboxet af Codex og er ansvarlige for at håndhæve deres egne sikkerhedsforanstaltninger.
Meddelelsen er bygget fra en skabelon, hvor de vigtigste indholdselementer kommer fra uddrag af Markdown, der er bundtet i Codex CLI, såsom workspace_write.md(åbner i et nyt vindue) og on_request.md(åbner i et nyt vindue):
2. (Valgfrit) En meddelelse med role=developer, hvis indhold er værdien developer_instructions, der læses fra brugerens config.toml-fil.
3. (Valgfrit) En meddelelse med role=user, hvis indhold er “brugerinstruktionerne”, som ikke stammer fra en enkelt fil, men er samlet fra flere kilder(åbner i et nyt vindue). Generelt kommer mere specifikke instruktioner senere frem:
- Indholdet af
AGENTS.override.mdogAGENTS.mdi$CODEX_HOME - Underlagt en grænse (32 KiB som standard), kig i hver mappe fra Git-/projektroden af
cwd(hvis den findes) op til selvecwd: tilføj indholdet af enhverAGENTS.override.md,AGENTS.mdeller et hvilket som helst filnavn angivet afproject_doc_fallback_filenames i config.toml - Hvis der er konfigureret færdigheder(åbner i et nyt vindue):
- en kort indledning om færdigheder
- færdigheds-metadata(åbner i et nyt vindue) for hver færdighed
- et afsnit om sådan bruger du færdigheder(åbner i et nyt vindue)
4. En besked med role=user, der beskriver det lokale miljø, som agenten i øjeblikket opererer i. Dette angiver den aktuelle arbejdsmappe og brugerens shell(åbner i et nyt vindue):
Når Codex har udført alle de ovennævnte beregninger for at initialisere input, tilføjer den brugerens besked for at starte samtalen.
De tidligere eksempler fokuserede på indholdet af hver besked, men bemærk, at hvert element i input er et JSON-objekt med type, role(åbner i et nyt vindue) og content som følger:
Når Codex har opbygget den fulde JSON-payload til at sende til Responses API, foretager den derefter en HTTP POST-anmodning med en Authorization-header afhængigt af, hvordan Responses API-endepunktet er konfigureret i ~/.codex/config.toml (yderligere HTTP-headers og forespørgselsparametre tilføjes, hvis de er angivet).
Når en OpenAI Responses API-server modtager anmodningen, bruger den JSON til at udlede prompten til modellen som følger (for en sikkerheds skyld kan en brugerdefineret implementering af Responses-API'en træffe et andet valg):
Som du kan se, bestemmes rækkefølgen af de første tre elementer i prompten af serveren, ikke klienten. Når det er sagt, er det af de tre elementer kun indholdet af systemmeddelelsen, der også styres af serveren, da tools og instructions bestemmes af klienten. Disse efterfølges af input fra JSON-payloaden for at fuldføre prompten.
Nu hvor vi har vores prompt, er vi klar til at prøve modellen.
Denne HTTP-anmodning til Responses API starter det første “trin” i en samtale i Codex. Serveren svarer med en Server-Sent Events (SSE(åbner i et nyt vindue))-strøm. Dataene for hver begivenhed er en JSON-payload med en "type", der starter med "response", hvilket kunne være noget i retning af dette (en fuld liste over begivenheder kan findes i vores API-dokumentation(åbner i et nyt vindue)):
Codex forbruger strømmen af begivenheder(åbner i et nyt vindue) og genudgiver dem som interne begivenhedsobjekter, der kan bruges af en klient. Begivenheder som response.output_text.delta bruges til at understøtte streaming i UI'en, mens andre begivenheder som response.output_item.added transformeres til objekter, der føjes til input til efterfølgende Responses API-kald.
Antag, at den første anmodning til Responses API inkluderer to response.output_item.done-hændelser: en med type=ræsonnering og en med type=function_call. Disse hændelser skal være repræsenteret i input-feltet i JSON, når vi forespørger modellen igen med svaret på værktøjsopkaldet.
Den resulterende prompt, der bruges til at sample modellen som en del af den efterfølgende forespørgsel, vil se sådan ud:
Bemærk især, hvordan den gamle prompt er et nøjagtigt præfiks af den nye prompt. Dette er tilsigtet, da det gør efterfølgende anmodninger meget mere effektive, fordi det gør det muligt for os at drage fordel af prompt-cachelagring (som vi vil drøfte i det næste afsnit om ydeevne).
Når vi ser tilbage på vores første diagram over agentloopet, ser vi, at der kan være mange iterationer mellem inferens og værktøjskald. Prompten kan fortsætte med at vokse, indtil vi endelig modtager en assistentbesked, der angiver afslutningen på turen:
I Codex CLI præsenterer vi assistentmeddelelsen for brugeren og fokuserer skriveprogrammet for at indikere over for brugeren, at det er deres “tur” til at fortsætte samtalen. Hvis brugeren svarer, skal både assistentens meddelelse fra den forrige tur og brugerens nye meddelelse føjes til input i Responses API-anmodningen for at starte den nye tur:
Igen, fordi vi fortsætter en samtale, bliver længden af input, vi sender til Responses API, ved med at stige:
Lad os undersøge, hvad denne stadigt voksende prompt betyder for præstationen.
Du spørger måske dig selv: “Vent, er agent-loopet ikke kvadratisk i forhold til mængden af JSON, der sendes til Responses API under samtalen?” Og du ville have ret. Selvom Responses API'et understøtter en valgfri previous_response_id(åbner i et nyt vindue)-parameter for at afbøde dette problem, bruger Codex den ikke i dag, primært for at holde anmodninger fuldstændig statsløse og for at understøtte konfigurationer uden datalagring (Zero Data Retention, ZDR).
At undgå previous_response_id forenkler tingene for udbyderen af Responses-API'en, fordi det sikrer, at hver anmodning er tilstandsløs. Dette gør det også ligetil at støtte kunder, der har tilvalgt ingen datalagring (ZDR)(åbner i et nyt vindue), da lagring af de data, der kræves for at understøtte previous_response_id, ville være i strid med ZDR. Bemærk, at ZDR-kunder ikke mister muligheden for at drage fordel af proprietære ræsonneringsmeddelelser fra tidligere omgange, da det tilknyttede encrypted_content kan dekrypteres på serveren. (OpenAI opbevarer en ZDR-kundes dekrypteringsnøgle, men ikke deres data). Se PRs #642(åbner i et nyt vindue) og #1641(åbner i et nyt vindue) for de relaterede ændringer til Codex for at understøtte ZDR.
Generelt set er omkostningerne ved at sample modellen større end omkostningerne ved netværkstrafik, hvilket gør sampling til det primære mål for vores effektiviseringsindsats. Derfor er cachelagring af prompt så vigtigt, da det gør det muligt for os at genbruge beregning fra et tidligere inferenskald. Når vi får cachehits, er modellens sampling lineær frem for kvadratisk. Vores prompt caching (åbner i et nyt vindue)-dokumentation forklarer dette mere detaljeret:
Cache-hits er kun mulige ved præcise præfiks-match inden for en prompt. For at opnå caching-fordele skal statisk indhold placeres som instruktioner og eksempler i begyndelsen af din prompt, og variabelt indhold, såsom brugerspecifikke oplysninger, skal lægges til sidst. Dette gælder også for billeder og værktøjer, som skal være identiske mellem forespørgsler.
Med dette i tankerne, så lad os overveje, hvilke typer operationer der kunne forårsage et “cache miss” i Codex:
- Ændring af de
værktøjer, der er tilgængelige for modellen midt i samtalen. - Ændring af
modellen, der er målet for Responses API-anmodningen (i praksis ændrer dette det tredje element i den oprindelige prompt, da det indeholder modelspecifikke instruktioner). - Ændring af sandbox-konfigurationen, godkendelsestilstand eller den aktuelle arbejdsmappe.
Codex-teamet skal være omhyggelige, når de introducerer nye funktioner i Codex CLI, der kan kompromittere prompt-caching. Som et eksempel introducerede vores indledende understøttelse af MCP-værktøjer en fejl, hvor vi ikke fik opregnet værktøjerne i en ensartet rækkefølge(åbner i et nyt vindue), hvilket forårsagede cache-misses. Bemærk, at MCP-værktøjer kan være særligt vanskelige, fordi MCP-servere kan ændre listen over værktøjer, de stiller til rådighed, i farten via en notifications/tools/list_changed(åbner i et nyt vindue)-notifikation. At efterkomme denne meddelelse midt i en lang samtale kan forårsage en dyr cache-miss.
Når det er muligt, håndterer vi konfigurationsændringer, der sker midt i en samtale, ved at tilføje en ny besked til input for at afspejle ændringen i stedet for at ændre en tidligere besked:
- Hvis sandbox-konfigurationen eller godkendelsestilstanden ændres, indsætter(åbner i et nyt vindue) vi en ny
role=developer-meddelelse med samme format som det oprindelige<permissions instructions>-element. - Hvis den aktuelle arbejdsmappe ændres, indsætter(åbner i et nyt vindue) vi en ny
role=user-meddelelse med samme format som den oprindelige<environment_context>.
Vi gør os umage for at sikre cache-hits for at forbedre ydeevnen. Der er endnu en vigtig ressource, vi skal administrere: kontekstvinduet.
Vores generelle strategi for at undgå at løbe tør for kontekstvinduet er at komprimere samtalen, når antallet af token overstiger en vis tærskel. Specifikt erstatter vi input med en ny, mindre liste over elementer, der repræsenterer samtalen, hvilket gør det muligt for agenten at fortsætte med en forståelse af, hvad der er sket indtil nu. En tidlig implementering af komprimering(åbner i et nyt vindue) krævede, at du manuelt kørte /compact-kommandoen, som ville forespørge Responses API ved hjælp af den eksisterende samtale plus tilpassede instruktioner til sammenfatning(åbner i et nyt vindue). Codex brugte den resulterende assistentbesked, der indeholdt resuméet som det nye input(åbner i et nyt vindue) til efterfølgende samtaleomgange.
Siden da har Responses API'en udviklet sig til at understøtte et særligt /responses/compact-endepunkt(åbner i et nyt vindue), der udfører komprimering mere effektivt. Den returnerer en liste over elementer(åbner i et nyt vindue), der kan bruges i stedet for det tidligere input til at fortsætte samtalen, mens kontekstvinduet frigøres. Denne liste inkluderer et særligt type=compaction-element med et uigennemsigtigt encrypted_content-element, der bevarer modellens latente forståelse af den oprindelige samtale. Nu bruger Codex automatisk dette endepunkt til at komprimere samtalen, når auto_compact_limit(åbner i et nyt vindue) overskrides.
Vi har introduceret Codex-agentloopet og gennemgået, hvordan Codex udformer og administrerer sin kontekst, når den forespørger en model. Undervejs fremhævede vi praktiske overvejelser og bedste praksis, der gælder for alle, der bygger en agent-loop oven på Responses API.
Selvom agent-loopet danner grundlaget for Codex, er det kun starten. I kommende indlæg vil vi dykke ned i CLI'ens arkitektur, udforske, hvordan værktøjsbrug er implementeret, og tage et nærmere kig på Codex' sandboxing-model.


