Skip to content

07. 子代理系统:任务委托的艺术

专业化分工,保持上下文干净

引言:为什么需要子代理?

想象一个场景:你让 AI 代理研究一个技术主题,它执行了 10 次网络搜索,每次返回大量结果。很快,对话上下文就被这些中间结果"淹没"了,主代理可能会迷失方向或遗漏重要信息。

子代理就是解决这个问题的方案——通过上下文隔离,让专门的"助理"处理细节工作,主代理只接收精炼的结果。

核心概念

上下文隔离模式

┌────────────────────────────────────────────────────────────────┐
│                        主代理                                   │
│  ┌──────────────────────────────────────────────────────────┐ │
│  │  上下文(保持干净)                                        │ │
│  │  - 用户请求                                               │ │
│  │  - 高层规划                                               │ │
│  │  - 子代理返回的摘要 ←──────────────────────┐              │ │
│  └──────────────────────────────────────────────────────────┘ │
│                           │ task()                             │
│                           ▼                                    │
│  ┌──────────────────────────────────────────────────────────┐ │
│  │                     子代理                                │ │
│  │  ┌────────────────────────────────────────────────────┐  │ │
│  │  │  隔离的上下文                                       │  │ │
│  │  │  - 搜索结果 1 (5000 tokens)                        │  │ │
│  │  │  - 搜索结果 2 (8000 tokens)                        │  │ │
│  │  │  - 搜索结果 3 (6000 tokens)                        │  │ │
│  │  │  - 分析过程...                                     │  │ │
│  │  │  → 最终输出:300 词摘要 ─────────────────────────┘  │ │
│  │  └────────────────────────────────────────────────────┘  │ │
│  └──────────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────┘

上下文隔离模式:主代理保持干净上下文,子代理在隔离空间处理大量数据后只返回精炼摘要

生活类比

子代理系统就像一个公司的组织架构

  • 主代理 = 项目总监

    • 负责理解客户需求
    • 制定整体计划
    • 协调各部门工作
    • 做最终决策
  • 子代理 = 专业顾问/部门

    • 研究员:负责信息搜索和整理
    • 分析师:负责数据分析
    • 写手:负责报告撰写
    • 各自有独立的"办公室"(上下文)
    • 完成后只汇报结论

何时使用/不使用子代理

✅ 适合使用子代理

  • 多步骤任务会"污染"主代理上下文
  • 需要专门化指令或工具的领域
  • 不同任务需要不同的模型能力
  • 希望主代理专注于高层协调

❌ 不需要子代理

  • 简单的单步任务
  • 需要保留中间上下文的场景
  • 开销大于收益时

何时使用子代理:适合多步骤任务、专门化指令、不同模型需求;不适合简单单步、需保留中间上下文的场景

子代理类型

DeepAgents 支持两种子代理类型:

1. SubAgent(基于字典)

最常用的方式,通过字典配置:

typescript
import { createDeepAgent, SubAgent } from "deepagents";

const researchAgent: SubAgent = {
  name: "researcher",
  description: "使用网络搜索开展深入研究并综合结论",
  systemPrompt: `你是一位严谨的研究员。你的工作是:
    1. 将研究问题拆解为可搜索的查询
    2. 使用 internet_search 查找相关信息
    3. 将发现综合为全面但简洁的摘要
    4. 在提出主张时引用来源
    
    输出格式:
    - 摘要(2-3 段)
    - 关键发现(要点列表)
    - 来源(包含 URL)
    
    请将响应控制在 500 词以内。`,
  tools: [internetSearchTool],
  model: "gpt-4o",  // 可选:覆盖主代理的模型
};

const agent = createDeepAgent({
  model: "claude-sonnet-4-5-20250929",
  subagents: [researchAgent],
  systemPrompt: "你是一个项目经理。对于需要深入研究的任务,委托给 researcher。"
});

SubAgent 配置项详解

字段类型必填说明
namestring唯一标识符,主代理通过此名称委托任务
descriptionstring功能描述,主代理据此决定何时委托
systemPromptstring子代理的角色定义和工作指令
toolsTool[]子代理可用的工具(不继承主代理)
modelstring覆盖主代理的模型(默认继承)
middlewareMiddleware[]子代理的中间件(不继承)
interruptOnobject人机协作配置(默认继承,可覆盖)
skillsstring[]子代理的技能路径(不继承)

2. CompiledSubAgent(预构建图)

用于复杂工作流,可以传入预构建的 LangGraph 图:

typescript
import { createDeepAgent, CompiledSubAgent } from "deepagents";
import { createAgent } from "langchain";

const customGraph = createAgent({
  model: new ChatOpenAI({ model: "gpt-4o" }),
  tools: [dataAnalysisTool, visualizationTool],
  prompt: "你是一个专门用于数据分析的代理...",
});

const dataAnalyzer: CompiledSubAgent = {
  name: "data-analyzer",
  description: "用于复杂数据分析任务的专门化代理",
  runnable: customGraph,  // 已编译的 LangGraph 图
};

const agent = createDeepAgent({
  subagents: [dataAnalyzer],
});

