Ir al contenido principal
OpenAI

23 de enero de 2026

Ingeniería

Desenrollando el bucle del Agente Codex

Por Michael Bolin, miembro del personal técnico

Cargando…

Codex CLI(se abre en una ventana nueva) es nuestro agente de software local multiplataforma, diseñado para realizar cambios de software de alta calidad y fiabilidad, operando de manera segura y eficiente en tu equipo. Hemos aprendido muchísimo sobre cómo construir un agente de software de clase mundial desde que lanzamos por primera vez la CLI en abril. Para desglosar esas ideas, este es el primer post de una serie continua en la que exploraremos varios aspectos de cómo funciona Codex, así como lecciones aprendidas con esfuerzo. (Para obtener una visión aún más detallada de cómo se construye Codex CLI, visita nuestro repositorio de código abierto en https://github.com/openai/codex(se abre en una ventana nueva). Muchos de los detalles más sutiles de nuestras decisiones de diseño están documentados en issues y pull requests de GitHub, por si quieres obtener más información.)

Para empezar, nos centraremos en el bucle del agente, que es la lógica central en Codex CLI responsable de orquestar la interacción entre el usuario, el modelo y las herramientas que el modelo invoca para realizar un trabajo de software significativo. Esperamos que esta publicación te ofrezca una buena visión del papel que desempeña nuestro agente (o «harness») al utilizar un LLM.

Antes de sumergirnos, una nota rápida sobre la terminología: en OpenAI, «Codex» abarca un conjunto de ofertas de agentes de software, incluidas Codex CLI, Codex Cloud y la extensión de Codex para VS Code. Esta publicación se centra en el harness de Codex, que proporciona el bucle principal del agente y la lógica de ejecución que sustenta todas las experiencias de Codex y se muestra a través de la CLI de Codex. Para mayor comodidad aquí, usaremos los términos «Codex» y «Codex CLI» indistintamente.

El ciclo del agente

En el corazón de cada agente de IA hay algo llamado «el ciclo del agente». Una ilustración simplificada del ciclo del agente es la siguiente:

Diagrama titulado «Ciclo del agente» que ilustra cómo un sistema de IA procesa una solicitud de usuario, llama a herramientas, observa los resultados, actualiza su plan y devuelve resultados. Las flechas conectan pasos como la entrada del usuario, el razonamiento del modelo, las acciones de las herramientas y la respuesta final.

Para empezar, el agente toma input del usuario para incluirlo en el conjunto de instrucciones textuales que prepara para el modelo, conocido como un prompt.

El siguiente paso es consultar al modelo enviándole nuestras instrucciones y pidiéndole que genere una respuesta, un proceso conocido como inferencia. Durante la inferencia, el prompt textual se convierte primero en una secuencia de tokens(se abre en una ventana nueva)—números enteros que se indexan en el vocabulario del modelo. Estos tokens se usan para muestrear el modelo, generando una nueva secuencia de tokens de salida.

Los tokens de salida se traducen de nuevo a texto, que se convierte en la respuesta del modelo. Dado que los tokens se generan de forma incremental, esta traducción puede realizarse mientras el modelo se ejecuta, por lo que muchas aplicaciones basadas en LLM muestran resultados en tiempo real. En la práctica, la inferencia suele estar encapsulada detrás de una API que opera sobre texto, abstrayendo los detalles de la tokenización.

Como resultado del paso de inferencia, el modelo (1) produce una respuesta final a la entrada original del usuario, o (2) solicita una llamada a herramienta que se espera que el agente realice (por ejemplo, «ejecuta ls e informa del resultado»). En el caso de (2), el agente ejecuta la llamada a la herramienta y añade su salida al prompt original. Esta salida se utiliza para generar una nueva entrada que se emplea para volver a consultar al modelo; el agente puede entonces considerar esta nueva información e intentarlo de nuevo.

Este proceso se repite hasta que el modelo deja de emitir llamadas a herramientas y, en su lugar, produce un mensaje para el usuario (denominado mensaje de asistente en los modelos de OpenAI). En muchos casos, este mensaje responde directamente a la solicitud original del usuario, pero también puede ser una pregunta de seguimiento para él.

Dado que el agente puede ejecutar llamadas a herramientas que modifican el entorno local, su «output» no se limita al mensaje del asistente. En muchos casos, el resultado principal de un agente de software es el código que escribe o edita en tu ordenador. No obstante, cada turno siempre termina con un mensaje del asistente, como «He añadido el architecture.md que pediste», lo que indica un estado de terminación en el ciclo del agente. Desde la perspectiva del agente, su trabajo está completo y el control vuelve al usuario.

El recorrido desde la entrada del usuario hasta la respuesta del agente que se muestra en el diagrama se denomina un turno de una conversación (un hilo en Codex). Aunque este turno de conversación puede incluir muchas iteraciones entre la inferencia del modelo y las llamadas a herramientas. Cada vez que envías un nuevo mensaje a una conversación existente, el historial de la conversación se incluye como parte del prompt para el nuevo turno, que abarca los mensajes y las llamadas a herramientas de turnos anteriores:

Diagrama titulado «Ciclo de agente de varios turnos» que muestra cómo un agente de IA toma iterativamente la entrada del usuario, genera acciones, consulta herramientas, actualiza el estado y devuelve resultados. Incluye pasos etiquetados, flechas y resultados de herramientas de ejemplo que ilustran el ciclo de razonamiento del agente.

Esto significa que, a medida que la conversación crece, también aumenta la longitud del prompt usado para muestrear el modelo. Esta longitud importa porque cada modelo tiene una ventana de contexto, que es el número máximo de tokens que puede usar para una llamada de inferencia. Ten en cuenta que esta ventana incluye tanto tokens de entrada como de salida. Como te puedes imaginar, un agente podría decidir hacer cientos de llamadas a herramientas en un solo turno, lo que podría agotar la ventana de contexto. Por esta razón, la gestión de la ventana de contexto es una de las muchas responsabilidades del agente. Ahora, vamos a sumergirnos para ver cómo Codex ejecuta el bucle del agente.

Inferencia de modelo

El Codex CLI envía solicitudes HTTP a la API de Respuestas(se abre en una ventana nueva) para ejecutar la inferencia del modelo. Examinaremos cómo fluye la información a través de Codex, que utiliza la API de Respuestas para impulsar el ciclo del agente.

El punto de acceso de la API de Respuestas que utiliza Codex CLI es configurable(se abre en una ventana nueva), por lo que se puede usar con cualquier punto de acceso que implemente la API de Respuestas(se abre en una ventana nueva):

Vamos a explorar cómo Codex crea el prompt para la primera llamada de inferencia en una conversación.

Construyendo el prompt inicial

Como usuario final, no especificas el prompt utilizado para muestrear el modelo literalmente cuando consultas la API de Respuestas. En su lugar, especificas varios tipos de entrada como parte de tu consulta, y el servidor de la API de Respuestas decide cómo estructurar esta información en un prompt que el modelo está diseñado para consumir. Puedes pensar en el prompt como una «lista de elementos»; esta sección explicará cómo tu consulta se transforma en esa lista.

En el prompt inicial, cada elemento de la lista está asociado con un rol. El rol indica cuánto peso debe tener el contenido asociado y es uno de los siguientes valores (en orden decreciente de prioridad): sistema, desarrollador, usuario, asistente.

La API de Respuestas(se abre en una ventana nueva) acepta una carga JSON con muchos parámetros. Nos enfocaremos en estos tres:

En Codex, el campo instrucciones se lee del model_instructions_file(se abre en una ventana nueva) en ~/.codex/config.toml, si se especifica; de lo contrario, se usan las base_instructions asociadas con un modelo(se abre en una ventana nueva). Las instrucciones específicas del modelo están en el repositorio de Codex y se integran en el CLI (por ejemplo, gpt-5.2-codex_prompt.md(se abre en una ventana nueva)).

El campo herramientas es una lista de definiciones de herramientas que se ajustan a un esquema definido por la API de Respuestas. Para Codex, esto incluye herramientas proporcionadas por el CLI de Codex, herramientas proporcionadas por la API de Respuestas que deberían estar disponibles para Codex, así como herramientas proporcionadas por el usuario, generalmente a travé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, el campo entrada de la carga útil JSON es una lista de elementos. Codex inserta los siguientes elementos(se abre en una ventana nueva) en la entrada antes de añadir el mensaje del usuario:

1. Un mensaje con role=developer que describe el entorno aislado que se aplica solo a la herramienta shell proporcionada por Codex definida en la sección herramientas. Es decir, otras herramientas, como las proporcionadas por servidores MCP, no están en un entorno de pruebas de Codex y son responsables de implementar sus propias medidas de seguridad.

El mensaje se construye a partir de una plantilla donde las piezas clave del contenido provienen de fragmentos de Markdown integrados en el Codex CLI, como workspace_write.md(se abre en una ventana nueva) y on_request.md(se abre en una ventana nueva):

Texto plano

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) Un mensaje con rol=desarrollador cuyo contenido es el valor developer_instructions leído del archivo config.toml del usuario.

