智能体设计模式 第二章:路由
路由模式概述
提示链虽然是执行确定性线性工作流的基础方法,但在需要自适应响应的场景下显得力不从心。现实场景中,智能体系统往往要根据环境状态、用户输入或上一步的执行结果等情境信息,从多个可选方案中选择合适的行动路径。路由(Routing)机制就是实现这种控制流分发的关键技术,它决定该将请求交给哪个功能模块、工具或子流程处理。

路由为智能体引入了条件分支能力,让系统不再沿着固定流程执行,而是能根据实际情况动态选择最优的后续动作,从而实现更灵活、更懂上下文的智能行为。
以客户咨询智能体为例。集成路由功能后,系统会先识别用户的真实意图,然后将请求分发到对应的处理单元:简单问题交由问答智能体处理,账户查询则调用数据库检索工具,复杂问题则升级到人工处理,而非采用单一的预设响应路径。

因此,具有路由功能的智能体可以:
1. 分析用户的请求。
2. 基于查询的意图将其路由到相应的处理路径:
「查订单」→ 调用订单查询子智能体或工具链
「问产品」→ 调用产品目录检索子智能体或工具链
「求技术支持」→ 查阅故障排除手册或转人工
「意图不明」→ 转到澄清子智能体追问细节

路由模式的核心在于评估与决策机制,即判断请求类型并确定执行路径。常见的实现方式包括:
-
- 基于大语言模型的路由 (LLM-based Routing):通过提示词引导语言模型分析输入并输出特定的分类标识或指令,以指示下一步的执行目标。例如,提示词可以要求模型「分析以下用户查询并仅输出类别:订单状态、产品信息、技术支持或其他」。智能体系统读取该输出后,据此将工作流导向相应的处理路径。

- 基于大语言模型的路由 (LLM-based Routing):通过提示词引导语言模型分析输入并输出特定的分类标识或指令,以指示下一步的执行目标。例如,提示词可以要求模型「分析以下用户查询并仅输出类别:订单状态、产品信息、技术支持或其他」。智能体系统读取该输出后,据此将工作流导向相应的处理路径。
-
- 向量路由 (Embedding-based Routing):将输入查询转换为向量嵌入,然后与代表不同路由或能力的嵌入向量进行比较,将查询路由到嵌入相似度最高的路径。此方法适用于语义路由场景,其决策基于输入的语义含义而非仅仅关键词匹配。例如,「帮我退款」和「订单有问题想取消」虽然措辞不同,但向量距离相近,因此都会被路由到退款处理流程。

- 向量路由 (Embedding-based Routing):将输入查询转换为向量嵌入,然后与代表不同路由或能力的嵌入向量进行比较,将查询路由到嵌入相似度最高的路径。此方法适用于语义路由场景,其决策基于输入的语义含义而非仅仅关键词匹配。例如,「帮我退款」和「订单有问题想取消」虽然措辞不同,但向量距离相近,因此都会被路由到退款处理流程。
-
- 规则路由(Rule-based Routing):基于关键词、模式或从输入中提取的结构化数据,使用预定义规则或逻辑(如 if-else 语句、switch 语句)进行决策。此方法比大模型路由更快速且具有确定性,但在处理复杂语境或新颖输入时灵活性较低。

-
- 机器学习路由(Machine Learning Model-Based Routing):采用判别式模型(如分类器),该模型在少量标注数据上经过专门训练以执行路由任务。虽然在概念上与向量路由方法有相似之处,但其关键特征在于监督微调过程,通过调整模型参数来创建专门的路由功能。此技术与大模型路由的区别在于,其决策组件并非在推理时执行提示词的生成式模型,而是将路由逻辑编码在微调后模型的学习权重中。虽然在预处理阶段可能使用大语言模型生成合成数据以扩充训练集,但实时路由决策本身并不涉及大模型。

