【与我学 ClaudeCode】规划与协调篇 之 Subagent:上下文隔离的极简子代理框架

作者:逆境不可逃

技术永无止境

希望我的内容可以帮助到你!!!!!


大家吼 ! 我是 逆境不可逃 今天给大家带来文章《【与我学 ClaudeCode】规划与协调篇 之 Subagent:上下文隔离的极简子代理框架》.

Learn-Claude-Code 官方地址 : https://github.com/shareAI-lab/learn-claude-code

Subagent 是在 TodoWrite 基础上迭代的第 4 个版本(s04),核心解决 Agent 上下文臃肿 问题,通过「大任务拆小、每个子任务独立干净上下文」的方式,让主对话始终保持清晰,避免被大量工具调用输出污染。

学习路线:s01 > s02 > s03 > s04 > s05 > s06 | s07 > s08 > s09 > s10 > s11 > s12


一、问题根源:为什么 Agent 越跑越慢、越跑越偏?

随着 Agent 工作时间变长,messages 数组会急剧膨胀:

  • 每次 read_filebash 命令的输出都永久留在上下文中
  • 比如回答「这个项目用什么测试框架?」,可能要读 5 个文件、跑 3 条命令,主 Agent 上下文会被大量无关信息填满
  • 上下文窗口是有限的,冗余信息会挤占关键任务的 token 空间,稀释系统提示的影响力,导致模型后期跑偏、重复工作

传统解决方案(比如直接复制父上下文给子代理)会让问题更糟:子代理继承了父代理的所有历史对话,不仅信息过载,还会被无关细节干扰判断。


二、三大核心设计决策(图片内容详解)

Subagent 通过三个硬约束,从架构层面实现上下文隔离,同时保证系统的可控性和安全性。

1. 子代理获得全新上下文,而非共享历史

核心设计 :父代理通过 task 工具创建子代理时,子代理从全新的消息历史 启动,只包含系统提示词和委派的任务描述,不继承父代理的任何对话历史。子代理的交互过程(哪怕数十轮),最终只以一条 tool_result 摘要的形式返回给父代理。

三大关键优势

  • 子代理完全专注于当前子任务,不会被父代理过往的对话、工具输出干扰
  • 父代理的上下文始终保持干净,不会被子任务的细节污染
  • 大幅降低 token 消耗:子代理的完整对话历史直接丢弃,父代理只保留最终摘要

替代方案的致命缺陷

共享父代理完整上下文会给子代理更多信息,但也会用无关细节淹没它。上下文窗口是有限的,填充父历史会挤占子代理自身工作的空间;基于 fork 的复制上下文方案是折中方案,但仍会在无关历史上浪费大量 token。

2. Explore 类型子代理强制只读权限(可选扩展)

核心设计 :针对「探索类子任务」(如查找函数使用位置、分析项目结构),子代理仅能使用只读工具:受限的 bashread_file 和搜索工具,禁止调用 write_file/edit_file。这遵循了最小权限原则

解决的风险

  • 消除探索过程中误修改文件的风险
  • 缩小工具空间,让模型在更少的选项中做出更精准的决策(减少工具选择的困惑)

替代方案的致命缺陷

给所有子代理完整工具权限实现最简单,但违反最小权限原则;权限申请系统(子代理向父代理请求写权限)会增加复杂度和延迟;按角色静态过滤工具是务实的折中方案 ------ 实现简单,能有效防止意外修改。

3. 子代理不能再创建子代理(禁止递归委派)

核心设计task 工具不包含在子代理的工具集中,子代理必须直接完成工作,不能继续委派任务。这直接防止了无限委派循环

为什么禁止递归

  • 没有这个约束,代理可能创建子代理,子代理又创建子代理,每一层都用略微不同的措辞重新委派同一任务,白白消耗 token 却毫无进展
  • 单层级委派足以处理绝大多数真实编码场景,过度分层只会增加复杂度

替代方案的致命缺陷

