Una specifica open-source per l’orchestrazione di Codex: Symphony
Di Alex Kotliarskyi, Victor Zhu e Zach Brock
Sei mesi fa, mentre lavoravamo a uno strumento interno di produttività, il nostro team ha preso una decisione controversa (all’epoca): avremmo costruito il nostro repository senza codice scritto da esseri umani. Ogni riga nel nostro repository di progetto doveva essere generata da Codex.
Per farlo funzionare, abbiamo riprogettato il nostro flusso di lavoro di ingegneria da zero. Abbiamo creato un repository adatto agli agenti, investito molto in test automatizzati e guardrail, e trattato Codex come un vero compagno di squadra. Abbiamo documentato quel percorso nel nostro precedente post del blog sull’harness engineering.
E ha funzionato, ma poi ci siamo scontrati con il collo di bottiglia successivo: il cambio di contesto.
Per risolvere questo nuovo problema, abbiamo creato un sistema chiamato Symphony. Symphony(si apre in una nuova finestra) è un orchestratore di agenti che trasforma una board di gestione dei progetti come Linear in un piano di controllo per agenti di coding. Ogni task aperto riceve un agente, gli agenti lavorano in modo continuo e gli esseri umani ne revisionano i risultati.
Questo articolo spiega come abbiamo creato Symphony, ottenendo su alcuni team un aumento del 500% nelle pull request integrate, e come usarlo per trasformare il tuo issue tracker in un orchestratore di agenti sempre attivo.
Il limite massimo degli agenti di coding interattivi
Anche se stanno diventando più facili da usare, gli agenti di coding — sia tramite app web sia tramite CLI — restano strumenti interattivi.
Con l’aumento della scala del lavoro agentico in OpenAI, abbiamo scoperto un nuovo tipo di peso. Ogni ingegnere apriva alcune sessioni di Codex, assegnava task, rivedeva l’output, guidava l’agente e ripeteva. In pratica, la maggior parte delle persone riusciva a gestire comodamente da tre a cinque sessioni alla volta prima che il cambio di contesto diventasse doloroso. Oltre quel punto, la produttività calava. Ci dimenticavamo quale sessione stesse facendo cosa, saltavamo tra terminali per rimettere in carreggiata gli agenti e facevamo debug di task di lunga durata che si bloccavano a metà.
Gli agenti erano veloci, ma avevamo un collo di bottiglia di sistema: l’attenzione umana. Avevamo di fatto costruito un team di ingegneri junior estremamente capaci, per poi assegnare ai nostri ingegneri umani il compito di microgestirli. Non era una cosa scalabile.
Un cambio di prospettiva
Ci siamo resi conto che stavamo ottimizzando la cosa sbagliata. Stavamo orientando il nostro sistema attorno alle sessioni di coding e alle PR unite, quando in realtà PR e sessioni sono solo un mezzo per arrivare a un fine. I flussi di lavoro software sono per lo più organizzati attorno ai deliverable: issue, task, ticket, milestone.
Così ci siamo chiesti cosa sarebbe successo se avessimo smesso di supervisionare direttamente gli agenti e li avessimo invece lasciati attingere lavoro dal nostro task tracker.
Quell’idea è diventata Symphony, una specifica scritta che funge da supervisore per orchestrare il lavoro agentico.
Trasformare il nostro issue tracker in un orchestratore di agenti
Symphony è nata da un concetto semplice: qualsiasi task aperto dovrebbe essere preso in carico e completato da un agente. Invece di gestire sessioni di Codex in più schede, abbiamo reso il nostro issue tracker il piano di controllo.
In questa configurazione, ogni issue aperta in Linear corrisponde a un’area di lavoro dedicata per un agente. Symphony monitora continuamente la board dei task e garantisce che ogni task attivo abbia un agente in esecuzione nel ciclo finché non è completato. Se un agente va in crash o si blocca, Symphony lo riavvia. Se compare nuovo lavoro, Symphony lo rileva e inizia a organizzarlo.
Abbiamo costruito il nostro flusso di lavoro sulla base degli stati dei ticket, usando il task manager Linear come macchina a stati.
In pratica, Symphony disaccoppia il lavoro dalle sessioni e dalle pull request. Alcune issue producono più PR in diversi repository; altre sono pura indagine o analisi e non toccano mai la codebase.
Una volta astratto il lavoro in questo modo, i ticket possono rappresentare unità di lavoro molto più grandi.
Usiamo regolarmente Symphony per orchestrare funzionalità complesse e migrazioni dell’infrastruttura. Per esempio, possiamo aprire un task chiedendo all’agente di analizzare la codebase, Slack o Notion e produrre un piano di implementazione. Una volta soddisfatti del piano, l’agente genera un albero di task, suddividendo il lavoro in fasi e definendo le dipendenze tra i task.
Gli agenti iniziano a lavorare solo sui task che non sono bloccati, quindi l’esecuzione si sviluppa naturalmente e in modo ottimale in parallelo per questo DAG (una sequenza di passaggi di esecuzione). Nell’esempio qui sotto, abbiamo segnato l’aggiornamento di React come bloccato da una migrazione a Vite. Come previsto, gli agenti hanno iniziato ad aggiornare React solo dopo il completamento della migrazione a Vite.
Gli agenti possono anche creare lavoro da soli. Durante l’implementazione o la revisione, spesso notano miglioramenti che esulano dallo scopo del task corrente: un problema di prestazioni, un’opportunità di refactoring o un’architettura migliore. Quando accade, aprono semplicemente una nuova issue che possiamo valutare e pianificare in seguito; molti di questi task di follow-up vengono poi anch’essi presi in carico dagli agenti. Mentre supervisioniamo questo processo, gli agenti restano organizzati e fanno avanzare il lavoro.
Questo modo di lavorare riduce drasticamente il costo cognitivo di avviare lavoro ambiguo. Se l’agente sbaglia qualcosa, è comunque un’informazione utile, e per noi il costo è quasi nullo. Possiamo aprire ticket a costo molto basso perché l’agente vada a prototipare ed esplorare, e scartare qualsiasi esplorazione che non ci piaccia.
Poiché l’orchestratore gira su devbox e non dorme mai, possiamo aggiungere task da qualsiasi luogo e sapere che un agente se ne occuperà. Per esempio, un ingegnere del nostro team ha apportato tre modifiche significative dall’app Linear sul suo telefono, da una comoda baita con un wifi scadente.
Un aumento dell’esplorazione lavorando in questo modo
Osservando gli effetti del lavoro con Symphony, il cambiamento più evidente è stato l’output. In alcuni team di OpenAI, abbiamo visto il numero di PR integrate aumentare di 6 volte nelle prime tre settimane. Al di fuori di OpenAI, il fondatore di Linear Karri Saarinen ha evidenziato un picco nelle aree di lavoro create(si apre in una nuova finestra) mentre rilasciavamo Symphony. Tuttavia, il cambiamento più profondo riguarda il modo in cui i team pensano al lavoro.
Quando i nostri ingegneri non passano più tempo a supervisionare le sessioni di Codex, l’economia delle modifiche al codice cambia completamente. Il costo percepito di ogni modifica diminuisce perché non stiamo più investendo sforzo umano nel guidare l’implementazione stessa.
Questo ha cambiato il nostro comportamento. È diventato banale avviare task speculativi in Symphony. Prova un’idea, esplora un refactoring, testa un’ipotesi e conserva solo i risultati che sembrano promettenti.
Questo amplia anche chi può avviare il lavoro. Il nostro product manager e il nostro designer possono ora inserire richieste di funzionalità direttamente in Symphony. Non hanno bisogno di fare il check-out del repository o gestire una sessione di Codex. Descrivono la funzionalità e ricevono un pacchetto di revisione che include una video walkthrough della funzionalità in esecuzione nel prodotto reale.
Symphony dà il meglio di sé anche nei grandi monorepo (come quello che abbiamo in OpenAI) dove l’ultimo miglio per integrare una PR è lento e fragile. Il sistema monitora la CI, esegue il rebase quando necessario, risolve i conflitti, ritenta i controlli instabili e, in generale, accompagna le modifiche attraverso la pipeline. Quando un ticket arriva a Merging, abbiamo un’elevata fiducia che la modifica entrerà nel branch principale senza babysitting umano.
Dopo aver implementato Symphony, deleghiamo più lavoro agli agenti e ci concentriamo su task più difficili e più esplorativi.
Il progresso porta con sé problemi nuovi e diversi
Operare a questo livello comporta dei compromessi. Quando siamo passati dal guidare gli agenti in modo interattivo all’assegnare loro lavoro a livello di ticket, abbiamo perso la possibilità di correggerli costantemente in corsa e riorientarli quando necessario. A volte l’agente produceva qualcosa che mancava completamente l’obiettivo. È stato utile: quei fallimenti hanno rivelato lacune nel sistema e ci hanno aiutato a renderlo più robusto.
Invece di correggere manualmente il risultato, abbiamo aggiunto guardrail e competenze affinché gli agenti potessero riuscirci la volta successiva. Con il tempo, questo ci ha portato ad aggiungere nuove capacità al nostro harness, come eseguire test end-to-end, pilotare l’app tramite Chrome DevTools e gestire smoke test QA. Abbiamo migliorato significativamente la nostra documentazione e chiarito che cosa significa un buon risultato.
Non tutti i task si adattano allo stile di lavoro di Symphony. Alcuni problemi richiedono ancora ingegneri che lavorano direttamente con sessioni interattive di Codex, soprattutto problemi ambigui o lavoro che richiede forte giudizio ed esperienza. In pratica, questi sono di solito i task più interessanti e piacevoli su cui i nostri ingegneri possono dedicare tempo.
La differenza è che Symphony può gestire la maggior parte del lavoro di implementazione di routine. Questo permette agli ingegneri di concentrarsi su un solo problema difficile alla volta invece di cambiare continuamente contesto tra task più piccoli.
Abbiamo anche imparato che trattare gli agenti come nodi rigidi in una macchina a stati non funziona bene. I modelli diventano più intelligenti e possono risolvere problemi più grandi del contenitore in cui cerchiamo di farli entrare. Per esempio, nelle prime versioni tutte le integrazioni GitHub facevano parte dell’harness esterno: per esempio, le prime versioni si aspettavano che Codex facesse solo modifiche al codice, specificando il resto del processo (invio delle modifiche, esecuzione dei test) nel codice. Le nostre prime versioni del lavoro agentico chiedevano a Codex solo di implementare il task. Questo approccio si è rivelato troppo limitante. Codex è perfettamente in grado di creare più PR, nonché leggere il feedback di revisione e affrontarlo. Quindi gli abbiamo fornito strumenti — CLI gh, competenze per leggere i log della CI, ecc. — e ora possiamo chiedere a Codex di fare di più, come chiudere vecchie PR o recuperare report sul lavoro completato rispetto a quello abbandonato. Questi tipi di task andavano ben oltre il contenitore iniziale dell’implementazione di funzionalità.
Alla fine ci siamo quindi orientati verso il dare agli agenti degli obiettivi invece di transizioni rigide, un po’ come farebbe un buon manager assegnando un obiettivo a un diretto collaboratore del team. La potenza dei modelli deriva dalla loro capacità di ragionamento, quindi dai loro strumenti e contesto e lascia che facciano il loro lavoro.
Usare Symphony per costruire Symphony
Quando apri il repository di Symphony, la prima cosa che noterai è che Symphony è tecnicamente solo un file SPEC.md: una definizione del problema e della soluzione prevista. Invece di costruire un sistema di supervisione complesso, abbiamo definito il problema e le soluzioni previste, offrendo agli agenti una guida di alto livello.
L’implementazione di riferimento è scritta in Elixir, perché quando il codice è di fatto gratuito puoi finalmente scegliere i linguaggi per i loro punti di forza, come la concorrenza di Elixir, ma l’idea centrale può essere espressa in un semplice documento Markdown. Ti incoraggiamo a puntare il tuo agente di coding preferito verso la specifica e fargli implementare una propria versione.
La prima versione di Symphony era semplicemente una sessione di Codex in esecuzione in tmux, che interrogava Linear e avviava sotto-agenti per i nuovi task. Funzionava, ma non era particolarmente affidabile. La seconda versione viveva nel nostro repository di progetto principale, costruito pensando agli agenti. Avevamo già creato l’harness per fornire agli agenti le competenze e il contesto necessari a svolgere lavoro di alta qualità in questo repository, quindi Symphony si limita a collegare tutto.
Una volta esistita la funzionalità di base, abbiamo usato Symphony per costruire Symphony.
Quando abbiamo mostrato internamente il sistema che gestiva i task e allegava il suo video di prova del lavoro svolto, la reazione è stata estremamente positiva: il canale del nostro progetto Symphony è cresciuto e i team di tutta l’organizzazione hanno iniziato a usarlo spontaneamente. L’aderenza interna prodotto-mercato è un prerequisito per il lancio esterno in OpenAI. In base all’utilizzo che abbiamo visto in OpenAI, è diventato chiaro che dovevamo condividere Symphony oltre i confini aziendali.
Così abbiamo estratto l’idea in un SPEC.md autonomo e chiesto a Codex di implementarlo. Per l’implementazione di riferimento abbiamo scelto Elixir, un linguaggio relativamente di nicchia con primitive eccellenti per orchestrare e supervisionare processi concorrenti. Codex ha realizzato l’implementazione in Elixir in un colpo solo, e da lì abbiamo continuato a iterare sia sulla specifica sia sull’implementazione. Per rifinire la specifica, abbiamo perfino chiesto a Codex di implementarla in diversi altri linguaggi — TypeScript, Go, Rust, Java, Python — e di usare i risultati per individuare ambiguità e semplificare il sistema. Ci è riuscito in ogni linguaggio.
Nel processo di costruzione di Codex, abbiamo rimosso molta complessità accidentale, come le dipendenze da repository specifici o da Linear MCP. Symphony non dipende più dai nostri repository o flussi di lavoro interni. L’approccio di base è diventato semplice:
Per ogni task aperto, garantisci che un agente sia in esecuzione nella propria area di lavoro.
Oltre ad aiutare con il lavoro attivo, il flusso di lavoro di sviluppo è ora qualcosa che gli agenti conoscono e seguono. Il flusso di lavoro di sviluppo — lavorare su una issue, fare il check-out di un repository, metterla in corso così il PM sa che ci si sta lavorando, aggiungere la PR, spostarla nello stato Review, allegare video, ecc. — è ora catturato in un semplice file WORKFLOW.md. Tutto questo era un processo seguito dagli esseri umani, ma non era mai stato documentato. Invece di affidarci a questo insieme implicito di passaggi, ora lo documentiamo, e Symphony fa sì che gli agenti lo seguano. Questo ci permette di costruire agenti che lavorano al nostro fianco. Se decidiamo che gli agenti debbano anche allegare un’autoriflessione al lavoro completato, lo aggiungeremo al WORKFLOW.md e Symphony guiderà gli agenti fino a quel passaggio.
Abbiamo anche potuto usare Codex in modalità app server(si apre in una nuova finestra), una modalità headless integrata per Codex. Questa modalità ci ha permesso di eseguire Codex e interagirci programmaticamente tramite un’API JSON-RPC ben documentata per operazioni come avviare un thread o reagire ai turni. È un modo molto più comodo e scalabile rispetto al tentativo di interagire con Codex tramite CLI o sessioni tmux live.
Codex App Server era perfetto per il nostro caso d’uso: sfruttiamo l’harness fornito da Codex avendo al contempo manopole e hook a cui collegarci. Per esempio, per evitare di esporre il token di accesso di Linear ai sotto-agenti, usiamo chiamate dinamiche agli strumenti(si apre in una nuova finestra) per esporre la funzione grezza linear_graphql che esegue richieste arbitrarie verso Linear, senza fare affidamento su MCP né esporre il token di accesso ai container.
Cosa c’è dopo
Symphony è un livello di orchestrazione intenzionalmente minimale. Lo rendiamo open source per dimostrare la potenza di Codex App Server quando viene abbinato a diversi strumenti di flusso di lavoro, come Linear. Per questo, non prevediamo di mantenere Symphony come prodotto autonomo. Consideralo un’implementazione di riferimento. In modo simile a come molti sviluppatori hanno indirizzato i loro agenti di coding verso il post sull’harness engineering per fare lo scaffold dei loro repository, speriamo che tu indirizzi il tuo agente di coding preferito verso la specifica(si apre in una nuova finestra) e il repository(si apre in una nuova finestra) di Symphony per creare versioni personalizzate per i tuoi ambienti.
La potenza deriva da Codex e dal suo app server. Symphony è stato un modo per collegare Codex a Linear, due cose che già usavamo, per risolvere il problema della gestione del lavoro. Man mano che gli agenti di coding migliorano nel ragionamento e nel seguire istruzioni, sospettiamo che anche in altre aziende il collo di bottiglia si sposterà dalla scrittura del codice alla gestione del lavoro agentico. La parte entusiasmante è che la barriera alla sperimentazione con questi sistemi di agenti di coding è ora sorprendentemente bassa. Puoi semplicemente costruire cose con Codex.
Segnalazioni della community
Siamo entusiasti di vedere la community ingegneristica usare Symphony nelle settimane successive al rilascio, raggiungendo oltre 15.000 stelle su GitHub(si apre in una nuova finestra) al 23 aprile.