Une spécification open source pour l’orchestration de Codex : Symphony
Par Alex Kotliarskyi, Victor Zhu et Zach Brock
Il y a six mois, alors que nous travaillions sur un outil interne de productivité, notre équipe a pris une décision controversée (à l’époque) : construire notre dépôt sans aucun code écrit par des humains. Chaque ligne de notre dépôt de projet devait être générée par Codex.
Pour que cela fonctionne, nous avons repensé notre workflow d’ingénierie de fond en comble. Nous avons créé un dépôt adapté aux agents, investi massivement dans les tests automatisés et les garde-fous, et traité Codex comme un véritable coéquipier. Nous avons documenté ce parcours dans notre précédent billet de blog sur le ingénierie de l’infrastructure d’agents.
Et cela a fonctionné, mais nous nous sommes ensuite heurtés au goulot d’étranglement suivant : le changement de contexte.
Pour résoudre ce nouveau problème, nous avons construit un système appelé Symphony. Symphony(ouverture dans une nouvelle fenêtre) est un orchestrateur d’agents qui transforme un tableau de gestion de projet comme Linear en plan de contrôle pour les agents de codage. Chaque tâche ouverte reçoit un agent, les agents s’exécutent en continu, et les humains examinent les résultats.
Ce billet explique comment nous avons créé Symphony—ce qui a entraîné une hausse de 500 % des pull requests fusionnées dans certaines équipes—et comment l’utiliser pour transformer votre propre outil de suivi des tickets en orchestrateur d’agents toujours actif.
Le plafond des agents de codage interactifs
Même s’ils deviennent plus faciles à utiliser, les agents de codage—qu’on y accède via des applications web ou la CLI—restent des outils interactifs.
À mesure que l’ampleur du travail agentique augmentait chez OpenAI, nous avons découvert un nouveau type de charge. Chaque ingénieur ouvrait quelques sessions Codex, assignait des tâches, examinait les résultats, orientait l’agent, puis recommençait. En pratique, la plupart des personnes pouvaient gérer confortablement trois à cinq sessions à la fois avant que le changement de contexte ne devienne pénible. Au-delà, la productivité chutait. Nous finissions par oublier quelle session faisait quoi, passer d’un terminal à l’autre pour remettre les agents sur les rails, et déboguer des tâches de longue durée qui se bloquaient à mi-parcours.
Les agents étaient rapides, mais nous étions confrontés à un goulot d’étranglement : l’attention humaine. Nous avions en réalité constitué une équipe de jeunes ingénieurs extrêmement performants, puis demandé à nos ingénieurs humains de les superviser de manière très rapprochée. Ce modèle n’était pas viable à grande échelle.
Un changement de perspective
Nous avons compris que nous optimisions la mauvaise chose. Nous organisions notre système autour des sessions de codage et des PR fusionnées, alors que les PR et les sessions ne sont en réalité qu’un moyen d’atteindre une fin. Les workflows logiciels sont largement organisés autour de livrables : issues, tâches, tickets, jalons.
Nous nous sommes donc demandé ce qui se passerait si nous arrêtions de superviser directement les agents et les laissions plutôt récupérer du travail depuis notre outil de suivi des tâches.
Cette idée est devenue Symphony, une spécification écrite qui agit comme superviseur pour orchestrer le travail agentique.
Transformer notre outil de suivi des tickets en orchestrateur d’agents
Symphony a commencé avec un concept simple : toute tâche ouverte doit être prise en charge et menée à bien par un agent. Au lieu de gérer des sessions Codex dans plusieurs onglets, nous avons fait de notre outil de suivi des tickets le plan de contrôle.
Dans cette configuration, chaque ticket Linear ouvert correspond à un espace de travail d’agent dédié. Symphony surveille en continu le tableau des tâches et s’assure que chaque tâche active a un agent en cours d’exécution dans la boucle jusqu’à ce qu’elle soit terminée. Si un agent plante ou se bloque, Symphony le redémarre. Si un nouveau travail apparaît, Symphony le prend en charge et commence à organiser le travail.
Nous avons construit notre workflow autour des statuts de ticket, en utilisant l’outil de gestion des tâches Linear comme machine à états.
En pratique, Symphony découple le travail des sessions et des pull requests. Certaines issues produisent plusieurs PR sur différents dépôts ; d’autres relèvent uniquement de l’investigation ou de l’analyse et ne touchent jamais à la base de code.
Une fois le travail abstrait de cette façon, les tickets peuvent représenter des unités de travail bien plus grandes.
Nous utilisons régulièrement Symphony pour orchestrer des fonctionnalités complexes et des migrations d’infrastructure. Par exemple, nous pouvons créer une tâche demandant à l’agent d’analyser la base de code, Slack ou Notion et de produire un plan d’implémentation. Une fois satisfaits du plan, l’agent génère un arbre de tâches, en découpant le travail en étapes et en définissant les dépendances entre elles.
Les agents ne commencent à travailler que sur les tâches qui ne sont pas bloquées, si bien que l’exécution se déroule naturellement et de manière optimale en parallèle pour ce DAG (une séquence d’étapes d’exécution). Dans l’exemple ci-dessous, nous avons indiqué que la mise à niveau de React était bloquée par une migration vers Vite. Comme prévu, les agents n’ont commencé à mettre à niveau React qu’une fois la migration vers Vite terminée.
Les agents ne commencent à travailler que sur les tâches qui ne sont pas bloquées, si bien que l’exécution se déroule naturellement et de manière optimale en parallèle pour ce DAG (une séquence d’étapes d’exécution). Dans l’exemple ci-dessous, nous avons indiqué que la mise à niveau de React était bloquée par une migration vers Vite. Comme prévu, les agents n’ont commencé à mettre à niveau React qu’une fois la migration vers Vite terminée.
Cette façon de travailler réduit fortement le coût cognitif du lancement d’un travail ambigu. Si l’agent se trompe, cela reste une information utile, et le coût pour nous est proche de zéro. Nous pouvons créer des tickets à très faible coût pour que l’agent aille prototyper et explorer, puis jeter toutes les explorations qui ne nous plaisent pas.
Comme l’orchestrateur s’exécute sur des devboxes et ne se met jamais en veille, nous pouvons ajouter des tâches de n’importe où en sachant qu’un agent les prendra en charge. Par exemple, un ingénieur de notre équipe a effectué trois changements importants depuis l’application Linear sur son téléphone, depuis un chalet confortable avec un wifi médiocre.
Une hausse de l’exploration grâce à cette façon de travailler
En observant les effets du travail avec Symphony, le changement le plus évident a été le volume produit. Dans certaines équipes chez OpenAI, nous avons vu le nombre de PR fusionnées multiplié par 6 au cours des trois premières semaines. En dehors d’OpenAI, le fondateur de Linear, Karri Saarinen, a mis en avant un pic du nombre d’espaces de travail créés(ouverture dans une nouvelle fenêtre) lors de la sortie de Symphony. Cependant, le changement plus profond concerne la manière dont les équipes pensent le travail.
Quand nos ingénieurs ne passent plus leur temps à superviser des sessions Codex, l’économie des changements de code change complètement. Le coût perçu de chaque changement baisse, car nous n’investissons plus d’effort humain pour piloter l’implémentation elle-même.
Cela a changé notre comportement. Il est devenu trivial de lancer des tâches spéculatives dans Symphony. Essayer une idée, explorer une refactorisation, tester une hypothèse, puis ne conserver que les résultats prometteurs.
Cela élargit aussi le nombre de personnes pouvant initier du travail. Notre chef de produit et notre designer peuvent désormais déposer directement des demandes de fonctionnalité dans Symphony. Ils n’ont pas besoin de cloner le dépôt ni de gérer une session Codex. Ils décrivent la fonctionnalité et reçoivent en retour un dossier de revue comprenant une vidéo de démonstration de la fonctionnalité en fonctionnement dans le produit réel.
Symphony excelle aussi dans les grands monodépôts (comme celui que nous avons chez OpenAI), où la dernière étape avant de fusionner une PR est lente et fragile. Le système surveille la CI, rebase lorsque nécessaire, résout les conflits, relance les vérifications instables et, de façon générale, accompagne les changements tout au long du pipeline. Au moment où un ticket atteint le statut Merging, nous avons un haut niveau de confiance dans le fait que le changement arrivera dans la branche principale sans babysitting humain.
Le progrès s’accompagne de problèmes nouveaux et différents
Fonctionner à ce niveau implique des compromis. Quand nous sommes passés du pilotage interactif des agents à l’assignation de travail au niveau du ticket, nous avons perdu la capacité de les réorienter constamment en cours de route et de corriger le cap si nécessaire. Il arrivait que l’agent produise quelque chose qui passe complètement à côté. C’était utile—ces échecs révélaient des lacunes du système et nous aidaient à le rendre plus robuste.
Au lieu de corriger le résultat manuellement, nous avons ajouté des garde-fous et des compétences afin que les agents puissent réussir la fois suivante. Avec le temps, cela nous a conduits à ajouter de nouvelles capacités à notre harnais, comme l’exécution de tests de bout en bout, le pilotage de l’application via Chrome DevTools et la gestion de tests de fumée QA. Nous avons considérablement amélioré notre documentation et clarifié à quoi ressemble un bon résultat.
Toutes les tâches ne se prêtent pas au style de travail de Symphony. Certains problèmes nécessitent encore des ingénieurs travaillant directement avec des sessions Codex interactives, en particulier les problèmes ambigus ou le travail demandant un jugement et une expertise solides. En pratique, ce sont généralement les tâches les plus intéressantes et les plus agréables pour nos ingénieurs.
La différence, c’est que Symphony peut prendre en charge l’essentiel du travail d’implémentation de routine. Cela permet aux ingénieurs de se concentrer sur un seul problème difficile à la fois au lieu de changer constamment de contexte entre de petites tâches.
Nous avons aussi appris que traiter les agents comme des nœuds rigides dans une machine à états fonctionne mal. Les modèles deviennent plus intelligents et peuvent résoudre des problèmes plus vastes que la boîte dans laquelle nous essayons de les faire entrer. Par exemple, dans les premières versions, toutes les intégrations GitHub faisaient partie du harnais externe—par exemple, les premières versions attendaient de Codex qu’il ne fasse que des changements de code, en spécifiant le reste du processus (soumettre les changements, exécuter les tests) dans le code. Dans nos premières versions du travail agentique, nous demandions seulement à Codex d’implémenter la tâche. Cette approche s’est révélée trop limitante. Codex est parfaitement capable de créer plusieurs PR, ainsi que de lire les retours de revue et d’y répondre. Nous lui avons donc donné des outils—la CLI gh, des compétences pour lire les journaux CI, etc.—et désormais nous pouvons demander à Codex d’en faire plus, comme fermer d’anciennes PR ou extraire des rapports sur le travail terminé par rapport au travail abandonné. Ces types de tâches sortaient largement du cadre initial de l’implémentation de fonctionnalités.
Nous avons donc fini par évoluer vers une approche où nous donnons aux agents des objectifs plutôt que des transitions strictes, un peu comme un bon manager assignerait un but à un collaborateur de son équipe. La puissance des modèles vient de leur capacité de raisonnement, alors donnez-leur des outils et du contexte, et laissez-les faire.
Utiliser Symphony pour construire Symphony
Quand vous ouvrez le dépôt Symphony, la première chose que vous remarquerez, c’est que Symphony n’est techniquement qu’un fichier SPEC.md—une définition du problème et de la solution visée. Plutôt que de construire un système de supervision complexe, nous avons défini le problème et les solutions visées, en donnant aux agents une orientation de haut niveau.
L’implémentation de référence est écrite en Elixir—car lorsque le code est quasiment gratuit, vous pouvez enfin choisir les langages pour leurs points forts, comme la concurrence d’Elixir—mais l’idée centrale peut s’exprimer dans un simple document Markdown. Nous vous encourageons à faire pointer votre agent de développement préféré vers la spécification et à lui faire implémenter sa propre version.
La première version de Symphony n’était qu’une session Codex exécutée dans tmux, qui interrogeait Linear et lançait des sous-agents pour les nouvelles tâches. Cela fonctionnait, mais ce n’était pas particulièrement fiable. La deuxième version vivait dans notre dépôt de projet principal, conçu en pensant aux agents. Nous avions déjà construit le infrastructure d’agent pour donner aux agents les compétences et le contexte nécessaires afin de produire un travail de haute qualité dans ce dépôt, donc Symphony ne fait que tout relier.
Une fois les fonctionnalités de base en place, nous avons utilisé Symphony pour construire Symphony.
Lorsque nous avons fait une démo en interne du système gérant les tâches et joignant sa vidéo de preuve de travail, la réaction a été extrêmement positive : notre canal de projet Symphony s’est développé, et des équipes de toute l’organisation ont commencé à l’utiliser spontanément. L’adéquation produit-marché en interne est un prérequis avant un lancement externe chez OpenAI. Au vu de l’usage observé chez OpenAI, il est devenu clair que nous devions partager Symphony au-delà des murs de l’entreprise.
Nous avons donc extrait l’idée dans un SPEC.md autonome et demandé à Codex de l’implémenter. Pour l’implémentation de référence, nous avons choisi Elixir, un langage de niche avec d’excellents primitives essentielles pour orchestrer et superviser des processus concurrents. Codex a construit l’implémentation Elixir d’un seul coup, puis nous avons continué à itérer à partir de là sur la spécification comme sur l’implémentation. Pour peaufiner la spécification, nous avons même demandé à Codex de l’implémenter dans plusieurs autres langages—TypeScript, Go, Rust, Java, Python—et d’utiliser les résultats pour repérer les ambiguïtés et simplifier le système. Il a réussi dans chaque langage.
Au cours du processus de construction de Codex, nous avons éliminé beaucoup de complexité accidentelle, comme les dépendances à des dépôts spécifiques ou à Linear MCP. Symphony ne dépend plus de nos dépôts internes ni de nos workflows. L’approche centrale est devenue simple :
Pour chaque tâche ouverte, garantir qu’un agent s’exécute dans son propre espace de travail.
En plus d’aider sur le travail en cours, le workflow de développement est désormais quelque chose que les agents connaissent et suivent. Le workflow de développement—travailler sur une issue, cloner un dépôt, la passer en cours pour que le PM sache qu’elle est en train d’être traitée, ajouter la PR, la faire passer au statut Review, joindre des vidéos, etc.—est maintenant consigné dans un simple fichier WORKFLOW.md. Tout cela correspond à un processus que les humains suivaient, mais qui n’avait jamais été documenté. Au lieu de nous appuyer sur cet ensemble implicite d’étapes, nous le documentons désormais, et Symphony veille à ce que les agents le suivent. Cela nous permet de construire des agents qui travaillent à nos côtés. Si nous décidons que les agents doivent aussi joindre une auto-réflexion au travail terminé, nous l’ajouterons au WORKFLOW.md, et Symphony guidera les agents vers cette étape.
Nous avons également pu utiliser Codex en mode serveur d’application(ouverture dans une nouvelle fenêtre), un mode sans interface intégré. Ce mode nous a permis d’exécuter Codex et d’interagir avec lui de manière programmatique via une API JSON-RPC bien documentée, pour des opérations comme le démarrage d’un fil de discussion ou la gestion des tours de conversation. Il s’agit d’une approche bien plus pratique et évolutive que d’interagir avec Codex via une interface en ligne de commande ou des sessions tmux en direct.
Codex App Server convenait parfaitement à notre cas d’usage : nous profitons du harnais fourni par Codex tout en disposant de réglages et de hooks pour nous y brancher. Par exemple, pour éviter d’exposer le token d’accès Linear aux sous-agents, nous utilisons des appels d’outils dynamiques(ouverture dans une nouvelle fenêtre) afin d’exposer la fonction brute linear_graphql qui exécute des requêtes arbitraires sur Linear, sans dépendre de MCP ni exposer le token d’accès aux conteneurs.
La suite
Symphony est une couche d’orchestration volontairement minimale. Nous le publions en open source pour démontrer la puissance de Codex App Server lorsqu’il est associé à différents outils de workflow, comme Linear. À ce titre, nous ne prévoyons pas de maintenir Symphony comme produit autonome. Voyez-le comme une implémentation de référence. De la même façon que de nombreux développeurs ont orienté leurs agents de codage vers le billet sur le ingénierie de l’infrastructure d’agents pour poser les bases de leurs dépôts, nous espérons que vous dirigerez votre agent de codage préféré vers la spécification(ouverture dans une nouvelle fenêtre) et le dépôt(ouverture dans une nouvelle fenêtre) Symphony afin de construire vos propres versions adaptées à vos environnements.
La puissance vient de Codex et de son app server. Symphony était un moyen de relier Codex à Linear, deux choses que nous utilisions déjà, pour résoudre le problème de la gestion du travail. À mesure que les agents de codage deviendront meilleurs en raisonnement et en suivi d’instructions, nous pensons que le goulot d’étranglement dans d’autres entreprises se déplacera lui aussi de l’écriture de code vers la gestion du travail agentique. Le plus enthousiasmant, c’est que la barrière à l’expérimentation avec ces systèmes d’agents de codage est désormais étonnamment basse. Vous pouvez simplement construire des choses avec Codex.
Saluts à la communauté
Nous sommes ravis de voir la communauté d’ingénierie utiliser Symphony dans les semaines qui ont suivi sa sortie, avec plus de 15 K étoiles sur GitHub(ouverture dans une nouvelle fenêtre) au 23 avril.