Pular para o conteúdo principal
OpenAI

23 de janeiro de 2026

Engenharia

Desdobrando o loop do agente Codex

Por Michael Bolin, membro da equipe técnica.

Carregando…

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.

O loop do agente

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:

Diagrama intitulado "Ciclo do agente" ilustrando como um sistema de IA processa uma solicitação do usuário, chama ferramentas, observa os resultados, atualiza seu plano e retorna as saídas. As setas conectam etapas como entrada do usuário, raciocínio do modelo, ações da ferramenta e resposta final.

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:

Diagrama intitulado "Loop de agente de múltiplas voltas" mostrando como um agente de IA recebe entradas do usuário de forma iterativa, gera ações, consulta ferramentas, atualiza o estado e retorna resultados. Inclui etapas rotuladas, setas e exemplos de saídas de ferramentas que ilustram o ciclo de raciocínio do agente.

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.

Inferência de modelo

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):

Vamos explorar como o Codex cria o prompt para a primeira chamada de inferência em uma conversa.

Construindo o prompt inicial

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:

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:

JavaScript

1
[
2
// Codex's default shell tool for spawning new processes locally.
3
{
4
"type": "function",
5
"name": "shell",
6
"description": "Runs a shell command and returns its output...",
7
"strict": false,
8
"parameters": {
9
"type": "object",
10
"properties": {
11
"command": {"type": "array", "description": "The command to execute", ...},
12
"workdir": {"description": "The working directory...", ...},
13
"timeout_ms": {"description": "The timeout for the command...", ...},
14
...
15
},
16
"required": ["command"],
17
}
18
}
19

20
// Codex's built-in plan tool.
21
{
22
"type": "function",
23
"name": "update_plan",
24
"description": "Updates the task plan...",
25
"strict": false,
26
"parameters": {
27
"type": "object",
28
"properties": {"plan":..., "explanation":...},
29
"required": ["plan"]
30
}
31
},
32

33
// Web search tool provided by the Responses API.
34
{
35
"type": "web_search",
36
"external_web_access": false
37
},
38

39
// MCP server for getting weather as configured in the
40
// user's ~/.codex/config.toml.
41
{
42
"type": "function",
43
"name": "mcp__weather__get-forecast",
44
"description": "Get weather alerts for a US state",
45
"strict": false,
46
"parameters": {
47
"type": "object",
48
"properties": {"latitude": {...}, "longitude": {...}},
49
"required": ["latitude", "longitude"]
50
}
51
}
52
]

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):

Texto simples

1
<permissions instructions>
2
- description of the sandbox explaining file permissions and network access
3
- instructions for when to ask the user for permissions to run a shell command
4
- list of folders writable by Codex, if any
5
</permissions instructions>

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:

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):

Texto simples

1
<environment_context>
2
<cwd>/Users/mbolin/code/codex5</cwd>
3
<shell>zsh</shell>
4
</environment_context>

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:

JSON

1
{
2
"type": "message",
3
"role": "user",
4
"content": [
5
{
6
"type": "input_text",
7
"text": "Add an architecture diagram to the README.md"
8
}
9
]
10
}

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):

Diagrama instantâneo mostrando uma única etapa em um ciclo de um agente de IA. Uma solicitação do usuário entra no modelo, que produz um pensamento, uma ação com o nome de uma ferramenta e uma entrada de ferramenta. O diagrama destaca essa etapa intermediária de raciocínio antes da utilização da ferramenta.

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.

A primeira curva

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)):

Texto simples

1
data: {"type":"response.reasoning_summary_text.delta","delta":"ah ", ...}
2
data: {"type":"response.reasoning_summary_text.delta","delta":"ha!", ...}
3
data: {"type":"response.reasoning_summary_text.done", "item_id":...}
4
data: {"type":"response.output_item.added", "item":{...}}
5
data: {"type":"response.output_text.delta", "delta":"forty-", ...}
6
data: {"type":"response.output_text.delta", "delta":"two!", ...}
7
data: {"type":"response.completed","response":{...}}

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: 

JavaScript

1
[
2
/* ... original 5 items from the input array ... */
3
{
4
"type": "reasoning",
5
"summary": [
6
"type": "summary_text",
7
"text": "**Adding an architecture diagram for README.md**\n\nI need to..."
8
],
9
"encrypted_content": "gAAAAABpaDWNMxMeLw..."
10
},
11
{
12
"type": "function_call",
13
"name": "shell",
14
"arguments": "{\"command\":\"cat README.md\",\"workdir\":\"/Users/mbolin/code/codex5\"}",
15
"call_id": "call_8675309..."
16
},
17
{
18
"type": "function_call_output",
19
"call_id": "call_8675309...",
20
"output": "<p align=\"center\"><code>npm i -g @openai/codex</code>..."
21
}
22
]

O prompt resultante, usado para amostrar o modelo como parte da consulta subsequente, seria semelhante a este:

Diagrama rotulado como "Snapshot 2" mostrando um agente de IA após uma chamada de ferramenta. O modelo recebe uma observação da ferramenta e produz um novo pensamento e ação. As setas conectam entradas, observações e saídas para ilustrar como o agente itera seu ciclo de raciocínio.

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:

Texto simples

1
data: {"type":"response.output_text.done","text": "I added a diagram to explain...", ...}
2
data: {"type":"response.completed","response":{...}}

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:

JavaScript

1
[
2
/* ... all items from the last Responses API request ... */
3
{
4
"type": "message",
5
"role": "assistant",
6
"content": [
7
{
8
"type": "output_text",
9
"text": "I added a diagram to explain the client/server architecture."
10
}
11
]
12
},
13
{
14
"type": "message",
15
"role": "user",
16
"content": [
17
{
18
"type": "input_text",
19
"text": "That's not bad, but the diagram is missing the bike shed."
20
}
21
]
22
}
23
]

Mais uma vez, como estamos dando continuidade a uma conversa, o tamanho da input que enviamos para a API de Respostas continua aumentando:

Diagrama intitulado "Instantâneo 3" mostrando o estágio final de um ciclo de agente de IA. Após receber os resultados da ferramenta, o modelo gera uma conclusão e uma resposta final é retornada ao usuário. As setas ilustram a transição da saída da ferramenta para a resposta completa.

Vamos analisar o que esse estímulo cada vez maior significa para o desempenho.

Considerações sobre 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 tools disponíveis para o modelo no meio da conversa.
  • Alterar o model que é 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=developer com 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=user com 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.

Próximos passos

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.

Autoria

Michael Bolin

Agradecimentos

Um agradecimento especial a toda a equipe que desenvolveu a CLI do Codex.