Skip to content

子智能体 (Subagents) ​

在子智能体 (subagents) 架构中,一个中央主 Agent(通常被称为主管 (supervisor))通过将子智能体作为工具调用来协调它们。主 Agent 决定调用哪个子智能体、提供什么输入以及如何组合结果。默认情况下,子智能体是无状态的——它们不保留过去的交互记忆,所有对话记忆均由主 Agent 维护。这提供了上下文隔离:每次子智能体调用都在干净的上下文窗口中运行,从而防止主对话中的上下文膨胀。

当子智能体需要这些上下文时,你也可以将主 Agent 的对话历史传递给子智能体。请参见子智能体输入。

有关内置的子智能体支持,请参见 Deep Agents。

核心特性 ​

  • 集中式控制:所有路由均通过主 Agent 进行
  • 无直接用户交互:子智能体将结果返回给主 Agent,而不是用户(尽管你可以在子智能体中使用中断 (interrupts) 来允许用户交互)
  • 通过工具调用子智能体:子智能体通过工具进行调用
  • 并行执行:主 Agent 可以在单轮交互中调用多个子智能体

NOTE

主管 vs. 路由器 (Supervisor vs. Router):主管 Agent(即本模式)与路由器 (router) 不同。主管是一个完整的 Agent,它维护对话上下文,并在多轮交互中动态决定调用哪些子智能体。而路由器通常只是单一的分类步骤,仅负责分发给各个 Agent,而不维护持续的对话状态。

何时使用 ​

当你有多个不同的领域(例如日历、电子邮件、CRM、数据库)、子智能体无需直接与用户对话,或者你希望对工作流进行集中控制时,请使用子智能体模式。对于只有少数几个工具的更简单场景,请使用单一 Agent。

TIP

需要在子智能体内进行用户交互? 尽管子智能体通常将结果返回给主 Agent 而不是直接与用户对话,但你可以在子智能体内使用中断 (interrupts) 来暂停执行并收集用户输入。这在子智能体在继续执行之前需要澄清或批准时非常有用。主 Agent 仍然是协调器,但子智能体可以在任务中途向用户收集信息。

基础实现 ​

其核心机制是将子智能体包装为主 Agent 可以调用的工具:

typescript
import { createAgent, tool } from "langchain";
import { z } from "zod";

// 创建子智能体
const subagent = createAgent({ model: "google:gemini-3.6-flash", tools: [...] });

// 将其包装为工具
const callResearchAgent = tool(
  async ({ query }) => {
    const result = await subagent.invoke({
      messages: [{ role: "user", content: query }]
    });
    return result.messages.at(-1)?.content;
  },
  {
    name: "research",
    description: "Research a topic and return findings",
    schema: z.object({ query: z.string() })
  }
);

// 将子智能体作为工具的主 Agent
const mainAgent = createAgent({ model: "google:gemini-3.6-flash", tools: [callResearchAgent] });

教程:使用子智能体构建个人助理

了解如何使用子智能体模式构建个人助理,其中由一个中央主 Agent(主管)协调专门的 Worker Agent。

设计决策 ​

在实现子智能体模式时,你需要做出几个关键的设计抉择。下表总结了这些选项——下文各节将对每一项进行详细介绍。

决策选项
同步 vs. 异步同步(阻塞) vs. 异步(后台)
工具模式单 Agent 单工具 vs. 单一分发工具
子智能体规范系统提示词 vs. 枚举约束 vs. 基于工具的发现(仅限单一分发工具)
子智能体输入仅查询 vs. 完整上下文
子智能体输出子智能体结果 vs. 完整对话历史

同步 vs. 异步 ​

子智能体的执行可以是同步(阻塞)或异步(后台)。你的选择取决于主 Agent 是否需要该结果才能继续。

模式主 Agent 行为最适合权衡
同步 (Sync)等待子智能体完成主 Agent 需要结果才能继续简单,但会阻塞对话
异步 (Async)在子智能体于后台运行时继续响应独立任务,用户无需等待响应迅速,但复杂度更高

TIP

请勿将其与 Python 的 async/await 混淆。在这里,“异步”意味着主 Agent 启动一个后台任务(通常在单独的进程或服务中)并在不阻塞的情况下继续运行。

同步(默认) ​

默认情况下,子智能体调用是同步的:主 Agent 会等待每个子智能体执行完成,然后再继续。当主 Agent 的下一步行动依赖于子智能体的结果时,请使用同步模式。

