Subagent
给父 Agent 一个"外包"能力——把脏活累活派给子进程,
子进程干完只汇报结果,中间过程全部丢弃。
问题:上下文越干活越脏
Agent 每做一件事——读一个文件、跑一个命令、检查一个输出——结果都永久留在 messages 数组里。做 10 件事,就有 10 条工具结果堆积在对话中。
🧪 具体例子:一个简单问题,大量上下文垃圾
用户问父 Agent:"这个项目用什么测试框架?"
要看 requirements.txt、setup.cfg、pyproject.toml、Makefile 里面有没有测试命令、再看看 tests/ 目录结构。可能要读 5 个文件。
这 5 个文件的完整内容全部进入 messages,而父 Agent 最终只需要一个词:"pytest"。
████████████████████████████████████████ ← 5 个文件的内容占满
"pytest" ← 父 Agent 真正需要的
5 个文件的全文换来了 1 个词。上下文窗口就像桌面——放满了无关工具的说明书,你要的那把螺丝刀被埋在底下。对话越长,有效信息比例越低,Agent 的决策质量越差。
解决方案:派一个"临时工",干完活只汇报结果
👤 父 Agent
messages 里已经有很多历史。
调 task 工具:"帮我找出测试框架"。
←
🧑💻 子 Agent(临时工)
messages = [](全新空白上下文!)
读 5 个文件 → 分析 →
返回摘要:"pytest"
核心机制:上下文隔离
① 子 Agent 以 messages=[] 启动 —— 完全干净的对话历史,不继承父 Agent 的任何上下文垃圾。
② 子 Agent 在自己的 while 循环里干活 —— 读文件、分析、搜索,一切照常。
③ 子 Agent 完成后,整个 messages 被丢弃 —— 只有最后一条文本回复返回给父 Agent。
④ 父 Agent 收到的是一个普通 tool_result —— 内容是子 Agent 的摘要,就像任何其他工具调用一样。
案例对比:同一个问题,有无 Subagent 的差异
❌ 没有 Subagent:父 Agent 亲自查
父 Agent 调 read_file 5 次,每个文件的全部内容进入 messages。对话历史迅速膨胀。后续的 Todo 列表、之前的讨论、系统提示——全部被这 5 个文件的内容稀释。
██ 系统提示
██ 用户原始问题
██ Todo 列表
████████████████ requirements.txt 全文
████████████████ setup.cfg 全文
████████████████ pyproject.toml 全文
████████████████ Makefile 全文
████████████████ tests/ 目录结构
█ "pytest" ← 有用的就这一个词
✅ 有 Subagent:外包给临时工
父 Agent 调一次 task(prompt="找出这个项目的测试框架")。
██ 系统提示
██ 用户原始问题
██ Todo 列表
█ task_result: "这个项目使用 pytest" ← 只有这一个 tool_result
(子 Agent 的 5 次文件读取全在子 messages 里,已随函数返回而销毁)
效果:父 Agent 的上下文保持干净,后续决策质量不受影响。子 Agent 干了所有脏活,但父 Agent 只看到一个干净的摘要。
task 工具 & 两层工具集
父 Agent 有 task 工具,子 Agent 没有
这是 s04 最精妙的设计:两套工具列表,防止无限递归。
父 Agent 的 agent_loop 里,task 是特殊处理的
其他工具走 TOOL_HANDLERS 字典,但 task 走特殊分支——因为它不是一个 handler 函数,而是启动一个完整的子 Agent:
task 的特殊性:它不是"执行一个操作",而是"创建一个新的 Agent 实例"。所以它不走 dispatch map,而是在循环里硬编码处理——就像 s01 里硬编码 bash 一样。因为你目前只有一个"生孩子"的工具。
run_subagent 内部:一个缩小版的 s01 Agent Loop
run_subagent(prompt) 就是一个函数,内部跑了一个完整的 Agent Loop——和 s01 一模一样,但有三个不同:
run_subagent 的 Agent Loop(和 s01 对比)
四个关键不同点
① 全新 messages=[]:子 Agent 不继承父的任何上下文。它只知道自己被分配的任务(prompt)。这是隔离的核心。
② 最多 30 轮:用 for _ in range(30) 而不是 while True。安全上限——防止子 Agent 陷入无限循环。
③ 不同的 System Prompt:SUBAGENT_SYSTEM = "You are a coding subagent... Complete the given task, then summarize your findings." ——告诉子 Agent 你不是主角,干完活就汇报。
④ 只返回最后一条文本:"".join(b.text for b in response.content)... ——子 Agent 最后一轮输出的 text block 作为返回值。中间的几十条工具调用全部丢弃。
为什么禁止子 Agent 再生子 Agent?
如果允许递归:1 个任务 → 10 个子任务 → 100 个孙任务
父 Agent 说"帮我研究这个项目"→ 派了一个子 Agent。
子 Agent 说"这个项目太大了,我也派几个子 Agent"→ 派了 5 个孙 Agent。
每个孙 Agent 又说"我也派几个"...
结果:API 调用量爆炸、token 消耗失控、层层嵌套的摘要越来越失真(就像传话游戏)。而且 30 轮 × N 层嵌套 → 最坏情况下调用次数指数级增长。
CHILD_TOOLS 不包含 task = 一刀切
子 Agent 的工具列表里没有 task 工具,LLM 根本看不到这个选项。它不会说"我想派子 Agent"——因为它的世界里不存在这个能力。
这并不是说多层 Agent 架构不好——s09 会讲多 Agent 团队,但那是通过不同的机制(邮箱通信、显式协调),而不是无限制递归。
代码拆解 & s03 vs s04
两套工具 + 两套 System Prompt
父 Agent 和子 Agent 有不同的身份(system)和不同的能力(tools),这是隔离的基础。
run_subagent() — 完整的子 Agent Loop
一个函数 = 一个 Agent。内部跑完整的 while 循环,但 messages 从空开始,结束后销毁。
Agent Loop 里 task 的特殊分支
task 不走 dispatch map,而是在循环里 if 判断。因为它的行为不是"执行一个函数",而是"启动一个新 Agent"。
| 组件 | s03 | s04 |
|---|---|---|
| 工具数量 | 5 | 父6(+task)子4(无 task) |
| 上下文 | 单一共享 | 父子隔离,子用完即弃 |
| System Prompt | 1 个 | 2 个(SYSTEM + SUBAGENT_SYSTEM) |
| Agent Loop | 父 Agent 的循环结构不变(只在工具分发处加了一个 if 分支) | |
| 外层 REPL | 完全不变 | |
从 s04 带走的三个核心认识
上下文隔离 = 函数调用隔离
子 Agent 是一个函数调用。函数的局部变量(子 messages)在 return 后自动回收。不需要手动清理,Python 帮你做了。
外包脏活,保护注意力
父 Agent 不是不能自己做,而是自己做会污染上下文。外包不是"没能力",是"保护自己的决策质量"。
递归需要显式治理
CHILD_TOOLS 不包含 task,不是功能限制,是安全边界。子不能生子 = 深度可控。多层架构是好东西,但需要治理机制。
落地案例:Subagent 在真实 AI 产品中的应用
🔄 场景一:Claude Code 的 Subagent(你每次用都在触发)
Claude Code 的 Task 工具就是 s04 的生产级实现。当你提出复杂需求时,Claude Code 会自动把大任务拆成子任务,每个子任务在一个 subagent 里执行。
你有没有注意过 Claude Code 有时会显示 Task: "搜索代码库中的认证逻辑"?那就是它在派 subagent。子 agent 搜完几十个文件后只汇报一个摘要:"认证逻辑在 auth.py 和 middleware.py 中"。父 agent 的上下文保持干净,继续专注在主任务上。
下次 Claude Code 列出 Task 的时候,你就知道:它在用 s04 的 run_subagent()。
🧪 场景二:SWE-bench Agent — 学术界的"外包"策略
在 SWE-bench(软件工程基准测试)上,很多高分 Agent 的策略就是 subagent 模式:
定位阶段:父 Agent 阅读 issue 描述,生成需要搜索的关键词。
搜索阶段:派多个 subagent,每个搜一个关键词,各自返回相关代码片段。
修复阶段:父 Agent 汇总所有 subagent 的发现,决定修改方案,执行修改。
如果没有 subagent,父 Agent 要读完整个代码库——在大型项目中动辄几千个文件,上下文根本装不下。Subagent 让它把"大海捞针"变成了"分头找"。
🔍 场景三:Code Review Agent — 多维度并行审查
一个"代码审查 Agent"可以用 subagent 并行检查多个维度:
Subagent 1:只检查安全漏洞(SQL 注入、XSS、密钥泄露)。
Subagent 2:只检查性能问题(N+1 查询、不必要的循环、内存泄漏)。
Subagent 3:只检查代码风格(命名规范、注释质量、复杂度)。
三个 subagent 同时工作,各自在干净的上下文里专注一个维度。父 Agent 收到三份摘要后,合成一份完整的审查报告。如果没有 subagent,父 Agent 要在同一个上下文里切换三种思维模式——容易遗漏、容易混淆。
这也是你可以在 Claude Code 里用的模式——对 Claude Code 说:"帮我做 code review,用三个子任务分别检查安全性、性能、可读性。"