引言:从 LangChain 到 LangGraph
在 AI 应用开发中,LangChain 已经成为构建大模型应用的事实标准。它提供了从基础模型调用到复杂 Agent 构建的全套工具链。然而,随着应用复杂度提升,开发者面临新的挑战:
- 线性流程限制:传统 Chain 只能单向执行,难以处理需要循环、分支的场景
- 状态管理困难:对话历史、中间结果需要在不同组件间手动传递
- 调试复杂度高:Agent 内部执行过程如同"黑盒",难以追踪和调试
- 人机协作缺失:缺乏标准化的中断和人工干预机制
LangGraph 正是为解决这些问题而生。它不是一个替代品,而是 LangChain 的工程化增强层——为"大模型调用智能体"这个充满不确定性的过程,套上了一层稳定可控的外壳。
LangGraph 核心概念
1. Graph(图):工作流的蓝图
LangGraph 的核心是图结构,由节点(Nodes)和边(Edges)组成:
from langgraph.graph import StateGraph, START, END
from typing import TypedDict
# 定义状态结构
class SimpleState(TypedDict):
message: str
processed: bool
# 定义节点函数
def greet_node(state: SimpleState) -> dict:
"""欢迎节点:生成问候语"""
return {"message": f"你好!{state['message']}"}
def process_node(state: SimpleState) -> dict:
"""处理节点:标记为已处理"""
return {"processed": True}
# 构建图
builder = StateGraph(SimpleState)
builder.add_node("greet", greet_node)
builder.add_node("process", process_node)
builder.add_edge(START, "greet")
builder.add_edge("greet", "process")
builder.add_edge("process", END)
# 编译并运行
graph = builder.compile()
result = graph.invoke({"message": "世界", "processed": False})
2. State(状态):贯穿始终的数据流
State 是工作流的共享数据结构,每个节点都可以读取和更新它。LangGraph 提供了多种状态管理方式:
from typing import Annotated, Optional
from langgraph.graph.message import add_messages
# 方式1:使用 TypedDict(灵活)
class AgentState(TypedDict):
# 消息历史(自动追加而非覆盖)
messages: Annotated[list, add_messages]
# 普通字段(直接覆盖)
user_id: str
session_id: str
# 可选字段
error: Optional[str]
# 计数器(自定义 reducer)
retry_count: Annotated[int, lambda x, y: x + y]
# 方式2:使用 Pydantic(推荐生产环境)
from pydantic import BaseModel, Field
class ProductionState(BaseModel):
messages: Annotated[list, add_messages] = Field(default_factory=list)
user_id: str = ""
confidence_score: float = 0.0
class Config:
arbitrary_types_allowed = True
# 方式3:使用内置 MessagesState(对话场景专用)
from langgraph.graph import MessagesState
# 等价于:messages: Annotated[list[AnyMessage], add_messages]
3. Nodes(节点):执行单元
节点是普通的 Python 函数,接收当前 State,返回更新后的 State:
# 普通函数节点
def simple_node(state: AgentState) -> dict:
last_message = state["messages"][-1]
response = f"收到: {last_message.content}"
return {"messages": [{"role": "assistant", "content": response}]}
# LLM 调用节点
def llm_node(state: dict) -> dict:
"""调用 LLM 的节点"""
from langchain_core.messages import SystemMessage
system_prompt = SystemMessage(content="你是一个有帮助的助手。")
messages = [system_prompt] + state["messages"]
response = llm.invoke(messages)
return {"messages": [response]}
# 异步节点(适合 I/O 密集型操作)
import asyncio
async def async_node(state: AgentState) -> dict:
"""异步节点,适合 API 调用、数据库查询"""
await asyncio.sleep(0.1)
result = await some_async_api_call(state["messages"][-1].content)
return {"messages": [{"role": "assistant", "content": result}]}
# 使用类作为节点(可维护状态)
class RouterNode:
def __init__(self, llm, system_prompt: str):
self.llm = llm
self.system_prompt = system_prompt
def __call__(self, state: AgentState) -> dict:
"""类实例可以作为节点使用"""
messages = [
SystemMessage(content=self.system_prompt),
*state["messages"]
]
response = self.llm.invoke(messages)
return {"messages": [response]}
4. Edges(边):控制流转逻辑
边定义了节点之间的流转方式,支持条件路由和并行执行:
# 普通边(线性流转)
builder.add_edge(START, "greet")
builder.add_edge("greet", "process")
builder.add_edge("process", END)
# 条件边(动态路由)
def route_after_llm(state: AgentState) -> str:
"""
路由函数:根据 LLM 的最新输出决定下一步
返回值必须是已注册节点名称或 END
"""
last_message = state["messages"][-1]
# 如果 LLM 请求使用工具
if hasattr(last_message, "tool_calls") and last_message.tool_calls:
return "tools"
# 否则结束
return END
# 添加条件边
builder.add_conditional_edges(
"llm", # 源节点
route_after_llm, # 路由函数
{
"tools": "tool_executor", # 返回 "tools" 时 -> tool_executor 节点
END: END # 返回 END 时 -> 结束
}
)
# 并行执行(Fan-out)
builder.add_edge("start_node", "branch_a")
builder.add_edge("start_node", "branch_b")
builder.add_edge("start_node", "branch_c")
# 汇聚执行(Fan-in)
builder.add_edge("branch_a", "merge_node")
builder.add_edge("branch_b", "merge_node")
builder.add_edge("branch_c", "merge_node")
实战:构建智能客服工作流
让我们基于 LangChain 智能客服示例,用 LangGraph 重构一个更可控的版本:
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.prebuilt import ToolNode, tools_condition
from typing import TypedDict, Annotated
from langgraph.graph.message import add_messages
from langchain.tools import tool
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage, SystemMessage
from langgraph.checkpoint.sqlite import SqliteSaver
from langgraph.types import interrupt, Command
import os
from dotenv import load_dotenv
# 加载环境变量
load_dotenv()
# ========== 1. 定义状态 ==========
class CustomerServiceState(TypedDict):
"""客服工作流状态"""
messages: Annotated[list, add_messages] # 对话历史
user_id: str # 用户ID
retry_count: int # 重试次数
need_human: bool # 是否需要人工介入
# ========== 2. 定义工具 ==========
@tool
def search_kb(query: str) -> str:
"""搜索知识库获取官方信息"""
# 模拟知识库查询
knowledge = {
"VIP价格": "VIP 会员费用为 ¥99/月,¥799/年",
"退款政策": "购买 7 天内且在 3 节课以内可全额退款",
"工作时间": "周一至周五 9:00-18:00,周末 10:00-16:00"
}
return knowledge.get(query, "未找到相关信息")
@tool
def query_order(order_id: str) -> str:
"""查询订单状态"""
orders_db = {
"ORD-2024-001": {"item": "VIP 年费会员", "status": "已完成"},
"ORD-2024-002": {"item": "Python 实战课程", "status": "配送中"},
}
order = orders_db.get(order_id.upper())
return f"订单 {order_id}: {order['item']} | 状态: {order['status']}" if order else "订单不存在"
@tool
def transfer_to_human(reason: str) -> str:
"""转接人工客服"""
approval = interrupt({
"action": "transfer_to_human",
"reason": reason,
"message": f"用户请求转接人工客服,原因:{reason}"
})
if approval.get("confirmed"):
return f"已转接人工,工单号:TK-{approval.get('ticket_id', 'N/A')}"
return "转接已取消"
# ========== 3. 定义节点函数 ==========
def welcome_node(state: CustomerServiceState) -> dict:
"""欢迎节点:初始化对话"""
welcome_msg = "您好!我是菜鸟教程客服小菜,请问有什么可以帮您?"
return {
"messages": [{"role": "assistant", "content": welcome_msg}],
"retry_count": 0,
"need_human": False
}
def llm_agent_node(state: CustomerServiceState) -> dict:
"""LLM 代理节点:分析用户意图并决定行动"""
llm = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
system_prompt = """你是菜鸟教程 RUNOOB 的智能客服"小菜"。
职责:
1. 热情接待用户,用"您"称呼
2. 关于平台信息、课程内容、政策等问题,使用 search_kb 工具
3. 关于订单查询,使用 query_order 工具
4. 遇到无法解决的问题,使用 transfer_to_human 工具
5. 回答简洁,每次 2-3 句话"""
messages = [SystemMessage(content=system_prompt)] + state["messages"]
response = llm.invoke(messages)
return {"messages": [response]}
def tool_executor_node(state: CustomerServiceState) -> dict:
"""工具执行节点:执行 LLM 调用的工具"""
last_message = state["messages"][-1]
tool_calls = getattr(last_message, "tool_calls", [])
results = []
for tool_call in tool_calls:
tool_name = tool_call["name"]
tool_args = tool_call["args"]
# 执行对应工具
if tool_name == "search_kb":
result = search_kb.invoke(tool_args["query"])
elif tool_name == "query_order":
result = query_order.invoke(tool_args["order_id"])
elif tool_name == "transfer_to_human":
result = transfer_to_human.invoke(tool_args["reason"])
if "已转接人工" in result:
return {"need_human": True, "messages": [{"role": "assistant", "content": result}]}
else:
result = f"未知工具: {tool_name}"
results.append(f"工具 {tool_name} 执行结果: {result}")
return {"messages": [{"role": "tool", "content": "\n".join(results)}]}
def human_intervention_node(state: CustomerServiceState) -> dict:
"""人工介入节点:等待人工客服处理"""
return {
"messages": [{"role": "assistant", "content": "人工客服已接入,请稍候..."}],
"need_human": True
}
def check_retry_node(state: CustomerServiceState) -> dict:
"""检查重试节点:防止无限循环"""
if state["retry_count"] >= 3:
return {
"messages": [{"role": "assistant", "content": "抱歉,问题处理失败,已为您转接人工客服。"}],
"need_human": True
}
return {"retry_count": state["retry_count"] + 1}
# ========== 4. 构建图 ==========
def build_customer_service_graph():
"""构建客服工作流图"""
builder = StateGraph(CustomerServiceState)
# 添加节点
builder.add_node("welcome", welcome_node)
builder.add_node("llm_agent", llm_agent_node)
builder.add_node("tools", tool_executor_node)
builder.add_node("human_intervention", human_intervention_node)
builder.add_node("check_retry", check_retry_node)
# 设置起始节点
builder.add_edge(START, "welcome")
# 欢迎后进入 LLM 处理
builder.add_edge("welcome", "llm_agent")
# LLM 后根据输出决定下一步
def route_after_llm(state: CustomerServiceState) -> str:
last_message = state["messages"][-1]
# 检查是否需要人工介入
if state.get("need_human", False):
return "human_intervention"
# 检查是否有工具调用
if hasattr(last_message, "tool_calls") and last_message.tool_calls:
return "tools"
# 检查是否应该结束
content = str(getattr(last_message, "content", "")).lower()
if any(word in content for word in ["再见", "谢谢", "结束", "拜拜"]):
return END
# 默认继续对话
return "check_retry"
builder.add_conditional_edges(
"llm_agent",
route_after_llm,
{
"tools": "tools",
"human_intervention": "human_intervention",
"check_retry": "check_retry",
END: END
}
)
# 工具执行后返回 LLM
builder.add_edge("tools", "llm_agent")
# 检查重试后返回 LLM
builder.add_edge("check_retry", "llm_agent")
# 人工介入后结束
builder.add_edge("human_intervention", END)
# 添加检查点(持久化状态)
checkpointer = SqliteSaver.from_conn_string("customer_service.db")
return builder.compile(checkpointer=checkpointer)
# ========== 5. 运行工作流 ==========
if __name__ == "__main__":
# 构建图
graph = build_customer_service_graph()
# 配置运行参数
config = {"configurable": {"thread_id": "user_123"}}
# 测试对话
print("=== 测试 1:知识库查询 ===")
result1 = graph.invoke(
{"messages": [HumanMessage(content="VIP 会员多少钱?")], "user_id": "user_123"},
config=config
)
print(f"客服回复: {result1['messages'][-1].content}")
print("\n=== 测试 2:订单查询 ===")
result2 = graph.invoke(
{"messages": [HumanMessage(content="我的订单 ORD-2024-001 状态是什么?")], "user_id": "user_123"},
config=config
)
print(f"客服回复: {result2['messages'][-1].content}")
print("\n=== 测试 3:查看对话历史 ===")
# 获取保存的状态
state = graph.get_state(config)
print(f"对话轮次: {len(state.values['messages']) // 2}")
print(f"重试次数: {state.values.get('retry_count', 0)}")
LangGraph 与 LangChain 的对比
| 特性 | 传统 LangChain Chain | LangGraph |
|---|---|---|
| 工作流结构 | 线性,单向执行 | 图结构,支持循环、分支、并行 |
| 状态管理 | 需手动传递 | 内置状态持久化和共享 |
| 条件路由 | 实现复杂 | 原生支持,语法简洁 |
| 人机协作 | 需要额外开发 | 内置 interrupt 机制 |
| 多 Agent 协调 | 实现困难 | 一流支持,易于扩展 |
| 调试工具 | 有限 | LangGraph Studio 可视化调试 |
| 执行可视化 | 无 | 实时图形化展示执行过程 |
LangGraph Studio:可视化调试利器
LangGraph Studio 是官方提供的可视化开发环境,大幅提升开发和调试效率:
安装与配置
# 安装 LangGraph CLI
pip install langgraph-cli -i https://mirrors.aliyun.com/pypi/simple/
# 创建配置文件 langgraph.json
{
"dependencies": ["."],
"graphs": {
"customer_service": "./customer_service.py:graph"
},
"env": ".env"
}
# 启动开发服务器
langgraph dev
访问 http://localhost:8123 即可使用 LangGraph Studio。
主要功能
- 实时可视化:图形化展示节点执行过程
- 状态检查:在任意节点暂停查看当前 State
- 时间旅行:回放历史执行步骤
- 热重载:修改代码后自动更新图结构
高级特性与最佳实践
1. 使用内置 ToolNode 简化工具调用
from langgraph.prebuilt import ToolNode
# 创建工具列表
tools = [search_kb, query_order, transfer_to_human]
# 使用内置 ToolNode
builder.add_node("tools", ToolNode(tools))
# 使用内置条件路由
from langgraph.prebuilt import tools_condition
builder.ad
314



被折叠的 条评论
为什么被折叠?



