如何编排 Codex 去自动做工单: Symphony
OpenAI 把 Codex 编排做成了语言无关的规范。
- 三 大要素:SPEC.md(规范) + elixir/(参考实现) + WORKFLOW.md(仓内配置)
- 定位升级:从「管 agent」升级到「管工单」
- 主循环:拉 tracker → 建 per-issue workspace → 跑 Codex app-server
- 边界:低密预览版,只跑受信环境,无内置沙箱
- 设计巧思:host 侧注入 tracker 凭据,主动从 Codex 子进程剔除
- License:Apache-2.0,可绕过 Elixir 按规范自实现
Symphony 不是产品,是规范
OpenAI 这次发的不是一个新 agent runtime,而是一份规范外加一个参考实现——Symphony 把「如何编排 Codex 去干工单」拆成了两件可以分开看的东西。
- SPEC.md:语言无关、RFC-2119 风格的规范。它定义域模型(Issue / Workspace / RunAttempt / LiveSession / RetryEntry)、定义抽象分层(Policy / Config / Coordination / Execution / Integration / Observability),但不告诉你用哪种语言、用哪种 tracker、用什么沙箱策略。
- elixir/:Elixir/OTP 参考实现。它证明规范可以照着实现出来,但 README 开头就警告「prototype software intended for evaluation only, presented as-is」——不要直接拿这份实现当生产框架。
这种「先 spec 后 impl」的拆分很像 OpenAPI/Swagger。规范是契约,参考实现是说服读者的最小证明,真正的实现由各团队按自己的栈、安全策略、tracker 接入方式自己写。
WORKFLOW.md:把编排策略塞进仓库
Symphony 最值得抄的一点是把编排策略装进仓库的 WORKFLOW.md,跟着代码一起发版。
- YAML front matter 是 typed config:tracker 类型(
tracker.kind: linear)、workspace 根目录、并发上限、Codex 启动命令、超时阈值都在这一层。 - Markdown body 是 Codex session 的 prompt 模板,
{{ issue.identifier }}、{{ issue.title }}这类占位符在每轮 dispatch 时由 Symphony 渲染后下发。
---
tracker:
kind: linear
provider:
project_slug: "..."
workspace:
root: ~/code/workspaces
hooks:
after_create: |
git clone git@github.com:your-org/your-repo.git .
agent:
max_concurrent_agents: 10
max_turns: 20
codex:
command: codex app-server
---
You are working on an issue from the configured tracker {{ issue.identifier }}.
Title: {{ issue.title }} Body: {{ issue.description }}
直接结果:「怎么管 coding agent」这件事跟着代码一起 review、一起发版本、一起回滚,而不是藏在某个长工序列里。
主循环:拉工单 → 开 workspace → 跑 Codex
抽象层看下来,Execution Layer 是这样的:
每个 issue 一个独立 workspace,Codex 以 app-server mode 在里面跑(JSON-RPC over stdio),Symphony 作为 orchestrator 收流式事件、维护运行时状态。tracker 适配器顺手暴露 provider-native 工具给 Codex——Linear 拿到 linear_graphql、GitHub 拿到 github_api、Jira 拿到 jira_rest——这样 Codex 自己能写评论、改状态,不用 Symphony 在外面再代理一层。
tracker 的 API key 通过 host 侧的环境变量注入,Symphony 在 fork 出 Codex 子进程时主动剔除这些 token 变量。Codex 拿不到原始凭据,要写 tracker 就走 Symphony 暴露的代理工具。凭证泄漏面比传统 bot 收敛一截——agent 没有第二个 tracker 登录入口。
状态机比想象中克制
Symphony 故意不做「通用工作流引擎」,规范里的边界划得很小:
- 终止态:issue 一旦进
Done/Closed/Cancelled/Duplicate,Symphony 立刻停掉对应 agent、清理 workspace。这意味着你可以手动通过改工单状态中止任何正在跑的 task——不需要单独的 kill 通道。 - Blocked 态:Codex 上报「需要 operator 审批 / MCP elicitation」时,Symphony 把这个 issue 标记为 blocked,暴露在运行时状态、JSON API、dashboard 里等人介入。
- Retry:指数退避;retry queue 在内存里;进程重启后清空,让 tracker 自己重新成为 source of truth。
非目标里更值得看:规范没有强制多租户控制平面,没有规定 UI 形态,也没有强加审批/沙箱策略——安全这件事由各家实现自己背书。
信任边界:先认清它是 Draft
打开 README 第一眼是 > [!WARNING] Symphony is a low-key engineering preview for testing in trusted environments. 把它当 demo 跑没问题。
SPEC.md 状态写的是 Draft v1,RFC-2119 用得很标准(MUST / SHOULD / MAY 分得很清楚),但照着写实现之前先想清楚几件事:
- 不要把 token 显式写进仓库——
WORKFLOW.md引用环境变量靠 host 侧 secret ref,不是文件本身。 - 默认
codex.approval_policy是reject、thread_sandbox是workspace-write,别自己改成never之类的危险值。 - 进程重启会丢 retry queue。这是设计选择不是 bug——意味着你得有上游 tracker 当 source of truth,否则别省这一步。
怎么参与:自己写一份也行
README 给两条路。
第一条:按 SPEC.md 自己实现。 README 鼓励的方式——「Tell your favorite coding agent to build Symphony in a programming language of your choice」。SPEC.md 是语言无关的,所以 Go / Rust / TypeScript 都能起一份。
第二条:用 Elixir 参考实现。 v* tag 提 供 Burrito 打的单文件 release,四个平台都有:
chmod +x ./symphony-v0.0.1-macos_arm64
./symphony-v0.0.1-macos_arm64 ./WORKFLOW.md
单文件里已经打了 Erlang/OTP + Elixir + Symphony,目标机器上只需要 codex 和 git 在 PATH 里。嫌 Elixir 维护成本高,自己起一份 Go 实现也没问题——这就是规范先行的好处。
References
- Open source Codex orchestration: Symphony —— OpenAI, 官方博客
- openai/symphony —— Apache-2.0,截至 2026-07-27 仓库 26k Star
- Harness engineering —— OpenAI