何时使用同步:

  • 主 Agent 需要子智能体的结果来组织其回复
  • 任务具有顺序依赖性(例如:获取数据 → 分析 → 回复)
  • 子智能体的失败应当阻断主 Agent 的回复

权衡:

  • 实现简单——只需调用并等待即可
  • 在所有子智能体完成之前,用户看不到任何响应
  • 运行时间较长的任务会冻结对话

异步 ​

当子智能体的工作是独立的——主 Agent 不需要其结果即可继续与用户对话时,请使用异步执行。主 Agent 会启动一个后台任务,并保持对用户的即时响应。

何时使用异步:

  • 子智能体的工作独立于主对话流程
  • 用户在工作进行期间应该能够继续聊天
  • 你希望并行运行多个独立任务

三工具模式 (Three-tool pattern):

  1. 启动任务 (Start job):启动后台任务,返回任务 ID
  2. 检查状态 (Check status):返回当前状态(pending、running、completed、failed)
  3. 获取结果 (Get result):获取已完成的结果

处理任务完成: 当任务完成时,你的应用程序需要通知用户。一种方法是:显示一条通知,点击后发送一条类似“检查 job_123 并总结结果”的 HumanMessage。

工具模式 ​

将子智能体作为工具公开主要有两种方式:

模式最适合权衡
单 Agent 单工具 (Tool per agent)对每个子智能体的输入/输出进行细粒度控制更多设置工作,但可定制性更强
单一分发工具 (Single dispatch tool)大量子智能体、分布式团队、约定优于配置组合更简单,但每个 Agent 的定制较少

单 Agent 单工具 ​

其核心思想是将子智能体包装为主 Agent 可以调用的工具:

typescript
import { createAgent, tool } from "langchain";
import * as z from "zod";

// 创建子智能体
const subagent = createAgent({...});  

// 将其包装为工具
const callSubagent = tool(  
  async ({ query }) => {  
    const result = await subagent.invoke({
      messages: [{ role: "user", content: query }]
    });
    return result.messages.at(-1)?.text;
  },
  {
    name: "subagent_name",
    description: "subagent_description",
    schema: z.object({
      query: z.string().describe("The query to send to subagent")
    })
  }
);

// 将子智能体作为工具的主 Agent
const mainAgent = createAgent({ model, tools: [callSubagent] });  

当主 Agent 判定任务与子智能体的描述相匹配时,便会调用该子智能体工具,接收其结果并继续编排。有关细粒度控制,请参见上下文工程。

单一分发工具 ​

另一种方法是使用单个参数化工具来为独立任务调用临时子智能体。与将每个子智能体包装为单独工具的单 Agent 单工具方法不同,这种方法采用基于约定的方式,使用单一的 task 工具:将任务描述作为人类消息传递给子智能体,并将子智能体的最终消息作为工具结果返回。

当你想将 Agent 开发分发给多个团队、需要将复杂任务隔离到独立的上下文窗口中、需要一种无需修改协调器即可添加新 Agent 的可扩展方式,或者更青睐约定而非定制时,可以使用这种方法。该方法牺牲了上下文工程的灵活性,换取了 Agent 组合的简单性以及强大的上下文隔离。

核心特性:

  • 单一 task 工具:一个参数化工具,可以通过名称调用任何已注册的子智能体
  • 基于约定的调用:通过名称选择 Agent,任务作为人类消息传递,最终消息作为工具结果返回
  • 团队分发:不同的团队可以独立开发和部署 Agent
  • Agent 发现机制:可以通过系统提示词(列出可用 Agent)或通过渐进式披露 (progressive disclosure)(通过工具按需加载 Agent 信息)来发现子智能体

TIP

这种方法的一个有趣之处在于,子智能体可能具有与主 Agent 完全相同的功能。在这种情况下,调用子智能体真正的核心原因在于上下文隔离——允许复杂的多步骤任务在隔离的上下文窗口中运行,而不会导致主 Agent 的对话历史膨胀。子智能体自主完成工作并仅返回简要摘要,从而保持主线程的专注与高效。

带有任务分发器的 Agent 注册表

typescript
import { tool, createAgent } from "langchain";
import * as z from "zod";

// 由不同团队开发的子智能体
const researchAgent = createAgent({
  model: "gpt-5.5",
  prompt: "You are a research specialist...",
});

const writerAgent = createAgent({
  model: "gpt-5.5",
  prompt: "You are a writing specialist...",
});

