4. 从零开始搭建一个 AI Agent —— 任务委派与子 Agent 并行处理

8 阅读21分钟

任务委派与子 Agent 并行处理:让 Agent 学会"带团队"

这不是一个"给 Agent 多加几个工具"的简单升级。 我们要做的,是让 Agent 从一个"什么都自己扛"的独行侠,进化成一个能 分解任务委派子 Agent并行执行 的团队领袖。 整个过程基于前三篇文章的项目,增量改动涉及 src/subagents.ts(新增)、src/agents.tssrc/index.tsworkspace/AGENTS.md


目录

  1. 回顾:前三篇文章结束时 Agent 能做什么?
  2. 单 Agent 的天花板:为什么需要任务委派?
  3. Subagents:给 Agent 配一支专业团队
  4. write_todos:先计划,再执行
  5. AsyncSubAgent:后台长任务不阻塞
  6. HarnessProfile:给不同模型因材施教
  7. 三个新测试:验证任务委派能力
  8. 回顾与展望

1. 回顾:前三篇文章结束时 Agent 能做什么?

阶段能力新增工具
第一篇计算器、报时、流式输出、多轮对话、Extended Thinkingcalculatorget_current_time
第二篇文件读写、搜索、执行 shell 命令lsread_filewrite_fileedit_fileglobgrepexecute
第三篇持久记忆、按需加载领域知识、长对话自动压缩Memory (AGENTS.md)、Skills (SKILL.md)、Summarization

到了第四篇,我们要解决三个新问题:

  1. 上下文爆炸 — 一个 Agent 干所有活,对话历史越来越长,上下文窗口迟早不够用
  2. 串行瓶颈 — 同时需要搜索文件和写代码?只能排队一件件做
  3. 职责混杂 — 系统提示词塞满了各种角色指令,模型容易"分心"

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 思考] → 生成最终回复

问题在哪?

  1. 每一步都往上下文里塞消息 — 5 次 tool_call + 5 次 tool_result = 10 条消息,加上思考过程,轻松吃掉几千 token
  2. 全程串行 — 搜索文件、统计行数、写报告、写代码,排队执行,总耗时 = 所有步骤之和
  3. 上下文污染 — 搜索过程中看到的文件内容,和最终写代码需要的上下文混在一起,模型注意力被稀释

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 的其他对话                   │
└──────────────────────────────────────────────┘

好处:

  1. 节省主 Agent 的上下文空间 — 搜索过程中读到的 10 个文件内容不会塞进主 Agent 的窗口
  2. 减少干扰 — 子 Agent 不会被主 Agent 的其他对话"分心"
  3. 独立试错 — 子 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)
  单线程,AI/O 时切去算 BBI/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,等完成后,再委派 coderresearcher + 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 的 backendLocalShellBackend(支持 execute)。

问题permissions 不能与 execute 能力共存——shell 命令可以绕过路径限制(cat /private/secret.txt),所以 deepagents 会报 ConfigurationError

怎么修的? reviewerpermissions 只影响 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);

预期行为:

  1. 主 Agent 判断这是研究任务,选择 researcher
  2. 调用 task({ subagent_type: 'researcher', description: '...' })
  3. researcher 在独立上下文中搜索和分析
  4. 结果返回主 Agent,主 Agent 汇总回复

4. write_todos:先计划,再执行

4.1 为什么需要任务规划?

当任务比较复杂时,Agent 容易"丢步骤"——上下文太长,模型可能忘了中间某一步该做什么。

write_todoscreateDeepAgent 内置的任务规划工具(不需要手动创建),它让 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 更新状态。这样:

  1. Agent 自己不会"忘步骤"(todo list 是持久化的状态)
  2. 用户能看到执行进度(哪些完成了,哪些还没做)
  3. 调试更容易(可以看到 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);

预期行为:

  1. Agent 先调用 write_todos 列出计划
  2. 逐步执行:搜索文件 → 统计行数 → 生成报告 → 保存文件
  3. 每完成一步更新 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);

预期行为:

  1. Agent 先调用 write_todos 列出计划
  2. 逐步执行:搜索文件 → 统计行数 → 生成报告 → 保存文件
  3. 每完成一步更新 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);

预期行为:

  1. Agent 判断这是一个研究任务,选择 researcher 子 Agent
  2. 调用 task({ subagent_type: 'researcher', description: '...' })
  3. researcher 在独立上下文中搜索和分析
  4. 结果返回主 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);

预期行为:

  1. Agent 先用 write_todos 规划:编码 → 审查
  2. 委派 coder 写代码
  3. 委派 reviewer 审查代码
  4. 汇总审查结果,回复用户

这就是一个典型的"编码-审查"工作流——编码和审查由不同的子 Agent 负责,主 Agent 充当项目经理协调两方。

7.4 验证

# 取消 src/index.ts 中测试 10、11、12 的注释后运行
pnpm dev

预期输出:三个测试场景依次执行,分别验证 write_todos 任务规划、同步子 Agent 委派、多子 Agent 协作。


8. 回顾与展望

8.1 本文新增了什么?

能力实现方式代码位置
同步子 AgentSubAgent 接口 + task 工具src/subagents.ts
任务规划write_todos 内置工具createDeepAgent 自动集成
异步子 AgentAsyncSubAgent 接口src/subagents.ts(示例配置)
模型配置档案HarnessProfilesrc/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 系统。接下来可以探索的方向:

  1. LangGraph Studio — 可视化调试 Agent 的执行流程
  2. Human-in-the-loop — 关键操作前暂停,等人类确认
  3. 自定义 Middleware — 扩展 Agent 的行为(如日志、监控、自动修复)
  4. 部署到生产 — Docker 沙箱、LangGraph Platform、监控告警

从"会算数"到"会操作电脑"到"有记忆有知识"再到"会带团队"——Agent 从一个单兵作战的工具,变成了一个能协调团队的项目经理。