用 LangGraph 构建智能客服系统:我的状态图编程学习笔记
💡 这是一篇学习经验分享,记录我在 smart-customer-service 项目中学习 LangGraph、Memory、MCP、多 Agent 协作的过程和收获。
前言
在掌握了 LangChain 基础组件和 RAG 之后,我开始探索更复杂的场景:如何让 AI 处理多步骤、有分支、需要记忆的复杂任务?
答案是 LangGraph——LangChain 团队推出的状态图编排框架。通过 smart-customer-service 项目,我系统学习了 LangGraph 的核心概念,并延伸学习了 MCP 和多 Agent 协作。
这篇文章重点分享我的学习路径和知识总结。
一、从 Chain 到 Agent 到 LangGraph
1.1 三种范式的演进
┌─────────────────────────────────────────────────────────┐
│ 三种编程范式对比 │
├─────────────────────────────────────────────────────────┤
│ │
│ Chain (链) │
│ ┌───┐ ┌───┐ ┌───┐ ┌───┐ │
│ │ A │ → │ B │ → │ C │ → │ D │ 固定流程 │
│ └───┘ └───┘ └───┘ └───┘ │
│ │
│ Agent (代理) │
│ ┌───┐ │
│ ┌──→ │ A │ ──┐ │
│ ┌───┐ └───┘ │ ┌───┐ │
│ │ LLM│ ←─────────┘ → │回答│ 动态决策 │
│ └───┘ ┌───┐ │ │
│ └──→ │ B │ ──┘ │
│ └───┘ │
│ │
│ LangGraph (状态图) │
│ ┌───┐ │
│ ┌──→ │ A │ ──┬──→ ┌───┐ │
│ ┌───┐ └───┘ │ │ C │ ──→ ┌───┐ │
│ │开始│ ←─────────┤ └───┘ │结束│ │
│ └───┘ ┌───┐ │ └───┘ │
│ └──→ │ B │ ──┘ │
│ └───┘ 条件路由 + 循环 + 状态管理 │
│ │
└─────────────────────────────────────────────────────────┘
我的理解:
| 范式 | 特点 | 适用场景 |
|---|---|---|
| Chain | 线性流程,简单直接 | 简单的 RAG、固定流程任务 |
| Agent | 动态决策,自主选择工具 | 需要调用工具的问答任务 |
| LangGraph | 状态图,支持循环/分支/记忆 | 复杂业务流程、多步骤任务 |
1.2 为什么需要 LangGraph?
Agent 的局限:
- 无法处理复杂的条件分支
- 难以实现循环和重试
- 状态管理不方便
- 无法持久化对话状态
LangGraph 的解决方案:
- 用状态图描述工作流
- 用条件边实现分支
- 用循环边实现重试
- 用State统一管理状态
二、LangGraph 核心概念
2.1 四大核心概念
┌─────────────────────────────────────────────────────────┐
│ LangGraph 四大核心概念 │
├─────────────────────────────────────────────────────────┤
│ │
│ 1. State (状态) │
│ └─ 全局数据容器,在节点间传递 │
│ │
│ 2. Node (节点) │
│ └─ 处理单元,执行具体逻辑 │
│ │
│ 3. Edge (边) │
│ └─ 连接节点,定义执行流向 │
│ │
│ 4. Conditional Edge (条件边) │
│ └─ 根据状态决定走向,实现分支 │
│ │
└─────────────────────────────────────────────────────────┘
2.2 State:统一的状态管理
from typing import TypedDict
class State(TypedDict):
message: str # 用户输入
emotion: str # 情感分析结果
need_human: bool # 是否需要转人工
intention: str # 意图分类
info: str # 检索到的信息
result: str # 最终答案
rounds: int # 生成轮次
quality: bool # 质量检查结果
memory: list # 对话历史
学习心得:
- State 是整个图的"共享内存"
- 所有节点通过读写 State 通信
- TypedDict 让状态结构清晰可预测
2.3 Node:处理节点
def analyze_emotion(state: State) -> dict:
"""节点函数:接收 State,返回更新的 State"""
message = state["message"]
# ... 处理逻辑 ...
return {"emotion": emotion, "need_human": need_human}
关键点:
- 节点函数接收完整 State
- 返回一个字典,只包含需要更新的字段
- 框架会自动合并到全局 State
2.4 Edge 和 Conditional Edge
# 普通边:固定连接
graph.add_edge("node_a", "node_b")
# 条件边:根据状态决定
def route_after_emotion(state: State) -> str:
if state["need_human"]:
return "to_human" # 返回的是边的标签
else:
return "to_intent"
graph.add_conditional_edges(
"analyze_emotion", # 源节点
route_after_emotion, # 路由函数
{"to_human": "human_intervention", # 标签 → 目标节点
"to_intent": "intent_recognition"},
)
学习心得:
- 路由函数返回的是边的标签,不是节点名
- 标签和节点名可以不同,增加了灵活性
- 条件边是实现分支逻辑的关键
三、智能客服系统的状态图设计
3.1 工作流程
┌─────────────────┐
│ START │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 情感分析 │
│ analyze_emotion│
└────────┬────────┘
│
┌──────────────┴──────────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ 转人工客服 │ │ 意图识别 │
│ human_ │ │ intent_ │
│ intervention │ │ recognition │
└────────┬────────┘ └────────┬────────┘
│ │
▼ │
┌─────────────────┐ │
│ END │ │
└─────────────────┘ │
│
┌───────────────────────┼───────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 知识库检索 │ │ 计算 │ │ 闲聊 │
│ knowledge_ │ │ calculator │ │ direct_ │
│ retrieval │ │ │ │ answer │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└───────────────────────┼───────────────────────┘
│
▼
┌─────────────────┐
│ 生成答案 │
│ generate_answer│
└────────┬────────┘
│
▼
┌─────────────────┐
│ 质量检查 │
│ check_quality │
└────────┬────────┘
│
┌─────────────┴─────────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ END │ │ 重新生成 │
│ (质量合格) │ │ (质量不合格) │
└─────────────────┘ └─────────────────┘
│
└──→ generate_answer
3.2 关键设计点
| 设计点 | 实现方式 | 作用 |
|---|---|---|
| 情感转人工 | Conditional Edge | 负面情绪直接转人工 |
| 意图路由 | Conditional Edge | 根据意图选择处理路径 |
| 质量重试 | Conditional Edge + 循环 | 质量不合格重新生成 |
| 最大轮次 | 路由函数判断 | 防止无限循环 |
3.3 质量检查的循环设计
def route_after_quality(state: State) -> str:
"""质量检查后的路由"""
if state["quality"] or state["rounds"] >= 3:
return "end" # 质量合格或超过3次,结束
else:
return "retry" # 质量不合格,重试
graph.add_conditional_edges(
"check_quality",
route_after_quality,
{"end": END, "retry": "generate_answer"}, # retry 指向 generate_answer,形成循环
)
关键点:
- 循环边实现了重试机制
rounds计数防止无限循环- 这是 LangGraph 相比普通 Agent 的重要优势
四、Memory:让 AI 记住上下文
4.1 为什么需要 Memory?
没有 Memory:
用户:公司的年假有几天?
助手:工作满1年享有5天年假...
用户:那病假呢? ← AI 不知道"那"指的是什么
助手:请问您想了解什么?
有 Memory:
用户:公司的年假有几天?
助手:工作满1年享有5天年假...
用户:那病假呢? ← AI 知道在问公司的病假制度
助手:病假规定如下...
4.2 Memory 的实现方式
在 smart-customer-service 中,我用了最简单的方式——手动管理对话历史:
# State 中定义 memory
class State(TypedDict):
...
memory: list # 对话历史
# 在节点中使用 memory
def generate_answer(state: State) -> dict:
memory = state["memory"]
messages = [SystemMessage(content="...")]
# 加入最近的对话历史(最多2轮)
recent = memory[-4:] if len(memory) > 4 else memory
for msg in recent:
if msg["role"] == "user":
messages.append(HumanMessage(content=msg["content"]))
else:
messages.append(HumanMessage(content=f"[助手之前说]: {msg['content'][:100]}"))
messages.append(HumanMessage(content=state["message"]))
result = llm.invoke(messages)
return {"result": result.content.strip()}
# 在主程序中更新 memory
chat_history.append({"role": "user", "content": message})
chat_history.append({"role": "assistant", "content": result["result"]})
4.3 Memory 的优化方向
| 方案 | 优点 | 缺点 |
|---|---|---|
| 手动管理 | 简单可控 | 需要自己管理 |
| ConversationBufferMemory | 开箱即用 | 占用 token 多 |
| ConversationSummaryMemory | 节省 token | 可能丢失细节 |
| ConversationEntityMemory | 记住实体 | 实现复杂 |
我的建议:简单场景用手动管理,复杂场景用 LangChain 内置的 Memory 类。
五、MCP:标准化的工具协议
5.1 MCP 是什么?
MCP (Model Context Protocol) 是一个开放协议,用于标准化 AI 应用如何连接外部工具。
核心思想:
- 无论工具用什么语言编写,都通过统一协议暴露给 AI
- 客户端-服务器架构,工具可独立部署
- 支持多种传输方式(stdio、HTTP、WebSocket)
5.2 MCP 的架构
┌─────────────────────────────────────────────────────────┐
│ MCP 架构 │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ AI Agent │ │
│ │ (LangChain / LangGraph) │ │
│ └─────────────────────────────────────────────────┘ │
│ ↑ │
│ │ 调用工具 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ MCP Client │ │
│ │ (MultiServerMCPClient) │ │
│ └─────────────────────────────────────────────────┘ │
│ ↑ │
│ │ MCP 协议 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ MCP Server │ │
│ │ (FastMCP) │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Tool A │ │ Tool B │ │ Tool C │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
5.3 MCP Server 实现
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("我的工具箱")
@mcp.tool()
def calculate(expression: str) -> str:
"""计算数学表达式,例如 2 + 3 * 4"""
result = eval(expression)
return f"{expression} = {result}"
@mcp.tool()
def get_weather(city: str) -> str:
"""查询城市天气,例如 杭州"""
data = {"杭州": "晴,28°C", "北京": "多云,25°C"}
return data.get(city, f"{city}:暂无数据")
if __name__ == "__main__":
mcp.run(transport="stdio")
关键点:
@mcp.tool()装饰器注册工具- 函数名即工具名
- docstring 是工具描述
- 支持 stdio、HTTP 等传输方式
5.4 MCP Client 使用
from langchain_mcp_adapters.client import MultiServerMCPClient
from langgraph.prebuilt import create_react_agent
async def main():
# 创建客户端,连接 MCP 服务器
client = MultiServerMCPClient({
"my_tools": {
"command": "python",
"args": ["my_mcp_server.py"],
"transport": "stdio",
}
})
# 获取工具
tools = await client.get_tools()
print(f"获取了 {len(tools)} 个工具")
# 创建 Agent
agent = create_react_agent(model=llm, tools=tools)
# 使用
result = await agent.ainvoke({"messages": [HumanMessage(content="帮我算 123 + 456")]})
asyncio.run(main())
5.5 MCP vs 直接定义工具
| 对比点 | 直接定义工具 | MCP |
|---|---|---|
| 复杂度 | 简单 | 中等 |
| 复用性 | 低(绑定项目) | 高(独立部署) |
| 跨语言 | 不支持 | 支持 |
| 适用场景 | 个人项目 | 团队协作、生产环境 |
六、多 Agent 协作
6.1 为什么需要多 Agent?
单 Agent 的局限:
- 所有职责集中在一个 Agent
- System Prompt 越来越长
- 难以优化不同场景的表现
多 Agent 的思路:
用户 → 主管 Agent(分配任务)
├→ 技术专家 Agent
├→ HR 专家 Agent
└→ 财务专家 Agent
↓
主管 Agent(整合结果)→ 回答
6.2 多 Agent 实现
from langchain_core.tools import tool
from langgraph.prebuilt import create_react_agent
# 专家 Agent 定义为工具
@tool
def tech_expert(question: str) -> str:
"""当用户问技术、编程相关问题时,调用这个专家。
Args:
question: 技术相关的问题
"""
result = llm.invoke([
{"role": "system", "content": "你是一个资深技术专家。"},
{"role": "user", "content": question},
])
return result.content
@tool
def hr_expert(question: str) -> str:
"""当用户问公司制度、人事相关问题时,调用这个专家。
Args:
question: HR相关的问题
"""
result = llm.invoke([
{"role": "system", "content": "你是一个公司HR专家。"},
{"role": "user", "content": question},
])
return result.content
# 主管 Agent 负责调度
supervisor = create_react_agent(
model=llm,
tools=[tech_expert, hr_expert, finance_expert],
)
# 使用
result = supervisor.invoke({"messages": [{"role": "user", "content": "Python 的装饰器是什么?"]]})
6.3 多 Agent 的设计模式
| 模式 | 描述 | 适用场景 |
|---|---|---|
| 路由模式 | 主管分配给专家 | 任务可明确分类 |
| 流水线模式 | Agent 依次处理 | 任务有先后顺序 |
| 辩论模式 | 多个 Agent 讨论 | 需要多角度分析 |
| 投票模式 | 多个 Agent 投票 | 需要高可靠性的决策 |
七、知识体系总结
通过这个项目,我形成了完整的 LangGraph 知识体系:
┌─────────────────────────────────────────────────────────┐
│ LangGraph + Memory + MCP + Multi-Agent │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 应用层 │ │
│ │ 智能客服 / 智能助手 / 多专家系统 │ │
│ └─────────────────────────────────────────────────┘ │
│ ↑ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 编排层 (LangGraph) │ │
│ │ State / Node / Edge / Conditional Edge │ │
│ └─────────────────────────────────────────────────┘ │
│ ↑ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 能力层 │ │
│ │ Memory (记忆) / MCP (工具协议) / Multi-Agent │ │
│ └─────────────────────────────────────────────────┘ │
│ ↑ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 基础层 │ │
│ │ LLM / RAG / Embedding / VectorDB │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
八、学习路径建议
LangChain 基础
│
├── Prompt Template
├── LLM 调用
├── Chain 链式调用
└── Tools 工具定义
│
▼
Agent 入门
│
├── ReAct 模式
├── create_react_agent
└── 工具调用机制
│
▼
RAG 应用
│
├── 文档加载切分
├── 向量化存储
└── 检索生成
│
▼
LangGraph
│
├── State 状态管理
├── Node 节点定义
├── Edge 边连接
└── Conditional Edge 条件路由
│
▼
进阶主题
│
├── Memory 记忆系统
├── MCP 工具协议
└── Multi-Agent 协作
九、项目地址
- 智能客服系统:github.com/clwf/smart-…
- MCP 学习教程:github.com/clwf/mcp-le…
- LangChain 学习教程:github.com/clwf/langch…