3. (Opcional) Un mensaje con rol=usuario cuyo contenido son las «instrucciones del usuario», que no proceden de un único archivo, sino que se agregan a partir de múltiples fuentes(se abre en una ventana nueva). En general, las instrucciones más específicas aparecen más adelante:

4. Un mensaje con rol=usuario que describe el entorno local en el que el agente está operando actualmente. Esto especifica el directorio de trabajo actual y el shell del usuario(se abre en una ventana nueva):

Texto plano

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

Una vez que Codex ha hecho todos los cálculos anteriores para inicializar la entrada, añade el mensaje del usuario para comenzar la conversación.

Los ejemplos anteriores se centraron en el contenido de cada mensaje, pero ten en cuenta que cada elemento de entrada es un objeto JSON con tipo, rol(se abre en una ventana nueva) y contenido como se muestra a continuación:

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
}

Una vez que Codex construye la carga útil JSON completa para enviarla a la API de Respuestas, realiza la solicitud HTTP POST con un encabezado Autorización según cómo esté configurado el punto de acceso de la API de Respuestas en ~/.codex/config.toml (se añaden encabezados HTTP adicionales y parámetros de consulta si se especifican).

Cuando un servidor de la API de Respuestas de OpenAI recibe la solicitud, usa el JSON para derivar el prompt para el modelo de la siguiente manera (para estar seguro, una implementación personalizada de la API de Respuestas podría tomar una decisión diferente):

