第 9 章 · Subagents 子代理委派
本章目标:掌握
useSubagent()定义专家子代理,理解"新鲜上下文委派"的机制与继承规则,学会设计主从协作的多 Agent 架构。
9.1 为什么需要子代理
单一 agent 的上下文窗口是稀缺资源:让它同时做调研、写代码、跑测试,中间产物会把上下文撑爆,注意力也被稀释。
子代理(Subagent)= 有名字的专家委托对象。父 agent 把聚焦任务交给它,子代理在全新的上下文里独立完成,只把最终答案交回来——中间推理、工具调用、文件读取统统不进父会话:
text
父 Agent(主会话)
│ task(name='reproducer', prompt='复现 issue #42…')
▼
子代理 Reproducer(全新上下文)── 独立读文件、跑命令 ──┐
│ │
└── ◀──────── 只有最终结论回传 ──────────────────────┘9.2 声明一个子代理
typescript
// src/agents/case-agent.ts
'use agent';
import { useModel, useSubagent } from '@flue/runtime';
// 注意:Summarizer 故意【不导出】!
// 导出的大写函数会被注册为顶层 agent,
// 而子代理只是父 agent 挂载的一种能力
function Summarizer() {
return '你将支持案例总结成三句话,突出问题与影响。'; // 返回值 = 子代理指令
}
export function CaseAgent() {
useModel('anthropic/claude-sonnet-4-6');
// 三要素:name、description、agent 函数
useSubagent({
name: 'summarizer',
// description 是父模型决定是否委派的唯一依据——像工具描述一样认真写
description: '总结单个支持案例。当需要压缩长对话或案例细节时使用。',
agent: Summarizer,
});
return `
调查支持案例并给出处理建议。
完成调查后,把案例摘要工作委派给 summarizer 子代理。`;
}子代理的 agent 函数就是普通 agent 函数:返回指令、可用 Hook 组合能力。但它是能力而非实体——没有自己的会话 ID、没有 HTTP 路由。
9.3 委派的运行机制
委派由模型驱动。每个 agent 的工具集里都有一个框架自带的 task 工具;已声明的子代理以"Available Agents"目录形式进入系统提示。流程:
- 父模型判断某块工作匹配某子代理 → 调用
task工具(带 name + 任务 prompt); - 运行时在委派时刻全新渲染子代理函数,生成其指令与工具集;
- 子代理作为分离会话在父环境中运行,用自己的上下文窗口干到完成;
- 只有最终消息作为
task结果回到父会话。
两个推论值得牢记:
- 任务 prompt 就是全部简报。子代理看不到父对话,所以父指令必须要求"用完整、自包含的 prompt 委派";
- 任务天然可并行。同一批工具调用并发执行,五个独立检查 = 五个并发子会话,各用各的窗口。
9.4 继承规则:给什么用什么
typescript
'use agent';
import { useModel, useSkill, useSubagent, useTool } from '@flue/runtime';
import { searchIssues } from '../tools/search-issues.ts';
import reproduce from '../skills/reproduce/SKILL.md';
function Reproducer() {
// 子代理自己的能力组合——只有这里声明的才属于它
useSkill(reproduce);
return '按复现技能中的流程验证 issue 是否可复现。';
}
export function Triage() {
useModel('anthropic/claude-sonnet-4-6');
useTool(searchIssues); // ← 父的工具,子代理【不】继承
useSubagent({
name: 'reproducer',
description: '验证 bug 报告是否可复现。',
agent: Reproducer,
// 可选:覆盖继承来的模型(不设则用父的模型)
model: 'anthropic/claude-haiku-4-5',
});
return '分诊 issue。需要确认可复现性时委派给 reproducer。';
}| 继承 ✅ | 不继承 ❌ |
|---|---|
| 沙箱及其内置工具 | 父的对话历史 |
| 工作区上下文(AGENTS.md 等) | 父的指令、自有工具 |
| 父的模型与推理档位* | 父的技能与其他子代理 |
| (*除非定义时覆盖) | 父的持久状态与初始数据 |
一句话:环境继承,对话隔离。子代理的世界 = 你在它的渲染中组合的一切。
9.5 协作模式与限制
typescript
'use agent';
import { useModel, useSubagent } from '@flue/runtime';
function SecurityCheck() { return '对给定模块做安全审计,输出风险清单。'; }
function PerfCheck() { return '分析给定模块的性能瓶颈并量化。'; }
export function ReleaseManager() {
useModel('anthropic/claude-opus-4-6');
// 发布前的并行体检团:三个独立检查互不干扰
useSubagent({ name: 'security', description: '安全审计。发布前必做。', agent: SecurityCheck });
useSubagent({ name: 'perf', description: '性能分析。发现回归时使用。', agent: PerfCheck });
return `
执行发布前检查:
1. 并行委派 security 与 perf 两项检查;
2. 收集双方结论后汇总为放行/拦截建议。
委派时请使用包含目标模块路径的完整 prompt。`; // ← 明确要求自包含简报
}关键约束:
typescript
// 子代理定义的类型签名——理解三要素背后的契约
interface SubagentDefinition {
name: string; // 目录中的唯一标识,同名重复声明会抛错
description: string; // 父模型的委派依据,务必写清"做什么+何时用"
agent: () => string; // 普通 agent 函数,可组合 Hook,但不要导出
model?: string; // 可选:覆盖继承的模型,如 'anthropic/claude-haiku-4-5'
}
// 委派深度上限 4 层:父 → 子 → 孙 → 曾孙,再往下会被运行时拒绝- 同名子代理一次渲染只能声明一个,重复抛错;
- 委派深度上限 4 层(子代理还可以再声明自己的子代理);
- 支持条件声明——阶段翻转后才出现在目录里,运行时会播报变化;
- 应用代码也能委派:
harness.prompt()的指令中点名某个子代理即可强制路由。
本章小结
- 子代理 = 具名的专家委托:新上下文干活,只回传最终答案;
useSubagent({ name, description, agent })三要素;delegate 函数不要导出;- 委派走框架保留的
task工具;prompt 是全部简报,务必自包含; - 继承沙箱/工作区/模型,不继承对话与父级资源;深度上限 4 层;
- 多个子代理 + 并行 task = 天然的多专家流水线。
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. 子代理完成后,哪些内容会回到父 agent 的会话?
2. 为什么 Summarizer 这类子代理函数故意不导出?
3. 关于委派时的任务 prompt,正确理解是?
4. 子代理默认会继承父 agent 的哪一项?
🛠️ 动手实践
- 给第 6 章的客服 agent 加一个"退款专员"子代理,对比委派前后主会话的 token 消耗。
- 设计两个可并行的检查子代理,让模型同批发起两个 task,观察耗时是否接近单项耗时。
- 验证隔离性:让子代理尝试访问父会话中提到的临时变量信息,确认它确实看不到。
至此 Flue 前 9 章的核心编程模型完整了:Agent 函数 + Hook 组合出模型、工具、技能、MCP、子代理五大能力面。后续章节将走向沙箱、持久化与生产部署。