2026 年 3 月初,OpenAI 在 GitHub 上公开了 openai/symphony 仓库。这个仓库很容易被误读成「一份 SPEC 加一点示例代码」,因为根目录 README 把第一个选项写成了「让你喜欢的 coding agent 按 SPEC.md 自己实现一版」。但真正打开目录看,仓库里有三块东西:根目录的 SPEC.md,一套可以构建和运行的 elixir/ 参考实现,以及 .codex/.github/media/ 这类围绕 Codex workflow、演示视频和工程素材组织起来的配套内容。elixir/README.md 里的启动命令不是伪代码,而是完整的本地构建链路:mise exec -- mix setupmise exec -- mix build,最后用 ./bin/symphony ./WORKFLOW.md 启动服务 [Symphony Elixir README]

所以 Symphony 首先不是一篇规范文档,而是一个被拆成两层发布的工程项目:SPEC.md 说明什么叫 Symphony,elixir/ 证明这套东西能作为一个长驻服务跑起来。前者是协议和行为边界,后者是可执行的 orchestrator。前一版文章的问题就在这里:它把镜头几乎全对准了 SPEC.md,读者看完只记得「OpenAI 发了一个编排规范」,却没有建立起「这个仓库里已经有一个能 build、能连 Linear、能拉起 Codex、能跑 dashboard、能跑测试的参考系统」这个基本印象。

将近两个月之后,OpenAI 官方博客 An open-source spec for Codex orchestration: Symphony 与 InfoWorld 2026 年 4 月 28 日那篇 OpenAI’s Symphony spec pushes coding agents from prompts to orchestration 把仓库里的一组内部数据放大到了企业读者面前——“some internal teams seeing landed pull requests rising 500% in the first three weeks” [InfoWorld, 2026-04-28]。与此同期还有一条外溢信号:据 Help Net Security 转述,Linear 联合创始人兼 CEO Karri Saarinen 提到 Symphony 发布前后 Linear 平台上 workspace 创建数出现了一个可观测的高峰 [Help Net Security, 2026-04-28];The Neuron 的一份独立汇编把这条信号具体化为"Linear 平台发布当日的 workspace 创建数跃升"[The Neuron, 2026-06]。两处转述都来自二手媒体,Karri 本人没有留下可检索的一手发言,这条外溢信号只能作为弱证据看待。真正值得先看的,反而不是 500% 这个数字,而是仓库本身把「工单驱动的 Codex 编排器」做成了什么样。

先把仓库当工程看

openai/symphony 根目录给出的运行路径有两个。第一个路径是「Make your own」:把 SPEC.md 丢给 coding agent,让它用任意语言实现一版 Symphony。第二个路径是「Use our experimental reference implementation」:进入 elixir/,按 README 装依赖、构建 escript、启动 bin/symphony。这两个路径的顺序容易造成一种错觉,好像 OpenAI 只关心 spec,Elixir 只是附带样例。实际情况更像是:OpenAI 用 SPEC.md 固化跨语言契约,用 elixir/ 给出一份能落地的 OTP 版本。

elixir/ 不是一个薄 demo。它的 README 直接列了工作流:轮询 Linear 候选工单,为每条 issue 创建 workspace,在 workspace 里以 Codex App Server mode 启动 Codex,把 WORKFLOW.md 渲染出的 prompt 发给 Codex,并持续推进这条 issue,直到工作完成或 issue 进入终态。运行侧有 WORKFLOW.md,构建侧有 mix setupmix buildbin/symphony,验证侧有 make all,真实集成侧还有 make e2emix.exs 里把 build 映射到 escript.build,产物路径就是 bin/symphonyMakefile 里的 all 会串起 setupbuildfmt-checklintcoveragedialyzer。这已经是一个按 Elixir 项目方式组织的运行时,而不是 README 里的几段伪命令。

工程面也不止 CLI。elixir/README.md 明确写了可选的 Phoenix observability service:/ 路径下是 LiveView dashboard,/api/v1/state/api/v1/<issue_identifier>/api/v1/refresh 等路径提供 JSON API,HTTP server 用 Bandit。这个服务默认关闭,需要通过 server.port 或 CLI 的 --port 显式打开。换句话说,仓库里不仅有调度循环,还有一个用于观察运行状态的控制面雏形,只是 OpenAI 没有把它包装成正式产品。

测试也不是摆设。elixir/README.md 里把普通验证写成 make all,把真实外部端到端测试写成 make e2e。后者会创建临时 Linear project 和 issue,写临时 WORKFLOW.md,启动真实的 codex app-server session,验证 workspace 副作用,并要求 Codex 在 Linear 上评论和关闭 issue。E2E 还覆盖本地 worker 和 SSH worker 两种场景;如果没有配置真实 SSH host,它会用 docker compose 启动一次性 SSH worker,并把宿主机的 ~/.codex/auth.json 挂进去。这个细节很重要:Symphony 不是只在本机开子进程的玩具,它已经把「worker 可能跑在 SSH 远端」这条生产化路径放进了测试叙事里。

WORKFLOW.md 则是这套工程的运行契约。它的头部 YAML front matter 配 tracker、workspace、hooks、agent 并发和 Codex 命令,正文 Markdown 是发给 Codex 的 prompt 模板。最小示例里,hooks.after_create 可以直接 git clone git@github.com:your-org/your-repo.git .agent.max_concurrent_agents 可以设成 10codex.command 默认为 codex app-server。这份文件解释了 Symphony 为什么要强调「管理 work 而不是监督 agent」:工程师不是手动打开十个终端盯 Codex,而是把队列、workspace、hook、prompt、sandbox 这些行为写进一个仓库内可 review 的 workflow 文件。

