O Codex CLI(abre em uma nova janela) é o nosso agente de software local multiplataforma, projetado para produzir alterações de software confiáveis e de alta qualidade, operando de forma segura e eficiente em sua máquina. Aprendemos uma quantidade enorme sobre como criar um agente de software de nível mundial desde que lançamos a CLI pela primeira vez em abril. Para desvendar essas ideias, este é o primeiro post de uma série contínua onde exploraremos vários aspectos de como o Codex funciona, bem como lições aprendidas com muito esforço. (Para uma visão ainda mais detalhada de como a CLI do Codex é construída, confira nosso repositório de código aberto em https://github.com/openai/codex(abre em uma nova janela).) Muitos dos detalhes mais minuciosos de nossas decisões de design estão registrados em issues e pull requests do GitHub, caso você queira saber mais.
Para começar, vamos nos concentrar no loop do agente, que é a lógica central do Codex CLI responsável por orquestrar a interação entre o usuário, o modelo e as ferramentas que o modelo invoca para realizar tarefas de software significativas. Esperamos que esta publicação lhe proporcione uma boa visão do papel que nosso agente (ou "intermediário") desempenha na utilização de um LLM.
Antes de começarmos, uma breve nota sobre a terminologia: na OpenAI, "Codex" engloba um conjunto de ofertas de agentes de software, incluindo o Codex CLI, o Codex Cloud e a extensão Codex para VS Code. Esta postagem se concentra no framework Codex, que fornece o núcleo do loop do agente e a lógica de execução que sustentam todas as experiências do Codex e são apresentadas por meio da CLI do Codex. Para simplificar, usaremos os termos “Codex” e “Codex CLI” como sinônimos.
No cerne de todo agente de IA está algo chamado "ciclo do agente". Uma ilustração simplificada do loop do agente se parece com isto:
Para começar, o agente recebe informações do usuário para incluir no conjunto de instruções textuais que prepara para o modelo, conhecido como prompt.
O próximo passo é consultar o modelo, enviando-lhe nossas instruções e pedindo que ele gere uma resposta, um processo conhecido como inferência. Durante a inferência, o texto de entrada é primeiro traduzido em uma sequência de tokens(abre em uma nova janela)de entrada — números inteiros que indexam o vocabulário do modelo. Esses tokens são então usados para amostrar o modelo, produzindo uma nova sequência de tokens de saída.
Os tokens de saída são traduzidos de volta para texto, que se torna a resposta do modelo. Como os tokens são produzidos incrementalmente, essa tradução pode ocorrer durante a execução do modelo, e é por isso que muitos aplicativos baseados em LLM exibem saída em fluxo contínuo. Na prática, a inferência geralmente é encapsulada por trás de uma API que opera sobre o texto, abstraindo os detalhes da tokenização.
Como resultado da etapa de inferência, o modelo (1) produz uma resposta final à entrada original do usuário ou (2) solicita uma chamada de ferramenta que o agente deve executar (por exemplo, “execute ls e relate a saída”). No caso de (2), o agente executa a chamada da ferramenta e anexa sua saída ao prompt original. Essa saída é usada para gerar uma nova entrada que é usada para consultar novamente o modelo; o agente pode então levar em consideração essas novas informações e tentar novamente.
Esse processo se repete até que o modelo pare de emitir chamadas de ferramentas e, em vez disso, produza uma mensagem para o usuário (chamada de mensagem de assistente nos modelos da OpenAI). Em muitos casos, esta mensagem responde diretamente à solicitação original do usuário, mas também pode ser uma pergunta complementar.
Como o agente pode executar chamadas de ferramentas que modificam o ambiente local, sua "saída" não se limita à mensagem do assistente. Em muitos casos, o principal resultado de um agente de software é o código que ele escreve ou edita em sua máquina. No entanto, cada turno sempre termina com uma mensagem do assistente — como “Adicionei o architecture.md que você pediu” — que sinaliza um estado de término no loop do agente. Do ponto de vista do agente, seu trabalho está completo e o controle retorna ao usuário.
O percurso desde a entrada do usuário até a resposta do agente, mostrado no diagrama, é denominado de uma rodada de conversa (um tópico no Codex). Embora essa rodada de conversa possa incluir muitas iterações entre a inferência do modelo e as chamadas de ferramentas. Sempre que você envia uma nova mensagem para uma conversa existente, o histórico da conversa é incluído como parte da solicitação para a nova interação, incluindo as mensagens e chamadas de ferramentas das interações anteriores:
Isso significa que, à medida que a conversa se desenvolve, o mesmo acontece com o tamanho do estímulo usado para amostrar o modelo. Esse comprimento é importante porque cada modelo possui uma janela de contexto, que é o número máximo de tokens que ele pode usar para uma chamada de inferência. Observe que esta janela inclui tokens de entrada e saída. Como você pode imaginar, um agente poderia decidir fazer centenas de chamadas de ferramentas em uma única operação, potencialmente esgotando a janela de contexto. Por essa razão, o gerenciamento da janela de contexto é uma das muitas responsabilidades do agente. Agora, vamos analisar como o Codex executa o loop do agente.
A CLI do Codex envia solicitações HTTP para a API de Respostas(abre em uma nova janela) para executar a inferência do modelo. Analisaremos como as informações fluem pelo Codex, que usa a API de Respostas para controlar o ciclo do agente.
O endpoint da API de Respostas que a CLI do Codex utiliza é configurável(abre em uma nova janela), podendo ser usado com qualquer endpoint que implemente a API de Respostas(abre em uma nova janela):
- Ao usar o login do ChatGPT(abre em uma nova janela) com a CLI do Codex, ele utiliza
https://chatgpt.com/backend-api/codex/responsescomo o endpoint - Ao usar a autenticação por chave de API(abre em uma nova janela) com modelos hospedados pela OpenAI, utiliza-se
https://api.openai.com/v1/responsescomo o endpoint - Ao executar o Codex CLI com
--osspara usar o gpt-oss com o ollama 0.13.4+(abre em uma nova janela) ou LM Studio 0.3.39+(abre em uma nova janela), o padrão é executarhttp://localhost:11434/v1/responseslocalmente no seu computador. - A CLI do Codex pode ser usada com a API de Respostas hospedada por um provedor de nuvem, como o Azure.
Vamos explorar como o Codex cria o prompt para a primeira chamada de inferência em uma conversa.
Como usuário final, você não especifica o prompt usado para amostrar o modelo literalmente ao consultar a API de Respostas. Em vez disso, você especifica vários tipos de entrada como parte da sua consulta, e o servidor da Responses API decide como estruturar essas informações em um prompt que o modelo foi projetado para consumir. Você pode pensar no prompt como uma “lista de itens”; esta seção explicará como sua consulta é transformada nessa lista.
Na mensagem inicial, cada item da lista está associado a uma função. A role indica o peso que o conteúdo associado deve ter e é um dos seguintes valores (em ordem decrescente de prioridade): system, developer, user, assistant.
A API de Respostas(abre em uma nova janela) aceita uma carga útil JSON com vários parâmetros. Vamos nos concentrar nestes três:
instructions(abre em uma nova janela): mensagem do sistema (ou do desenvolvedor) inserida no contexto do modelotools(abre em uma nova janela): uma lista de ferramentas que o modelo pode chamar ao gerar uma respostainput(abre em uma nova janela): uma lista de entradas de texto, imagem ou arquivo para o modelo
No Codex, o campo instructions é lido do arquivo model_instructions_file(abre em uma nova janela) em ~/.codex/config.toml, se especificado; caso contrário, as base_instructions associadas a um modelo(abre em uma nova janela) são utilizadas. As instruções específicas para cada modelo estão no repositório Codex e são incluídas na CLI (por exemplo, gpt-5.2-codex_prompt.md(abre em uma nova janela)).
O campo tools é uma lista de definições de ferramentas que estão em conformidade com um esquema definido pela API de Respostas. Para o Codex, isso inclui ferramentas fornecidas pela CLI do Codex, ferramentas fornecidas pela API de Respostas que devem ser disponibilizadas ao Codex, bem como ferramentas fornecidas pelo usuário, geralmente por meio de servidores MCP:
Por fim, o campo input da carga útil JSON é uma lista de itens. O Codex insere os seguintes itens(abre em uma nova janela) no input antes de adicionar a mensagem do usuário:
1. Uma mensagem com role=developer que descreve o ambiente de teste (sandbox) que aplica-se somente à ferramenta shell fornecida pelo Codex e definida na seção tools. Ou seja, outras ferramentas, como as fornecidas pelos servidores MCP, não são isoladas pelo Codex e são responsáveis por impor suas próprias salvaguardas.
A mensagem é construída a partir de um modelo onde as partes principais do conteúdo vêm de trechos de Markdown incorporados no Codex CLI, como workspace_write.md(abre em uma nova janela) e on_request.md(abre em uma nova janela):
2. (Opcional) Uma mensagem com role=developer cujo conteúdo é o valor de developer_instructions lido do arquivo config.toml do usuário.
3. (Opcional) Uma mensagem com role=user cujo conteúdo são as “instruções do usuário”, que não são provenientes de um único arquivo, mas são agregadas de várias fontes(abre em uma nova janela). Em geral, instruções mais específicas aparecem posteriormente:
- Conteúdo dos arquivos
AGENTS.override.mdeAGENTS.mdem$CODEX_HOME - Sujeito a um limite (32 KiB, por padrão), procure em cada pasta desde a raiz Git/do projeto do
cwd(se existir) até o própriocwd: adicione o conteúdo de qualquer um dosAGENTS.override.md,AGENTS.md, ou qualquer nome de arquivo especificado porproject_doc_fallback_filenames in config.toml - Caso alguma habilidade(abre em uma nova janela) tenha sido configurada:
- um breve preâmbulo sobre habilidades
- os metadados de habilidade(abre em uma nova janela) para cada habilidade
- uma seção sobre como usar competências(abre em uma nova janela)
4. Uma mensagem com role=user que descreve o ambiente local em que o agente está operando atualmente. Isso especifica o diretório de trabalho atual e o shell do usuário(abre em uma nova janela):
Após o Codex realizar todos os cálculos acima para inicializar a input, ele anexa a mensagem do usuário para iniciar a conversa.
Os exemplos anteriores focaram no conteúdo de cada mensagem, mas observe que cada elemento de input é um objeto JSON com type, role(abre em uma nova janela) e content , conforme segue:
Depois que o Codex constrói a carga útil JSON completa para enviar à API de Respostas, ele faz a solicitação HTTP POST com um cabeçalho Authorization , dependendo de como o endpoint da API de Respostas está configurado em ~/.codex/config.toml (cabeçalhos HTTP e parâmetros de consulta adicionais são adicionados, se especificados).
Quando um servidor da API de Respostas da OpenAI recebe a solicitação, ele usa o JSON para derivar o prompt para o modelo da seguinte forma (é importante ressaltar que uma implementação personalizada da API de Respostas poderia fazer uma escolha diferente):
Como você pode ver, a ordem dos três primeiros itens no prompt é determinada pelo servidor, não pelo cliente. Dito isso, desses três itens, apenas o conteúdo da mensagem do sistema também é controlado pelo servidor, já que as tools e instructions são determinadas pelo cliente. Em seguida, são inseridos os input do payload JSON para completar a solicitação.
Agora que temos nosso prompt, estamos prontos para testar o modelo.
Esta requisição HTTP para a API de Respostas inicia a primeira "interrupção" de uma conversa no Codex. O servidor responde com um fluxo de Eventos Enviados pelo Servidor (SSE(abre em uma nova janela)). O data de cada evento é um payload JSON com um "type" que começa com "response", que pode ser algo assim (uma lista completa de eventos pode ser encontrada em nossa documentação da API(abre em uma nova janela)):
O Codex consome o fluxo de eventos(abre em uma nova janela) e os republica como objetos de eventos internos que podem ser usados por um cliente. Eventos como response.output_text.delta são usados para dar suporte ao streaming na interface do usuário, enquanto outros eventos, como response.output_item.added são transformados em objetos que são anexados à input para chamadas subsequentes da API de Respostas.
Suponha que a primeira solicitação à API de Respostas inclua dois eventos response.output_item.done : um com type=reasoning e outro com type=function_call. Esses eventos devem estar representados no campo input do JSON quando consultarmos o modelo novamente com a resposta à chamada da ferramenta:
O prompt resultante, usado para amostrar o modelo como parte da consulta subsequente, seria semelhante a este:
Observe, em particular, como o prompt antigo é um prefixo exato do novo prompt. Isso é intencional, pois torna as solicitações subsequentes muito mais eficientes, já que nos permite aproveitar o cache de consultas rápidas (que discutiremos na próxima seção sobre desempenho).
Analisando novamente nosso primeiro diagrama do ciclo do agente, vemos que pode haver muitas iterações entre a inferência e a chamada da ferramenta. O prompt pode continuar a crescer até que finalmente recebamos uma mensagem do assistente, indicando o fim da rodada:
Na CLI do Codex, apresentamos a mensagem do assistente ao usuário e direcionamos o foco do compositor para indicar ao usuário que é a sua vez de continuar a conversa. Se o usuário responder, tanto a mensagem do assistente da rodada anterior quanto a nova mensagem do usuário devem ser anexadas à input na solicitação da API de Respostas para iniciar a nova rodada:
Mais uma vez, como estamos dando continuidade a uma conversa, o tamanho da input que enviamos para a API de Respostas continua aumentando:
Vamos analisar o que esse estímulo cada vez maior significa para o desempenho.
Você pode estar se perguntando: "Espere, o ciclo do agente não é quadrático em termos da quantidade de JSON enviada para a API de Respostas ao longo da conversa?" E você está certo. Embora a API de Respostas suporte um parâmetro opcional previous_response_id(abre em uma nova janela) para mitigar esse problema, o Codex não o utiliza atualmente, principalmente para manter as solicitações totalmente sem estado e para dar suporte a configurações de Retenção Zero de Dados (ZDR).
Evitar previous_response_id simplifica as coisas para o provedor da API de Respostas, pois garante que cada solicitação seja sem estado. Isso também facilita o suporte a clientes que optaram pela Retenção Zero de Dados (ZDR)(abre em uma nova janela), já que armazenar os dados necessários para dar suporte previous_response_id seria incompatível com a ZDR. Note que os clientes ZDR não perdem a capacidade de se beneficiarem de mensagens de raciocínio proprietárias de turnos anteriores, já que o encrypted_content associado pode ser descriptografado no servidor. (A OpenAI armazena a chave de descriptografia de um cliente ZDR, mas não seus dados.) Consulte os PRs #642(abre em uma nova janela) e #1641(abre em uma nova janela) para obter informações sobre as alterações relacionadas ao Codex para oferecer suporte ao ZDR.
Em geral, o custo da amostragem do modelo supera o custo do tráfego de rede, tornando a amostragem o principal alvo de nossos esforços de eficiência. É por isso que o cache de prompts é tão importante, pois nos permite reutilizar cálculos de uma chamada de inferência anterior. Quando temos acertos de cache, a amostragem do modelo é linear em vez de quadrática. Nossa documentação sobre cache de prompts (abre em uma nova janela)explica isso com mais detalhes:
A obtenção de resultados em cache só é possível para correspondências exatas de prefixos dentro de um prompt. Para aproveitar os benefícios do cache, coloque o conteúdo estático, como instruções e exemplos, no início do seu prompt e o conteúdo variável, como informações específicas do usuário, no final. Isso também se aplica a imagens e ferramentas, que devem ser idênticas entre as solicitações.
Com isso em mente, vamos considerar quais tipos de operações podem causar uma "falha de cache" no Codex:
- Alterar as
toolsdisponíveis para o modelo no meio da conversa. - Alterar o
modelque é o alvo da solicitação da API de Respostas (na prática, isso altera o terceiro item no prompt original, pois ele contém instruções específicas do modelo). - Alterar a configuração do sandbox, o modo de aprovação ou o diretório de trabalho atual.
A equipe do Codex deve ser diligente ao introduzir novos recursos na CLI do Codex que possam comprometer o cache de prompts. Como exemplo, nosso suporte inicial para ferramentas MCP introduziu um bug no qual não conseguíamos enumerar as ferramentas em uma ordem consistente(abre em uma nova janela), causando falhas de cache. Note que as ferramentas MCP podem ser particularmente complicadas, pois os servidores MCP podem alterar a lista de ferramentas que fornecem dinamicamente por meio de uma notificação notifications/tools/list_changed(abre em uma nova janela) . Ignorar essa notificação no meio de uma conversa longa pode causar uma falha de cache dispendiosa.
Sempre que possível, lidamos com alterações de configuração que ocorrem durante a conversa adicionando uma nova mensagem à input para refletir a alteração, em vez de modificar uma mensagem anterior:
- Se a configuração do sandbox ou o modo de aprovação for alterado, inseriremos(abre em uma nova janela) uma nova mensagem
role=developercom o mesmo formato do item<permissions instructions>original. - Se o diretório de trabalho atual for alterado, inserimos(abre em uma nova janela) uma nova mensagem
role=usercom o mesmo formato da mensagem original<environment_context>.
Fazemos todos os esforços possíveis para garantir acertos no cache e, consequentemente, um melhor desempenho. Existe outro recurso fundamental que precisamos gerenciar: a janela de contexto.
Nossa estratégia geral para evitar ficar sem espaço na janela de contexto é compactar a conversa assim que o número de tokens ultrapassar um determinado limite. Especificamente, substituímos a input por uma nova lista menor de itens que seja representativa da conversa, permitindo que o agente continue com uma compreensão do que aconteceu até o momento. Uma implementação inicial de compactação(abre em uma nova janela) exigia que o usuário invocasse manualmente o comando /compact , que consultava a API de Respostas usando a conversa existente mais instruções personalizadas para sumarização(abre em uma nova janela). Codex utilizou a mensagem do assistente resultante, contendo o resumo , como a nova input(abre em uma nova janela) para as interações subsequentes na conversa.
Desde então, a API de Respostas evoluiu para oferecer suporte a um /responses/compact endpoint(abre em uma nova janela) que realiza a compactação de forma mais eficiente. Retorna uma lista de itens(abre em uma nova janela) que podem ser usados no lugar da input anterior para continuar a conversa, liberando a janela de contexto. Esta lista inclui um item especial type=compaction com um item opaco encrypted_content que preserva o entendimento latente que o modelo tem da conversa original. Agora, o Codex usa automaticamente esse endpoint para compactar a conversa quando o auto_compact_limit(abre em uma nova janela) é excedido.
Apresentamos o loop do agente Codex e explicamos como o Codex cria e gerencia seu contexto ao consultar um modelo. Ao longo do processo, destacamos considerações práticas e boas práticas que se aplicam a qualquer pessoa que esteja criando um loop de agente com base na API de Respostas.
Embora o loop do agente forneça a base para o Codex, ele é apenas o começo. Em publicações futuras, vamos analisar a arquitetura da CLI, explorar como o uso de ferramentas é implementado e examinar mais de perto o modelo de sandbox do Codex.


