多 Agent 不是拉群聊天:我用黑板、仲裁器和对抗评审拆了一次协作实验

#AI Agent#Multi-Agent#Agent 测试 共 5,900 字 约 19 分钟

多 Agent 最容易做成一场很贵的群聊。一个模型说“我建议先分析需求”,另一个模型说“我同意”,第三个模型补一句“我们还要注意测试”,几轮之后 token 花了不少,任务没有推进多少。

我第一次做多 Agent 协作时就是这样。角色名很完整:Planner、Researcher、Coder、Reviewer。看起来像一个小团队,实际每个 Agent 都在重复读上下文、重复表达常识,真正落到工具和证据上的动作很少。

后来我改了一个判断标准:多 Agent 协作不能看对话热不热闹,要看它是否比单 Agent 更好地解决了信息分工、错误发现和风险控制。如果不能,它只是把一个不稳定系统扩展成多个不稳定系统。

多 Agent 的问题不是角色太少,而是共享状态太差

最朴素的多 Agent 是轮流发言:

Text
UTF-8|4 Lines|
Planner: 我们应该先读需求。
Researcher: 我找到了相关文件。
Coder: 我准备修改实现。
Reviewer: 我认为需要测试。

如果所有信息都靠聊天文本传递,问题很快出现。

第一,关键信息埋在长对话里。Reviewer 想确认 Coder 是否读过某个文件,需要翻整个上下文。

第二,事实和意见混在一起。Researcher 说“可能是 date parser 问题”,这是猜测;工具返回“parseEssayDateInput 在 schema 中被调用”,这是事实。聊天里二者很容易被同等对待。

第三,角色之间没有权限边界。Reviewer 也能改代码,Coder 也能宣布评审通过,最后谁负责什么并不清楚。

所以我放弃了“群聊中心”的结构,改成共享黑板。每个 Agent 不直接把所有想法丢给另一个 Agent,而是把结构化结果写到黑板上。

黑板保存事实、假设和待办,不保存闲聊

共享黑板可以理解成协作状态库:

Text
UTF-8|11 Lines|
Facts:
工具确认过的事实,例如文件路径、测试结果、函数引用

Hypotheses:
当前解释和候选根因,必须引用事实

Tasks:
待执行节点、负责人、状态和依赖

Artifacts:
补丁、测试输出、评审意见、最终报告

一个 Researcher 找到相关文件,不是说一句“我觉得这些文件有用”,而是写入:

JSON
UTF-8|10 Lines|
{
  "type": "fact",
  "id": "fact_17",
  "source": "search_files",
  "content": {
    "path": "src/utils/date-only.ts",
    "symbol": "parseEssayDateInput",
    "referenced_by": ["src/content.config.ts", "tests/date-only.test.ts"]
  }
}

Coder 提出修改假设时,必须引用 fact_17。Reviewer 反驳时,也应该引用事实或测试结果。

黑板的重点不是形式,而是把协作从“谁说了什么”改成“系统知道什么、还缺什么、谁负责下一步”。这样多个 Agent 才不会反复讨论已经确认的事实。

角色拆分应该按能力边界,不是按人类职位幻想

我一开始照搬人类团队角色:产品、架构师、开发、测试。后来发现这不适合模型。模型角色应该按工具权限和评测目标拆。

例如一个代码任务可以拆成:

角色主要权限输出
Locator只读搜索和读文件相关文件、符号、调用链
Patch Writer读文件和写候选补丁diff、修改假设
Test Runner运行允许的测试命令测试输出、失败摘要
Reviewer只读 diff、trace 和测试结果阻断意见或通过理由
Arbiter读所有结构化结果决定继续、回滚或请求人工

这里 Reviewer 没有写权限,Patch Writer 不能自己宣布通过,Test Runner 不负责解释需求。权限边界让角色分工有实际意义。

