Agent 工程学习站 做棵大树 出品 · beatree.cn 免费 · 无需登录 · 进度存本机
II 版 · Harness 工程 第 06 章 Orchestration
II · Harness 工程 06 / 22 深入

Subagent 与多智能体编排

Orchestration
预计阅读 34 分钟
难度 深入
关键词 Subagent · Supervisor
本机状态 未读

「什么时候该多开一个 Agent,什么时候那是自找麻烦」——这是 Agent 架构里最容易答错的一道题。正确答案不是「复杂任务就该多 Agent」,而是三个具体条件:上下文隔离、并行提速、权限隔离。三个都不满足,多开 Agent 只会让系统更难调试、更贵、更慢。

编辑部注 · 本章的核心判断题(为什么用 Subagent 而不是长上下文单 Agent)在真实面经中出现过,请重点看第二节。

先说一个反直觉的结论:大多数「感觉该上多 Agent」的场景,正确的解法是把单 Agent 的上下文管理做好。多 Agent 引入了进程间通信、上下文传递损失、结果汇总冲突、调试难度指数上升这四项成本,只有当它换来的收益明确大于这些成本时才值得。

一、三种收益,对应三种拆分方式

收益什么时候成立拆分方式典型场景
上下文隔离子任务会产生大量只要用一次、但会严重污染主上下文的中间输出子 Agent 只把结论返回给父 Agent深度检索:读了 30 篇文档,主 Agent 只需要 5 条结论
并行提速子任务之间彼此独立、无数据依赖同时派生多个子 Agent,最后汇总对 8 个候选方案各做一次可行性评估
权限隔离某些操作需要更高权限或更强的沙箱约束用独立的 Agent 持有不同工具集读数据的 Agent 与能写生产库的 Agent 分开

注意:只有第一种收益是「结构性」的——它解决了单 Agent 无法解决的问题(上下文容量)。第二种收益可以用并发工具调用实现,第三种收益可以用工具级权限控制实现。所以真正必须上多 Agent 的场景,比想象中少。

二、为什么用 Subagent,而不是长上下文的单 Agent

这道题在真实面经里出现过,它是本章最值得背下来的答案框架。

同一个任务,两种结构 A · 单 Agent + 长上下文 上下文里累积: 系统指令 + 工具定义(固定) 文档 1 全文(4,000) 文档 2 全文(4,000) 文档 3 全文(4,000) …… 文档 30 全文(4,000) 共 12 万 token,其中真正需要的结论约 500 字 每一轮都要重新送一遍 → 成本平方级上涨 信噪比 0.4%,注意力被 99.6% 的噪音稀释 B · Subagent 拆分 Subagent 1 读文档 1-10,产出结论 Subagent 2 读文档 11-20,产出结论 Subagent 3 读文档 21-30,产出结论 各自的上下文 用完即释放 父 Agent 的上下文 只装 3 段结论(约 600 token) 信噪比接近 100%,前缀稳定 成本:3 次并行子调用,各自一次性 主循环上下文稳定 → 缓存命中率极高 答题框架:为什么用 Subagent(可直接背) ① 上下文隔离:子 Agent 的中间过程用完即弃,父 Agent 只接收结构化的结论,避免主上下文被噪音稀释。 ② 并行提速:彼此独立的子任务可以同时跑,墙钟时间从「相加」变成「取最大」。 ③ 权限隔离:读操作与写操作、可信区与不可信区分属不同 Agent,缩小能力边界。 代价必须一起说:上下文传递有信息损失、结果汇总可能冲突、调试与 trace 难度显著上升。
图 1 关键差别不在「有几个 Agent」,而在父 Agent 的上下文里装的是什么。A 装的是 12 万 token 的原始材料(信噪比 0.4%),B 装的是 600 token 的结论(信噪比接近 100%)。这个对比本身就是这道题的答案。

三、编排形态:三种模式与各自的坑

形态结构适合主要风险
主管制(Supervisor)父 Agent 分解任务、派生、汇总任务分解清晰、子任务同质父 Agent 成为瓶颈与单点;汇总时信息压缩损失
流水线(Pipeline)A 的输出交给 B,B 交给 C阶段界限清晰的加工流程上游错误被逐级放大且难以定位;某一环卡住整体阻塞
评审制(Critic)生成者 + 评审者交替循环质量要求高、可验证的产出容易陷入「改了又改」的无进展循环,必须设轮数上限
评审制最常见的失控方式

生成者与评审者互相不满意,来回修改十几轮——每次修改都「看起来有道理」,但整体质量并不提升。这在日志里表现为一轮正常的对话,实际上已经烧掉大量预算。

修法三件套:① 评审必须有明确的通过标准(rubric),而不是「你觉得好不好」;② 设最大轮数上限;③ 每轮必须产生实质变化(改动点可枚举),否则判定为无进展、直接退出。这与第 2 章的无进展检测是同一个思路。

四、结果汇总:比想象中难

多 Agent 的成果最终要合到一起,这里有三个具体的坑:

