Uma especificação open source para orquestração do Codex: Symphony
Por Alex Kotliarskyi, Victor Zhu e Zach Brock
Há seis meses, enquanto trabalhava numa ferramenta interna de produtividade, a nossa equipa tomou uma decisão controversa (na altura): iríamos construir o nosso repositório sem código escrito por humanos. Cada linha no nosso repositório de projeto teria de ser gerada pelo Codex.
Para fazer isso funcionar, redesenhámos o nosso fluxo de engenharia de raiz. Construímos um repositório amigável para agentes, investimos fortemente em testes automatizados e guardrails, e tratámos o Codex como um colega de equipa a sério. Documentámos esse percurso no nosso artigo anterior do blogue sobre harness engineering.
E funcionou, mas depois deparámo-nos com o próximo estrangulamento: mudança de contexto.
Para resolver este novo problema, criámos um sistema chamado Symphony. O Symphony(abre numa nova janela) é um orquestrador de agentes que transforma um quadro de gestão de projetos como o Linear num plano de controlo para agentes de programação. Cada tarefa aberta recebe um agente, os agentes correm continuamente e os humanos revêm os resultados.
Este artigo explica como criámos o Symphony — resultando num aumento de 500% em pull requests integrados em algumas equipas — e como o usar para transformar o seu próprio issue tracker num orquestrador de agentes sempre ativo.
O teto dos agentes de programação interativos
Mesmo à medida que se tornam mais fáceis de usar, os agentes de programação — quer sejam acedidos através de aplicações web ou de CLI — continuam a ser ferramentas interativas.
À medida que a escala do trabalho com agentes aumentou na OpenAI, encontrámos um novo tipo de sobrecarga. Cada engenheiro abria algumas sessões do Codex, atribuía tarefas, revia o resultado, orientava o agente e repetia. Na prática, a maioria das pessoas conseguia gerir confortavelmente entre três e cinco sessões de cada vez antes de a mudança de contexto se tornar penosa. Para além disso, a produtividade caía. Esquecíamo-nos de qual sessão estava a fazer o quê, saltávamos entre terminais para voltar a encaminhar os agentes, e depurávamos tarefas de longa duração que bloqueavam a meio.
Os agentes eram rápidos, mas tínhamos um estrangulamento no sistema: a atenção humana. Na prática, tínhamos criado uma equipa de engenheiros júnior extremamente capazes e depois atribuído aos nossos engenheiros humanos a tarefa de os microgerirem. Isso não iria escalar.
Uma mudança de perspetiva
Percebemos que estávamos a otimizar a coisa errada. Estávamos a orientar o nosso sistema em torno de sessões de programação e PRs integrados, quando os PRs e as sessões são, na verdade, um meio para atingir um fim. Os fluxos de trabalho de software são largamente organizados em torno de entregáveis: issues, tarefas, tickets, milestones.
Por isso, perguntámo-nos o que aconteceria se deixássemos de supervisionar agentes diretamente e, em vez disso, os deixássemos puxar trabalho do nosso rastreador de tarefas.
Essa ideia tornou-se o Symphony, uma especificação escrita que funciona como supervisor para orquestrar trabalho com agentes.
Transformar o nosso issue tracker num orquestrador de agentes
O Symphony começou com um conceito simples: qualquer tarefa aberta deve ser recolhida e concluída por um agente. Em vez de gerir sessões do Codex em vários separadores, fizemos do nosso issue tracker o plano de controlo.
Nesta configuração, cada issue aberta no Linear corresponde a um espaço de trabalho dedicado de agente. O Symphony monitoriza continuamente o quadro de tarefas e garante que cada tarefa ativa tem um agente a correr no ciclo até estar concluída. Se um agente falhar ou bloquear, o Symphony reinicia-o. Se surgir novo trabalho, o Symphony apanha-o e começa a organizar o trabalho.
Construímos o nosso fluxo de trabalho com base nos estados dos tickets, usando o gestor de tarefas Linear como uma máquina de estados.
Na prática, o Symphony dissocia o trabalho das sessões e dos pull requests. Algumas issues produzem vários PRs em diferentes repositórios; outras são pura investigação ou análise e nunca tocam na base de código.
Assim que o trabalho é abstraído desta forma, os tickets podem representar unidades de trabalho muito maiores.
Usamos regularmente o Symphony para orquestrar funcionalidades complexas e migrações de infraestrutura. Por exemplo, podemos abrir uma tarefa a pedir ao agente que analise a base de código, o Slack ou o Notion e produza um plano de implementação. Quando ficamos satisfeitos com o plano, o agente gera uma árvore de tarefas, dividindo o trabalho em fases e definindo dependências entre tarefas.
Os agentes só começam a trabalhar em tarefas que não estão bloqueadas, por isso a execução desenrola-se naturalmente e de forma ótima em paralelo para este DAG (uma sequência de passos de execução). No exemplo abaixo, marcámos a atualização do React como bloqueada por uma migração para Vite. Como esperado, os agentes só começaram a atualizar o React depois de a migração para Vite estar concluída.
Os agentes também podem criar trabalho por iniciativa própria. Durante a implementação ou a revisão, muitas vezes reparam em melhorias que ficam fora do âmbito da tarefa atual: um problema de desempenho, uma oportunidade de refatoração ou uma arquitetura melhor. Quando isso acontece, limitam-se a abrir uma nova issue que podemos avaliar e agendar mais tarde — muitas destas tarefas de seguimento também são recolhidas por agentes. Embora supervisionemos este processo, os agentes mantêm-se organizados e fazem o trabalho avançar.
Esta forma de trabalhar reduz drasticamente o custo cognitivo de iniciar trabalho ambíguo. Se o agente fizer algo mal, isso continua a ser informação útil, e o custo para nós é próximo de zero. Podemos abrir tickets de forma muito barata para o agente prototipar e explorar, e deitar fora quaisquer explorações de que não gostemos.
Como o orquestrador corre em devboxes e nunca dorme, podemos adicionar tarefas a partir de qualquer lugar e saber que um agente as vai recolher. Por exemplo, um engenheiro da nossa equipa fez três alterações significativas a partir da aplicação Linear no telemóvel, numa cabana confortável com wifi instável.
Um aumento da exploração ao trabalhar desta forma
Ao observar os efeitos de trabalhar com o Symphony, a mudança mais óbvia foi o output. Entre algumas equipas na OpenAI, vimos o número de PRs integrados aumentar 6x nas primeiras três semanas. Fora da OpenAI, o fundador da Linear, Karri Saarinen, destacou um aumento no número de espaços de trabalho criados(abre numa nova janela) quando lançámos o Symphony. No entanto, a mudança mais profunda está em como as equipas pensam sobre o trabalho.
Quando os nossos engenheiros deixam de passar tempo a supervisionar sessões do Codex, a economia das alterações ao código muda completamente. O custo percebido de cada alteração diminui porque já não estamos a investir esforço humano em conduzir a própria implementação.
Isso mudou o nosso comportamento. Tornou-se trivial lançar tarefas especulativas no Symphony. Experimente uma ideia, explore uma refatoração, teste uma hipótese e fique apenas com os resultados que parecerem promissores.
Também alarga quem pode iniciar trabalho. O nosso product manager e o designer podem agora abrir pedidos de funcionalidades diretamente no Symphony. Não precisam de fazer checkout do repositório nem de gerir uma sessão do Codex. Descrevem a funcionalidade e recebem de volta um pacote de revisão que inclui um vídeo de demonstração da funcionalidade a funcionar no produto real.
O Symphony também se destaca em grandes monorepos (como o que temos na OpenAI), onde a última milha até integrar um PR é lenta e frágil. O sistema observa o CI, faz rebase quando necessário, resolve conflitos, repete verificações instáveis e, de um modo geral, guia as alterações ao longo do pipeline. Quando um ticket chega a Merging, temos elevada confiança de que a alteração entrará no branch principal sem babysitting humano.
O progresso traz problemas novos e diferentes
Operar a este nível implica compromissos. Quando passámos de orientar agentes de forma interativa para lhes atribuir trabalho ao nível do ticket, perdemos a capacidade de os ir ajustando constantemente a meio do percurso e de corrigir a rota quando necessário. Por vezes, o agente produzia algo que falhava completamente o alvo. Isso foi útil — essas falhas revelaram lacunas no sistema e ajudaram-nos a torná-lo mais robusto.
Em vez de corrigir manualmente o resultado, adicionámos guardrails e competências para que os agentes pudessem ter êxito da vez seguinte. Com o tempo, isto levou-nos a acrescentar novas capacidades ao nosso harness, como executar testes end-to-end, conduzir a aplicação através do Chrome DevTools e gerir testes de fumo de QA. Melhorámos significativamente a nossa documentação e clarificámos o que significa um bom resultado.
Nem todas as tarefas se enquadram no estilo de trabalho do Symphony. Alguns problemas continuam a exigir engenheiros a trabalhar diretamente com sessões interativas do Codex, especialmente problemas ambíguos ou trabalho que exige forte capacidade de julgamento e especialização. Na prática, estas são geralmente as tarefas mais interessantes e agradáveis para os nossos engenheiros passarem tempo a fazer.
A diferença é que o Symphony consegue tratar do grosso do trabalho rotineiro de implementação. Isso permite que os engenheiros se concentrem num único problema difícil de cada vez, em vez de estarem constantemente a alternar entre tarefas mais pequenas.
Também aprendemos que tratar os agentes como nodes rígidos numa máquina de estados não funciona bem. Os modelos ficam mais inteligentes e conseguem resolver problemas maiores do que a caixa em que tentamos encaixá-los. Por exemplo, as primeiras versões tinham todas as integrações com o GitHub como parte do harness exterior — por exemplo, as primeiras versões esperavam que o Codex fizesse apenas alterações ao código, especificando o resto do processo (submeter alterações, correr testes) em código. As nossas primeiras versões de trabalho com agentes limitavam-se a pedir ao Codex que implementasse a tarefa. Essa abordagem revelou-se demasiado limitadora. O Codex é perfeitamente capaz de criar vários PRs, bem como de ler feedback de revisão e tratá-lo. Por isso, demos-lhe ferramentas — CLI gh, competências para ler logs de CI, etc. — e agora podemos pedir ao Codex para fazer mais, como fechar PRs antigos ou gerar relatórios sobre trabalho concluído vs. abandonado. Este tipo de tarefas ficava muito fora da caixa inicial de implementação de funcionalidades.
Por isso, acabámos por evoluir no sentido de dar aos agentes objetivos em vez de transições rígidas, tal como um bom gestor atribuiria um objetivo a um subordinado direto da sua equipa. O poder dos modelos vem da sua capacidade de raciocínio, por isso dê-lhes ferramentas e contexto e deixe-os trabalhar.
Usar o Symphony para construir o Symphony
Quando abre o repositório do Symphony, a primeira coisa que nota é que o Symphony é tecnicamente apenas um ficheiro SPEC.md — uma definição do problema e da solução pretendida. Em vez de construir um sistema de supervisão complexo, definimos o problema e as soluções pretendidas, dando aos agentes orientação de alto nível.
A implementação de referência foi escrita em Elixir — porque, quando o código é efetivamente gratuito, pode finalmente escolher linguagens pelos seus pontos fortes, como a concorrência do Elixir — mas a ideia central pode ser expressa num simples documento Markdown. Encorajamo-lo a apontar o seu agente de programação favorito para a especificação e a fazer com que implemente a sua própria versão.
A primeira versão do Symphony era apenas uma sessão do Codex a correr em tmux, a sondar o Linear e a lançar subagentes para novas tarefas. Funcionava, mas não era particularmente fiável. A segunda versão passou a viver dentro do nosso repositório principal do projeto, que foi concebido a pensar em agentes. Já tínhamos criado o harness de agentes para lhes dar as competências e o contexto necessários para fazer trabalho de elevada qualidade neste repositório, por isso o Symphony simplesmente liga tudo.
Assim que a funcionalidade básica existiu, usámos o Symphony para construir o Symphony.
Quando fizemos uma demonstração interna do sistema a gerir tarefas e a anexar o seu vídeo de prova de trabalho, a reação foi extremamente positiva: o canal do nosso projeto Symphony cresceu e equipas de toda a organização começaram a usá-lo de forma orgânica. O product-market fit interno é um pré-requisito para lançar externamente na OpenAI. Com base na utilização que vimos na OpenAI, ficou claro que devíamos partilhar o Symphony para além das paredes da empresa.
Por isso, extraímos a ideia para um SPEC.md autónomo e pedimos ao Codex que o implementasse. Para a implementação de referência, escolhemos Elixir, uma linguagem relativamente de nicho com excelentes primitivas para orquestrar e supervisionar processos concorrentes. O Codex criou a implementação em Elixir de uma só vez, e a partir daí continuámos a iterar tanto na especificação como na implementação. Para aperfeiçoar a especificação, até pedimos ao Codex que a implementasse em várias outras linguagens — TypeScript, Go, Rust, Java, Python — e que usasse os resultados para identificar ambiguidades e simplificar o sistema. Teve êxito em todas as linguagens.
Ao longo do processo de construção do Codex, removemos muita complexidade acidental, como dependências de repositórios específicos ou do Linear MCP. O Symphony já não depende dos nossos repositórios internos nem dos nossos fluxos de trabalho. A abordagem central tornou-se simples:
Para cada tarefa aberta, garanta que um agente está a correr no seu próprio espaço de trabalho.
Além de ajudar no trabalho ativo, o fluxo de desenvolvimento é agora algo que os agentes conhecem e seguem. O fluxo de desenvolvimento — trabalhar numa issue, fazer checkout de um repositório, colocá-la em progresso para o PM saber que está a ser tratada, adicionar o PR, movê-la para o estado Review, anexar vídeos, etc. — está agora capturado num simples ficheiro WORKFLOW.md. Tudo isto era um processo seguido por humanos, mas nunca tinha sido documentado. Em vez de dependermos deste conjunto implícito de passos, documentamo-lo agora, e o Symphony garante que os agentes o seguem. Isto permite-nos construir agentes que trabalham ao nosso lado. Se decidirmos que os agentes também devem anexar autoavaliação ao trabalho concluído, acrescentaremos isso ao WORKFLOW.md, e o Symphony guiará os agentes até esse passo.
Também pudemos usar o Codex em modo app server(abre numa nova janela), um modo headless integrado do Codex. Este modo permitiu-nos correr o Codex e comunicar com ele programaticamente através de uma API JSON-RPC bem documentada para coisas como iniciar uma thread ou reagir a turnos. É uma forma muito mais conveniente e escalável do que tentar interagir com o Codex através de CLI ou de sessões tmux ao vivo.
O Codex App Server era perfeito para o nosso caso de uso: tiramos partido do harness que o Codex fornece, ao mesmo tempo que temos controlos e hooks para integrar. Por exemplo, para evitar expor o token de acesso do Linear aos subagentes, usamos dynamic tool calls(abre numa nova janela) para expor a função bruta linear_graphql, que executa pedidos arbitrários contra o Linear, sem depender de MCP nem expor o token de acesso aos contentores.
O que se segue
O Symphony é uma camada de orquestração intencionalmente minimalista. Estamos a disponibilizá-lo em open source para demonstrar o poder do Codex App Server quando emparelhado com diferentes ferramentas de fluxo de trabalho, como o Linear. Como tal, não planeamos manter o Symphony como um produto autónomo. Pense nele como uma implementação de referência. Tal como muitos programadores apontaram os seus agentes de programação ao artigo de harness engineering para estruturar os seus repositórios, esperamos que aponte o seu agente de programação favorito à especificação(abre numa nova janela) e ao repositório(abre numa nova janela) do Symphony para criar as suas próprias versões adaptadas aos seus ambientes.
O poder vem do Codex e do seu app server. O Symphony foi uma forma de ligar o Codex ao Linear, duas coisas que já usávamos, para resolver o problema de gestão do trabalho. À medida que os agentes de programação melhoram no raciocínio e no seguimento de instruções, suspeitamos que o estrangulamento noutras empresas também se deslocará da escrita de código para a gestão do trabalho com agentes. A parte entusiasmante é que a barreira à experimentação com estes sistemas de agentes de programação é agora surpreendentemente baixa. Pode simplesmente construir coisas com o Codex.
Destaques da comunidade
Estamos entusiasmados por ver a comunidade de engenharia a usar o Symphony nas semanas desde o lançamento, tendo já ultrapassado as 15 mil estrelas no GitHub(abre numa nova janela) a 23 de abril.