☰
Claude Code Sub Agents 实战:拆分任务、隔离权限与稳定输出
2026/10/22 2:57:37 网站建设 项目流程

做跨平台自动化测试平台的时候,我真正体会到 Sub Agents 的价值。以前要让 Claude Code 一次性完成“读取需求文档 → 生成测试用例 → 跑回归脚本 → 汇总报告”这种链式任务,经常做到一半上下文就乱了,前面喂给它的信息后面全忘,最后输出一个四不像的结果。后来我把整套流程拆成五个子代理,每个子代理只干一件事,主 Agent 负责调度和汇总,整条流水线瞬间稳定下来,单次任务的返工率降了六成以上。这篇就聊 Sub Agents 的创建方法,不扯概念,直接给你能落地的命令、配置和排错思路。

1. 为什么要拆出 Sub Agents

1.1 单 Agent 模式的两个死穴

先说一个很反直觉的结论:在 Claude Code 里,让一个大 Agent 干所有事,看起来省事,实际上是最费事的方案。我踩过两次坑之后才彻底想明白这个问题。

第一个死穴是上下文窗口的浪费。Claude Code 的每次对话都要把历史消息、工具结果、你贴进去的文件内容全部塞进上下文窗口。任务一长,早期读过的文件内容、中间过程的中间产物、甚至已经废弃的草稿,全都会堆积在上下文里。我用过一个很典型的场景:让 Claude Code 先分析某开源项目的目录结构,再根据分析结果写一个数据迁移脚本。前十分钟它记得很清楚,到写脚本那一步,它已经把目录结构忘得差不多,频繁问我“这个项目的配置文件在哪”这种刚开头就回答过的问题。这不是它变笨了,而是窗口被历史信息塞满,关键信息被挤出去了。

第二个死穴是工具权限的不可控。单一 Agent 同时拥有读文件、写文件、执行命令、访问网络等一大堆工具的权限,一旦 prompt 写得宽泛,它就容易自作主张。我遇到过不止一次,让它“帮忙看一下日志”,它顺手就把日志目录里的缓存文件删了;让它“优化一下构建脚本”,它直接改了依赖版本号。出了事只能靠 Git 回滚,效率极其糟糕。

1.2 Sub Agents 解决的核心问题

Sub Agents 的设计思路其实很简单,就一句话:把一个庞大的任务拆成若干个边界清晰的小任务,每个小任务由专门的子代理负责,主 Agent 只做调度和汇总。

这个思路对应解决我上面说的两个问题。上下文方面,每个子代理拥有独立的上下文空间。子代理完成任务后,只把结论返回给主 Agent,而不是把整个思考过程和中间文件全部传回去。也就是说,主 Agent 的上下文里只积累各子代理的“报告”,不会积累每个子代理的“草稿纸”。这个机制带来的直接好处是,任务可以变得很长,但主 Agent 的上下文消耗是可控的。

权限方面,每个子代理可以单独配置允许使用的工具、可访问的目录、可执行的操作。你可以给“代码评审子代理”只开读文件的权限,给“脚本执行子代理”只开指定目录下的读写权限,谁都不能越界。这样即使某个子代理的 prompt 写得不够严谨,它也只能在自己的一亩三分地里折腾,根本碰不到不该碰的东西。

1.3 哪些任务适合上交子代理

不是所有任务都适合拆给子代理。我自己总结了一个判断标准:任务是否需要“多轮交互式反馈”。需要你不断纠正、反复讨论的任务,比如“帮我想想这个功能该怎么设计”,留在主 Agent 对话里更合适,因为交互本身就是信息的一部分。而那种输入明确、边界清晰、产出物固定的任务,比如“把某个目录下的所有 Markdown 文件转成结构化 YAML”“扫描代码库并列出所有未处理异常”“根据模板生成周报”,就很适合丢给子代理。

还有一个适合的场景是“周期性重复任务”。比如我每周都会让一个 review-agent 检查代码库里的 TODO 清单、输出未提交的变更、生成代码质量报告。这类任务逻辑固定、模板明确,抽成子代理之后,每次调用只需要一句话,省掉的 prompt 编写时间不容小觑。

