我们实际做Agent开发的时候,并不是像之前那样提示词链进行顺序处理执行线性工作流。现实环境往往更复杂,我们有很多Agent子系统,需要动态决策,到底当前使用哪个Agent子系统,用哪个工具函数、工具或者子流程。这种动态决策是通过路由实现的。
文章目录
举例
一个设计用于客户查询的Agent,当配备路由功能时,可以首先对传入查询进行分类确定用户的意图。基于此分类,它可以将查询定向到专门的Agent进行直接回答、用于账户信息的数据库检索工具,或用于复杂问题的升级程序,而不是默认使用单一的预定响应路径。
因此,使用复杂的Agent可以:
- 分析用户的查询
- 基于其 意图 路由查询:
- 如果意图是"检查订单状态",路由到与订单数据库交互的子Agent
- 如果意图是"产品信息",路由到搜索产品目录的子Agent或链
- 如果意图是"技术支持",路由到访问故障排除指南
- 如果意图不清楚,路由到澄清子Agent或提示词链
实际应用
路由模式是自适应Agent系统设计中的关键控制机制。
在人机交互中,例如虚拟助手或AI驱动导师,路由用于解释用户意图。对自然语言查询的初始分析确定后续的操作,无论是调用特定信息检索工具、升级到人工操作员,还是根据用户表现选择课程中的上下模块。在涉及多个专门工具或Agent的复杂系统中,路由充当高级调度器。类似地,AI编码助手使用路由来识别编程语言和用户意图------调试、解释或翻译----然后将代码片段传递到正确的专门工具。
最终,路由提供了创建功能多样化和上下文感知系统所需的逻辑仲裁能力。他将Agent从预定义序列的静态执行器转变为在可变条件下就完成任务的最有效任务做出决策的动态系统。
python
import os
from langchain_community.chat_models import ChatTongyi
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough, RunnableBranch
os.environ["DASHSCOPE_API_KEY"] = "sk-xxx"
###路由的案例
try:
llm = ChatTongyi(model="qwen-turbo-2025-04-28")
print(f"llm已经初始化:{llm}")
except Exception as e:
print(f"初始化模型失败:{e}")
llm = None
## --- 定义模拟子 Agent 处理程序(相当于 ADK 的 sub_agents)---
def booking_handler(request: str) -> str:
"""模拟预订 Agent 处理请求。"""
print("\n--- 委托给预订处理程序 ---")
return f"预订处理程序处理了请求:'{request}'。结果:模拟预订操作。"
def info_handler(request: str) -> str:
"""模拟信息 Agent 处理请求。"""
print("\n--- 委托给信息处理程序 ---")
return f"信息处理程序处理了请求:'{request}'。结果:模拟信息检索。"
def unclear_handler(request: str) -> str:
"""处理无法委托的请求。"""
print("\n--- 处理不清楚的请求 ---")
return f"协调器无法委托请求:'{request}'。请澄清。"
## --- 定义协调器路由链(相当于 ADK 协调器的指令)---
## 此链决定应委托给哪个处理程序。
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()
## --- 定义委托逻辑(相当于 ADK 的基于 sub_agents 的自动流)---
## 使用 RunnableBranch 根据路由链的输出进行路由。
## 为 RunnableBranch 定义分支
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"])
),
}
## 创建 RunnableBranch。它接受路由链的输出
## 并将原始输入('request')路由到相应的处理程序。
delegation_branch = RunnableBranch(
(lambda x: x['decision'].strip() == 'booker', branches["booker"]), # 添加了 .strip()
(lambda x: x['decision'].strip() == 'info', branches["info"]), # 添加了 .strip()
branches["unclear"] # 'unclear' 或任何其他输出的默认分支
)
## 将路由链和委托分支组合成单个可运行对象
## 路由链的输出('decision')与原始输入('request')一起传递
## 到 delegation_branch。
coordinator_agent = {
"decision": coordinator_router_chain,
"request": RunnablePassthrough()
} | delegation_branch | (lambda x: x['output']) # 提取最终输出
## --- 示例用法 ---
def main():
if not llm:
print("\n由于 LLM 初始化失败,跳过执行。")
return
print("--- 运行预订请求 ---")
request_a = "给我预订去伦敦的航班。"
result_a = coordinator_agent.invoke({"request": request_a})
print(f"最终结果 A: {result_a}")
print("\n--- 运行信息请求 ---")
request_b = "意大利的首都是什么?"
result_b = coordinator_agent.invoke({"request": request_b})
print(f"最终结果 B: {result_b}")
print("\n--- 运行不清楚的请求 ---")
request_c = "告诉我关于量子物理学的事。"
result_c = coordinator_agent.invoke({"request": request_c})
print(f"最终结果 C: {result_c}")
if __name__ == "__main__":
main()
代码提供了一个协调器,根据请求的意图(预定、信息、不清楚)将用户请求路由到不同模拟子Agent处理程序。系统使用语言模型对请求进行分类,然后将其委托给适当的处理函数,模拟多Agent架构中常用的基本委托模式。
路由模式的核心组件是执行评估并指导流程的机制,这种机制除了上面的语言模型进行分类,还可以通过如下方式:
- 基于LLM路由
- 基于嵌入的路由
- 基于规则的路由
- 基于机器学习模型的路由
核心
在这里我们会发现,LLM不是用来干活的,而是用来判断让谁干活的!
python
coordinator_router_prompt = ChatPromptTemplate.from_messages([
("system", """分析用户的请求并确定哪个专家处理程序应处理它。
...
只输出一个词:'booker'、'info' 或 'unclear'。"""),
("user", "{request}")
])
RunnableBranch = if / elif / else:
python
if decision == "booker":
用 booking_handler
elif decision == "info":
用 info_handler
else:
用 unclear_handler
LangChain 写法是:
delegation_branch = RunnableBranch(
(lambda x: x['decision'].strip() == 'booker', branches["booker"]),
(lambda x: x['decision'].strip() == 'info', branches["info"]),
branches["unclear"]
)
你可以暂时无视 RunnableBranch 的写法
把它当成一个"高级 if-else"
我们也可以用Google 推出的Google ADK来实现这个路由,这里就不具体展示了。