首页
/ learn-claude-code s06 深度解析:用 Subagent 的 task 工具为子任务创建独立上下文

learn-claude-code s06 深度解析:用 Subagent 的 task 工具为子任务创建独立上下文

2026-09-04 14:25:26作者:苗圣禹Peter

本篇技术指南基于 learn-claude-code 仓库的课程章节 s06_subagent/README.ja.md,讲解 subagent(子代理)机制的设计与实现:当主 Agent 需要处理一个耗上下文的重型子任务(如追踪调用链、批量阅读文件)时,如何通过 task 工具启动一个持有全新 messages[] 的嵌套 Agent Loop,让中间对话留在子上下文中、仅把最终文本作为 tool result 返回父会话。读完本文,你将能够理解“消息级隔离”与“进程级隔离”的本质区别、subagent 的边界决策(工具集、返回契约、委托深度、权限策略),并可以基于 s06_subagent/code.py 在本仓库中实际运行与验证这一机制。

Subagent Overview:task 工具启动持有全新 messages 的嵌套 Agent Loop,仅最终文本返回父会话

1. 问题背景:中间过程会持续占用父上下文

s06 章节针对的问题很具体:Agent 在修一个 bug,为了追踪调用链读了很多文件,每一次工具调用及其结果都会留在父 Agent 的 messages[] 中。等调用链搞清楚之后,这些中间细节已经不再需要,却仍然持续占用上下文窗口。

这正是本仓库课程主线("Bash is all you need" —— 从 0 到 1 构建一个 claude code 风格的 agent harness)中 harness 层的 Delegation(委托) 机制要解决的场景:把聚焦的子任务放进一个独立的会话上下文里执行。s06 在课程链中位于 s05 之后、s07 Skill Loading 之前(课程链:s01 → s02 → s03 → s04 → s05 → s06 → s07 → … → s16 → s17),此前章节已经搭建好了 Agent Loop、工具调用与 Hooks 基础设施,s06 在此之上加一个 task 工具即可。

2. 方案核心:嵌套 Agent Loop + 全新 messages[]

调用 task 后,系统会同步启动一个使用全新 messages[] 的嵌套 Agent Loop;该循环结束时,其最终文本成为父会话中的一条 tool result。

这里必须明确一个边界:被隔离的是消息,而不是进程或文件系统。父 Agent 与 subagent 运行在同一个 Python 进程中,共享 WORKDIR,因此子代理的写文件和命令执行仍然作用于同一个工作区。subagent 拥有 5 个基础工具但没有 task,并且使用与父 Agent 相同的权限 Hooks 与生命周期 Hooks。

2.1 实现代码:run_subagent

对照 s06_subagent/code.py 的源码,run_subagent 的逻辑如下:

SUB_TOOLS = list(BASE_TOOLS)  # no task tool

def run_subagent(prompt: str) -> str:
    messages = [{"role": "user", "content": prompt}]

    for _ in range(30):
        response = client.messages.create(
            model=MODEL, system=SUB_SYSTEM,
            messages=messages, tools=SUB_TOOLS, max_tokens=8000,
        )
        messages.append({"role": "assistant", "content": response.content})
        if response.stop_reason != "tool_use":
            return extract_text(response.content) or "(no summary)"

        results = []
        for block in response.content:
            if block.type == "tool_use":
                output = execute_tool(block, SUB_HANDLERS)
                results.append({... "content": output})
        messages.append({"role": "user", "content": results})

    return "Subagent stopped after 30 turns without a final answer."

