要让 Agent 干大工程,单 loop 是不够的。这一章讲如何把"长任务",拆给多个 subagent 并行执行;讲怎么避免"群里每个 agent 都在重复劳动"的灾难。这是后面第 28 章:实战多 Agent 系统 的方法论基础。

13.1 Subagent 是什么

Subagent 是 harness 内部又开出来的一个 Agent,它本身的 LLM 调用是独立的,自己去拿工具、自己做 ReAct、自己结束。能并行 = 能在一台机器上同时让 3 个 Agent 都在工作。

主 Agent (Build / Sisyphus)
    ├─ task → explore    "找叫 lock  的所有文件"
    ├─ task → explore    "find all TODO 与 FIXME"
    └─ task → librarian  "查 anthropic Messages API 当前最大 token"
    并行 (.await asyncio.gather)
    合并 sub-agent 的最终输出 → 给主 Agent

主 Agentsubagent 的差别只有一点:

维度 主 Agent subagent
对话历史 你写下的 一个全新的对话流
心智窗口 整个会话上下文 它自己 system prompt + 它做的 task
工具 拿到开放 看 system prompt 限定的子集
终止 user 认 YES 任务自报完成 (DONE)
成本 继续累积 单 task 的 token,结束后不再算

13.2 何时用 subagent

1. 探索任务 (explore):多个独立方向可同时找
2. 文档查询 (librarian):外部知识 / API / OSS 库
3. 复杂推理 (oracle):把"想清楚"的任务外包给一个强推理 sub
4. 多步骤执行:拆成 5 个,每个 sub 独立写自己报告
   合并出 "5 个并列 PR-able 子任务" 后再合并

13.3 派发模式:3 种

13.3.1 Parallel Exploration(并发探索)

async def parallel_explore(query: str) -> list[str]:
    tasks = [
        explore("find files matching: " + query, scope="src"),
        explore("find files matching: " + query, scope="tests"),
        explore("find files matching: " + query, scope="scripts"),
    ]
    return await asyncio.gather(*tasks)

Tool 调用并行:每个 subagent 独立 token 预算,并发降低墙钟。

13.3.2 Sequential Refinement(串行精化)

async def sequential_refine(goal: str) -> str:
    plan    = await metis(f"plan: {goal}")
    critique = await momus(f"review plan: {plan}")
    revised = await metis(f"adjust plan with: {critique}")
    return revised

下一步的输入依赖上一步。不是为加速,是为质量。

13.3.3 Fan-out / Merge(同一任务多方案)

async def multi_draft(prompt: str, k: int = 3) -> str:
    drafts = await asyncio.gather(*[
        build_subagent(f"draft {i+1}{prompt}")
        for i in range(k)
    ])
    return await oracle(f"挑 3 稿中最优的一部分,合并出 final: {drafts}")

代价 = 多一次调用的成本。优势:通过对比降低幻觉——这是 [2026 年 Easy Hard Tasks]的常见 boosting。

注意 Token 预算:Fan-out k=3kuje。模型每次不是免费,超 k=5 后边际回报小于 Premium Opus 一次性给。

13.4 派发 prompt 的 6 段式

每个 subagent prompt 包括 6 块:

[CONTEXT]
   project: ai_learning/ai-course, hugo.laozhang.work repo
[GOAL]
   找出所有 .md 中文 front matter 不规范的文章(少了 slug)
[DOWNSTREAM]
   我要把结果汇报给主 Agent, 它要修这些文章
[REQUEST]
   列表 → 每行一个文件路径 + 标题 + 缺的 field
[REQUIRED TOOLS]
   rg / Glob / Read
[MUST DO]
   - 不修改任何文件(read-only)
   - 不输出冗长 markdown, 仅列表
[MUST NOT DO]
   - 不要解读为何缺
   - 不要并行修改文件

Vague prompt → subagent 摸鱼,最终返回的是大使意:“我已找到 ~30 个文件”——主 Agent 又得返工。结构化 = 失败指数级减少。

13.5 OpenCode 派 Pattern 的工具化示范

OpenCode 内置 6 种 subagent 角色,对应 6 类任务:

───────┬─────────┬─────────────────────────────────────
 role  │ cost    │ typical use