两种子代理类型对比:SubAgent 基于字典简单配置 vs CompiledSubAgent 预构建 LangGraph 图

task 工具

主代理通过内置的 task 工具将任务委托给子代理:

typescript
// 主代理内部调用
task({
  name: "researcher",  // 子代理名称
  task: "研究量子计算的最新进展"  // 任务描述
})

执行流程

  1. 主代理调用 task() 工具
  2. 系统创建子代理实例,传入任务
  3. 子代理在隔离的上下文中执行任务
  4. 子代理返回摘要结果
  5. 主代理接收结果,继续处理

task() 工具执行流程:调用→创建实例→隔离执行→返回摘要→继续处理

通用子代理

除了自定义子代理,DeepAgents 始终提供一个名为 general-purpose 的通用子代理:

特点

  • 与主代理具有相同的系统提示词
  • 可访问完全相同的工具
  • 使用相同的模型(除非被覆盖)
  • 继承主代理的技能

何时使用

当你只需要上下文隔离,而不需要专门化行为时:

typescript
// 主代理不必进行 10 次 Web 搜索填满上下文,
// 而是委托给通用子代理:
task({
  name: "general-purpose",
  task: "研究量子计算趋势,返回摘要报告"
})
// 子代理在内部执行所有搜索,仅返回摘要

多代理协作模式

模式一:流水线协作

typescript
const subagents = [
  {
    name: "data-collector",
    description: "从各种来源收集原始数据",
    systemPrompt: "收集关于该主题的全面数据",
    tools: [webSearch, apiCall, databaseQuery],
  },
  {
    name: "data-analyzer",
    description: "分析收集到的数据以提取洞察",
    systemPrompt: "分析数据并提取关键洞察",
    tools: [statisticalAnalysis],
  },
  {
    name: "report-writer",
    description: "基于分析结果撰写报告",
    systemPrompt: "将洞察整理为专业报告",
    tools: [formatDocument],
  },
];

const agent = createDeepAgent({
  systemPrompt: `你是项目经理,负责协调数据分析与报告撰写。
    
    工作流程:
    1. 将数据收集委派给 data-collector
    2. 将结果交给 data-analyzer
    3. 将洞察交给 report-writer
    4. 汇总最终输出`,
  subagents: subagents,
});

模式二:专家会诊

typescript
const subagents = [
  {
    name: "contract-reviewer",
    description: "审阅法律文件与合同",
    systemPrompt: "你是一位资深法律审阅专家...",
    tools: [readDocument, analyzeContract],
    model: "claude-sonnet-4-5-20250929",  // 长上下文
  },
  {
    name: "financial-analyst",
    description: "分析财务数据与市场趋势",
    systemPrompt: "你是一位资深金融分析师...",
    tools: [getStockPrice, analyzeFundamentals],
    model: "gpt-4o",  // 数值分析更强
  },
  {
    name: "tech-expert",
    description: "评估技术可行性与架构方案",
    systemPrompt: "你是一位资深技术架构师...",
    tools: [codeAnalysis, architectureReview],
    model: "claude-sonnet-4-5-20250929",
  },
];

const agent = createDeepAgent({
  systemPrompt: `你是一位综合顾问,可以召集不同领域的专家。
    根据问题类型,选择合适的专家进行咨询。`,
  subagents: subagents,
});

模式三:软件开发团队

typescript
const subagents = [
  {
    name: "pm",
    description: "需求分析和任务拆解",
    systemPrompt: "你是产品经理,负责理解需求并拆解任务...",
    tools: [],
  },
  {
    name: "architect",
    description: "技术方案设计",
    systemPrompt: "你是技术架构师,负责设计系统架构...",
    tools: [readCodeTool],
  },
  {
    name: "developer",
    description: "代码实现",
    systemPrompt: "你是高级开发者,负责编写高质量代码...",
    tools: [readFileTool, writeFileTool, editFileTool],
  },
  {
    name: "reviewer",
    description: "代码审查",
    systemPrompt: "你是代码审查专家,负责发现潜在问题...",
    tools: [readFileTool, grepTool],
  },
  {
    name: "tester",
    description: "测试执行",
    systemPrompt: "你是 QA 工程师,负责编写和执行测试...",
    tools: [readFileTool, executeTool],
  },
];

三种多代理协作模式:流水线串行执行、专家会诊按需召集、开发团队分工协作

技能与子代理

技能继承规则

子代理类型技能继承
通用子代理 (general-purpose)✅ 自动继承主代理技能
自定义子代理❌ 不继承,需显式配置

配置子代理技能

typescript
const researchSubagent: SubAgent = {
  name: "researcher",
  description: "具备专门化技能的研究助手",
  systemPrompt: "你是一名研究员。",
  tools: [webSearch],
  skills: ["/skills/research/", "/skills/web-search/"],  // 子代理专属技能
};

const agent = createDeepAgent({
  skills: ["/skills/main/"],  // 主代理 + 通用子代理的技能
  subagents: [researchSubagent],  // 只获得自己配置的技能
});

技能隔离