如果所有 Agent 都有同样的上下文和工具,只是 system prompt 不同,那它们很容易互相污染。一个“Reviewer”也可能忍不住写补丁,一个“Coder”也会为自己辩护。角色名不能当边界,工具权限才是边界。

仲裁器不是更聪明的模型,而是决策规则的集中点

多 Agent 最难的是冲突。Coder 说补丁合理,Reviewer 说不合理,Test Runner 说相关测试通过但全量构建没跑。谁决定下一步?

我不想让所有 Agent 无限争论,所以加了 Arbiter。它的职责不是写最多文字,而是按规则做决策:

Text
UTF-8|5 Lines|
如果测试失败 -> 回到 Patch Writer,附失败摘要
如果 Reviewer 阻断且引用事实成立 -> 回到对应节点
如果 Reviewer 只是风格建议 -> 可继续但记录建议
如果写操作超出权限 -> 请求用户确认
如果迭代次数超过预算 -> 停止并报告当前状态

Arbiter 可以由模型辅助判断语义冲突,但规则优先。例如测试失败就是失败,不能因为 Coder 解释得好听就通过。禁止工具被调用也是失败,不能由模型投票决定。

这个设计让多 Agent 不再像民主讨论,而像一条带评审关卡的流水线。听起来没有“智能群体”那么浪漫,但更可控。

对抗评审比互相同意更有价值

多 Agent 如果只是互相补充,很容易变成重复确认。我更看重对抗评审。

Reviewer 的提示里不应该写“帮助 Coder 完成任务”,而应该写:

Text
UTF-8|3 Lines|
你的任务是寻找补丁中的错误、证据缺口和测试不足。
如果没有足够证据,不要为了推进任务而通过。
你必须引用 diff、测试输出或 trace。

Reviewer 需要检查:

  • 补丁是否真的对应用户需求
  • 是否修改了无关文件
  • 是否删除或放宽测试
  • 是否缺少回归测试
  • 是否只修症状不修根因
  • 最终解释是否夸大验证范围

这类评审和安全测试类似。它默认补丁可能有问题,而不是默认大家一起完成任务。

我也给 Reviewer 限制输出格式:

JSON
UTF-8|10 Lines|
{
  "decision": "block" | "pass" | "comment",
  "findings": [
    {
      "severity": "high",
      "evidence": "diff modifies test expectation only",
      "recommendation": "fix parser implementation instead"
    }
  ]
}

结构化输出能让 Arbiter 判断是否阻断。否则 Reviewer 写一大段“总体不错,但建议……”很难转成执行动作。

多 Agent 成本会平方增长,必须控制通信

多个 Agent 最容易爆 token。每个角色都读完整上下文、完整 diff、完整测试日志,几轮之后成本上升很快。

我做了几条限制。

第一,角色只看自己需要的信息。Locator 不需要看补丁,Patch Writer 不需要看完整对话历史,Reviewer 需要看 diff、测试和关键证据,但不需要所有搜索结果。

第二,黑板内容分层。事实可以很多,但传给模型的是筛选后的相关事实。原始工具输出存引用,需要时再展开。

第三,通信通过 artifacts,不通过长篇对话。Coder 输出 diff 和修改假设,Reviewer 读这两个 artifact。不要让 Coder 先写一篇心路历程。

第四,限制协作轮数。比如一个补丁最多经历三轮 Coder 与 Reviewer 循环。超过后停止,报告当前阻塞原因。

多 Agent 不是越多人越好。每增加一个角色,都要问它是否带来新的工具权限、新的检查视角或新的失败隔离。如果只是多一个模型复述同样内容,就应该删掉。

多 Agent 的测试要看协作是否发现单 Agent 漏掉的问题

评测多 Agent,不能只看最终成功率。否则它可能只是更贵地完成同样任务。

我会设置几类对照:

Text
UTF-8|14 Lines|
单 Agent 能完成:
多 Agent 不应显著增加成本和时间

单 Agent 容易漏测试:
Reviewer 应该阻断缺少回归测试的补丁

