单 Agent 就够了
自 GPT-4 时代以来,模型的智力与泛化能力已经经历多次飞跃,提示词和工具设计带来的差距在前沿模型下也正被抹平。但是,模型上下文目前依然有限,且长上下文下模型会出现上下文腐烂(Context Rot),在面对长程任务时,上下文工程(Context Engineering)已经成为了当前 Agent 设计的重中之重。
关于上下文管理的具体含义,已经被很多文章透彻分析过,这里不再详细讲解,推荐 AWS 的 Agentic AI基础设施实践经验系列(九):Context Engineering 上下文工程. 我将直接开始对比目前主流 Coding Agent 的上下文管理策略,并说明,为什么我选择了单 Agent 作为"执行者"。
自动压缩 - 单执行者,保守、合理、简单
作为两个用户量断层领先的 Coding Agent, 无论是 Claude Code 还是 Codex, 默认状态下都一致地选择了保守而简单的做法:接近上下文的上限之后,进行一次性的自动压缩(Auto-Compaction),将整个会话总结成概要,并以概要作为新的上下文继续工作。这种方法的缺点似乎很明显:从一个已经长对话浓缩到一个很短的概要,一定会丢失信息……吗?
自动压缩的缺点并不那么简单,而是,模型目前缺乏良好的总结能力,或者说,有着长 Context 的 Agent 目前难以推断出,一个没有这些上下文的 Agent,究竟需要哪些信息。最容易在压缩过程中被错误丢掉或扭曲的,一方面是用户和 Agent 对话过程中做出决策的过程,另一方面是编写代码的过程中所做的一些隐性决策。而用户的意图,以及 Agent 行动过程中的隐性决策,在执行任务时非常重要,它们内化了隐性约束,这些约束实际上让 Agent 可以按用户预期完成任务,但在实际使用中,自动压缩难以保全这些隐性知识,这似乎是 Agent 的一种知识的诅咒。
不过,真实的机制可能更复杂,如果你常用这个功能,就会发现有时自动压缩之后的对话质量意料之外地差,模型似乎以某种未知的方式不可逆地变"笨"了,这可能与概要中的思维/推理断层有关。
这种情况下,对于需自动压缩、甚至跨多个自动压缩的长程任务,Agent 的实现无法避免地逐渐走样,我们只能尽可能用系统提示词强化自动压缩的表现。Claude Code 做了特别设计,他们在 prompt 中要求:自动压缩时,要详细列出所有的用户消息。这种设计尽可能地保留用户的决策以及决策的意图不会被遗落,一定程度上缓解了该问题。
多 Agent 协作 - 多执行者,模仿人类
在过去几个月,尤其是 Claude Code 推出 Agent Team 功能之后,多 Agent 并行协作,成为了最热门的 Agent 设计理念之一,但这种模仿人类的工作方式的实现,并未取得明显成功。
首先,在多 Agent 协作流程中,一个事无巨细的计划,只是并行工作的起步标准。假设我们要求两个 Agent 按照计划分别开发同一个应用的两个页面,计划中规划了风格、每个页面的功能、字体… 但不可能约定每一个实现细节——否则写计划的 context 成本将比直接实现还高。结果,同样的列表加载,一个页面用骨架占位,另一个却用转圈动画;同样的输入框,一个用小圆角,另一个却用大圆角,且各自造了轮子。每个页面似乎都合理,但拼凑到一起之后,处处不同的交互逻辑和视觉效果摧毁了用户的体验。
Agent 之间的沟通系统是最困难的部分。人类没有硬性的上下文容量限制,思考、完成任务的速度也有限,沟通得以在工作间隙自然发生。但是,Agent 收到的每条消息都会永久写入上下文,并且,由于 Agent 的实现速度太快,中途发现问题时再沟通将变得异常困难,所以沟通几乎只能发生在计划阶段。几个 Agent 互相同步各自的决策,每条消息在每个 Agent 的上下文里各存一份,加上各自的回应和评估,上下文的消耗速度远超实际工作的产出。沟通越充分,每个 Agent 留给真正做事的空间就越少。最坏的情况是,某个 Agent 的上下文在协作途中被耗尽、触发了自动压缩,它将带着残缺的记忆继续参与共同的任务,不仅自身的工作质量下降,还可能把错误的理解传播给其他 Agent。不沟通,各做各的拼不到一起;沟通,又会迅速耗尽做事的空间。这是并行协作在 Agent 架构下的根本困境。
所以,人类的协作过程中,哪一部分可以拿来借鉴?人类沟通、分工、各自完成自己的工作,关于"沟通"的部分,我将在子 Agent 不写代码这一节中详细分析,前面已经分析过的是——并行完成工作在 Agent 系统里非常困难,那么分工,然后串行完成呢?
Droid 做了这样的尝试。
依次执行(根据计划) - 多执行者,各自为政?
Droid 的 Mission 功能采用了先计划、后串行实现的编排路线,Orchestrator(架构师)负责与用户进行对话并商讨出详细的计划、验收契约,计划好之后,让独立的 Runner 根据计划,派出 Worker 执行每个子任务节点,并在每个里程碑达成后派 Validator 对这一阶段进行 Review.
这类架构的首要问题仍是隐性知识的丢失。决策时的意图和意图中包含的隐性约束,能否被计划完美地传达到 Worker? 下一个 Worker 能否真正理解上一个 Worker 行动中所做的隐性决策?计划不可能覆盖所有细节,Worker 在实现中必然会做出一些计划之外的判断,比如选择某种数据结构,或者在两种等价方案中选了其中一种,这些决策的原因不会被下一个 Worker 显式获悉,却会影响后续 Worker 的工作。在缺少这些隐性知识的情况下,每个 Worker 只能"各自为政",写出能运行的代码,但更难在前一个 Worker 的基础上做好优化,甚至可能做出相互矛盾的设计选择。
偏离一旦发生,纠正代价很高。Worker 被要求完成特定的子任务,即使它意识到前面的实现有可以优化的地方,也不会主动转向去修正,因为那并非它的工作。更坏的情况是,前面某个 Worker 的实现已经部分偏离了计划,后续 Worker 看到已有代码,容易把它当作权威的实现继续向下开发,导致偏离被放大。而 Orchestrator 只在里程碑处通过 Validator 获得反馈,等它发现问题时,可能已经有好几个 Worker 在错误的方向上堆叠了大量代码,此时重写契约、回滚重做的成本将很高。并且,Validator 也只能根据计划审查代码,如果 Worker / Validator 自己对计划的解读和 Orchestrator 的实际意图有冲突,当 Orchestrator 发现时,很可能为时已晚,此时再重写一整套契约来修?或者接受这个没那么完美的结果?
另一个潜在的问题是 over-engineering. Droid 为了控制较为复杂、容易失控的多 agent 架构,在中间的过程中制定了大量的规范,把几乎所有能固定的流程都进行了固定,通过大量的硬性约束来规范整套流程。比如当 Worker 无论是正常做完还是认为有问题需要反馈,都不会通过输出的方式向上级反映,而是通过 EndFeatureRun 工具的字段 returnToOrchestrator 来控制自己的 handoff 是否由系统直接转交给 Orchestrator. 较重的系统设计显著提升了系统运行的稳定性,保证了系统能长时间稳定运行,但重流程同样有代价:它增加模型认知负担(特别是对于不同智力、做事风格的模型,引入同一套系统带来的结果很可能相差较大),也限制了系统的灵活性,想要做什么都要写好计划、写好契约… 但这只能保证 Agent 执行稳定,不能保证实现方式更好。
但是,Droid 的 Mission 功能仍是当前最合理的多执行者设计,串行执行的路线避免了最严重的并行协作的混乱,重流程也确实能让系统持续运行数小时而不崩溃。
Claude Code 在我撰写这篇文章的过程中,推出了 Dynamic Workflows / Ultracode, 这是一种较为落后的实践方式,类似一个轻量化、定制版、动态化的 LangGraph, 过去已经有非常多相关实践。它完全没有解决上述问题,包括隐性知识的丢失与中途纠偏能力的缺失(灵活性问题)。尤其是一次性编排+派发后只等待返回(同步等待)的机制,让它大幅丧失了普通多 Agent 系统的灵活性,虽然可以写一些条件分支/while, 或是由子 Agent 返回的清单在运行中决定 Agent 派发的数量, 但编排路线本身必须在每次运行前写定,子 Agent 一经派出,与主 Agent 之间就只剩一次性的返回值,没有任何运行中的沟通与协商。换句话说,为了避免"沟通混乱"带来的上下文占用,它直接禁止了主 Agent 在运行过程中干预流程。这使它作为一个"多执行者"体系处境尴尬:论流程约束,远不如 Mission 系统齐全;论灵活性,又比不上 Agent Team 系统。
唯一的好处似乎是:能够避免多 Agent 编排的中间过程占用上下文。因此,该系统只对少数场景有价值,如自动化生产高质量的 review(派出多个 Reviewer 审查代码的不同部分,随后给每个 Reviewer 配一个 Adversarial Agent 来做交叉审查),但是,居高不下的成本,让这个功能的性价比变得很低。
Fermi 的自我压缩 - 单执行者,更灵活,但尚需实践验证
无论是单 Agent 的 auto-compaction, 还是多 Agent 协作的编排,其面临的问题都是隐性知识能否传递,但目前的模型似乎还并不擅长做这种任务,常常过于强调细节,而忽略背后的决策逻辑,这也是为什么 Worker / Executor 需要一个持久、尽可能完整的大脑来处理任务。
因此,在 Fermi 中,我引入了自我压缩的工具来帮助单执行者应对长程任务,为了防止全量自我压缩带来的对用户意图的破坏,以及对决策、计划过程中的思路的破坏,我采用了更细粒的压缩,让 Fermi 能够做到单工具级别的压缩,从而在不压缩关键思路和对话的情况下,对部分没那么重要的地方进行大幅压缩,例如:
- 对特别大的 tool call/tool result 进行压缩,从大量信息精炼出少部分真正对未来工作有用的部分
- 在 auto-research 任务中,在每次探索实验之后压缩本次探索的详细过程,包括脚本写法和运行过程,只在 summary 中保留实验思路、脚本的简单介绍、实验结果以及一些对未来实验有用的启示和 insight
- 在涉及到大型仓库多个部分的修改时,每完成一个或几个部分就做一次压缩
通过这种方式,Fermi 避免了压缩和用户的讨论过程,尽可能保留了决策过程,避免隐性约束的丢失。目前,Fermi 的自我压缩分为两种:
- 用户手动使用 /summarize 进行压缩,通过这种方式,用户可以选择压缩连续的 turns(一条用户消息和对应的 Agent 的行动和输出),并可以给出自定义压缩指令;
- Fermi 自己进行压缩,但此过程被限制在单个 turn 内部,也即:用户消息是不可被压缩的,Fermi 无法跨用户消息进行压缩。
但自我压缩目前还存在不少未确定的问题:
- Agent 能否合理地判定出什么是需要被压缩的?避免过量压缩或压缩不足?能否合理地选择压缩的时机?
- Agent 能否写好 summary, 保留真正"对未来有用"的信息?这个部分面临着和 auto-compaction 一样的问题,但是范围更小
- 每次压缩都会造成一定的缓存命中的破坏,频繁压缩会导致额外花费
- 误压缩之后无法自我恢复,最重要的是:误压缩之后,由于丢失了一部分不该压缩的上下文,Agent 反而更难意识到"误压缩"这件事,导致无法有效纠正
子 Agent 不写代码
目前为止,最行之有效的子 Agent 的工作方式仍是不直接修改代码,仅承担其余工作,如下:
- 子 Agent 非常适合探索类工作,能够避免主 Agent 在不知道代码仓库具体结构的情况下过度探索,包括过量的文件搜索和读取。Explorer 能够代替主 Agent 完成相应工作,只按照主 Agent 的要求给主 Agent 一个干净的探索结果。
- 子 Agent 同样很适合作为 Reviewer 工作,独立的 Reviewer 能够识别出更多实现中的盲点。
事实上,正如 Cognition 的 Walden Yan 在博客 Multi-Agents: What's Actually Working 中所提到的,子 Agent 作为 Reviewer 的效果出乎意料地好,尤其是在和执行者完全不共享上下文的情况下。他给出的解释是:Reviewer 被迫从实现反推而非根据 spec/用户指令推进,以及长上下文的 Context Rot 两个因素共同作用。但还可能存在的原因是:LLM 受到的训练更多是逻辑严密的推理(reasoning),训练中可能不会要求它自己找出自己的错误(因为训练者想要训出第一次就输出最优质代码的模型,而不是输出之后自己纠错的模型),这导致:它在自己的代码中找 Bug 相比让 Reviewer 来找 Bug 更困难,也就是代码是 role="assistant" 还是 role="user" 所引起的 LLM 行为的不同。
- 人类协作中,"沟通"不仅是简单的确定计划的过程,我们往往能在沟通中互补,甚至提出更好的思路,这一点在 Agent 协作中同样适用。在计划时的"头脑风暴"阶段(尤其是复杂计划),启动多个子 Agent 进行规划是合理的,由于现在大多数 provider 的后端把温度设置为 1.0, 多个子 Agent 给出的方案往往能够实现互补,对于复杂的工程任务,比单个 Agent 的一次性计划往往更有效,盲点更少。
- 咨询工作:使用弱模型执行工作,在碰到困难、复杂问题的时候咨询强模型,Anthropic 有一篇相关文章,显示了方法的有效性,但该路径的难点是:搭载了弱模型的 Agent 需要能在恰当的时候对强模型进行咨询,并能够提供恰当的上下文供参看。太频繁时,会消耗太多高级模型的 token, 得不偿失,不如直接使用高级模型;咨询不足,也就是"不知道自己不会做",或是无法提供良好的上下文,则一样难以生效。另一个例子是 Ampcode 的 Oracle, 用了同样的路径。不过,有趣且反直觉的是,在我的经验中,即使并非"更强大的模型",而是相同等级、稍微弱一些的模型甚至相同模型,往往也有咨询价值,在一个 Agent 无法找到问题所在时,重开一个 session 来专门针对该问题下手,可能很快就能找到并解决这个问题。这很可能是"不同模型采样分布的不同"和"干净的上下文"共同作用的结果,这也是 Sub-Agent 真正有奇效的地方之一。
建议给 Sub-Agent 设置多种不同的模型,Fermi 提供了三种 Tier 可配置,当碰到困难问题时,主 Agent 将自动选择 Tier-High 的模型作为 Sub-Agent. 我自己常用的配置:主 Agent - DeepSeek V4 Pro max, High - Codex/GPT 5.5 xhigh, Medium - DeepSeek V4 Pro max, Low - Codex/GPT 5.4 mini.
- 还有一种特殊实现:Spawn Sub-Agent 的时候,把当前进程的完整上下文(或者省略了 tool result 的上下文)一起传入 Sub-Agent, 通过这种方式,Sub-Agent 也能拿到完整的思路和用户意图。有两种具体的做法:
- 把这部分上下文包裹起来作为一个整体传入 Sub-Agent(类似 auto-compaction summary, 只是作为一条语义上来源于 system, 上下文组装时作为 role="user" 的信息来传入),这种方式更节省 token, 因为可以省略 tool result
- 直接 fork 主 Agent, 然后传入系统提示,说明"你是被 fork 的 Agent"
在这种方式下,主 Agent 不断一路 fork 自己直接执行各项子任务,或许也是一种选择?由于无需压缩与传递,这种方式不会有隐性知识的问题。目前 Codex 设置了这种方案,Fermi 将在不久的将来支持该功能。
写在最后
"执行者必须只有一个"……吗?其实,多执行者体系并非天生有错,只是:当前模型的瓶颈在于隐性知识无法可靠传递——无论是自动压缩时传给"未来的自己",还是协作时传给别的 Agent。单执行者 + 干净上下文的辅助 Agent(探索、审查、咨询),是该瓶颈下最可靠的设计。但是,如果模型真摆脱了"知识的诅咒",本文大部分结论便都可以重写了——但在那之前,还请先保住执行者的大脑。