04 子智能体
⭐ 核心路径 — 本课是 Agent Harness 工程的第四块基石,属于核心路径:[[01-agent-loop/01-智能体循环|s01]] → [[02-tool-use/02-工具使用|s02]] → [[03-todo-write/03-待办写入|s03]] → s04 → s05 → s07 → s09
"大任务拆小,每个小任务干净的上下文" — 子智能体用独立 messages[],不污染主对话。
Harness 层:上下文隔离 — 守护模型的思维清晰度。
问题:上下文膨胀
智能体工作越久,messages 数组越胖。每次读文件、跑命令的输出都永久留在上下文里。考虑这个场景:
父智能体问"这个项目用什么测试框架?",子任务需要读 5 个文件才能回答。但父智能体只需要一个词:"pytest"。
然而在没有子智能体的架构中,这 5 个文件的全部内容、tool_result 的详细输出、路径尝试的历史记录——全都堆积在父智能体的消息历史中。随着对话进行:
- 上下文窗口被无关细节吞噬
- 模型注意力被稀释
- 关键决策依据被埋没在海量日志中
- 每次 API 调用成本线性增长
这正对应 [[01-agent-loop/01-智能体循环|s01 智能体循环]] 中提到的循环代价:循环跑得越多,上下文越脏,下一条推理越慢越贵。
解决方案:子智能体隔离
子智能体的核心思想极其简单:为子任务创建一个全新的、干净的上下文环境,任务完成后丢弃。
Parent agent Subagent
+------------------+ +------------------+
| messages=[...] | | messages=[] | <-- fresh
| | dispatch | |
| tool: task | ----------> | while tool_use: |
| prompt="..." | | call tools |
| | summary | append results |
| result = "..." | <---------- | return last text |
+------------------+ +------------------+
Parent context stays clean. Subagent context is discarded.这解决了 [[03-todo-write/03-待办写入|s03 待办写入]] 无法处理的场景:待办任务是规划层面的"做什么",而子智能体是执行层面的"怎么做的过程数据不污染主脑"。
子智能体的价值
- 上下文隔离:子智能体的所有 tool_use → tool_result 循环全部在隔离环境中完成,父智能体只看结果不看过程
- 成本可控:子任务 token 用完即弃,不计入父智能体的长期上下文成本
- 并行潜力:子智能体之间天然无共享状态,为后续 [[09-agent-teams/09-智能体团队|s09 智能体团队]] 的多 Agent 并行打下基础
- 错误包裹:子智能体崩溃不影响父智能体状态,可添加重试逻辑
工作原理
工具层面
父智能体有一个额外的 task 工具。子智能体拥有除 task 外的所有基础工具——禁止递归创建子智能体,这是防止无限生成的关键约束。
PARENT_TOOLS = CHILD_TOOLS + [
{"name": "task",
"description": "Spawn a subagent with fresh context.",
"input_schema": {
"type": "object",
"properties": {"prompt": {"type": "string"}},
"required": ["prompt"],
}},
]执行层面
子智能体以 messages=[] 启动,运行自己的工具调用循环。只有最终文本返回给父智能体,以普通 tool_result 的形式嵌入父对话。
def run_subagent(prompt: str) -> str:
sub_messages = [{"role": "user", "content": prompt}]
for _ in range(30): # safety limit
response = client.messages.create(
model=MODEL, system=SUBAGENT_SYSTEM,
messages=sub_messages,
tools=CHILD_TOOLS, max_tokens=8000,
)
sub_messages.append({"role": "assistant",
"content": response.content})
if response.stop_reason != "tool_use":
break
results = []
for block in response.content:
if block.type == "tool_use":
handler = TOOL_HANDLERS.get(block.name)
output = handler(**block.input)
results.append({"type": "tool_result",
"tool_use_id": block.id,
"content": str(output)[:50000]})
sub_messages.append({"role": "user", "content": results})
return "".join(
b.text for b in response.content if hasattr(b, "text")
) or "(no summary)"关键细节:
- 安全上限:30 次迭代防止子智能体死循环,也隐含了预算控制
- 截断保护:
[:50000]限制单次 tool_result 大小,防止子智能体自己把自己撑爆 - 默认摘要:即使子智能体没有返回有效文本,也不会让父智能体卡住
子智能体可能跑了 30+ 次工具调用,但整个消息历史直接丢弃。父智能体收到的只是一段摘要文本,作为普通 tool_result 返回。这与 [[03-todo-write/03-待办写入|s03 的待办写入]] 形成互补:待办管理"接下来做什么",子智能体管理"怎么做以及做完就忘"。
相对 s03 的变更
| 组件 | 之前 (s03) | 之后 (s04) |
|---|---|---|
| Tools | 5 | 5 (基础) + task (仅父端) |
| 上下文 | 单一共享 | 父 + 子隔离 |
| Subagent | 无 | run_subagent() 函数 |
| 返回值 | 不适用 | 仅摘要文本 |
从 harness 演进的角度看:
- [[01-agent-loop/01-智能体循环|s01]] 搭建了循环骨架
- [[02-tool-use/02-工具使用|s02]] 添加了工具交互能力
- [[03-todo-write/03-待办写入|s03]] 引入了任务规划和持久化
- s04 解决了执行过程中的上下文污染问题
何时使用子智能体
子智能体不是银弹。使用前需要判断任务是否值得"派遣"的成本:
适合派生子智能体:
- 纯信息收集任务("查一下这个库的 API 签名")
- 文件内容阅读与总结("读这 10 个文件,告诉我它们的关系")
- 确定性操作("创建这个目录结构")
- 高噪音操作("搜索所有出现某个模式的代码")
不适合派生子智能体:
- 结果需要立即参与父决策链("帮我看看这个文件,然后基于它改另一个文件")
- 子任务本身极轻量(读一个文件,还不如父智能体自己干)
- 需要子智能体状态延续到后续操作
试一试
cd learn-claude-code
python agents/s04_subagent.py试试这些 prompt(英文 prompt 对 LLM 效果更好,也可以用中文):
Use a subtask to find what testing framework this project usesDelegate: read all .py files and summarize what each one doesUse a task to create a new module, then verify it from here
观察父智能体的上下文在子智能体运行前后是否保持精简——这是子智能体有效的直接证据。
本课小结
- 子智能体是用 干净的 messages[] 执行子任务的机制
- 核心价值是 上下文隔离,防止过程数据污染主对话
task工具是父智能体独有的入口,子智能体不可递归- 子智能体返回的 仅摘要文本,完整工具调用过程全部丢弃
- 适合信息收集型任务,不适合需要状态延续的操作
- 这是 Agent Harness 从"单体"走向"分布式"的第一步
下节预告
上下文隔离之后,下一个问题是知识隔离。子智能体解决了执行环境的干净,但智能体仍然需要知道"遇到某个问题该用什么知识"。[[05-skill-loading/05-技能加载|s05 技能加载]] 将教你如何按需加载领域知识,让智能体在需要时才注入专业知识。
延伸阅读
- Learn Claude Code 项目 — 练习代码和完整文档
- [[00-课程概览/教学大纲|教学大纲]] — 查看完整课程地图
- [[00-课程概览/综述|课程综述]] — Agent Harness 工程导论
「干净的上下文是模型理性思考的前提」