2. 创建 Sub Agents 的基础方式:子命令、配置文件与 Skills

2.1 方式一:子命令直接创建专用代理

最简单的创建方式,是用子命令直接指定一个任务角色。打开 Claude Code 交互界面,输入斜杠命令即可触发子代理:

/agent bug-triage "检查当前仓库 issues 中的报错信息,按严重程度分为高、中、低三档,输出分档清单"

也可以在非交互模式下直接调用,适合写进脚本或自动化流水线:

claude --agent bug-triage "检查当前仓库 issues 中的报错信息,按严重程度分为高、中、低三档,输出分档清单"

这条命令的含义是:让 Claude Code 以bug-triage这个角色的身份去执行引号里的任务。如果bug-triage还没有被定义,Claude Code 会临时以你给的文字描述为角色定义来执行任务;如果已经存在同名配置,则会严格套用配置里的系统提示词、工具列表和权限范围。

这种方式适合快速验证想法。我经常在正式写配置之前,先随手用/agent试一下这个任务的提示词靠不靠谱。但它的缺点也很明显:每次都要写完整的任务描述,无法沉淀复用,而且执行过程中的权限是默认的全量权限,安全隐患比较明显。所以它只能算“临时工”,不是长期方案。

2.2 方式二:通过 Agent 配置文件批量注册

如果你打算长期使用某个子代理,推荐用配置文件的方式把它注册成“正式编制”。Claude Code 会在启动时自动加载项目级.claude/agent/目录和用户级~/.claude/agent/目录下的所有配置文件。同名配置时,项目级优先于用户级。

每个子代理对应一个 Markdown 文件,文件头部用 YAML 格式写元信息,正文写更详细的职责说明。我拿自己常用的code-reviewer举例,文件放在.claude/agent/code-reviewer.md:

--- name: code-reviewer description: 资深代码评审代理,只读模式,输出结构化问题清单 model: opus system_prompt: | 你是资深代码评审者。评审时关注:逻辑正确性、边界条件、错误处理、 可维护性。禁止修改任何文件。每次评审必须按以下格式输出: 问题位置、严重级别(高/中/低)、问题描述、修改建议。 tools: - Read - Grep - Glob permissions: filesystem: - read: true - write: false network: - enabled: false --- # 补充指令 只处理用户指定范围内的文件。评审结束后输出问题清单,不要打印无关内容。

拆开看几个关键字段:

  • name:子代理的唯一标识,触发时用这个名字。建议用短横线分隔的小写英文,尽量避免空格和特殊字符。
  • description:非常重要。这一字段不只是给人看的,父 Agent 决定“这个任务该交给谁”时,主要就是读各个子代理的 description 来做匹配。所以描述里要写清楚“这个代理擅长什么、适用于什么场景、不做什么”,让匹配更精准。
  • model:指定该子代理使用哪个模型版本。预算有限的任务可以选轻量模型,要求高推理质量的任务选旗舰模型。这里我多说一句,不是所有任务都要上最贵的模型,批量分类、格式转换这类简单任务用轻量模型非常划算,实测成本能省一半以上。
  • system_prompt:子代理的系统提示词,是它所有行为的底层约束。这里定义的角色越具体,输出越稳定。比如上面例子中明确规定了输出格式,执行结果就很规整,套一层模板就能直接写进报告。
  • tools:白名单式的工具列表。没列进去的工具,子代理一律不能用。这是权限隔离的核心。
  • permissions:文件系统读写、网络访问等扩展权限。我习惯默认全部关闭,只给当前任务必要的范围再开小口。

配置文件写好后,在 Claude Code 主对话里直接输入/agent code-reviewer "评审 src/core/ 目录下的所有 JS 文件"就能触发。每次调用都会自动加载这个角色定义,不需要重复写提示词。

2.3 方式三:借助 Skills 机制创建更灵活的子代理

