Saltar para o conteúdo principal
OpenAI

23 de janeiro de 2026

Engenharia

Desenrolar o ciclo do agente Codex

Por Michael Bolin, membro da Equipa Técnica

A carregar…

A Codex CLI(abre numa nova janela) é o nosso agente de software local multiplataforma, concebido para realizar alterações de software de alta qualidade e fiáveis, operando de forma segura e eficiente no teu computador. Aprendemos muito sobre como construir um agente de software de classe mundial desde que lançámos a CLI em abril. Para desvendar estas ideias, este é o primeiro artigo de uma série contínua onde iremos explorar vários aspetos do funcionamento do Codex, bem como lições aprendidas com muito esforço. (Para uma visão ainda mais detalhada sobre como a Codex CLI é construída, visita o nosso repositório de código aberto em https://github.com/openai/codex(abre numa nova janela). Muitos dos detalhes mais minuciosos das nossas decisões de design estão registados em issues e pull requests do GitHub, caso queiras saber mais.)

Para começar, vamos focar-nos no ciclo do agente, que é a lógica central da Codex CLI responsável por orquestrar a interação entre o utilizador, o modelo e as ferramentas que o modelo invoca para realizar trabalho de software significativo. Esperamos que esta publicação te dê uma boa perspetiva sobre o papel que o nosso agente (ou "harness") desempenha na utilização de um LLM.

Antes de começarmos, uma nota breve sobre a terminologia: na OpenAI, o "Codex" abrange um conjunto de ofertas de agentes de software, incluindo Codex CLI, Codex Cloud e a extensão Codex VS Code. Esta publicação foca-se no harness do Codex, que fornece o ciclo central do agente e a lógica de execução que sustenta todas as experiências do Codex e é apresentada através da Codex CLI. Para facilitar, usaremos os termos "Codex" e "Codex CLI" como sinónimos.

O ciclo do agente

No centro de cada agente de IA está algo chamado "o ciclo do agente". Uma ilustração simplificada do ciclo do agente tem este aspeto:

Diagrama intitulado "Ciclo do agente" que ilustra como um sistema de IA processa um pedido do utilizador, faz chamada de ferramentas, observa os resultados, atualiza o seu plano e devolve os resultados. As setas ligam etapas como a entrada do utilizador, o raciocínio do modelo, as ações das ferramentas e a resposta final.

Para começar, o agente recebe inputs do utilizador para incluir no conjunto de instruções textuais que prepara para o modelo, conhecido como um prompt.

O passo seguinte é interagir com o modelo, enviando-lhe as nossas instruções e pedindo-lhe para gerar uma resposta, um processo conhecido como inferência. Durante a inferência, o prompt textual é primeiro traduzido numa sequência de tokens(abre numa nova janela) de entrada — números inteiros que indexam o vocabulário do modelo. Estes tokens são então utilizados para testar o modelo, produzindo uma nova sequência de tokens de saída.

Os tokens de saída são convertidos novamente em texto, que se torna a resposta do modelo. Como os tokens são gerados de forma incremental, esta tradução pode ocorrer durante a execução do modelo, e é por isso que muitas aplicações baseadas em LLM apresentam resultados em fluxo contínuo. Na prática, a inferência é geralmente encapsulada por detrás de uma API que opera em texto, abstraindo os detalhes da tokenização.

Como resultado da etapa de inferência, o modelo (1) produz uma resposta final à entrada original do utilizador, ou (2) solicita uma chamada de ferramenta que o agente deve executar (por exemplo, "executar ls e reportar o resultado"). No caso de (2), o agente executa a chamada da ferramenta e adiciona o seu resultado ao prompt original. Este resultado é utilizado para gerar uma nova entrada que é utilizada para consultar novamente o modelo; o agente pode então ter em conta esta nova informação e tentar novamente.

Este processo repete-se até que o modelo deixe de emitir chamadas de ferramentas e, em vez disso, produza uma mensagem para o utilizador (chamada mensagem do assistente nos modelos da OpenAI). Em muitos casos, esta mensagem responde diretamente ao pedido original do utilizador, mas também pode ser uma questão complementar.

Como o agente pode executar chamadas de ferramentas que modificam o ambiente local, o seu "resultado" não se limita à mensagem do assistente. Em muitos casos, o resultado principal de um agente de software é o código que escreve ou edita no teu equipamento. No entanto, cada interação termina sempre com uma mensagem do assistente — como "Adicionei o architecture.md que pediste" —, que sinaliza o fim do ciclo do agente. Da perspetiva do agente, o seu trabalho está concluído e o controlo regressa ao utilizador.

O percurso desde a entrada do utilizador até à resposta do agente apresentado no diagrama é denominado interação de uma conversa (um thread no Codex). Embora esta interação de conversa possa incluir muitas iterações entre a inferência do modelo e chamadas de ferramentas. Sempre que envias uma nova mensagem para uma conversa existente, o histórico da conversa é incluído como parte do prompt para a nova interação, que inclui as mensagens e as chamadas de ferramentas de interações anteriores:

Diagrama intitulado "Ciclo de agente de múltiplas interações" que mostra como um agente de IA recebe entradas do utilizador de forma iterativa, gera ações, consulta ferramentas, atualiza o estado e devolve resultados. Inclui passos rotulados, setas e exemplos de resultados de ferramentas que ilustram o ciclo de raciocínio do agente.

Isto significa que, à medida que a conversa se desenvolve, o mesmo acontece com o tamanho do prompt utilizado para testar o modelo. Este comprimento é importante porque cada modelo tem uma janela de contexto, que é o número máximo de tokens que pode utilizar para uma chamada de inferência. Nota que esta janela inclui tokens de entrada e de saída. Como podes imaginar, um agente poderia decidir fazer centenas de chamadas de ferramentas numa única interação, esgotando, potencialmente, a janela de contexto. Por isto mesmo, a gestão da janela de contexto é uma das várias responsabilidades do agente. Agora, vamos analisar como o Codex executa o ciclo do agente.

Inferência de modelo

A Codex CLI envia pedidos HTTP para a API Responses(abre numa nova janela) para executar a inferência do modelo. Vamos analisar como a informação flui através do Codex, que usa a API Responses para conduzir o ciclo do agente.

O endpoint da API Responses que o Codex CLI utiliza é configurável(abre numa nova janela), por isso pode ser utilizado com qualquer endpoint que implemente a API Responses(abre numa nova janela):

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

Criar o prompt inicial

Como utilizador final, não especificas o prompt utilizado para testar o modelo palavra por palavra quando consultas a API Responses. Em vez disso, especificas vários tipos de entrada como parte da tua consulta, e o servidor da API Responses decide como estruturar essa informação num prompt que o modelo foi concebido para consumir. Podes pensar no prompt como uma "lista de itens"; esta secção vai explicar como a tua consulta é transformada nessa lista.

No prompt inicial, cada item na lista está associado a uma função. O função indica a importância que o conteúdo associado deve ter, e é um dos seguintes valores (por ordem decrescente de prioridade): sistema, programador, utilizador, assistente.

A Responses API(abre numa nova janela) aceita uma capacidade JSON com vários parâmetros. Vamos concentrar-nos nestes três:

No Codex, o campo instruções é lido a partir do model_instructions_file(abre numa nova janela) em ~/.codex/config.toml, se especificado; caso contrário, são utilizadas as base_instructions associadas a um modelo(abre numa nova janela). As instruções específicas do modelo estão no repositório Codex e estão incluídas na CLI (por exemplo, gpt-5.2-codex_prompt.md(abre numa nova janela)).

O campo ferramentas é uma lista de definições de ferramentas que estão em conformidade com um esquema definido pela API Responses. Para o Codex, isto inclui ferramentas fornecidas pela Codex CLI, ferramentas fornecidas pela API Responses que devem ser disponibilizadas ao Codex, bem como ferramentas fornecidas pelo utilizador, geralmente através 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
]

