Don't Build Multi-Agents:不要拆出多个隐形决策中心

Published on
18 mins read
--- views

Don't Build Multi-Agents:不要拆出多个隐形决策中心

原文:Don't Build Multi-Agents

一句话结论

这篇文章表面是在说“不要构建 multi-agent”,但我最后读到的核心不是反对多个智能体,而是反对:

在没有统一决策上下文、没有清晰边界、没有回收验证机制的情况下,让多个 agent 并行成为独立决策中心。

更工程化地说:

多 agent 系统的关键不是创建更多智能体,
而是治理多个智能体产生的上下文、决策和写入行为。

如果要拆 agent,不能先问“能不能并行”,而要先问:

边界是否清楚?
契约是否明确?
输出是否可验证?
冲突是否可回收?

文章核心观点

文章的两个核心原则可以压缩成:

  1. 共享上下文不是共享一条消息,而是共享完整操作记录和决策背景。
  2. 每个行为背后都携带隐含决策,多个互不感知的隐含决策会互相冲突。

我一开始觉得这个观点并不难理解:如果一个主任务被拆成两个子任务,两个子 agent 分别工作,它们可能误解主任务,也可能各自补全不同的细节,最后结果拼不回去。

后来进一步意识到,真正麻烦的不是“信息量不够”,而是:

每个 agent 都会在行动中自行补全未知部分。

也就是我用过的这个类比:

理想情况是:
1 = 0.5 + 0.5

但如果只是强行拆成两个局部任务:
1 -> 0.5 + 0.5
子 agent 会因为缺少完整语境而各自发散。

稍微好一点的做法,是给每个 0.5 补充公共背景:
0.5 + 公共信息 = 0.8

但如果没有明确边界,这个 0.8 仍然会继续自我补全未知的 0.2。
不同 agent 补全出的 0.2 可能互相冲突。

更准确地说,问题甚至不是两个 0.8,而是:

每个局部 agent 都可能把自己的 0.5 补全成一个局部的 1。
多个局部的 1,最后不一定能拼成原来的 1。

这就是文章说的 Actions carry implicit decisions:行为不是单纯执行,它会把某些判断固化到系统里。

我的阅读过程

这部分不是三个并列分层,而是我理解逐步递进的过程:

上下文断裂
-> 多个隐形决策员
-> 先切边界再决定是否拆 agent

1. 第一层:并行拆分会造成上下文断裂

最初我理解到的是:主 agent 把任务拆成多个子 agent 后,每个子 agent 只拿到局部任务,因此可能丢失原始语境。

比如主任务是一个整体功能,但子 agent 只知道自己那一小块。它不知道另一个子任务正在怎么做,也不知道主任务里哪些约束更重要。这样最后合并时就容易出现:

  • 重复实现同一个工具函数。
  • 接口命名不一致。
  • 错误格式不一致。
  • UI 风格不一致。
  • 测试和实现各自猜了不同接口。

这一层可以理解成“上下文缺失导致拼接失败”。

2. 第二层:上下文断裂背后是多个隐形决策员

继续读以后,我把问题理解成:

多 agent 并行时,危险点不是多个 agent,而是多个隐形决策员。

每个 agent 在执行任务时都会做很多局部判断:

  • 这个接口叫什么?
  • 这个状态放在哪里?
  • 是复用旧组件还是新建组件?
  • 错误应该抛异常还是返回错误对象?
  • 公共方法应该抽在哪里?
  • UI 应该紧凑还是卡片化?

这些判断不一定出现在 prompt 里,但会体现在代码、文档、接口和测试中。

如果多个 agent 同时做这些判断,而且彼此不可见,就会产生“隐含决策冲突”。最后主 agent 合并时看到的不是几个独立产物,而是几套局部世界观。

所以第一层和第二层说的其实是同一个问题的两个角度:表面上是上下文缺失,深层是每个子 agent 会在缺失处自行决策。

3. 第三层:不是不要拆,而是先切边界再拆

文章真正给我的启发不是“不要创建多个智能体”,而是:

不要先拆 agent,再让 agent 自己理解边界;要先从上下文中抽取边界,再决定是否拆 agent。

错误顺序:

任务
-> 拆 agent
-> agent 各自补上下文
-> 各自生成隐含决策
-> 合并冲突

更稳的顺序:

任务
-> 统一决策上下文
-> 提炼目标、契约、边界、权限、验收
-> 判断是否可拆
-> 派发受控任务
-> 回收验证
-> 主 agent 合并

这一步让我意识到:context engineering 不是简单保存记忆,也不是把所有材料塞给 agent,而是把混乱上下文加工成可执行、可约束、可验证的任务结构。

最终沉淀:一条主线、四层设计、五步流程

一条主线

设计多 agent 系统时,主线不是“怎么拆更多 agent”,而是:

先建立统一决策上下文,再决定哪些任务可以被边界化地派发。

也就是说,主 agent 的第一职责不是发号施令,而是把原始需求整理成:

  • 为什么要做。
  • 要做成什么。
  • 哪些决策已经确定。
  • 哪些问题还没确定。
  • 哪些地方不能乱改。
  • 最终怎么验收。

四层设计

这四层是系统结构,不是执行步骤;后面的五步流程,才是一次任务具体怎么流动。

1. 主控层:Coordinator / 主 Agent
2. 共享上下文层:Decision Ledger / Context Store
3. 执行层:Specialist Agents / 子 Agent
4. 验证层:Validator / Reviewer

1. 主控层

主控层负责理解完整背景、维护决策账本、拆任务、分配权限、最终合并。

它要回答:

这个任务为什么存在?
最终要实现什么?
哪些公共契约要先定下来?
哪些任务可以拆?
哪些任务必须串行?
哪些结果可以进入主线?

2. 共享上下文层

共享上下文层不是普通记忆库,也不是把全部材料塞给所有 agent,而是从主线上下文中压缩、结构化出来的“决策账本”。

里面应该沉淀:

  • 目标。
  • 约束。
  • 接口契约。
  • 数据模型。
  • UI / 代码规范。
  • 已确认决策。
  • 未决问题。
  • 任务状态。
  • 验收标准。

它不是一次生成后就结束,而是在执行过程中持续更新。子 agent 读取的也不应该是完整大上下文,而是这份决策账本中和自己任务相关的切片。

3. 执行层

执行层由多个 specialist agent 组成,比如:

  • 前端 agent。
  • 后端 agent。
  • 测试 agent。
  • 代码审查 agent。
  • 文档 agent。
  • 研究 agent。

但子 agent 不是另一个老板,而是受控的执行单元。它可以分析、建议、生成候选 patch,但不应该随意把自己的隐含判断写成最终事实。

4. 验证层

验证层负责把子 agent 的结果重新拉回主上下文检查:

  • 是否符合接口契约。
  • 是否违反权限边界。
  • 是否引入重复抽象。
  • 是否和其他 agent 冲突。
  • 测试是否通过。
  • 是否有未解决风险。

所以稳定架构不是:

主 agent -> 多个子 agent -> 直接合并

而是:

主 agent
-> 决策上下文
-> 受控子 agent
-> 验证 / 回收
-> 主 agent 合并

五步流程

这五步是一次任务如何流过系统。

1. 需求归一
2. 契约定义
3. 判断可拆
4. 派发任务
5. 回收、验证、合并

1. 需求归一

不要一上来拆 agent。先把用户输入整理成:

  • 背景:为什么要做。
  • 目标:做成什么。
  • 成功标准:什么叫完成。
  • 异常情况:哪些错误路径必须处理。
  • 非目标:哪些东西这次不做。

这里的“背景”不能只是泛泛说项目背景,而要拆成可执行的信息。

2. 契约定义

把会影响多个模块的公共决策先定下来:

  • 接口格式。
  • 数据结构。
  • 错误格式。
  • 状态流转。
  • UI 交互规则。
  • 代码规范。
  • 测试标准。

这些是公共决策,不能让子 agent 各自猜。

3. 判断可拆

能不能拆,不看任务大不大,而看边界清不清楚。

可拆的标准:

任务边界清楚
输入输出清楚
写入范围清楚
和其他任务低耦合
结果可以独立验证
冲突可以回收

如果这些不清楚,就先由主 agent 串行推进,不要开多个 agent。

4. 派发任务

子 agent 拿到的不是一句“去做登录”,而是一个任务包:

你的角色是什么?
你要完成什么?
你能看什么上下文?
你能改哪些文件?
你不能做什么?
你必须遵守哪些契约?
你最后输出什么?
遇到不确定问题怎么办?

