Codex CLI(se abre en una nueva ventana) es nuestro agente de software local multiplataforma, diseñado para realizar cambios de software de alta calidad y confiables, operando de manera segura y eficiente en tu máquina. Hemos aprendido muchísimo sobre cómo desarrollar un agente de software de clase mundial desde que lanzamos por primera vez el CLI en abril. Para desglosar esas ideas, esta es la primera publicación de una serie continua en la que exploraremos varios aspectos de cómo funciona Codex, así como lecciones aprendidas con esfuerzo. (Para una vista aún más detallada de cómo se construye la CLI de Codex, visita nuestro repositorio de código abierto en https://github.com/openai/codex(se abre en una nueva ventana). Muchos de los detalles más sutiles de nuestras decisiones de diseño están documentados en Pull requests e incidencias de GitHub si deseas obtener más información).
Para comenzar, nos enfocaremos en el ciclo del agente, que es la lógica central en Codex CLI encargada de orquestar la interacción entre el usuario, el modelo y las herramientas que el modelo invoca para llevar a cabo un trabajo de software significativo. Esperamos que esta publicación te ofrezca una buena perspectiva del papel que desempeña nuestro agente (o “harness”) al utilizar un LLM.
Antes de profundizar, una nota rápida sobre la terminología: en OpenAI, “Codex” abarca una serie de ofertas de agentes de software, incluyendo Codex CLI, Codex Cloud y la extensión 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 subyace a todas las experiencias de Codex y se presenta a través de la CLI de Codex. Para facilitar aquí, usaremos los términos “Codex” y “Codex CLI” de manera intercambiable.
En el corazón de cada agente de IA hay algo llamado "el bucle del agente". Una ilustración simplificada del ciclo del agente se ve así:
Para empezar, el agente recibe 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 de texto se traduce primero en una secuencia de tokens(se abre en una nueva ventana)de entrada: enteros que indexan en el vocabulario del modelo. Luego se utilizan estos tokens para muestrear el modelo, produciendo una nueva secuencia de tokens de salida.
Los tokens de salida se convierten nuevamente en texto, lo que se transforma en la respuesta del modelo. Debido a que los tokens se generan de manera 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 generalmente se encapsula detrás de una API que opera sobre texto, ocultando 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 el 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 el modelo; el agente puede entonces considerar esta nueva información e intentarlo nuevamente.
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 ti.
Dado que el agente puede ejecutar llamadas a herramientas que modifican el entorno local, su “resultado” no se limita al mensaje del asistente. En muchos casos, la salida principal de un agente de software es el código que escribe o edita en tu computadora. Sin embargo, cada turno siempre termina con un mensaje del asistente, como “He agregado el architecture.md que pediste”, que indica un estado de finalización en el ciclo del agente. Desde la perspectiva del agente, su trabajo está completo y el control regresa 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 las herramientas. Cada vez que envías un mensaje nuevo 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 utilizado para muestrear el modelo. Esta longitud importa porque cada modelo tiene una ventana de contexto, que es la cantidad máxima de tokens que puede utilizar en una llamada de inferencia. Ten en cuenta que esta ventana incluye tanto tokens de entrada como tokens de salida. Como te puedes imaginar, un agente podría decidir realizar 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 ciclo del agente.
El Codex CLI envía solicitudes HTTP a la API de Responses(se abre en una nueva ventana) 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 Responses que utiliza Codex CLI es configurable(se abre en una nueva ventana), por lo que se puede usar con cualquier punto de acceso que implemente la API de Responses(se abre en una nueva ventana):
- Al usar el inicio de sesión de ChatGPT(se abre en una nueva ventana) con el CLI de Codex, 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 nueva ventana) con 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 nueva ventana) o LM Studio 0.3.39+(se abre en una nueva ventana), se predetermina ahttp://localhost:11434/v1/responsesejecutándose localmente en tu computadora - Codex CLI se puede utilizar con la API de Respuestas alojada por un proveedor de nube como Azure
Exploremos 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 textualmente cuando consultas la API de Responses. En su lugar, especificas varios tipos de entrada como parte de tu consulta, y el servidor de la API de Responses 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 a un rol. El role indica el peso que debe tener el contenido asociado y es uno de los siguientes valores (en orden decreciente de prioridad): system, developer, user, assistant.
La API de respuestas(se abre en una nueva ventana) acepta una carga útil JSON con muchos parámetros. Nos enfocaremos en estos tres:
instrucciones(se abre en una nueva ventana): mensaje del sistema (o del desarrollador) insertado en el contexto del modelotools(se abre en una nueva ventana): una lista de herramientas que el modelo puede utilizar mientras genera una respuestainput(se abre en una nueva ventana): una lista de entradas de texto, imagen o archivo al modelo
En Codex, el campo instructions se lee desde el model_instructions_file(se abre en una nueva ventana) en ~/.codex/config.toml, si está especificado; de lo contrario, se utilizan las base_instructions asociadas con un modelo(se abre en una nueva ventana). 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 nueva ventana)).
El campo tools es una lista de definiciones de herramientas que se ajustan a un esquema definido por la API de Responses. 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 input del payload JSON es una lista de elementos. Codex inserta los siguientes elementos(se abre en una nueva ventana) en el input antes de agregar el mensaje del usuario:
1. Un mensaje con role=developer que describe el entorno de pruebas que se aplica solo a la herramienta shell proporcionada por Codex definida en la sección tools. Es decir, otras herramientas, como las proporcionadas por servidores MCP, no están aisladas por Codex y son responsables de implementar sus propias medidas de seguridad.
El mensaje se construye a partir de una plantilla donde las piezas clave de contenido provienen de fragmentos de Markdown integrados en el Codex CLI, como workspace_write.md(se abre en una nueva ventana) y on_request.md(se abre en una nueva ventana):
2. (Opcional) Un mensaje con role=developer cuyos contenidos son el valor de developer_instructions leído del archivo config.toml del usuario.
3. (Opcional) Un mensaje con role=user cuyo contenido son las “instrucciones del usuario”, que no provienen de un solo archivo, sino que se agregan de múltiples fuentes(se abre en una nueva ventana). En general, las instrucciones más específicas se presentan 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/proyecto del
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 nueva ventana):
- un breve preámbulo sobre destrezas
- los metadatos de la habilidad(se abre en una nueva ventana) para cada habilidad
- una sección sobre cómo utilizar habilidades(se abre en una nueva ventana)
4. Un mensaje con role=user que describe el entorno local en el que el agente está operando en este momento. Esto especifica el directorio de trabajo actual y el intérprete de comandos del usuario(se abre en una nueva ventana):
Una vez que Codex ha realizado todos los cálculos mencionados para inicializar el input, 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 input es un objeto JSON con type, role(se abre en una nueva ventana) y content como se muestra a continuación:
Una vez que Codex construye la carga útil JSON completa para enviar a la API de Respuestas, realiza la solicitud HTTP POST con un encabezado Authorization 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 Responses de OpenAI recibe la solicitud, utiliza el JSON para derivar el prompt para el modelo de la siguiente manera (para estar seguro, una implementación personalizada de la API de Responses podría tomar una decisión diferente):
Como puedes ver, el orden de los primeros tres elementos en el 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 tools y las instructions son determinadas por el cliente. A estos les sigue el input del payload JSON para completar el prompt.
Ahora que tenemos nuestro prompt, estamos listos para probar el modelo.
Esta solicitud HTTP a la API de Responses 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 nueva ventana)). Los datos de cada evento son una carga útil JSON con un "tipo" que comienza con "response", que podría ser algo como esto (una lista completa de eventos se puede encontrar en nuestra documentación de la API(se abre en una nueva ventana)):
Codex consume el flujo de eventos(se abre en una nueva ventana) y los republica como objetos de eventos internos que un cliente puede utilizar. 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 convierten en objetos que se añaden al input para llamadas posteriores a la API de Respuestas.
Supongamos que la primera solicitud a la API de Responses incluya dos eventos response.output_item.done: uno con type=reasoning y otro con type=function_call. Estos eventos deben representarse en el campo input del JSON cuando volvamos a consultar el modelo con la respuesta a la llamada de la herramienta:
El prompt resultante que se usa para muestrear el modelo como parte de la consulta siguiente se vería así:
En particular, observa cómo el prompt anterior 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 almacenamiento en caché de prompt (que discutiremos en la siguiente sección sobre rendimiento).
Al mirar hacia atrás en nuestro primer diagrama del ciclo 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 indicarle que es su “turno” de 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 al input en la solicitud de la API de Respuestas para comenzar el nuevo turno:
Una vez más, porque estamos continuando una conversación, la longitud del input que enviamos a la API de respuestas sigue aumentando:
Examinemos qué implica este prompt en constante crecimiento para el rendimiento.
Quizás 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 Responses admite un parámetro opcional previous_response_id(se abre en una nueva ventana) para mitigar este problema, Codex no lo utiliza actualmente, principalmente para mantener las solicitudes completamente sin estado y para admitir configuraciones de Retención Cero 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 el soporte a los clientes que han optado por Retención Cero de Datos (ZDR)(se abre en una nueva ventana), ya que almacenar los datos necesarios para soportar previous_response_id estaría en conflicto con ZDR. Ten en cuenta que los clientes de ZDR no sacrifican la capacidad de beneficiarse de los 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 Retención Cero de Datos (ZDR), pero no sus datos.) Consulta los Pull requests #642(se abre en una nueva ventana) y #1641(se abre en una nueva ventana) para los cambios relacionados en Codex para soportar ZDR.
En general, el costo de muestrear el modelo supera al costo del tráfico de red, convirtiendo al muestreo en el objetivo principal de nuestros esfuerzos de eficiencia. Por eso el almacenamiento en caché de prompt es tan importante, ya que nos permite reutilizar el cálculo 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 nueva ventana)explica esto 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 inicio de tu prompt, y coloca el contenido variable, como 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 las solicitudes.
Con esto en mente, consideremos qué tipos de operaciones podrían causar un “fallo de caché” en Codex:
- Cambiar las
toolsdisponibles para el modelo en medio de la conversación. - Cambiar el
modeloque es el objetivo de la solicitud de la API de Responses (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 entorno de pruebas, 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 en el que no enumeramos las herramientas en un orden consistente(se abre en una nueva ventana), lo que causó fallos de caché. Ten en cuenta que las herramientas de MCP pueden ser especialmente complicadas porque los servidores MCP pueden cambiar la lista de herramientas que ofrecen al instante mediante una notificación notifications/tools/list_changed(se abre en una nueva ventana). Atender esta notificación en medio de una conversación larga puede provocar un costoso fallo de caché.
Cuando es posible, gestionamos los cambios de configuración que ocurren a mitad de la conversación añadiendo un mensaje nuevo a input para reflejar el cambio, en lugar de modificar un mensaje anterior:
- Si la configuración del sandbox o el modo de aprobación cambia, insertamos(se abre en una nueva ventana) un nuevo mensaje
role=developercon el mismo formato que el elemento original<permissions instructions>. - Si el directorio de trabajo actual cambia, insertamos(se abre en una nueva ventana) un nuevo mensaje
role=usercon 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 supere cierto umbral. Específicamente, sustituimos el input por una lista nueva y más reducida de elementos que represente la conversación, permitiendo al agente continuar con una comprensión de lo que ha ocurrido hasta el momento. Una implementación temprana de la compactación(se abre en una nueva ventana) requería que el usuario invocara manualmente el comando /compact, que consultaría la API de Responses usando la conversación existente más instrucciones personalizadas para resumir(se abre en una nueva ventana). Codex utilizó el mensaje del asistente resultante que contenía el resumen como el nuevo input(se abre en una nueva ventana) para los turnos posteriores de la conversación.
Desde entonces, la API de Responses ha evolucionado para admitir un /responses/compact punto de acceso especial(se abre en una nueva ventana) que realiza la compactación de manera más eficiente. Devuelve una lista de elementos(se abre en una nueva ventana) que se puede usar en lugar del input anterior para continuar la conversación mientras se libera 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 nueva ventana).
Hemos introducido el bucle de agentes de Codex y hemos recorrido 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 bucle de agente sobre la API de Responses.
Aunque el bucle del agente sienta las bases para Codex, es solo el principio. En próximas publicaciones, profundizaremos en la arquitectura de la CLI, exploraremos cómo se implementa el uso de herramientas y examinaremos más de cerca el modelo de sandboxing de Codex.


