Una especificació de codi obert per a l’orquestració de Codex: Symphony
Per Alex Kotliarskyi, Victor Zhu i Zach Brock
Fa sis mesos, mentre treballàvem en una eina interna de productivitat, el nostre equip va prendre una decisió controvertida (en aquell moment): crearíem el nostre repositori sense cap línia de codi escrita per humans. Cada línia del repositori del nostre projecte l'havia de generar Codex.
Per aconseguir-ho, vam redissenyar el nostre flux de treball d'enginyeria des de zero. Vam crear un repositori adaptat als agents, vam invertir molt en proves automatitzades i barreres de protecció, i vam tractar Codex com un company d’equip de ple dret. Vam documentar aquest procés a la nostra anterior entrada al blog sobre enginyeria de proves.
I va funcionar, però aleshores ens vam topar amb el següent coll d’ampolla: el canvi de context.
Per resoldre aquest nou problema, hem creat un sistema anomenat Symphony. Symphony(s'obre en una finestra nova) és un orquestrador d’agents que converteix un tauler de gestió de projectes com Linear en un pla de control per a agents de programació. Cada tasca oberta té un agent assignat, els agents s'executen contínuament i els humans revisen els resultats.
Aquest article explica com vam crear Symphony —que va suposar un increment del 500% de les pull requests integrades en alguns equips— i com utilitzar-lo per convertir el nostre propi sistema de seguiment d’incidències en un orquestrador d’agents sempre actiu.
El límit dels agents de codificació interactius
Tot i que cada vegada són més fàcils d’utilitzar, els agents de codificació —tant si s’hi accedeix a través d’aplicacions web com de la CLI— continuen sent eines interactives.
A mesura que augmentava l’escala del treball agentiu a OpenAI, ens vam trobar amb un nou tipus de càrrega. Cada enginyer obria unes quantes sessions de Codex, assignava tasques, revisava el resultat, guiava l'agent i repetia el procés. A la pràctica, la majoria de les persones podien gestionar amb comoditat entre tres i cinc sessions alhora abans que el canvi de context comencés a resultar feixuc. A partir d’aquí, la productivitat va baixar. Oblidàvem quina sessió feia què, saltàvem entre terminals per tornar a encarrilar els agents i depuràvem tasques de llarga durada que s’encallaven a mig camí.
Els agents eren ràpids, però teníem un coll d’ampolla al sistema: l’atenció humana. A la pràctica, havíem creat un equip d’enginyers júnior extremament capaços i després havíem assignat els nostres enginyers humans a microgestionar-los. Això no era escalable.
Un canvi de perspectiva
Ens vam adonar que estàvem optimitzant l’objectiu equivocat. Estàvem orientant el nostre sistema al voltant de les sessions de programació i les pull requests fusionades, quan en realitat les PRs i les sessions són un mitjà per aconseguir un objectiu. Els fluxos de treball de programari s'organitzen principalment al voltant de lliurables: incidències, tasques, tiquets i fites.
Així que ens vam preguntar què passaria si deixéssim de supervisar directament els agents i, en canvi, els deixéssim agafar feina del nostre gestor de tasques.
Aquella idea es va convertir en Symphony, una especificació escrita que actua com a supervisor per orquestrar el treball dels agents.
Conversió del nostre sistema de seguiment d’incidències en un orquestrador d’agents
Symphony va començar amb un concepte senzill: qualsevol tasca oberta hauria de ser assumida i completada per un agent. En lloc de gestionar les sessions de Codex en diverses pestanyes, hem convertit la nostra eina de seguiment d’incidències en el pla de control.
En aquesta configuració, cada incidència oberta de Linear es correspon amb un espai de treball d’agent específic. Symphony supervisa contínuament el tauler de tasques i garanteix que cada tasca activa tingui un agent executant-se en el cicle fins que finalitzi. Si un agent falla o es queda aturat, Symphony el reinicia. Si apareix feina nova, Symphony se n’encarrega i comença a organitzar-la.
Hem creat el nostre flux de treball basant-nos en els estats dels tiquets, fent servir el gestor de tasques Linear com a màquina d’estats.
A la pràctica, Symphony desacobla la feina de les sessions i de les pull request. Algunes incidències generen diverses sol·licituds d'extracció en diferents repositoris; d’altres són purament d’investigació o d’anàlisi i no arriben mai a tocar la base de codi.
Un cop la feina s’abstrau d’aquesta manera, els tiquets poden representar unitats de treball molt més grans.
Fem servir Symphony habitualment per orquestrar funcionalitats complexes i migracions d'infraestructura. Per exemple, podríem crear una tasca en què demanéssim a l’agent que analitzés la base de codi, Slack o Notion, i elaborés un pla d’implementació. Un cop estiguem satisfets amb el pla, l’agent genera un arbre de tasques, desglossa la feina en etapes i defineix les dependències entre tasques.
Els agents només comencen a treballar en tasques que no estan bloquejades, de manera que l’execució es desenvolupa de manera natural i òptima en paral·lel en aquest DAG (una seqüència de passos d’execució). Per exemple, vam marcar l’actualització de React com a bloquejada per una migració a Vite. Com era d’esperar, els agents van començar a actualitzar React només un cop completada la migració a Vite.
Els agents també poden crear feina per si mateixos. Durant la implementació o la revisió, sovint detecten millores que queden fora de l’abast de la tasca actual: un problema de rendiment, una oportunitat de refactorització o una arquitectura millor. Quan això passa, simplement creen una incidència nova que podem avaluar i programar més endavant. I moltes d’aquestes tasques de seguiment també les acaben assumint els agents. Mentre supervisem aquest procés, els agents es mantenen organitzats i fan que la feina continuï avançant.
Aquesta manera de treballar redueix dràsticament el cost cognitiu d’encetar feina ambigua. Si l’agent s’equivoca en alguna cosa, això continua sent informació útil i el cost per a nosaltres és pràcticament zero. Podem crear tiquets a molt baix cost perquè l’agent Go faci prototips i explori, i descartar qualsevol exploració que no ens agradi.
Com que l’orquestrador s’executa en devboxes i no s’atura mai, podem afegir tasques des de qualsevol lloc i saber que un agent se’n farà càrrec. Per exemple, un enginyer del nostre equip va fer tres canvis significatius des de l’aplicació Linear al seu mòbil, des d’una cabana acollidora amb una connexió wifi precària.
Increment de l’exploració en treballar d’aquesta manera
L'observació dels efectes de treballar amb Symphony evidencia que el canvi més evident és la productivitat. En alguns equips d’OpenAI, vam observar que el nombre de pull requests integrades augmentava un 500% durant les tres primeres setmanes. Fora d'OpenAI, el fundador de Linear, Karri Saarinen, va destacar un augment en la creació d'espais de treball(s'obre en una finestra nova) quan vam llançar Symphony. Tanmateix, el canvi més profund és la manera en què els equips conceben la feina.
Quan els nostres enginyers ja no dediquen temps a supervisar sessions de Codex, l’economia dels canvis en el codi canvia completament. El cost percebut de cada canvi disminueix perquè ja no invertim esforç humà a impulsar la implementació en si.
Això va canviar el nostre comportament. Ara és molt senzill engegar tasques especulatives a Symphony. Prova una idea, explora una refactorització, posa a prova una hipòtesi i conserva només els resultats que semblin prometedors.
També amplia el nombre de persones que poden iniciar una feina. Ara, la persona responsable de producte i la de disseny poden crear sol·licituds de funcions directament a Symphony. No han de clonar el repositori ni gestionar una sessió de Codex. Descriuen la funció i reben un paquet de revisió que inclou una guia pas a pas en vídeo de la funció funcionant dins del producte real.
Symphony també destaca en monorepositoris grans (com el que tenim a OpenAI), on el tram final per integrar una pull request és lent i fràgil. El sistema supervisa CI, fa rebase quan cal, resol conflictes, torna a provar verificacions inestables i, en general, guia els canvis al llarg del pipeline. Quan un tiquet arriba a Merging, tenim molta confiança que el canvi s’integrarà a la branca principal sense supervisió humana constant.
Després d'implementar Symphony, delegues més feina a agents i et centres en tasques més difícils i més exploratòries.
El progrés comporta problemes nous i diferents
Treballar a aquest nivell comporta contrapartides. Quan vam passar de guiar els agents de manera interactiva a assignar-los feina al nivell de tiquet, vam perdre la capacitat d’anar-los orientant constantment en ple vol i de corregir el rumb quan calgués. De vegades, l’agent generava alguna cosa que s’allunyava completament de l’objectiu. Va ser útil: aquelles fallades van revelar mancances en el sistema i ens van ajudar a fer-lo més robust.
En comptes de corregir el resultat manualment, vam afegir barreres de protecció i habilitats perquè els agents poguessin tenir èxit la propera vegada. Amb el temps, això ens va portar a afegir noves capacitats al nostre banc de proves, com ara executar proves d’extrem a extrem, controlar l’aplicació mitjançant Chrome DevTools i gestionar proves de fum de QA. Hem millorat significativament la nostra documentació i hem aclarit què es considera un bon resultat.
No totes les tasques encaixen amb l’estil de treball de Symphony. Alguns problemes encara requereixen que els enginyers treballin directament amb sessions interactives de Codex, especialment els problemes ambigus o les tasques que requereixen un bon criteri i expertesa. A la pràctica, aquestes solen ser les tasques més interessants i agradables a les quals els nostres enginyers dediquen temps.
La diferència és que Symphony pot assumir el gruix de la feina rutinària d’implementació. Això permet als enginyers centrar-se en un sol problema difícil cada vegada, en lloc d’anar canviant constantment de context entre tasques més petites.
També hem après que tractar els agents com a nodes rígids en una màquina d’estats no funciona gaire bé. Els models es tornen més intel·ligents i poden resoldre problemes de més envergadura que la caixa on intentem encabir-los. Les nostres primeres versions del treball agentiu consistien només a demanar a Codex que implementés la tasca. Aquest enfocament va resultar massa restrictiu. Codex és perfectament capaç de crear diverses pull requests, així com de llegir els comentaris de revisió i respondre-hi. Així que li vam donar eines —CLI de gh, habilitats per llegir registres de CI, etc.— i ara podem demanar-li a Codex que faci més coses, com ara tancar pull requests antics o generar-ne de nous sobre la feina completada en comparació amb feina abandonada. Aquest tipus de tasques quedaven molt fora de l’abast de la implementació inicial de la funcionalitat.
Així doncs, amb el temps vam avançar cap a assignar objectius als agents en lloc de transicions estrictes, de manera molt semblant a com un bon directiu assignaria un objectiu a un col·laborador directe del seu equip. La potència dels models rau en la seva capacitat de raonar; per tant, dona’ls eines i context, i deixa’ls fer.
Fer servir Symphony per crear Symphony
Quan obris el repositori de Symphony,(s'obre en una finestra nova) el primer que notaràs és que Symphony, tècnicament, no és més que un fitxer SPEC.md: una definició del problema i de la solució prevista. En lloc de crear un sistema de supervisió complex, vam definir el problema i les solucions previstes, donant als agents una orientació d’alt nivell.
La implementació de referència està escrita en Elixir —perquè quan el codi és pràcticament de franc, per fi pots triar els llenguatges pels seus punts forts, com ara la concurrència d’Elixir—, però la idea central es pot expressar en un document Markdown senzill. T’animem a donar l’especificació al teu agent de programació preferit i fer que implementi la seva pròpia versió.
La primera versió de Symphony era simplement una sessió de Codex que s’executava a tmux, consultava periòdicament Linear i creava subagents per a les tasques noves. Funcionava, però no era especialment fiable. La segona versió es trobava dins del repositori principal del nostre projecte, creat pensant en els agents. Ja havíem creat la infraestructura d’agents perquè tinguessin les habilitats i el context necessaris per fer feina d’alta qualitat en aquest repositori, així que Symphony simplement ho connecta tot.
Un cop creada la funcionalitat bàsica, vam fer servir Symphony per construir Symphony.
Quan vam fer una demostració interna del sistema gestionant tasques i adjuntant-ne el vídeo de prova de treball, la reacció va ser extraordinàriament positiva: el nostre canal del projecte Symphony va créixer, i equips de tota l’organització van començar a fer-lo servir de manera orgànica. El fet que un producte encaixi amb el mercat intern és un requisit imprescindible per llançar-lo al mercat extern a OpenAI. A partir de l'ús que vam observar a OpenAI, va quedar clar que havíem de fer arribar Symphony més enllà de l'ús intern de l'empresa.
Així que vam plasmar la idea en un SPEC.md independent i vam demanar al Codex que la implementés. Per a la implementació de referència vam triar Elixir, un llenguatge relativament especialitzat amb primitives excel·lents per orquestrar i supervisar processos concurrents. Codex va crear la implementació a Elixir amb un sol exemple, i a partir d’aquí vam continuar iterant tant en l’especificació com en la implementació. Per polir l'especificació, fins i tot vam demanar a Codex que la implementés en altres llenguatges —TypeScript, Go, Rust, Java i Python— i vam fer servir els resultats per identificar ambigüitats i simplificar el sistema. Va funcionar en tots els llenguatges.
Durant el procés de desenvolupament de Symphony, vam eliminar molta complexitat accidental, com ara dependències de repositoris específics o de Linear MCP. Symphony ja no depèn dels nostres repositoris ni fluxos de treball interns. L'enfocament central es va simplificar:
Per a cada tasca oberta, garanteix que un agent s’executi en el seu propi espai de treball.
A més d’ajudar amb el treball en curs, ara els agents coneixen i segueixen el flux de treball de desenvolupament. El flux de treball de desenvolupament —treballar en una incidència, fer checkout d’un repositori, posar-la en curs perquè el PM sàpiga que s’hi està treballant, afegir la PR, moure-la a l’estat Revisió, adjuntar vídeos, etc.— ara queda recollit en un senzill fitxer WORKFLOW.md. Tot això era un procés que els humans seguien, però que mai no es va documentar. En lloc de basar-nos en aquest conjunt implícit de passos, ara el documentem, i Symphony s’assegura que els agents el segueixin. Això ens permet crear agents que treballen conjuntament amb nosaltres. Si decidim que els agents també han d’adjuntar una autoreflexió a la feina acabada, ho afegirem a WORKFLOW.md, i Symphony guiarà els agents fins a aquest pas.
També vam poder utilitzar Codex en mode de servidor d’aplicacions(s'obre en una finestra nova), un mode sense interfície integrat per a Codex. Aquest mode ens va permetre executar Codex i comunicar-nos-hi programàticament mitjançant una API JSON-RPC ben documentada per a accions com ara iniciar un fil o reaccionar als torns. És més pràctic i escalable que intentar interactuar amb Codex a través de la CLI o de sessions de tmux en directe.
El servidor d'aplicacions de Codex va ser perfecte pera al nostre cas d’ús: aprofitem l’entorn que proporciona Codex i, alhora, disposem de paràmetres ajustables i punts d’extensió on connectar-nos. Per exemple, per evitar exposar el segment d’accés de Linear als subagents, fem servir crides dinàmiques a eines(s'obre en una finestra nova) per exposar la funció sense processar linear_graphql, que executa sol·licituds arbitràries contra Linear, sense dependre de l’MCP ni exposar el segment d’accés als contenidors.
Què ens ofereix el futur?
Symphony és una capa d’orquestració deliberadament mínima. Ho publiquem com a codi obert per demostrar la potència del servidor d'aplicacions de Codex quan es combina amb diferents eines de flux de treball, com ara Linear. Per tant, no tenim previst mantenir Symphony com a producte independent. Considera-ho una implementació de referència. De manera similar a com molts desenvolupadors van dirigir els seus agents de programació a la publicació d’enginyeria de Harness per crear l’esquelet dels seus repositoris, esperem que dirigeixis el teu agent de programació preferit a l'especificació(s'obre en una finestra nova) i al repositori(s'obre en una finestra nova) de Symphony per crear les teves pròpies versions adaptades als teus entorns.
La potència prové de Codex i del seu servidor d’aplicacions. Symphony era una manera de connectar Codex amb Linear, dues eines que ja fèiem servir, per resoldre el problema de la gestió del treball. A mesura que els agents de programació millorin en el raonament i el seguiment d’instruccions, sospitem que el coll d’ampolla en altres empreses també passarà d’escriure codi a gestionar el treball agentiu. El més interessant és que ara la barrera per experimentar amb aquests sistemes d'agents de programació és sorprenentment baixa. Pots crear coses simplement amb Codex.
Reconeixements de la comunitat
Ens entusiasma veure que la comunitat d'enginyeria fa servir Symphony en les setmanes transcorregudes des del llançament, i que hagi acumulat més de 15.000 estrelles a GitHub(s'obre en una finestra nova) a data del 23 d'abril.