En open source-spec til Codex-orkestrering: Symphony
Af Alex Kotliarskyi, Victor Zhu og Zach Brock
For seks måneder siden, mens vi arbejdede på et internt produktivitetsværktøj, traf vores team en kontroversiel beslutning (på det tidspunkt): Vi ville bygge vores lager uden kode skrevet af mennesker. Hver linje i vores projektlager skulle genereres af Codex.
For at få det til at fungere redesignede vi vores engineering-workflow fra bunden. Vi byggede et agentvenligt lager, investerede massivt i automatiserede tests og sikkerhedsforanstaltninger og behandlede Codex som en fuldgyldig holdkammerat. Vi dokumenterede den rejse i vores tidligere blogindlæg om harness engineering.
Og det virkede, men så stødte vi på den næste flaskehals: kontekstskift.
For at løse dette nye problem byggede vi et system kaldet Symphony. Symphony(åbner i et nyt vindue) er en orkestrator-agent, der gør en projektstyringstavle som Linear til et kontrolplan for kode-agenter. Hver åben opgave får en agent, agenter kører kontinuerligt, og mennesker gennemgår resultaterne.
Dette indlæg forklarer, hvordan vi skabte Symphony – hvilket resulterede i en stigning på 500 % i merged pull-requests på nogle teams – og hvordan du kan bruge det til at gøre din egen issue tracker til en altid aktiv orkestrator-agent.
Grænsen for interaktive kode-agenter
Selv om de bliver lettere at bruge, er kode-agenter – uanset om de tilgås via webapps eller CLI – stadig interaktive værktøjer.
Efterhånden som omfanget af agentisk arbejde voksede hos OpenAI, fandt vi en ny type byrde. Hver ingeniør ville åbne nogle få Codex-sessioner, tildele opgaver, gennemgå outputtet, styre agenten og gentage. I praksis kunne de fleste komfortabelt håndtere tre til fem sessioner ad gangen, før kontekstskift blev vanskeligt. Ud over det faldt produktiviteten. Vi glemte, hvilken session der lavede hvad, sprang mellem terminaler for at puffe agenter tilbage på sporet og fejlrettede langvarige opgaver, der gik i stå halvvejs.
Agenterne var hurtige, men vi havde en systemflaskehals: menneskelig opmærksomhed. Vi havde i praksis bygget et team af ekstremt dygtige junioringeniører og derefter sat vores menneskelige ingeniører til at mikrostyre dem. Det ville kunne ikke skalere.
Et perspektivskifte
Vi indså, at vi optimerede den forkerte ting. Vi indrettede vores system omkring kodningssessioner og mergede PRs, når PRs og sessioner i virkeligheden bare er et middel til et mål. Software-workflows er i høj grad organiseret omkring leverancer: issues, opgaver, tickets, milepæle.
Så vi spurgte os selv, hvad der ville ske, hvis vi holdt op med at overvåge agenter direkte og i stedet lod dem hente arbejde fra vores opgavetracker.
Den idé blev til Symphony, en skriftlig specifikation, der fungerer som supervisor til at orkestrere agentisk arbejde.
At gøre vores issue tracker til en orkestratoragent
Symphony startede med et simpelt koncept: enhver åben opgave skal tages op og fuldføres af en agent. I stedet for at administrere Codex-sessioner i flere faner gjorde vi vores issue-tracker til kontrolplanet.
I denne opsætning svarer hver åben Linear-sag til et dedikeret agent-arbejdsområde. Symphony overvåger løbende opgavetavlen og sikrer, at hver aktiv opgave har en agent kørende i loopet, indtil den er færdig. Hvis en agent crasher eller går i stå, genstarter Symphony den. Hvis der dukker nyt arbejde op, samler Symphony det op og begynder at organisere arbejdet.
Vi byggede vores workflow baseret på ticket-statusser og brugte opgavestyringsværktøjet Linear som en state machine.
I praksis afkobler Symphony arbejde fra sessioner og fra pull requests. Nogle issues producerer flere PRs på tværs af lagre; andre er ren undersøgelse eller analyse, som aldrig berører kodebasen.
Når arbejde abstraheres på denne måde, kan tickets repræsentere langt større arbejdsenheder.
Vi bruger regelmæssigt Symphony til at orkestrere komplekse funktioner og infrastrukturmigreringer. Vi kan for eksempel oprette en opgave, der beder agenten analysere kodebasen, Slack eller Notion og udarbejde en implementeringsplan. Når vi er tilfredse med planen, genererer agenten et træ af opgaver, opdeler arbejdet i faser og definerer afhængigheder mellem opgaver.
Agenter begynder kun at arbejde på opgaver, der ikke er blokerede, så udførelsen forløber naturligt og optimalt parallelt for denne DAG (en sekvens af udførelsestrin). I eksemplet nedenfor markerede vi React-opgraderingen som blokeret af en migrering til Vite. Som forventet begyndte agenterne først at opgradere React, efter at migreringen til Vite var fuldført.
Agenter kan også selv skabe arbejde. Under implementering eller gennemgang opdager de ofte forbedringer, der ligger uden for den aktuelle opgaves omfang: et performanceproblem, en mulighed for refaktorering eller en bedre arkitektur. Når det sker, opretter de blot en ny issue, som vi kan evaluere og planlægge senere, mange af disse opfølgende opgaver bliver også samlet op af agenter. Mens vi holder opsyn med denne proces, forbliver agenter organiserede og holder arbejdet i gang.
Denne måde at arbejde på reducerer dramatisk den kognitive omkostning ved at sætte uklart arbejde i gang. Hvis agenten tager fejl, er det stadig nyttig information, og omkostningen for os er tæt på nul. Vi kan meget billigt oprette tickets, så agenten kan lave prototyper og udforske, og kassere de udforskninger , vi ikke kan lide.
Fordi orkestratoren kører på devboxes og aldrig sover, kan vi tilføje opgaver hvor som helst fra og vide, at en agent vil samle dem op. For eksempel foretog en ingeniør på vores team tre væsentlige ændringer fra Linear-appen på sin telefon fra en hyggelig hytte med ustabil wifi.
En stigning i udforskning ved at arbejde på denne måde
Da vi observerede effekterne af at arbejde med Symphony, var den mest åbenlyse ændring output. Blandt nogle teams hos OpenAI så vi antallet af landede PRs stige 6x i løbet af de første tre uger. Uden for OpenAI fremhævede Linears grundlægger Karri Saarinen en stigning i oprettede arbejdsområder(åbner i et nyt vindue), da vi udgav Symphony. Den dybere ændring er dog, hvordan teams tænker om arbejde.
Når vores ingeniører ikke længere bruger tid på at overvåge Codex-sessioner, ændres økonomien i kodeændringer fuldstændigt. Den oplevede omkostning ved hver ændring falder, fordi vi ikke længere investerer menneskelig indsats i at drive selve implementeringen.
Det ændrede vores adfærd. Det er blevet trivielt at starte spekulative opgaver op i Symphony. Prøve en idé, udforske en refaktorering, teste en hypotese og kun beholde de resultater, der ser lovende ud.
Det udvider også, hvem der kan sætte arbejde i gang. Vores produktchef og designer kan nu oprette feature requests direkte i Symphony. De behøver ikke at tjekke lageret ud eller administrere en Codex-session. De beskriver funktionen og får en review-pakke tilbage, der inkluderer en videogennemgang af funktionen, som virker i det rigtige produkt.
Symphony brillerer også i store mono-lagre (som det, vi har hos OpenAI), hvor den sidste del af at lande en PR er langsom og skrøbelig. Systemet overvåger CI, rebaser ved behov, løser konflikter, prøver ustabile checks igen og fører generelt ændringer gennem pipelinen. Når en ticket når Merging, har vi stor tiltro til, at ændringen kommer ind i hovedgrenen uden menneskelig babysitting.
Med fremskridt kommer nye, anderledes problemer
At operere på dette niveau kommer med afvejninger. Da vi gik fra at styre agenter interaktivt til at tildele dem arbejde på ticketniveau, mistede vi evnen til konstant at puffe til dem undervejs og korrigere kursen, når det var nødvendigt. Nogle gange producerede agenten noget, der ramte helt ved siden af. Det var nyttigt – de fejl afslørede huller i systemet og hjalp os med at gøre det mere robust.
I stedet for at lappe resultatet manuelt tilføjede vi sikkerhedsforanstaltninger og færdigheder, så agenterne kunne lykkes næste gang. Med tiden førte det til, at vi tilføjede nye kapaciteter til vores harness, såsom at køre end-to-end-tests, styre appen via Chrome DevTools og håndtere QA-smoketests. Vi forbedrede vores dokumentation markant og tydeliggjorde, hvordan godt ser ud.
Ikke alle opgaver passer til Symphonys arbejdsstil. Nogle problemer kræver stadig, at ingeniører arbejder direkte med interaktive Codex-sessioner, især uklare problemer eller arbejde, der kræver stærk dømmekraft og ekspertise. I praksis er disse som regel de mest interessante og behagelige opgaver for vores ingeniører at bruge tid på.
Forskellen er, at Symphony kan håndtere hovedparten af det rutinemæssige implementeringsarbejde. Det lader ingeniører fokusere på ét svært problem ad gangen i stedet for konstant at skifte kontekst mellem mindre opgaver.
Vi lærte også, at det ikke fungerer godt at behandle agenter som rigide knuder i en state machine. Modeller bliver klogere og kan løse større problemer end den kasse, vi prøver at presse dem ned i. For eksempel havde de tidlige versioner alle GitHub-integrationerne som en del af den ydre harness – for eksempel forventede de tidlige versioner, at Codex kun foretog kodeændringer, mens resten af processen (indsendelse af ændringer, kørsel af tests) blev specificeret i kode. Vores tidlige versioner af agentisk arbejde bad kun Codex om at implementere opgaven. Den tilgang viste sig at være for begrænsende. Codex er fuldt ud i stand til at oprette flere PRs samt læse reviewfeedback og håndtere den. Så vi gav den værktøjer – gh CLI, skills til at læse CI-logs osv. – og nu kan vi bede Codex om at gøre mere, såsom at lukke gamle PRs eller hente rapporter om fuldført kontra opgivet arbejde. Disse typer opgaver lå langt uden for den oprindelige boks for featureimplementering.
Så vi bevægede os efterhånden i retning af at give agenter mål i stedet for stramme overgange, meget ligesom en god leder ville tildele et mål til en medarbejder på sit team. Kraften i modeller kommer fra deres evne til ræsonnering, så giv dem værktøjer og kontekst, og lad dem arbejde.
At bruge Symphony til at bygge Symphony
Når du åbner Symphony-lageret, er det første, du vil bemærke, at Symphony teknisk set bare er en SPEC.md-fil, en definition af problemet og den tilsigtede løsning. I stedet for at bygge et komplekst supervisionssystem definerede vi problemet og de tilsigtede løsninger og gav agenter styring på højt niveau.
Referenceimplementeringen er skrevet i Elixir, fordi når kode reelt er gratis, kan man endelig vælge sprog ud fra deres styrker, som Elixirs samtidighed, men kerneideen kan udtrykkes i et simpelt Markdown-dokument. Vi opfordrer dig til at rette din foretrukne kode-agent mod specifikationen og få den til at implementere sin egen version.
Den første version af Symphony var blot en Codex-session, der kørte i tmux, tjekkede og udhentede data fra Linear og startede underagenter til nye opgaver. Det virkede, men det var ikke særlig pålideligt. Den anden version lå i vores primære projektlager, som var bygget med agenter i tankerne. Vi havde allerede bygget agent-harnessen til at give agenter de skills og den kontekst, der skulle til for at udføre arbejde af høj kvalitet i dette lager, så Symphony forbinder det hele ganske enkelt.
Da den grundlæggende funktionalitet var på plads, brugte vi Symphony til at bygge Symphony.
Da vi internt demonstrerede systemet, der håndterede opgaver og vedhæftede sin proof-of-work-video, var reaktionen overvældende positiv: vores Symphony-projektkanal voksede, og teams på tværs af organisationen begyndte at bruge det helt naturligt. Intern product-market fit (PMF) er en forudsætning for ekstern lancering hos OpenAI. Baseret på den brug, vi så hos OpenAI, stod det klart, at vi burde dele Symphony uden for virksomhedens vægge.
Så vi trak ideen ud i en selvstændig SPEC.md og bad Codex om at implementere den. Til referenceimplementeringen valgte vi Elixir, et relativt nichepræget sprog med fremragende primitiver til at orkestrere og overvåge samtidige processer. Codex byggede Elixir-implementeringen i et one-shot, og derfra fortsatte vi med at iterere på både specifikationen og implementeringen. For at finpudse specifikationen bad vi endda Codex om at implementere den på flere andre sprog–TypeScript, Go, Rust, Java, Python – og bruge resultaterne til at identificere tvetydigheder og forenkle systemet. Det lykkedes på alle sprog.
Gennem processen med at bygge Codex fjernede vi en masse tilfældig kompleksitet, som afhængigheder til specifikke lagre eller Linear MCP. Symphony afhænger ikke længere af vores interne lagre eller workflows. Kernetilgangen blev enkel:
For hver åben opgave skal du sikre, at en agent kører i sit eget arbejdsområde.
Ud over at hjælpe med det aktive arbejde er udviklingsworkflowet nu også noget, agenter kender og følger. Udviklingsworkflowet – arbejd på et issue, tjek et lager ud, sæt den til i behandling, så PM'en ved, at der arbejdes på den, tilføj PR'en, flyt den til status Review, vedhæft videoer osv. – er nu indfanget i en simpel WORKFLOW.md-fil. Alt dette var en proces, som mennesker fulgte, men den var aldrig dokumenteret. I stedet for at stole på dette implicitte sæt af trin dokumenterer vi det nu, og Symphony sikrer, at agenter følger det. Det lader os bygge agenter, der arbejder side om side med os. Hvis vi beslutter, at agenter også skal vedhæfte selvrefleksion til færdigt arbejde, tilføjer vi det til WORKFLOW.md, og Symphony vil guide agenterne til det trin.
Vi fik også mulighed for at bruge Codex i app server mode(åbner i et nyt vindue), en indbygget headless-tilstand til Codex. Denne tilstand gjorde det muligt for os at køre Codex og kommunikere med det programmæssigt v+ia et veldokumenteret JSON-RPC-API til ting som at starte en tråd eller reagere på ture. Det er en langt mere bekvem og skalerbar måde end at forsøge at interagere med Codex via CLI eller live-tmux-sessioner.
Codex App Server passede perfekt til vores use case: Vi udnytter den harness, som Codex leverer, samtidig med at vi har knapper og hooks at koble os på. For eksempel bruger vi dynamiske værktøjskald(åbner i et nyt vindue) til at eksponere den rå linear_graphql-funktion, der udfører vilkårlige forespørgsler mod Linear, uden at være afhængige af MCP eller eksponere adgangstoken til containere, for at undgå at eksponere Linear-adgangstoken til underagenter.
Hvad er det næste
Symphony er et bevidst minimalistisk orkestreringslag. Vi open sourcer det for at demonstrere styrken i Codex App Server, når det kobles sammen med forskellige workflowværktøjer, som Linear. Som sådan planlægger vi ikke at vedligeholde Symphony som et selvstændigt produkt. Tænk på det som en referenceimplementering. Ligesom mange udviklere rettede deres kode-agenter mod indlægget om harness engineering for at scaffolde deres lagre, håber vi, at du retter din foretrukne kode-agent mod Symphony-specifikationen(åbner i et nyt vindue) og lageret(åbner i et nyt vindue) for at bygge dine egne versioner tilpasset dine miljøer.
Kraften kommer fra Codex og dets appserver. Symphony var en måde at forbinde Codex med Linear, to ting vi allerede brugte, for at løse problemet med arbejdsstyring. Efterhånden som kode-agenter bliver bedre til ræsonnering og til at følge instruktioner, formoder vi, at flaskehalsen i andre virksomheder også vil flytte sig fra at skrive kode til at styre agentisk arbejde. Det spændende er, at barrieren for at eksperimentere med disse kode-agentsystemer nu er overraskende lav. Du kan bare bygge ting med Codex.
Community-shoutouts
Vi er begejstrede for at se ingeniør-fællesskabet bruge Symphony i ugerne siden udgivelsen, og at det pr. 23. april har fået over 15.000 GitHub-stjerner(åbner i et nyt vindue).