Una especificación de código abierto para la orquestación de Codex: Symphony
Por Alex Kotliarskyi, Victor Zhu y Zach Brock
Hace seis meses, mientras trabajábamos en una herramienta interna de productividad, nuestro equipo tomó una decisión controvertida (en ese momento): construiríamos nuestro repositorio sin código escrito por humanos. Cada línea de nuestro repositorio del proyecto tenía que ser generada por Codex.
Para lograrlo, rediseñamos nuestro flujo de trabajo de ingeniería desde cero. Creamos un repositorio amigable para agentes, invertimos mucho en pruebas automatizadas y barreras de protección, y tratamos a Codex como un compañero de equipo de pleno derecho. Documentamos ese recorrido en nuestra entrada de blog anterior sobre harness engineering.
Y funcionó, pero luego nos topamos con el siguiente cuello de botella: el cambio de contexto.
Para resolver este nuevo problema, construimos un sistema llamado Symphony. Symphony(se abre en una nueva ventana) es un orquestador de agentes que convierte un tablero de gestión de proyectos como Linear en un plano de control para agentes de programación. Cada tarea abierta recibe un agente, los agentes se ejecutan continuamente y los humanos revisan los resultados.
Esta publicación explica cómo creamos Symphony, lo que resultó en un aumento del 500 % en Pull requests integrados en algunos equipos, y cómo usarlo para convertir tu propio rastreador de incidencias en un orquestador de agentes siempre activo.
El límite de los agentes de programación interactivos
Aunque cada vez son más fáciles de usar, los agentes de programación, ya sea a través de aplicaciones web o CLI, siguen siendo herramientas interactivas.
A medida que aumentó la escala del trabajo con agentes en OpenAI, encontramos un nuevo tipo de carga. Cada ingeniero abría unas cuantas sesiones de Codex, asignaba tareas, revisaba la salida, guiaba al agente y repetía. En la práctica, la mayoría de las personas podía gestionar cómodamente de tres a cinco sesiones a la vez antes de que el cambio de contexto se volviera doloroso. Más allá de eso, la productividad bajaba. Olvidábamos qué estaba haciendo cada sesión, saltábamos entre terminales para volver a encaminar a los agentes y depurábamos tareas de larga duración que se atascaban a mitad del camino.
Los agentes eran rápidos, pero teníamos un cuello de botella del sistema: la atención humana. En la práctica, habíamos creado un equipo de ingenieros junior extremadamente capaces y luego asignado a nuestros ingenieros humanos a microgestionarlos. Eso no iba a escalar.
Un cambio de perspectiva
Nos dimos cuenta de que estábamos optimizando lo incorrecto. Estábamos orientando nuestro sistema en torno a sesiones de programación y PRs fusionados, cuando en realidad los PRs y las sesiones son un medio para un fin. Los flujos de trabajo de software se organizan en gran medida alrededor de entregables: incidencias, tareas, tickets, hitos.
Así que nos preguntamos qué pasaría si dejáramos de supervisar directamente a los agentes y, en cambio, les permitiéramos tomar trabajo de nuestro rastreador de tareas.
Esa idea se convirtió en Symphony, una especificación escrita que funciona como supervisor para orquestar trabajo con agentes.
Convertir nuestro rastreador de incidencias en un orquestador de agentes
Symphony comenzó con un concepto simple: cualquier tarea abierta debería ser tomada y completada por un agente. En lugar de gestionar sesiones de Codex en múltiples pestañas, convertimos nuestro rastreador de incidencias en el plano de control.
En esta configuración, cada incidencia abierta de Linear se asigna a un Espacio de trabajo dedicado para un agente. Symphony supervisa continuamente el tablero de tareas y se asegura de que cada tarea activa tenga un agente ejecutándose en el ciclo hasta que termine. Si un agente falla o se queda atascado, Symphony lo reinicia. Si aparece trabajo nuevo, Symphony lo toma y empieza a organizarlo.
Construimos nuestro flujo de trabajo con base en los estados de los tickets, usando el gestor de tareas Linear como una máquina de estados.
En la práctica, Symphony desacopla el trabajo de las sesiones y de las Pull requests. Algunas incidencias producen múltiples PRs en distintos repositorios; otras son investigación o análisis puros que nunca tocan la base de código.
Una vez que el trabajo se abstrae de esta manera, los tickets pueden representar unidades de trabajo mucho más grandes.
Usamos Symphony con regularidad para orquestar funciones complejas y migraciones de infraestructura. Por ejemplo, podríamos crear una tarea pidiéndole al agente que analice la base de código, Slack o Notion y produzca un plan de implementación. Una vez que estamos conformes con el plan, el agente genera un árbol de tareas, desglosando el trabajo en etapas y definiendo dependencias entre tareas.
Los agentes solo empiezan a trabajar en tareas que no están bloqueadas, así que la ejecución se desarrolla de forma natural y óptima en paralelo para este DAG (una secuencia de pasos de ejecución). Por ejemplo, marcamos la actualización de React como bloqueada por una migración a Vite. Como era de esperarse, los agentes empezaron a actualizar React solo después de que se completó la migración a Vite.
Los agentes también pueden crear trabajo por sí mismos. Durante la implementación o la revisión, a menudo detectan mejoras que quedan fuera del alcance de la tarea actual: un problema de rendimiento, una oportunidad de refactorización o una mejor arquitectura. Cuando eso ocurre, simplemente crean una nueva incidencia que podemos evaluar y programar más adelante; muchas de estas tareas de seguimiento también son tomadas por agentes. Aunque supervisamos este proceso, los agentes se mantienen organizados y hacen que el trabajo siga avanzando.
Esta forma de trabajar reduce drásticamente el costo cognitivo de iniciar trabajo ambiguo. Si el agente se equivoca en algo, eso sigue siendo información útil, y el costo para nosotros es casi cero. Podemos crear tickets muy fácilmente para que el agente haga prototipos y explore, y descartar cualquier exploración que no nos guste.
Como el orquestador se ejecuta en devboxes y nunca duerme, podemos agregar tareas desde cualquier lugar y saber que un agente las tomará. Por ejemplo, un ingeniero de nuestro equipo hizo tres cambios importantes desde la app de Linear en su teléfono, desde una acogedora cabaña con wifi deficiente.
Un aumento en la exploración al trabajar de esta manera
Al observar los efectos de trabajar con Symphony, el cambio más evidente fue la producción. En algunos equipos de OpenAI, vimos que la cantidad de PRs integrados aumentó un 500 % en las primeras tres semanas. Fuera de OpenAI, el fundador de Linear, Karri Saarinen, destacó un aumento en los Espacios de trabajo creados(se abre en una nueva ventana) cuando lanzamos Symphony. Sin embargo, el cambio más profundo está en cómo los equipos piensan sobre el trabajo.
Cuando nuestros ingenieros ya no dedican tiempo a supervisar sesiones de Codex, la economía de los cambios de código cambia por completo. El costo percibido de cada cambio baja porque ya no estamos invirtiendo esfuerzo humano en impulsar la implementación en sí.
Eso cambió nuestro comportamiento. Se ha vuelto trivial poner en marcha tareas especulativas en Symphony. Prueba una idea, explora una refactorización, pon a prueba una hipótesis y conserva solo los resultados que parezcan prometedores.
También amplía quién puede iniciar trabajo. Nuestro gerente de producto y nuestro diseñador ahora pueden crear solicitudes de funciones directamente en Symphony. No necesitan clonar el repositorio ni gestionar una sesión de Codex. Describen la función y reciben un paquete de revisión que incluye un recorrido en video de la función funcionando dentro del producto real.
Symphony también destaca en monorepos grandes (como el que tenemos en OpenAI), donde el último tramo para integrar un PR es lento y frágil. El sistema supervisa CI, hace rebase cuando es necesario, resuelve conflictos, reintenta verificaciones inestables y, en general, acompaña los cambios a través del pipeline. Para cuando un ticket llega al estado Merging, tenemos alta confianza en que el cambio llegará a la rama principal sin supervisión humana constante.
Después de implementar Symphony, delegamos más trabajo a los agentes y nos enfocamos en tareas más difíciles y exploratorias.
El progreso trae problemas nuevos y diferentes
Operar a este nivel implica concesiones. Cuando pasamos de guiar a los agentes de forma interactiva a asignarles trabajo a nivel de ticket, perdimos la capacidad de darles pequeños empujones constantemente a mitad del proceso y corregir el rumbo cuando era necesario. A veces, el agente producía algo que no daba en el blanco en absoluto. Eso fue útil: esos fracasos revelaron vacíos en el sistema y nos ayudaron a hacerlo más robusto.
En lugar de corregir manualmente el resultado, agregamos barreras de protección y habilidades para que los agentes pudieran tener éxito la próxima vez. Con el tiempo, esto nos llevó a añadir nuevas capacidades a nuestro harness, como ejecutar pruebas de extremo a extremo, controlar la app a través de Chrome DevTools y gestionar pruebas rápidas de QA. Mejoramos significativamente nuestra documentación y aclaramos cómo se ve un buen resultado.
No todas las tareas encajan con el estilo de trabajo de Symphony. Algunos problemas todavía requieren ingenieros trabajando directamente con sesiones interactivas de Codex, especialmente problemas ambiguos o trabajo que exige criterio sólido y experiencia. En la práctica, estas suelen ser las tareas más interesantes y agradables en las que nuestros ingenieros pueden dedicar su tiempo.
La diferencia es que Symphony puede encargarse de la mayor parte del trabajo rutinario de implementación. Eso permite que los ingenieros se concentren en un solo problema difícil a la vez, en lugar de estar cambiando constantemente de contexto entre tareas más pequeñas.
También aprendimos que tratar a los agentes como nodos rígidos en una máquina de estados no funciona bien. Los modelos se vuelven más inteligentes y pueden resolver problemas más grandes que la caja en la que intentamos meterlos. Nuestras primeras versiones del trabajo con agentes solo le pedían a Codex que implementara la tarea. Ese enfoque resultó demasiado limitado. Codex es perfectamente capaz de crear múltiples PRs, así como de leer comentarios de revisión y atenderlos. Así que le dimos herramientas —CLI de gh, habilidades para leer registros de CI, etc.— y ahora podemos pedirle a Codex que haga más, como cerrar PRs antiguos o extraer reportes sobre trabajo completado frente a abandonado. Este tipo de tareas quedaba muy fuera de la caja inicial de implementación de funciones.
Así que con el tiempo avanzamos hacia darles a los agentes objetivos en lugar de transiciones estrictas, de forma muy parecida a como un buen gerente asignaría una meta a un colaborador directo de su equipo. El poder de los modelos proviene de su capacidad de razonamiento, así que dales herramientas y contexto, y deja que hagan su magia.
Usar Symphony para crear Symphony
Cuando abres el repositorio de Symphony,(se abre en una nueva ventana) lo primero que notarás es que Symphony es técnicamente solo un archivo SPEC.md: una definición del problema y de la solución prevista. En lugar de construir un sistema de supervisión complejo, definimos el problema y las soluciones previstas, dando a los agentes una guía de alto nivel.
La implementación de referencia está escrita en Elixir, porque cuando el código es prácticamente gratis, por fin puedes elegir lenguajes por sus fortalezas, como la concurrencia de Elixir, pero la idea central puede expresarse en un simple documento Markdown. Te animamos a apuntar tu agente de programación favorito a la especificación y hacer que implemente su propia versión.
La primera versión de Symphony era solo una sesión de Codex ejecutándose en tmux, consultando Linear y generando subagentes para nuevas tareas. Funcionaba, pero no era particularmente confiable. La segunda versión vivía dentro de nuestro repositorio principal del proyecto, que estaba diseñado pensando en agentes. Ya habíamos creado el harness de agentes para darles las habilidades y el contexto necesarios para hacer trabajo de alta calidad en este repositorio, así que Symphony simplemente lo conecta todo.
Una vez que existió la funcionalidad básica, usamos Symphony para crear Symphony.
Cuando hicimos una demo interna del sistema gestionando tareas y adjuntando su video de prueba de trabajo, la reacción fue abrumadoramente positiva: creció el canal de nuestro proyecto Symphony y equipos de toda la organización empezaron a usarlo de forma orgánica. El ajuste producto-mercado interno es un requisito previo para lanzar algo externamente en OpenAI. Con base en el uso que vimos en OpenAI, quedó claro que debíamos compartir Symphony más allá de las paredes de la empresa.
Así que extrajimos la idea a un SPEC.md independiente y le pedimos a Codex que lo implementara. Para la implementación de referencia, elegimos Elixir, un lenguaje relativamente de nicho con primitivas excelentes para orquestar y supervisar procesos concurrentes. Codex creó la implementación en Elixir de una sola vez, y desde ahí seguimos iterando tanto en la especificación como en la implementación. Para pulir la especificación, incluso le pedimos a Codex que la implementara en varios otros lenguajes —TypeScript, Go, Rust, Java, Python— y usara los resultados para identificar ambigüedades y simplificar el sistema. Tuvo éxito en todos los lenguajes.
Durante el proceso de crear Symphony, eliminamos mucha complejidad incidental, como dependencias de repositorios específicos o de Linear MCP. Symphony ya no depende de nuestros repositorios ni flujos de trabajo internos. El enfoque central se volvió simple:
Para cada tarea abierta, garantiza que haya un agente ejecutándose en su propio Espacio de trabajo.
Además de ayudar con el trabajo activo, el flujo de trabajo de desarrollo ahora es algo que los agentes conocen y siguen. El flujo de trabajo de desarrollo —trabajar en una incidencia, clonar un repositorio, ponerla en progreso para que el PM sepa que se está trabajando en ella, agregar el PR, moverla al estado Review, adjuntar videos, etc.— ahora está capturado en un simple archivo WORKFLOW.md. Todo esto era un proceso que los humanos seguían, pero nunca estuvo documentado. En lugar de depender de este conjunto implícito de pasos, ahora lo documentamos, y Symphony se asegura de que los agentes lo sigan. Esto nos permite crear agentes que trabajan junto a nosotros. Si decidimos que los agentes también deben adjuntar una autorreflexión al trabajo terminado, lo agregaremos al WORKFLOW.md, y Symphony guiará a los agentes hasta ese paso.
También pudimos usar Codex en modo app server(se abre en una nueva ventana), un modo headless integrado para Codex. Este modo nos permitió ejecutar Codex y comunicarnos con él de forma programática mediante una API JSON-RPC bien documentada para cosas como iniciar un hilo o reaccionar a turnos. Es más conveniente y escalable que intentar interactuar con Codex mediante CLI o sesiones en vivo de tmux.
Codex App Server fue perfecto para nuestro caso de uso: aprovechamos el harness que Codex proporciona mientras contamos con controles y hooks para integrarnos. Por ejemplo, para evitar exponer el token de acceso de Linear a los subagentes, usamos dynamic tool calls(se abre en una nueva ventana) para exponer la función sin procesar linear_graphql que ejecuta solicitudes arbitrarias contra Linear, sin depender de MCP ni exponer el token de acceso a los contenedores.
Lo que sigue
Symphony es una capa de orquestación intencionalmente mínima. La estamos liberando como código abierto para demostrar el poder de Codex App Server cuando se combina con distintas herramientas de flujo de trabajo, como Linear. Por eso, no planeamos mantener Symphony como un producto independiente. Piensa en ella como una implementación de referencia. De forma similar a como muchos desarrolladores apuntaron sus agentes de programación a la publicación sobre harness engineering para estructurar sus repositorios, esperamos que apuntes tu agente de programación favorito a la especificación(se abre en una nueva ventana) y al repositorio(se abre en una nueva ventana) de Symphony para crear tus propias versiones adaptadas a tus entornos.
El poder proviene de Codex y de su app server. Symphony fue una forma de conectar Codex con Linear, dos cosas que ya usábamos, para resolver el problema de la gestión del trabajo. A medida que los agentes de programación mejoren en razonamiento y en seguir instrucciones, sospechamos que el cuello de botella en otras empresas también pasará de escribir código a gestionar trabajo con agentes. Lo emocionante es que la barrera para experimentar con estos sistemas de agentes de programación ahora es sorprendentemente baja. Simplemente puedes crear cosas con Codex.
Menciones a la comunidad
Nos entusiasma ver a la comunidad de ingeniería usando Symphony en las semanas desde su lanzamiento, alcanzando más de 15 mil estrellas en GitHub(se abre en una nueva ventana) al 23 de abril.