关键实现细节(以 code.py 为准):

  • 全新消息列表messages = [{"role": "user", "content": prompt}],父会话历史完全不复制进来,这是"上下文隔离"的全部来源;
  • 循环上限 30 轮:这是一个安全阀,30 轮内没有产出最终回答时返回 "Subagent stopped after 30 turns without a final answer.",避免子循环无限消耗 token;
  • SUB_SYSTEM 独立系统提示:子代理的系统提示(code.py#L49-L52)要求"Complete the given task, then return a concise final answer",与父提示"Use task for focused exploration"形成分工;
  • extract_text 兜底:从最终响应中提取 text 类型的内容块,若为空则返回 "(no summary)",保证父循环总能拿到一个字符串;
  • 可观察性:运行时打印 [Subagent started] / [Subagent done],子循环内的每次工具调用以 [sub] 工具名: 输出前100字符 的形式打印,方便调试时确认委托确实发生。

2.2 父 Agent 视角:task 只是一个普通工具

主 Agent 调用 task 的方式与其他工具完全一致(code.py#L296-L307):

TASK_TOOL = {
    "name": "task",
    "description": "Run a subagent with fresh conversation context and return its final text.",
    "input_schema": {
        "type": "object",
        "properties": {"prompt": {"type": "string", "minLength": 1}},
        "required": ["prompt"],
    },
}

TOOLS = [*BASE_TOOLS, TASK_TOOL]
TOOL_HANDLERS = {**BASE_HANDLERS, "task": run_subagent}

也就是说 run_subagent 被放进 TOOL_HANDLERS 映射后,父循环(code.py#L312-L340agent_loop)在分发 task 工具调用时不会有任何特殊处理——task 的返回值和 bashread_file 的返回值一样,作为一条 tool_result 追加到父 messages[] 中。父 Agent 因此只能看到 subagent 的最终文本,看不到子循环里发生过的任何工具调用。

2.3 边界决策表

文档给出的设计决策及理由如下:

决策点 选择 理由
会话 全新 messages[] 不将父会话复制给 subagent
执行 同一进程、同一 WORKDIR 两个循环都能看到文件系统变更
返回值 仅最终文本 子的工具调用与结果不进入父 messages
委托深度 SUB_TOOLS 中没有 task 本章只允许一层委托
工具策略 共享 Hooks 父子使用相同的权限检查

从源码结构看,前四条都有明确的落点:

  • SUB_TOOLS = list(BASE_TOOLS)code.py#L243-L244):子工具集直接复制 5 个基础工具(bashread_filewrite_fileedit_fileglob,见 code.py#L113-L124),不含 task,从而硬性保证委托深度为 1;
  • WORKDIR = Path.cwd()code.py#L41):模块级常量,父循环与子循环的所有文件操作(如 run_writerun_globroot_dir=WORKDIR)与 subprocess.run(..., cwd=WORKDIR) 都作用于同一目录;
  • 第 5 条(Hooks 共享)见下一节。

3. 权限与生命周期:子代理走同一套 Hooks

run_subagent 内每次工具执行都经过统一的 execute_toolcode.py#L226-L238):先触发 PreToolUse hooks(任一 hook 返回非 None 即拦截),再调用 SUB_HANDLERS 中的处理函数,最后触发 PostToolUse hooks。由于 SUB_HANDLERS = dict(BASE_HANDLERS) 且 hooks 是模块级全局注册(code.py#L137-L223),父与子共享完全相同的权限边界:

  • permission_hook(PreToolUse)DENY_LISTrm -rf /sudoshutdownrebootmkfsdd if=)直接拦截;DESTRUCTIVE 关键字(rm > /etc/chmod 777)需要用户交互确认;read_file/write_file/edit_file 访问 WORKDIR 之外的路径时也会弹出确认(code.py#L156-L180)。这意味着子代理并不能借"隔离"之名绕过权限系统
  • log_hook / large_output_hook / summary_hook:分别记录每次工具调用、警告超过 100000 字符的大输出、在 Stop 事件时统计本次会话的工具调用次数。

值得注意的一个细节:Stop hook 返回非 None 时,父循环和子循环(见 agent_looprun_subagentforce = trigger_hooks("Stop", messages))都会把该返回值作为新的 user 消息追加并继续循环——这是 harness 层的"强制继续"机制,s06 让子循环也继承了它。

4. 测试印证:契约如何被固定

tests/test_s06_subagent.py 用 mock 的 anthropic 客户端把上述设计固化为可运行的断言,是理解"实际行为"的可靠依据:

  • test_s06_is_kernel_plus_tasktests/test_s06_subagent.py#L66-L81):断言父工具集 = 5 个基础工具 ∪ {task},子工具集 = 恰好 5 个基础工具且不含 taskTASK_TOOLinput_schema.required["prompt"]
  • test_subagent_starts_with_fresh_messages_and_returns_final_texttests/test_s06_subagent.py#L83-L113):模拟"先 read_fileend_turn"的两轮响应,断言第一次 client.messages.create 收到的 messages 恰好是 [{"role": "user", "content": "Read note.txt and report its contents."}](全新上下文)、tools 只有 5 个基础工具、返回值就是最终文本 "The note says child input."
  • test_subagent_file_tools_keep_the_kernel_permission_boundarytests/test_s06_subagent.py#L116-L132):让子代理通过 write_file 写工作区外路径,模拟用户回答 n,断言结果为 "Permission denied by user" 且目标文件确实不存在——验证了子代理与父 Agent 共享同一权限边界。

仓库另有一份旧版 12 课时的 subagent 实现 agents/s04_subagent.py(旧课程轨的 s04),其 docstring 中"Process isolation gives context isolation for free"的思路与 s06 一脉相承,但 s06 把 Hooks 权限体系也纳入了子循环,边界控制更完整。

5. 动手运行

环境依赖见 requirements.txtanthropic>=0.25.0python-dotenv>=1.0.0pyyaml>=6.0)。按 README.md 的 Quick Start 配置后运行:

pip install -r requirements.txt
cp .env.example .env   # 配置 ANTHROPIC_API_KEY;code.py 还会读取 MODEL_ID、ANTHROPIC_BASE_URL(可选)

cd learn-claude-code
python s06_subagent/code.py

注意 code.py#L38-L43 的两处环境细节:设置了 ANTHROPIC_BASE_URL(用于指向代理/网关)时会主动 popANTHROPIC_AUTH_TOKENMODEL_ID 是必填环境变量,缺省会直接抛 KeyError

文档推荐以下三个试验 prompt(README.ja.md "試してみよう" 一节原文继承):

  1. Use a subtask to find what testing framework this project uses(子代理负责读文件,主 Agent 只接收结论);
  2. Delegate: read all .py files in agents/ and summarize what each one does
  3. Use a task to create s06_subagent/example/string_tools.py with a slugify(text: str) function, then verify it from the parent agent(验证"共享 WORKDIR":子代理写入的文件,父代理可以直接验证——注意这会在你的工作区生成文件)。

观察要点:

  • 是否出现 [Subagent started] / [Subagent done] 标记;
  • 子代理的工具调用是否以 [sub] ... 前缀打印(且只打印输出前 100 字符);
  • 父 Agent 是否只拿到 task 返回的最终文本继续后续推理;
  • 用 prompt 3 时,父 Agent 的 messages[] 中不会出现子代理逐文件读取的中间结果。

6. 局限与后续方向

当前实现是同步阻塞的委托:父循环在 task 执行期间暂停,且只允许一层委托(子代理没有 task 工具);30 轮上限与 8000 max_tokens 是写死的安全阀,没有后台任务、并行子代理或子代理间的消息传递——这些是课程后续章节(如 s11 背景任务)的方向。

此外,"把知识塞进 system prompt"并不能解决上下文膨胀问题:不同任务需要不同知识(前端组件改动需要 React 约定,写 SQL 需要表结构),全量预载必然撑爆上下文。这正是下一课 → s07 Skill Loading 的主题:按需注入技能(skill),而不是向 system prompt 堆积文档——在需要时才读取,如同读一个文件一样自然(见 s07_skill_loading/README.zh.md)。

7. 小结

s06 subagent 机制的核心可以浓缩为四条可复用的设计原则:

  1. 隔离单位是消息而非进程messages = [{"role": "user", "content": prompt}] 一行代码即完成上下文隔离,同时天然共享文件系统与工作目录;
  2. 返回值契约只有"最终文本":子循环的全部中间对话被丢弃,父上下文保持干净;
  3. 委托深度用工具集硬性封顶SUB_TOOLS 不含 task,杜绝递归派生;
  4. 权限策略不可旁路:子循环复用父循环的 execute_tool + Hooks 管线,denied/dangerous 检查对两者一视同仁。

这四个决策点使 subagent 成为 harness 中一个"低成本、强边界"的委托原语:模型决定何时委托,harness 负责隔离与回收。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
980
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384