路由机制可在智能体运行周期的多个节点实施:可在初始阶段对主要任务进行分类,可在处理链的中间点确定后续操作,也可在子程序中从给定工具集中选择最合适的工具。
LangChain、LangGraph 和 Google 智能体开发套件(ADK)等计算框架为定义和管理此类条件逻辑提供了明确的构造。凭借基于状态的图架构,LangGraph 特别适合复杂的路由场景,其中决策取决于整个系统的累积状态。类似地,Google ADK 提供了用于构建智能体能力和交互模型的基础组件,这些组件是实现路由逻辑的基础。在这些框架提供的执行环境中,开发人员可定义可能的操作路径,以及决定计算图中节点间转换的函数或基于模型的评估。
路由的实现使系统能够超越确定性的顺序处理。它促进了更具适应性的执行流程的开发,能够动态且恰当地响应更广泛的输入和状态变化。

实际应用场景
路由模式是自适应智能体系统设计中的关键控制机制,使系统能够根据可变输入和内部状态动态调整执行路径,从而提供必要的条件逻辑层,其应用范围涵盖多个领域。
人机交互:在虚拟助手或 AI 驱动的辅导系统等场景中,路由用于解释用户意图。通过对自然语言查询的初步分析,系统可确定最合适的后续操作,无论是调用特定的信息检索工具、升级至人工操作员,还是根据用户表现选择课程中的下一个模块。这使系统能够超越线性对话流程,进行上下文相关的响应。

数据处理流水线:在自动化数据和文档处理流水线中,路由充当分类和分发功能。系统基于内容、元数据或格式对传入的数据(如电子邮件、支持工单或 API 负载)进行分析,然后将每项内容导向相应的工作流,例如销售线索录入流程、针对 JSON 或 CSV 格式的特定数据转换功能,或紧急问题升级路径。

多智能体协作:在涉及多个专业工具或智能体的复杂系统中,路由充当高级调度器。由用于搜索、总结和分析信息的不同智能体组成的研究系统,会使用路由器根据当前目标将任务分配给最合适的智能体。类似地,AI 编码助手在将代码片段传递给正确的专业工具之前,会使用路由来识别编程语言和用户意图(调试、解释或翻译)。
归根结底,路由提供了创建功能多样化和上下文感知系统所必需的逻辑仲裁能力。它将智能体从预定义序列的静态执行器转变为能够在变化条件下决定完成任务最有效方法的动态系统。

实战示例:LangChain 实现
在代码中实现路由涉及定义可能的路径以及决定选择哪条路径的逻辑。LangChain 和 LangGraph 等框架为此提供了特定的组件和结构。LangGraph 基于状态的图结构对于可视化和实现路由逻辑特别直观。
以下代码演示了使用 LangChain 和 DeepSeek 生成式 AI 构建的简单类智能体系统。它设置了一个「协调员」,根据请求的意图(预订、信息查询或不明确)将用户请求路由到不同的模拟「子智能体」处理器。系统使用语言模型对请求进行分类,然后将其委派给适当的处理函数,模拟了多智能体架构中常见的基本委派模式。
首先,确保你安装了必要的库:
javascript
pip install langchain langgraph google-cloud-aiplatform langchain-google-genai google-adk deprecated pydantic
你还需要在环境中设置所选语言模型(如 OpenAI、Google Gemini、Anthropic)的 API 密钥。
javascript
注意先加载配置文件:
javascript
import os
from dotenv import load_dotenv
load_dotenv() # 加载 .env 文件
api_key = os.getenv("GOOGLE_API_KEY")
print(api_key)
javascript
运行输出:
javascript
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI # 用 DeepSeek 时用这个而不是 Gemini
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough, RunnableBranch
# ─────────────────────────────────────────────────
# 1. 载入 .env 环境变量
# ─────────────────────────────────────────────────
load_dotenv()
# ─────────────────────────────────────────────────
# 2. 初始化 DeepSeek 作为主路由 LLM
# - 使用 openai 兼容协议
# - 读取:
# DEEPSEEK_API_KEY=sk-xxxx
# DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
# ─────────────────────────────────────────────────
try:
_ApiKey = os.getenv("DEEPSEEK_API_KEY")
_BaseUrl = os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com/v1")
# ChatOpenAI 其实可以对接任意 OpenAI 格式的服务
llm = ChatOpenAI(
model="deepseek-chat", # DeepSeek 常用对话模型名
api_key=_ApiKey,
base_url=_BaseUrl,
temperature=0
)
print(f"[Init] Language model initialized: {llm.model_name}")
except Exception as e:
print(f"[Init] Error initializing language model: {e}")
llm = None
# ─────────────────────────────────────────────────
# 3. 子智能体处理器(模拟子Agent)
# 这些函数未来可以换成实际业务逻辑,比如订票API/知识库检索
# ─────────────────────────────────────────────────
def booking_handler(_Request: str) -> str:
print("\n--- 委派给预订智能体 ---")
return f"预订智能体已处理请求:「{_Request}」。结果:模拟完成机票或酒店预订。"
def info_handler(_Request: str) -> str:
print("\n--- 委派给信息智能体 ---")
return f"信息智能体已处理请求:「{_Request}」。结果:模拟完成信息查询。"
def unclear_handler(_Request: str) -> str:
print("\n--- 请求无法识别 ---")
return f"协调员无法判断请求:「{_Request}」。请您补充说明。"
# ─────────────────────────────────────────────────
# 4. 路由器 Prompt
# 让 LLM 只输出一个标签:booker / info / unclear
# ─────────────────────────────────────────────────
coordinator_router_prompt = ChatPromptTemplate.from_messages([
(
"system",
"""分析用户的请求,判断应该由哪个智能体处理:
- 如果请求涉及"订机票"、"订酒店"等出行预订相关,输出"booker";
- 如果是一般信息查询问题,输出"info";
- 如果问题不明确或不属于以上两类,输出"unclear";
只输出一个单词:booker、info 或 unclear。"""
),
("user", "{request}")
])
if llm:
coordinator_router_chain = coordinator_router_prompt | llm | StrOutputParser()
else:
coordinator_router_chain = None # 防御:如果 LLM 初始化失败,后面不再继续跑
# ─────────────────────────────────────────────────
# 5. 定义路由分支(根据 decision 选择对应子Agent)
# 注意这里 request 是字符串,不是 dict
# ─────────────────────────────────────────────────
branches = {
"booker": RunnablePassthrough.assign(
output=lambda x: booking_handler(x["request"])
),
"info": RunnablePassthrough.assign(
output=lambda x: info_handler(x["request"])
),
"unclear": RunnablePassthrough.assign(
output=lambda x: unclear_handler(x["request"])
),
}
delegation_branch = RunnableBranch(
(lambda x: x["decision"].strip() == "booker", branches["booker"]),
(lambda x: x["decision"].strip() == "info", branches["info"]),
branches["unclear"] # 默认分支兜底
)
# ─────────────────────────────────────────────────
# 6. 组装最终的协调智能体
# 输入: {"request": "...用户说的话..."}
# 流程:
# 调用 llm -> 得到 decision 标签
# 根据标签走某个 handler
# 返回 handler 的字符串结果
# ─────────────────────────────────────────────────
if coordinator_router_chain:
coordinator_agent = (
{
"decision": coordinator_router_chain,
"request": RunnablePassthrough()
}
| delegation_branch
| (lambda x: x["output"]) # 只拿最终结果字符串
)
else:
coordinator_agent = None
# ─────────────────────────────────────────────────
# 7. 演示主函数
# ─────────────────────────────────────────────────
def main():
if not coordinator_agent:
print("\n[Run] 由于语言模型初始化失败或代理未准备好,因此跳过执行。")
return
print("--- Running with a booking request ---")
_ReqA = "请为我预订一张去伦敦的机票。"
_ResA = coordinator_agent.invoke({"request": _ReqA})
print(f"Final Result A: { _ResA }")
print("\n--- Running with an info request ---")
_ReqB = "意大利的首都是哪里?"
_ResB = coordinator_agent.invoke({"request": _ReqB})
print(f"Final Result B: { _ResB }")
print("\n--- Running with an unclear request ---")
_ReqC = "跟我讲讲量子物理学吧。"
_ResC = coordinator_agent.invoke({"request": _ReqC})
print(f"Final Result C: { _ResC }")
if __name__ == "__main__":
main()

如前所述,这段 Python 代码使用 LangChain 库和 Deepseek 模型构建了一个简单的类智能体系统。它定义了三个模拟的子智能体处理器:booking_handler、info_handler 和 unclear_handler,分别用于处理特定类型的请求。
- 核心组件是 coordinator_router_chain,它通过 ChatPromptTemplate 指示模型将用户请求分为三类:'booker'、'info' 或 'unclear'。随后 RunnableBranch 使用路由链的输出将原始请求委派给相应的处理函数。
- RunnableBranch 根据模型的判断,将请求数据发送到 booking_handler、info_handler 或 unclear_handler。
- coordinator_agent 将这些部分组合在一起:先进行路由决策,然后把请求转给选定的处理器,最后从处理器的响应中提取并返回最终结果。
主函数通过三个示例请求展示了系统的实际用法,说明不同的输入如何被路由并由各个模拟智能体处理。为了保证稳定性,代码还包含了语言模型初始化的错误处理。整体代码结构类似一个简化的多智能体框架:中央协调器根据意图把任务分配给各个专长不同的智能体。
DeepSeek 多智能体路由示例
实现了一个最小可用的"多智能体路由"Demo:
1. 用 DeepSeek(OpenAI 兼容接口) 当"协调员 LLM(Coordinator)"
3. 协调员只做一件事:把用户请求分到 3 个标签之一:booker / info / unclear
4. 再由你自己写的三个处理函数(handler)去真正处理
5. 运行 main() 会用 4 条示例请求跑一遍完整流程
核心思想:LLM 负责意图分类(routing),业务逻辑由确定性的代码实现。
代码关键点解析
init_llm:把 DeepSeek 当成"协调员模型"
-
base_url=_BaseUrl:把请求发到 DeepSeek 的 OpenAI 兼容地址(而不是 OpenAI 官方)。
-
model="deepseek-chat":指定 DeepSeek 的模型名(走兼容层)。
-
temperature=0:路由/分类任务必须稳定。温度高会让输出漂移,比如把 booker 写成 booking 或加解释文字,导致后续
if/else 分支跑偏。
三个 handler:子智能体"业务执行层"
- booking_handler:预订类
- info_handler:信息问答类
- unclear_handler:兜底
路由 Prompt:整个脚本的"决策规则"
- 给协调员定义角色:只做"请求分配"
- 给出分类规则(预订→booker,事实/查询→info,模糊/长科普/观点→unclear)
- 强约束输出格式:只允许输出 3 个词之一,不能带解释
这个"只输出一个词"的约束非常关键,否则 LLM 输出一长段解释会让后续 if/else 失效。
run_coordinator:一次请求的完整闭环
-
生成 session_id(仅用于日志追踪)
-
调用路由链拿到决策
-
.strip().lower():做轻微清洗,避免 "\nBOOKER " 这种导致分支失败
-
决策分发到不同 handler
-
返回 handler 的输出
javascript
# ─────────────────────────────────────────────────
# 项目:DeepSeek 多智能体路由示例
# 说明:
# 1. 使用 DeepSeek (OpenAI 兼容接口) 做"协调员LLM"
# 2. Coordinator 判断应该由哪个子Agent处理
# 3. Booker / Info 子智能体由我们自己实现(函数+描述)
# 4. 不依赖 google.adk / google.genai / InMemoryRunner
# 5. 支持中文/英文请求
# ─────────────────────────────────────────────────
import os
import uuid
from dotenv import load_dotenv
from typing import Literal, Dict
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
# ─────────────────────────────────
# 0. 加载环境变量(DeepSeek API)
# 需要 .env 中有:
# DEEPSEEK_API_KEY=你的key
# DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
# ─────────────────────────────────
load_dotenv()
# ─────────────────────────────────
# 1. 初始化 DeepSeek 作为协调员用的 LLM
# ─────────────────────────────────
def init_llm():
_ApiKey = os.getenv("DEEPSEEK_API_KEY")
_BaseUrl = os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com/v1")
if not _ApiKey:
raise RuntimeError("DEEPSEEK_API_KEY 未设置,请在 .env 中配置")
llm = ChatOpenAI(
model="deepseek-chat",
api_key=_ApiKey,
base_url=_BaseUrl,
temperature=0 # 路由用分类逻辑,需要稳定输出
)
print(f"[Init] DeepSeek coordinator model ready: {llm.model_name}")
return llm
_CoordinatorLLM = init_llm()
# ─────────────────────────────────
# 2. 定义"子智能体"的处理函数
# 这些相当于 google.adk 里的 sub_agents + tools
# ─────────────────────────────────
def booking_handler(_Request: str) -> str:
"""
预订智能体:
负责机票 / 酒店 / 行程安排类请求
"""
print("-------------------------- Booking Handler Called ----------------------------")
return (
f"【预订智能体】已处理请求:「{_Request}」。"
f"模拟结果:已为你查询/预订行程(示例)。"
)
def info_handler(_Request: str) -> str:
"""
信息智能体:
负责一般知识问答 / 信息查询
"""
print("-------------------------- Info Handler Called ----------------------------")
return (
f"【信息智能体】已处理请求:「{_Request}」。"
f"模拟结果:已为你检索并整理相关信息(示例)。"
)
def unclear_handler(_Request: str) -> str:
"""
兜底智能体:
当协调员无法明确分类时触发
"""
print("-------------------------- Fallback Handler Called ----------------------------")
return (
f"【协调员未能识别】无法明确分类该请求:「{_Request}」。"
f"请进一步说明你想做什么。"
)
# ─────────────────────────────────
# 3. 协调员(Coordinator)的"路由决策 Prompt"
#
# 我们让 DeepSeek 输出 3 个标签中的一个:
# - booker (交给 booking_handler)
# - info (交给 info_handler)
# - unclear (交给 unclear_handler)
#
# 这里相当于你原来 google.adk 里的父 Agent 的 instruction
# ─────────────────────────────────
CoordinatorRouterPrompt = ChatPromptTemplate.from_messages([
(
"system",
"""你是一个请求分配协调员。你的工作是分析用户的意图,
并决定把这个请求交给哪一个专属子智能体去处理。请按以下规则分类,并且只输出一个词:
- 如果用户在请求预订出行服务(例如 订机票 / 订酒店 / 安排行程 / 查询航班 / 预定交通),输出:booker
- 如果用户是在问明确的信息或知识问题(例如 某地的首都 / 某个定义 / 某个事实 / 查询某项信息),输出:info
- 如果用户的请求模糊、不完整、范围太宽(例如 解释复杂理论 / 闲聊 / 观点类 / 让你长篇科普),输出:unclear
只输出:booker、info 或 unclear
不要输出其它任何文字。"""
),
("user", "{request}")
])
CoordinatorRouterChain = CoordinatorRouterPrompt | _CoordinatorLLM | StrOutputParser()
# ─────────────────────────────────
# 4. 定义一个"run_coordinator"函数
#
# 作用:
# - 生成 session_id(模拟会话)
# - 问 DeepSeek:这个 request 应该路由给哪一个子智能体?
# - 根据决策调用对应 handler
# - 返回最终子智能体给到的结果字符串
#
# 这相当于你之前 run_coordinator(runner, request) 的 DeepSeek 版本。
# ─────────────────────────────────
def run_coordinator(_UserRequest: str) -> str:
"""
这里扮演"协调员 + 自动委派"的角色。
"""
# 给这个任务一个"session_id"(只是为了演示和可追踪)
_SessionId = str(uuid.uuid4())
print(f"\n=== [Coordinator] New session: { _SessionId } ===")
print(f"[Coordinator] Incoming request: { _UserRequest }")
# 第一步:让 DeepSeek 判定路由标签
_DecisionRaw = CoordinatorRouterChain.invoke({"request": _UserRequest})
_Decision = _DecisionRaw.strip().lower()
print(f"[Coordinator] Routing decision: { _Decision }")
# 第二步:根据路由结果调用对应的子智能体
if _Decision == "booker":
_Result = booking_handler(_UserRequest)
elif _Decision == "info":
_Result = info_handler(_UserRequest)
else:
# 兜底:unclear 或任何未识别输出
_Result = unclear_handler(_UserRequest)
# 打印最终结果
print(f"[Coordinator] Final delegated result:\n{_Result}")
return _Result
# ─────────────────────────────────
# 5. main() 演示
#
# 等价于你原来 main() 里用 InMemoryRunner 多次 run_coordinator(...)
# ─────────────────────────────────
def main():
print("--- DeepSeek Multi-Agent Routing Demo (no Google ADK) ---")
# A. 典型预订类请求 → 应该走 booker
_ResA = run_coordinator("请帮我预订明天早上去上海的高铁票。")
print(f"Final Output A: { _ResA }")
# B. 明确信息查询 → 应该走 info
_ResB = run_coordinator("世界上海拔最高的山是哪一座?")
print(f"Final Output B: { _ResB }")
# C. 泛泛科普 / 宽泛主题 → 我们希望它走 unclear
_ResC = run_coordinator("跟我讲讲量子力学的本质是什么。")
print(f"Final Output C: { _ResC }")
# D. 英文请求也可以,会自动分类
_ResD = run_coordinator("Find me a cheap flight to Tokyo next month.")
print(f"Final Output D: { _ResD }")
if __name__ == "__main__":
main()

要点速览
问题所在:智能体系统往往需要应对各种各样的输入和情境,单一的线性流程无法满足这一需求。简单的顺序工作流缺乏基于上下文做出决策的能力。如果没有为特定任务选择正确工具或子流程的机制,系统将保持僵化且缺乏适应性。这一局限性使得构建能够管理真实世界用户请求的复杂性和可变性的成熟应用变得困难。

解决之道:路由模式通过在智能体操作框架中引入条件逻辑,提供了标准化解决方案。它使系统能够首先分析传入查询以确定其意图或性质,然后基于此分析,智能体动态地将控制流导向最合适的专业工具、函数或子智能体。这一决策可由多种方法驱动,包括提示大语言模型、应用预定义规则或使用基于嵌入的语义相似度。最终,路由将静态的预定执行路径转变为能够选择最佳可能操作的灵活且具上下文感知的工作流。

经验法则:当智能体必须根据用户输入或当前状态在多个不同的工作流、工具或子智能体之间做出选择时,应使用路由模式。此模式对于需要对传入请求进行分类或分派以处理不同类型任务的应用至关重要,例如客户支持机器人需要区分销售咨询、技术支持和账户管理问题,并将每种类型的请求路由到相应的处理模块。

javascript
可视化总结:

核心要点
-
路由使智能体能够根据条件,动态决定工作流的下一步。
-
它允许智能体处理多样化的输入并适应其行为,超越了线性的执行方式。
-
路由逻辑可以使用大语言模型、基于规则的系统或嵌入相似度来实现。
-
像 LangGraph 和 Google ADK 这样的框架,为在智能体工作流中定义和管理路由提供了结构化的方法,尽管它们的架构方式有所不同。

结语
路由模式是构建真正动态且能响应变化的智能体系统的关键环节。通过实施路由,系统得以超越简单的线性执行流程,使智能体能够就如何处理信息、响应用户输入以及使用可用工具或子智能体做出智能决策。
路由在各个领域都有应用,从客户服务聊天机器人到复杂的数据处理流水线。分析输入并根据条件引导工作流的能力,是创建能够处理真实世界任务固有可变性的智能体的基础。
使用 LangChain 和 Google ADK 的代码示例展示了两种不同但都有效的路由实现方法。LangGraph 基于图的结构提供了一种可视化和明确的方式来定义状态与转换,非常适合具有复杂路由逻辑的多步骤工作流。另一方面,Google ADK 通常侧重于定义独立的能力(工具),并依赖框架将用户请求路由到适当的工具处理器的能力,这对于具有明确定义的离散操作集的智能体来说可能更简单。
掌握路由模式对于构建能根据不同场景和上下文智能选择响应或执行操作的智能体至关重要,是打造灵活可靠的智能体应用的核心要素。

参考文献
暂时先这样吧,如果实在看不明白就留言,看到我会回复的。希望这个教程对您有帮助!
路漫漫其修远,与君共勉。