任务委派与子 Agent 并行处理:让 Agent 学会"带团队"
这不是一个"给 Agent 多加几个工具"的简单升级。 我们要做的,是让 Agent 从一个"什么都自己扛"的独行侠,进化成一个能 分解任务、委派子 Agent、并行执行 的团队领袖。 整个过程基于前三篇文章的项目,增量改动涉及
src/subagents.ts(新增)、src/agents.ts、src/index.ts和workspace/AGENTS.md。
目录
- 回顾:前三篇文章结束时 Agent 能做什么?
- 单 Agent 的天花板:为什么需要任务委派?
- Subagents:给 Agent 配一支专业团队
- write_todos:先计划,再执行
- AsyncSubAgent:后台长任务不阻塞
- HarnessProfile:给不同模型因材施教
- 三个新测试:验证任务委派能力
- 回顾与展望
1. 回顾:前三篇文章结束时 Agent 能做什么?
| 阶段 | 能力 | 新增工具 |
|---|---|---|
| 第一篇 | 计算器、报时、流式输出、多轮对话、Extended Thinking | calculator、get_current_time |
| 第二篇 | 文件读写、搜索、执行 shell 命令 | ls、read_file、write_file、edit_file、glob、grep、execute |
| 第三篇 | 持久记忆、按需加载领域知识、长对话自动压缩 | Memory (AGENTS.md)、Skills (SKILL.md)、Summarization |
到了第四篇,我们要解决三个新问题:
- 上下文爆炸 — 一个 Agent 干所有活,对话历史越来越长,上下文窗口迟早不够用
- 串行瓶颈 — 同时需要搜索文件和写代码?只能排队一件件做
- 职责混杂 — 系统提示词塞满了各种角色指令,模型容易"分心"
2. 单 Agent 的天花板:为什么需要任务委派?
2.1 一个真实场景
假设用户说:
"帮我分析 workspace 目录下所有的 TypeScript 文件,统计代码行数,写一份报告,然后写一个工具函数把报告里的数据格式化。"
一个 Agent 要完成这个任务,流程是这样的:
[用户提问]
↓
[Agent 思考] → 用 glob 搜索 *.ts 文件
↓
[Agent 执行] → glob("/**/*.ts")
↓
[Agent 思考] → 逐个读取文件
↓
[Agent 执行] → read_file("mergeArrays.ts")
↓
[Agent 思考] → 统计行数
↓
[Agent 执行] → execute("wc -l ...")
↓
[Agent 思考] → 写报告
↓
[Agent 执行] → write_file("report.md")
↓
[Agent 思考] → 写工具函数
↓
[Agent 执行] → write_file("formatReport.ts")
↓
[Agent 思考] → 生成最终回复
问题在哪?
- 每一步都往上下文里塞消息 — 5 次 tool_call + 5 次 tool_result = 10 条消息,加上思考过程,轻松吃掉几千 token
- 全程串行 — 搜索文件、统计行数、写报告、写代码,排队执行,总耗时 = 所有步骤之和
- 上下文污染 — 搜索过程中看到的文件内容,和最终写代码需要的上下文混在一起,模型注意力被稀释
2.2 解法:任务委派
如果我们有一支"团队"呢?
[用户提问]
↓
[主 Agent(项目经理)]
↓ 分析任务,分解为 3 个子任务
↓
├──→ [researcher 子 Agent] 搜索 + 统计行数
├──→ [coder 子 Agent] 写工具函数
└──→ [reviewer 子 Agent] 审查代码质量
↓
[主 Agent 汇总结果]
↓
[最终回复]
核心变化:
- 主 Agent 只负责理解意图、分解任务、汇总结果(轻装上阵)
- 子 Agent 各司其职,在独立的上下文窗口中完成专业任务(互不干扰)
- 并行执行 — 多个子 Agent 可以同时干活(总耗时 = 最慢的那个)
2.3 类比
| 模式 | 类比 | 优点 | 缺点 |
|---|---|---|---|
| 单 Agent | 一个人干所有活 | 简单、无协调成本 | 容易累(上下文爆炸)、慢(串行) |
| 子 Agent | 项目经理 + 专家团队 | 快(并行)、专注(上下文隔离) | 需要协调、子 Agent 看不到全局 |
3. Subagents:给 Agent 配一支专业团队
3.1 新增文件:src/subagents.ts
这一层我们来做一件关键的事——定义子 Agent。代码在新增的 src/subagents.ts 中。
deepagents 提供了 SubAgent 接口来定义子 Agent,三个必填字段各司其职:
| 字段 | 作用 | 类比 |
|---|---|---|
name | 唯一标识,主 Agent 通过名字选择子 Agent | "你叫什么?" |
description | 描述能力,主 Agent 据此决定何时委派 | "你能干什么?" |
systemPrompt | 角色定义,规范子 Agent 的行为 | "你怎么干活?" |
3.2 完整代码:三个专业子 Agent
// src/subagents.ts
import type { SubAgent, AsyncSubAgent } from 'deepagents';
// ============================================================
// 子 Agent 1:研究员 — 只读,专注搜索和分析
// ============================================================
export const researcher: SubAgent = {
name: 'researcher',
// description 是主 Agent 判断"该不该把任务交给你"的依据
// 写得越具体,主 Agent 的选择越准确
description: '研究助手,擅长信息收集、文件搜索和内容分析。当需要查找资料、阅读文件、总结内容时,委派给这个子 Agent。',
systemPrompt:
'你是一个研究助手。你的任务是收集信息、搜索文件、阅读内容并给出详细的分析报告。\n' +
'工作流程:\n' +
'1. 理解任务要求\n' +
'2. 使用 ls/glob/grep/read_file 搜索和阅读相关文件\n' +
'3. 整理信息,给出结构化的分析报告\n' +
'注意:你只负责研究和报告,不要修改任何文件。',
};
// ============================================================
// 子 Agent 2:编码员 — 完整的文件操作和 shell 能力
// ============================================================
export const coder: SubAgent = {
name: 'coder',
description: '编码助手,擅长编写代码、创建文件、执行 shell 命令。当需要写代码、修改文件、运行脚本时,委派给这个子 Agent。',
systemPrompt:
'你是一个编码助手。你的任务是编写高质量的代码、创建文件、执行命令。\n' +
'工作流程:\n' +
'1. 理解任务需求\n' +
'2. 使用 write_file/edit_file 创建或修改代码\n' +
'3. 使用 execute 运行和测试代码\n' +
'4. 修复问题直到代码正确运行\n' +
'编码规范:TypeScript,camelCase 命名,中文注释。',
};
// ============================================================
// 子 Agent 3:审查员 — 只有只读权限
// ============================================================
export const reviewer: SubAgent = {
name: 'reviewer',
description: '代码审查助手,擅长代码审查、质量检查、发现潜在问题。当需要审查代码质量、检查最佳实践时,委派给这个子 Agent。',
systemPrompt:
'你是一个代码审查助手。你的任务是审查代码质量、发现潜在问题、提出改进建议。\n' +
'审查维度:\n' +
'1. 代码正确性 — 逻辑是否正确,边界情况是否处理\n' +
'2. 代码可读性 — 命名是否清晰,注释是否充分\n' +
'3. 性能 — 是否有明显的性能问题\n' +
'4. 安全性 — 是否有安全隐患\n' +
'5. 最佳实践 — 是否符合 TypeScript/Node.js 最佳实践\n' +
'注意:你只负责审查和建议,不要修改任何文件。',
// 安全设计:审查者不应该同时是修改者
// permissions 会替换主 Agent 的权限(不是合并),所以这里只给 read
permissions: [
{ operations: ['read'], paths: ['/**'] },
],
};
// 导出子 Agent 数组,方便在 agents.ts 中统一传入
export const subagents: SubAgent[] = [researcher, coder, reviewer];
3.3 逐段拆解
| 部分 | 做了什么 | 为什么 |
|---|---|---|
researcher | 定义研究助手,systemPrompt 指定了工作流程(搜索→阅读→分析) | 明确的工作流程让子 Agent 知道"第一步干什么、第二步干什么",不会漫无目的 |
coder | 定义编码助手,systemPrompt 包含编码规范 | 编码规范(TypeScript、camelCase、中文注释)和 AGENTS.md 中的用户偏好一致 |
reviewer | 定义审查助手,额外设置了 permissions | 只读权限 — 即使模型"想"修改文件,权限系统也会阻止它 |
subagents 数组 | 导出三个子 Agent 的数组 | 方便在 agents.ts 中一行传入 subagents |
3.4 上下文隔离:子 Agent 为什么要有独立窗口?
这是子 Agent 最重要的设计决策之一。
┌──────────────────────────────────────────────┐
│ 主 Agent 的上下文窗口 │
│ │
│ [用户消息] [思考] [task调用] [子Agent结果] │
│ │
│ 只看到:任务描述 + 最终结果 │
│ 看不到:子 Agent 中间的搜索、试错过程 │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ researcher 子 Agent 的上下文窗口 │
│ │
│ [任务描述] [glob结果] [read结果] [分析] │
│ │
│ 只看到:自己的任务 + 自己的工具调用 │
│ 看不到:主 Agent 的其他对话 │
└──────────────────────────────────────────────┘
好处:
- 节省主 Agent 的上下文空间 — 搜索过程中读到的 10 个文件内容不会塞进主 Agent 的窗口
- 减少干扰 — 子 Agent 不会被主 Agent 的其他对话"分心"
- 独立试错 — 子 Agent 可以自由尝试,失败了也不影响主对话
3.5 串行、并发、并行:三个概念一次说清楚
这三个词经常被混用,先厘清:
每个任务 = 计算 + 等待 I/O
串行(Sequential)
单线程,排队执行,等 I/O 时也干等着
假设每个任务 = 1 单位计算 + 2 单位等待
时间: 0 1 2 3 4 5
线程: [A算][A等][A等][B算][B等][B等]
总耗时 = (1+2) × 3 = 6 单位
并发(Concurrency)
单线程,A 等 I/O 时切去算 B,B 等 I/O 时切去算 C
同样假设每个任务 = 1 单位计算 + 2 单位等待
时间: 0 1 2 3
线程: [算A][算B][算C][等I/O]
A的等待期: [───────────────────]
B的计算期: [算B]
C的计算期: [算C]
看图:
- A 的等待期从时间 1 开始,持续到时间 3
- B 的计算在时间 1(在 A 的等待期内)
- C 的计算在时间 2(也在 A 的等待期内)
A 的等待期覆盖了 B 和 C 的计算,所以总耗时 = 3(计算)+ 1(等待)= 4 单位,
比串行的 6 单位省了 2 单位。
并行(Parallelism)
多线程,真正同时执行
线程1:──[A算][A等]──────→
线程2:──────[B算][B等]──→
线程3:──────────[C算][C等]→
总耗时 = max(A, B, C)
关键区别:并发是"交替执行 + 利用等待时间",并行是"真正同时执行"。并发靠的是重叠 I/O 等待,并行靠的是多核 CPU。
在 Agent 场景中,三种模式对应:
| 模式 | 做法 | 总耗时 |
|---|---|---|
| 串行 | 先委派 researcher,等完成后,再委派 coder | researcher + coder |
| 并发 | 同时发出多个 LLM API 调用,利用网络等待重叠 | 接近 max(researcher, coder) |
| 并行 | 多个 CPU 核心同时处理 | max(researcher, coder) |
deepagents 框架支持在主 Agent 一次回复中调用多次 task 工具,框架会在内部并发执行这些子 Agent — 多个 LLM API 调用同时发出,利用网络 I/O 的等待时间重叠来缩短总耗时。两个子任务没有依赖关系时,它们可以同时开始。
注意:Node.js 是单线程的,所以子 Agent 的"同时执行"本质是 I/O 并发,不是多核 CPU 并行。但因为子 Agent 的瓶颈在网络等待(等 LLM 返回),不在 CPU 计算,所以并发已经能达到接近并行的效果 — 对用户来说,就像同时有多个人在干活。
3.6 集成到 createDeepAgent
定义好子 Agent 后,在 src/agents.ts 中只需要两处改动:
改动 1:导入 subagents
// src/agents.ts — 新增导入
import { subagents, asyncSubagents } from './subagents';
改动 2:传入 createDeepAgent
// src/agents.ts — createDeepAgent 配置(只展示新增部分)
export const agent = createDeepAgent({
model,
tools: [calculatorTool, getCurrentTimeTool],
backend,
memory: ['/AGENTS.md'],
skills: ['/skills/'],
// ↓↓↓ 新增:子 Agent 列表 ↓↓↓
// createDeepAgent 收到 subagents 后会自动:
// 1. 创建一个 task 工具,让主 Agent 可以委派任务
// 2. 把每个子 Agent 的 name 和 description 注入系统提示词
// 3. 额外添加一个 GENERAL_PURPOSE_SUBAGENT(通用子 Agent)兜底
subagents,
// ↑↑↑ 就这一行 ↑↑↑
systemPrompt:
'你是一个乐于助人的 AI 助手。\n' +
// ...前面的内容省略...
// ↓↓↓ 新增:告诉主 Agent 有哪些子 Agent 可用 ↓↓↓
'你有三个专业子 Agent 可以委派任务:\n' +
' - researcher:研究助手,擅长信息收集和分析\n' +
' - coder:编码助手,擅长写代码和执行命令\n' +
' - reviewer:审查助手,擅长代码审查和质量检查\n' +
'当任务比较复杂时,先用 write_todos 规划任务,然后用 task 工具委派给合适的子 Agent。\n' +
'简单的任务(如单步计算、简单文件操作)自己处理即可,不需要委派。',
checkpointer: new MemorySaver()
});
3.7 task 工具的调用方式
主 Agent 通过 task 工具委派任务,参数很简单:
{
"subagent_type": "researcher",
"description": "搜索 workspace 目录下所有 TypeScript 文件,统计每个文件的代码行数"
}
subagent_type— 选择哪个子 Agent(对应SubAgent.name)description— 任务描述,告诉子 Agent 具体要做什么
子 Agent 收到描述后,在独立的上下文窗口中自主执行,完成后把最终结果返回给主 Agent。
3.8 踩坑记录
踩坑记录:最初我把
subagents传入了createDeepAgent,但主 Agent 从来不调用task工具——它总是自己硬扛所有任务。为什么? 因为
createDeepAgent虽然会自动把子 Agent 信息注入系统提示词,但注入的内容很简略(只有名字和 description),模型不知道"什么时候该委派"。怎么修的? 在
systemPrompt中明确列出三个子 Agent 的能力和使用建议("当任务比较复杂时,先用 write_todos 规划任务,然后用 task 工具委派")。加了这段话后,主 Agent 就能准确地判断何时委派了。教训:不要假设模型"自动就知道"该用什么工具——系统提示词里写清楚的行为,模型才会执行。
踩坑记录:
reviewer子 Agent 设置了permissions: [{ operations: ['read'], paths: ['/**'] }],但主 Agent 的backend是LocalShellBackend(支持execute)。问题:
permissions不能与execute能力共存——shell 命令可以绕过路径限制(cat /private/secret.txt),所以 deepagents 会报ConfigurationError。怎么修的?
reviewer的permissions只影响 reviewer 自己的文件操作工具(ls/read/write/edit),不影响execute。但如果主 Agent 也设置了permissions,就会和LocalShellBackend冲突。解决方案:子 Agent 可以单独设 permissions,主 Agent 不设。
3.9 验证
取消 src/index.ts 中测试 11 的注释,运行 pnpm dev:
// 取消注释后,测试 11 的代码:
console.log('--- 测试 11:同步子 Agent 委派(task 工具) ---');
console.log('用户:帮我研究一下 workspace 目录的项目结构');
const stream11 = await agent.stream(
{ messages: [{ role: 'user', content: '帮我研究一下 workspace 目录的项目结构,给我一个详细的分析报告' }] },
{ ...config, streamMode: 'messages' },
);
await printStream(stream11);
预期行为:
- 主 Agent 判断这是研究任务,选择
researcher - 调用
task({ subagent_type: 'researcher', description: '...' }) - researcher 在独立上下文中搜索和分析
- 结果返回主 Agent,主 Agent 汇总回复
4. write_todos:先计划,再执行
4.1 为什么需要任务规划?
当任务比较复杂时,Agent 容易"丢步骤"——上下文太长,模型可能忘了中间某一步该做什么。
write_todos 是 createDeepAgent 内置的任务规划工具(不需要手动创建),它让 Agent 在执行前先"列计划":
write_todos({
todos: [
{ content: "搜索所有 TypeScript 文件", status: "pending" },
{ content: "统计每个文件的代码行数", status: "pending" },
{ content: "生成分析报告", status: "pending" },
{ content: "保存报告到 report.md", status: "pending" }
]
})
每个 todo item 有三种状态:
| 状态 | 含义 |
|---|---|
pending | 待执行 |
in_progress | 正在执行 |
completed | 已完成 |
Agent 每完成一步,就调用 write_todos 更新状态。这样:
- Agent 自己不会"忘步骤"(todo list 是持久化的状态)
- 用户能看到执行进度(哪些完成了,哪些还没做)
- 调试更容易(可以看到 Agent 卡在哪一步)
4.2 任务分解方法论
怎么把一个大任务拆成好的小任务?几个原则:
1. MECE 原则(互不重叠、完全穷尽)
❌ 错误拆分:
- 搜索文件
- 读取文件 ← 和"搜索文件"有重叠
- 统计行数
- 写报告 ← 漏了"保存文件"
✅ 正确拆分:
- 搜索所有 TypeScript 文件(输入:目录 → 输出:文件列表)
- 统计每个文件的代码行数(输入:文件列表 → 输出:行数数据)
- 生成分析报告(输入:行数数据 → 输出:报告内容)
- 保存报告到文件(输入:报告内容 → 输出:report.md)
2. 每步有明确的输入和输出
❌ 模糊: "处理数据"
✅ 清晰: "读取 workspace 下所有 .ts 文件,统计每个文件的行数,输出表格"
3. 粒度适中
太粗 = 没有分解的意义,太细 = 管理成本高于执行成本。经验法则:一个子任务应该在 1-5 步 tool_call 内完成。
4.3 write_todos 和子 Agent 的配合
最佳实践是:先用 write_todos 规划,再用 task 委派:
[用户] 帮我分析项目结构,写一份报告,再写一个格式化工具
[主 Agent 思考]
→ 这个任务可以拆成 3 步
→ 调用 write_todos 列出计划
[主 Agent 执行]
→ 步骤 1:委派 researcher 分析项目结构
→ 步骤 2:委派 coder 写格式化工具
→ 步骤 3:自己汇总结果,回复用户
[每完成一步,更新 todo 状态]
4.4 持久记忆中的任务偏好
我们在 workspace/AGENTS.md 中新增了任务处理偏好:
## 任务处理偏好
- 遇到复杂任务时,先用 write_todos 规划步骤,再逐步执行
- 需要信息收集时,优先委派给 researcher 子 Agent
- 需要写代码时,优先委派给 coder 子 Agent
- 代码完成后,委派给 reviewer 子 Agent 审查
这些偏好被 Memory 中间件加载后注入系统提示词,Agent 从此"记住"了用户希望它先规划再执行的习惯。
4.5 验证
取消 src/index.ts 中测试 10 的注释,运行 pnpm dev:
console.log('--- 测试 10:write_todos 任务规划 ---');
console.log('用户:帮我分析 workspace 目录下的所有 TypeScript 文件,统计代码行数,并生成一份报告');
const stream10 = await agent.stream(
{ messages: [{ role: 'user', content: '帮我分析 workspace 目录下的所有 TypeScript 文件,统计代码行数,并生成一份报告保存到 workspace/report.md' }] },
{ ...config, streamMode: 'messages' },
);
await printStream(stream10);
预期行为:
- Agent 先调用
write_todos列出计划 - 逐步执行:搜索文件 → 统计行数 → 生成报告 → 保存文件
- 每完成一步更新 todo 状态
5. AsyncSubAgent:后台长任务不阻塞
5.1 同步 vs 异步子 Agent
前面介绍的 SubAgent 是同步的——主 Agent 调用 task 后,必须等子 Agent 完成才能继续。对于秒级任务没问题,但如果子任务要跑几分钟甚至几小时呢?
AsyncSubAgent 解决这个问题:
┌──────────────────────────────────────────────────────┐
│ 同步 SubAgent │
│ │
│ 主 Agent ──task──→ 子 Agent ──结果──→ 主 Agent │
│ [阻塞等待................] │
└──────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ 异步 AsyncSubAgent │
│ │
│ 主 Agent ──start_async_task──→ 远程服务器 │
│ [不阻塞,继续做其他事] │
│ │
│ 主 Agent ──check_async_task──→ 查询状态 │
│ [随时可以查] │
│ │
│ 远程服务器 ──完成──→ 主 Agent 收到结果 │
└──────────────────────────────────────────────────────┘
5.2 完整代码:异步子 Agent 示例配置
代码在 src/subagents.ts 底部(当前为注释状态,需要远程服务器才能启用):
// src/subagents.ts — AsyncSubAgent 示例配置
// ⚠️ 需要部署 Agent Protocol 兼容服务器(如 LangGraph Platform)
// 和 SubAgent 的关键区别是 graphId — 它指向远程服务器上的一个 Agent 图
// 本地不需要运行子 Agent 的代码,所有执行都在远程服务器上完成
const asyncResearcher: AsyncSubAgent = {
name: 'async-researcher',
description: '异步研究助手,适合长时间的信息收集任务',
graphId: 'research_agent', // 远程服务器上的 graph 名称
url: 'https://your-langgraph-server.example.com', // Agent Protocol 服务器地址
headers: {
'Authorization': `Bearer ${process.env.LANGGRAPH_API_KEY}`,
},
};
export const asyncSubagents: AsyncSubAgent[] = [asyncResearcher];
5.3 五个异步工具
AsyncSubAgent 提供五个工具(而不是同步的一个 task):
| 工具 | 作用 |
|---|---|
start_async_task | 启动一个后台任务 |
check_async_task | 查询任务状态(running / success / error) |
update_async_task | 给运行中的任务发送追加消息 |
cancel_async_task | 取消任务 |
list_async_tasks | 列出所有任务 |
任务状态流转:
pending → running → success
→ error
→ cancelled
→ timeout
→ interrupted
5.4 适用场景对比
| 场景 | 同步 SubAgent | 异步 AsyncSubAgent |
|---|---|---|
| 搜索文件、读内容 | ✅ | ❌ 杀鸡用牛刀 |
| 写代码、执行测试 | ✅ | ❌ |
| 大规模数据分析(分钟级) | ⚠️ 会阻塞 | ✅ |
| 爬虫任务(小时级) | ❌ 不现实 | ✅ |
| 需要人工审批的流程 | ⚠️ | ✅ |
生产建议:异步子 Agent 需要部署 Agent Protocol 兼容服务器(如 LangGraph Platform)。如果没有远程服务器,使用同步 SubAgent 即可满足大部分需求。本项目中
asyncSubagents数组为空,代码中保留了示例配置供参考。
6. HarnessProfile:给不同模型因材施教
6.1 为什么需要 Profiles?
不同的 LLM 有不同的"脾气":
- Claude 喜欢详细的工具描述
- GPT 系列对简洁的描述反应更好
- qwen 系列可能需要额外的中文提示
HarnessProfile 让你可以针对特定模型调整 Agent 的行为,而不需要改代码。
6.2 完整代码:创建和注册 Profile
代码在 src/agents.ts 中,位于 createDeepAgent 调用之前:
// src/agents.ts — HarnessProfile 配置
import {
createDeepAgent,
LocalShellBackend,
createHarnessProfile, // ← 新增导入
registerHarnessProfile, // ← 新增导入
} from 'deepagents';
// 创建一个 Profile:为 qwen 系列模型定制行为
const qwenProfile = createHarnessProfile({
// 追加系统提示词:提醒 qwen 模型注意任务委派
// systemPromptSuffix 会被追加到系统提示词末尾
systemPromptSuffix: '\n\n当任务复杂时,优先使用 task 工具委派给子 Agent 处理。',
});
// 注册到全局(key 支持通配符)
// 格式:provider/model 或 provider/* 通配
// 注册后,当 Agent 使用的模型匹配 anthropic/qwen* 时,自动应用这个 Profile
registerHarnessProfile('anthropic/qwen*', qwenProfile);
6.3 Profile 可以控制什么?
| 配置项 | 作用 |
|---|---|
systemPromptSuffix | 追加系统提示词 |
toolDescriptions | 改写工具描述(适配不同模型的偏好) |
generalPurposeSubagent | 配置通用子 Agent 的行为 |
maxInputTokens | 模型的最大输入 token 数(影响摘要压缩策略) |
兼容性说明:HarnessProfile 是 Beta 功能,API 可能在未来版本中变化。大多数场景下,默认 Profile 已经够用。
6.4 验证
运行 pnpm dev,启动日志中应该看到:
📋 HarnessProfile: 已为 qwen 系列模型注册自定义配置
由于本项目使用的底层模型是 qwen3.7-plus(匹配 anthropic/qwen*),这个 Profile 会自动生效。
7. 三个新测试:验证任务委派能力
7.1 测试 10:write_todos 任务规划
代码在 src/index.ts 中(默认注释,取消注释后运行)。
// src/index.ts — 测试 10
console.log('--- 测试 10:write_todos 任务规划 ---');
console.log('用户:帮我分析 workspace 目录下的所有 TypeScript 文件,统计代码行数,并生成一份报告');
const stream10 = await agent.stream(
{ messages: [{ role: 'user', content: '帮我分析 workspace 目录下的所有 TypeScript 文件,统计代码行数,并生成一份报告保存到 workspace/report.md' }] },
{ ...config, streamMode: 'messages' },
);
await printStream(stream10);
预期行为:
- Agent 先调用
write_todos列出计划 - 逐步执行:搜索文件 → 统计行数 → 生成报告 → 保存文件
- 每完成一步更新 todo 状态
7.2 测试 11:同步子 Agent 委派
// src/index.ts — 测试 11
console.log('--- 测试 11:同步子 Agent 委派(task 工具) ---');
console.log('用户:帮我研究一下 workspace 目录的项目结构');
const stream11 = await agent.stream(
{ messages: [{ role: 'user', content: '帮我研究一下 workspace 目录的项目结构,给我一个详细的分析报告' }] },
{ ...config, streamMode: 'messages' },
);
await printStream(stream11);
预期行为:
- Agent 判断这是一个研究任务,选择
researcher子 Agent - 调用
task({ subagent_type: 'researcher', description: '...' }) - researcher 在独立上下文中搜索和分析
- 结果返回主 Agent,主 Agent 汇总回复
7.3 测试 12:多子 Agent 协作
// src/index.ts — 测试 12
console.log('--- 测试 12:多子 Agent 协作(编码 + 审查) ---');
console.log('用户:写一个 flattenArray 函数,写完帮我审查一下');
const stream12 = await agent.stream(
{ messages: [{ role: 'user', content: '写一个 TypeScript 函数 flattenArray,把多维数组展平成一维数组,要有类型注解和注释。写完后帮我审查一下代码质量' }] },
{ ...config, streamMode: 'messages' },
);
await printStream(stream12);
预期行为:
- Agent 先用
write_todos规划:编码 → 审查 - 委派
coder写代码 - 委派
reviewer审查代码 - 汇总审查结果,回复用户
这就是一个典型的"编码-审查"工作流——编码和审查由不同的子 Agent 负责,主 Agent 充当项目经理协调两方。
7.4 验证
# 取消 src/index.ts 中测试 10、11、12 的注释后运行
pnpm dev
预期输出:三个测试场景依次执行,分别验证 write_todos 任务规划、同步子 Agent 委派、多子 Agent 协作。
8. 回顾与展望
8.1 本文新增了什么?
| 能力 | 实现方式 | 代码位置 |
|---|---|---|
| 同步子 Agent | SubAgent 接口 + task 工具 | src/subagents.ts |
| 任务规划 | write_todos 内置工具 | createDeepAgent 自动集成 |
| 异步子 Agent | AsyncSubAgent 接口 | src/subagents.ts(示例配置) |
| 模型配置档案 | HarnessProfile | src/agents.ts |
| 任务处理偏好 | AGENTS.md 新增段落 | workspace/AGENTS.md |
8.2 项目文件结构(截至本文)
lingshi/
├── src/
│ ├── index.ts # 入口文件,12 个测试场景
│ ├── agents.ts # Agent 配置(model + backend + memory + skills + subagents + profile)
│ ├── tools.ts # 自定义工具(calculator + get_current_time)
│ └── subagents.ts # 子 Agent 定义(researcher + coder + reviewer) ← 新增
├── workspace/
│ ├── AGENTS.md # 持久记忆(含任务委派偏好)
│ ├── skills/ # 领域知识库
│ └── ...
├── 1. langChain + TypeScript 实战.md
├── 2. LocalShellBackend 与命令执行.md
├── 3. Memory Skills 与上下文工程.md
└── 4. 任务委派与子 Agent 并行处理.md ← 本文
8.3 完整运行
pnpm dev
启动日志中应该看到:
Backend 已创建,工作目录: /path/to/workspace
📋 HarnessProfile: 已为 qwen 系列模型注册自定义配置
Agent 已创建,可用工具:calculator、get_current_time + 文件系统工具 + execute
📝 Memory: /AGENTS.md(持久记忆已加载)
📚 Skills: /skills/(product-faq / refund-policy / code-review)
🔄 Summarization: 超过 20 条消息自动压缩
🤖 Subagents: researcher / coder / reviewer(可通过 task 工具委派)
📋 HarnessProfile: qwen 系列模型自定义配置已注册
📝 write_todos: 内置任务规划工具已启用
⚠️ LocalShellBackend 无沙箱隔离,命令以当前用户身份执行
8.4 后续可以做什么
到目前为止,我们已经构建了一个功能相当完整的 Agent 系统。接下来可以探索的方向:
- LangGraph Studio — 可视化调试 Agent 的执行流程
- Human-in-the-loop — 关键操作前暂停,等人类确认
- 自定义 Middleware — 扩展 Agent 的行为(如日志、监控、自动修复)
- 部署到生产 — Docker 沙箱、LangGraph Platform、监控告警
从"会算数"到"会操作电脑"到"有记忆有知识"再到"会带团队"——Agent 从一个单兵作战的工具,变成了一个能协调团队的项目经理。