router 架构中,路由步骤会对输入进行分类,并将其定向到专门的代理。当你有不同的垂直领域(彼此独立且各自需要专属代理的知识领域)时,这很有用。

关键特征

  • Router 会分解查询
  • 零个或多个专门代理会被并行调用
  • 结果会被综合成连贯响应

何时使用

当你有不同垂直领域(彼此独立且各自需要专属代理的知识领域)、需要并行查询多个来源,并希望将结果综合为合并响应时,请使用 router 模式。

基本实现

Router 会对查询分类,并将其定向到合适的代理。单代理路由请使用 Command,并行扇出到多个代理请使用 Send
使用 Command 路由到单个专门代理:
import { z } from "zod";
import { Command } from "@langchain/langgraph";

const ClassificationResult = z.object({
  query: z.string(),
  agent: z.string(),
});

function classifyQuery(query: string): z.infer<typeof ClassificationResult> {
  // Use LLM to classify query and determine the appropriate agent
  // Classification logic here
  ...
}

function routeQuery(state: z.infer<typeof ClassificationResult>) {
  const classification = classifyQuery(state.query);

  // Route to the selected agent
  return new Command({ goto: classification.agent });
}
如需完整实现,请参阅下面的教程。

教程:使用路由构建多源知识库

构建一个并行查询 GitHub、Notion 和 Slack 的 router,然后将结果综合为连贯答案。涵盖状态定义、专门代理、使用 Send 进行并行执行,以及结果综合。

无状态与有状态

两种方法:

无状态

每个请求都会被独立路由,调用之间没有记忆。对于多轮对话,请参阅有状态 router
Router 与 Subagents:两种模式都可以将工作分派给多个代理,但路由决策的方式不同:
  • Router:一个专门的路由步骤(通常是一次 LLM 调用或基于规则的逻辑),用于对输入分类并分派给代理。Router 本身通常不维护对话历史,也不执行多轮编排,它是一个预处理步骤。
  • Subagents:主监督代理会在持续对话中动态决定调用哪些子代理。主代理维护上下文,可以跨轮次调用多个子代理,并编排复杂的多步骤工作流。
当输入类别清晰,并且你想要确定性或轻量分类时,请使用 router。当你需要灵活、感知对话的编排,让 LLM 根据不断变化的上下文决定下一步时,请使用 supervisor

有状态

对于多轮对话,需要跨调用维护上下文。

工具封装

最简单的方法是将无状态 router 封装为对话代理可调用的工具。对话代理处理记忆和上下文,router 保持无状态。这避免了在多个并行代理之间管理对话历史的复杂性。
const searchDocs = tool(
  async ({ query }) => {
    const result = await workflow.invoke({ query });
    return result.finalAnswer;
  },
  {
    name: "search_docs",
    description: "Search across multiple documentation sources",
    schema: z.object({
      query: z.string().describe("The search query"),
    }),
  }
);

// Conversational agent uses the router as a tool
const conversationalAgent = createAgent({
  model,
  tools: [searchDocs],
  systemPrompt: "You are a helpful assistant. Use search_docs to answer questions.",
});

完整持久化

如果需要 router 本身维护状态,请使用持久化来存储消息历史。路由到代理时,从状态中获取先前消息,并选择性地将它们包含在代理上下文中,这是 context engineering 的一个控制手段。
有状态 router 需要自定义历史管理。 如果 router 跨轮次在代理之间切换,而代理拥有不同语气或提示词,终端用户可能会觉得对话不连贯。使用并行调用时,你需要在 router 层维护历史(输入和综合输出),并在路由逻辑中利用这些历史。可以考虑改用 handoffs 模式subagents 模式,二者都为多轮对话提供更清晰的语义。