上面两种方式解决的场景都是“文本问答型”的子任务。如果你的子代理需要执行一系列固定的操作步骤,比如“下载数据 → 清理空值 → 生成汇总表 → 保存结果”,那么纯 prompt 定义就显得力不从心,因为你不可能在提示词里把每步数据处理的具体命令都写清楚。

这种情况我会用 Skills 机制来封装。每个 Skill 本质上是一个包含SKILL.md描述文件和若干可执行脚本的目录。目录建好后,Claude Code 就能识别到对应的技能。子代理通过 Skill 名称来调用,而 Skill 内部则通过脚本来完成多步骤、确定性的操作。

以我封装的security-scan为例。目录结构长这样:

skills/ security-scan/ SKILL.md scan.py requirements.txt

SKILL.md的内容负责描述这个技能做什么、什么时候用它、需要什么参数,以及如何运行scan.py。这样主 Agent 调度到security-scan技能时,并不是去“理解”安全扫描这个领域,而是直接把scan.py跑起来,再把脚本输出结果整理反馈。

这种方式的优势是“确定性”。AI 每次用自然语言描述怎么扫描,答案都会飘忽不定;而脚本在第 100 次运行和第 1 次运行时的行为完全一致。Skills 机制把 AI 的“不稳定理解”和脚本的“稳定执行”结合起来,很适合那些要求结果可复现的自动化场景。

3. 让子代理真正能打:任务描述与参数设计

3.1 写好任务描述的三个层次

创建子代理的门槛很低,但用得好不好,差距全在任务描述上。我把它分成三个层次。

第一层是“说清边界”。给子代理的任务描述必须明确指出:输入是什么,输出是什么,禁止做什么。我见过太多模糊的描述,比如“看看这个项目的代码质量”,这种描述交给子代理,它根本不知道从哪里入手。合格的描述是“扫描 src/ 和 tests/ 目录下的 Python 文件,检查函数定义是否符合项目规范,输出不符合清单,不要修改任何文件”。边界清晰了,输出才可控。

第二层是“给足上下文”。子代理虽然有独立上下文,但它不会自动知道你的项目背景。所以描述里要把必要的信息带进去,比如项目路径、技术栈、约束条件、参考模板。必要的时候直接把示例输出贴在 prompt 里,效果比说十句抽象要求都好。

第三层是“定义验收标准”。告诉子代理“什么算做完”。比如“生成 5 个测试用例”比“写一些测试用例”强,“必须覆盖所有分支”比“尽量提高覆盖率”强。验收标准越明确,你越不需要检查它的产出,这在批量调用多个子代理时格外重要。

3.2 给子代理传参数与运行脚本

在某些场景下,你需要动态地向子代理传递参数。比如做一个“发布周报”子代理,每次周报的具体数据不同,但处理逻辑完全一样。我不希望你每次都要重写一遍完整的任务描述。

一种可行的方式,是在任务描述里用占位符,然后通过命令行变量传入。比如子代理创建好后,主 Agent 调用它时,可以通过配置文件里的variables字段声明支持的参数。实际触发时,对占位符做模板替换:

--- name: weekly-report description: 生成固定格式的周报 variables: - name: week_range description: 本周日期范围 - name: highlights description: 本周主要成果 ---

调用时传入相应参数:

claude --agent weekly-report "周报日期: 2025-06-01 至 2025-06-07,本周完成: 登录模块重构、性能优化上线"

其实这种方式背后的逻辑并不复杂,就是“模板化提示词 + 按次注入数据”。它保证每次任务的结构一致,同时内容可以灵活变化。在写自动化脚本时,我可以把调用封装成函数,传不同参数就能反复触发,省掉大量重复输入。

3.3 任务结果的回收与结构化输出

子代理执行完任务后,需要把结果返回给主 Agent。这中间有个常见误区:以为“结果”就是子代理最后那段话。如果你想把这些子代理接入自动化流水线,自由文本的结果是最难处理的,因为后续脚本需要解析自然语言,解析逻辑写起来又丑又脆。

