The Orchestrator's Tax
TL;DR · AI 摘要
多代理系统的真正价值在于减少协调器工作内存负担,而非单纯并行性。Martin Fowler通过案例指出,协调器自身开销可能超过子代理并行收益,需明确治理规则。
核心要点
- 协调器工作内存是稀缺资源,子代理应减少其负担而非追求并行速度
- 四个子代理案例显示协调器检查开销可能超过并行收益
- 明确治理规则(如何时委托)是优化多代理系统的关键
结构提纲
按章节快速跳转。
思维导图
用一张图看清主题之间的关系。
查看大纲文本(无障碍 / 无 JS 友好)
- 协调器税
- 核心机制
- 工作内存稀缺性
- 子代理价值定位
- 案例分析
- 四子代理开销对比
- 协调器检查成本
- 治理方案
- 显式规则制定
- 动态评估指标
金句 / Highlights
值得收藏与分享的关键句。
协调器的工作内存是稀缺资源,子代理的真正价值在于减少其负担,而非运行速度。
四个子代理案例显示,协调器检查其他代理的开销可能超过并行执行的收益。
明确治理规则(如何时委托)是优化多代理系统的关键,需避免过度依赖模糊的'大概没问题'判断。
协调器的税负
子代理通过节省时间和并行执行获得合理性,但长期多代理工作中最重要的因素并非如此。协调器上下文中的每个标记都在争夺其注意力,子代理的真实价值在于它能阻止多少内容进入该上下文,而非运行速度。我认为子代理应被视为保护协调器工作内存的工具,卸载那些不需要保留的推理任务。做好这一点意味着为协调器提供明确的规则,规定何时以及如何委派任务。这项工作基于一次真实事件,目前仍存在更多开放问题而非确定答案。
2026年7月28日
Rahul Garg
Rahul是Thoughtworks的首席工程师,总部位于印度古尔冈。他热衷于通过领域驱动设计(DDD)和整洁架构构建可维护软件,并探索AI如何帮助团队实现工程卓越。
目录
- 事件并非真正关于并行性
- 成本并非全部相同
- 稀缺资源是协调器的工作内存
- 认知局部性改变并行性的意义
- 将会话转化为固定规则
- 下一步错误将是更多治理
- 这让我陷入的境地
我正在深入一个.NET代码库的Claude Code会话时,一个疑问打断了工作。四个子代理已经在响应管道重构上运行,结果以无序方式到达,会话开始变得比代码本身更难理解。
通常这就是我停止相信模糊直觉"大概没问题"的时刻。有时问题出在代码本身,有时是架构,偶尔工作流本身值得审查。这次我决定问题出在工作流上。
起初我认为自己已经知道问题。四个子代理是否太多了?这种表述看似合理。多代理系统通常被宣传为显而易见的生产力提升,如果一个代理有帮助,四个应该帮助更多。但每个额外代理也会消耗标记、重复部分工作,并增加协调器需要协调的信息流。这看起来像是并行性和成本之间的直接权衡。
我并不想进行关于多代理系统的泛泛而谈。我想要关于这次会话的答案。因此我停止了编码工作,要求协调器尽可能客观地批评自己的委派决策。
答案并非我预期的那样。这次会话中最大的成本似乎并非来自运行四个子代理,而是来自协调器本身,特别是当它建议检查其他代理时发生的情况。
事件并非真正关于并行性
四个子代理是一次性启动的。三个有明确的运行时间:约12分钟、5.5分钟和7分钟。第四个仍在运行。从一个角度看,这已经证明了委派的合理性。三个任务并行执行,因此总耗时约为12分钟,而非如果工作串行执行可能需要的约25分钟。
但速度只是显而易见的副产品,而非有趣的部分。我试图寻找的是成本,我的第一直觉是这一定来自委派本身的重复工作:每个子代理都要读取文件、重建上下文、独立理解任务。
这并不是最大的惊喜出现的地方。
在工作过程中,协调器曾建议检查正在运行的代理。这是一个微不足道的提示:“检查一下代理。”我照做了。但工具返回的并非轻量级摘要,而是将某个后台代理的完整原始转录内容拉取到了主线程:数以万计的JSONL格式数据、中间推理过程和工具输出,被整批导入主线程。随后在第二次状态检查时,这种情况再次发生。
这里有一个重要前提。声称这种轮询行为的成本超过了四个代理的复制税,这是协调器在评估自己的错误。由于我没有真实的每次调用令牌统计,因此将这种排名视为协调器的主观判断,而非实际测量结果。可信的部分更有限:转录数据是真实的,墙钟时间记录是真实的,状态检查路径确实引入了大量可避免的成本。至于这是否是最大的成本,目前仍是一个假设,直到工具能够正确记录这些数据之前都无法确认。
更重要的是,我发现自己发现了一种原本没有预料到的成本。
成本并非都相同
当我停止将整个会话视为单一的“子代理成本”后,画面开始分裂成互不相关的碎片。
首先,四个子代理中有两个在响应管道的同一区域工作。虽然任务不同、处理的文件也不同,但两者都必须先理解相同的架构、相同的测试惯例以及大量相同的周边代码,才能开始工作。每个代理都独立承担了这一定位成本。这并不是反对委托的论据,而是说明工作被划分得过于细致。
其次,某个代理在兄弟代理于同一目录树的其他位置进行写入时,执行了git stash和git stash pop操作。虽然没有造成任何故障,但这种风险是结构性的,因为仓库范围的操作在单线程会话中是完全合理的,而一旦有多个写入者同时活跃,这种操作就变得难以合理解释。
到这时,我已列出一个不断增长的罪魁祸首清单:状态轮询、重复定位、不安全的git操作。我发现自己试图对它们进行排序。哪个成本最高?我越是尝试回答这个问题,就越不确信这种排序本身是否是正确的提问方式。
稀缺资源是协调器的工作内存
让我持续困扰的 transcript-polling 事件背后有一个与令牌成本无关的原因。令牌账单是一次性的,支付后就结束了。但这里发生的事情不同。原始转录内容在工具调用完成后仍保留在协调器的上下文中,之后的每次交互都会携带它继续前进,无论它是否仍然有用。
正是在这一刻,我意识到自己一直在将两种截然不同的成本类型视为相同。令牌只消耗一次,而上下文会影响之后的每一个决策。我关注的已不仅仅是令牌消耗,而是协调器工作内存的质量。
那是一个包含两个独立想法的结论,我希望将它们分开而不是让它们模糊在一起。第一个想法就是我刚才描述的:在上下文中残留的污染会不断消耗后续的处理能力。第二个想法与空间耗尽毫无关系。上下文中堆积的内容越多,争夺注意力的元素越多,模型就越难识别当前真正重要的信息,即使还有大量空间未被使用。更大的上下文窗口无法解决这个问题,它只是让噪音在被注意到之前有更多空间堆积。
上下文窗口只会变得越来越大,这是毋庸置疑的。关键不在于空间大小,而在于这个空间中哪些内容值得模型关注。如果子代理被正确使用,这才是真正需要解决的问题。
从这个角度看,协调器是系统中唯一能跨长时间积累理解的部分。它会记住设计决策背后的原因,延续架构约束,并知道哪些权衡已经讨论过。子代理不具备这些能力,这正是设计使然。它们本应是可丢弃的。探索过程、重复文件读取、失败的尝试以及嘈杂的中间推理都应保留在工作上下文中,永远不需要返回主线程。
认知局部性改变并行性的意义
这重新定义了重复方向的问题,它本质上不是"两个代理读取相同文件"。而是两个代理各自独立地重建相同的代码库心智模型,因为工作是按任务划分的,而不是按每个任务所需的认知需求划分的。我开始将这种区别称为认知局部性:需要相同心智模型的任务通常应该保持在一起。拆分它们只会迫使多个代理从头开始重建相同的理解。
并行性在这里仍然重要,只是不再是核心问题。同时运行四个代理是有用的,但属于常规操作。真正的价值在于它们将嘈杂的中间推理隔离在主线程之外,只返回主线程仍需要的信息。这就是子代理本应提供的隔离效果,但前提是主线程必须尊重这种隔离。
我目前的信念是:这就是子代理真正的用途。不是节省时间,而是让你能够卸载协调器不需要保留的推理过程,从而减少它需要承载的内容和争夺其注意力的元素。正确实现隔离,通过认知局部性保持局部性,子代理就会成为保护协调器工作内存的工具,而不仅仅是并行性需要容忍的成本。这只是一个信念,而非经过测量的结果。我实际测量的是另一面:隔离失败带来的代价。
将会话转化为持续规则
我的下一步行动是显而易见的。将这个教训编码到CLAUDE.md中,这是每个会话加载的持续指令文件。
在这里写一个庞大的修正策略很容易,但持续指令文件中的每一行都会在未来每次会话中重复产生成本。因此我将修复压缩成最简规则集,这些规则正好对应我观察到的失败案例。每个规则实际上都在回答同一个问题:这些信息或这种工作划分方式,是否值得保留在协调器的上下文中?
- 每波建议使用两个到四个代理。如果协调器想要启动五个或更多代理,应首先确认是否需要合并共享文件或遵循相同规范的任务。
- 当已有信息足以回答问题时,不要轮询后台代理的状态。不要为了回答简单问题而获取完整的对话记录。
- 不要在并发代理提示中允许执行影响整个仓库的Git操作。
- 将重叠的文件所有权视为整合信号,而非需要创建更多代理的提示。
这些规则并未明确告诉协调器在每种情况下该做什么。每个规则都只是在行动前提供了一些需要检查或自问的事项,而非提供可执行的脚本。单独来看,这些规则本身并不深刻。它们真正的价值在于所有规则都指向同一个目标:保持临时推理的临时性,并为协调器后续会话中需要的内容保留上下文空间。
下一个错误可能是过度治理
后续的一次会话暴露了另一个漏洞。我曾明确为协调器配置了特定技能,例如在某个场景中配置编码指导,在另一个场景中配置设计指导。我假设一旦某个技能在主线程中激活,子代理会自动继承该技能。但事实并非如此。除非协调器明确传递,子代理不会继承父会话中激活的技能。
我的第一反应是添加一个"启动前确认"机制。协调器会暂停,列出想要启动的代理及其应加载的技能,然后等待我的批准。
我很庆幸没有保留这个版本。它解决的是错误的问题。我没有证据表明缺少确认步骤导致了错误的启动计划。我发现的是关于技能传播的一个缺失事实,这属于完全不同的漏洞类型。一个通用的确认机制会让每次类似会话都增加一次往返交互,很快我就可能在不假思索的情况下开始自动批准这些提示。
到那时,我意识到自己并没有真正改善治理,只是添加了另一个仪式。
而且这个方案甚至无法解决之前的问题,即协调器污染自身上下文的问题。
更精准的修复方案效果更好。在启动前,协调器会明确说明哪些激活技能与每个代理的任务相关,并将子代理指向对应的技能文件进行加载,而非直接复制整个技能内容。只有在达到现有批量阈值或文件所有权存在歧义时才需要确认。
这让我形成了一个现在比规则本身使用频率更高的启发式方法:在向持续指令文件添加新内容前,先问自己:如果一个能力正常的协调器知道这一个缺失的事实,是否能做出正确决策?
如果答案是肯定的,规则只需陈述这个事实即可。如果修复方案开始指定决策流程(如审批、检查点、强制步骤等),这通常意味着我在用流程替代本可以通过简单澄清就能解决的问题。
目前还不清楚这个启发式方法是否能经受更复杂场景的考验。但至少现在它阻止了我将每个有趣的事件都变成微型官僚主义流程。
这让我陷入的境地
我尚未形成关于治理程度的明确观点,而且我认为这篇文章本身也没有给出明确答案。
我所拥有的是一个小型飞轮,而人类仍然稳固地处于其中心。一次会话暴露了差距。必须有人注意到这种感觉不对,暂停工作足够长的时间进行检查,判断问题是真实存在还是仅仅是噪音,并决定哪些内容值得成为固定规则。协调器可以对自己的会话进行评分并提供线索,就像这里所做的那样,但它本身无法做出判断。决定哪些内容需要被编码、哪些应该被忽略、哪些可能是过度反应,仍然取决于我。下一次会话将告诉我,这个判断是否改进了工作,还是仅仅创造了另一种形式的浪费。
这一轮的产物是当前版本的CLAUDE.md文件。它并不是一份完成的处方,而是这次迭代后校准状态的体现。其中的阈值设置——每波2到4个代理,5个作为整合信号——符合我撰写它们时所进行的工作。我不会将它们视为任何接近于普遍常数的东西,对于任何声称如此的协调方案写法我都会持怀疑态度。这些阈值也是针对Claude Sonnet 5进行校准的,而我尚未测试它们在其他模型上的表现。不同的模型可能需要不同的平衡。自那时以来,文件中又积累了一些额外的规则。请将此文件视为一个示例,而非模板,根据你实际运行的模型和工作流程对其进行修改和优化。重要的是不是具体文件本身,而是其背后的习惯:注意到失败,询问它实际造成了什么代价,并写出能够捕捉到它的规则。
多年来,我们围绕CPU、内存和吞吐量优化软件系统。第一波LLM工具教会我们关注标记。这次会话让我怀疑,在长期运行的代理工作流中,还有一个值得关注的第三要素:协调器自身工作内存的质量。这是唯一一个一旦被污染,就会在整个会话期间持续产生成本的资源。我认为这还不是一条确定的定律,但这是我迄今为止观察到的会话中保持成立的模式。
我寻找的代价从未出现在子代理上,而是在协调器上,体现在它选择继续携带的内容上。这个问题现在成为我每个多代理设计中必须面对的:不是要运行多少代理,而是什么内容值得进入协调器的上下文中。
我留下的问题都是真正开放的:
- 我该如何正确衡量这一点,而不是依赖协调器对自己错误的描述?
- 何时应将缺失的事实纳入指令中,何时又会变成过度的流程?
- 我尚未注意到的下一个协调错误是什么?
我预计我会再次修改这个文件。这感觉更像是与一个足够不透明的系统合作时,怀疑本身成为方法一部分的正常成本,而不是远见的失败。
相关工作
我之前从不同角度写过关于上下文作为稀缺资源的内容。上下文锚定理论认为,决策上下文应被外部化到一个活文档中,因为随着对话增长,对话会忘记自己的推理,且任何内容都无法跨越会话边界。那篇文章讨论的是上下文如何跨会话持续存在。而这篇文章的范围更窄。它关注的是在单个会话中保持上下文的清洁,同时多个代理同时向同一工作内存写入时如何实现这一点。
致谢
将这一事件转化为持续有效的 CLAUDE.md 规则,部分归功于 Birgitta Böckeler 的 Harness Engineering 方法论。该方法论为我当时正在运行但尚未有明确名称的循环提供了命名。一个前馈指南(即 CLAUDE.md 规则)与反馈信号(协调器的自我批评和我对本次会议的复盘)相遇,由人类主导更新过程。该文章定义了三种 Harness 类型:可维护性、架构适应性和行为特征。本文描述的通过刻意使用子代理来保护协调器传递内容的做法,属于第四种类型——编排过程本身。
感谢 Martin Fowler 在整个过程中的指导与反馈,这些帮助推动了本文思路的清晰化和表述的完善。
ChatGPT 和 Claude 在本文的编辑过程中担任了协作编辑角色,协助优化了语法、句式结构和段落组织。
重大修订
2026年7月28日: