Skip to content

路由器 (Router) ​

在路由器 (router) 架构中,路由步骤对输入进行分类,并将其定向至专业的智能体。当你拥有截然不同的垂直领域 (verticals)(各个独立的知识领域,每个领域都需要专属的智能体)时,这非常有用。

核心特性 ​

  • 路由器分解查询
  • 并行调用零个或多个专业智能体
  • 将结果合成为连贯的响应

何时使用 ​

当你有截然不同的垂直领域(各个独立的知识领域,每个领域都需要专属的智能体)、需要并行查询多个数据源,并且希望将结果合成为统一的响应时,请使用路由器模式。

基础实现 ​

路由器对查询进行分类并将其定向至适当的智能体。对于单智能体路由,请使用 Command;对于并行扇出至多个智能体,请使用 Send。

单个智能体

使用 Command 路由至单个专业智能体:

typescript
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> {
  // 使用 LLM 对查询进行分类并确定适当的智能体
  // 此处为分类逻辑
  ...
}

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

  // 路由至所选智能体
  return new Command({ goto: classification.agent });
}

多个智能体(并行)

使用 Send 并行扇出至多个专业智能体:

typescript
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>[] {
  // 使用 LLM 对查询进行分类并确定适当的智能体
  // 此处为分类逻辑
  ...
}

function routeQuery(state: typeof State.State) {
  const classifications = classifyQuery(state.query);

  // 并行扇出至所选智能体
  return classifications.map(
    (c) => new Send(c.agent, { query: c.query })
  );
}

有关完整实现,请参阅以下教程。

教程:构建带路由的多源知识库

构建一个能并行查询 GitHub、Notion 和 Slack 并将结果合成为连贯答案的路由器。涵盖状态定义、专业智能体、使用 Send 并行执行以及结果合成。

无状态 vs. 有状态 ​

两种方法:

无状态 ​

每个请求都被独立路由——调用之间没有记忆。有关多轮对话,请参阅有状态路由器。

TIP

路由器 vs. 子智能体:这两种模式都可以将工作分发给多个智能体,但它们在做出路由决策的方式上有所不同:

  • 路由器:一个专用的路由步骤(通常是单次 LLM 调用或基于规则的逻辑),用于对输入进行分类并分发给各个智能体。路由器本身通常不维护对话历史,也不执行多轮编排——它是一个预处理步骤。
  • 子智能体:中央主管智能体在持续的对话中动态决定调用哪些子智能体。主管智能体维护上下文,可以跨轮次调用多个子智能体,并编排复杂的多步骤工作流。

当你有明确的输入类别并希望进行确定性或轻量级的分类时,请使用路由器。当你需要灵活的、具备对话感知能力的编排(其中 LLM 根据不断演变的上下文来决定下一步操作)时,请使用主管。

有状态 ​

对于多轮对话,你需要在多次调用之间维护上下文。

工具包装器 ​

最简单的方法:将无状态路由器包装为一个可供对话智能体调用的工具。对话智能体处理记忆和上下文;路由器保持无状态。这避免了跨多个并行智能体管理对话历史的复杂性。

typescript
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"),
    }),
  }
);

// 对话智能体将路由器作为工具使用
const conversationalAgent = createAgent({
  model,
  tools: [searchDocs],
  systemPrompt: "You are a helpful assistant. Use search_docs to answer questions.",
});

完全持久化 ​

如果你需要路由器本身维护状态,请使用持久化来存储消息历史。在路由到某个智能体时,从状态中获取先前的消息,并有选择地将其包含在智能体的上下文中——这是上下文工程的一个抓手。

WARNING

有状态路由器需要自定义历史管理。如果路由器在不同轮次之间切换智能体,当不同智能体具有不同的语气或提示词时,终端用户可能会感觉对话不够流畅。在并行调用时,你需要在路由器级别维护历史记录(输入和合成后的输出),并在路由逻辑中利用这一历史记录。建议考虑改用移交模式或子智能体模式——这两种模式都能为多轮对话提供更清晰的语义。