Odprtokodna specifikacija za orkestracijo Codex: Symphony
Avtorji: Alex Kotliarskyi, Victor Zhu in Zach Brock
Pred šestimi meseci je naša ekipa med delom na internem orodju za produktivnost sprejela sporno (takrat) odločitev: naš repozitorij bomo zgradili brez kode, ki bi jo napisal človek. Vsaka vrstica v našem projektnem repozitoriju je morala biti ustvarjena s Codexom.
Da bi to delovalo, smo svoj inženirski potek dela preoblikovali od temeljev. Zgradili smo agentom prijazen repozitorij, veliko vložili v avtomatizirane teste in varovalke ter Codex obravnavali kot polnopravnega člana ekipe. To pot smo opisali v naši prejšnji objavi na blogu o harness engineering (inženiring z ogrodjem).
In delovalo je, potem pa smo naleteli na naslednje ozko grlo: preklapljanje med konteksti.
Da bi rešili to novo težavo, smo zgradili sistem z imenom Symphony. Symphony(odpre se v novem oknu) je orkestrator agentov, ki ploščo za vodenje projektov, kot je Linear, spremeni v nadzorno raven za agente za programiranje. Vsako odprto opravilo dobi agenta, agenti tečejo neprekinjeno, ljudje pa pregledajo rezultate.
Ta objava pojasnjuje, kako smo ustvarili Symphony—kar je pri nekaterih ekipah privedlo do 500 % povečanja uspešnih zahtev pull request—in kako ga uporabiti, da svoj sledilnik težav spremenite v vedno vključen orkestrator agentov.
Meja interaktivnih agentov za programiranje
Tudi ko postajajo enostavnejši za uporabo, so agenti za programiranje—ne glede na to, ali do njih dostopate prek spletnih aplikacij ali CLI—še vedno interaktivna orodja.
Ko se je obseg agentnega dela v OpenAI povečal, smo naleteli na novo vrsto bremena. Vsak inženir je odprl nekaj sej Codex, dodelil naloge, pregledal rezultate, usmerjal agenta in to ponovil. V praksi je večina ljudi lahko udobno upravljala tri do pet sej hkrati, preden je preklapljanje med konteksti postalo boleče. Pri višjem številu sej je produktivnost padla. Pozabili smo, katera seja počne kaj, preskakovali med terminali, da bi agente usmerili nazaj na pravo pot, in razhroščevali dolgotrajna opravila, ki so obstala na pol poti.
Agenti so bili hitri, vendar smo imeli sistemsko ozko grlo: človeško pozornost. Dejansko smo zgradili ekipo izjemno sposobnih mlajših inženirjev, nato pa našim človeškim inženirjem dodelili, da jih mikroupravljajo. Pri tem ni bilo mogoče povečati obsega.
Premik v perspektivi
Spoznali smo, da optimiziramo napačno stvar. Svoj sistem smo usmerjali okoli sej programiranja in združenih zahtev pull request, čeprav so zahteve pull request in seje v resnici le sredstvo za cilj. Poteki dela v programski opremi so v veliki meri organizirani okoli dobavljivih rezultatov: težav, nalog, zahtevkov, mejnikov.
Zato smo se vprašali, kaj bi se zgodilo, če bi agente nehali neposredno nadzorovati in jim namesto tega dovolili, da delo prevzemajo iz našega sledilnika opravil.
Ta ideja je postala Symphony, zapisana specifikacija, ki deluje kot nadzornik za orkestracijo agentnega dela.
Kako svoj sledilnik težav spremeniti v orkestrator agentov
Symphony se je začel s preprostim konceptom: vsako odprto opravilo mora prevzeti in dokončati agent. Namesto upravljanja sej Codex v več zavihkih smo svoj sledilnik težav oblikovali v nadzorno raven.
V tej postavitvi se vsaka odprta težava v Linear preslika v namenski delovni prostor agenta. Symphony neprekinjeno spremlja ploščo z opravili in zagotavlja, da ima vsako aktivno opravilo zagnanega agenta v zanki, dokler ni dokončano. Če se agent sesuje ali obstane, ga Symphony zažene znova. Če se pojavi novo delo, ga Symphony prevzame in začne organizirati delo.
Svoj potek dela smo zgradili na podlagi statusov zahtevkov, pri čemer smo upravljalnik opravil Linear uporabljali kot avtomat.
V praksi Symphony loči delo od sej in od zahtev pull request. Nekatere težave ustvarijo več zahtev pull request v različnih repozitorijih; druge so zgolj raziskava ali analiza, ki se kode sploh ne dotakne.
Ko je delo abstrahirano na ta način, lahko zahtevki predstavljajo veliko večje enote dela.
Symphony redno uporabljamo za orkestracijo kompleksnih funkcij in migracij infrastrukture. Na primer, lahko odpremo opravilo, v katerem agenta prosimo, naj analizira kodno bazo, Slack ali Notion in pripravi načrt implementacije. Ko smo z načrtom zadovoljni, agent ustvari drevo opravil, delo razdeli na faze in določi odvisnosti med opravili.
Agenti začnejo delati samo na opravilih, ki niso blokirana, zato se izvajanje za ta DAG (zaporedje korakov izvajanja) odvija naravno in optimalno vzporedno. V spodnjem primeru smo nadgradnjo React označili kot blokirano zaradi migracije na Vite. Kot pričakovano so agenti začeli nadgrajevati React šele po končani migraciji na Vite.
Agenti lahko delo ustvarjajo tudi sami. Med implementacijo ali pregledom pogosto opazijo izboljšave, ki ne sodijo v obseg trenutnega opravila: težavo z zmogljivostjo, priložnost za refaktorizacijo ali boljšo arhitekturo. Ko se to zgodi, preprosto odprejo novo težavo, ki jo lahko ocenimo in razporedimo pozneje—mnoga od teh nadaljnjih opravil prevzamejo tudi agenti. Medtem ko ta proces nadzorujemo, agenti ostajajo organizirani in skrbijo, da delo napreduje.
Tak način dela dramatično zmanjša kognitivni strošek zaganjanja dvoumnega dela. Če agent kaj naredi narobe, je to še vedno koristna informacija, strošek za nas pa je skoraj ničeln. Zelo poceni lahko predložimo zahtevke, da gre agent izdelovat prototipe in raziskovat, nato pa zavržemo vse raziskave, ki nam niso všeč.
Ker orkestrator teče na razvojnih računalnikih in nikoli ne spi, lahko opravila dodajamo od koder koli in vemo, da jih bo agent prevzel. Tako je na primer en inženir v naši ekipi v aplikaciji Linear na telefonu opravil tri pomembne spremembe iz prijetne koče s slabim omrežjem wifi.
Povečanje raziskovanja pri takem načinu dela
Ko smo opazovali učinke dela s Symphonyjem, je bila najbolj očitna sprememba količina izhodov. Pri nekaterih ekipah v OpenAI se je število uspešnih zahtev pull request v prvih treh tednih povečalo za 6-krat. Zunaj OpenAI je ustanovitelj Linear Karri Saarinen ob izdaji Symphonyja izpostavil skok v številu ustvarjenih delovnih prostorov(odpre se v novem oknu). Vendar je globlji premik v tem, kako ekipe razmišljajo o delu.
Ko naši inženirji ne porabljajo več časa za nadzor sej Codex, se ekonomika sprememb kode popolnoma spremeni. Zaznani strošek vsake spremembe pade, ker človeškega truda ne vlagamo več v samo usmerjanje implementacije.
To je spremenilo naše vedenje. V Symphonyju je postalo trivialno zagnati špekulativna opravila. Poskusi idejo, razišči refaktorizacijo, preveri hipotezo in ohrani samo rezultate, ki se zdijo obetavni.
To tudi razširi krog ljudi, ki lahko začnejo delo. Naš produktni vodja in oblikovalec lahko zdaj zahteve za funkcije vložita neposredno v Symphony. Ni jima treba prenesti repozitorija ali upravljati seje Codex. Opišeta funkcijo in v odgovor dobita paket za pregled, ki vključuje videoprehod delujoče funkcije v resničnem izdelku.
Symphony blesti tudi v velikih monorepozitorijih (kot je ta, ki ga imamo v OpenAI), kjer je zadnji korak do uspešne zahteve pull request počasen in krhek. Sistem spremlja CI, po potrebi ponastavi osnovo, rešuje konflikte, znova izvaja nezanesljive preglede in na splošno vodi spremembe skozi cevovod. ko zahtevek doseže stanje Merging (združevanje), smo zelo prepričani, da bo sprememba brez človeškega varstva prišla v glavno vejo.
Napredek prinaša nove, drugačne težave
Delovanje na tej ravni prinaša kompromise. Ko smo prešli z interaktivnega usmerjanja agentov na dodeljevanje dela na ravni zahtevkov, smo izgubili možnost, da bi jih med delom nenehno usmerjali in po potrebi popravljali smer. Včasih je agent ustvaril nekaj, kar je povsem zgrešilo bistvo. To je bilo koristno—takšni neuspehi so razkrili vrzeli v sistemu in nam pomagali, da smo ga naredili bolj robustnega.
Namesto da bi rezultat ročno popravljali, smo dodali varovalke in veščine, da bi agenti naslednjič uspeli. Sčasoma nas je to pripeljalo do tega, da smo v svoje ogrodje dodali nove zmožnosti, kot so izvajanje celovitih testov, upravljanje aplikacije prek Chrome DevTools in vodenje osnovnih testov za zagotavljanje kakovosti. Znatno smo izboljšali našo dokumentacijo in pojasnili, kako je videti dober rezultat.
Vsako opravilo ne ustreza slogu dela Symphony. Nekatere težave še vedno zahtevajo inženirje, ki delajo neposredno z interaktivnimi sejami Codex, zlasti dvoumne težave ali delo, ki zahteva močno presojo in strokovno znanje. V praksi so to običajno najbolj zanimive in prijetne naloge, za katere naši inženirji porabijo čas.
Razlika je v tem, da lahko Symphony prevzame glavnino rutinskega implementacijskega dela. To inženirjem omogoča, da se osredotočijo na en sam težak problem, namesto da bi nenehno preklapljali med manjšimi nalogami.
Naučili smo se tudi, da obravnavanje agentov kot togih vozlišč v avtomatu ne deluje dobro. Modeli postajajo pametnejši in lahko rešujejo večje probleme od škatle, v katero jih skušamo stlačiti. Na primer, v zgodnjih različicah so bile vse integracije z GitHub del zunanjega ogrodja—na primer, zgodnje različice so pričakovale, da bo Codex delal le spremembe kode, preostanek procesa (pošiljanje sprememb, izvajanje testov) pa je bil določen v kodi. Naše zgodnje različice agentnega dela so Codex prosile le, naj implementira nalogo. Ta pristop se je izkazal za preveč omejujočega. Codex je povsem sposoben ustvariti več zahtev pull request ter prebrati povratne informacije pri pregledu in jih upoštevati. Zato smo mu dali orodja—CLI gh, veščine za branje dnevnikov CI itd.—in zdaj lahko Codex prosimo, da naredi več, na primer zapre stare zahteve pull request ali pripravi poročila o dokončanem v primerjavi z opuščenim delom. Takšne naloge so bile daleč zunaj začetnega okvira implementacije funkcije.
Zato smo se sčasoma premaknili k temu, da agentom dajemo cilje namesto strogih prehodov, podobno kot dober vodja članu svoje ekipe dodeli cilj. Moč modelov izhaja iz njihove sposobnosti sklepanja, zato jim dajte orodja in kontekst ter jih pustite, da opravijo svoje.
Uporaba Symphonyja za gradnjo Symphonyja
Ko odprete repozitorij Symphony, boste najprej opazili, da je Symphony tehnično zgolj datoteka SPEC.md—opredelitev problema in predvidene rešitve. Namesto da bi zgradili kompleksen nadzorni sistem, smo opredelili problem in predvidene rešitve ter agentom dali usmerjanje na visoki ravni.
Referenčna implementacija je napisana v Elixirju—ko je koda dejansko brezplačna, lahko jezike končno izbirate glede na njihove prednosti, kot je sočasnost v Elixirju—vendar je mogoče osnovno idejo izraziti v preprostem dokumentu Markdown. Spodbujamo vas, da svojega najljubšega agenta za programiranje usmerite na specifikacijo in mu naročite, naj implementira svojo različico.
Prva različica Symphonyja je bila le seja Codex, ki je tekla v tmux, preverjala Linear in za nova opravila zaganjala podagente. Delovalo je, vendar ni bilo posebej zanesljivo. Druga različica je živela v našem glavnem projektnem repozitoriju, ki je bil zgrajen z mislijo na agente. Agentom smo že zgradili ogrodje, ki jim v tem repozitoriju daje sposobnosti in kontekst za kakovostno delo, zato Symphony vse to preprosto poveže.
Ko je osnovna funkcionalnost obstajala, smo Symphony uporabili za gradnjo Symphonyja.
Ko smo sistem interno predstavili pri upravljanju opravil in pripenjanju videa kot dokazila o opravljenem delu, je bil odziv izjemno pozitiven: naš projektni kanal za Symphony je zrasel, ekipe po celotni organizaciji pa so ga začele uporabljati organsko. Notranje ujemanje izdelka-trga je pri OpenAI predpogoj za zunanjo objavo. Na podlagi uporabe, ki smo jo videli v OpenAI, je postalo jasno, da moramo Symphony deliti tudi zunaj podjetja.
Zato smo idejo izluščili v samostojen SPEC.md in prosili Codex, naj jo implementira. Za referenčno implementacijo smo izbrali Elixir, razmeroma nišni jezik z odličnimi gradniki za orkestracijo in nadzor sočasnih procesov. Codex je implementacijo v Elixirju zgradil v enem poskusu, nato pa smo od tam naprej iterirali tako specifikacijo kot implementacijo. Da bi izpilili specifikacijo, smo Codex celo prosili, naj jo implementira še v več drugih jezikih—TypeScript, Go, Rust, Java, Python—ter rezultate uporabi za prepoznavanje dvoumnosti in poenostavitev sistema. Uspelo mu je v vseh jezikih.
Skozi proces gradnje Codexa smo odstranili veliko naključne kompleksnosti, na primer odvisnosti od določenih repozitorijev ali od Linear MCP. Symphony ni več odvisen od naših internih repozitorijev ali potekov dela. Osnovni pristop je postal preprost:
Za vsako odprto opravilo zagotovi, da agent teče v svojem delovnem prostoru.
Poleg tega, da pomaga pri aktivnem delu, je razvojni potek dela zdaj nekaj, kar agenti poznajo in čemur sledijo. Razvojni potek dela—delo na težavi, prevzem repozitorija, označitev kot v teku, da produktni vodja ve, da se na tem dela, dodajanje zahteve pull request, premik v stanje Pregled, pripenjanje videoposnetkov itd.—je zdaj zajeto v preprosti datoteki WORKFLOW.md. Vse to je proces, ki so mu ljudje sledili, vendar nikoli ni bil dokumentiran. Namesto da bi se zanašali na ta impliciten nabor korakov, ga zdaj dokumentiramo, Symphony pa zagotavlja, da mu agenti sledijo. To nam omogoča gradnjo agentov, ki delajo skupaj z nami. Če se odločimo, da morajo agenti končanemu delu priložiti tudi samorefleksijo, bomo to dodali v WORKFLOW.md, Symphony pa bo agente vodil do tega koraka.
Uporabili smo lahko tudi Codex v načinu strežnika za aplikacije(odpre se v novem oknu), vgrajenem načinu brez glave za Codex. Ta način nam je omogočil, da smo Codex poganjali in z njim komunicirali programsko prek dobro dokumentiranega API-ja JSON-RPC za zadeve, kot sta začetek niti ali odzivanje na poteze. To je veliko priročnejši način z bolj prilagodljivim obsegom kot poskušanje komunikacije s Codex prek CLI ali sej tmux v živo.
Codex App Server je bil popoln za naš primer uporabe: izkoriščamo ogrodje, ki ga ponuja Codex, hkrati pa imamo gumbe in priključke za priklop. Da na primer dostopnega žetona Linear ne bi izpostavili podagentom, uporabljamo dinamične klice orodij(odpre se v novem oknu) za izpostavitev surove funkcije linear_graphql, ki izvaja poljubne zahteve proti Linear, ne da bi se zanašali na MCP ali dostopni žeton izpostavili vsebnikom.
Kaj sledi
Symphony je namerno minimalistična plast orkestracije. Odpiramo jo v odprto kodo, da pokažemo moč Codex App Server, ko je povezan z različnimi orodji za potek dela, kot je Linear. Zato Symphonyja ne nameravamo vzdrževati kot samostojnega izdelka. Razumete ga lahko kot referenčno implementacijo. Podobno kot so mnogi razvijalci svoje agente za programiranje usmerili na objavo o inženiringu z ogrodjem, da bi si postavili ogrodje repozitorijev, upamo, da boste svojega najljubšega agenta za programiranje usmerili na Symphonyjevo specifikacijo(odpre se v novem oknu) in repozitorij(odpre se v novem oknu), da zgradite svoje različice, prilagojene vašim okoljem.
Moč prihaja iz Codexa in njegovega strežnika za aplikacije. Symphony je bil način, kako povezati Codex z Linear, dvema storitvama, ki smo ju že uporabljali, da bi rešili problem upravljanja dela. Ker agenti za programiranje postajajo vse boljši pri sklepanju in sledenju navodilom, sumimo, da se bo ozko grlo tudi v drugih podjetjih premaknilo od pisanja kode k upravljanju agentnega dela. Vznemirljiv del pa je, da je prag za eksperimentiranje s temi sistemi agentov za programiranje zdaj presenetljivo nizek. S Codexom lahko preprosto začnete graditi stvari.
Pohvale skupnosti
Navdušeni smo, da inženirska skupnost v tednih po izdaji uporablja Symphony, ki je do 23. aprila zbral že več kot 15 tisoč zvezdic na GitHubu(odpre se v novem oknu).