Codex CLI(се отвора во нов прозорец) е нашиот локален софтверски агент за повеќе платформи, дизајниран да произведува висококвалитетни и сигурни софтверски промени, додека работи безбедно и ефикасно на твојата машина. Научивме многу за тоа како да изградиме софтверски агент од светска класа откако првпат го лансиравме CLI во април. За да ги откриеме тие сознанија, ова е првиот пост од тековната серија во која ќе истражуваме различни аспекти на тоа како функционира Codex, како и лекциите што сме ги научиле со многу труд. (За уште подетален преглед на тоа како е изграден Codex CLI, провери го нашиот репозиториум со отворен код на https://github.com/openai/codex(се отвора во нов прозорец). Многу од поситните детали за нашите одлуки за дизајн се меморирани во GitHub issues и барања за повлекување ако сакаш да дознаеш повеќе.)
За почеток, ќе се фокусираме на циклусот на агентот, кој претставува основната логика во Codex CLI, одговорна за оркестрирање на интеракцијата помеѓу корисникот, моделот и алатките што моделот ги повикува за да изврши значајна софтверска работа. Се надеваме дека оваа објава ти дава добар увид во улогата што нашиот агент (или „harness“) ја игра во користењето на големи јазични модели (LLM).
Пред да започнеме, кратка забелешка за терминологијата: во OpenAI, „Codex“ опфаќа пакет софтверски агенти, вклучувајќи Codex CLI, Codex Cloud и екстензијата Codex VS Code. Оваа објава се фокусира на Codex harness, кој го обезбедува основниот циклус на агентот и логиката за извршување што е основа на сите Codex искуства и е достапна преку Codex CLI. За полесно, овде ќе ги користиме термините „Codex“ и „Codex CLI“ наизменично.
Во срцето на секој AI агент е нешто што се нарекува „циклус на агентот“. Поедноставена илустрација на циклусот на агентот изгледа вака:
За почеток, агентот прима влезни податоци од корисникот за да го вклучи во збирот текстуални инструкции што ги подготвува за моделот, познат како промпт.
Следниот чекор е да го испрашаме моделот со тоа што ќе му ги испратиме нашите инструкции и ќе побараме да генерира одговор, процес познат како инференција. За време на инференцијата, текстуалниот промпт прво се претвора во низа од влезни токени(се отвора во нов прозорец) - цели броеви кои се користат за индексирање во речникот на моделот. Овие токени потоа се користат за семплирање на моделот, што произведува нова секвенца од излезни токени.
Излезните токени се претвораат назад во текст, што станува одговор на моделот. Бидејќи токените се произведуваат постепено, преводот може да се одвива додека моделот работи, што е причината зошто многу апликации базирани на големи јазични модели (LLM) прикажуваат тековен резултат. Во пракса, заклучувањето обично е инкапсулирано зад API што работи со текст, апстрахирајќи ги деталите за токенизација.
Како резултат на чекорот на инференција, моделот или (1) дава конечен одговор на оригиналниот внес на корисникот, или (2) бара повик на алатка што се очекува агентот да го изврши (на пр., „изврши ls и пријави го резултатот“). Во случајот (2), агентот го извршува повикот на алатката и го додава резултатот на оригиналниот промпт. Овој резултат се користи за генерирање нов внес што се користи за повторно испраќање барање до моделот; агентот потоа може да ги земе предвид овие нови информации и да се обиде повторно.
Овој процес се повторува додека моделот не престане да емитува повици на алатки и наместо тоа не создаде порака за корисникот (во моделите на OpenAI наречена порака од асистент). Во многу случаи, оваа порака директно одговара на првичното барање на корисникот, но може да биде и дополнително прашање за него.
Бидејќи агентот може да извршува повици на алатки што ја менуваат локалната средина, неговиот „резултат“ не е ограничен само на пораката од асистентот. Во многу случаи, примарниот резултат на софтверскиот агент е кодот што го пишува или уредува на твојата машина. Сепак, секој чекор секогаш завршува со порака од асистентот - на пример, „Го додадов architecture.md што го побаравте“ - што сигнализира состојба на завршување во циклусот на агентот. Од перспектива на агентот, неговата работа е завршена и контролата се враќа кај корисникот.
Процесот од кориснички внес до одговор на агент прикажан на дијаграмот се нарекува еден круг на разговор (една нишка во Codex). Иако овој круг на разговор може да вклучува многу итерации помеѓу инференцијата на моделот и повиците на алатките. Секој пат кога ќе испратиш нова порака во постоечки разговор, историјата на разговорот се вклучува како дел од промптот за новиот круг, кој ги содржи пораките и повиците на алатки од претходните кругови:
Ова значи дека како што се развива разговорот, така се зголемува и должината на промптот што се користи за да се земе примерок од моделот. Оваа должина е важна бидејќи секој модел има контекстуален прозорец, што претставува максимален број токени што може да ги користи за една инференција. Имај предвид дека овој прозорец вклучува и влезни и излезни токени. Како што можеш да замислиш, агентот може да одлучи да направи стотици повици на алатки во еден чекор, потенцијално исцрпувајќи го контекстуалниот прозорец. Поради оваа причина, управувањето со контекстниот прозорец е една од многуте одговорности на агентот. Сега, ајде да навлеземе за да видиме како Codex го извршува агентскиот циклус.
Codex CLI испраќа HTTP барања до Responses API(се отвора во нов прозорец) за да изврши инференција на моделот. Ќе разгледаме како информациите течат низ Codex, кој го користи Responses API за да го води циклусот на агентот.
Крајната точка на Responses API што ја користи Codex CLI е конфигурирана(се отвора во нов прозорец), така што може да се користи со која било крајна точка што го имплементира Responses API(се отвора во нов прозорец):
- Кога се најавуваш со ChatGPT(се отвора во нов прозорец) преку Codex CLI, се користи
https://chatgpt.com/backend-api/codex/responsesкако крајна точка - Кога користиш автентикација со API-клуч(се отвора во нов прозорец) со модели хостирани на OpenAI, се користи
https://api.openai.com/v1/responsesкако крајна точка - Кога го стартуваш Codex CLI со
--ossза да го користиш gpt-oss со ollama 0.13.4+(се отвора во нов прозорец) или LM Studio 0.3.39+(се отвора во нов прозорец), стандардно се поставува наhttp://localhost:11434/v1/responsesшто работи локално на вашиот компјутер - Codex CLI може да се користи со Responses API кој е хостиран од провајдер во облак како што е Azure
Ајде да истражиме како Codex го создава промптот за првиот повик за инференција во разговор.
Како краен корисник, не го наведуваш промптот што се користи за да се земе пример од моделот буквално кога го прашуваш Responses API. Наместо тоа, наведуваш различни типови на внесови како дел од твоето барање, а серверот на API за одговори одлучува како да ја структурира оваа информација во промпт што моделот е дизајниран да го обработи. Можеш да го замислиш промптот како „листа на ставки“; овој дел ќе објасни како твоето барање се претвора во таа листа.
Во почетниот промпт, секоја ставка на листата е поврзана со улога. role покажува колкава важност треба да има поврзаната содржина и е една од следниве вредности (по опаѓачки редослед на приоритет): system, developer, user, assistant.
Responses API(се отвора во нов прозорец) прима JSON payload со многу параметри. Ќе се фокусираме на овие три работи:
инструкции(се отвора во нов прозорец): системска (или развивачка) порака вметната во контекстот на моделоталатки(се отвора во нов прозорец): список на алатки кои моделот може да ги повика додека генерира одговорвлезни податоци(се отвора во нов прозорец): листа на текстуални, сликовни или датотечни влезни податоци во моделот
Во Codex, полето instructions се чита од model_instructions_file(се отвора во нов прозорец) во ~/.codex/config.toml, ако е наведено; во спротивно, се користат the base_instructions associated with a model(се отвора во нов прозорец). Инструкции специфични за моделот се наоѓаат во Codex репозиториумот и се вклучени во CLI (на пр., gpt-5.2-codex_prompt.md(се отвора во нов прозорец)).
Полето tools е листа на дефиниции на алатки кои се усогласени со шема дефинирана од API за одговори. За Codex, ова ги вклучува алатките што ги обезбедува Codex CLI, алатките што ги обезбедува Responses API кои треба да бидат достапни за Codex, како и алатките што ги обезбедува корисникот, обично преку MCP сервери:
На крај, полето input на JSON payload е листа од ставки. Codex ги вметнува следниве ставки(се отвора во нов прозорец) во input пред да ја додаде корисничката порака:
1. Порака со role=developer што ја опишува средината на sandbox која се однесува само на алатката shell обезбедена од Codex, дефинирана во делот алатки. Односно, другите алатки, како оние обезбедени од MCP сервери, не се изолирани од Codex и се одговорни за спроведување на сопствените заштитни мерки.
Пораката е создадена од шаблон каде што клучните делови од содржината доаѓаат од исечоци од Markdown спакувани во Codex CLI, како што се workspace_write.md(се отвора во нов прозорец) и on_request.md(се отвора во нов прозорец):
2. (Опционално) Порака со role=developer чија содржина е вредноста на developer_instructions прочитана од корисничката датотека config.toml.
3. (Опционално) Порака со role=user чии содржини се „кориснички инструкции“, кои не се преземени од една датотека, туку се собрани од повеќе извори(се отвора во нов прозорец). Воопшто, подетални инструкции се појавуваат подоцна:
- Содржина на
AGENTS.override.mdиAGENTS.mdво$CODEX_HOME - Со ограничување (32 KiB, по стандарден избор), пребарај во секоја папка од Git/project root на
cwd(ако постои) до самиотcwd: додај ја содржината на која било одAGENTS.override.md,AGENTS.md, или кое било име на датотека наведено воproject_doc_fallback_filenames во config.toml - Доколку се конфигурирани некои вештини(се отвора во нов прозорец):
- краток вовед за вештини
- метаподатоците за вештините(се отвора во нов прозорец) за секоја вештина
- дел за како да се користат вештините(се отвора во нов прозорец)
4. Порака со role=user која ја опишува локалната средина во која агентот моментално работи. Ова го специфицира тековниот работен директориум и корисничкиот shell(се отвора во нов прозорец):
Откако Codex ќе ги заврши сите горенаведени пресметки за иницијализација на input, ја додава корисничката порака за започнување на разговорот.
Претходните примери се фокусираа на содржината на секоја порака, но забележи дека секој елемент од input е JSON објект со type, role(се отвора во нов прозорец) и content како што следува:
Откако Codex ќе го изгради целосниот JSON payload за испраќање до Responses API, потоа прави HTTP POST барање со заглавие Authorization, во зависност од тоа како е конфигурирана крајната точка на Responses API во ~/.codex/config.toml (дополнителни HTTP заглавија и параметри за барање се додаваат ако се наведени).
Кога серверот на OpenAI Responses API ќе го прими барањето, го користи JSON за да го изведе промптот за моделот на следниов начин (за да биде сигурен, прилагодена имплементација на Responses API би можела да направи поинаков избор):
Како што можеш да видиш, редоследот на првите три ставки во промптот го одредува серверот, а не клиентот. Сепак, од тие три ставки, само содржината на системската порака е исто така контролирана од серверот, бидејќи алатките и упатствата се определени од клиентот. По нив следува input од JSON payload за да се заврши промптот.
Сега кога го имаме нашиот промпт, подготвени сме да го испробаме моделот.
Ова HTTP барање до API за одговори го иницира првиот „круг“ од разговорот во Codex. Серверот одговара со тек на настани испратени од серверот (SSE(се отвора во нов прозорец)). data на секој настан е JSON payload со „type“ што започнува со „response“, што може да биде нешто како ова (целосна листа на настани може да се најде во нашите API документи(се отвора во нов прозорец)):
Codex го обработува текот на настани(се отвора во нов прозорец) и ги објавува повторно како внатрешни објекти на настани што клиентот може да ги користи. Настани како response.output_text.delta се користат за поддршка на стриминг во корисничкиот интерфејс, додека други настани како response.output_item.added се трансформираат во објекти што се додаваат на input за следни повици до Responses API.
Да претпоставиме дека првото барање до Responses API вклучува два настани response.output_item.done: еден со type=расудување и еден со type=function_call. Овие настани мора да бидат претставени во полето input на JSON кога повторно ќе го испрашаме моделот со одговорот на повикот на алатката:
Резултирачкиот промпт што се користи за семплирање на моделот како дел од следното прашање би изгледал вака:
Особено забележи како стариот промпт е точен префикс на новиот промпт. Ова е намерно, бидејќи тоа ги прави следните барања многу поефикасни затоа што ни овозможува да го искористиме кеширањето на промпт (за кое ќе зборуваме во следниот дел за изведба).
Кога ќе се навратиме на нашиот прв дијаграм на циклусот на агентот, гледаме дека може да има многу повторувања помеѓу изведувањето заклучоци и повикувањето на алатки. Промптот може да продолжи да расте сè додека конечно не добиеме порака од асистент, што укажува на крајот на рундата:
Во Codex CLI, ја прикажуваме пораката од асистентот на корисникот и го фокусираме композиторот за да му укажеме на корисникот дека е негов „ред“ да го продолжи разговорот. Ако корисникот одговори, и пораката од асистентот од претходниот потег, како и новата порака од корисникот, мора да се додадат во input во барањето до Responses API за да започне новиот потег:
Уште еднаш, бидејќи продолжуваме со разговорот, должината на input што го испраќаме до Responses API постојано се зголемува:
Да разгледаме што значи овој постојано растечки промпт за перформансите.
Можеби се прашуваш, „Чекај, зарем циклусот на агентот не е квадратен во однос на количината на JSON испратена до Responses API за време на разговорот?“ И во право си. Иако Responses API поддржува опционален параметар previous_response_id(се отвора во нов прозорец) за ублажување на овој проблем, Codex не го користи денес, главно за да ги задржи барањата целосно без состојба и за да поддржи конфигурации за нула задржување на податоци (ZDR).
Избегнувањето на previous_response_id го поедноставува процесот за давателот на Responses API бидејќи гарантира дека секое барање е без состојба. Ова исто така го прави лесно да се поддржат клиентите кои се одлучиле за нула задржување на податоци (ZDR)(се отвора во нов прозорец), бидејќи складирањето на податоците потребни за поддршка на previous_response_id би било во спротивност со ZDR. Имај предвид дека корисниците на ZDR не ја губат можноста да имаат корист од сопственички пораки за расудување од претходните чекори, бидејќи поврзаната encrypted_content може да се дешифрира на серверот. (OpenAI го задржува клучот за декрипција на клиент со ZDR, но не и нивните податоци.) Види ги барањата за повлекување #642(се отвора во нов прозорец) и #1641(се отвора во нов прозорец) за поврзаните промени во Codex за поддршка на ZDR.
Општо земено, трошокот за земање примероци од моделот е поголем од трошокот за мрежен сообраќај, што го прави земањето примероци главна цел на нашите напори за ефикасност. Затоа кеширањето на промпт е толку важно, бидејќи ни овозможува повторно да ја искористиме пресметката од претходен повик за инференција. Кога добиваме погодоци во кеш-меморијата, семплирањето на моделот е линеарно, а не квадратно. Нашата документација за кеширање на промпт(се отвора во нов прозорец)го објаснува ова подетално:
Погодоците во кеш-меморијата се можни само за точни совпаѓања на префикс во рамките на промпт. За да ги оствариш придобивките од кеширањето, постави статична содржина како инструкции и примери на почетокот на твојот промпт, а променливата содржина, како што се информации специфични за корисникот, стави ја на крајот. Ова важи и за сликите и алатките, кои мора да бидат идентични меѓу барањата.
Имајќи го ова предвид, да разгледаме кои типови операции би можеле да предизвикаат „промашување на кеш-меморијата“ во Codex:
- Промена на
toolsшто се достапни за моделот во средината на разговорот. - Промена на
modelкој е цел на барањето до Responses API (во пракса, ова ја менува третата ставка во оригиналниот промпт, бидејќи содржи инструкции специфични за моделот). - Промена на конфигурацијата на песочната кутија, режимот на одобрување или тековниот работен директориум.
Тимот на Codex мора да биде внимателен при воведувањето на нови функции во Codex CLI кои би можеле да го загрозат кеширањето на промптовите. Како пример, нашата почетна поддршка за MCP алатки воведе грешка при која не успеавме да ги наброиме алатките во доследен редослед(се отвора во нов прозорец), што предизвика промашувања на кешот. Имај предвид дека алатките на MCP може да бидат особено незгодни бидејќи серверите на MCP можат да ја променат листата на алатки што ги обезбедуваат во моментот преку известување notifications/tools/list_changed(се отвора во нов прозорец). Почитувањето на ова известување среде долг разговор може да предизвика скапо промашување на кешот.
Кога е можно, ги обработуваме промените во конфигурацијата што се случуваат среде разговор со додавање на нова порака во input за да се одрази промената, наместо да се менува претходната порака:
- Ако се промени конфигурацијата на sandbox или режимот на одобрување, ќе вметнеме(се отвора во нов прозорец) нова порака
role=developerсо истиот формат како оригиналната ставка<permissions instructions>. - Ако се промени тековниот работен директориум, ќе вметнеме(се отвора во нов прозорец) нова порака
role=userсо истиот формат како оригиналниот<environment_context>.
Ние вложуваме големи напори за да обезбедиме погодоци во кеш-меморијата за подобрување на перформансите. Постои уште еден важен ресурс што треба да го управуваме: прозорецот за контекст.
Нашата општа стратегија за да избегнеме да останеме без прозорец за контекст е да го собереме разговорот штом бројот на токени ќе надмине одреден праг. Поконкретно, го заменуваме input со нова, помала листа на ставки што ја претставува суштината на разговорот, овозможувајќи му на агентот да продолжи со разбирање на тоа што се случило досега. Рана имплементација на компактирање(се отвора во нов прозорец) бараше корисникот рачно да ја повика командата /compact, која ќе го прашува Responses API користејќи го постојниот разговор и сопствени инструкции за сумирање(се отвора во нов прозорец). Codex ја искористи добиената порака од асистентот што го содржи резимето како ново внесување(се отвора во нов прозорец) за следните чекори во разговорот.
Оттогаш, Responses API еволуираше за да поддржува специјален /responses/compact крајна точка(се отвора во нов прозорец) што извршува компактирање поефикасно. Тоа враќа листа на ставки(се отвора во нов прозорец) што може да се користи наместо претходниот input за да се продолжи разговорот, додека се ослободува прозорецот за контекст. Оваа листа вклучува специјален елемент type=compaction со непрозирен елемент encrypted_content кој го зачувува латентното разбирање на моделот за оригиналниот разговор. Сега, Codex автоматски ја користи оваа крајна точка за да го компактира разговорот кога ќе се надмине auto_compact_limit(се отвора во нов прозорец).
Го воведовме циклусот на агентот Codex и објаснивме како Codex го создава и управува својот контекст при поставување барања до моделот. Патем, ги истакнавме практичните размислувања и најдобрите практики што важат за секој што гради агент циклус врз Responses API.
Иако циклусот на агентот ја обезбедува основата за Codex, тоа е само почеток. Во претстојните објави, ќе навлеземе во архитектурата на CLI, ќе истражиме како е имплементирана употребата на алатки и ќе го разгледаме поблиску моделот на песочна кутија на Codex.