单 Agent 容易被注入诱导:
权限隔离应阻止非授权角色执行工具

单 Agent 容易修症状:
对抗评审应发现只改测试不改实现

信息分散任务:
Locator 和 Patch Writer 分工应减少无关文件读取

报告要比较:

Text
UTF-8|8 Lines|
成功率
阻断真实错误次数
误阻断次数
平均 token
平均工具调用
平均迭代轮数
最终补丁大小
未验证声明数量

如果多 Agent 成功率只提高 2%,成本翻三倍,还增加很多误阻断,就不值得。多 Agent 的价值必须用数据证明,而不是靠架构图证明。

我踩过的坑:Reviewer 会被 Coder 的自信解释带偏

有一次 Coder 写了一个补丁,测试没覆盖到真正边界。它在说明里写得很完整,说“该修改保持了原有行为,只调整异常输入”。Reviewer 读完解释后通过了。

后来我检查 trace,发现 Reviewer 没有读关键 diff,只读了 Coder 的解释。

我把 Reviewer 输入改成强制顺序:

Text
UTF-8|4 Lines|
1. 先读用户需求
2. 再读 diff
3. 再读测试输出
4. 最后读 Coder 的解释

并且要求每条 finding 引用 diff 或测试,而不是引用 Coder 的自述。

这很像人类 code review。PR 描述可以帮助理解,但不能替代读代码。模型 Reviewer 也一样。让一个模型审另一个模型,不能让它只审对方的作文。

并行探索适合信息收集,不适合抢着写补丁

多 Agent 一个真正有用的地方是并行探索。比如一个测试失败可能来自配置、数据处理或 UI 层,可以让几个只读角色分别调查:

Text
UTF-8|8 Lines|
Config Scout:
检查配置、环境变量和构建脚本

Code Scout:
检查失败栈附近实现和调用链

Test Scout:
检查测试意图、fixture 和断言

它们都只有只读权限,把事实写入黑板。然后 Arbiter 汇总事实,决定哪个方向最值得继续。

我不喜欢让多个 Coder 同时写补丁。并行补丁看起来提高命中率,实际会带来合并冲突、测试成本和评审成本。除非任务很明确,且每个候选补丁能在隔离工作树里独立验证,否则多个写入角色会让系统变乱。

并行探索的关键是去重。三个 Scout 可能读到同一个文件,黑板需要合并相同事实,而不是让后续模型看到三份重复证据。事实可以按来源、路径和符号去重,假设则保留多个候选。

并行也要有预算。不能因为有三个 Agent,就把每个 Agent 的搜索预算都开到单 Agent 的水平。总预算应该固定,再分配给角色。否则多 Agent 的成本会自然膨胀。

黑板需要并发控制,否则事实会互相覆盖

多个 Agent 同时写黑板,会出现并发问题。一个角色标记任务完成,另一个角色刚写入新的阻断事实;一个角色更新假设,另一个角色基于旧假设生成补丁。

我给黑板事件加版本号:

JSON
UTF-8|7 Lines|
{
  "board_version": 42,
  "event": {
    "type": "hypothesis.added",
    "depends_on": ["fact_17", "fact_21"]
  }
}

写入时检查版本。如果黑板已经变化,角色需要重新读取相关摘要再提交。这样可以避免基于过期状态做决定。

任务状态也不能随便覆盖。节点从 in_progressdone,必须带完成证据;从 done 回到 blocked,必须说明新证据是什么。状态转换仍然是运行时控制,不是 Agent 在黑板上写一句话就生效。

这些听起来像后端系统细节,但多 Agent 一旦并行,就必须面对。否则所谓协作只是多个模型轮流改同一份笔记,最后谁覆盖谁看运气。

多 Agent 不是替代测试,而是生成更多需要测试的路径

多 Agent 看起来更谨慎,但系统状态也更多。每个角色的输出、黑板写入、仲裁规则、权限隔离、轮次控制,都可能出错。

所以多 Agent 本身也要测试:

  • Locator 是否只写事实,不写未经验证的结论
  • Patch Writer 是否只能写候选补丁,不能改真实工作区
  • Reviewer 是否能阻断已知坏补丁
  • Arbiter 是否按规则处理测试失败
  • 黑板是否拒绝没有证据引用的假设
  • 角色是否只能访问自己的工具
  • 轮次预算是否能停止争论

如果不测这些,多 Agent 只是把错误藏在更多层里。单 Agent 至少错得直接,多 Agent 错起来可能每个角色都说自己没问题。

多 Agent 的记忆要隔离,避免错误共识扩散

多 Agent 还有一个不明显的问题:错误会通过共享上下文传播。一个角色提出错误假设,其他角色如果直接读到它的自然语言解释,可能会把它当成事实。几轮之后,整个系统形成错误共识。

所以我不让所有角色共享完整聊天记录。它们读取黑板里的事实和被标注的假设。假设必须带状态:

Text
UTF-8|11 Lines|
proposed:
刚提出,未验证

supported:
已有部分证据支持

rejected:
被测试或事实否定

accepted:
被 Arbiter 采纳为当前工作方向

角色看到 proposed 假设时,不能把它写进最终答案。Reviewer 甚至应该优先检查这些假设是否证据不足。

这有点像科研里的引用规范。事实、推测和结论要分开。多 Agent 如果不区分这三者,就会出现一群模型互相引用未经验证的说法,最后生成一个很自信的错误报告。

记忆隔离还包括工具输出。某个角色读到了未经信任的网页内容,不应该把里面的指令传播给其他角色。它只能提取事实,并保留来源和可信度。否则 prompt injection 可以通过黑板扩散,从一个 Researcher 影响到 Coder 或 Arbiter。

什么时候不该用多 Agent

我现在会先问三个问题。

第一,任务是否真的需要多个视角?简单问答、小范围格式修改、确定工作流,不需要多 Agent。

第二,角色之间是否有不同工具权限或检查目标?如果没有,只是换 system prompt,收益有限。

第三,多 Agent 是否能被评测证明更好?没有对照实验,不要默认复杂架构更高级。

很多时候,一个单 Agent 加一个确定性 Verifier,比三个 Agent 互相聊天更可靠。比如 JSON schema、测试退出码、路径权限,这些不需要模型评审。用代码检查即可。

多 Agent 适合这些场景:

Text
UTF-8|5 Lines|
信息收集和补丁生成需要分离
生成者容易自我辩护,需要独立评审
任务风险高,需要对抗性检查
多个工具权限不能放在同一个角色
需要并行探索多个候选方案

即使在这些场景里,也应该从最少角色开始。两个角色能解决的问题,不要上五个。

我最后保留的最小多 Agent 形态

经过几轮实验后,我保留的最小结构其实很克制:

Text
UTF-8|11 Lines|
Locator:
只读,负责找证据

Worker:
根据证据生成候选产物,例如补丁或报告

Reviewer:
只读候选产物、证据和测试结果,负责阻断

Arbiter:
按规则推进状态,控制预算和确认

很多任务甚至可以把 Locator 和 Worker 合并,只保留 Reviewer 和 Arbiter。只有在信息收集很复杂时,才拆多个 Locator。

这个结构比“十个专家 Agent”朴素得多,但更容易测试。每个角色的输入输出清楚,权限清楚,失败模式也清楚。系统能回答:谁提出了假设,谁写了补丁,谁检查了补丁,谁决定继续。

我也开始把角色数量当成成本,而不是卖点。每多一个角色,就多一份 prompt、上下文、权限、评测和日志。没有明确收益,就不要加。

协作报告应该暴露分歧,不只给最终结论

多 Agent 最终给用户的报告,不应该把所有分歧抹平。如果 Reviewer 阻断过一次补丁,后来 Coder 修正通过,报告里应该说明:

