En åpen kildekode-spesifikasjon for Codex-orkestrering: Symphony
Av Alex Kotliarskyi, Victor Zhu og Zach Brock
For seks måneder siden, mens vi jobbet med et internt produktivitetsverktøy, tok teamet vårt en (den gang) kontroversiell beslutning: Vi skulle bygge repoet vårt uten kode skrevet av mennesker. Hver eneste linje i prosjektlageret måtte genereres av Codex.
For å få det til redesignet vi ingeniørarbeidsflyten fra grunnen av. Vi bygde et agentvennlig repositorium, satset stort på automatiserte tester og sikkerhetsmekanismer, og behandlet Codex som et fullverdig teammedlem. Den reisen dokumenterte vi i forrige blogginnlegg om rammeverkutvikling.
Og det fungerte, men så støtte vi på neste flaskehals: kontekstbytte.
For å løse dette nye problemet bygde vi et system kalt Symphony. Symphony(åpnes i et nytt vindu) er en agentorkestrator som gjør en prosjektstyringstavle som Linear om til et kontrollplan for kodeagenter. Hver åpne oppgave får en agent, agentene kjører kontinuerlig, og mennesker gjennomgår resultatene.
Dette innlegget forklarer hvordan vi skapte Symphony (noe som førte til en økning på 500 % i fullførte pull requests i enkelte team), og hvordan du kan bruke det til å gjøre din egen sakssporer om til en agentorkestrator som alltid er på.
Grensen for hva interaktive kodeagenter kan oppnå
Selv om de blir enklere å bruke, er kodeagenter, enten de brukes via nettapper eller CLI, fortsatt interaktive verktøy.
Etter hvert som omfanget av agentisk arbeid økte i OpenAI, oppdaget vi en ny type belastning. Hver ingeniør åpnet noen Codex-økter, tildelte oppgaver, gjennomgikk resultatet, styrte agenten og gjentok. I praksis kunne de fleste komfortabelt håndtere tre til fem økter samtidig før kontekstbytte ble problematisk. Utover det falt produktiviteten. Vi glemte hvilken økt som gjorde hva, hoppet fra terminal til terminal for å dytte agentene tilbake på sporet, og feilsøkte langvarige oppgaver som stoppet halvveis.
Agentene var raske, men vi hadde en systemflaskehals: menneskelig oppmerksomhet. Vi hadde i praksis bygget et team av svært kapable juniorutviklere, og deretter satt de menneskelige utviklerne våre til å mikrostyre dem. Det ville ikke skalere.
Et perspektivskifte
Vi innså at vi optimaliserte feil ting. Vi innrettet systemet vårt rundt kodeøkter og fullførte pull requests, når pull requests og økter egentlig bare er et middel mot et mål. Programvarearbeidsflyter er i stor grad organisert rundt leveranser: saker, oppgaver, tickets, milepæler.
Så vi spurte oss selv hva som ville skje hvis vi sluttet å overvåke agenter direkte og i stedet lot dem hente arbeid fra oppgavesporeren vår.
Den ideen ble til Symphony, en skriftlig spesifikasjon som fungerer som en styringskomponent for å orkestrere agentisk arbeid.
Sakssporeren gjøres om til en agentorkestrator
Symphony startet med et enkelt konsept: enhver åpen oppgave burde tas opp og fullføres av en agent. I stedet for å håndtere Codex-økter i flere faner lot vi sakssporeren bli kontrollplanet.
I dette oppsettet tilordnes hver åpne sak i Linear et dedikert agent-arbeidsområde. Symphony overvåker oppgavetavlen kontinuerlig og sørger for at hver aktive oppgave har en agent som kjører i loopen til den er ferdig. Hvis en agent krasjer eller stopper opp, starter Symphony den på nytt. Hvis nytt arbeid dukker opp, plukker Symphony det opp og begynner å organisere arbeidet.
Vi bygde arbeidsflyten vår rundt ticket-statuser og brukte oppgavebehandleren Linear som en tilstandsmaskin.
I praksis frikobler Symphony arbeid fra økter og pull requests. Noen saker fører til flere pull requests i ulike repositorier; andre er ren utredning eller analyse som aldri berører kodebasen.
Når arbeid abstraheres på denne måten, kan tickets representere mye større arbeidsenheter.
Vi bruker jevnlig Symphony til å orkestrere komplekse funksjoner og migrering av infrastruktur. For eksempel kan vi opprette en oppgave som ber agenten analysere kodebasen, Slack eller Notion og lage en implementeringsplan. Når vi er fornøyde med planen, genererer agenten et tre av oppgaver, bryter arbeidet ned i faser og definerer avhengigheter mellom oppgavene.
Agenter begynner bare å jobbe med oppgaver som ikke er blokkert, så kjøringen utfolder seg naturlig og optimalt parallelt for denne DAG-en (en sekvens av kjøretrinn). I eksemplet nedenfor markerte vi React-oppgraderingen som avhengig av en migrering til Vite først. Som forventet begynte agentene å oppgradere React først etter at migreringen til Vite var fullført.
Agenter kan også opprette arbeid selv. Under implementering eller gjennomgang legger de ofte merke til forbedringer som faller utenfor omfanget av den gjeldende oppgaven: et ytelsesproblem, en refaktoreringsmulighet eller en bedre arkitektur. Når det skjer, oppretter de ganske enkelt en ny sak som vi kan evaluere og planlegge senere. Mange av disse oppfølgingsoppgavene blir også tatt hånd om av agenter. Mens vi holder oversikten over denne prosessen, holder agentene seg organiserte og får arbeidet til å gå fremover.
Denne måten å jobbe på reduserer dramatisk den kognitive kostnaden ved å sette i gang tvetydig arbeid. Hvis agenten gjør noe feil, er det fortsatt nyttig informasjon, og kostnaden for oss er nær null. Vi kan svært billig opprette tickets som agenten kan bruke til å lage en prototype og utforske, og forkaste alle utforskninger vi ikke liker.
Fordi orkestratoren kjører på devboxer og aldri sover, kan vi legge til oppgaver fra hvor som helst og vite at en agent vil ta hånd om dem. For eksempel gjorde en ingeniør på teamet vårt tre betydelige endringer fra Linear-appen på telefonen sin fra en hytte med ustabil wifi.
Mer utforskning ved å jobbe på denne måten
Da vi observerte effektene av å jobbe med Symphony, var den mest åpenbare endringen output. I noen team i OpenAI så vi at antallet mergede PR-er ble seksdoblet i løpet av de første tre ukene. Utenfor OpenAI fremhevet Linear-grunnlegger Karri Saarinen en økning i opprettede arbeidsområder(åpnes i et nytt vindu) da vi lanserte Symphony. Den dypere endringen er imidlertid hvordan team tenker om arbeid.
Når ingeniørene våre ikke lenger bruker tid på å overvåke Codex-økter, endres økonomien i kodeendringer fullstendig. Den opplevde kostnaden ved hver endring faller fordi vi ikke lenger investerer menneskelig innsats på selve implementeringen.
Det endret atferden vår. Det er blitt svært enkelt å sette i gang spekulative oppgaver i Symphony. Prøv en idé, utforsk en refaktorering, test en hypotese, og behold bare resultatene som ser lovende ut.
Det gjør det også mulig for flere å sette i gang arbeid. Produktlederen og designeren vår kan nå sende inn funksjonsforespørsler direkte i Symphony. De trenger ikke å sjekke ut repoet eller håndtere en Codex-økt. De beskriver funksjonen og får tilbake en pakke for gjennomgang med en videodemonstrasjon av hvordan den fungerer i det faktiske produktet.
Symphony utmerker seg også i store monorepoer (som det vi har i OpenAI), der den siste etappen med å få en pull request helt i mål er treg og sårbar. Systemet overvåker CI, gjør rebase ved behov, løser konflikter, prøver på nytt når sjekker er ustabile, og følger generelt endringer gjennom pipelinen. Når en sak når Merging, har vi ganske stor sikkerhet for at endringen går inn i main-branchen uten behov for menneskelig oppfølging.
Fremgang kommer med nye, andre problemer
Å operere på dette nivået innebærer avveininger. Da vi gikk fra å styre agenter interaktivt til å tildele dem arbeid på ticket-nivå, mistet vi muligheten til å dytte på dem kontinuerlig underveis og korrigere kursen ved behov. Noen ganger produserte agenten noe som bommet fullstendig. Men det var nyttig – disse feilene avdekket mangler i systemet og hjalp oss med å gjøre det mer robust.
I stedet for å lappe resultatet manuelt, la vi til sikkerhetsmekanismer og ferdigheter slik at agentene kunne lykkes neste gang. Over tid førte dette til at vi la til nye funksjoner i rammeverket, som å kjøre ende-til-ende-tester, styre appen gjennom Chromes DevTools og håndtere QA-smoketester. Vi forbedret dokumentasjonen betydelig og satte standarden for hva som er bra.
Ikke alle oppgaver passer til arbeidsstilen i Symphony. Noen problemer krever fortsatt ingeniører som jobber direkte med interaktive Codex-økter, spesielt tvetydige problemer eller arbeid som krever god dømmekraft og ekspertise. I praksis er dette vanligvis de mest interessante og givende oppgavene for ingeniørene våre å bruke tid på.
Forskjellen er at Symphony kan håndtere hovedandelen av rutinemessig implementeringsarbeid. Det gir ingeniørene muligheten til å fokusere på ett vanskelig problem om gangen i stedet for å stadig bytte kontekst mellom mindre oppgaver.
Vi lærte også at det ikke fungerer særlig godt å behandle agenter som rigide noder i en tilstandsmaskin. Modeller blir smartere og kan løse større problemer enn boksen vi prøver å få dem til å passe inn i. For eksempel hadde de tidlige versjonene alle GitHub-integrasjonene som en del av det ytre rammeverket – for eksempel forventet tidlige versjoner at Codex bare skulle gjøre kodeendringer, mens resten av prosessen (sende inn endringer, kjøre tester) ble spesifisert i kode. De tidlige versjonene av agentisk arbeid ba bare Codex om å implementere oppgaven. Den tilnærmingen viste seg å være for begrensende. Codex er fullt i stand til å opprette flere PR-er, samt lese tilbakemeldinger fra gjennomgang og følge dem opp. Så vi ga den verktøy (gh-CLI, ferdigheter til å lese CI-logger osv.), og nå kan vi be Codex gjøre mer, som å lukke gamle PR-er eller hente rapporter om fullført kontra forlatt arbeid. Denne typen oppgaver falt langt utenfor den opprinnelige boksen for funksjonsimplementering.
Til slutt gikk vi derfor i retning av å gi agenter mål i stedet for strenge overganger, omtrent slik en god leder gir et mål til en ansatt. Kraften i modellene kommer fra evnen deres til å resonnere, og så gi dem verktøy og kontekst og la dem gjøre resten.
Bruke Symphony til å bygge Symphony
Når du åpner Symphony-repoet, er det første du legger merke til, at Symphony teknisk sett bare er en SPEC.md-fil: en definisjon av problemet og den tiltenkte løsningen. I stedet for å bygge et komplekst tilsynssystem definerte vi problemet og de tiltenkte løsningene, og ga agenter styring på høyt nivå.
Referanseimplementasjonen er skrevet i Elixir (for når kode i praksis er gratis, kan du endelig velge språk ut fra styrkene deres, som Elixirs samtidighet) men kjerneideen kan uttrykkes i et enkelt Markdown-dokument. Vi oppfordrer deg til å peke din favorittkodeagent mot spesifikasjonen og la den implementere sin egen versjon.
Den første versjonen av Symphony var bare en Codex-økt som kjørte i tmux, som jevnlig sjekket Linear og startet underagenter til nye oppgaver. Det fungerte, men det var ikke spesielt pålitelig. Den andre versjonen var i hovedrepoet vårt, som var bygget med tanke på agenter. Vi hadde allerede bygget agentrammeverket for å gi agenter ferdighetene og konteksten de trengte for å gjøre arbeid av høy kvalitet i dette repoet, så Symphony kobler ganske enkelt alt sammen.
Da grunnfunksjonaliteten først fantes, brukte vi Symphony til å bygge Symphony.
Da vi internt demonstrerte systemet som håndterte oppgaver og la ved dokumentasjonsvideoen, var reaksjonen overveldende positiv: Symphony-prosjektkanalen vokste, og team i hele organisasjonen begynte å bruke det organisk. Intern product-market fit er en forutsetning for å lansere eksternt i OpenAI. Basert på bruken vi så i OpenAI, ble det klart at vi burde dele Symphony utenfor selskapets vegger.
Så vi trakk ut ideen til en frittstående SPEC.md og ba Codex om å implementere den. For referanseimplementasjonen valgte vi Elixir, et relativt nisjepreget språk med utmerkede primitiver for å orkestrere og overvåke samtidige prosesser. Codex bygde Elixir-implementasjonen i one-shot, og derfra fortsatte vi å iterere på både spesifikasjon og implementasjon. For å finpusse spesifikasjonen ba vi til og med Codex om å implementere den på flere andre språk (TypeScript, Go, Rust, Java, Python) og bruke resultatene til å identifisere tvetydigheter og forenkle systemet. Det lyktes på alle språkene.
Gjennom prosessen med å bygge Codex fjernet vi mye tilfeldig kompleksitet, som bindinger til spesifikke repositorier eller Linear MCP. Symphony er ikke lenger avhengig av våre interne repoer eller arbeidsflyter. Kjerneopplegget ble enkelt:
Hver åpne oppgave må ha en agent som kjører i sitt eget arbeidsområde.
I tillegg til å hjelpe med det aktive arbeidet er utviklingsarbeidsflyten nå noe agentene kjenner til og følger. Utviklingsarbeidsflyten (jobb med en sak, sjekk ut et repo, sett den til «pågår», slik at prosjektlederen vet at det jobbes med den, legg til PR-en, flytt den til gjennomgangsstatus, legg ved videoer osv.) er nå fanget opp i en enkel WORKFLOW.md-fil. Alt dette er en prosess som mennesker fulgte, men som aldri var dokumentert. I stedet for å basere oss på denne underforståtte arbeidsflyten, dokumenterer vi den nå, og Symphony sørger for at agentene følger den. Dette gjør at vi kan bygge agenter som jobber side om side med oss. Hvis vi bestemmer at agenter også skal legge ved selvrefleksjon til ferdig arbeid, legger vi det til i WORKFLOW.md, og Symphony vil lede agentene til det trinnet.
Vi fikk også bruke Codex i appserver-modus(åpnes i et nytt vindu), en innebygd headless-modus for Codex. I denne modusen kunne vi kjøre Codex og snakke med den programmatisk via et godt dokumentert JSON-RPC-API for ting som å starte en tråd eller reagere på samtalesteg. Det er en langt mer praktisk og skalerbar måte enn å prøve å samhandle med Codex via CLI eller live tmux-økter.
Codex App Server passet perfekt til brukstilfellet vårt: vi utnytter rammeverket som Codex tilbyr, samtidig som vi har justeringsmuligheter og integrasjonspunkter å koble oss på. For eksempel, for å unngå å eksponere Linear-tilgangstokenet til underagenter, bruker vi dynamiske verktøykall(åpnes i et nytt vindu) for å eksponere den rå linear_graphql-funksjonen som utfører vilkårlige forespørsler til Linear, uten å være avhengig av MCP eller eksponere tilgangstokenet til containere.
Hva skjer videre
Symphony er et bevisst minimalt orkestreringslag. Vi publiserer det som åpen kildekode for å demonstrere kraften i Codex App Server når det kobles med ulike arbeidsflytverktøy, som Linear. Derfor planlegger vi ikke å vedlikeholde Symphony som et selvstendig produkt. Tenk på det som en referanseimplementasjon. På samme måte som mange utviklere pekte kodeagentene sine mot innlegget om rammeverkutvikling for å sette opp grunnstrukturene i repoene sine, håper vi at du peker din favorittkodeagent mot Symphony-spesifikasjonen(åpnes i et nytt vindu) og -repoet(åpnes i et nytt vindu) for å bygge dine egne versjoner tilpasset miljøene dine.
Kraften kommer fra Codex og appserveren. Symphony var en måte å koble Codex til Linear på, to ting vi allerede brukte, for å løse problemet med arbeidshåndtering. Etter hvert som kodeagenter blir bedre til å resonnere og følge instruksjoner, tenker vi at flaskehalsen i andre selskaper også vil gå fra å skrive kode til å håndtere agentisk arbeid. Det spennende er at terskelen for å eksperimentere med disse kodeagentsystemene nå er overraskende lav. Du kan bare bygge ting med Codex.
Hyllester fra fellesskapet
Det gleder oss å se ingeniørfellesskapet bruke Symphony i ukene siden lanseringen, og at det har fått over 15 000 stjerner på GitHub(åpnes i et nytt vindu) per 23. april.