我的习惯是,在配置子代理时强制要求结构化输出。最简单的做法就是在system_prompt里规定输出格式。比如前面code-reviewer的配置里,我明确要求“按 问题位置、严重级别、问题描述、修改建议 输出”。如果数据量再大一点,我直接要求输出 JSON:

每次评审结果以 JSON 数组返回,字段包含: file, line, severity, issue, suggestion

触发后返回的示例:

[ { "file": "src/core/auth.js", "line": 42, "severity": "high", "issue": "未校验 token 的过期时间", "suggestion": "在解密后增加 exp 字段校验" } ]

这样做的好处是,子代理产出可以直接落到日志文件,也可以用脚本汇总成表格,甚至喂给另一个分析代理继续处理。子代理之间通过这种结构化文本接力,整套流程的稳健性会提升一个档次。

4. 子代理的权限隔离与 Workflow 授权

4.1 权限设计的最小化原则

子代理能做权限隔离之后,最需要注意的反而成了“别把所有子代理都配成同一个权限”。我给团队定过一个规矩:默认全部拒绝,按任务最小集逐个开权限。

读操作类任务,比如代码搜索、日志分析,只开Read、Grep、Glob,文件系统里write一定为 false。生成代码或配置类任务,才额外开对应目录的写权限,而且范围要精确到子目录,不要直接给项目根目录写权限。真正要执行命令的子代理,比如跑测试、做静态检查,才允许挂Bash工具,同时必须限定可执行的命令范围。

拿我自己项目里的实际配置举例。我的test-runner子代理配置较长,其中最重要的部分是:

--- name: test-runner tools: - Read - Grep - Bash(limit: "pytest, python, npm test") permissions: filesystem: - write: false network: - enabled: false ---

Bash(limit: ...)很关键,它限制了子代理只能执行括号内的命令,其余一律拒绝。这个字段帮我在“能跑测试”和“能删库”之间画了一条明确的线。权限隔离做得越细,你放心的程度就越高,也敢把更多任务交给子代理去并发执行。

4.2 遇到权限升级请求怎么办

子代理遇到自己当前工具集不能完成的操作时,会向上层申请额外权限。最常见的场景是:你想让一个只读子代理写文件,或者想让它执行某个不在限制范围内的命令。

这里要给一个很关键的提醒:不要直接点允许。原因在于,一旦允许,这个子代理在本次运行中都会保持这个额外权限,如果它后续行为失控,你刚才批下去的权限就变成了它闯祸的许可证。我第一次用的时候直接点了允许,结果它顺手把依赖配置文件改成了自己“认为合理”的版本,花了一下午才排查出来。

正确姿势是检查权限申请的具体内容,判断这个权限是否真的必要。然后回到配置文件里,给这个子代理补齐对应权限,重新运行。这样每次授权都会沉淀成配置,而不是变成一次性的临时放行。配置文件和临时授权的关系,就像“正式发布的API”和“临时打补丁”的关系,前者经得起重复推敲,后者迟早制造技术债。

4.3 多子代理共享资源时的陷阱

拆分子代理之后,一个容易被忽略的问题是共享资源的竞争。比如两个子代理同时往同一个临时目录写文件,或者一个子代理正在读取某个目录时,另一个子代理正好在清理该目录的内容。这种并发冲突在独立的单 Agent 场景里根本不会出现,是拆分布式结构之后才有的新问题。

我的策略是给每个子代理分配独立的临时目录。如果确实需要共享数据,比如做数据预处理时多个子代理都要读取同一份清洗结果,我会把共享数据放在只读目录中,并且明确写入所有相关子代理的配置里。子代理的 prompt 中我也会强调一句:“shared/data/目录下的文件为只读输入,不要写入任何内容”。配置加提示词双保险,目前跑了大半年,没有再因为共享资源出现互相覆盖的问题。

5. 常见问题与排错实录

5.1 子代理初始化挂起或长时间不响应

遇到最多的问题是调用子代理后,交互界面一直停在“正在初始化”的状态,十几秒不见动静。这种情况通常有两个原因,一是当前排队任务太多,新任务在前面等待;二是子代理配置中加载了过大的上下文内容,比如system_prompt里塞了太多背景材料,或者父 Agent 请求时传了一个体量很大的输入。