技能状态完全隔离:

  • 主代理的技能对子代理不可见
  • 子代理的技能对主代理不可见
  • 每个子代理有独立的 SkillsMiddleware 实例

技能继承规则:通用子代理自动继承主代理技能,自定义子代理需显式配置,技能状态完全隔离

流式输出

子代理支持流式输出,可以在元数据中区分来源:

typescript
const agent = createDeepAgent({
  name: "main-agent",
  subagents: [{
    name: "research-agent",
    ...
  }],
});

// 流式输出时,元数据中包含代理名称
for await (const chunk of stream) {
  const agentName = chunk.metadata?.lc_agent_name;
  if (agentName === "research-agent") {
    console.log("[研究员]", chunk.content);
  } else {
    console.log("[主代理]", chunk.content);
  }
}

最佳实践

1. 编写清晰的描述

主代理根据描述决定调用哪个子代理:

typescript
// ✅ 好:具体、以行动为导向
{
  name: "financial-analyst",
  description: "分析财务数据,并生成带置信度分数的投资洞察"
}

// ❌ 差:模糊、不具体
{
  name: "helper",
  description: "做点财务相关的事"
}

2. 详细的系统提示词

包含工具使用指导和输出格式要求:

typescript
const researchSubagent = {
  systemPrompt: `你是一位严谨的研究员。

    工作流程:
    1. 将问题拆解为可搜索的查询
    2. 使用 internet_search 查找信息
    3. 综合发现为简洁摘要
    4. 引用来源

    输出格式:
    - 摘要(2-3 段)
    - 关键发现(要点列表)
    - 来源(包含 URL)

    重要:响应控制在 500 词以内。`,
};

3. 最小化工具集合

只给子代理必要的工具:

typescript
// ✅ 好:聚焦的工具集合
const emailAgent = {
  tools: [sendEmail, validateEmail],  // 仅与邮件相关
};

// ❌ 差:工具过多
const emailAgentBad = {
  tools: [sendEmail, webSearch, databaseQuery, fileUpload],
};

4. 返回简洁结果

指示子代理返回摘要,而非原始数据:

typescript
const dataAnalyst = {
  systemPrompt: `分析数据并返回:
    1. 关键洞察(3-5 条要点)
    2. 总体置信度分数
    3. 推荐的下一步行动

    不要包含:
    - 原始数据
    - 中间计算过程
    - 详细的工具输出

    响应控制在 300 词以内。`,
};

5. 按任务选择模型

typescript
const subagents = [
  {
    name: "contract-reviewer",
    model: "claude-sonnet-4-5-20250929",  // 长上下文,适合文档
  },
  {
    name: "code-generator",
    model: "gpt-4o",  // 代码生成更强
  },
  {
    name: "quick-lookup",
    model: "gpt-4o-mini",  // 简单任务用小模型
  },
];

子代理设计五大最佳实践:清晰描述、详细提示词、最小化工具、简洁结果、按任务选模型

故障排查

子代理未被调用

原因:主代理倾向于自己完成工作。

解决方案

  1. 让描述更具体
  2. 在主代理提示词中明确指示委托
typescript
const agent = createDeepAgent({
  systemPrompt: `...你的指令...

    IMPORTANT: 对于复杂任务,请使用 task() 工具委派给子代理。
    这能保持上下文干净并提升结果质量。`,
  subagents: [...],
});

上下文仍然膨胀

原因:子代理返回了太多原始数据。

解决方案

  1. 指示子代理返回简洁结果
  2. 对大数据使用文件系统
typescript
systemPrompt: `当你收集了大量数据时:
  1. 将原始数据保存到 /data/raw_results.txt
  2. 处理并分析数据
  3. 仅返回分析摘要
  
  响应控制在 500 词以内。`

选择了错误的子代理

原因:描述不够区分度。

解决方案:清晰区分子代理的职责

typescript
const subagents = [
  {
    name: "quick-researcher",
    description: "用于简单、快速的研究,仅需 1-2 次搜索。适合基本事实或定义。",
  },
  {
    name: "deep-researcher",
    description: "用于复杂、深入的研究,需多次搜索和综合分析。适合生成全面报告。",
  },
];

小结

本文介绍了 DeepAgents 子代理系统的核心概念和使用方法:

概念说明
上下文隔离子代理在独立空间工作,只返回摘要
SubAgent基于字典的简单配置方式
CompiledSubAgent预构建 LangGraph 图
task 工具主代理委托任务的接口
通用子代理继承主代理配置,用于简单隔离

最佳实践

  • ✅ 清晰具体的描述
  • ✅ 详细的系统提示词
  • ✅ 最小化工具集合
  • ✅ 返回简洁结果
  • ✅ 按任务选择模型

下一步

在下一篇文章中,我们将学习人机协作——如何配置敏感操作的审批流程。

实践任务

  1. 创建一个研究型子代理,让它能搜索并整理信息
  2. 尝试多子代理协作,构建"研究员 + 写手"组合
  3. 比较使用子代理和不使用子代理时的上下文消耗

参考资源

读文档、看源码、写代码,理解 AI Agent 本质 🤖