单靠一个对话窗口开发多模块项目,撑到第三周就到了极限。下载器、AI 总结、前端、支付四个模块互相拉扯,让同一个 Agent 从头管到尾,它要么记不住前面的决策,要么改一处崩三处。正是那段时间,我系统地用上了 SubAgents(子代理)模式——主 Agent 负责拆任务、派活、汇总,每个子 Agent 只干一件具体的事。这篇文章就围绕我在这个项目里怎么用 SubAgent、跟直接 Agent 比差在哪、踩过哪些坑、最后沉淀出什么协作规则来展开。
一、SubAgent 是什么
先说清楚概念。SubAgent 是主 Agent 派生的子任务执行者。在 AI 视频下载总结器项目里,我的做法是让主 Agent 先做整体规划,再按模块把任务派给不同的 SubAgent,每个子 Agent 有独立的上下文和清晰的边界,互不干扰。它的结构大概长这样:
主 Agent(规划)
├── SubAgent 1(实现下载器)
├── SubAgent 2(实现 AI 服务)
├── SubAgent 3(实现前端)
└── SubAgent 4(写测试)
主 Agent 拿到大任务后,拆解成子任务并行/串行派发,每个子 Agent 独立完成自己的部分。
最初我以为这只是”多开几个聊天窗口”的花活,直到用它并行写了四个模块,才真正体会到它的价值在任务隔离——子 Agent 之间的上下文互不污染,A 改挂了不会拖累 B,单个子任务的失败也不会让整个项目归零。
二、与直接 Agent 的区别
光说不直观,我把直接 Agent 和主+子 Agent 两种方式放在一张表里对比:
| 维度 | 直接 Agent | 主+子 Agent |
|---|---|---|
| 任务规模 | 单一任务 | 大任务拆解 |
| 上下文 | 单一上下文 | 子 Agent 独立上下文 |
| 错误恢复 | 一错全错 | 子任务重试不影响整体 |
| 并行能力 | 串行 | 并行 |
| 复杂度 | 简单 | 复杂(需要协调) |
一句话概括:直接 Agent 适合一条线走到头的活,主+子 Agent 适合能拆成多块的大项目。
我在项目里感受最直观的是”错误恢复”那一行。用直接 Agent 写下载器时,中途一个异常没处理,整个任务就断了,得从头再来;拆成子 Agent 之后,某个子任务失败,主 Agent 只需单独重试那一个,其他模块照常推进。代价是协调变复杂了,这个后面单独讲。
三、平台用 SubAgent 的典型场景
1. 大型功能开发
“做完整的视频总结功能”是一个大任务。我让主 Agent 拆解:
主 Agent Prompt:
"视频总结功能需要:
1. 后端 API(视频提交 + 总结接口 + SSE 流式)
2. 前端页面(提交表单 + 实时展示 + 思维导图)
3. AI Prompt 设计
4. 测试用例
请派 4 个 SubAgent 并行实现,最后汇总"
子 Agent 各自领任务并行执行。
这类任务如果让一个 Agent 串行做,先写后端再写前端,中间反复切换上下文,很容易把后端定好的接口字段忘干净。拆开后,前端子 Agent 拿到一份接口契约就能开工,两边同时跑,互相不踩。
2. 跨模块重构
“把用户系统从 SQLAlchemy 1.x 升级到 2.0″涉及多个文件:
主 Agent:
"请按以下顺序派 SubAgent:
1. SubAgent 1:升级 model 定义
2. SubAgent 2:升级 query/relationship
3. SubAgent 3:更新所有 service
4. SubAgent 4:跑测试并修复"
每个 SubAgent 只关心自己那部分,主 Agent 协调顺序和合并。
这里我坚持用串行派发而不是全并行,因为 model 定义不升级完,后面的 query 和 service 都会改错方向。主 Agent 像流水线班长一样卡住先后顺序,每一棒交出去时都带着上一棒的产出。
3. 大量相似任务
“为 20 个平台写下载器”是重复工作:
主 Agent:
"请派 SubAgent 循环为以下平台写下载器:
YouTube, B站, 抖音, 小红书, Twitter, TikTok, Instagram,
Vimeo, Dailymotion, Twitch, Reddit, Pinterest, Tumblr,
Facebook, LinkedIn, Threads, Bluesky, Mastodon, VK, Rutube
每个 SubAgent 写一个,最后汇总测试"
20 个 SubAgent 并行。
这种场景我只跑过一次就学乖了:并行开 20 个子任务,成本直接乘以 20,而且各平台反爬差异大,出来的代码质量参差。后来改成先让一个子 Agent 写 YouTube 版本当样板,再派 4 个并行的子 Agent 基于样板适配其他平台,成本和速度都更可控。
四、SubAgent 的关键设计
1. 任务边界
每个 SubAgent 的任务要清晰、单一:
# 好的任务
"实现 YouTube 下载器,返回 VideoInfo dataclass,含 yt-dlp 调用"
# 不好的任务(太大)
"实现整个下载器模块"
任务边界这条我踩过坑。有一次我偷懒,让一个子 Agent”实现整个下载器模块”,它交回来的代码里混杂了反爬、缓存、重试三套逻辑,接口命名前后矛盾,我花了一晚上才拆开。把任务切成单一职责之后,每个子 Agent 的产出都能独立验证,合并时也少了很多冲突。
2. 输入输出契约
# 明确告诉 SubAgent 输入是什么
"输入:URL 字符串
输出:VideoInfo dataclass(url, platform, video_id, title, duration, thumbnail, uploader, formats)
约束:必须 async 函数,30s 超时"
契约的价值在于:多个子 Agent 并行开发时,只要大家遵守同一份输入输出定义,各自的模块就能像乐高一样拼起来。我后来把这份契约写进了项目文档,子 Agent 派发时直接引用,接口不一致的问题基本绝迹。
3. 上下文共享
子 Agent 不共享上下文。主 Agent 通过 prompt 传关键信息:
主 Agent 传给子 Agent 的 prompt:
"项目用 FastAPI 0.110+、Python 3.11、SQLAlchemy 2.0。
数据库连接在 app.db 模块。
现有 Video 模型字段:url, platform, video_id, title, duration, thumbnail, uploader, status, error_msg, created_at。
你的任务:写 YouTube 下载器..."
这里有个矛盾点:上下文传少了,子 Agent 乱猜;传多了,token 成本又上去了。我的经验是只传”跟当前任务直接相关”的部分——数据库连接、要用的模型字段、技术栈版本,其余一律不塞。项目背景这类公共信息,用一个固定的 system prompt 缓存起来,每个子 Agent 进来先注入,效果和传完整上下文差不多,成本却低得多。
五、平台的具体用法
Claude Code 中
Claude Code 1.0+ 支持 Task 工具派生子 Agent:
// 主 Agent 调用
const result = await Task({
description: '实现 YouTube 下载器',
prompt: '...',
subagent_type: 'general-purpose'
})
我在 Claude Code 里用它做大型重构,比在网页版来回粘贴省心得多。有一点要注意:Task 返回的是字符串结果,复杂结构化产出(比如完整文件内容)建议让子 Agent 直接写文件,主 Agent 再读取校验,避免结果被截断。
Cursor 中
Cursor 用 @ 提及文件或 Agent:
@file:src/services/downloader/base.py 请基于这个实现 YouTube 下载器
Cursor 的做法更像”圈定范围干活”,@ 一个文件,AI 就知道该往哪改。适合小步调整,不承担大型任务的协调职责,我一般把 Cursor 当子 Agent 的执行器来用。
自建多 Agent 框架
import asyncio
class MainAgent:
def __init__(self):
self.sub_agents = []
async def dispatch(self, task: str, sub_agent: 'SubAgent'):
sub_agent.context = self.build_context(task)
result = await sub_agent.run(task)
return result
def build_context(self, task: str) -> str:
return f"""
项目背景:AI 视频下载总结器
技术栈:FastAPI + SQLite + yt-dlp
当前任务:{task}
"""
class SubAgent:
async def run(self, task: str) -> str:
# 独立 LLM 调用
response = await openai.ChatCompletion.create(
model='gpt-4',
messages=[
{'role': 'system', 'content': self.context},
{'role': 'user', 'content': task}
]
)
return response.choices[0].message.content
这份简化代码是我第一版子代理框架的原型,核心就两件事:主 Agent 派活时带上构建好的上下文,子 Agent 用自己的独立对话跑任务。后面我往里加了重试、超时和结果校验,才敢拿它跑真实项目。
六、SubAgent 的协调模式
1. 串行(pipeline)
SubAgent 1 → SubAgent 2 → SubAgent 3
适用:有依赖关系的任务(先做 A,再做 B)。
重构那轮我就用的串行,每个子 Agent 的产出是下一个的输入,谁也不能跳步。
2. 并行(fork-join)
┌→ SubAgent 1
Main → ─┼→ SubAgent 2 → Join
└→ SubAgent 3
适用:独立任务(同时跑更快)。
注意”Join”这一步,主 Agent 要负责合并结果并处理冲突,否则并行变成各写各的,最后没人收尾。
3. 主从递归
Main
├── Plan SubAgent(设计)
├── Code SubAgent(实现)
└── Test SubAgent(测试)
适用:复杂任务的多阶段处理。
这一层我是在支付模块上试的:先让 Plan 子 Agent 出设计,Code 子 Agent 照设计实现,Test 子 Agent 专门挑毛病。三个阶段互不干扰,比一个 Agent 既当裁判又当运动员要干净。
七、平台开发的真实案例
案例 1:实现完整支付流程
主 Agent: "实现 Stripe 订阅支付,需要 6 个文件:
1. api/payment.py
2. services/stripe_service.py
3. models/payment.py
4. schemas/payment.py
5. services/quota_service.py (更新)
6. tests/test_payment.py
请派 6 个 SubAgent 并行实现"
6 个 SubAgent 同时跑,10 分钟完成(人工要 3 小时)。
这个案例是我印象很深的一次提速。难点不在写代码,而在 6 个文件之间的依赖关系——payment 的 schema 没定,quota_service 就不知道字段。我让主 Agent 先把一份契约文件发给所有子 Agent,再并行开工,10 分钟拿回来的代码基本能拼起来。
案例 2:批量写文档
主 Agent: "为已实现的 12 个 API 写 OpenAPI 注释:
api/auth.py, api/video.py, api/summary.py, ...
每文件派 1 个 SubAgent,要求中英文 summary"
12 个 SubAgent 并行。
批量文档这类活是子代理模式的甜区,任务重复、边界清楚、结果可自动校验。不过我吸取了 20 个下载器的教训,12 个并行已经是我的成本上限,再多就分批跑了。
八、SubAgent 的限制
1. 上下文限制
子 Agent 拿不到主 Agent 全部历史,必须显式传上下文。
这意味着主 Agent 自身必须能”讲清楚任务”,讲不清,子 Agent 就瞎猜。我几次失败都发生在 prompt 偷懒的时候。
2. 协调成本
主 Agent 协调 N 个子 Agent,主 Agent 自身的上下文会爆。
子任务一多,主 Agent 要把每个子任务的上下文、进度、结果都背在身上,几轮下来自己先超限。我的对策是每完成一个子任务,就把它的结论压缩成一行摘要存进文件,让主 Agent 只记摘要。
3. 错误传播
子 Agent 失败,主 Agent 要重试或降级。一个子 Agent 反复失败会拖慢整体。
遇到反复失败的子 Agent,我先降级成”拆更小的任务重试”,还不行就人工接手,别让它无限重试烧钱。
4. 成本
每个子 Agent 都是一次 LLM 调用。20 个子任务 = 20 倍成本。
这是最容易被忽略的一条。并行不是免费的,派发前先算一笔账:这个任务拆几个划算,哪些可以合并。
九、成本优化
1. 复用子 Agent
// 一个 SubAgent 跑 10 个相似任务
const sub = new YouTubeDownloaderAgent()
for (const url of urls) {
await sub.run(url)
}
vs
// 10 个 SubAgent 各跑 1 个
for (const url of urls) {
await dispatch('youtube-downloader', url)
}
后者贵 10 倍。
相似任务复用同一个子 Agent,等于把”建立上下文”的成本摊到了多次执行上。我在批量测试时就是这么干的,一个子 Agent 循环跑几十个用例。
2. 用小模型做简单任务
// 简单格式化用 GPT-3.5
// 复杂生成用 GPT-4
简单任务用小模型,复杂生成用大模型,这是成本优化的分水岭。文档注释、代码格式化这类活,小模型完全够用;架构设计、跨模块重构才值得上大模型。
3. 缓存公共上下文
const systemPrompt = `项目背景...` // 不变
subAgent.system = systemPrompt // 复用
公共上下文是每个子 Agent 都要用的,缓存下来能省掉一大块重复的 token 开销。我把项目背景、技术栈、代码规范做成一个常量,所有子 Agent 共享。
十、SubAgent 与 Function Calling
SubAgent 适合”复杂任务”,Function Calling 适合”工具调用”,两者定位完全不同:
| 维度 | SubAgent | Function Calling |
|---|---|---|
| 任务 | 复杂多步 | 单步工具 |
| 决策 | Agent 自己 | 程序决定 |
| 灵活性 | 高 | 低 |
平台两者都用:
- SubAgent:写大模块;
- Function Calling:调用 API、查数据库。
判断标准就一条:任务要不要 Agent 自己动脑子。要动脑子、要拆步骤,用 SubAgent;只是调个接口、查个表,用 Function Calling 更便宜更快。
十一、最佳实践
1. 任务粒度
- 太粗:1 个 SubAgent 写整个应用(容易跑偏)
- 太细:1 个 SubAgent 写 1 行(成本爆炸)
- 合适:1 个 SubAgent 写 1 个文件或 1 个功能
粒度是我调过最多的参数。写整个应用会跑偏,写 1 行是烧钱,落到”一个文件或一个功能”这个量级,产出质量、合并成本、可验证性三者才平衡。
2. 输入契约
明确告诉子 Agent:
- 输入是什么(参数、文件);
- 输出是什么(函数签名、数据结构);
- 约束(性能、风格、库版本)。
3. 失败处理
async def dispatch_with_retry(task, max_retry=3):
for i in range(max_retry):
try:
return await dispatch(task)
except Exception as e:
if i == max_retry - 1:
# 标记失败,主 Agent 决定怎么办
return {'status': 'failed', 'error': str(e)}
await asyncio.sleep(2 ** i)
指数退避重试这段代码我直接挪用了,重试间隙按 2 的幂次递增,既给了模型”重新思考”的时间,又不会在失败边缘空转。
4. 结果验证
主 Agent 必须验证子 Agent 的结果:
result = await dispatch('write-youtube-downloader')
assert 'class YouTubeDownloader' in result.code
# 不通过 → 重试或修正 prompt
验证这步不能省。我见过子 Agent 交回一份”看起来完整但核心函数压根没写”的结果,没有断言检查就会直接混进主分支。
十二、踩坑
1. 子 Agent 串改接口
主 Agent 让子 Agent 实现函数 A,子 Agent 改函数 B 的签名。必须严格契约。
子 Agent 拿到任务后,为了”让代码能跑”,经常顺手改掉别的模块的接口,结果合并时崩一片。后来契约里写明”只允许新增,禁止修改他人接口”,这类问题才消停。
2. 上下文爆
主 Agent 把整个项目代码塞给子 Agent,token 超限。只传相关部分。
我犯过把整个项目塞给子 Agent 的错,第一轮就报 token 超限。后来改成只传当前任务相关的模型定义和调用关系,问题立刻消失。
3. 调试困难
子 Agent 失败要查原因,但子 Agent 的 prompt 链不直观。用日志记录每个子 Agent 的输入输出。
调试子代理最头疼的是不知道它内部发生了什么。我给每个子 Agent 的输入输出加了日志,失败时直接看它收到的 prompt 和返回结果,一两分钟就能定位是上下文没传够还是模型理解偏了。
4. 并行冲突
两个子 Agent 同时改同一个文件,主 Agent 必须协调。
并行写同一个文件是我遇到过的真事故。后来派活前先检查任务之间有没有文件交集,有交集的要么合并成一个子 Agent,要么明确”先写待定区域,主 Agent 最后合并”。
十三、与 Skills 关系
Skills 是一组预定义能力,SubAgent 是任务执行者:
- Skills:写好的能力(”我是 yt-dlp 专家”)
- SubAgent:执行任务(”去实现 YouTube 下载器”)
平台把常用工具封装为 Skills,让 SubAgent 调用:
# Skill:提供 yt-dlp 最佳实践
# SubAgent:调用 Skill 完成具体任务
我的理解是:Skills 是弹药库,SubAgent 是拿弹药上战场的人。子 Agent 在项目里积累的成功做法,我定期沉淀回 Skills,下次派活时直接引用,子 Agent 的起点一次比一次高。
十四、未来趋势
- 多 Agent 协作框架:AutoGPT、MetaGPT、LangGraph;
- Agent-as-a-Service:云厂商提供专业 Agent;
- 自我改进 Agent:Agent 反思+学习。
这些方向我都在跟。多 Agent 协作框架今年明显变多,但框架只是脚手架,任务怎么拆、上下文怎么传、结果怎么验,还是得靠项目里的人把规则定清楚。工具在变,协作方法论不会过时。
常见问题(FAQ)
Q1:SubAgent 与 RAG 关系?
RAG 提供知识,SubAgent 执行任务。互补。
Q2:多少个子 Agent 合适?
5-10 个最佳。再多主 Agent 协调不来。
Q3:SubAgent 会取代程序员?
不会。SubAgent 是工具,程序员决定用在哪。