// 可用子智能体注册表
const SUBAGENTS = {
  research: researchAgent,
  writer: writerAgent,
};

const task = tool(
  async ({ agentName, description }) => {
    const agent = SUBAGENTS[agentName];
    const result = await agent.invoke({
      messages: [
        { role: "user", content: description }
      ],
    });
    return result.messages.at(-1)?.content;
  },
  {
    name: "task",
    description: `Launch an ephemeral subagent.

Available agents:
- research: Research and fact-finding
- writer: Content creation and editing`,
    schema: z.object({
      agentName: z
        .string()
        .describe("Name of agent to invoke"),
      description: z
        .string()
        .describe("Task description"),
    }),
  }
);

// 主协调 Agent
const mainAgent = createAgent({
  model: "gpt-5.5",
  tools: [task],
  prompt: (
    "You coordinate specialized sub-agents. " +
    "Available: research (fact-finding), " +
    "writer (content creation). " +
    "Use the task tool to delegate work."
  ),
});

上下文工程 ​

控制主 Agent 与其子智能体之间的上下文流动:

类别目的影响
子智能体规范确保子智能体在应被调用时得到调用主 Agent 的路由决策
子智能体输入确保子智能体能在优化后的上下文中良好执行子智能体的性能表现
子智能体输出确保主管能够根据子智能体的结果采取行动主 Agent 的性能表现

另请参阅我们针对 Agent 的上下文工程全面指南。

子智能体规范 ​

与子智能体关联的名称和描述是主 Agent 了解调用哪些子智能体的主要途径。这些是提示词杠杆——请审慎选择。

  • 名称:主 Agent 引用该子智能体的方式。保持清晰且面向行动(例如 research_agent、code_reviewer)。
  • 描述:主 Agent 对该子智能体能力的了解。明确说明它处理哪些任务以及何时使用它。

对于单一分发工具设计,你还必须为主 Agent 提供有关其可调用的子智能体的信息。 你可以根据 Agent 的数量以及注册表是静态还是动态,通过不同的方式提供此信息:

方法最适合权衡
系统提示词枚举小型、静态的 Agent 列表(< 10 个 Agent)简单,但当 Agent 发生变化时需要更新提示词
枚举约束小型、静态的 Agent 列表(< 10 个 Agent)类型安全且显式,但当 Agent 发生变化时需要修改代码
基于工具的发现机制大型或动态的 Agent 注册表灵活且可扩展,但增加了复杂度

系统提示词枚举 ​

直接在主 Agent 的系统提示词中列出可用 Agent。主 Agent 将 Agent 列表及其描述作为其指令的一部分来查看。

何时使用:

  • 拥有少量的固定 Agent 集合(< 10 个)
  • Agent 注册表极少发生变化
  • 你希望使用最简单的实现

示例:

python
main_agent = create_agent(
    model="...",
    tools=[task],
    system_prompt=(
        "You coordinate specialized sub-agents. "
        "Available agents:\n"
        "- research: Research and fact-finding\n"
        "- writer: Content creation and editing\n"
        "- reviewer: Code and document review\n"
        "Use the task tool to delegate work."
    ),
)

分发工具上的枚举约束 ​

在分发工具的 agent_name 参数中添加枚举约束。这提供了类型安全性,并在工具 schema 中明确列出了可用的 Agent。

何时使用:

  • 拥有少量的固定 Agent 集合(< 10 个)
  • 需要类型安全和显式的 Agent 名称
  • 相比于基于提示词的引导,更倾向于基于 schema 的验证

示例:

python
from enum import Enum

class AgentName(str, Enum):
    RESEARCH = "research"
    WRITER = "writer"
    REVIEWER = "reviewer"

@tool
def task(
    agent_name: AgentName,  # 枚举约束
    description: str
) -> str:
    """Launch an ephemeral subagent for a task."""
    # ...

基于工具的发现机制 ​

提供一个单独的工具(例如 list_agents 或 search_agents),主 Agent 可以调用该工具来按需发现可用 Agent。这支持了渐进式披露并适配动态注册表。

何时使用:

  • 拥有大量 Agent(> 10 个)或不断增长的注册表
  • Agent 注册表变动频繁或是动态的
  • 希望缩减提示词大小和 Token 消耗
  • 不同的团队独立管理不同的 Agent

示例:

python
@tool
def list_agents(query: str = "") -> str:
    """List available subagents, optionally filtered by query."""
    agents = search_agent_registry(query)
    return format_agent_list(agents)

