多智能体编排器选零Worker可能有两个完全不同的原因:路由决策(决定不值得委托)vs 发现失败(根本不知道有编排工具)。区分二者比继续跑benchmark更有价值。
上周,我写了一个出乎意料的结果。
我构建了一个多智能体编程编排器,故意制造了本应有利于并行化的工作负载,然后看着 supervisor 在全部六次自由选择规模的运行中都选择了零个 worker。
当我强制委托时,在我所测量的工作负载上,worker 仍然没有超过 supervisor 单独工作的表现。
当时我的主要结论是:
最优的 worker 数量可以是零。
我仍然相信这一点。
但在继续构建和实际使用这个系统之后,我发现另一个问题隐藏在相同的症状背后。
有时"零个 worker"意味着:
Supervisor 看到了编排工具,认为委托不值得。
其他时候意味着:
Supervisor 根本没有发现编排器。
这是完全不同的失败模式。
这个区别改变了这个项目,比再跑一轮基准测试要有意义得多。
两种截然不同的零 worker 情况
在我上篇文章的基准测试中,发现从来不是问题。
提示词明确告诉 supervisor 存在 delegate_task 和 delegate_tasks 选项。
所以当它选择零个 worker 时,这是一个路由决策。
它有委托的选项。
而且在我所测量的工作负载上,强制它委托并没有产生证据表明这个决定是错的。
但正常使用是不同的。
我不想每次 Codex 会话都以这种方式开始:
Use sol-luna-orchestrator.
Use three workers.
Run them in parallel.
Give one High effort and two Medium.
如果我每次都必须手动设计 worker 拓扑,那我并没有真正构建一个编排器。
目标是安装工具一次,打开一个普通的 Codex 会话,描述工程任务,然后让父级决定:
任务
|
v
发现编排指导
|
v
委托值得吗?
|
+--> 否 -> 独自工作
|
+--> 是 -> 一个有边界的任务
| 有依赖的任务按顺序
| 独立的任务并行
|
v
审查证据
在新的会话中,这种情况并不能持续发生。
CLI 安装的原始发现提示实际上说的是:
当委托可能有帮助时,考虑使用编排器。
"考虑"的问题在于,父级首先必须知道有什么东西值得考虑。
如果它从不查看 MCP server,就永远不会看到详细的路由指导。
所以这不是委托策略问题。
而是发现问题。
发现必须发生在路由决策之前
我改变了引导行为。
sol-luna-orchestrator init 现在安装一个小型的托管指令,告诉新的 Codex 会话首先发现已配置的编排器,然后使用实际指导在以下选项中选择:
按顺序运行有依赖的任务
并行运行独立的任务
重要部分在于这个提示没有说的内容。
零个 worker 仍然明确有效。
不好的做法:
我应该委托吗?
|
v
也许我应该找一个工具?
现在的做法:
发现编排策略
|
v
理解可用的执行模式
|
v
现在决定委托是否有意义
我希望发现能力更强,而不要把它变成强制扇出。
这是两个独立的决策。
然后我在一个真实任务上试了它
发布 v0.8.0 后,我想在基准测试工具之外测试它。
所以我在另一个仓库中打开了一个新的 Codex 会话。
那个仓库是我的 GitHub profile README。
它已经过时了,尤其是关于我在 Sol-Luna 上做的开源工作,我希望智能体研究我当前的公开工作并将 profile 重写为一个简洁的着陆页。
重要的是提示词。
我只是简单地要求它检查仓库、从主要来源研究相关公开工作、验证说法、更新 README、检查链接,并保留未提交的更改。
父级自主发现了编排指导。
然后它做出了一个相当合理的拆分。
广泛的公开来源调查是一个实质性的、有边界的研究任务,所以它委托给一个 Luna worker,设置为 High effort。
新的 Codex 会话:父级自主发现了编排指导,选择了一个有边界的研究 worker,而没有被告知要委托。