第一,格式不一致。 三个子 Agent 返回三种结构,父 Agent 得先做格式归一。解法:子 Agent 的输出必须强制结构化(固定字段的 JSON),而不是自由文本。

第二,结论冲突。 两个子 Agent 对同一问题给出相反结论。解法:不要静默选一个,要让父 Agent 显式处理——要么用可判定的依据(时间、来源权威性)裁决,要么在最终产物里同时呈现两种观点及其依据。

第三,信息损失。 子 Agent 的结论太简略,父 Agent 无法判断其可信度。解法:结论里必须带最小必要证据——一句话的依据、来源标识、以及该子任务的置信度。

subagent_protocol.pypython
from dataclasses import dataclass, asdict
from typing import Literal
import json
@dataclass
class SubResult:
    """
    子 Agent 返回给父 Agent 的唯一格式。
    设计要点:带证据、带置信度、带来源——否则父 Agent 无法判断该不该采信。
    """
    sub_task: str
    status: Literal["ok", "partial", "failed"]
    conclusion: str                 # 结论,尽量短
    evidence: list[str]             # 支撑结论的最小证据(每条一句)
    sources: list[str]              # 来源标识,可点开核对
    confidence: float               # 0-1,子 Agent 自评
    tokens_used: int = 0
    caveats: list[str] = None       # 已知局限 / 未覆盖范围
    def render(self) -> str:
        """父 Agent 看到的形态:结构化、紧凑、可核验"""
        return json.dumps(asdict(self), ensure_ascii=False, indent=1)
def dispatch(sub_tasks: list[str], run_subagent) -> list[SubResult]:
    """
    并行派发。注意三点:
      1. 每个子任务必须有明确的产出要求(否则子 Agent 会返回无法使用的东西)
      2. 失败不能中断整体(partial 也要收)
      3. 汇总前先做冲突检测
    """
    results: list[SubResult] = []
    for t in sub_tasks:
        spec = (f"{t}\n\n"
                f"要求:只返回结论 + 最少必要证据 + 来源 + 你的置信度(0-1)。"
                f"不要返回过程、不要返回原始文档全文。")
        try:
            results.append(run_subagent(spec))
        except Exception as e:
            results.append(SubResult(
                sub_task=t, status="failed",
                conclusion=f"子任务执行失败:{type(e).__name__}",
                evidence=[], sources=[], confidence=0.0))
    return results
def merge(results: list[SubResult]) -> dict:
    """汇总:先归并,再显式暴露冲突,绝不静默选边"""
    ok = [r for r in results if r.status == "ok"]
    partial = [r for r in results if r.status == "partial"]
    failed = [r for r in results if r.status == "failed"]
    # 低置信度结论不进主结论,只作为参考项
    strong = [r for r in ok if r.confidence >= 0.7]
    weak = [r for r in ok if r.confidence < 0.7]
    conflicts = detect_conflicts(strong)
    return {
        "primary": [{"conclusion": r.conclusion, "sources": r.sources} for r in strong],
        "reference": [{"conclusion": r.conclusion, "confidence": r.confidence} for r in weak],
        "unresolved": conflicts,        # 交给父 Agent 或用户裁决,不自己选
        "partial": [r.sub_task for r in partial],
        "failed": [{"task": r.sub_task, "reason": r.conclusion} for r in failed],
        "coverage": f"{len(ok)}/{len(results)} 个子任务完成",
    }
def detect_conflicts(results: list[SubResult]) -> list[dict]:
    """
    冲突检测:真实系统里可以用语义比对或让父 Agent 判断;
    这里用一个可插拔的占位实现,重点是**把冲突显式暴露出来**。
    """
    found = []
    for i in range(len(results)):
        for j in range(i + 1, len(results)):
            a, b = results[i], results[j]
            if contradicts(a.conclusion, b.conclusion):     # 占位
                found.append({"a": a.conclusion, "b": b.conclusion,
                              "a_sources": a.sources, "b_sources": b.sources})
    return found
def contradicts(a: str, b: str) -> bool:
    return False        # 占位:真实实现用语义模型或规则

五、什么时候不要用多 Agent

这份清单比「什么时候用」更值钱:

情形为什么不该拆更好的做法
子任务之间有强数据依赖串行依赖下并行收益为零,只增加通信开销单 Agent,按顺序执行
子任务产出很小拆分的收益(隔离噪音)小于成本(通信 + 汇总)直接在主 Agent 里调用工具
需要全局一致性判断每个子 Agent 都只看到局部,无法做全局判断单 Agent 持有全局视图
调试期、问题还没有定位多一层派生就多一层不确定性,排查成本剧增先让单 Agent 跑通,再考虑拆分
延迟敏感且并行度低进程启动与上下文传递带来的固定延迟可能超过收益单 Agent + 并发工具调用

六、自测