SPEC 和实现的关系

把仓库当工程看之后,SPEC.md 的位置会清楚很多。它不是文章的全部主角,而是 Elixir 实现背后的行为合约。根 README 让用户「按 SPEC 自己实现一版」,说明 OpenAI 不想把 Symphony 绑定到 Elixir;elixir/README.md 又说当前目录是「based on SPEC.md at the repository root」,说明这份实现不是随意写出来的 demo,而是规范的一份执行结果。

这也解释了为什么官方同时强调两句话:一边说 Symphony 是 “a low-key engineering preview for testing in trusted environments”,另一边又说可以让 coding agent 用 TypeScript、Go、Rust、Java、Python 重写一遍 [openai/symphony README][AllThings.how, 2026-04-28]。前一句约束的是当前实现的生产可靠性,后一句强调的是 spec 的跨语言意图。Elixir 版本可以跑,但不等于它就是企业应当原封不动搬进生产的产品;SPEC.md 可以指导重写,但不等于仓库只是一个纸面协议。

这层关系决定了读这份仓库的正确顺序:先看 elixir/README.md,确认它实际构建出什么服务;再看 WORKFLOW.md,理解一条 issue 怎样变成 agent session;最后看 SPEC.md,理解哪些行为是实现必须遵守的契约,哪些行为是 Elixir 版本自己的工程选择。倒过来读,很容易陷入规范条款里,反而忘了仓库里已经有一个跑得起来的 orchestrator。

Symphony 的定位

用工程语言概括,Symphony 是一个长驻守护进程:把 Linear 这类 issue tracker 当成输入端,按固定节奏轮询工单。每拿到一条符合条件的工单,就在文件系统上开出一个独立 workspace,在里面启动一个 Codex App Server session,持续把这条 issue 往前推。Symphony 本身不生成代码,也不直接调用大模型 HTTP API;它负责调度、隔离、观测、重试、回收和凭据边界,代码由 Codex 写,工单状态由 workflow 和工具链推进。

Symphony 架构与数据流

Symphony 的核心数据流:tracker 提供工单状态,orchestrator 只负责读 tracker 与调度 run,具体实现发生在每条 issue 对应的 workspace 里。WORKFLOW.md 把策略留在仓库,Codex app-server 子进程把代码执行面和调度面分开。

这套定位和 OpenAI 过去陆续发布的几件东西刚好互补。Agents SDK 是「在代码里组合多个 agent 的 Python SDK」;更早的 Swarm 是多 agent 的教学性实验框架(官方 README 标注为 educational,不推荐生产使用);Codex CLI 和 Codex App Server 是「在终端里跑一个 coding agent」;Symphony 填上的那一层,是「让多个 Codex session 不再靠工程师手动切换窗口,而是由一个常驻服务根据工单面板自己去排」。

Symphony 的起源,需要拆成两条线索。一条来自 Digit 在 2026-04-28 对官方博客的转述:OpenAI 内部一个小组给自己定下过一条硬约束——“none of it could be written by humans; everything needed to be programmed by Codex”,也就是这个生产力工具本身的代码不许由人直接写 [Digit, 2026-04-28]。另一条来自 Latent Space 对 OpenAI Harness Engineering 负责人 Ryan 的访谈:Codex 负责写 app 逻辑、测试、CI/CD 和文档,人只做架构决策与 review [Latent Space, 2026]。两条线索相互印证。为了让这条约束站得住脚,团队先重构自己的工程工作流,把代码仓库改造成适合 agent 工作的形态——自动化测试、可机读的结构、限制 agent 行为的 guardrail——这套做法对应的就是 OpenAI 发布的 Harness engineering 博客 [OpenAI, Harness engineering]

等 harness engineering 成型,新的瓶颈才浮现出来:Codex 再快,一个工程师同时监督多少个会话才会被 context switching 拖垮?OpenAI 给出的数字是 3 到 5:

Engineers could manage only three to five sessions before context switching became painful, the company said, limiting the productivity gains from faster coding agents. [InfoWorld, 2026-04-28]

Symphony 要解决的就是这个上限。GitHub 仓库 README 开篇那句是这条定位的一手表述——“Symphony turns project work into isolated, autonomous implementation runs, allowing teams to manage work instead of supervising coding agents” [openai/symphony README]。Digit 的记者把这条定位概括成了一句更抢眼的口号——“stop managing agents, start managing work” [Digit, 2026-04-28],这句不是官方博客原文,是转述方的提炼。

Symphony 的规范里专门用一节 Non-Goals 把它不做的事一条条摆出来:不做 rich web UI 或多租户控制面、不规定具体的 dashboard 实现、不做通用工作流引擎 / 分布式调度器、不替用户写 ticket/PR/comment 的业务逻辑、不强制任何一种 sandbox 控制、不规定统一的 approval / sandbox / operator-confirmation 姿态。照这几条看下来,它不是 Temporal、不是 n8n、不是 Devin,只是一个很薄的调度与运行层。这个「薄」不等于没有工程实现,而是说它把业务策略、代码修改和 tracker 写操作尽量留给 workflow 与 Codex,把自己收束在 orchestrator 这条边界内。