Worker 被明确设为只读。
父级保留了实际的编辑决策、综合判断、README 编辑、来源检查和最终审查。
后来,当我要求它优化刚写好的 README 时,它明确选择了零个 worker。
它的推理基本上是:剩余的变更涉及一个简短的、紧耦合的文件,而且已经被理解了。
所以在一个真实的工作流中,我同时得到了:
广泛的独立研究
|
v
1 个有边界的 worker
紧耦合的最终优化
|
v
0 个 worker
这更接近我启动这个项目时的目标。
不是"总是多智能体"。
我并不是声称这次运行证明了委托更快或更便宜。
没有单独的对照。
这不是基准测试。
它所展示的更窄:一个新的会话自主发现了编排器,没有被引导向它,在看到清晰的边界时使用了 worker,后来在没有看到时拒绝了使用。
这比简单检查工具调用在技术上是否可行,是一个更有用的验收测试。
下一个问题是上下文
一旦 worker 真正开始接收有用的任务,另一个显而易见的问题出现了:
Supervisor 的上下文有多少应该给它们?
简单的答案是"全部"。
我越来越认为这是错误的答案。
完整的对话
架构历史
更广泛的产品上下文
处理一个有边界任务的 worker 通常不需要所有这些。
但给得太少又会造成另一个问题:它必须重新发现父级已经知道的信息。
所以我添加了结构化的 Context Capsule(上下文胶囊)。
Worker 可以接收选定的字段,例如:
相关上下文
接口
依赖项
不变量
上游决策
已知陷阱
目标不是上下文最大化。
而是上下文分配。
给 worker 关于问题的最小有用视图。
事实证明,这在另一个方向也同样适用。
Supervisor 也不需要接收每个字节的返回
委托的任务返回证据。
最初,成功的验证可能会将大量常规命令输出发送回父级的上下文。
但一旦检查通过,那些输出很少再有用。
父级通常需要的是:
验证是否通过
是否超出了范围
worker 的声明是否与观察到的变更匹配
任何需要关注的差异或失败
所以 v0.7.0 添加了 Compact Evidence Packet(紧凑证据包)。
成功的验证 stdout/stderr 可以从结构化结果中移除,而失败内容保持完整。
这给了我一个更通用的规则:
好的多智能体系统需要双向优化上下文。
Worker 不应该接收整个 supervisor 历史。
Supervisor 不应该接收 worker 产生的每个常规字节。
有用的信息在中间的某个地方。
Worker 的 PASS 仍然只是一个声明
这可能是我现在在这个项目中最关心的设计原则。
假设一个 worker 返回:
PASS
Changed:
- src/auth.ts
- src/auth.test.ts
Tests passed.
这看起来令人安心。
但它仍然是生成的文本。
所以 Sol-Luna 将 worker 的报告视为一个声明,而不是最终真相。
Worker 报告的 PASS 被视为需要检查的证据,而不是最终结论。

编排器可以独立地:
观察哪些文件实际发生了变更
将观察到的编辑与声称的编辑进行比较
检查声明的文件范围
检测集成冲突
重新运行白名单中的验证命令
向父级报告差异
然后父级仍然拥有最终审查权。
信任链变成:
Worker 执行任务
|
v
Worker 报告发生了什么
|
v
编排器独立观察和验证
|
v
父级审查证据
|
v
决策
这与以下方式故意不同:
Worker 说 PASS
|
v
发布
真实世界的 README 运行甚至在这个逻辑中发现了一个小缺陷。
研究 worker 有一份明确只读的合同。
它正确地没有修改任何文件。
但编排器仍然发出了一条通用警告:
Worker 声称 PASS 但未记录到文件变更。
对于实现任务来说,那条警告可能是有用的。
对于一个明确禁止编辑任何内容的调查,没有文件变更正是预期结果。
所以这次运行产生了一个新的待办事项:从任务契约的意图而不是单纯从结果来分类零变更结果。
这才是我希望真实使用场景能暴露的那种 bug。
并行 agent 最终会变成普通的并发工程
从构建 worker 路径中还得到了另一个教训,与语言模型关系不大。
并行 worker 使用隔离的 Git worktree。
有一段时间,批处理偶尔会失败,即使 worker 本身没有任何问题。
问题是 git worktree add。
多个进程同时修改 .git/worktrees 下的共享元数据。
故障很罕见。我大约每千次 worktree 创建会出现一次。
这使得它足够烦人因而真实存在,但又足够罕见在常规测试中隐藏。
修复方法不是更好的提示词。
而是同步。
修改共享 worktree 元数据的操作现在被序列化了。
Worker 执行保持并行。
这个经历是一个有用的提醒:一旦你在 AI agent 周围构建了足够的基础设施,很多困难的 bug 就变成了非常普通的软件工程问题:
向后兼容性
模型只是系统的一部分。
如果 worker 并行运行,我还需要知道它们在做什么
在更长的委托过程中,另一个问题变得很明显。
从普通的 Codex 界面来看,一个 worker 可能正在运行一段时间,而父进程可能看起来什么都没做。
sol-luna-orchestrator activity --watch
它显示如下内容:
活跃和总 worker 数
模型和推理投入
最初这听起来像一个简单的可观测性功能。
然后隐私问题出现了:
活动日志具体应该包含什么?
早期的活动输出暴露了委托任务中远多于必要的信息。

重新设计的视图保留了操作信号:worker、投入、状态、时长和验证状态。

