1. 项目概述:构建基于LangGraph的Router架构Agent
在构建复杂AI工作流时,路由架构(Router Pattern)是一种将请求智能分发到不同处理节点的经典设计模式。这种架构特别适合需要根据输入内容动态选择处理路径的场景,比如同时具备闲聊和工具调用能力的智能助手系统。
我最近在实际项目中实现了一个基于LangGraph的路由架构Agent,核心流程遵循"状态定义→功能节点开发→路由逻辑实现→图构建"的递进式开发路径。这种顺序不是随意安排的,而是基于三个关键认知:
- 状态即契约:所有节点间的数据交互都通过共享状态(State)完成,必须先明确数据结构
- 节点即能力:功能节点是具体能力的载体,路由决策必须知道有哪些"目的地"可选
- 路由即调度:决策逻辑需要同时理解状态结构和可用节点能力
提示:LangGraph是LangChain生态系统中的工作流编排工具,它通过有向图的方式组织AI处理流程,特别适合构建多步骤、有条件分支的复杂Agent系统。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心组件开发详解
2.1 状态定义:构建数据契约
状态对象是整个工作流的数据中枢,需要精心设计其结构。在我们的聊天/搜索路由场景中,状态需要至少包含对话历史,还可以根据需要添加路由决策标记:
python复制from typing import Annotated, Literal, TypedDict
from langgraph.graph.message import add_messages
class AgentState(TypedDict):
messages: Annotated[list, add_messages] # 对话历史记录
# intent: str # 可选字段:存储路由决策结果
这里有几个关键设计点:
- 使用
TypedDict确保类型安全 Annotated配合add_messages实现消息的追加而非覆盖- 注释掉的可选字段展示了如何扩展状态结构
我在实际项目中发现,初始状态设计不宜过于复杂,应该遵循最小可用原则。随着功能迭代再逐步扩展状态结构,这样可以降低初期开发复杂度。
2.2 功能节点开发:实现具体能力
功能节点是实际执行业务逻辑的单元。在我们的示例中需要开发两个节点:
python复制from langchain_core.messages import AIMessage
def general_chat_node(state: AgentState):
"""处理通用闲聊请求"""
last_msg = state["messages"][-1].content
response = f"收到您的闲聊内容:'{last_msg}'。我是您的AI助手!"
return {"messages": [AIMessage(content=response)]}
def search_tool_node(state: AgentState):
"""处理搜索类请求"""
query = state["messages"][-1].content
# 这里可以接入真实搜索API
result = f"关于'{query}'的搜索结果:..."
return {"messages": [AIMessage(content=result)]}
开发技巧:
- 每个节点函数都接收并返回状态对象
- 初期可以用模拟实现快速验证流程
- 节点应该保持单一职责原则
- 返回的消息建议使用LangChain的消息类型(如AIMessage)
2.3 路由逻辑实现:智能决策核心
路由决策是架构中最关键的部分。我们采用结构化输出方式实现稳健的路由判断:
python复制from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field
class RouteDecision(BaseModel):
"""路由决策数据模型"""
next: Literal["general_chat", "search_tool"] = Field(
description="闲聊选general_chat,查询选search_tool"
)
# 初始化带结构化输出的LLM
llm = ChatOpenAI(model="gpt-4o")
router_llm = llm.with_structured_output(RouteDecision)
def route_decision(state: AgentState):
"""路由判断函数"""
last_message = state["messages"][-1]
decision = router_llm.invoke([last_message])
return decision.next + "_node" # 返回目标节点名称
关键实现细节:
- 使用Pydantic模型定义决策结构
with_structured_output确保LLM返回规范化的JSON- 函数返回的是目标节点的名称标识符
- GPT-4o模型在意图识别任务上表现优异
3. 工作流组装与测试
3.1 图结构构建
将各个组件组装成完整工作流:
python复制from langgraph.graph import StateGraph, END
workflow = StateGraph(AgentState)
# 添加功能节点
workflow.add_node("general_chat_node", general_chat_node)
workflow.add_node("search_tool_node", search_tool_node)
# 设置条件入口
workflow.set_conditional_entry_point(
route_decision,
path_map={
"general_chat_node": "general_chat_node",
"search_tool_node": "search_tool_node"
}
)
# 设置终止边
workflow.add_edge("general_chat_node", END)
workflow.add_edge("search_tool_node", END)
# 编译可执行应用
app = workflow.compile()
3.2 测试与验证
编写测试用例验证不同路径:
python复制# 测试闲聊路径
inputs = {"messages": [("user", "你好啊")]}
print("测试1 - 闲聊场景:")
for output in app.stream(inputs):
print(output)
# 测试搜索路径
inputs = {"messages": [("user", "北京明天天气")]}
print("\n测试2 - 搜索场景:")
for output in app.stream(inputs):
print(output)
预期输出应该显示请求被正确路由到不同节点,并返回相应的处理结果。
4. 高级技巧与生产级优化
4.1 性能优化策略
在实际生产环境中,我们可以采用以下优化手段:
- 路由缓存:对相似请求缓存路由决策
python复制from functools import lru_cache
@lru_cache(maxsize=100)
def cached_route_decision(text: str):
return router_llm.invoke([HumanMessage(content=text)])
- 异步执行:对IO密集型节点使用异步实现
python复制async def async_search_node(state: AgentState):
# 异步调用搜索API
pass
- 批量处理:对队列中的多个请求进行批量路由决策
4.2 监控与可观测性
添加监控指标收集:
python复制from prometheus_client import Counter
ROUTING_DECISIONS = Counter(
'routing_decisions_total',
'Total routing decisions',
['target_node']
)
def monitored_route_decision(state: AgentState):
target = route_decision(state)
ROUTING_DECISIONS.labels(target_node=target).inc()
return target
4.3 常见问题排查
-
路由抖动问题:
- 现象:相同输入在不同时间被路由到不同节点
- 解决方案:调整LLM温度参数,添加示例few-shot
-
状态污染问题:
- 现象:某个节点意外修改了其他节点依赖的状态字段
- 解决方案:使用深拷贝或不可变数据结构
-
性能瓶颈:
- 现象:路由决策延迟高
- 解决方案:使用轻量级模型进行初步过滤
5. 架构演进建议
随着业务复杂度提升,可以考虑以下演进方向:
- 多级路由:先粗粒度分类,再细粒度分发
- 动态节点注册:运行时动态添加/移除功能节点
- 反馈学习:根据执行结果优化路由策略
- 熔断机制:对故障节点自动降级
路由架构的扩展性很强,但要注意控制复杂度。我的经验是:每增加一个功能节点,路由逻辑的复杂度呈指数级增长,因此需要建立完善的自动化测试体系。