Finalmente, o campo input da capacidade JSON é uma lista de itens. O Codex insere os seguintes itens(abre numa nova janela) no input antes de adicionar a mensagem do utilizador:

1. Uma mensagem com role=developer que descreve a sandbox que se aplica apenas à ferramenta shell fornecida pelo Codex definida na secção ferramentas. Ou seja, outras ferramentas, como as disponibilizadas pelos servidores MCP, não são isoladas em sandbox pelo Codex e são responsáveis por implementar as suas próprias salvaguardas.

A mensagem é construída a partir de um modelo onde os elementos principais do conteúdo provêm de excertos de Markdown incluídos na Codex CLI, como workspace_write.md(abre numa nova janela) e on_request.md(abre numa 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 developer_instructions lido do ficheiro config.toml do utilizador.

3. (Opcional) Uma mensagem com role=user cujo conteúdo são as "instruções do utilizador", que não são provenientes de um único ficheiro, mas agregadas a partir de várias fontes(abre numa nova janela). Em geral, as instruções mais específicas surgem posteriormente:

4. Uma mensagem com role=user que descreve o ambiente local em que o agente está a operar atualmente. Isto especifica o diretório de trabalho atual e a shell do utilizador(abre numa nova janela):

Texto simples

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

Assim que o Codex tiver efetuado todos os cálculos acima para inicializar o input, adiciona a mensagem do utilizador para iniciar a conversa.

Os exemplos anteriores centraram-se no conteúdo de cada mensagem, mas nota que cada elemento de input é um objeto JSON com tipo, função(abre numa nova janela) e conteúdo da seguinte forma:

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
}