允许带深度限制的递归委派可以处理深度嵌套的任务,但会增加系统复杂度和 token 失控的风险;在实践中,单层委派就能覆盖绝大多数场景,多层委派在后续版本中通过持久团队结构解决,而非递归生成。


三、系统整体架构与工作原理

1. 核心架构图

复制代码
Parent agent                     Subagent
+------------------+             +------------------+
| messages=[...]   |             | messages=[]      | <-- fresh
|                  |  dispatch   |                  |
| tool: task       | ----------> | while tool_use:  |
|   prompt="..."   |             |   call tools     |
|                  |  summary    |   append results |
|   result = "..." | <---------- | return last text |
+------------------+             +------------------+
  • 父代理通过 task 工具发起子任务,传递任务描述
  • 子代理以空上下文启动,独立执行工具调用,完成任务后返回摘要
  • 子代理的完整对话历史被丢弃,父代理只收到最终结果,上下文始终保持干净

2. 关键组件与工作流程

(1) 工具分层设计
  • 父代理工具集(PARENT_TOOLS :基础工具 + task 工具(用于创建子代理)

  • 子代理工具集(CHILD_TOOLS :仅基础工具(无 task 工具,禁止递归委派)

    子代理工具集:所有基础工具,不含 task

    CHILD_TOOLS = [
    {"name": "bash", ...},
    {"name": "read_file", ...},
    {"name": "write_file", ...},
    {"name": "edit_file", ...},
    ]

    父代理工具集:子代理工具 + task 工具

    PARENT_TOOLS = CHILD_TOOLS + [
    {"name": "task", "description": "Spawn a subagent with fresh context.", ...}
    ]

(2) 子代理执行函数 run_subagent

这是整个系统的核心,负责创建独立的子代理实例:

复制代码
def run_subagent(prompt: str) -> str:
    # 1. 初始化全新的消息上下文,仅包含任务提示
    sub_messages = [{"role": "user", "content": prompt}]
    
    # 2. 安全循环限制:最多30轮,防止无限执行
    for _ in range(30):
        # 调用模型,使用子代理专用系统提示
        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})
        
        # 3. 如果模型不再调用工具,说明任务完成,退出循环
        if response.stop_reason != "tool_use":
            break
        
        # 4. 执行子代理的工具调用,结果加入子代理上下文
        results = []
        for block in response.content:
            if block.type == "tool_use":
                handler = TOOL_HANDLERS.get(block.name)
                output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
                results.append({"type": "tool_result", "tool_use_id": block.id, "content": str(output)[:50000]})
        sub_messages.append({"role": "user", "content": results})
    
    # 5. 仅返回最终文本摘要,子代理上下文直接丢弃
    return "".join(b.text for b in response.content if hasattr(b, "text")) or "(no summary)"
(3) 父代理主循环与 task 工具处理
复制代码
def agent_loop(messages: list):
    while True:
        response = client.messages.create(
            model=MODEL, system=SYSTEM, messages=messages, tools=PARENT_TOOLS, max_tokens=8000,
        )
        messages.append({"role": "assistant", "content": response.content})
        
        if response.stop_reason != "tool_use":
            return
        
        results = []
        for block in response.content:
            if block.type == "tool_use":
                if block.name == "task":
                    # 处理 task 工具调用:创建子代理
                    desc = block.input.get("description", "subtask")
                    prompt = block.input.get("prompt", "")
                    print(f"> task ({desc}): {prompt[:80]}")
                    # 调用 run_subagent 执行子任务,获取摘要
                    output = run_subagent(prompt)
                else:
                    # 处理普通工具调用(bash/read_file等)
                    handler = TOOL_HANDLERS.get(block.name)
                    output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
                print(f"  {str(output)[:200]}")
                results.append({"type": "tool_result", "tool_use_id": block.id, "content": str(output)})
        
        # 将所有工具结果(包括子代理摘要)加入父代理上下文
        messages.append({"role": "user", "content": results})
(4) 执行流程

四、与 TodoWrite(s03)的关键变更对比