一条 issue 的完整生命周期

Symphony 在语义层其实是两级状态机叠加。外层是 Linear 的 ticket status:工单处于什么状态,Symphony 就做什么动作——状态流到 “In Progress”,Orchestrator 就把它纳入候选;状态流回 “Backlog” 或被别人改成 “Canceled”,Orchestrator 就把对应的 run 收走。AllThings.how 对官方博客的复述里把这一层明确写成了驱动编排行为的 state machine——“The orchestrator watches ticket statuses as a state machine and ensures each active issue always has a worker attached” [AllThings.how, 2026-04-28]。这一层设计也顺带解除了「issue 必须是代码任务」的假设:同源转述里明确"A single ticket might produce multiple PRs across repos, or none at all if the task is pure investigation",一条工单可以在多个仓库里产出 PR,也可以什么代码都不写、只是一次调研或分析 [AllThings.how, 2026-04-28];Help Net Security 用的措辞更偏工程侧:“Some Linear issues produce multiple pull requests across repositories, and others involve investigation or analysis that never touches the codebase” [Help Net Security, 2026-04-28]

内层是 Symphony 自己定义的 implementation run:针对一条 issue,一次隔离的、自主的实现过程。规范给这个内层状态机定义了 11 个阶段,从拿到工单到干完(或失败)都在里面。

一次典型 run 的生命周期是这样:

stateDiagram-v2
    [*] --> PreparingWorkspace: issue 进入 active 状态
    PreparingWorkspace --> BuildingPrompt: 创建/复用 workspace
    BuildingPrompt --> LaunchingAgentProcess: WORKFLOW.md + issue 字段渲染 prompt
    LaunchingAgentProcess --> InitializingSession: 启动 codex app-server
    InitializingSession --> StreamingTurn: stdio JSON line protocol
    StreamingTurn --> Finishing: 消费 tool calls / approvals / token usage
    Finishing --> StreamingTurn: issue 仍 active,继续下一轮 turn
    Finishing --> Succeeded: issue 进入完成态
    Finishing --> Failed: agent / hook / 协议失败
    Finishing --> TimedOut: 超时
    Finishing --> Stalled: 长时间无进展
    Finishing --> CanceledByReconciliation: tracker 状态被外部取消

这张图里最容易漏看的边,是 Finishing --> StreamingTurn。worker 正常跑完一轮并不代表 issue 结束;只要 tracker 上的工单还在 active,orchestrator 就会继续推进同一个 thread。

每个阶段都不复杂,关键在于状态之间的转移规则是完全由一个单点 Orchestrator 串行执行的。规范里原话是一句很克制的约束:

The orchestrator is the only component that mutates scheduling state.

翻成工程话就是:只有编排器能改调度状态,所有 worker 的结果都要回到它手里再转成状态跃迁。分布式并发下最恶心的竞态问题被直接封在语义层,不存在两个 worker 同时把一条 issue 声称自己拿到的情况。

状态机之外还有一层更微妙的设计:worker 正常退出并不等于 issue 处理结束。即便 worker 在本轮 turn 里正常跑完,Orchestrator 也不会立刻收摊,而是隔一秒左右重新拉一次工单状态,只要工单还在 active,下一轮 worker 就继续把 thread 推一个 turn。换句话说,Symphony 并不假设「一次 run 能一次搞定」,它假设的是「一条 issue 可能要在几次 worker session 里被逐步推进到 terminal state」。这个语义决定了它可以跑「写代码 → 等 CI → 读审查评论 → 补丁」这种多回合工作,而不是一锤子买卖。

规范里的五个工程决定

Symphony 的规范是一份 Draft v1 级别的文档,用 RFC 2119 关键字(MUST / SHOULD / MAY)写得非常正式。剥开语言无关的外壳,里面有分量的工程决策是下面这五个。

决定一,轮询而不是 webhook

Symphony 默认每 30 秒轮询一次 Linear 的 GraphQL,不用 webhook。每轮 tick 的动作序列很固定:先对正在跑的 issue 做一次 reconcile,跑一遍派发前的 preflight,再拉一次候选工单,按 priority + 创建时间 + identifier 做稳定排序,在并发槽位允许的范围内派发,最后把状态变化推给可观测性消费者。

这是一条典型的「为运维简单性让渡实时性」的设计。Webhook 要求公网可达的 endpoint、要处理重放与去重、要考虑 secret 泄露;轮询则只要一个出站 HTTP 请求就能解决。代价是 30 秒量级的延迟,但对于「一条工单从创建到被 agent 拿走」这个时间尺度来说,30 秒完全可以接受。更关键的一点是重启恢复是 tracker-driven + filesystem-driven 的:Symphony 没有持久化的 orchestrator DB,重启后不还原内存里的调度态,而是重新从 Linear 拉当前状态、从文件系统上重新发现已有 workspace。放弃 DB 这件事本身就把可靠性策略收得很窄,只要 tracker 在,Symphony 就不会丢工作。

决定二,文件系统工作区,不是容器

Symphony 的隔离粒度是 per-issue filesystem workspace,不是 container,也不是 VM。规范层面只规定了三条硬约束:agent 的 cwd 必须是这条 issue 的 workspace 路径;workspace 路径必须落在配置的根目录内,靠绝对路径前缀校验兜底;workspace 目录名(取自工单 identifier)必须经过严格清洗,只允许字母、数字、点、下划线、短横线。