Assim que o Codex cria a capacidade JSON completa para enviar à API Responses, faz o pedido HTTP POST com um cabeçalho Autorização, dependendo de como o endpoint da API Responses está configurado em ~/.codex/config.toml (cabeçalhos HTTP adicionais e parâmetros de consulta são adicionados, se especificados).

Quando um servidor da API Responses da OpenAI recebe o pedido, utiliza o JSON para derivar o prompt para o modelo da seguinte forma (é importante realçar que uma implementação personalizada da API Responses poderia fazer uma escolha diferente):

Diagrama que mostra uma única etapa num ciclo de um agente de IA. Um pedido do utilizador entra no modelo, que produz um pensamento, uma ação com o nome de uma ferramenta e uma entrada de ferramenta. O diagrama destaca esta etapa intermédia de raciocínio antes da utilização da ferramenta.

Como podes ver, a ordem dos três primeiros itens no prompt é determinada pelo servidor, não pelo cliente. Posto isto, desses três itens, apenas o conteúdo da mensagem do sistema também é controlado pelo servidor, uma vez que as ferramentas e as instruções são determinadas pelo cliente. Estes são seguidos pelo input da capacidade JSON para completar o prompt.

Agora que temos o nosso prompt, estamos prontos para testar o modelo.

A primeira interação

