O specificație open-source pentru orchestrarea Codex: Symphony
De Alex Kotliarskyi, Victor Zhu și Zach Brock
Acum șase luni, în timp ce lucram la un instrument intern de productivitate, echipa noastră a luat o decizie controversată (la acel moment): ne-am construi depozitul fără cod scris de oameni. Fiecare linie din depozitul proiectului nostru trebuia să fie generată de Codex.
Pentru ca asta să funcționeze, ne-am reproiectat fluxul de lucru de inginerie de la zero. Am construit un depozit prietenos cu agenții, am investit masiv în teste automate și bariere de protecție și am tratat Codex ca pe un coleg cu drepturi depline. Am documentat această călătorie în articolul nostru anterior de blog despre ingineria de orchestrare.
Și a funcționat, dar apoi ne-am lovit de următorul blocaj: schimbarea contextului.
Pentru a rezolva această nouă problemă, am construit un sistem numit Symphony. Symphony(se deschide într-o fereastră nouă) este un orchestrator de agenți care transformă un panou de management de proiect precum Linear într-un plan de control pentru agenți de programare. Fiecare sarcină deschisă primește un agent, agenții rulează continuu, iar oamenii revizuiesc rezultatele.
Această postare explică modul în care am creat Symphony—ceea ce a dus la o creștere de 500% a cererilor de extragere integrate în unele echipe—și cum să îl folosești pentru a transforma propriul tău instrument de urmărire a problemelor într-un orchestrator de agenți permanent activ.
Limita agenților interactivi de programare
Chiar dacă devin mai ușor de folosit, agenții de programare—fie că sunt accesați prin aplicații web sau prin CLI—sunt în continuare instrumente interactive.
Pe măsură ce amploarea muncii agentive a crescut la OpenAI, am descoperit un nou tip de povară. Fiecare inginer deschidea câteva sesiuni Codex, atribuia sarcini, revizuia rezultatele, ghida agentul și repeta. În practică, cei mai mulți oameni puteau gestiona confortabil trei până la cinci sesiuni odată înainte ca schimbarea contextului să devină dureroasă. Dincolo de asta, productivitatea scădea. Uitam ce făcea fiecare sesiune, săream între terminale ca să readucem agenții pe drumul cel bun și depanam sarcini de lungă durată care se blocau la jumătate.
Agenții erau rapizi, dar aveam un blocaj de sistem: atenția umană. Construiserăm practic o echipă de ingineri juniori extrem de capabili, apoi le atribuiserăm inginerilor noștri umani sarcina de a-i micromanageria. Asta nu avea cum să scaleze.
O schimbare de perspectivă
Ne-am dat seama că optimizam lucrul greșit. Ne orientam sistemul în jurul sesiunilor de programare și al PR-urilor îmbinate, când, în realitate, PR-urile și sesiunile sunt doar un mijloc pentru un scop. Fluxurile de lucru software sunt organizate în mare parte în jurul livrabilelor: probleme, sarcini, tichete, etape.
Așa că ne-am întrebat ce s-ar întâmpla dacă am înceta să supraveghem direct agenții și, în schimb, i-am lăsa să extragă munca din instrumentul nostru de urmărire a sarcinilor.
Această idee a devenit Symphony, o specificație scrisă care funcționează ca un supervizor pentru a orchestra munca agentivă.
Transformarea instrumentului nostru de urmărire a problemelor într-un orchestrator de agenți
Symphony a început cu un concept simplu: orice sarcină deschisă ar trebui să fie preluată și finalizată de un agent. În loc să gestionăm sesiuni Codex în mai multe file, am transformat instrumentul nostru de urmărire a problemelor în planul de control.
În această configurație, fiecare problemă Linear deschisă corespunde unui spațiu de lucru dedicat pentru un agent. Symphony monitorizează continuu panoul de sarcini și se asigură că fiecare sarcină activă are un agent care rulează în buclă până când este finalizată. Dacă un agent se blochează sau se oprește, Symphony îl repornește. Dacă apare muncă nouă, Symphony o preia și începe să organizeze munca.
Ne-am construit fluxul de lucru pe baza stărilor tichetelor, folosind managerul de sarcini Linear ca mașină de stări.
În practică, Symphony decuplează munca de sesiuni și de cererile de extragere. Unele probleme produc mai multe PR-uri în mai multe depozite; altele sunt simple investigații sau analize care nu ating niciodată baza de cod.
Odată ce munca este abstractizată în acest fel, tichetele pot reprezenta unități de muncă mult mai mari.
Folosim în mod regulat Symphony pentru a orchestra funcționalități complexe și migrări de infrastructură. De exemplu, putem crea o sarcină prin care îi cerem agentului să analizeze baza de cod, Slack sau Notion și să producă un plan de implementare. Odată ce suntem mulțumiți de plan, agentul generează un arbore de sarcini, împărțind munca în etape și definind dependențele dintre sarcini.
Agenții încep să lucreze doar la sarcini care nu sunt blocate, astfel încât execuția se desfășoară natural și optim în paralel pentru acest DAG (o secvență de pași de execuție). În exemplul de mai jos, am marcat upgrade-ul React ca fiind blocat de o migrare la Vite. După cum era de așteptat, agenții au început upgrade-ul React doar după ce migrarea la Vite a fost finalizată.
Agenții pot crea și ei muncă. În timpul implementării sau revizuirii, observă adesea îmbunătățiri care nu intră în sfera sarcinii curente: o problemă de performanță, o oportunitate de refactorizare sau o arhitectură mai bună. Când se întâmplă asta, pur și simplu creează o nouă problemă pe care o putem evalua și programa mai târziu—iar multe dintre aceste sarcini ulterioare sunt de asemenea preluate de agenți. În timp ce noi supraveghem acest proces, agenții rămân organizați și mențin munca în mișcare.
Acest mod de lucru reduce dramatic costul cognitiv al pornirii unei munci ambigue. Dacă agentul greșește ceva, asta rămâne totuși o informație utilă, iar costul pentru noi este aproape zero. Putem crea foarte ieftin tichete pentru ca agentul să facă prototipuri și să exploreze și să aruncăm orice explorări care nu ne plac.
Pentru că orchestratorul rulează pe devbox-uri și nu doarme niciodată, putem adăuga sarcini de oriunde și să știm că un agent le va prelua. De exemplu, un inginer din echipa noastră a făcut trei schimbări semnificative din aplicația Linear de pe telefon, dintr-o cabană confortabilă cu wifi slab.
O creștere a explorării datorită acestui mod de lucru
Când am observat efectele lucrului cu Symphony, cea mai evidentă schimbare a fost producția. În unele echipe de la OpenAI, am văzut numărul de PR-uri integrate crescând de 6 ori în primele trei săptămâni. În afara OpenAI, fondatorul Linear, Karri Saarinen, a evidențiat o creștere bruscă a spațiilor de lucru create(se deschide într-o fereastră nouă) în timp ce lansam Symphony. Totuși, schimbarea mai profundă ține de felul în care echipele gândesc despre muncă.
Când inginerii noștri nu mai petrec timp supervizând sesiuni Codex, economia modificărilor de cod se schimbă complet. Costul perceput al fiecărei schimbări scade, pentru că nu mai investim efort uman în conducerea implementării în sine.
Asta ne-a schimbat comportamentul. A devenit banal să lansăm sarcini speculative în Symphony. Încearcă o idee, explorează o refactorizare, testează o ipoteză și păstrează doar rezultatele care par promițătoare.
De asemenea, extinde numărul de persoane care pot iniția munca. Managerul nostru de produs și designerul nostru pot acum să introducă direct cereri de funcționalități în Symphony. Nu trebuie să verifice depozitul sau să gestioneze o sesiune Codex. Ei descriu funcționalitatea și primesc înapoi un pachet de revizuire care include un tur video al funcționalității care rulează în produsul real.
Symphony excelează și în monodepozite mari (precum cel pe care îl avem la OpenAI), unde ultima sută de metri a integrării unui PR este lentă și fragilă. Sistemul urmărește CI, face realinierea istoricului commit-urilor când este nevoie, rezolvă conflicte, reîncearcă verificările instabile și, în general, păstorește schimbările prin fluxul de execuție. Până când un tichet ajunge la Merging, avem un grad ridicat de încredere că schimbarea va ajunge în ramura principală fără supraveghere umană constantă.
Progresul vine cu probleme noi, diferite
Operarea la acest nivel vine cu compromisuri. Când am trecut de la ghidarea interactivă a agenților la atribuirea de muncă la nivel de tichet, am pierdut capacitatea de a-i orienta constant în timpul execuției și de a corecta cursul atunci când era nevoie. Uneori, agentul producea ceva care rata complet ținta. Asta a fost util—acele eșecuri au scos la iveală lacune din sistem și ne-au ajutat să-l facem mai robust.
În loc să corectăm manual rezultatul, am adăugat bariere de protecție și competențe, astfel încât agenții să poată reuși data viitoare. În timp, acest lucru ne-a determinat să adăugăm noi capabilități cadrului nostru, precum rularea testelor end-to-end, controlarea aplicației prin Chrome DevTools și gestionarea testelor de tip smoke pentru QA. Ne-am îmbunătățit semnificativ documentația și am clarificat cum arată un rezultat bun.
Nu orice sarcină se potrivește stilului de lucru Symphony. Unele probleme încă necesită ingineri care lucrează direct cu sesiuni Codex interactive, mai ales probleme ambigue sau muncă ce necesită judecată și expertiză puternice. În practică, acestea sunt de obicei cele mai interesante și mai plăcute sarcini pentru inginerii noștri.
Diferența este că Symphony poate gestiona grosul muncii de implementare de rutină. Asta le permite inginerilor să se concentreze pe o singură problemă dificilă odată, în loc să schimbe constant contextul între sarcini mai mici.
Am mai învățat și că tratarea agenților ca noduri rigide într-o mașină de stări nu funcționează bine. Modelele devin mai inteligente și pot rezolva probleme mai mari decât cutia în care încercăm să le încadrăm. De exemplu, versiunile timpurii aveau toate integrările GitHub ca parte a cadrului exterior — de pildă, versiunile timpurii se așteptau ca Codex să facă doar modificări de cod, specificând restul procesului (trimiterea modificărilor, rularea testelor) în cod. Versiunile noastre timpurii de muncă agentivă îi cereau lui Codex doar să implementeze sarcina. Această abordare s-a dovedit prea limitativă. Codex este perfect capabil să creeze mai multe PR-uri, precum și să citească feedbackul de revizuire și să îl abordeze. Așa că i-am oferit instrumente — CLI-ul gh, competențe de a citi jurnalele CI etc. — iar acum îi putem cere lui Codex să facă mai mult, cum ar fi să închidă PR-uri vechi sau să extragă rapoarte despre munca finalizată vs. abandonată. Aceste tipuri de sarcini depășeau cu mult cadrul inițial al implementării unei funcționalități.
Așa că, în cele din urmă, am trecut la a oferi agenților obiective în loc de tranziții stricte, la fel cum un manager bun ar atribui un scop unei persoane care îi raportează direct în echipă. Puterea modelelor vine din capacitatea lor de raţionament, așa că oferă-le instrumente și context și lasă-le să gătească.
Folosirea Symphony pentru a construi Symphony
Când deschizi depozitul Symphony, primul lucru pe care îl observi este că Symphony este, tehnic, doar un fișier SPEC.md—o definiție a problemei și a soluției intenționate. În loc să construim un sistem complex de supervizare, am definit problema și soluțiile intenționate, oferind agenților ghidare la nivel înalt.
Implementarea de referință este scrisă în Elixir—pentru că, atunci când codul este practic gratuit, poți în sfârșit să alegi limbajele după punctele lor forte, cum ar fi concurența din Elixir—dar ideea de bază poate fi exprimată într-un simplu document Markdown. Te încurajăm să îndrepți agentul tău preferat de programare către specificație și să îl lași să implementeze propria sa versiune.
Prima versiune a Symphony era doar o sesiune Codex care rula în tmux, interogând Linear și lansând subagenți pentru sarcini noi. A funcționat, dar nu era deosebit de fiabilă. A doua versiune a trăit în depozitul nostru principal de proiect, care a fost construit având în vedere agenții. Construiserăm deja harness-ul pentru agenți ca să le ofere agenților abilitățile și contextul necesare pentru a face muncă de înaltă calitate în acest depozit, așa că Symphony pur și simplu le conectează pe toate.
Odată ce funcționalitatea de bază a existat, am folosit Symphony pentru a construi Symphony.
Când am demonstrat intern sistemul care gestionează sarcini și atașează videoclipul său de dovadă a execuției, reacția a fost covârșitor de pozitivă: canalul nostru de proiect Symphony a crescut, iar echipe din întreaga organizație au început să-l folosească organic. Dovada internă că produsul se potrivește cu cererea din piață este o condiție prealabilă pentru lansarea externă la OpenAI. Pe baza utilizării observate la OpenAI, a devenit clar că ar trebui să distribuim Symphony dincolo de limitele companiei.
Așa că am extras ideea într-un SPEC.md independent și i-am cerut lui Codex să îl implementeze. Pentru implementarea de referință, am ales Elixir, un limbaj relativ de nișă, cu primitive excelente pentru orchestrarea și supervizarea proceselor concurente. Codex a construit implementarea în Elixir dintr-o singură încercare, iar de acolo am continuat să iterăm atât asupra specificației, cât și asupra implementării. Pentru a rafina specificația, i-am cerut chiar lui Codex să o implementeze și în alte câteva limbaje—TypeScript, Go, Rust, Java, Python—și să folosească rezultatele pentru a identifica ambiguitățile și a simplifica sistemul. A reușit în fiecare limbaj.
Prin procesul de construire a Codex, am eliminat multă complexitate incidentală, cum ar fi dependențele de depozite specifice sau de Linear MCP. Symphony nu mai depinde de depozitele sau fluxurile noastre de lucru interne. Abordarea de bază a devenit simplă:
Pentru fiecare sarcină deschisă, garantează că un agent rulează în propriul său spațiu de lucru.
Pe lângă faptul că ajută la munca activă, fluxul de dezvoltare este acum ceva ce agenții cunosc și urmează. Fluxul de dezvoltare — lucrul la o problemă, verificarea unui depozit, marcarea lui ca în progres astfel încât PM-ul să știe că se lucrează la el, adăugarea PR-ului, mutarea lui în starea Revizuire, atașarea videoclipurilor etc. — este acum capturat într-un simplu fișier WORKFLOW.md. Toate acestea erau un proces urmat de oameni, dar care nu fusese niciodată documentat. În loc să ne bazăm pe acest set implicit de pași, acum îl documentăm, iar Symphony se asigură că agenții îl urmează. Acest lucru ne permite să construim agenți care lucrează alături de noi. Dacă decidem că agenții ar trebui să atașeze și auto-reflecții la munca finalizată, vom adăuga asta în WORKFLOW.md, iar Symphony îi va ghida pe agenți către acel pas.
Am ajuns de asemenea să folosim Codex în modul app server(se deschide într-o fereastră nouă), un mod headless încorporat pentru Codex. Acest mod ne-a permis să rulăm Codex și să comunicăm cu el programatic printr-un API JSON-RPC bine documentat pentru lucruri precum pornirea unui fir sau reacția la ture. Este o metodă mult mai convenabilă și mai scalabilă decât încercarea de a interacționa cu Codex prin CLI sau prin sesiuni tmux live.
Codex App Server a fost o potrivire perfectă pentru cazul nostru de utilizare: profităm de cadrul oferit de Codex, având în același timp controale și puncte de extensie în care ne putem integra. De exemplu, pentru a evita expunerea tokenului de acces Linear către subagenți, folosim apeluri dinamice de instrumente(se deschide într-o fereastră nouă) pentru a expune funcția brută linear_graphql care execută cereri arbitrare către Linear, fără a ne baza pe MCP sau a expune tokenul de acces containerelor.
Ce urmează
Symphony este un strat de orchestrare intenționat minimal. Îl facem open source pentru a demonstra puterea Codex App Server atunci când este asociat cu diferite instrumente de flux de lucru, precum Linear. Ca atare, nu intenționăm să menținem Symphony ca produs de sine stătător. Gândește-te la el ca la o implementare de referință. Similar cu modul în care mulți dezvoltatori și-au îndreptat agenții de programare către articolul despre ingineria de orchestrare pentru a-și genera structura depozitelor, sperăm să îndrepți agentul tău preferat de programare către specificația(se deschide într-o fereastră nouă) și depozitul(se deschide într-o fereastră nouă) Symphony pentru a-ți construi propriile versiuni adaptate mediilor tale.
Puterea vine de la Codex și de la app server-ul său. Symphony a fost o modalitate de a conecta Codex la Linear, două lucruri pe care le foloseam deja, pentru a rezolva problema gestionării muncii. Pe măsură ce agenții de programare devin mai buni la raţionament și la urmarea instrucțiunilor, bănuim că blocajul din alte companii se va muta și el de la scrierea codului către gestionarea muncii agentive. Partea interesantă este că bariera de intrare pentru a experimenta cu aceste sisteme de agenți de programare este acum surprinzător de redusă. Poți pur și simplu să construiești lucruri cu Codex.
Mențiuni din partea comunității
Suntem încântați să vedem comunitatea de inginerie folosind Symphony în săptămânile de după lansare, strângând peste 15K stele pe GitHub(se deschide într-o fereastră nouă) la data de 23 aprilie.