Text
UTF-8|4 Lines|
第一版补丁只修改了测试断言,被 Reviewer 阻断。
阻断原因是没有修复 parseEssayDateInput 的根因。
第二版补丁修改了解析逻辑,并新增回归测试。
相关测试已通过。

这不是增加废话,而是展示系统怎样避免错误。对面试官或项目 reviewer 来说,这种过程比一句“最终修复完成”更有价值。

如果仍有分歧,也要保留。比如 Reviewer 认为缺少全量构建,Arbiter 因预算停止,那么最终报告应该写“相关测试通过,但未跑全量构建,Reviewer 保留风险意见”。这比强行给一个完美结论更可信。

多 Agent 的优势之一,就是能产生独立检查意见。最终输出如果只剩一个圆滑总结,就浪费了这个优势。

多 Agent 的线上观测要按角色拆开

如果多 Agent 上线后只看总耗时和最终成功率,排查会很痛苦。一次任务失败,可能是 Locator 没找到证据,Worker 写错产物,Reviewer 误阻断,也可能是 Arbiter 规则太保守。总指标看不出责任位置。

我会按角色记录指标:

Text
UTF-8|11 Lines|
Locator:
平均读取文件数、有效事实比例、重复事实比例

Worker:
候选产物通过率、补丁大小、被阻断原因

Reviewer:
真实阻断数、误阻断数、漏检数、平均评审耗时

Arbiter:
回滚次数、请求人工确认次数、预算终止次数

这些指标能帮助决定是否保留某个角色。比如 Reviewer 阻断了很多真实错误,值得保留;如果它主要产生风格评论,拖慢任务,还很少发现实际问题,就应该收紧 rubric 或移除。

观测也要串起 trace id。一个最终报告里的结论,应该能追到 Worker 的产物、Reviewer 的意见、Locator 的事实和工具原始结果。否则多 Agent 只是把黑盒拆成几个小黑盒,表面更复杂,实际更难调。

我还会看角色之间的信息传递成本。如果每次 Reviewer 都需要读取完整黑板,说明黑板摘要做得不好。如果 Worker 经常请求 Locator 已经提供过的信息,说明事实索引或任务分配有问题。这些不是模型能力问题,而是协作协议问题。

这些角色指标也能反过来指导评测集。如果线上经常因为 Reviewer 误阻断而失败,离线 eval 就要增加“合理补丁被错误阻断”的样例。如果 Locator 有效事实比例低,就要补充更复杂的仓库检索场景。观测不是上线后的报表,而是下一轮测试数据的来源。

我希望多 Agent 系统能形成这样的闭环:线上观测发现薄弱角色,离线评测复现问题,角色提示词、权限或黑板协议被修正,再用同一批 case 验证。没有这个闭环,多 Agent 的复杂度很难被控制,也很难长期持续稳定维护。

从单 Agent 到多 Agent,核心变化是治理结构

这篇已经比前面更往上。它不再只讨论一个 Agent 怎样规划、读仓库和改代码,而是讨论多个不稳定组件怎样协作。

我的结论是:多 Agent 的重点不是“多个模型”,而是治理结构。黑板管理事实,角色权限限制动作,Reviewer 做对抗检查,Arbiter 按规则裁决,评测台证明协作是否带来收益。

如果没有这些结构,多 Agent 只是群聊。它会产生更多文字、更多 token、更多看起来合理的建议,但不一定产生更可靠的结果。

我现在更愿意把多 Agent 当成一种工程手段,而不是智能程度的象征。单 Agent 能稳定完成,就不要拆。拆的时候,要说清楚每个角色解决什么失败模式,怎样防止它越权,怎样评估它的贡献。

下一阶段如果继续做应用,我会把这套多 Agent 结构放到具体任务里:代码修复、日志诊断、文档问答和测试生成。真正有价值的不是“我用了几个 Agent”,而是它们能不能在复杂任务里发现单 Agent 漏掉的问题,并且用可控成本完成。