本章自测第 1 题来自真实面经
Q1用 Subagent 而不是让单 Agent 读全部材料,最主要的收益是什么?
C。答案是上下文隔离,这是唯一「结构性」的收益(单 Agent 无论怎么优化都无法让 12 万 token 的材料不占上下文)。顺带说清成本逻辑:Material 只在子 Agent 里出现一次,主循环前缀稳定、缓存命中率高,总成本反而下降。D 是典型的错误直觉。
Q2评审制(生成者 + 评审者)最常见的失控方式是?
A。这是最隐蔽的一种:每一轮改动都「看起来有道理」,日志里是一串正常的对话,质量却没有提升。对策是给评审明确的通过标准(rubric)+ 轮数上限 + 每轮必须有可枚举的改动点,与第 2 章的无进展检测同理。
Q3两个子 Agent 对同一问题给出相反结论,父 Agent 应该怎么处理?
D。静默选边是在假装自己知道答案——这在评测中会被记为「看似自信的错误」,比明确的「不确定」危害更大。正确做法与记忆系统的冲突处理一致:能裁决就裁决(并说明依据),不能裁决就把冲突交出去。

七、小结

议题结论
该不该拆只有满足上下文隔离 / 并行提速 / 权限隔离之一才拆
最主要收益上下文隔离(父 Agent 只装结论,不装材料)
三种形态主管制 / 流水线 / 评审制,各有对应的失控方式
汇总三坑格式不一致、结论冲突、信息损失
冲突处理绝不静默选边;能裁决就裁决并说明依据
上线顺序先让单 Agent 跑通,再按需拆分

多 Agent 不是能力升级,是上下文管理的一种手段。把它当成能力升级来用,就会在不需要的地方引入三个新的失败面。本刊编辑部

下一章处理把能力交出去之后必须面对的问题:怎么守住边界。

面试官会怎么问

共 3 条 · 其中 1 条高频 · 先自己答一遍,再展开对照

为什么要用 Subagent,而不是一个长上下文 Agent 一直做下去? 高频
三个理由,按重要性排序:
① 上下文隔离——如果所有子任务都塞进同一个上下文,会互相污染(无关信息干扰判断),而且上下文会迅速膨胀到需要频繁压缩,压缩就会丢信息。Subagent 各自持有干净上下文,只装载与自己任务相关的信息。
② 并行提速——互相独立的子任务可以并发执行,端到端时间从求和变成取最大值。
③ 权限与提示隔离——不同子任务需要的工具权限不同(读数据的和写数据库的不该有同样的能力),分 Agent 天然形成权限边界;同时每个 Subagent 可以有专门化的提示词,比一个"什么都会"的通用提示更稳。
但要多说一句:跨 Agent 传递只走结构化产物(文件、JSON 结果),不要用自然语言转述,否则会引入"传话失真",这是多 Agent 系统最常见的翻车方式。

追问链

  1. 部分子任务失败了怎么办?
  2. 结果汇总阶段会不会又变成一个长上下文问题?
多 Agent 怎么协作?有哪些组件?
一个可用的最小架构包含六部分:
① Orchestrator(调度与规划,负责拆任务、下发、汇总);
② Subagent Pool(执行单元,各自独立上下文与能力集);
③ 通信层(结构化消息:任务目标 + 约束 + 输入产物指针 + 期望输出 schema);
④ 结果汇总层(按 schema 校验、去重、冲突标注);
⑤ 失败隔离层(单个子任务失败不阻塞其他,可重试或降级,明确"部分成功"语义);
⑥ 可观测层(每个 Subagent 的 trace 挂到同一个任务链路下)。
模式上主要三种:Supervisor(集中调度,适合任务可明确分解)、Pipeline(固定阶段流水线,适合稳定流程)、去中心化(适合探索型、无明确分解方案)。
选型标准是任务可分解性 + 失败隔离需求——不是 Agent 越多越好。
加分点:主动说"不是越多越好,多 Agent 的协调成本会吃掉收益",是成熟的信号。
多 Agent 系统的常见踩坑有哪些?
四类高频问题:
① 传话失真——用自然语言在 Agent 之间转述需求,每转一手丢一点信息。解法是只传结构化产物,且用 schema 强制约束。
② 上下文重复膨胀——为了"让子 Agent 有足够信息",把主上下文整个复制下去,结果每个子 Agent 都背着几十 K token,成本和延迟全部失控。解法是只下发该子任务的最小必要上下文,其余通过工具按需拉取。
③ 汇总变成新的长上下文——Oracle 把所有子结果原文拼起来再决策,又回到长上下文问题。解法是要求子 Agent 输出结构化摘要 + 产物指针,汇总层只读摘要,需要细节时回读产物。
④ 死锁与循环等待——A 等 B 的结论、B 等 A 的输入。解法是显式依赖图 + 超时 + 单向数据流。
答这类题的通用结构

结论先行(一句话给出取舍)→ 机制(为什么是这样,涉及哪条链路)→ 代价或边界(这么做放弃了什么)→ 你的实践或数字(真实场景里怎么落地)。 只讲机制不讲代价,是背题;只讲取舍没有数字,是空谈。

本章收尾 · Wrap up
掌握度自评
点一下给自己打分;低于 3 分建议加入复习队列
个人笔记 · Notes