也就是说,子 agent 的自由度要被边界化。

5. 回收、验证、合并

子 agent 的结果不能直接成为最终事实。

它要回到主 agent,由主 agent:

  • 检查冲突。
  • 验证契约一致性。
  • 运行测试。
  • 处理未决问题。
  • 再合并进入主上下文。

这一步会慢一点,但比错误合并后的返工成本低。

可以做分级验证:

风险等级验证方式
低风险检查输出格式和文件范围
中风险检查契约一致性和局部测试
高风险完整测试、代码审查、主 agent 手动合并

登录功能例子

如果要用多 agent 系统实现登录功能,不能直接说:

前端 agent 去写页面
后端 agent 去写接口
测试 agent 去写测试

更稳的做法是先由主 agent 定义公共层:

背景:为什么需要登录,登录后进入哪里,失败时如何反馈。
目标:支持 email / password 登录,成功返回 token 和 user。
验收:正常登录、密码错误、用户不存在、接口异常都要覆盖。
契约:POST /api/login,统一 request / success / error 格式。
约束:复用现有 UI 组件、auth service、错误处理和测试框架。

然后再判断是否可拆。如果可拆,子 agent 的任务应该是边界化的:

前端 agent:只做登录页、表单校验、接口调用和错误展示。
后端 agent:只做 auth endpoint / service,并遵守接口契约。
测试 agent:只基于同一份契约写正常和异常用例。

最后由主 agent 回收检查:

前端、后端、测试是否遵守同一份接口契约?
有没有 agent 越界修改公共抽象?
测试是否覆盖一开始定义的验收标准?

这个例子的重点不是登录本身,而是:先定义公共契约和边界,再让小型角色单元分别行动。

和 Building Effective Agents 的关系

这篇文章可以直接接上 Building Effective Agents 里的“最小智能单元”理解。

这篇文章补上的关键提醒是:

小型智能单元一旦并行,就必须治理它们的边界、决策权和写入权。

所以“最小智能单元”不是越多越好,而是要在清晰任务边界、共享契约和验证机制下使用。

和上下文工程的关系

这篇文章也能和 Effective Context Engineering for AI Agents 串起来。

我现在对 context engineering 的理解不再只是:

保存记忆
压缩历史
检索相关资料
塞进 prompt

这些只是底层动作。更高级的上下文工程是:

把混乱的上下文加工成可执行的任务边界、角色边界、行为边界、写入边界。

也就是说,上下文不是直接拿来喂给 agent 的。它要先经过加工:

原始上下文
-> 提炼目标
-> 提炼约束
-> 提炼已做决策
-> 提炼未决问题
-> 切出任务边界
-> 定义输入输出
-> 判断是否启用子 agent

这比“共享上下文”更进一步:

不是把整体上下文全部塞给子 agent,
而是把整体上下文加工成清晰切片,再分发给对应 agent。

所以 context engineering 在 multi-agent 里的作用,是把原始需求变成:

  • 目标。
  • 契约。
  • 边界。
  • 权限。
  • 输入输出。
  • 验收标准。

然后再决定是否拆 agent。

我自己的可复用原则

前面的主线、四层设计和五步流程已经是主要方法论。这里再压缩成几个判断:

  • 不是有任务就拆,而是有清晰边界才拆。
  • 公共接口、数据模型、UI 风格、错误格式、公共抽象等关键决策要先由主上下文确定。
  • 子 agent 可以做局部判断,但必须限制在自己的角色、文件、权限和输出边界内。
  • 子 agent 如果产生新假设,要显式上报,不能直接写成最终事实。
  • 最终写入权和合并权要集中,最好通过沙箱 / 隔离分支产出候选变更,再由主 agent 验证后合并。

最终记忆版

多 agent 可以分工劳动,
但不要分裂判断中心。

可以共享任务,
但必须共享决策账本。

可以让子 agent 提建议,
但不要让它们各自把假设写成事实。

或者压缩成一句:

多 agent 不是把一个大脑拆成多个小脑,而是让一个决策中心调度多个受限执行单元。

后续阅读关注点

继续读 agent 架构文章时,可以优先看三个问题:

  1. 它怎么组织上下文?
  2. 它怎么限制决策权?
  3. 它怎么验证子任务结果?

这三个问题比“它用了几个 agent”更重要。