Diagrama instantáneo que muestra un solo paso en un bucle de un agente de IA. Una solicitud de usuario entra en el modelo, que genera un pensamiento, una acción con el nombre de una herramienta y una entrada para la herramienta. El diagrama destaca este paso intermedio de razonamiento antes de que se llame a la herramienta.

Como puedes ver, el orden de los tres primeros elementos del prompt lo determina el servidor, no el cliente. Dicho esto, de esos tres elementos, solo el contenido del mensaje del sistema también está controlado por el servidor, ya que las herramientas y las instrucciones las determina el cliente. A continuación, sigue la entrada de la carga útil JSON para completar el prompt.

Ahora que tenemos nuestro prompt, estamos listos para probar el modelo.

El primer turno

Esta solicitud HTTP a la API de Respuestas inicia el primer «turno» de una conversación en Codex. El servidor responde con un flujo de eventos enviados por el servidor (SSE(se abre en una ventana nueva)). Los datos de cada evento son una carga útil JSON con un «tipo» que empieza por «respuesta», que podría ser algo como esto (puedes encontrar una lista completa de eventos en nuestra documentación de la API(se abre en una ventana nueva)):

Texto plano

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":{...}}

Codex consume el flujo de eventos(se abre en una ventana nueva) y los vuelve a publicar como objetos de eventos internos que puede usar un cliente. Eventos como response.output_text.delta se utilizan para admitir la transmisión en la interfaz de usuario, mientras que otros eventos como response.output_item.added se transforman en objetos que se añaden a la entrada para llamadas posteriores a la API de Respuestas.