实际的安全边界由 Codex 自己的 sandbox 来守。规范对具体默认值留白(approval_policy / thread_sandbox / turn_sandbox_policy 都标成 “implementation-defined”),把选择权交给实现方。Elixir 参考实现则把默认值写在了 README 里:codex.thread_sandbox 默认 workspace-writecodex.turn_sandbox_policy 默认是一个 rooted 在当前 issue workspace 的 workspaceWrite 策略,codex.approval_policy 默认拒绝 sandbox 提权、规则改动与 MCP 的 elicitation 请求(原文是 {"reject":{"sandbox_approval":true,"rules":true,"mcp_elicitations":true}})。想跑进外部生产,这组默认值仍然偏弱,官方自己把「再套一层 OS / 容器 / VM 级隔离」写成了硬化建议而不是硬性要求。

这个选择有一个直接的合理性:绝大多数企业内网都跑在已经受信的机器上,强行加容器层会让 Elixir 参考实现跑不起来,也会让文件系统级工作区的热缓存优势失掉(Symphony 的工作区是跨 run 复用的,不是每次重建)。但这条决策也是 Symphony 在「外部生产环境」使用时最脆弱的一点。官方在 README 里把自己定位成「a low-key engineering preview for testing in trusted environments」,不是随便挑的措辞。

决定三,把 agent 当子进程,而不是 API

Symphony 不直接调用 OpenAI 的大模型 HTTP API。Symphony 自己对 Linear 的调用是用 tracker adapter 做读侧工作(拉 candidate issues、拉终态 issues、拉 issue 当前状态),所有"往 Linear 写入"的动作——状态跃迁、评论、PR link 挂载——由 coding agent 通过 workflow 里配置的 tool 完成;SPEC 第 1 节 Problem Statement 里的 Important boundary 明确写过:“Symphony is a scheduler/runner and tracker reader” / “Ticket writes … are typically performed by the coding agent using tools available in the workflow/runtime environment”。Symphony 在 workspace 目录里起一个 Codex 子进程(默认命令就是 codex app-server),再按 SPEC 18.1 写死的那条要求 “Coding-agent app-server subprocess client with JSON line protocol”,用 Codex App Server 协议在 stdio 上和子进程通信。规范把这套协议定位成 pass-through:Symphony 不自己维护一份 Codex schema,而是让实现方直接从 Codex 官方 schema 里取。

把 agent 当子进程而不是当 HTTP 服务,带来两个不小的好处:进程崩溃天然就是 session 结束信号,不用再设计 keepalive;stdin/stdout 天然带背压,不用另写流控。代价是 Symphony 被绑死在「能讲 Codex App Server 协议的 agent」上,规范里找不到任何和 Claude Code、Cursor 背景 agent 互操作的条款。理论上可以另起一个兼容协议的桥接器,但这个扩展性是口头承诺,不是规范承诺。

决定四,把策略放回仓库

Symphony 明确把所有「业务策略」外置到仓库里的一份 WORKFLOW.md。这份文件头部是 YAML front matter,tracker 连接、轮询间隔、workspace 配置、hooks、agent 并发、Codex sandbox 等所有可调项都在这里。body 是一份 Liquid 兼容语法的 prompt 模板,必须走严格模式,未知变量和未知过滤器都要让渲染失败。模板可访问的输入只有一条 issue 的规范化字段集合(标题、描述、优先级、状态、标签、blocked_by 等)加一个当前重试次数。规范同时要求实现方支持动态 reload:文件改了,orchestrator 不用重启就要把新配置应用到下一轮 dispatch。