一个有用的活动源不需要 worker 的完整目标、提示词、上下文、源代码或成功的命令输出。
所以当前的事件流故意排除了这些。
Task ID 是不透明的,而不是从任务描述中派生的。
父进程可以选择提供一个简短的活动标签,但这被记录为本地可见的,因为它会被持久化。
还有一个单独的诊断日志,包含更多细节,那个文件被明确视为敏感的。
Sol-Luna 不会远程传输任何内容。
两个文件都是本地的。
对我来说更广泛的教训是:
Agent 可观测性几乎立即成为一个隐私设计问题。
"记录一切"在提示词和源代码可能涉及的情况下不是一个好的默认选项。
我还不得不纠正我自己的架构描述
早期版本的项目将 GPT-5.6 Sol 描述为监督者。
这是我个人的使用方式,但实际上它并不是一个架构要求。
父进程现在明确与模型无关。
任何具有 MCP 访问权限的兼容 Codex 父进程都可以进行监督。
Worker 端仍然是 GPT-5.6 Luna。
这个纠正也迫使我清理了我一直在围绕成本进行的另一个论证。
我一直在这样思考:
昂贵的父进程
+
便宜的 worker
=
可能的节省
但这只有在用户选择的父进程模型在当前定价表上实际高于 worker 时才有意义。
这不是架构的通用属性。
原始 token 数量也不是成本。
一个系统可以使用更多原始 token 但消耗更少积分,反之亦然,取决于涉及的模型。
所以项目现在做出更有限的声明:
委托有协调成本。Worker 经济取决于你实际选择的模型。没有测量到实际成本节省。
我宁愿弱化一个声明,而不是保留一个实现或数据不再支持的声明。
项目现在的样子
基本哲学仍然很简单:
不是每个任务都应该被委托。
但围绕这个想法的机制已经增长了很多。
父进程现在可以选择:
0 个 worker -> 单独执行
1 个 worker -> 一个有边界的任务
N 个顺序 worker -> 依赖/共享工作
N 个并行 worker -> 独立/分离工作
每个 worker 有自己的推理投入。
并行 worker 使用隔离的 worktree。
任务有声明的作用域。
验证独立重新运行。
声明的变更和观察到的变更被比较。
Worker 收到结构化的上下文而不是完整的对话转储。
成功的证据可以在返回父进程之前被压缩。
新的会话可以发现编排策略而无需用户命名工具。
单个和批量委托可通过 CLI 观测。
重要的是,父进程仍然拥有最终判断权。
v0.8.0 现已在 npm 上线。
在我写这篇文章时,npm 显示过去七天有 659 次下载。
我故意称之为下载,而不是用户。包下载可能包括 CI、重装、自动活动和其他不能一对一映射到人的事情。
不过,对于一个我最初因为想在自己的 Codex 工作流中使用而构建的东西,看到其他人拉取这个包还是很酷的。
我下一步想构建什么
我不认为下一步是"添加更多 worker"。
路线图目前走向不同的方向。
如果一个 worker 已经调查或实现了某件事,父进程应该能够继续那个 worker 的线程,而不是每次从头开始。
如果验证发现了一个 narrow 的问题,同一个 worker 应该能够接收证据并进行有边界的修正。
不是无限的自主循环。
有理由的重试和投入升级
超时、理解需求错误、实现失败和环境问题不应该都触发相同的重试行为。
系统应该在决定下一步做什么之前对失败进行分类。
自适应 worker 路由
最终我希望监督者不仅选择投入和 worker 数量,还要在用户授权的 worker 能力池中进行选择。
但我故意把这个放在路线图的后面。
在存在延续和有界修复之前将失败的任务路由到更昂贵的模型,这主要是一种复杂地花更多钱在相同失败上的方式。
恢复循环需要先变得更智能。
我改变主意的事情
当我开始构建 Sol-Luna 时,有趣的问题看起来是:
如何一次运行几个编码 agent?
我不认为这还是有趣的问题了。
运行几个 agent 相对容易。
更难的问题是:
监督者是否发现了编排策略?
这个任务应该被委托吗?
干净的任务边界在哪里?
Worker 实际上需要什么上下文?
父进程实际上需要什么证据回来?
Worker 的声明可以被独立检查吗?
验证失败时会发生什么?
可观测性应该持久化多少信息?
协调开销什么时候根本不值得?
新的会话 README 任务是一个小例子,但我喜欢发生的事情。
一部分工作有一个干净的研究边界。
监督者使用了 worker。
下一部分紧密耦合且已经被理解。
监督者使用了零个 worker。
没有规则说更多的 agent 一定更好。
只是两个不同的工作部分有两个不同的执行决策。
这越来越是我认为编排应该意味着的东西。
项目在 GitHub 上开源:
mahadansar/sol-luna-orchestrator
npm install -g sol-luna-orchestrator
sol-luna-orchestrator init
然后打开一个新的 Codex 会话并正常工作。
你不需要选择 worker 数量或手动调用编排工具。
如果父进程决定正确的 worker 数量为零,那可能正是你想要的。
我的第一次实验教会了我:
更多的 agent 不是自动更好的。
之后的工作教会了我更广泛的东西:
委托本身是简单的部分。使委托值得其复杂性才是真正的工程问题。
我认为这是我想要继续推进的方向。