Supón que la primera solicitud a la API de Respuestas incluye dos eventos response.output_item.done: uno con type=razonamiento y otro con type=function_call. Estos eventos deben representarse en el campo entrada del JSON cuando volvamos a consultar al modelo con la respuesta a la llamada a la herramienta: 

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
]

El prompt resultante que se usa para muestrear el modelo como parte de la consulta posterior se vería así:

Diagrama etiquetado «Snapshot 2» que muestra un agente de IA después de una llamada a una herramienta. El modelo recibe una observación de una herramienta y genera un nuevo pensamiento y acción. Las flechas conectan las entradas, observaciones y salidas para ilustrar cómo el agente itera su ciclo de razonamiento.

En particular, observa cómo el prompt antiguo es un prefijo exacto del nuevo prompt. Esto es intencional, ya que hace que las solicitudes posteriores sean mucho más eficientes porque nos permite aprovechar el prompt caching (que trataremos en la siguiente sección sobre rendimiento).

Al mirar hacia atrás en nuestro primer diagrama del bucle del agente, vemos que podría haber muchas iteraciones entre la inferencia y la llamada a herramientas. El prompt puede seguir creciendo hasta que finalmente recibamos un mensaje del asistente, indicando el final del turno:

Texto plano

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

En la CLI de Codex, presentamos el mensaje del asistente al usuario y enfocamos el editor para indicar al usuario que es su «turno» para continuar la conversación. Si el usuario responde, tanto el mensaje del asistente del turno anterior como el nuevo mensaje del usuario deben añadirse a la entrada en la solicitud de la API de Respuestas para iniciar el nuevo turno:

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
]

Una vez más, porque seguimos con la conversación, la longitud de la entrada que enviamos a la API de Respuestas sigue aumentando:

Diagrama etiquetado «Snapshot 3» que muestra la etapa final de un bucle de un agente de IA. Después de recibir los resultados de la herramienta, el modelo genera un pensamiento final y una respuesta que se devuelve al usuario. Las flechas muestran la transición del resultado de la herramienta a la respuesta final.

Examinemos qué implica este prompt en constante crecimiento para el rendimiento.

Consideraciones sobre el rendimiento

Puede que te estés preguntando: «Espera, ¿no es el bucle del agente cuadrático en términos de la cantidad de JSON enviada a la API de Respuestas a lo largo de la conversación?» Y tendrías razón. Aunque la API de Respuestas admite un parámetro opcional previous_response_id(se abre en una ventana nueva) para mitigar este problema, Codex no lo utiliza actualmente, principalmente para mantener las solicitudes completamente sin estado y para admitir configuraciones de sin retención de datos (ZDR).

Evitar previous_response_id simplifica las cosas para el proveedor de la API de Respuestas porque garantiza que cada solicitud sea sin estado. Esto también facilita dar soporte a los clientes que han optado por sin retención de datos (ZDR)(se abre en una ventana nueva), ya que almacenar los datos necesarios para dar soporte a previous_response_id iría en contra de ZDR. Ten en cuenta que los clientes de ZDR no sacrifican la capacidad de beneficiarse de mensajes de razonamiento propietarios de turnos anteriores, ya que el encrypted_content asociado se puede descifrar en el servidor. (OpenAI conserva la clave de descifrado de un cliente de ZDR, pero no sus datos.) Consulta los Pull requests #642(se abre en una ventana nueva) y #1641(se abre en una ventana nueva) para los cambios relacionados en Codex para dar soporte a ZDR.

En general, el coste de muestrear el modelo supera al del tráfico de red, convirtiendo el muestreo en el principal objetivo de nuestros esfuerzos de eficiencia. Por eso el almacenamiento en caché de prompt es tan importante, ya que nos permite reutilizar el cómputo de una llamada de inferencia anterior. Cuando obtenemos aciertos de caché, el muestreo del modelo es lineal en lugar de cuadrático. Nuestra documentación sobre el almacenamiento en caché de prompt (se abre en una ventana nueva)lo explica con más detalle:

Los aciertos de caché solo son posibles para coincidencias exactas de prefijo dentro de un prompt. Para aprovechar los beneficios del almacenamiento en caché, coloca el contenido estático, como instrucciones y ejemplos, al principio de tu prompt, y pon el contenido variable, como la información específica del usuario, al final. Esto también se aplica a las imágenes y herramientas, que deben ser idénticas entre solicitudes.

Teniendo esto en mente, consideremos qué tipos de operaciones podrían causar un «cache miss» en Codex:

  • Cambiar las herramientas disponibles para el modelo en mitad de la conversación.
  • Cambiar el modelo que es el objetivo de la solicitud de la API de Respuestas (en la práctica, esto cambia el tercer elemento del prompt original, ya que contiene instrucciones específicas del modelo).
  • Cambiar la configuración del sandbox, el modo de aprobación o el directorio de trabajo actual.

El equipo de Codex debe ser diligente al introducir nuevas funciones en el Codex CLI que podrían comprometer el almacenamiento en caché de los prompts. Por ejemplo, nuestro soporte inicial para las herramientas MCP introdujo un error que nos impedía enumerar las herramientas en un orden consistente(se abre en una ventana nueva), lo que causaba fallos de caché. Ten en cuenta que las herramientas MCP pueden ser especialmente complicadas porque los servidores MCP pueden cambiar la lista de herramientas que proporcionan sobre la marcha mediante una notificación notifications/tools/list_changed(se abre en una ventana nueva). Atender esta notificación en medio de una conversación larga puede causar un costoso fallo de caché.

Cuando es posible, manejamos los cambios de configuración que ocurren a mitad de la conversación añadiendo un mensaje nuevo a la entrada para reflejar el cambio, en lugar de modificar un mensaje anterior:

Hacemos todo lo posible para asegurar aciertos de caché para mejorar el rendimiento. Hay otro recurso clave que tenemos que gestionar: la ventana de contexto.

Nuestra estrategia general para evitar quedarnos sin ventana de contexto es compactar la conversación una vez que el número de tokens supera cierto umbral. Específicamente, reemplazamos la entrada con una lista nueva y más pequeña de elementos que representa la conversación, permitiendo al agente continuar con una comprensión de lo que ha sucedido hasta ahora. Una implementación temprana de la compactación(se abre en una ventana nueva) requería que el usuario invocara manualmente el comando /compact, que consultaría la API de Respuestas usando la conversación existente más instrucciones personalizadas para el resumen(se abre en una ventana nueva). Codex usó el mensaje del asistente resultante que contenía el resumen como la nueva entrada(se abre en una ventana nueva) para los turnos siguientes de la conversación.

Desde entonces, la API de Respuestas ha evolucionado para admitir un /responses/compact punto de acceso(se abre en una ventana nueva) especial que realiza la compactación de forma más eficiente. Devuelve una lista de elementos(se abre en una ventana nueva) que puedes usar en lugar de la entrada anterior para continuar la conversación mientras liberas espacio en la ventana de contexto. Esta lista incluye un elemento especial type=compaction con un elemento opaco encrypted_content que preserva la comprensión latente del modelo sobre la conversación original. Ahora, Codex usa automáticamente este punto de acceso para compactar la conversación cuando se supera el auto_compact_limit(se abre en una ventana nueva).

Próximamente

Hemos presentado el ciclo del agente Codex y hemos explicado cómo Codex crea y gestiona su contexto al consultar un modelo. A lo largo del camino, destacamos consideraciones prácticas y mejores prácticas que se aplican a cualquiera que esté construyendo un ciclo de agente sobre la API de Respuestas.

Aunque el ciclo del agente proporciona la base para Codex, es solo el comienzo. En próximas publicaciones, profundizaremos en la arquitectura de la CLI, exploraremos cómo se implementa el uso de herramientas y echaremos un vistazo más de cerca al modelo de sandboxing de Codex.

Autor

Michael Bolin

Reconocimientos

Agradecimientos especiales a todo el equipo que creó Codex CLI.