解决办法按优先级来:先等 20 到 30 秒,确认是不是短暂排队;如果持续不响应,检查system_prompt是否过于臃肿,精简到只保留角色定义和输出规范;再看看模型选择,某些大模型版本的推理延迟确实更高,简单任务换轻量模型就能明显提速。

5.2 上下文被遗忘的排查思路

子代理忘记早期任务信息,是最容易被误判为“模型变傻”的问题。实际上原因通常很具体:子代理处理多步骤任务时,每一步都把很大块的中间内容写入上下文,导致窗口被撑爆,早期指令被挤出去。

排查时先看任务日志里的 token 消耗曲线。如果某个子代理任务的中途 token 数直线上升,说明它每步都在向上下文里堆内容。这种情况的解法是:把子代理的职责再拆细,让一个代理只做一步,完成即返回结论。另外,要求子代理在每个步骤开始时先输出“本步输入摘要”,也能显著减少它为了“记住”而上翻下翻的消耗。

5.3 配置文件无法被识别

写好了 agent 配置文件,结果调用时报“agent not found”。这类问题绝大多数出在路径和格式。先确认文件是否放在正确目录下,项目级是.claude/agent/,用户级是~/.claude/agent/,两个目录之间不要搞混。其次检查 YAML 头部元信息是否完整,name、description、model这三个字段必填,缺失任意一个都会被跳过。还有一个很隐蔽的错误:name里用了大写字母或空格。目前实测最稳妥的命名规则是小写字母加短横线,比如code-reviewer而不是Code Reviewer。

如果路径和命名都没问题,就在项目根目录执行claude --agents列出所有已加载的子代理,能直接看到加载失败的报错信息。这个列表是排查子代理加载问题最直接的入口。

5.4 子代理权限申请被反复拒绝

明明在配置里开了对应工具,子代理执行时却仍然提示权限不足。先确认是不是路径问题:文件系统权限是精确目录级别生效的,如果你只授权了reports/目录,子代理尝试写reports_note/一样会被拒绝。其次确认是不是工具层面的限制,比如Bash(limit: "pytest")配置下,子代理执行python -m pytest本身就不在允许列表里,应该写成Bash(limit: "pytest, python -m pytest")。

这类问题通常不是玄学,而是配置范围和实际调用范围存在偏差。把调用命令原样抄进限制列表,问题往往就解决了。

5.5 多个子代理同名冲突

同时存在项目级和用户级配置目录时,同名子代理可能互相覆盖。默认情况下,项目级配置会覆盖用户级配置,但前提是项目级文件合法可加载。如果项目级文件格式错误,行为可能回退到用户级配置,导致你以为执行的是 A 版本,结果跑的是 B 版本。

排查方案很简单:运行claude --agents查看实际加载配置的路径来源。如果列表中显示的路径和你预期的目录不一致,就能立即定位到是谁在覆盖谁。我自己是尽量只维护用户级配置里的通用子代理,项目级配置只放该项目专用的子代理,从命名上就避开重叠。

最后分享两个我在实战中的体会

第一个体会是关于“拆的粒度”。子代理拆得越细,单个代理越稳定,但调度成本会增加。我的经验是一个子代理的任务步数尽量控制在 5 步以内,超过这个数就考虑继续拆分。第二个体会是,配置文件的维护本身也是一份代码,需要纳入版本控制。我把.claude/agent/目录放进了 Git 仓库,每次修改配置都会留痕,出了问题可以快速看到是哪次调整引入了变化。

如果你也打算在项目里引入 Sub Agents,我的建议是从一个最耗时的固定任务开始,比如每周的代码质量报告或日志分拣。先写一个粗糙的子代理跑起来,再逐步完善它的提示词和权限,比一开始就想设计出一套完美的多代理架构要靠谱得多。子代理这东西,真正的门槛不在创建,而在创建之后你对它的持续调教。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询