Appearance
课 3 · SubAgent 与任务委派
本课目标
理解什么时候该把任务拆给 SubAgent,学会用受限工具集做安全委派。课后你会拿到一个能把独立检索子任务委派出去的 Agent。
这一课先掌握“什么时候该拆、为什么要做最小授权”即可,不要求一上来把委派系统做成无限嵌套的复杂架构。
前两课解决了两个问题:
- Agent 怎么调用工具
- Agent 怎么做多步循环
但还有一个常见问题没有解决:一个 Agent 什么都自己做,容易又长又乱。
当任务里包含多个彼此独立的子问题时,让主 Agent 亲自处理所有细节,往往会带来三个问题:
- 上下文越来越长,模型容易丢失重点
- 工具权限太大,不利于安全控制
- 主 Agent 同时负责拆解、执行、汇总,职责不清
这时候就适合引入 SubAgent。
什么是 SubAgent
SubAgent 不是“更强的大模型”,而是一个被主 Agent 委派、只负责局部任务的小执行单元。
它更像是把一个局部任务隔离出去的“小工作线程”或“小执行单元”:目标更单一、上下文更短、权限更小。对前端工程师来说,这节课的重点不是“又学一个新模型技巧”,而是学会把任务边界和权限边界拆清楚。
主 Agent 的职责:
- 理解用户总目标
- 判断是否需要委派
- 决定给 SubAgent 哪些工具
- 汇总最终结果并回复用户
SubAgent 的职责:
- 只完成一个明确的子任务
- 只使用被授权的工具
- 返回简明的执行结果
什么时候该拆 SubAgent
适合拆的场景
- 多个独立检索子问题
- 需要最小授权
- 想降低主 Agent 上下文压力
不适合拆的场景
- 子任务强依赖共享状态
- 必须严格串行推进
- 任务边界根本不清楚
最小实现思路
核心做法很简单:
- 主 Agent 暴露一个
spawnSubAgent工具 - 调用这个工具时传入
task和toolNames - 代码层根据
toolNames做白名单过滤 - 用受限工具集启动一个单独的
generateText - SubAgent 返回
{ text, steps, toolCalls } - 主 Agent 再负责最终汇总
你可以把它理解成:主 Agent 负责总控,SubAgent 负责局部代办;课程并不是要你把所有事都拆成 SubAgent,而是在“上下文太长”或“权限不该全开”时,多一个隔离手段。
SubAgent Helper
typescript
import { generateText, stepCountIs, type Tool } from 'ai'
export async function spawnSubAgent(options: {
task: string
tools: Record<string, Tool>
maxSteps?: number
stopWhen?: ReturnType<typeof stepCountIs>
}) {
const stopWhen = options.stopWhen ?? stepCountIs(options.maxSteps ?? 10)
const result = await generateText({
model,
tools: options.tools,
stopWhen,
system: `
你是一个专注执行子任务的 SubAgent。
规则:
- 只完成分配给你的具体任务
- 不主动扩展范围
- 如果失败,说明原因
- 完成后返回简明摘要
`.trim(),
messages: [{ role: 'user', content: options.task }],
})
return {
text: result.text,
steps: result.steps.length,
toolCalls: result.steps.flatMap((step) =>
step.staticToolCalls.map((tc) => ({
name: tc.toolName,
args: tc.input,
}))
),
}
}关键点有两个:
stopWhen: stepCountIs(...)限制 SubAgent 最多执行多少步tools是外部传入的受限工具集,不是主 Agent 的全量工具
白名单授权
真正的安全点不在 prompt,而在代码层白名单过滤。
这一点对前端工程师尤其重要:提示词可以约束模型“应该怎么做”,但真正能限制权限的还是代码。像前端里只把允许的 action 暴露给某个模块一样,这里也要把 SubAgent 能拿到的工具集合写死在应用层。
typescript
const availableTools: Record<string, Tool> = {
searchKnowledge,
}
const subTools: Record<string, Tool> = {}
for (const name of toolNames) {
if (availableTools[name]) {
subTools[name] = availableTools[name]
}
}这样即使模型生成了其他工具名,真正传给 SubAgent 的也只有白名单工具。
主 Agent 中如何暴露委派能力
typescript
spawnSubAgent: tool({
description: '将独立检索子任务委派给受限工具的 SubAgent 执行',
inputSchema: z.object({
task: z.string().describe('要执行的子任务描述'),
toolNames: z.array(z.string()).describe('授予 SubAgent 的工具白名单'),
}),
execute: async ({ task, toolNames }) => {
const availableTools: Record<string, Tool> = {
searchKnowledge,
}
const subTools: Record<string, Tool> = {}
for (const name of toolNames) {
if (availableTools[name]) {
subTools[name] = availableTools[name]
}
}
return spawnSubAgent({
task,
tools: subTools,
stopWhen: stepCountIs(10),
})
},
})注意这里的设计取舍:
- 主 Agent 可以决定要不要委派
- 主 Agent 可以决定给哪些工具
- 但 SubAgent 不能再递归创建新的 SubAgent
也就是说,这一课强调的是“受控委派”,不是把系统做成层层递归的多级代理网络。
实际例子
用户说:
分别检索 React 性能优化、TypeScript 泛型、Next.js Server Components,然后给我一份学习建议。
主 Agent 的理想行为是:
- 识别出这是三个独立检索维度
- 调用
spawnSubAgent - 给 SubAgent 一个明确子任务,例如“检索 React 性能优化相关内容并总结核心点”
- 授权工具
['searchKnowledge'] - 拿到结果后继续汇总
失败处理
SubAgent 失败时,不应该把异常细节直接甩给用户。更好的方式是返回结构化摘要:
typescript
{
text: '未找到与 React 性能优化相关的足够信息',
steps: 2,
toolCalls: [
{ name: 'searchKnowledge', args: { query: 'React 性能优化' } }
]
}然后由主 Agent 决定换关键词重试,或者直接告诉用户“知识库里信息不足”。
并入基础项目
这一课并入 basic/project 后,主线项目会多一个能力:
- 主 Agent 在处理多角度检索任务时,可以自动调用
spawnSubAgent - SubAgent 首版只允许使用
searchKnowledge - 最终回答仍由主 Agent 汇总
本课产物
- ✅ 理解什么时候该拆 SubAgent
- ✅ 学会
spawnSubAgent的最小实现 - ✅ 学会用
toolNames做白名单授权 - ✅ 理解主 Agent 与 SubAgent 的职责边界
- ✅ 把委派能力并入
basic/project主线项目
完整代码在 basic/examples/05-agent/03-subagent/index.ts。
试试看
bash
cd daqi-ai-agent
pnpm exec tsx basic/examples/05-agent/03-subagent/index.ts- 先输入一个简单检索问题,观察主 Agent 是否直接回答
- 再输入一个多角度检索问题,观察是否触发
spawnSubAgent - 故意给一个知识库中不存在的主题,确认系统会说信息不足,而不是编造
- 观察 SubAgent 返回的
toolCalls,理解它到底做了哪些动作
面试追问
Q:为什么不让主 Agent 直接做完所有事?
因为当任务包含多个独立子问题时,主 Agent 同时负责拆解、执行、汇总,容易让上下文膨胀、权限过大、职责不清。SubAgent 的价值不只是多一个模型调用,而是让执行边界更清楚。
Q:为什么一定要做白名单授权?
因为 prompt 不是安全边界,代码才是。只有在代码里过滤 toolNames,才能确保 SubAgent 实际拿到的工具确实是最小集合。