@tool
def task(agent_name: str, description: str) -> str:
    """Launch an ephemeral subagent for a task."""
    # ...

main_agent = create_agent(
    model="...",
    tools=[task, list_agents],
    system_prompt="Use list_agents to discover available subagents, then use task to invoke them."
)

子智能体输入 ​

自定义子智能体为执行其任务所接收的上下文。通过从 Agent 的状态中提取数据,添加在静态提示词中难以捕获的输入——完整的消息历史记录、先前的结果或任务元数据。

Subagent inputs example expandable
typescript
import { createAgent, tool, AgentState, ToolMessage } from "langchain";
import { Command } from "@langchain/langgraph";
import * as z from "zod";

// 通过 state 将完整对话历史传递给子智能体的示例。
const callSubagent1 = tool(
  async ({query}) => {
    const state = getCurrentTaskInput<AgentState>();
    // 应用所需的任何逻辑将消息转换为合适的输入
    const subAgentInput = someLogic(query, state.messages);
    const result = await subagent1.invoke({
      messages: subAgentInput,
      // 你也可以根据需要在此传递其他 state 键。
      // 请确保在主 Agent 和子智能体的
      // state schema 中都定义了这些键。
      exampleStateKey: state.exampleStateKey
    });
    return result.messages.at(-1)?.content;
  },
  {
    name: "subagent1_name",
    description: "subagent1_description",
  }
);

some_logic 控制子智能体接收多少上下文。仅传递任务描述 (query) 可以保持子智能体的隔离性。而传递父级的消息历史记录 (runtime.state["messages"]) 则可以为子智能体提供先前工作的基础,当它需要继续执行父级已经启动的任务时(例如继续 PR 审查或接手 Bug 修复),这非常有帮助。

Deep Agents 将这些模式命名为 mode: "isolated"(默认)和 mode: "fork",并为你实现了分支 (forking):分支子智能体会接收父级的完整消息历史记录(包括工具调用)和系统提示词。

模式子智能体接收的内容最适合权衡
隔离模式 (Isolated)(默认)仅任务描述需要较少先前上下文的专注工作上下文隔离,但需要重新推导父级已经完成的所有工作
分支模式 (Forked)父级的对话历史记录(在 Deep Agents 中还包括系统提示词)继续执行父级已经启动的任务注入了先前的上下文,但代价是更大的提示词和更弱的隔离性

子智能体输出 ​

自定义主 Agent 接收回的内容,以便其能够做出明智的决策。两种策略:

  1. 通过提示词引导子智能体:精确指定应该返回的内容。一个常见的失败模式是子智能体执行了工具调用或推理,但并未在其最终消息中包含结果——提醒它主管只能看到最终输出。
  2. 在代码中格式化:在返回响应之前对其进行调整或丰富。例如,使用 Command 在返回最终文本之外传回特定的状态键。
Subagent outputs example expandable
typescript
import { tool, ToolMessage } from "langchain";
import { Command } from "@langchain/langgraph";
import * as z from "zod";

const callSubagent1 = tool(
  async ({ query }, config) => {
    const result = await subagent1.invoke({
      messages: [{ role: "user", content: query }]
    });

    // 返回 Command 以更新多个 state 键
    return new Command({
      update: {
        // 从子智能体传回额外的 state
        exampleStateKey: result.exampleStateKey,
        messages: [
          new ToolMessage({
            content: result.messages.at(-1)?.text,
            tool_call_id: config.toolCall?.id!
          })
        ]
      }
    });
  },
  {
    name: "subagent1_name",
    description: "subagent1_description",
    schema: z.object({
      query: z.string().describe("The query to send to subagent1")
    })
  }
);

检查点与状态检查 ​

默认情况下,子智能体使用继承检查点 (inherited checkpointer) 模式——每次调用都以全新的状态开始,支持中断 (interrupts),并且能够安全地并行运行。如果你需要子智能体在跨调用之间保持其自身的持久对话历史记录,请在编译时设置 checkpointer=True(延续模式,continuations mode)。有关模式的完整比较,请参见子图持久化 (subgraph persistence)。

由于子智能体是在工具函数内部被调用的,LangGraph 无法对其进行静态发现 (statically discover)。这意味着带 subgraphs 的 get_state 将不会返回子智能体的状态。如果你需要读取嵌套图的状态(例如在中断期间),请改为在自定义图的节点函数 (node function) 中调用子智能体。有关每种模式如何影响状态可见性的详细信息,请参见子图持久化。