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.
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:
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:
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.
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):
- Al usar el inicio de sesión de ChatGPT(se abre en una ventana nueva) con la CLI de Codex, se utiliza
https://chatgpt.com/backend-api/codex/responsescomo el punto de acceso - Al usar la autenticación con clave de API(se abre en una ventana nueva) en modelos alojados por OpenAI, se utiliza
https://api.openai.com/v1/responsescomo el punto de acceso - Al ejecutar Codex CLI con
--osspara usar gpt-oss con ollama 0.13.4+(se abre en una ventana nueva) o LM Studio 0.3.39+(se abre en una ventana nueva), se predetermina ahttp://localhost:11434/v1/responsesejecutándose localmente en tu ordenador - Codex CLI se puede usar con la API de Respuestas alojada por un proveedor de nube como Azure
Vamos a explorar cómo Codex crea el prompt para la primera llamada de inferencia en una conversación.
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:
instrucciones(se abre en una ventana nueva): mensaje del sistema (o del desarrollador) insertado en el contexto del modeloherramientas(se abre en una ventana nueva): una lista de herramientas que el modelo puede invocar mientras genera una respuestaentrada(se abre en una ventana nueva): una lista de entradas de texto, imágenes o archivos para el modelo
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:
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):
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:
- Contenido de
AGENTS.override.mdyAGENTS.mden$CODEX_HOME - Sujeto a un límite (32 KiB, por defecto), busca en cada carpeta desde la raíz de Git/del proyecto de
cwd(si existe) hasta el propiocwd: añade el contenido de cualquiera deAGENTS.override.md,AGENTS.md, o cualquier nombre de archivo especificado porproject_doc_fallback_filenames en config.toml - Si se han configurado algunas habilidades(se abre en una ventana nueva):
- un breve preámbulo sobre las habilidades
- los metadatos de habilidades(se abre en una ventana nueva) para cada habilidad
- una sección sobre cómo utilizar habilidades(se abre en una ventana nueva)
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):
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:
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):
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.
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)):
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:
El prompt resultante que se usa para muestrear el modelo como parte de la consulta posterior se vería así:
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:
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:
Una vez más, porque seguimos con la conversación, la longitud de la entrada que enviamos a la API de Respuestas sigue aumentando:
Examinemos qué implica este prompt en constante crecimiento para 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
herramientasdisponibles para el modelo en mitad de la conversación. - Cambiar el
modeloque 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:
- Si cambia la configuración del sandbox o el modo de aprobación, insertamos(se abre en una ventana nueva) un nuevo mensaje
rol=desarrolladorcon el mismo formato que el elemento original<permissions instructions>. - Si el directorio de trabajo actual cambia, insertamos(se abre en una ventana nueva) un nuevo mensaje
rol=usuariocon el mismo formato que el<environment_context>original.
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).
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.