───────┼─────────┼─────────────────────────────────────
explore │ free    │ 找文件、grep 模式、survey 某区域
librarian│ cheap   │ 文档 / OSS / 库内幕
oracle  │ expensv │ 当局迷呼救:根基架构 / bug
metis   │ expensv │ plan 前把"意图不清晰"的部分祛
momus   │ expensv │ 挑 plan 毛病
build   │ default │ 主执行
───────┴─────────┴─────────────────────────────────────

调用方语法是 task(subagent_type="explore", prompt="...")task(category="quick", prompt="..."),见你的实际 harness 文档。核心都是异步 dispatch + 等回应的一类事。

13.6 合并多个 subagent 结果

async def collect(sources: list) -> str:
    results = await asyncio.gather(*sources, return_exceptions=True)
    return "\n\n".join(
        f"### {src.__name__}\n{r if not isinstance(r, Exception) else f'[ERROR]{r}'}"
        for src, r in zip(sources, results)
    )

四件事:

  1. return_exceptions=True - 单个错别毁整读;异常当数据回
  2. 结构化标签 - 主 Agent 拿到 “### explore T1 / ### librarian T2 …” 可一一识别
  3. 误差隔离 - 失败的一方不报失败,主 Agent 可重试或选择不用
  4. 限并发 - 一次 ≤ 5 个 subagent,超过的话排队避免 rate-limit

13.7 失败模式:委派出会出的坑

派 Agent 写 Skill 的幻觉

[NOTE] 让一个 sub-agent 写 skill 体系时,它倾向于:
        - 若只读文案符合工作流,写得很好
        - 若需要"实际经验感"(知道哪些 prompt 真起效),就写些听起来扎实但实战无效

⇒ 写 skill 的任务保留给主 Agent,不要外包给 subagent。sub 用来找 / 读,但写产出交给主 thread。

派 7 个 subagent 做同一事 vs 1 个仔细做

“7 个 explore 同时找 README 不一致"听起来很酷——其实3 个就够,7 个会让 grep 重复触发甚至 hit rate limit。

enkel经验:3 是 sweet spot,超过 5 才在大型重构里见到收益。

把主 Agent 当 dispatcher 用

主 Agent 是决策 chairs,自己不要下场做实际操作。如果主 Agent 每轮自己 sed/grep,你的 subagent 调度 没起作用——直接让 sub 去干 grep,主收结果。

13.8 实战示例:写一个 “double check” 套娃

需求:写信 commit message 前先做双重确认(grep + Lint verify):

async def double_check(commit_msg: str) -> tuple[bool, str]:
    # 并行派两个 sub:一个 grep 找 secret 风险,一个跑 ruff/basedpyright
    secret_check, lint_check = await asyncio.gather(
        explore_sub("[CONTEXT] 当前 staged diff\n"
                    f"[GOAL] 找任何可能 secret 泄漏痕迹\n"
                    f"[MUST DO] 只看 git diff\n"
                    f"[OUTPUT] 单文字 `clean` / `risk: <理由>`"),
        build_sub("[CONTEXT] 当前 staged diff\n"
                   "[GOAL] 跑 ruff check + basedpyright\n"
                   "[OUTPUT] two-line summary")
    )
    if "clean" not in secret_check or "0 errors" not in lint_check:
        return False, secret_check + "\n" + lint_check
    return True, commit_msg

读完 29 章:AI 测试与 CI 集成 你会发现这玩意儿正是 GitHub Action 内的 pre-commit hook。

13.9 小结

  • Subagent = 独立 LLM session,独立 token 预算
  • 三种派发:Just Parallel / Sequential / Fan-out Merge
  • 6 段式 prompt:Context/Goal/Downstream/Request/Required Tools/Must Do
  • 并发不超过 5,sweet spot = 3
  • 写产出保留给主 Agent,subagent 找 / 读 / 比较

下一篇:《14 Agent 调试与可观测性》 - 把 trace / log / replay 做好,让你的 Agent 真的能查出错、能改进。

Summary: Subagent = 5 个并发上限,3 个 sweet spot,6 段式 prompt 沉淀,主 Agent 只决策不下场。