LangGraph 学习教程
目录
- [为什么需要 LangGraph](#为什么需要 LangGraph "#1-%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81-langgraph")
- 核心概念
- 基础语法与示例
- 高级特性
- 设计模式与架构
- 最佳实践
- 优秀开源项目
- 完整案例:智能客服系统
1. 为什么需要 LangGraph
1.1 传统 LLM 应用的局限性
| 问题 | 说明 |
|---|---|
| 线性流程限制 | 简单的 prompt-response 模式无法处理复杂任务 |
| 无状态管理 | 无法跨步骤保持上下文和状态 |
| 无循环/重试 | 处理失败时无法自动重试或循环改进 |
| 无多代理协作 | 不同专长的 AI 无法协同工作 |
1.2 LangGraph 的优势
ini
┌─────────────────────────────────────────────────────────────┐
│ LangGraph = 有状态 + 图结构 + 循环 + 多代理 + 持久化 │
└─────────────────────────────────────────────────────────────┘
1.3 应用场景
| 场景 | 说明 |
|---|---|
| 智能客服 | 需要路由、工具调用、多轮对话 |
| 代码助手 | 需要规划、编码、测试、修复循环 |
| 研究助手 | 需要搜索、分析、总结多步流程 |
| 多代理协作 | 不同专长的 AI 协作完成复杂任务 |
2. 核心概念
2.1 三大核心组件
scss
┌─────────────────────────────────────────────────────────────┐
│ State (状态) ← 共享数据结构,贯穿整个流程 │
│ ↑ │
│ ↓ │
│ Nodes (节点) ← 执行具体工作的函数(可以是LLM、工具等) │
│ ↑ │
│ ↓ │
│ Edges (边) ← 定义流程走向(固定边或条件边) │
└─────────────────────────────────────────────────────────────┘
2.2 State(状态)详解
2.2.1 状态定义方式
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| TypedDict | 简单、灵活 | 类型检查弱 | 快速原型 |
| Pydantic | 强类型、验证 | 稍复杂 | 生产环境 |
| MessagesState | 内置消息管理 | 只适合对话 | 聊天应用 |
示例:TypedDict 状态
python
from typing import TypedDict, Annotated, Any
from langgraph.graph.message import add_messages
class AgentState(TypedDict):
messages: Annotated[list, add_messages] # 使用累加器
current_step: str
result: dict[str, Any]
error_count: int
max_steps: int
示例:Pydantic 状态
python
from pydantic import BaseModel, Field
from typing import Optional, List, Dict, Any
from langchain_core.messages import BaseMessage
class AgentState(BaseModel):
messages: List[BaseMessage] = Field(default_factory=list)
current_step: Optional[str] = None
result: Optional[Dict[str, Any]] = None
error_count: int = 0
max_steps: int = 10
user_id: Optional[str] = None
2.2.2 Reducer(累加器)
yaml
┌─────────────────────────────────────────────────────────────┐
│ Reducer: 决定新状态如何与旧状态合并 │
│ │
│ - add_messages: 消息列表自动追加 │
│ - add_values: 数值自动累加 │
│ - 自定义: 完全控制合并逻辑 │
└─────────────────────────────────────────────────────────────┘
2.3 Nodes(节点)详解
2.3.1 节点类型
| 节点类型 | 说明 | 应用场景 |
|---|---|---|
| LLM 节点 | 调用大模型 | 推理、决策 |
| 工具节点 | 执行外部工具 | 搜索、计算 |
| 路由节点 | 只做分类决策 | 任务分发 |
| 聚合节点 | 收集多个节点结果 | 结果汇总 |
示例:LLM 节点
python
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="doubao-seed-1-6-251015")
def llm_node(state: AgentState) -> dict:
"""调用 LLM 并返回更新"""
messages = state["messages"]
response = llm.invoke(messages)
return {"messages": [response]}
示例:工具调用节点
python
from langchain.agents import Tool
from langchain_experimental.utilities import PythonREPL
python_repl = PythonREPL()
tools = [
Tool(
name="PythonREPL",
func=python_repl.run,
description="用于执行 Python 代码"
)
]
def tool_node(state: AgentState) -> dict:
"""执行工具并返回结果"""
last_message = state["messages"][-1]
tool_name = last_message.additional_kwargs.get("tool_calls", [])[0].get("name")
result = execute_tool(tool_name, last_message.content)
return {"messages": [result]}
2.4 Edges(边)详解
2.4.1 边的类型
sql
┌─────────────────────────────────────────────────────────────┐
│ 类型 1: 固定边 (Normal Edge) │
│ START → node_a → node_b → END │
│ │
│ 类型 2: 条件边 (Conditional Edge) │
│ START → router? → (success_node | error_node) → END │
│ │
│ 类型 3: 循环边 (Cycle) │
│ START → node → (loop? | end?) → END │
└─────────────────────────────────────────────────────────────┘
示例:固定边
sql
from langgraph.graph import StateGraph, START, END
graph = StateGraph(AgentState)
graph.add_node("llm", llm_node)
graph.add_edge(START, "llm") # 入口边
graph.add_edge("llm", END) # 出口边
示例:条件边
python
def route_after_llm(state: AgentState) -> str:
"""根据状态决定下一个节点"""
last_message = state["messages"][-1]
# 如果有工具调用,去工具节点
if last_message.additional_kwargs.get("tool_calls"):
return "tools"
# 否则结束
return END
graph.add_node("tools", tool_node)
graph.add_conditional_edges(
"llm",
route_after_llm,
{
"tools": "tools",
END: END
}
)
graph.add_edge("tools", "llm") # 工具执行完回到 LLM
2.5 完整流程图
sql
┌─────────┐
│ START │
└────┬────┘
│
┌────▼────┐
│ LLM │
└────┬────┘
│
┌───────────┴───────────┐
▼ ▼
┌─────────────┐ ┌─────────┐
│ Tools │ │ END │
└─────┬─────┘ └─────────┘
│
└──────────┬──────────┘
│
┌────────▼────────┐
│ 回到 LLM (循环) │
└─────────────────┘
3. 基础语法与示例
3.1 最简单的 LangGraph(Hello World)
python
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
# 1. 定义状态
class HelloState(TypedDict):
name: str
greeting: str
# 2. 定义节点
def say_hello(state: HelloState) -> dict:
name = state["name"]
greeting = f"你好, {name}! 欢迎学习 LangGraph!"
return {"greeting": greeting}
# 3. 构建图
graph = StateGraph(HelloState)
graph.add_node("say_hello", say_hello)
graph.add_edge(START, "say_hello")
graph.add_edge("say_hello", END)
# 4. 编译图
app = graph.compile()
# 5. 运行图
result = app.invoke({"name": "张三"})
print(result["greeting"])
3.2 带循环的例子(反思改进)
scss
┌─────────────────────────────────────────────────────────────┐
│ 用户问:写一首诗 → 写诗句 → 反思评价 → (好?→ 结束 / 不好?→ 重写) │
└─────────────────────────────────────────────────────────────┘
python
from typing import TypedDict, Annotated
from langchain_core.messages import BaseMessage
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
llm = ChatOpenAI(model="doubao-seed-1-6-251015")
class PoemState(TypedDict):
messages: Annotated[list, add_messages]
iterations: int
max_iterations: int
poem: str
def write_poem(state: PoemState) -> dict:
"""写诗句"""
user_msg = state["messages"][0]
prompt = f"{user_msg.content}\n\n请写一首诗:"
response = llm.invoke(prompt)
return {"poem": response.content, "iterations": state["iterations"] + 1}
def reflect(state: PoemState) -> dict:
"""反思评价"""
poem = state["poem"]
prompt = f"请评价这首诗,给出改进建议(用中文):\n\n{poem}"
response = llm.invoke(prompt)
return {"messages": [response]}
def should_continue(state: PoemState) -> str:
"""决定是否继续"""
if state["iterations"] >= state["max_iterations"]:
return END
# 检查反思结果,如果说"满意"或"很好"就结束
reflection = state["messages"][-1].content
if "满意" in reflection or "很好" in reflection:
return END
return "write_poem"
# 构建图
graph = StateGraph(PoemState)
graph.add_node("write_poem", write_poem)
graph.add_node("reflect", reflect)
graph.add_edge(START, "write_poem")
graph.add_edge("write_poem", "reflect")
graph.add_conditional_edges(
"reflect",
should_continue,
{
"write_poem": "write_poem",
END: END
}
)
app = graph.compile()
# 运行
result = app.invoke({
"messages": [{"role": "user", "content": "写一首关于春天的诗"}],
"iterations": 0,
"max_iterations": 3,
"poem": ""
})
print(f"最终结果(迭代 {result['iterations']} 次):")
print(result["poem"])
3.3 多代理协作示例
sql
┌─────────┐
│ START │
└────┬────┘
│
┌──────▼──────┐
│ 路由器 │
└──────┬──────┘
┌──────────────────┼──────────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│ 搜索器 │ │ 计算器 │ │ 聊天器 │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
└──────────────────┼──────────────────┘
│
┌──────▼──────┐
│ 总结器 │
└──────┬──────┘
│
┌──────▼──────┐
│ END │
└─────────────┘
python
from typing import TypedDict, Annotated, List, Dict, Any
from langchain_core.messages import BaseMessage
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
llm = ChatOpenAI(model="doubao-seed-1-6-251015")
class MultiAgentState(TypedDict):
messages: Annotated[list, add_messages]
request: str
task_type: str
search_result: str
calc_result: str
chat_response: str
final_summary: str
def router(state: MultiAgentState) -> str:
"""路由任务到不同代理"""
request = state["request"].lower()
if any(keyword in request for keyword in ["搜索", "查找", "查询"]):
return "search_agent"
elif any(keyword in request for keyword in ["计算", "数学"]):
return "calculator_agent"
else:
return "chat_agent"
def search_agent(state: MultiAgentState) -> dict:
"""搜索代理"""
prompt = f"请搜索以下内容并给出结果:{state['request']}"
response = llm.invoke(prompt)
return {"search_result": response.content, "task_type": "search"}
def calculator_agent(state: MultiAgentState) -> dict:
"""计算代理"""
prompt = f"请解决以下数学问题并给出结果:{state['request']}"
response = llm.invoke(prompt)
return {"calc_result": response.content, "task_type": "calculator"}
def chat_agent(state: MultiAgentState) -> dict:
"""聊天代理"""
prompt = f"{state['request']}"
response = llm.invoke(prompt)
return {"chat_response": response.content, "task_type": "chat"}
def summarizer(state: MultiAgentState) -> dict:
"""总结代理"""
task_type = state["task_type"]
result = ""
if task_type == "search":
result = state["search_result"]
elif task_type == "calculator":
result = state["calc_result"]
else:
result = state["chat_response"]
prompt = f"请总结以下内容:\n\n{result}"
summary = llm.invoke(prompt)
return {"final_summary": summary.content}
# 构建图
graph = StateGraph(MultiAgentState)
graph.add_node("router", router)
graph.add_node("search_agent", search_agent)
graph.add_node("calculator_agent", calculator_agent)
graph.add_node("chat_agent", chat_agent)
graph.add_node("summarizer", summarizer)
# 边
graph.add_edge(START, "router")
graph.add_conditional_edges(
"router",
router,
{
"search_agent": "search_agent",
"calculator_agent": "calculator_agent",
"chat_agent": "chat_agent"
}
)
graph.add_edge("search_agent", "summarizer")
graph.add_edge("calculator_agent", "summarizer")
graph.add_edge("chat_agent", "summarizer")
graph.add_edge("summarizer", END)
app = graph.compile()
# 测试
result = app.invoke({
"messages": [],
"request": "搜索关于人工智能最新发展的信息",
"task_type": "",
"search_result": "",
"calc_result": "",
"chat_response": "",
"final_summary": ""
})
print(result["final_summary"])
4. 高级特性
4.1 持久化与检查点
scss
┌─────────────────────────────────────────────────────────────┐
│ Checkpoint (检查点) = 保存执行状态,可以暂停和恢复 │
│ │
│ 使用场景: │
│ - 长时间运行的任务可以暂停后恢复 │
│ - 出现错误时可以回滚到之前的状态 │
│ - 人类介入时可以保存状态等待干预 │
└─────────────────────────────────────────────────────────────┘
示例:SQLite 检查点
ini
from langgraph.checkpoint.sqlite import SqliteSaver
# 创建检查点保存器
checkpointer = SqliteSaver.from_conn_string("checkpoints.sqlite")
# 编译时使用检查点
app = graph.compile(checkpointer=checkpointer)
# 配置线程 ID(用于区分不同会话)
config = {"configurable": {"thread_id": "thread_123"}}
# 第一次运行
result1 = app.invoke(
{"messages": [("user", "我叫张三")]},
config=config
)
# 第二次运行(会记住之前的状态)
result2 = app.invoke(
{"messages": [("user", "我叫什么名字?")]},
config=config
)
示例:PostgreSQL 检查点(生产环境)
ini
from langgraph.checkpoint.postgres import PostgresSaver
# 生产环境用 PostgreSQL
DB_URI = "postgresql://user:password@localhost:5432/langgraph"
checkpointer = PostgresSaver.from_conn_string(DB_URI)
app = graph.compile(checkpointer=checkpointer)
4.2 人类介入(Human-in-the-Loop)
scss
┌─────────────────────────────────────────────────────────────┐
│ 人类介入模式: │
│ │
│ 1. 中断模式 (Interrupt) - 运行到特定节点时暂停 │
│ 2. 审核模式 (Approval) - 执行前需要人类审核 │
│ 3. 回滚模式 (Rewind) - 可以回滚到之前的状态 │
└─────────────────────────────────────────────────────────────┘
示例:中断模式
ini
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.sqlite import SqliteSaver
checkpointer = SqliteSaver.from_conn_string("checkpoints.sqlite")
app = graph.compile(
checkpointer=checkpointer,
interrupt_before=["critical_node"] # 在 critical_node 前暂停
)
# 第一次运行:会在 critical_node 前暂停
result = app.invoke(initial_state, config={"configurable": {"thread_id": "1"}})
# 查看当前状态
state = app.get_state(config)
# 人类可以修改状态
new_state = state.values.copy()
new_state["user_approval"] = True
# 更新状态后继续运行
result = app.invoke(new_state, config=config)
示例:审核模式
python
def need_human_approval(state: AgentState) -> bool:
"""决定是否需要人类审核"""
last_action = state.get("last_action")
return last_action in ["delete_data", "send_email"]
# 构建图时添加条件边
graph.add_conditional_edges(
"action_node",
need_human_approval,
{
True: "human_approval",
False: END
}
)
4.3 流式输出
python
# 流式执行
for chunk in app.stream(initial_state, config):
# 每个节点产生一个 chunk
for node_name, node_output in chunk.items():
print(f"节点 {node_name} 输出:")
if "messages" in node_output:
for msg in node_output["messages"]:
print(f" - {msg.content}")
4.4 发送 API(Send API)- Map-Reduce
rust
┌─────────────────────────────────────────────────────────────┐
│ Send API: 向多个节点并行发送消息,然后聚合结果 │
│ │
│ 应用:并行搜索多个来源,然后合并结果 │
└─────────────────────────────────────────────────────────────┘
python
from typing import List
from langgraph.constants import Send
def fan_out(state: MapReduceState) -> List[Send]:
"""分发任务到多个并行节点"""
queries = state["queries"]
return [Send("search_node", {"query": q}) for q in queries]
def search_node(state: dict) -> dict:
"""单个搜索节点"""
return {"result": search(state["query"])}
def aggregate(state: MapReduceState) -> dict:
"""聚合所有搜索结果"""
results = [s["result"] for s in state["search_results"]]
return {"final_result": combine_results(results)}
# 构建图
graph = StateGraph(MapReduceState)
graph.add_node("fan_out", fan_out)
graph.add_node("search_node", search_node)
graph.add_node("aggregate", aggregate)
graph.add_edge(START, "fan_out")
graph.add_edge("search_node", "aggregate")
graph.add_edge("aggregate", END)
4.5 命令 API(Command API)
yaml
┌─────────────────────────────────────────────────────────────┐
│ Command API: 同时返回状态更新和下一个节点跳转命令 │
│ │
│ 应用:动态路由到任意节点 │
└─────────────────────────────────────────────────────────────┘
python
from langgraph.types import Command
def smart_router(state: AgentState) -> Command:
"""智能路由,可以跳转到任意节点"""
intent = analyze_intent(state["messages"][-1].content)
return Command(
goto=intent["next_node"], # 跳转到哪里
update={ # 状态更新
"intent": intent,
"last_step": "routed"
}
)
5. 设计模式与架构
5.1 ReAct 模式(Reasoning and Acting)
ini
┌─────────────────────────────────────────────────────────────┐
│ ReAct = 思考 → 行动 → 观察 → 思考 → 行动 → ... │
└─────────────────────────────────────────────────────────────┘
swift
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个能使用工具的智能助手。"),
MessagesPlaceholder(variable_name="messages"),
("system", "请按以下格式回复:\n"
"1. 先思考该做什么\n"
"2. 再决定使用什么工具\n"
"3. 最后执行并总结")
])
5.2 Plan-and-Execute 模式
yaml
┌─────────────────────────────────────────────────────────────┐
│ Plan-and-Execute = 规划 → 执行 → 执行 → 执行 → 总结 │
│ │
│ 步骤 1: 规划器 - 创建详细步骤计划 │
│ 步骤 2: 执行器 - 按计划执行每一步 │
│ 步骤 3: 总结器 - 汇总结果 │
└─────────────────────────────────────────────────────────────┘
python
def planner(state: PlanState) -> dict:
"""规划节点:创建执行计划"""
user_request = state["request"]
plan = create_detailed_plan(user_request)
return {"plan": plan, "current_step": 0}
def executor(state: PlanState) -> dict:
"""执行节点:执行计划中的当前步骤"""
plan = state["plan"]
step_idx = state["current_step"]
result = execute_plan_step(plan, step_idx)
return {
"step_results": [*state["step_results"], result],
"current_step": step_idx + 1
}
def should_continue(state: PlanState) -> str:
"""检查是否还有步骤要执行"""
if state["current_step"] >= len(state["plan"]):
return "summarizer"
return "executor"
5.3 反射模式(Reflection)
ini
┌─────────────────────────────────────────────────────────────┐
│ Reflection = 执行 → 反思 → 改进 → 执行 → ... │
└─────────────────────────────────────────────────────────────┘
5.4 Tree of Thoughts(思维树)
markdown
┌───────┐
│ Idea1 │
└───┬───┘
│
┌─────────┼─────────┐
│ │ │
┌────▼───┐ ┌──▼────┐ ┌──▼────┐
│ Idea1a │ │ Idea1b│ │ Idea1c│
└────┬───┘ └───┬───┘ └───┬───┘
│ │ │
┌────▼───┐ ┌──▼────┐ ┌──▼────┐
│ Score1 │ │ Score2│ │ Score3│
└────────┘ └───────┘ └───────┘
5.5 Supervisor 模式(监督者模式)
scss
┌───────────────┐
│ Supervisor │
│ (监督者) │
└───────┬───────┘
┌─────────────┼─────────────┐
│ │ │
┌───────▼─────┐ ┌───▼────┐ ┌──────▼───────┐
│ Researcher │ │ Writer │ │ Critic │
│ (研究员) │ │ (作家) │ │ (批评家) │
└───────┬─────┘ └───┬────┘ └──────┬───────┘
│ │ │
└─────────────┴─────────────┘
│
┌───────▼───────┐
│ Result │
└───────────────┘
示例:Supervisor 代理
python
from typing import Literal
members = ["Researcher", "Writer", "Critic"]
def supervisor_node(state: SupervisorState) -> dict:
"""监督者节点:协调多个专业代理"""
prompt = f"""你是一个项目监督者,负责协调以下团队成员完成任务:
- Researcher: 负责研究和收集信息
- Writer: 负责写作和创作
- Critic: 负责批评和改进
当前任务:{state['task']}
聊天历史:{state['messages']}
接下来应该让谁工作?请选择:
{members} 或 FINISH(如果任务完成)"""
response = llm.invoke(prompt)
next_agent = parse_response(response)
return {
"next": next_agent,
"messages": [response]
}
def route_supervisor(state: SupervisorState) -> Literal["Researcher", "Writer", "Critic", END]:
"""监督者路由"""
next_agent = state["next"]
if next_agent == "FINISH":
return END
return next_agent
# 构建图
graph = StateGraph(SupervisorState)
graph.add_node("Supervisor", supervisor_node)
graph.add_node("Researcher", researcher_node)
graph.add_node("Writer", writer_node)
graph.add_node("Critic", critic_node)
graph.add_edge(START, "Supervisor")
graph.add_edge("Researcher", "Supervisor")
graph.add_edge("Writer", "Supervisor")
graph.add_edge("Critic", "Supervisor")
graph.add_conditional_edges("Supervisor", route_supervisor)
6. 最佳实践
6.1 状态设计最佳实践
| 实践 | 说明 |
|---|---|
| ✅ 保持状态简单 | 只放必要的信息,不要把临时变量塞进状态 |
| ✅ 使用类型定义 | Pydantic 或 TypedDict,保持代码清晰 |
| ✅ 只返回更新 | 节点函数只返回改变的键,不返回整个状态 |
| ✅ 合理使用累加器 | add_messages 等,避免手动管理列表 |
| ❌ 状态过大 | 避免把大对象塞进状态,用引用或外部存储 |
6.2 节点设计最佳实践
| 实践 | 说明 |
|---|---|
| ✅ 单一职责 | 每个节点只做一件事,保持简单 |
| ✅ 纯函数风格 | 不改变输入,只返回更新 |
| ✅ 错误处理 | 每个节点应该处理错误,把错误信息放进状态 |
| ✅ 可测试 | 独立测试每个节点函数 |
示例:带错误处理的节点
python
def safe_node(state: AgentState) -> dict:
"""安全的节点函数,处理错误"""
try:
result = do_something(state)
return {"result": result, "error_count": 0}
except Exception as e:
return {
"last_error": {
"type": type(e).__name__,
"message": str(e),
"timestamp": datetime.now().isoformat()
},
"error_count": state.get("error_count", 0) + 1
}
6.3 边设计最佳实践
| 实践 | 说明 |
|---|---|
| ✅ 优先简单边 | 尽量用固定边,条件边只在必要时用 |
| ✅ 明确的路由函数 | 路由函数应该简单、清晰、易读 |
| ✅ 循环要有边界 | 用 max_steps 或类似机制防止无限循环 |
| ❌ 过度复杂的路由 | 避免一个路由函数处理太多逻辑 |
6.4 生产环境最佳实践
6.4.1 使用 Postgres 检查点
ini
from langgraph.checkpoint.postgres import PostgresSaver
# 生产环境必须用 Postgres
DB_URI = "postgresql://user:password@localhost:5432/langgraph"
checkpointer = PostgresSaver.from_conn_string(DB_URI)
app = graph.compile(checkpointer=checkpointer)
6.4.2 监控与日志
python
import logging
from langsmith import traceable
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@traceable
def monitored_node(state: AgentState) -> dict:
"""带监控的节点"""
logger.info(f"开始执行节点,状态: {state}")
result = do_something(state)
logger.info(f"节点执行完成,结果: {result}")
return result
6.4.3 错误边界与重试
python
def safe_execute(func, state, max_retries=3):
"""带重试的安全执行"""
for i in range(max_retries):
try:
return func(state)
except Exception as e:
if i == max_retries - 1:
raise
time.sleep(2 ** i) # 指数退避
6.4.4 完整生产配置示例
ini
from langgraph.checkpoint.postgres import PostgresSaver
from langchain_core.globals import set_debug
import logging
import sys
# 1. 日志配置
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(sys.stdout),
logging.FileHandler('langgraph.log')
]
)
# 2. 调试模式(开发环境)
set_debug(True)
# 3. 检查点(生产环境)
DB_URI = "postgresql://user:password@localhost:5432/langgraph"
checkpointer = PostgresSaver.from_conn_string(DB_URI)
# 4. 编译图
app = graph.compile(
checkpointer=checkpointer,
interrupt_before=["critical_step"], # 关键步骤需要人工审核
)
# 5. 配置
config = {
"configurable": {
"thread_id": "production_thread",
"user_id": "user_123",
},
"recursion_limit": 50 # 提高递归限制
}
7. 优秀开源项目
7.1 langgraph-complete-guide
特点:
- 包含 ReAct、Plan-and-Execute、Reflection 等模式的完整实现
- 多代理协作示例
- 研究人员视角的深入讲解
学习点:
ini
# 仓库中的 ReAct 模式实现
from langgraph_examples import ReActAgent
agent = ReActAgent(
llm=llm,
tools=tools,
max_iterations=10
)
result = agent.invoke({"input": "What's 123 * 456?"})
7.2 storyteller
特点:
- 用 LangGraph 构建的故事创作工具
- 体现了状态管理和复杂流程控制
- 优秀的重构经验分享
学习点:
bash
# 仓库中的最佳实践:
# 1. 避免自定义路由,用 LangGraph 原生条件边
# 2. 保持状态与 LangGraph 系统一致
# 3. 节点只做一件事,职责单一
7.3 langgraphgo
特点:
- LangGraph 的 Go 语言实现
- 包含完整的预构建代理(ReAct、Supervisor、Planning 等)
- 优秀的内存管理策略
学习点:
bash
# Go 版本的预构建代理(概念类似)
agent, _ := prebuilt.CreateSupervisor(
llm,
{
"analyst": analystAgent,
"coder": coderAgent,
"reviewer": reviewerAgent
},
"router"
)
7.4 LangChain 官方示例
特点:
- 官方维护的最权威示例
- 涵盖所有特性的完整展示
- 持续更新
8. 完整案例:智能客服系统
8.1 系统架构
sql
┌─────────┐
│ START │
└────┬────┘
│
┌──────▼──────┐
│ 意图识别 │
└──────┬──────┘
┌──────────────────┼──────────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│ 技术支持 │ │ 订单查询 │ │ 闲聊 │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
└──────────────────┼──────────────────┘
│
┌──────▼──────┐
│ 满意度调查 │
└──────┬──────┘
│
┌──────▼──────┐
│ END │
└─────────────┘
8.2 完整代码
python
"""
智能客服系统 - LangGraph 完整案例
"""
import os
from typing import TypedDict, Annotated, Optional, List, Dict, Any
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.checkpoint.sqlite import SqliteSaver
from dotenv import load_dotenv
import json
# 加载环境变量
load_dotenv()
# 初始化 LLM
llm = ChatOpenAI(
model="doubao-seed-1-6-251015",
temperature=0,
api_key=os.getenv("ARK_API_KEY"),
base_url="https://ark.cn-beijing.volces.com/api/v3"
)
# ==================== 1. 状态定义 ====================
class CustomerServiceState(TypedDict):
messages: Annotated[list, add_messages]
user_id: Optional[str]
intent: Optional[str]
order_id: Optional[str]
technical_issue: Optional[str]
support_result: Optional[str]
satisfaction: Optional[int]
conversation_ended: bool
step_count: int
max_steps: int
# ==================== 2. 节点定义 ====================
def intent_recognition(state: CustomerServiceState) -> dict:
"""意图识别节点"""
user_msg = state["messages"][-1].content
prompt = f"""请分析用户的意图,从以下选项中选择一个:
- technical_support: 技术问题
- order_query: 订单查询
- chat: 闲聊
- satisfaction: 满意度评价
- end_conversation: 结束对话
用户输入: {user_msg}
只返回意图名称,不要其他内容。"""
response = llm.invoke(prompt)
intent = response.content.strip()
return {
"intent": intent,
"step_count": state["step_count"] + 1
}
def technical_support(state: CustomerServiceState) -> dict:
"""技术支持节点"""
user_msg = state["messages"][-1].content
prompt = f"""你是一个技术支持专家,请帮助用户解决技术问题。
用户问题: {user_msg}
请给出清晰的解决方案。"""
response = llm.invoke(prompt)
return {
"support_result": response.content,
"messages": [response],
"technical_issue": user_msg,
"step_count": state["step_count"] + 1
}
def order_query(state: CustomerServiceState) -> dict:
"""订单查询节点"""
user_msg = state["messages"][-1].content
# 模拟从数据库查询订单
prompt = f"""你是一个订单查询助手。
用户查询: {user_msg}
请给出礼貌的回复。(注:实际应用中这里会调用数据库)"""
response = llm.invoke(prompt)
return {
"support_result": response.content,
"messages": [response],
"step_count": state["step_count"] + 1
}
def chat(state: CustomerServiceState) -> dict:
"""闲聊节点"""
user_msg = state["messages"][-1].content
response = llm.invoke(user_msg)
return {
"support_result": response.content,
"messages": [response],
"step_count": state["step_count"] + 1
}
def satisfaction_survey(state: CustomerServiceState) -> dict:
"""满意度调查节点"""
if state.get("satisfaction") is not None:
# 已经评价过了
return {"conversation_ended": True}
survey_prompt = AIMessage(
content="对我们的服务满意吗?请用 1-5 分评价(5 分最满意)"
)
return {
"messages": [survey_prompt],
"step_count": state["step_count"] + 1
}
def record_satisfaction(state: CustomerServiceState) -> dict:
"""记录满意度节点"""
last_msg = state["messages"][-1].content
# 提取评分
score = None
import re
match = re.search(r'[1-5]', last_msg)
if match:
score = int(match.group())
thank_you = AIMessage(
content=f"感谢您的{'满意' if score and score >=4 else '宝贵'}评价!欢迎下次光临!"
)
return {
"satisfaction": score,
"messages": [thank_you],
"conversation_ended": True,
"step_count": state["step_count"] + 1
}
# ==================== 3. 路由函数 ====================
def route_after_intent(state: CustomerServiceState) -> str:
"""根据意图路由"""
intent = state["intent"]
if intent == "technical_support":
return "technical_support"
elif intent == "order_query":
return "order_query"
elif intent == "chat":
return "chat"
elif intent == "satisfaction":
return "record_satisfaction"
elif intent == "end_conversation":
return "satisfaction_survey"
else:
return "chat"
def route_after_service(state: CustomerServiceState) -> str:
"""服务后路由"""
if state["step_count"] >= state["max_steps"]:
return "satisfaction_survey"
return END # 等待用户下一个问题
def should_continue_survey(state: CustomerServiceState) -> str:
"""调查后续路由"""
if state["conversation_ended"]:
return END
return "record_satisfaction"
# ==================== 4. 构建图 ====================
graph = StateGraph(CustomerServiceState)
# 添加节点
graph.add_node("intent_recognition", intent_recognition)
graph.add_node("technical_support", technical_support)
graph.add_node("order_query", order_query)
graph.add_node("chat", chat)
graph.add_node("satisfaction_survey", satisfaction_survey)
graph.add_node("record_satisfaction", record_satisfaction)
# 添加边
graph.add_edge(START, "intent_recognition")
# 条件边:意图识别 → 各服务节点
graph.add_conditional_edges(
"intent_recognition",
route_after_intent,
{
"technical_support": "technical_support",
"order_query": "order_query",
"chat": "chat",
"satisfaction_survey": "satisfaction_survey",
"record_satisfaction": "record_satisfaction"
}
)
# 服务节点 → 满意度调查 或 结束
graph.add_conditional_edges(
"technical_support",
route_after_service,
{
"satisfaction_survey": "satisfaction_survey",
END: END
}
)
graph.add_conditional_edges(
"order_query",
route_after_service,
{
"satisfaction_survey": "satisfaction_survey",
END: END
}
)
graph.add_conditional_edges(
"chat",
route_after_service,
{
"satisfaction_survey": "satisfaction_survey",
END: END
}
)
# 满意度调查流程
graph.add_conditional_edges(
"satisfaction_survey",
should_continue_survey,
{
"record_satisfaction": "record_satisfaction",
END: END
}
)
graph.add_edge("record_satisfaction", END)
# ==================== 5. 编译图 ====================
# 使用 SQLite 检查点
checkpointer = SqliteSaver.from_conn_string("customer_service.sqlite")
app = graph.compile(checkpointer=checkpointer)
# ==================== 6. 使用示例 ====================
if __name__ == "__main__":
print("=== 智能客服系统 ===")
print("输入 'quit' 退出\n")
# 用户会话配置
user_id = "user_001"
config = {
"configurable": {
"thread_id": user_id
}
}
while True:
user_input = input("用户: ")
if user_input.lower() in ['quit', '退出', 'q']:
print("客服: 再见!")
break
# 调用 LangGraph
result = app.invoke({
"messages": [HumanMessage(content=user_input)],
"user_id": user_id,
"step_count": 0,
"max_steps": 10,
"conversation_ended": False
}, config=config)
# 显示 AI 回复
last_message = result["messages"][-1]
print(f"客服: {last_message.content}")
# 检查是否结束
if result["conversation_ended"]:
print(f"\n对话结束。满意度: {result.get('satisfaction', '未评价')}")
break
8.3 系统特性
| 特性 | 说明 |
|---|---|
| ✅ 意图识别 | 自动识别用户意图并路由 |
| ✅ 多轮对话 | 支持长时间会话 |
| ✅ 持久化 | 用 SQLite 保存对话状态 |
| ✅ 满意度调查 | 对话结束后收集用户反馈 |
| ✅ 可扩展 | 轻松添加新的服务类型 |
总结
LangGraph 学习路径
第 1 天: 基础概念 → Hello World 示例
第 2-3 天: 节点和边 → 简单对话系统
第 4-5 天: 循环和条件 → 反思改进、ReAct
第 6-7 天: 高级特性 → 持久化、人类介入
第 2 周: 设计模式 → 多代理、Supervisor
第 3 周: 最佳实践 → 生产环境配置
第 4 周: 实际项目 → 构建自己的应用
核心要点回顾
- State(状态) : 整个系统的共享数据结构,贯穿所有节点
- Nodes(节点) : 执行实际工作,返回状态更新
- Edges(边) : 定义流程走向,支持条件分支和循环
- Checkpoint(检查点) : 持久化状态,支持暂停/恢复
- Design Patterns(设计模式) : ReAct、Plan-and-Execute、Supervisor 等
祝你学习愉快! 🚀
参考资源
- 官方文档 : langchain-ai.github.io/langgraph/
- 官方示例 : github.com/langchain-a...
- 最佳实践博客 : www.swarnendu.de/blog/langgr...
- 完整指南仓库 : github.com/mkassaf/lan...`