组件 之前(s03 TodoWrite) 之后(s04 Subagent)
工具集 5 个基础工具(含 todo) 父端:5 基础工具 + task;子端:4 基础工具(无 task)
上下文 单一共享上下文,所有对话和工具输出永久保留 父 + 子隔离:子代理独立上下文,仅摘要返回父代理
子代理 新增 run_subagent() 函数,实现子代理生命周期管理
返回值 工具输出直接加入主上下文 子代理仅返回最终摘要,完整交互历史丢弃
核心约束 单任务聚焦、计划上限 上下文隔离、禁止递归委派、工具分层

五、核心优势与创新点

  1. 从架构上解决上下文臃肿问题:通过进程级别的上下文隔离,让子代理的所有细节交互不污染主对话,主上下文始终保持高效
  2. 极致的 token 优化:子代理的完整对话历史直接丢弃,父代理只保留关键摘要,大幅降低 token 消耗
  3. 最小权限与安全约束:工具分层设计(子代理无 task 工具、可扩展只读权限),防止无限委派和误操作
  4. 模型自主性与可控性平衡:父代理负责任务拆分和委派,子代理专注执行,既保留了模型的灵活性,又通过硬约束防止失控
  5. 实现简单、可扩展性强:仅 154 行代码,核心逻辑清晰,后续可扩展不同角色的子代理(探索型、执行型、测试型)

六、运行示例

假设用户输入:"实现登录功能并编写单元测试",父代理的典型执行流程:

  1. 父代理分析任务,决定拆分两个子任务:
    • 子任务 1:分析项目结构,确定登录功能的实现位置和测试框架
    • 子任务 2:实现登录接口并编写单元测试
  2. 父代理调用 task 工具,委派子任务 1:prompt="分析项目结构,找出用户认证相关的代码文件,说明项目使用的测试框架"
  3. 子代理以空上下文启动,执行 read_filebash ls 等工具调用,完成分析后返回摘要:"认证逻辑在 auth.py,使用 pytest 作为测试框架,现有测试文件为 test_auth.py"
  4. 父代理收到摘要后,调用 task 工具委派子任务 2:prompt="在 auth.py 中实现登录接口,密码使用 bcrypt 加密,在 test_auth.py 中添加单元测试"
  5. 子代理执行文件读写操作,完成后返回实现结果和测试报告
  6. 父代理收到两个子任务的摘要,整合后完成最终回复,整个过程主上下文仅增加两条摘要,无冗余信息
相关推荐
l1t5 小时前
DeepSeek总结的PostgreSQL 在 AI 基础设施中日益增长的作用
人工智能·postgresql
视***间5 小时前
全栈算力矩阵,全域智能赋能——视程空间六大产品系列,构建边缘智能完整生态
人工智能·机器人·智慧城市·边缘计算·ai算力·终端算力
real_haha5 小时前
我做了一个仅有 1.3 MB 的 macOS 原生 AI 助手:AskNow
人工智能·macos
名不经传的养虾人5 小时前
从0到1:企业级AI项目迭代日记 Vol.29|自然语言变工作流:Agent 自动拼装子图的实现路径
人工智能·agent·ai编程·工作流·ai创业·企业ai
云登指纹浏览器5 小时前
AI选品工具实战对比:Jungle Scout vs Helium 10 vs ChatGPT选品,2026跨境卖家选哪个?
人工智能·chatgpt·跨境电商
RSTJ_16255 小时前
PYTHON+AI LLM DAY FIFITY-ONE
开发语言·人工智能·python
丁劲犇5 小时前
QodeAssist:为msys2 ucrt64 Qt Creator 注入 AI 灵魂的开源插件
开发语言·人工智能·qt
无心水5 小时前
【分布式利器:SOAF】蚂蚁开源的金融级微服务全家桶:SOFAStack 核心架构与实战选型对比
人工智能·分布式·微服务·金融·架构·开源·分布式利器
梧桐和风5 小时前
2026 年 Java 趋势:AI 浪潮下,Java 会过时吗?
java·开发语言·人工智能