这一层抽象看起来是写给 YAML 党看的,但它其实解决的是一个很朴素的问题:谁拥有 agent 的行为。如果 prompt 和配置放在 SaaS 后台,那 agent 的行为就归 SaaS 提供方;放进 WORKFLOW.md,它就跟着代码一起走 Code Review、走 Git History、走回滚。这和 GitHub Actions 的 .github/workflows/*.yml 是同一个思路。

决定五,用 linear_graphql 这个 optional client-side tool 把 Linear 凭据留在 orchestrator 侧

SPEC 10.5 节在 “Approval, Tool Calls, and User Input Policy” 里把这条单列成了 optional client-side tool extension。官方措辞是:

An implementation MAY expose a limited set of client-side tools to the app-server session. Current standardized optional tool: linear_graphql. [SPEC 10.5]

这条 extension 不是强制项(是 MAY),也不是否定 MCP,它是规范为 Linear 这一特定集成提供的标准化客户端侧工具契约。同一节给出的 extension contract 把语义划得非常窄:tool 一次只允许执行一条 GraphQL 操作(“query MUST contain exactly one GraphQL operation”),多操作文档要被作为 invalid input 拒绝;operationName 选择被显式写成"intentionally out of scope";Linear 的 endpoint 与 auth 全部由 Symphony 配置,“do not require the coding agent to read raw tokens from disk”;tool 的结果语义分三档:transport 成功且无 GraphQL errors 为 success=true,有 errors 则 success=false 并保留响应体做调试,invalid input / 缺 auth / transport 失败则 success=false 并返回 error payload [SPEC 10.5]

sequenceDiagram
    participant Agent as Codex agent in workspace
    participant Symphony as Symphony orchestrator
    participant Linear as Linear GraphQL API

    Agent->>Symphony: linear_graphql(query, variables)
    Symphony->>Symphony: 校验只有一个 GraphQL operation
    Symphony->>Linear: 使用 orchestrator 侧 auth 发起请求
    Linear-->>Symphony: data / errors / transport failure
    Symphony-->>Agent: success flag + structured payload

    Note over Agent,Symphony: Linear token 不进入 workspace,也不暴露给 agent / subagent

二手转述里的总结值得引用,因为它说明了这条设计的工程动机。Help Net Security 的原话是 Symphony “uses dynamic tool calls that expose a raw linear_graphql function for executing arbitrary requests against Linear, avoiding both MCP and direct token exposure to containers” [Help Net Security, 2026-04-28];AllThings.how 的措辞更精确——“Rather than handing every subagent a Linear access token, Symphony can expose a single client-side dynamic tool, linear_graphql, that executes one GraphQL operation per call against the configured Linear endpoint. The agent supplies a query and optional variables; Symphony performs the request using its own auth and returns the response as structured tool output. This avoids both Linear MCP and direct token exposure to containers” [AllThings.how, 2026-04-28]

需要注意的是,“avoiding MCP” 是这一次 Linear 集成的具体选择,而不是 Symphony 反对 MCP 的通用主张。SPEC 没有禁止 MCP、也没有在其他场景排除 MCP;这里换成 dynamic tool call 的唯一理由是凭据隔离——token 始终留在 orchestrator 进程里,agent 和所有 subagent 都没有直接接触 token 的机会,即使出现 sandbox 逃逸或 subagent 权限越界,也不会把 Linear 凭据带出去。代价是 Linear GraphQL schema 的正确性要靠 prompt 模板和 Codex 自己兜,拼错 GraphQL 时调试链条更长;另外这是 Linear-only 的 extension(tracker.kind == "linear" 时才有意义),未来加新的 tracker 需要另起一份对应的 optional client-side tool,不能直接挂一个通用 MCP 了事。

这五条选择合起来组成了整份规范里最一致的设计口味:轮询而不是 webhook,文件系统而不是容器,子进程而不是 HTTP,仓库内配置而不是 SaaS 配置,在 Linear 这一侧用 optional dynamic tool 而不是让 agent 直接拿 token。每一条都是用一点性能、实时性或生态便利,换回来运维心智、可移植性、审计能力或凭据隔离。

参考实现为什么选 Elixir

elixir/ 当成一个可运行项目之后,再看「为什么选 Elixir」会比只看 FAQ 更容易理解。Symphony 的运行时不是一次性脚本,而是一个长期活着的 orchestrator:它要周期性轮询 Linear,要维护多条 issue run 的状态,要启动和回收 Codex 子进程,要在 worker 异常、配置 reload、外部服务抖动时保持主进程不倒。官方在 elixir/README.md 的 FAQ 里给的理由原文是:

Elixir is built on Erlang/BEAM/OTP, which is great for supervising long-running processes. It has an active ecosystem of tools and libraries. It also supports hot code reloading without stopping actively running subagents, which is very useful during development.

剥掉措辞,核心理由是两点:OTP supervision tree 和 hot code reloading。

OTP 是 BEAM 上一套沉淀了三十多年的「监督树」模型。一个 supervisor 下面挂一串 worker,worker 挂了,supervisor 按预设策略把它重启,整棵树不用跟着倒。Symphony 这种「同时跑 N 个可能崩的 Codex 子进程,还不能让一个崩溃拖垮整个调度器」的场景,正好落在 OTP 的舒适区。用 Python 或 Node.js 重写同样的编排语义当然可行,但进程池、信号处理、重启策略、异常隔离和状态恢复都要自己拼;在 Elixir/OTP 里,这些能力不是业务代码外面的脚手架,而是运行时模型的一部分。

Hot code reloading 的价值更偏研发期。Symphony 的工作流配置会变,prompt 模板会变,dashboard 和 API 也会跟着调试需求变化。参考实现允许在不中断正在跑的 subagent 的前提下迭代服务本身,这对一个 engineering preview 阶段的项目很实用。它解释了为什么 OpenAI 没有先选更大众的 TypeScript 或 Python 来发第一版,而是选了一个团队外读者不一定熟悉、但天然适合长驻监督进程的 BEAM 生态。

不过,Elixir 只是参考实现的工程选择,不是 Symphony 的抽象边界。根 README 同时给了两个入口:要么跑 elixir/,要么按 SPEC.md 自己实现。AllThings.how 还补充过一条工程事实:OpenAI 让 Codex 基于同一份 SPEC.md 分别用 TypeScript、Go、Rust、Java、Python 各实现了一遍,用这组平行实现去"挑出规范里模棱两可的地方" [AllThings.how, 2026-04-28]。这条事实把 Elixir 版本的位置说清楚了:它是当前仓库里可构建、可运行、可观察、可测试的参考系统,但跨语言契约层仍然在 SPEC.md,不在任何一份语言实现里。

那个 500% 站不站得住

Symphony 出圈最常被引用的就是那个 “some teams 500%”。把它和 Symphony 自己给出的另一个工程瓶颈数字(“3-5 sessions”)并排看,才能看清楚前者站不站得住、后者是不是更该被拿来做架构决策的证据。

数字一,「some teams 500% landed PR」

原文出处是 OpenAI 官方博客,被 InfoWorld 转述为:

some internal teams seeing landed pull requests rising 500% in the first three weeks. [InfoWorld, 2026-04-28]

几个细节必须摆出来:

  • 分子与分母都没说。“some teams” 是哪些队、原来的 PR 基线是多少、是按人均还是总量,OpenAI 都没给。没有分母的比例,本身就无法被第三方复现。

  • 三周是一个炒作期窗口。任何新工具在前三周都容易有 novelty boost,把"first three weeks"的数据外推到长期稳态,是行为经济学意义上的典型陷阱。

  • PR 数量从来不是软件工程的共识指标。InfoWorld 同一篇报道里引了两位独立分析师的判断。Forrester 首席分析师 Biswajeet Mahapatra 的原话是:

    Relevant measures include lead time to usable functionality, defect escape rates, rework and code churn, production stability, and perceived developer flow and cognitive load as part of DevEx. [Mahapatra via InfoWorld, 2026-04-28]

    记者在紧挨着那一段里直接点出了行业态度:企业需要跳出 LoC 和 PR 数这类产出指标,转而关注质量、交付速度、开发者体验和业务影响。Greyhound Research 首席分析师兼 CEO Sanchit Vir Gogia 在同一篇里针对那个 500% 的数字补了一刀:

    Generation scales effortlessly, validation does not. As output volume rises, the burden of review, testing, and governance rises with it. [Gogia via InfoWorld, 2026-04-28]

  • 这个比例在公开可查的材料里暂未看到第三方独立复现。目前能查到的讨论里,InfoWorld 的报道以及两位受访分析师(Mahapatra、Gogia)的评论都是在引用或评价 OpenAI 自己给出的数字,并没有哪家外部企业拿自己的基线数据公开重放过"三周 PR 增加 500%"这一结论。

这四条摆在一起,这个数字不能当作「Symphony 能带来多少收益」的普适依据,更谈不上可以直接写进企业采购决策。它是 OpenAI 的一条内部观察,不是一条可复现的工程结论。

数字二,Symphony 起源那个 “3-5 sessions” 的瓶颈

InfoWorld 那篇报道里,OpenAI 把做 Symphony 的起因讲得很直白:

Engineers could manage only three to five sessions before context switching became painful, the company said, limiting the productivity gains from faster coding agents. [InfoWorld, 2026-04-28]

这个 “3-5” 不是营销数字,是一个工程瓶颈声明:Codex 再快,一个工程师同时监督 3 到 5 个会话就到头了。Symphony 要解决的就是这个上限。

把 “3-5 sessions” 和 “500% landed PR” 放在一起看,对比就很清楚:前者描述的是引入 Symphony 前的人为瓶颈上限,后者描述的是越过这个上限之后的内部短期观察。一个是工程原因,一个是业务现象,不是同一类证据。能拿来做架构决策的是前者,它解释了 Symphony 为什么存在;后者只解释了它在 OpenAI 自己内部看起来有用。

关于更进一步的内部指标(例如广为流传的"一个团队几个月写了多少代码多少 PR"这类数字),OpenAI 的 Harness engineering 博客确实有相关论述,但具体分母(人数、时间窗、仓库类型、是否含测试和生成代码、是否含 CI 返工)在公开材料里并没有完整披露。没有分母的量级数字,在决策语境里只能当注脚。

数字三,被忽视但更值得引用的那个效应

OpenAI 官方博客里另有一条论断,量纲上不如 500% 那么抢眼——当工程师不再逐个 session 监督 Codex,每次代码变更的"感知成本"会下降,原本因为"不值得花一个工程师的注意力"而一直没修的东西会开始被修 [Help Net Security, 2026-04-28]

with engineers no longer supervising individual Codex sessions, the perceived cost of each code change drops because human effort is no longer driving the implementation itself. [Help Net Security, 2026-04-28]

下面是笔者对这三个数字证据强度的比较判断,不是原报道的论断:一,“感知成本下降"这条不需要分母,描述的是一种阈值变化——从"要 > 0 个工程师注意力"变成"要 ≈ 0”;二,这条能解释为什么工单数量会上去,而不是断言上去之后就等于产出更多;三,这条和前面 “3-5 sessions” 的瓶颈构成闭环,瓶颈是"人注意力供给有限",Symphony 的作用是"让 agent 执行不再消耗这种供给",结果是"曾经被这种供给稀缺性筛掉的任务可以回到待办队列里"。

按证据强度排序三个数字:3-5 sessions(工程事实,可独立核对)> 感知成本下降(机制性论断,可推导)> 500% landed PR(无分母的短期观察)。企业评估 Symphony 的时候,前两条适合做架构决策依据,第三条只能当"内部方向正确"的弱信号。

Symphony 在 agentic coding 赛道的位置

Symphony 发布之后,立刻被拿来和一堆竞品横着比。对比结果并不像"OpenAI 又赢了一次"那么简单——它不在同一个赛道。

产品 形态 后端 Agent / 模型 编排粒度 与 Symphony 的差异点
OpenAI Symphony 开源 spec + Elixir 参考实现(Apache 2.0) 仅 Codex App Server(本地子进程) 一条 issue → 一次 implementation run,允许多轮 continuation turn
Anthropic Claude Code CLI / IDE 扩展(闭源) Anthropic Claude 系列 交互式单 agent 需要人在回路;没有开源 spec
Claude Agent SDK SDK 框架(闭源) Anthropic Claude 系列 编程式组合多 agent 是给开发者组装 agent 的 SDK,不是调度守护进程
Devin (Cognition) SaaS(闭源) 自研 harness + 前沿基础模型 单 agent 全自主 agent 的行为权在 SaaS 供应商手里
GitHub Copilot Workspace SaaS / IDE 内置(闭源) GitHub 平台选择的多模型 单任务级 深度绑 GitHub Issues 且闭源
Aider CLI(Apache 2.0) 模型无关(BYO LLM API) 文件级单 agent 无 tracker / 无后台调度
SWE-agent 开源框架(MIT) 模型无关 单 agent(偏 SWE-bench 基准评测) 偏学术 benchmark 而非生产编排
OpenHands(旧名 OpenDevin) 开源框架(MIT) 模型无关 单 agent 通用 agent 平台,和 Symphony 的"工单驱动守护进程"不在同一个抽象层

表里的对比维度都限定在能从一手文档里直接核到的那几项;各产品对 Jira / Linear / GitHub Issues 的具体支持程度各家版本迭代很快,以其官方文档为准。

Symphony 在这张表里的差异化集中在三处。

一是它是 spec,不是 SaaS。GitHub Copilot Workspace、Devin 都是 SaaS,企业用它们等于把 agent 的行为权交出去。Symphony 的 WORKFLOW.md 让企业把 agent 的行为权留在自己仓库里。

二是它把 issue tracker 升格为 control plane,而不是当成 agent 的一个输入源。SPEC 里当前只内置了 Linear adapter,但规范本身写成了 tracker-agnostic:第 4.1.1 节把 issue 字段抽象成了通用模型,第 18.2 节 “RECOMMENDED Extensions” 里也明确把 “Add pluggable issue tracker adapters beyond Linear” 写成官方的 TODO。

三是它是 Codex 的编排层,不是替代品。Agents SDK 是在代码里组合多个 agent 的 SDK,Codex CLI / Codex App Server 是单个 coding agent 的客户端与协议服务端,Symphony 填上的那一层是「让多个 Codex session 被一个常驻服务调度起来」,这一格目前为止没有等价的开源对手。

2026 年上半年一个可以观察到的现象是,主要 AI 厂商都在把护城河从"模型本身"往"工作流编排"挪。Anthropic 的做法是 Claude Agent SDK + Claude Code,Microsoft 的做法是把 Copilot Workspace 做成 SaaS 入口。Symphony 属于 OpenAI 在这条轴上的开源选项:公开一份 spec 和参考实现,让外部团队自己把 Codex 嵌进工作流。这种做法省下了 OpenAI 自己运营一个 SaaS 形态产品的成本,对企业用户也回避了数据出境、审计、SLA 依赖等典型 SaaS 形态问题。AllThings.how 对官方博客的复述里把定位说得很直接——“Symphony is not a product OpenAI plans to maintain”,Symphony 不是产品,只是参考实现 [AllThings.how, 2026-04-28]

采用前的五个问题

基于规范、Elixir 参考实现和 Harness Engineering 文章里的前置条件,Symphony 不是"装一下就能用"的东西。能让它发挥作用的前提,是下面这五个问题都有答案。

一,工单质量能不能让 agent 看懂。 Symphony 的 prompt 模板能拿到的输入很窄——只有一条 issue 本身的规范化字段(标题、描述、优先级、标签、blocked_by 等)加一个重试计数。工单本身写得含糊,agent 收到的 prompt 就含糊,产出自然翻车。OpenAI 自己也承认:

Agents can miss the mark when given ticket-level work. [OpenAI via InfoWorld, 2026-04-28]

二,代码库有没有"harness"基础设施。 Symphony 的根 README 在 “Requirements” 这一节只留了一句硬性前置条件:

Symphony works best in codebases that have adopted harness engineering.

README 没有把 harness 的具体组成在文档里展开,而是把详细定义外链到了 OpenAI 的 Harness engineering 博客。采用 Symphony 前要问的是:代码库里有没有沿着那篇博客描述的方向,把自动化约束、知识沉淀、观测与清理这些"让 agent 能在里面安全跑"的脚手架铺好。没有这层基础,Symphony 就是把一辆没修过路的车开到一条没修过路的地方。

三,愿意为 CI 稳态付多少钱。 并发上限由 agent.max_concurrent_agents 控制,Elixir 参考实现 README 的 minimal example 里填的是 10,实际默认值 SPEC 留给实现方(SPEC 把 approval/sandbox/policy 这类字段标为 “implementation-defined”,并发上限同属这一层)。每个 attempt 启动前都会跑一轮 before-run hook,首次创建 workspace 时还会额外跑一次 after-create hook,Elixir 参考实现 README 的 minimal example 直接就是在 after-create 里跑 git clone,冷启动就发生在这里。多开几路 agent 意味着 hook 脚本里塞了什么,CI / lint / 冒烟测试就会被并行触发几倍,具体压力放大倍数取决于 workflow 里 hook 和 PR 流水线的设计,但量级跟并发数同阶。Gogia 那句 “the burden of review, testing, and governance rises with it” 说的就是这件事。

四,code review 流水线扛不扛得住。 这是 Symphony 最隐蔽的成本。PR 数量上去了,review 的人没增加,会立刻出现两种退化:review 变成 rubber stamp,或者 merge queue 堵死。Gogia 的原话 “Generation scales effortlessly, validation does not” 在这里会非常具体地兑现。企业采用 Symphony 如果没有同步扩 reviewer 容量、或者引入自动化 review gate(lint / 单测 / 契约测试 / 基于规则的机器 review),瓶颈会迅速从 “agent 产出速度” 转移到 “review 吞吐”,整套投入也就在这一步卡住。

五,安全边界在哪。 Symphony 的隔离止于 filesystem workspace + Codex sandbox。hook 脚本被 SPEC 15.4 节明确标为 “fully trusted configuration”,工作流作者写什么就跑什么。如果 after-create hook 里通过 HTTPS + token 的方式 git clone,token 就会出现在 hook 所在的执行环境里(SSH 部署 key 或公开仓库场景下这条风险会降低,取决于具体凭据形态)。prompt injection 又是另一个暴露面:Linear 工单描述是用户可写的,经过 Liquid 兼容的模板引擎直接拼进 prompt。SPEC 9.5 的清洗条款只覆盖 workspace 目录名(字符集限制在 [A-Za-z0-9._-]),并没有对工单内容本身设任何消毒条款。SPEC 15.5 “Harness Hardening Guidance” 把「在外层再套一层 OS / 容器 / VM 沙箱」明确写成 SHOULD 级建议,且要求 “treat harness hardening as part of the core safety model rather than an optional afterthought”,不是可有可无的加固项。

把这五个问题摆在一起,Symphony 的工具本身只是整个引入成本的一小部分,大头摊在工单质量治理、CI 容量、review 流水线、安全边界这四块。企业级评估如果不把这四块算进去,ROI 就会在引入三个月之后被悄悄抹平。

使用形态与外溢信号

Symphony 在 OpenAI 内部的跑法,官方博客里有一句可以直接引用的表述——“Because the orchestrator runs on devboxes and never sleeps”,orchestrator 跑在常驻 devbox 上,永不休眠 [OpenAI, An open-source spec for Codex orchestration: Symphony]。博客里紧接着给出了一个被多家媒体转述的例子——“one engineer on our team made three significant changes from the Linear app on his phone from a cozy cabin on shoddy wifi”,一名工程师在信号很差的木屋里只用 Linear 手机 App 就推动了三处有分量的代码改动 [OpenAI, An open-source spec for Codex orchestration: Symphony];Help Net Security 与 Digit 都复述了同一场景 [Help Net Security, 2026-04-28][Digit, 2026-04-28]。这个场景的意义不在于"手机写代码"本身,而在于当编排层稳定、工作区持续在线之后,工程师与代码交互的形态会从"打开 IDE、连 VPN、pull 分支、跑测试、推 PR"的完整链路,退化成"在 tracker 里改一个 ticket 字段"。

外部的一条佐证信号来自 Linear 平台。Help Net Security 转述 Linear 联合创始人兼 CEO Karri Saarinen 的表态:“Linear founder Karri Saarinen highlighted a spike in workspaces created on the platform as Symphony was released” [Help Net Security, 2026-04-28];The Neuron 的 Prompt Tips 汇编页面把同一信号具体化为"Linear’s CEO Karri Saarinen reported a measurable spike in workspace creation the day it released" [The Neuron — Prompt Tips of the Day]。这两处都是二手媒体转述,笔者未能检索到 Karri 本人的一手发言,所以这条只能作为弱证据看待——这段时间新建的 workspace 未必都是为了跑 Symphony,峰值也可能叠加了其他同期事件;它只能说明"Linear 平台侧确实感知到了外溢效应",不能直接证明 Symphony 的采用规模。

两条决定未来走向的线

Symphony 能走多远,不取决于 500% 这类短期数字,取决于另外两条更慢的线。

一是 Linear 之外的 tracker adapter 什么时候落地。官方 SPEC 第 18.2 节的 RECOMMENDED Extensions 里明确列了 “Add pluggable issue tracker adapters beyond Linear”,这是官方 TODO。只要 OpenAI 自己愿意排期、或者社区有团队愿意按规范补一份 Jira / GitHub Issues / GitLab Issues adapter,这件事就会发生。这条路径有明确的对接点——SPEC 4.1.1 节的通用 issue 字段模型已经抽象好了;也有明确的外部呼声,Agentic Daily 在 2026-04-28 那篇里直接点出 “Symphony’s utility depends on issue tracker vendors and AI tooling companies implementing the specification. GitHub, GitLab, Jira, and Linear would need to add Symphony support for meaningful adoption” [Agentic Daily, 2026-04-28]

二是 Codex 之外的 agent 什么时候能接进来。这条路径在官方 TODO 里不存在。Codex App Server 协议在 SPEC 第 10 节被定位为 pass-through 的契约源头,Symphony 的规范里找不到任何"允许换成别的 agent"的设计条款。想让 Claude Code、Cursor 背景 agent、或者任意第三方 agent 接进 Symphony,只有两条实操路径:一种是社区写一个"把别的 agent 包装成 Codex App Server 兼容进程"的桥接器,另一种是再出一份并列的 spec 把 agent 接入协议抽象化。前者是黑盒 workaround,后者要更改规范,两条都是社区事项,没有来自 OpenAI 的时间表。

接下来几个季度这两条线的实际提交节奏,比任何一次短期数字披露都更能说明 Symphony 会停在「OpenAI 的 Codex 编排器」这一步,还是有机会演化成一份通用的 agent 编排规范。

参考资料