Este pedido HTTP à API Responses inicia a primeira "interação" de uma conversa no Codex. O servidor responde com um fluxo de Server-Sent Events (SSE(abre numa nova janela)). Os dados de cada evento são uma capacidade JSON com um "tipo" que começa com "resposta", que poderia ser algo como isto (podes encontrar uma lista completa de eventos na nossa documentação da API(abre numa 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 numa nova janela) e republica-os como objetos de eventos internos que podem ser utilizados por um cliente. Eventos como response.output_text.delta são utilizados para suportar o streaming na IU, enquanto outros eventos, como response.output_item.added, são transformados em objetos que são anexados ao input para chamadas subsequentes da API Responses.

Supõe que o primeiro pedido à API Responses inclui dois eventos response.output_item.done: um com type=reasoning e outro com type=function_call. Estes eventos devem estar representados no campo input do JSON quando voltarmos a consultar o modelo 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, utilizado para testar o modelo como parte da consulta seguinte, seria semelhante a este:

Diagrama intitulado "Snapshot 2" que mostra 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 ligam entradas, observações e resultados para ilustrar como o agente itera o seu ciclo de raciocínio.

Em particular, nota como o prompt antigo é um prefixo exato do novo prompt. Isto é intencional, uma vez que torna os pedidos subsequentes muito mais eficientes porque nos permite tirar partido do prompt caching (que discutiremos na próxima secção sobre desempenho).

Ao analisarmos o nosso primeiro diagrama do ciclo do agente, percebemos que podem ocorrer muitas iterações entre a inferência e a chamada da ferramenta. O prompt pode continuar a crescer até finalmente recebermos uma mensagem do assistente, indicando o fim da interação:

Texto simples

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

Na Codex CLI, apresentamos a mensagem do assistente ao utilizador e direcionamos o foco do compositor para indicar ao utilizador que é a sua vez de continuar a conversa. Se o utilizador responder, tanto a mensagem do assistente da interação anterior como a nova mensagem do utilizador devem ser anexadas ao input no pedido da API Responses para iniciar a nova interação:

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 a dar continuidade a uma conversa, o comprimento do input que enviamos para a API Responses continua a aumentar:

Diagrama intitulado "Snapshot 3" que mostra a fase final de um ciclo de agente de IA. Após receber os resultados da ferramenta, o modelo gera uma conclusão e é devolvida uma resposta final ao utilizador. As setas ilustram a transição do resultado da ferramenta para a resposta finalizada.

Vamos analisar o que este prompt em constante crescimento significa para o desempenho.

Considerações de desempenho

Talvez estejas a perguntar-te: "Espera, o ciclo do agente não é quadrático em termos da quantidade de JSON enviada para a API Responses ao longo da conversa?" E tens razão. Embora a API Responses suporte um parâmetro opcional previous_response_id(abre numa nova janela) para mitigar este problema, o Codex não o utiliza atualmente, principalmente para manter os pedidos totalmente sem estado e para suportar configurações de Retenção Zero de Dados (ZDR).

Evitar previous_response_id simplifica as coisas para o fornecedor da API Responses porque garante que cada pedido é sem estado. Isto também facilita o apoio aos clientes que optaram pela Retenção Zero de Dados (ZDR)(abre numa nova janela), uma vez que armazenar os dados necessários para suportar previous_response_id seria incompatível com a ZDR. Note-se que os clientes ZDR não perdem a capacidade de beneficiar de mensagens de raciocínio proprietárias de interações anteriores, uma vez que o encrypted_content associado pode ser desencriptado no servidor. (A OpenAI armazena a chave de desencriptação de um cliente ZDR, mas não os seus dados.) Consulta os PR #642(abre numa nova janela) e #1641(abre numa nova janela) para mais informações sobre as alterações relacionadas com o Codex para suportar ZDR.

Em geral, o custo da teste do modelo supera o custo do tráfego de rede, fazendo do teste o principal alvo dos nossos esforços de eficiência. É por isso que a cache de prompts é tão importante, porque nos permite reutilizar cálculos de uma chamada de inferência anterior. Quando obtemos acertos de cache, o teste do modelo é linear em vez de quadrático. A nossa documentação sobre prompt caching (abre numa nova janela)explica isto com mais detalhe:

Os acertos de cache só são possíveis para correspondências exatas de prefixos num prompt. Para usufruíres dos benefícios da cache, coloca conteúdo estático, como instruções e exemplos, no início do teu prompt e coloca conteúdo variável, como informações específicas do utilizador, no final. Isto também se aplica a imagens e ferramentas, que devem ser idênticas entre pedidos.

Tendo isto em mente, vamos considerar que tipos de operações poderiam causar uma " falha de cache" no Codex:

  • Alterar as ferramentas disponíveis para o modelo a meio da conversa.
  • Alterar o modelo que é o alvo do pedido da API Responses (na prática, isto altera o terceiro item no prompt original, uma vez que contém instruções específicas do modelo).
  • Alterar a configuração da sandbox, o modo de aprovação ou o diretório de trabalho atual.

A equipa do Codex deve ser diligente ao introduzir novas funcionalidades na Codex CLI que possam comprometer a cache de prompts. Por exemplo, o nosso suporte inicial para ferramentas MCP introduziu um bug em que não conseguíamos enumerar as ferramentas numa ordem consistente(abre numa nova janela), causando falhas de cache. Nota que as ferramentas MCP podem ser particularmente complicadas, uma vez que os servidores MCP podem alterar a lista de ferramentas que fornecem em tempo real através de uma notificação notifications/tools/list_changed(abre numa nova janela) . Respeitar esta notificação a meio de uma conversa longa pode causar uma falha de cache dispendiosa.

Sempre que possível, gerimos as alterações de configuração que ocorrem durante uma conversa, adicionando uma nova mensagem ao input para refletir a alteração, em vez de modificar uma mensagem anterior:

  • Se a configuração da sandbox ou o modo de aprovação mudar, inserimos(abre numa nova janela) uma nova mensagem role=developer com o mesmo formato do item original <permissions instructions>.
  • Se o diretório de trabalho atual mudar, inserimos(abre numa nova janela) uma nova mensagem role=user com o mesmo formato da original <environment_context>.

Fazemos todos os esforços possíveis para garantir acertos na cache e, consequentemente, um melhor desempenho. Há outro recurso fundamental que temos de gerir: a janela de contexto.

A nossa estratégia geral para evitar esgotar a janela de contexto é comprimir a conversa assim que o número de tokens ultrapassa um determinado limite. Especificamente, substituímos o input por uma nova lista de itens mais pequena, que seja representativa da conversa, permitindo ao agente continuar a compreender o que aconteceu até então. Uma implementação inicial da compressão(abre numa nova janela) exigia que o utilizador invocasse manualmente o comando /compact, que consultaria a API Responses utilizando a conversa existente mais instruções personalizadas para resumir(abre numa nova janela). O Codex utilizou a mensagem do assistente resultante, que continha o resumo, como o novo input(abre numa nova janela) para as interações subsequentes na conversa.

Desde então, a API Responses evoluiu para suportar um /responses/compact endpoint(abre numa nova janela) especial que realiza a compreesão de forma mais eficiente. Devolve uma lista de itens(abre numa nova janela) que pode ser utilizada em vez do input anterior para continuar a conversa, libertando a janela de contexto. Esta lista inclui um item especial type=compaction com um item opaco encrypted_content que preserva a compreensão latente do modelo da conversa original. Agora, o Codex utiliza automaticamente este endpoint para comprimir a conversa quando o auto_compact_limit(abre numa nova janela) é excedido.

O que está para vir

Apresentámos o ciclo do agente Codex e explicámos como o Codex cria e gere o seu contexto ao consultar um modelo. Ao longo do processo, destacámos considerações práticas e boas práticas que se aplicam a qualquer pessoa que esteja a criar um ciclo de agente com base na API Responses.

Embora o ciclo do agente forneça a base para o Codex, é apenas o início. Em publicações futuras, vamos analisar a arquitetura da CLI, explorar como a utilização de ferramentas é implementada e examinar melhor o modelo de sandboxing do Codex.

Autor

Michael Bolin

Reconhecimentos

Um agradecimento especial a toda a equipa que construiu a Codex CLI.