Спецификация с открытым исходным кодом для оркестрации Codex: Symphony
Алекс Котлярский, Виктор Чжу и Зак Брок
Шесть месяцев назад, работая над внутренним инструментом продуктивности, наша команда приняла спорное (на тот момент) решение: мы будем собирать наш репозиторий без единой строки кода, написанной человеком. Каждая строка в нашем репозитории проекта должна была быть сгенерирована Codex.
Чтобы это заработало, мы с нуля переработали наш инженерный рабочий процесс. Мы создали репозиторий, удобный для агентов, серьезно вложились в автоматические тесты и защитные механизмы и стали относиться к Codex как к полноценному товарищу по команде. Мы описали этот путь в нашей предыдущей статье в блоге о harness engineering.
И это сработало, но затем мы уперлись в следующее узкое место: переключение контекста.
Чтобы решить эту новую проблему, мы создали систему под названием Symphony. Symphony(открывается в новом окне) — это оркестратор агентов, который превращает доску управления проектом, такую как Linear, в плоскость управления для агентов программирования. Каждая открытая задача получает агента, агенты работают непрерывно, а люди проверяют результаты.
В этой статье мы объясняем, как создали Symphony — что на некоторых командах привело к росту числа влитых пулл-реквестов на 500%, — и как использовать ее, чтобы превратить собственный трекер задач в постоянно работающий оркестратор агентов.
Предел возможностей интерактивных агентов для программирования
Даже несмотря на то, что ими становится проще пользоваться, агенты для программирования — будь то в веб-приложениях или через CLI — по-прежнему остаются интерактивными инструментами.
По мере роста масштаба агентной работы в OpenAI мы столкнулись с новым видом нагрузки. Каждый инженер открывал несколько сессий Codex, назначал задачи, просматривал результат, направлял агента и повторял все заново. На практике большинство людей могли с комфортом управлять тремя-пятью сессиями одновременно, прежде чем переключение контекста становилось болезненным. После этого продуктивность падала. Мы забывали, какая сессия чем занимается, перескакивали между терминалами, чтобы вернуть агентов на курс, и отлаживали долгие задачи, которые зависали на полпути.
Агенты были быстрыми, но у нас было системное узкое место: человеческое внимание. По сути, мы собрали команду чрезвычайно способных младших инженеров, а затем поручили нашим инженерам-людям микроменеджмент этой команды. Это не могло масштабироваться.
Смена перспективы
Мы поняли, что оптимизируем не то. Мы строили систему вокруг сессий программирования и смерженных PR, хотя PR и сессии на самом деле лишь средство для достижения цели. Разработка ПО в значительной степени организована вокруг результатов: issues, задач, тикетов, этапов.
И тогда мы спросили себя, что будет, если перестать напрямую контролировать агентов и вместо этого позволить им самим забирать работу из нашего трекера задач.
Эта идея превратилась в Symphony — письменную спецификацию, которая работает как супервайзер для оркестрации агентной работы.
Как мы превратили наш трекер задач в оркестратор агентов
Symphony началась с простой идеи: любую открытую задачу должен подхватывать и выполнять агент. Вместо того чтобы управлять сессиями Codex в нескольких вкладках, мы сделали наш трекер задач плоскостью управления.
В этой схеме каждой открытой задаче Linear соответствует выделенная рабочая область агента. Symphony непрерывно следит за доской задач и обеспечивает, чтобы у каждой активной задачи в цикле работал агент, пока она не будет завершена. Если агент падает или зависает, Symphony перезапускает его. Если появляется новая работа, Symphony подхватывает ее и начинает организовывать выполнение.
Мы построили свой рабочий процесс на основе статусов тикетов, используя менеджер задач Linear как машину состояний.
На практике Symphony отвязывает работу от сессий и от пулл-реквестов. По некоторым задачам появляется несколько PR в разных репозиториях; другие представляют собой чистое исследование или анализ и вообще не затрагивают кодовую базу.
Когда работа абстрагируется таким образом, тикеты могут представлять гораздо более крупные единицы работы.
Мы регулярно используем Symphony для оркестрации сложных функций и миграций инфраструктуры. Например, мы можем завести задачу с просьбой к агенту проанализировать кодовую базу, Slack или Notion и подготовить план реализации. Когда план нас устраивает, агент генерирует дерево задач, разбивая работу на этапы и определяя зависимости между задачами.
Агенты начинают работать только над задачами, которые ничем не заблокированы, поэтому выполнение для этого DAG (последовательности шагов выполнения) естественным образом и оптимально распараллеливается. В примере ниже мы отметили обновление React как заблокированное миграцией на Vite. Как и ожидалось, агенты начали обновлять React только после завершения миграции на Vite.
Агенты также могут сами создавать работу. Во время реализации или ревью они часто замечают улучшения, которые выходят за рамки текущей задачи: проблему производительности, возможность рефакторинга или более удачную архитектуру. Когда это происходит, они просто заводят новую задачу, которую мы можем позже оценить и запланировать, — многие из этих последующих задач тоже подхватываются агентами. Пока мы наблюдаем за этим процессом, агенты остаются организованными и продолжают продвигать работу вперед.
Такой способ работы резко снижает когнитивную стоимость запуска неоднозначной работы. Если агент что-то делает неправильно, это все равно полезная информация, а цена для нас почти нулевая. Мы можем очень дешево заводить тикеты, чтобы агент создавал прототипы и исследовал варианты, и выбрасывать любые исследования, которые нам не нравятся.
Поскольку оркестратор работает на devboxes и никогда не спит, мы можем добавлять задачи откуда угодно и быть уверенными, что агент их подхватит. Например, один инженер в нашей команде внес три значительных изменения из приложения Linear на своем телефоне, сидя в уютном домике с нестабильным wifi.
Рост исследовательской работы при таком подходе
Когда мы наблюдали за эффектами работы с Symphony, самым очевидным изменением был объем результата. В некоторых командах OpenAI мы увидели, что число влитых PR выросло в 6 раз за первые три недели. За пределами OpenAI основатель Linear Karri Saarinen отметил всплеск числа созданных рабочих областей(открывается в новом окне) после выпуска Symphony. Однако более глубокий сдвиг касается того, как команды думают о работе.
Когда наши инженеры больше не тратят время на контроль сессий Codex, экономика изменений в коде полностью меняется. Воспринимаемая стоимость каждого изменения падает, потому что мы больше не вкладываем человеческие усилия в то, чтобы непосредственно вести реализацию.
Это изменило наше поведение. В Symphony стало тривиально запускать спекулятивные задачи. Попробуйте идею, исследуйте рефакторинг, проверьте гипотезу и оставьте только те результаты, которые выглядят многообещающе.
Это также расширяет круг тех, кто может инициировать работу. Наш менеджер продукта и дизайнер теперь могут напрямую отправлять запросы на функции в Symphony. Им не нужно клонировать репозиторий или управлять сессией Codex. Они описывают функцию и получают пакет для ревью, который включает видеообзор того, как функция работает в реальном продукте.
Symphony также отлично проявляет себя в больших монорепозиториях (таких, как у нас в OpenAI), где последний этап вливания PR проходит медленно и хрупко. Система следит за CI, при необходимости делает rebase, разрешает конфликты, повторно запускает нестабильные проверки и в целом проводит изменения через весь пайплайн. К тому моменту, когда тикет доходит до статуса Merging, мы уже почти уверены, что изменение попадет в основную ветку без присмотра со стороны человека.
Прогресс приносит новые, но другие проблемы
Работа на этом уровне сопряжена с компромиссами. Когда мы перешли от интерактивного управления агентами к назначению им работы на уровне тикетов, мы потеряли возможность постоянно подталкивать их по ходу выполнения и корректировать курс при необходимости. Иногда агент выдавал результат, который полностью не попадал в цель. Это было полезно — такие неудачи выявляли пробелы в системе и помогали делать ее более устойчивой.
Вместо того чтобы вручную исправлять результат, мы добавляли защитные механизмы и навыки, чтобы в следующий раз агенты могли справиться. Со временем это привело к появлению новых возможностей в нашем harness, таких как запуск end-to-end тестов, управление приложением через Chrome DevTools и проведение smoke-тестов QA. Мы значительно улучшили нашу документацию и четче описали, что считается хорошим результатом.
Не каждая задача подходит под стиль работы Symphony. Некоторые проблемы по-прежнему требуют, чтобы инженеры работали напрямую с интерактивными сессиями Codex, особенно неоднозначные задачи или работа, требующая сильного суждения и экспертизы. На практике именно это обычно самые интересные и приятные задачи, на которые наши инженеры тратят время.
Разница в том, что Symphony может взять на себя основную массу рутинной работы по реализации. Это позволяет инженерам сосредоточиться на одной сложной проблеме за раз, а не постоянно переключаться между более мелкими задачами.
Мы также поняли, что рассматривать агентов как жесткие узлы в машине состояний плохо работает. Модели становятся умнее и могут решать более крупные задачи, чем те рамки, в которые мы пытаемся их поместить. Например, в ранних версиях все интеграции с GitHub были частью внешнего harness — например, ранние версии предполагали, что Codex будет только вносить изменения в код, а остальную часть процесса (отправку изменений, запуск тестов) задавал код. В ранних версиях агентной работы мы просили Codex только реализовать задачу. Такой подход оказался слишком ограничивающим. Codex вполне способен создавать несколько PR, а также читать отзывы по ревью и вносить исправления. Поэтому мы дали ему инструменты — CLI gh, навыки чтения логов CI и т. д. — и теперь можем просить Codex делать больше, например закрывать старые PR или собирать отчеты по завершенной и заброшенной работе. Такие задачи сильно выходили за рамки первоначальной «коробки» реализации функций.
Поэтому в итоге мы пришли к тому, чтобы давать агентам цели, а не жесткие переходы, примерно так же, как хороший менеджер ставит цель своему непосредственному подчиненному в команде. Сила моделей заключается в их способности к рассуждениям, так что дайте им инструменты и контекст — и пусть работают.
Использование Symphony для создания Symphony
Когда вы открываете репозиторий Symphony, первое, что вы замечаете: технически Symphony — это всего лишь файл SPEC.md, то есть определение проблемы и предполагаемого решения. Вместо построения сложной системы надзора мы определили проблему и предполагаемые решения, дав агентам высокоуровневые ориентиры.
Эталонная реализация написана на Elixir — потому что, когда код фактически бесплатен, наконец можно выбирать языки за их сильные стороны, такие как конкурентность Elixir, — но саму основную идею можно выразить в простом документе Markdown. Мы рекомендуем направить ваш любимый агент для программирования на спецификацию и попросить его реализовать собственную версию.
Первая версия Symphony была просто сессией Codex, запущенной в tmux, которая опрашивала Linear и запускала подагентов для новых задач. Это работало, но было не особенно надежно. Вторая версия жила внутри нашего основного репозитория проекта, который изначально создавался с учетом агентов. Мы уже построили harness для агентов, чтобы дать им навыки и контекст для качественной работы в этом репозитории, поэтому Symphony просто все это связывает.
Как только появилась базовая функциональность, мы использовали Symphony, чтобы строить Symphony.
Когда мы внутренне продемонстрировали систему, управляющую задачами и прикрепляющую свое proof-of-work-видео, реакция была исключительно положительной: наш канал проекта Symphony вырос, а команды по всей организации начали использовать ее органически. Внутренний product-market fit — обязательное условие для внешнего запуска в OpenAI. Судя по тому, как Symphony использовалась в OpenAI, стало ясно, что ею стоит поделиться и за пределами компании.
Поэтому мы выделили идею в отдельный SPEC.md и попросили Codex реализовать ее. Для эталонной реализации мы выбрали Elixir — сравнительно нишевый язык с отличными примитивами для оркестрации и надзора за конкурентными процессами. Codex создал реализацию на Elixir за один заход, а дальше мы продолжили итерации и над спецификацией, и над реализацией. Чтобы отполировать спецификацию, мы даже попросили Codex реализовать ее на нескольких других языках — TypeScript, Go, Rust, Java, Python — и использовать результаты, чтобы находить неоднозначности и упрощать систему. Он справился на каждом языке.
В процессе создания Codex мы убрали много побочной сложности, например зависимости от конкретных репозиториев или Linear MCP. Symphony больше не зависит от наших внутренних репозиториев или рабочих процессов. Базовый подход стал простым:
Для каждой открытой задачи гарантируется запуск агента в собственной рабочей области.
Помимо помощи с активной работой, сам процесс разработки теперь стал тем, что агенты знают и чему следуют. Процесс разработки — работа над задачей, checkout репозитория, перевод задачи в статус in progress, чтобы PM знал, что над ней работают, добавление PR, перевод в статус Review, прикрепление видео и так далее — теперь зафиксирован в простом файле WORKFLOW.md. Все это было процессом, которому следовали люди, но он никогда не был задокументирован. Вместо того чтобы полагаться на этот неявный набор шагов, теперь мы его документируем, а Symphony следит, чтобы агенты ему следовали. Это позволяет нам создавать агентов, которые работают вместе с нами. Если мы решим, что агенты также должны прикреплять самоанализ к завершенной работе, мы добавим это в WORKFLOW.md, и Symphony направит агентов к этому шагу.
Нам также удалось использовать Codex в режиме app server(открывается в новом окне) — встроенном headless-режиме для Codex. Этот режим позволил нам запускать Codex и программно взаимодействовать с ним через хорошо документированный API JSON-RPC для таких вещей, как запуск треда или реакция на ходы. Это гораздо более удобный и масштабируемый способ, чем пытаться взаимодействовать с Codex через CLI или живые сессии tmux.
Codex App Server идеально подошел для нашего сценария: мы используем harness, который предоставляет Codex, и при этом сохраняем точки расширения и интеграции. Например, чтобы не раскрывать субагентам токен доступа Linear, мы используем dynamic tool calls(открывается в новом окне), чтобы предоставить сырую функцию linear_graphql, выполняющую произвольные запросы к Linear, не полагаясь на MCP и не раскрывая токен доступа контейнерам.
Что дальше
Symphony — намеренно минималистичный слой оркестрации. Мы открываем ее исходный код, чтобы продемонстрировать мощь Codex App Server в сочетании с различными инструментами рабочего процесса, такими как Linear. Поэтому мы не планируем поддерживать Symphony как самостоятельный продукт. Считайте ее эталонной реализацией. Подобно тому как многие разработчики направляли своих агентов для программирования на пост о harness engineering, чтобы каркасно собрать свои репозитории, мы надеемся, что вы направите вашего любимого агента для программирования на спецификацию(открывается в новом окне) и репозиторий(открывается в новом окне) Symphony, чтобы собрать собственные версии, адаптированные под ваши среды.
Сила исходит от Codex и его app server. Symphony стала способом связать Codex и Linear — две вещи, которыми мы уже пользовались, — чтобы решить проблему управления работой. По мере того как агенты для программирования будут лучше рассуждать и следовать инструкциям, мы предполагаем, что узкое место в других компаниях тоже сместится от написания кода к управлению агентной работой. Самое захватывающее здесь то, что порог для экспериментов с такими системами агентов для программирования сейчас на удивление низкий. Вы можете просто создавать вещи с помощью Codex.
Благодарности сообществу
Мы рады видеть, как инженерное сообщество использует Symphony в недели после релиза: на 23 апреля проект уже набрал более 15 тыс. звезд на GitHub(открывается в новом окне).