En open source-spec för Codex-orkestrering: Symphony
Av Alex Kotliarskyi, Victor Zhu och Zach Brock
För sex månader sedan, medan vi arbetade med ett internt produktivitetsverktyg, fattade vårt team ett (då) kontroversiellt beslut: vi skulle bygga vårt förvar utan någon människoskriven kod. Varje rad i vårt projektförvar behövde genereras av Codex.
För att få det att fungera gjorde vi om vårt ingenjörsarbetsflöde från grunden. Vi byggde ett agentvänligt förvar, investerade stort i automatiserade tester och skyddsräcken och behandlade Codex som en fullvärdig teammedlem. Vi dokumenterade den resan i vårt tidigare blogginlägg om harness engineering.
Och det fungerade, men sedan stötte vi på nästa flaskhals: kontextväxling.
För att lösa det här nya problemet byggde vi ett system som heter Symphony. Symphony(öppnas i ett nytt fönster) är en agentorkestrerare som förvandlar en projektledningsboard som Linear till ett kontrollplan för kodningsagenter. Varje öppen uppgift får en agent, agenter kör kontinuerligt och människor granskar resultaten.
Det här inlägget förklarar hur vi skapade Symphony – vilket resulterade i en ökning på 500 % i landade pull request i vissa team – och hur du kan använda det för att förvandla din egen issue tracker till en alltid aktiv agentorkestrerare.
Taket för interaktiva kodningsagenter
Även när de blir enklare att använda är kodningsagenter – oavsett om de nås via webbappar eller CLI – fortfarande interaktiva verktyg.
När omfattningen av agentiskt arbete ökade på OpenAI fann vi en ny sorts börda. Varje ingenjör öppnade några Codex-sessioner, tilldelade uppgifter, granskade resultatet, styrde agenten och upprepade. I praktiken kunde de flesta bekvämt hantera tre till fem sessioner åt gången innan kontextväxlingen blev smärtsam. Utöver det sjönk produktiviteten. Vi glömde vilken session som gjorde vad, hoppade mellan terminaler för att puffa agenter tillbaka på rätt spår och felsökte långkörande uppgifter som stannade halvvägs.
Agenterna var snabba, men vi hade en systemflaskhals: mänsklig uppmärksamhet. Vi hade i praktiken byggt ett team av extremt kapabla junioringenjörer och sedan gett våra mänskliga ingenjörer uppgiften att detaljstyra dem. Det skulle inte skala.
Ett perspektivskifte
Vi insåg att vi optimerade fel sak. Vi orienterade vårt system kring kodningssessioner och sammanslagna PR:er, när PR:er och sessioner egentligen bara är ett medel för ett mål. Programvaruarbetsflöden är till stor del organiserade kring leverabler: problem, uppgifter, ärenden, milstolpar.
Så vi frågade oss vad som skulle hända om vi slutade övervaka agenter direkt och i stället lät dem hämta arbete från vår uppgiftshanterare.
Den idén blev Symphony, en skriven spec som fungerar som en handledare för att orkestrera agentiskt arbete.
Att förvandla vår problemspårare till en agentorkestrerare
Symphony började med ett enkelt koncept: varje öppen uppgift ska plockas upp och slutföras av en agent. I stället för att hantera Codex-sessioner i flera flikar gjorde vi vår problemspårare till kontrollpanel.
I den här uppsättningen mappas varje öppen Linear-issue till en dedikerad arbetsyta för en agent. Symphony bevakar kontinuerligt uppgiftsbrädet och ser till att varje aktiv uppgift har en agent som kör i loopen tills den är klar. Om en agent kraschar eller fastnar startar Symphony om den. Om nytt arbete dyker upp plockar Symphony upp det och börjar organisera arbetet.
Vi byggde vårt arbetsflöde utifrån ärendestatusar och använde uppgiftshanteraren Linear som en tillståndsmaskin.
I praktiken frikopplar Symphony arbete från sessioner och från pull request. Vissa problem ger upphov till flera PR:er över flera förvar, medan andra är ren utredning eller analys som aldrig rör kodbasen.
När arbetet abstraheras på det här sättet kan ärenden representera mycket större arbetsenheter.
Vi använder regelbundet Symphony för att orkestrera komplexa funktioner och migreringar av infrastruktur. Vi kan till exempel skapa en uppgift där agenten ombeds analysera kodbasen, Slack eller Notion och ta fram en implementeringsplan. När vi är nöjda med planen genererar agenten ett träd av uppgifter, delar upp arbetet i steg och definierar beroenden mellan uppgifter.
Agenter börjar bara arbeta med uppgifter som inte är blockerade, så körningen utvecklas naturligt och optimalt parallellt för denna DAG (en sekvens av körsteg). I exemplet nedan markerade vi React-uppgraderingen som blockerad av en migrering till Vite. Som väntat började agenter uppgradera React först efter att migreringen till Vite var klar.
Agenter kan också skapa arbete själva. Under implementation eller granskning lägger de ofta märke till förbättringar som ligger utanför den aktuella uppgiftens omfattning: ett prestandaproblem, en möjlighet till refaktorering eller en bättre arkitektur. När det händer skapar de helt enkelt ett nytt issue som vi kan utvärdera och schemalägga senare – många av dessa uppföljningsuppgifter plockas också upp av agenter. Medan vi har överblick över processen håller agenterna sig organiserade och för arbetet framåt.
Det här arbetssättet minskar dramatiskt den kognitiva kostnaden för att sätta igång tvetydigt arbete. Om agenten gör något fel är det fortfarande användbar information, och kostnaden för oss är nära noll. Vi kan mycket billigt skapa ärenden så att agenten kan bygga prototyper och utforska, och kasta bort alla utforskningar vi inte gillar.
Eftersom orkestreraren körs på devboxar och aldrig sover kan vi lägga till uppgifter var som helst och veta att en agent kommer att plocka upp dem. Till exempel gjorde en ingenjör i vårt team tre betydande ändringar från Linear-appen på sin telefon från en mysig stuga med dålig wifi.
Mer utforskning av att arbeta på det här sättet
När vi observerade effekterna av att arbeta med Symphony var den mest uppenbara förändringen output. I vissa team på OpenAI såg vi att antalet landade PR:er ökade med 6X under de första tre veckorna. Utanför OpenAI lyfte Linears grundare Karri Saarinen fram en ökning av skapade arbetsytor(öppnas i ett nytt fönster) när vi släppte Symphony. Men den djupare förändringen är hur team tänker om arbete.
När våra ingenjörer inte längre lägger tid på att övervaka Codex-sessioner förändras ekonomin i kodändringar helt. Den upplevda kostnaden för varje ändring sjunker eftersom vi inte längre investerar mänsklig ansträngning i att driva själva implementationen.
Det förändrade vårt beteende. Det har blivit trivialt att starta spekulativa uppgifter i Symphony. Prova en idé, utforska en refaktorering, testa en hypotes och behåll bara resultaten som ser lovande ut.
Det breddar också vilka som kan initiera arbete. Vår produktchef och designer kan nu skapa funktionsförfrågningar direkt i Symphony. De behöver inte checka ut förvaret eller hantera en Codex-session. De beskriver funktionen och får tillbaka ett granskningspaket som innehåller en videogenomgång av funktionen som fungerar i den riktiga produkten.
Symphony glänser också i stora monoförvar (som det vi har på OpenAI) där den sista ansträngningen för att landa en PR är långsam och skör. Systemet bevakar CI, rebasar vid behov, löser konflikter, försöker igen med flaky kontroller och lotsar i allmänhet ändringar genom pipelinen. När ett ärende når Merging är vi mycket säkra på att ändringen kommer in i huvudgrenen utan mänsklig barnpassning.
Framsteg kommer med nya, annorlunda problem
Att arbeta på den här nivån innebär avvägningar. När vi gick från att styra agenter interaktivt till att tilldela dem arbete på ärendenivå förlorade vi möjligheten att ständigt puffa dem under resans gång och korrigera kursen vid behov. Ibland producerade agenten något som missade målet helt. Det var användbart – de misslyckandena avslöjade luckor i systemet och hjälpte oss att göra det mer robust.
I stället för att lappa resultatet manuellt lade vi till skyddsräcken och färdigheter så att agenterna kunde lyckas nästa gång. Med tiden ledde det till att vi lade till nya förmågor i vår harness, som att köra end-to-end-tester, styra appen via Chrome DevTools och hantera QA-smoketester. Vi förbättrade vår dokumentation avsevärt och förtydligade hur bra ser ut.
Inte varje uppgift passar Symphony-stilen för arbete. Vissa problem kräver fortfarande ingenjörer som arbetar direkt med interaktiva Codex-sessioner, särskilt tvetydiga problem eller arbete som kräver starkt omdöme och expertis. I praktiken är detta vanligtvis de mest intressanta och roliga uppgifterna för våra ingenjörer att lägga tid på.
Skillnaden är att Symphony kan hantera huvuddelen av rutinmässigt implementationsarbete. Det gör att ingenjörer kan fokusera på ett svårt problem i taget i stället för att ständigt växla kontext mellan mindre uppgifter.
Vi lärde oss också att det inte fungerar särskilt bra att behandla agenter som rigida noder i en tillståndsmaskin. Modeller blir smartare och kan lösa större problem än den låda vi försöker passa in dem i. Till exempel hade de tidiga versionerna alla GitHub-integrationer som en del av den yttre harnessen – tidiga versioner förväntade sig till exempel att Codex bara skulle göra kodändringar och specificerade resten av processen (skicka in ändringar, köra tester) i kod. Våra tidiga versioner av agentiskt arbete bad bara Codex att implementera uppgiften. Det angreppssättet visade sig vara alltför begränsande. Codex är fullt kapabelt att skapa flera PR:er samt att läsa granskningsfeedback och åtgärda den. Så vi gav det verktyg – gh-CLI, färdigheter för att läsa CI-loggar osv. – och nu kan vi be Codex att göra mer, som att stänga gamla PR:er eller hämta rapporter om slutfört kontra övergivet arbete. Dessa typer av uppgifter låg långt utanför den ursprungliga rutan för funktionsimplementation.
Så vi gick så småningom mot att ge agenter mål i stället för strikta övergångar, ungefär som en bra chef skulle tilldela ett mål till en direkt underställd i sitt team. Kraften i modeller kommer från deras förmåga till resonemang, så ge dem verktyg och kontext och låt dem köra.
Använda Symphony för att bygga Symphony
När du öppnar Symphony-förvaret är det första du märker att Symphony tekniskt sett bara är en SPEC.md-fil – en definition av problemet och den avsedda lösningen. I stället för att bygga ett komplext övervakningssystem definierade vi problemet och de avsedda lösningarna, och gav agenter övergripande styrning.
Referensimplementeringen är skriven i Elixir – för när kod är så gott som gratis kan du äntligen välja språk utifrån deras styrkor, som Elixirs samtidighet – men kärnidén kan uttryckas i ett enkelt Markdown-dokument. Vi uppmuntrar dig att rikta din favoritkodningsagent mot specen och låta den implementera sin egen version.
Den första versionen av Symphony var bara en Codex-session som kördes i tmux, hämtade Linear och skapade underagenter för nya uppgifter. Det fungerade, men var inte särskilt tillförlitligt. Den andra versionen låg i vårt huvudsakliga projektförvar, som byggdes med agenter i åtanke. Vi hade redan byggt agentselen för att ge agenter färdigheter och kontext för att göra högkvalitativt arbete i detta förvar, så Symphony kopplar helt enkelt ihop allt.
När den grundläggande funktionaliteten väl fanns använde vi Symphony för att bygga Symphony.
När vi internt demonstrerade systemet som hanterade uppgifter och bifogade sin proof-of-work-video var reaktionen överväldigande positiv: vår Symphony-projektkanal växte och team över hela organisationen började använda det organiskt. Intern product-market-fit är en förutsättning för att lansera externt på OpenAI. Baserat på användningen vi såg på OpenAI blev det tydligt att vi borde dela Symphony utanför företagets väggar.
Så vi extraherade idén till en fristående SPEC.md och bad Codex att implementera den. För referensimplementeringen valde vi Elixir, ett relativt nischat språk med utmärkta primitiver för att orkestrera och övervaka samtidiga processer. Codex byggde Elixir-implementeringen i ett svep, och därifrån fortsatte vi att iterera på både spec och implementation. För att förfina specen bad vi till och med Codex att implementera den i flera andra språk –TypeScript, Go, Rust, Java, Python – och använda resultaten för att identifiera tvetydigheter och förenkla systemet. Det lyckades i varje språk.
Genom processen att bygga Codex tog vi bort mycket oavsiktlig komplexitet, som beroenden till specifika förvar eller Linear MCP. Symphony är inte längre beroende av våra interna förvar eller arbetsflöden. Kärnmetoden blev enkel:
För varje öppen uppgift, säkerställ att en agent körs i sin egen arbetsyta.
Förutom att hjälpa till med det aktiva arbetet är utvecklingsarbetsflödet nu något som agenter känner till och följer. Utvecklingsarbetsflödet – arbeta med ett problem, checka ut ett förvar, sätta det till pågående så att PM vet att det arbetas med, lägga till PR:n, flytta det till statusen Review, bifoga videor osv. – fångas nu i en enkel WORKFLOW.md-fil. Allt detta är en process som människor följde, men som aldrig dokumenterades. I stället för att förlita oss på denna implicita uppsättning steg dokumenterar vi den nu, och Symphony säkerställer att agenter följer den. Det gör att vi kan bygga agenter som arbetar sida vid sida med oss. Om vi bestämmer att agenter också ska bifoga självreflektion till färdigt arbete lägger vi till det i WORKFLOW.md, och Symphony guidar agenterna till det steget.
Vi fick också använda Codex i app server-läge(öppnas i ett nytt fönster), ett inbyggt headless-läge för Codex. Det här läget gjorde det möjligt för oss att köra Codex och prata med det programmatiskt via ett väldokumenterat JSON-RPC API för saker som att starta en tråd eller reagera på turer. Det är ett mycket bekvämare och mer skalbart sätt än att försöka interagera med Codex via CLI eller live-tmux-sessioner.
Codex App Server passade perfekt för vårt användningsfall: vi drar nytta av den harness som Codex tillhandahåller samtidigt som vi har reglage och krokar att koppla in i. För att undvika att exponera Linear-åtkomsttoken för underagenter använder vi till exempel dynamiska verktygsanrop(öppnas i ett nytt fönster) för att exponera den råa linear_graphql-funktionen som kör godtyckliga förfrågningar mot Linear, utan att förlita sig på MCP eller exponera åtkomsttoken för containrar.
Vad händer härnäst
Symphony är ett avsiktligt minimalt orkestreringslager. Vi gör det till open source för att demonstrera kraften i Codex App Server när det paras ihop med olika arbetsflödesverktyg, som Linear. Därför planerar vi inte att underhålla Symphony som en fristående produkt. Tänk på det som en referensimplementering. Precis som många utvecklare riktade sina kodningsagenter mot inlägget om harness engineering för att scaffolda sina förvar hoppas vi att du riktar din favoritkodningsagent mot Symphony-specen(öppnas i ett nytt fönster) och förvaret(öppnas i ett nytt fönster) för att bygga dina egna versioner anpassade till dina miljöer.
Kraften kommer från Codex och dess app server. Symphony var ett sätt att koppla samman Codex med Linear, två saker vi redan använde, för att lösa problemet med arbetshantering. När kodningsagenter blir bättre på resonemang och att följa instruktioner misstänker vi att flaskhalsen även hos andra företag kommer att flyttas från att skriva kod till att hantera agentiskt arbete. Den spännande delen är att tröskeln för att experimentera med dessa system av kodningsagenter nu är förvånansvärt låg. Du kan helt enkelt bygga saker med Codex.
Hälsningar till communityn
Vi är glada över att se ingenjörscommunityn använda Symphony under veckorna sedan lanseringen, och att det per den 23 april har samlat över 15 000 GitHub-stjärnor(öppnas i ett nytt fönster).