我在开发客服机器人和多轮对话系统时,最头疼的问题就是状态丢失。用户聊到一半换了个话题,回来之后系统完全不认识他了。这种体验糟糕透顶。后来我深入研究了LangGraph的状态管理机制,终于解决了这个痛点。今天我就手把手教大家如何用LangGraph构建一个能够"记住一切"的智能对话系统。
什么是LangGraph状态管理?
想象你在和一个人类客服对话。你说"我想查一下订单",他问"请问订单号是多少",你回答"刚才那个",他立刻就明白了。这就是状态管理的魔力——上下文被保留下来,对话是连续的、有记忆的。
传统的AI对话API每次请求都是独立的,就像问完一个问题就失忆了。而LangGraph通过状态管理,让AI能够记住之前的对话内容、用户信息、正在处理的任务进度等等。
实战项目:构建一个会"记住"你的智能助手
我们今天要构建的对话系统能够:
- 记住用户的名字和偏好
- 支持多轮对话,上下文连贯
- 能够在对话中调用工具(比如查询天气、计算)
- 当对话中断后恢复,还能认出老用户
第一步:安装必要的库
我们先安装LangGraph和相关依赖。我推荐使用HolySheep AI作为后端API,原因后面会说。
pip install langgraph langchain-core langchain-holysheep
如果提示没有langchain-holysheep,用这个替代
pip install langchain-openai # 稍后配置base_url即可
第二步:配置HolySheep API
HolySheep AI的优势非常明显:人民币直付、汇率1:1无损兑换、国内直连延迟小于50ms。对于我们这种需要频繁调试的开发者来说,光是充值不用换汇这点就太省心了。
import os
from langchain_openai import ChatOpenAI
配置HolySheep API
os.environ["OPENAI_API_KEY"] = "YOUR_HOLYSHEEP_API_KEY"
os.environ["OPENAI_API_BASE"] = "https://api.holysheep.ai/v1"
初始化模型
llm = ChatOpenAI(
model="gpt-4o-mini", # 性价比之选,输入$0.15/MTok,输出$0.60/MTok
temperature=0.7
)
print("HolySheep API配置成功!当前模型:", llm.model_name)
第三步:定义状态结构
这是LangGraph最核心的概念。我们需要定义一个"状态字典",用来存放对话过程中所有需要记住的信息。
from typing import TypedDict, Annotated, Sequence
from langgraph.graph import StateGraph, END
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
定义我们对话系统的状态结构
class ConversationState(TypedDict):
"""对话系统的状态定义"""
messages: Sequence[HumanMessage | AIMessage] # 对话历史
user_name: str | None # 用户名字
user_preferences: dict # 用户偏好设置
current_task: str | None # 当前正在处理的任务
task_history: list # 任务完成历史
# 下面这两个用于实现"短时记忆"功能
recent_entities: dict # 最近提到的实体(订单号、产品名等)
context_summary: str # 对话上下文的摘要
创建状态图
workflow = StateGraph(ConversationState)
初始化状态的方法
def initialize_state(state: ConversationState, user_id: str) -> ConversationState:
"""为新用户创建初始状态"""
return {
"messages": [],
"user_name": None,
"user_preferences": {"language": "中文", "notifications": True},
"current_task": None,
"task_history": [],
"recent_entities": {},
"context_summary": f"用户 {user_id} 的新对话"
}
print("状态结构定义完成!这就是我们对话系统的'大脑'。")
第四步:构建核心处理节点
现在我们需要定义几个处理节点。每个节点负责特定的任务,状态在节点之间流转。这就是LangGraph的核心思想——用图的结构管理对话流程。
# 节点1:理解用户意图
def understand_intent(state: ConversationState) -> ConversationState:
"""分析用户消息,判断意图"""
messages = state["messages"]
last_message = messages[-1].content
# 使用AI分析意图
intent_prompt = f"""分析用户消息的意图:
当前消息:{last_message}
可选意图:
- introduce: 用户想介绍自己(告诉名字)
- query: 用户想查询信息
- task: 用户想完成任务
- greeting: 用户在打招呼
- unclear: 无法判断
只返回意图类型,不要其他内容。"""
# 简单规则判断,实际项目中用更复杂的AI分析
if "叫" in last_message or "名字" in last_message:
intent = "introduce"
elif "查" in last_message or "问" in last_message:
intent = "query"
else:
intent = "greeting"
# 提取关键实体
if intent == "introduce" and "叫" in last_message:
import re
name_match = re.search(r'叫(\w+)', last_message)
if name_match:
name = name_match.group(1)
state["user_name"] = name
state["context_summary"] += f"\n用户介绍自己叫{name}"
state["current_task"] = intent
return state
节点2:生成回复
def generate_response(state: ConversationState) -> ConversationState:
"""根据当前状态生成AI回复"""
messages = list(state["messages"])
# 构建系统提示
user_name = state.get("user_name") or "朋友"
system_prompt = f"""你是一个友善的AI助手。
当前用户名叫{user_name}。
用户偏好:{state.get('user_preferences')}
当前任务:{state.get('current_task')}
"""
# 更新消息列表
messages.insert(0, SystemMessage(content=system_prompt))
# 调用AI生成回复
response = llm.invoke(messages)
# 将AI回复加入历史
state["messages"] = state["messages"] + [response]
# 更新上下文摘要
state["context_summary"] += f"\n最后AI回复:{response.content[:50]}..."
return state
节点3:记忆整合
def consolidate_memory(state: ConversationState) -> ConversationState:
"""定期整合对话记忆,避免上下文过长"""
if len(state["messages"]) > 20:
# 简化消息历史,保留关键信息
recent = state["messages"][-10:]
state["messages"] = recent
state["context_summary"] += "\n[记忆已整合,保留最近10条对话]"
return state
print("三个核心节点已定义!它们会按顺序处理用户的每条消息。")
第五步:组装工作流
# 添加节点到工作流
workflow.add_node("understand", understand_intent)
workflow.add_node("respond", generate_response)
workflow.add_node("consolidate", consolidate_memory)
定义边(流程控制)
workflow.set_entry_point("understand")
workflow.add_edge("understand", "respond")
workflow.add_edge("respond", "consolidate")
workflow.add_edge("consolidate", END)
编译工作流
app = workflow.compile()
print("工作流编译成功!现在可以开始对话了。")
启动对话的辅助函数
def chat(user_id: str, user_message: str):
"""简单的对话函数"""
# 初始化或获取用户状态
if user_id not in chat.sessions:
chat.sessions[user_id] = initialize_state({}, user_id)
state = chat.sessions[user_id]
state["messages"] = state["messages"] + [HumanMessage(content=user_message)]
# 运行工作流
result = app.invoke(state)
chat.sessions[user_id] = result
return result["messages"][-1].content
存储会话(实际项目用数据库)
chat.sessions = {}
测试对话
print("\n=== 第一次对话 ===")
print("用户: 你好")
print("AI:", chat("user001", "你好"))
print("\n=== 第二次对话 - 介绍自己 ===")
print("用户: 我叫小明")
print("AI:", chat("user001", "我叫小明"))
print("\n=== 第三次对话 - AI记住了吗?===")
print("用户: 你还记得我叫什么吗?")
print("AI:", chat("user001", "你还记得我叫什么吗?"))
常见报错排查
错误1:状态更新后数据丢失
# ❌ 错误写法:直接修改列表
state["messages"].append(new_message) # 可能不生效
✅ 正确写法:重新赋值
state["messages"] = state["messages"] + [new_message]
或者使用LangGraph提供的Operator
from langgraph.constants import AddableUpdates
def update_messages(state: ConversationState) -> ConversationState:
return {"messages": [{"$add": [state["messages"], [new_message]]}]}
错误2:上下文长度超出限制
# ❌ 错误:无限累积消息
state["messages"] = state["messages"] + [new_message] # 最终爆内存
✅ 正确:设置上限并摘要
MAX_MESSAGES = 50
if len(state["messages"]) > MAX_MESSAGES:
# 调用AI生成摘要
summary_prompt = "用一句话总结以下对话:\n" + "\n".join([m.content for m in state["messages"][-20:]])
summary = llm.invoke([HumanMessage(content=summary_prompt)])
# 只保留系统消息和摘要
state["messages"] = [SystemMessage(content=f"对话摘要:{summary.content}")]
state["context_summary"] = summary.content
错误3:API调用超时或连接失败
# ❌ 问题:使用默认超时设置
llm = ChatOpenAI(model="gpt-4o")
✅ 正确:配置合理的超时和重试
from langchain_openai import ChatOpenAI
from langchain_core.callbacks import CallbackManager
import time
def create_robust_llm():
for attempt in range(3):
try:
return ChatOpenAI(
model="gpt-4o-mini",
timeout=30, # 30秒超时
max_retries=2,
request_timeout=30
)
except Exception as e:
if attempt < 2:
wait = 2 ** attempt # 指数退避
time.sleep(wait)
else:
raise e
使用HolyShehe API的另一个好处:国内直连,延迟<50ms,超时很少见
错误4:状态条件判断逻辑错误
# ❌ 错误:直接修改传入的状态字典
def bad_node(state):
if state["user_name"] is None: # 第一次调用
state["user_name"] = "新用户" # 直接修改
return state # 返回的仍是同一个对象
✅ 正确:返回新字典或使用条件更新
def good_node(state):
updates = {}
if state.get("user_name") is None:
updates["user_name"] = "新用户"
# 在主逻辑中统一处理更新
return updates # 返回更新指令
或者在图定义中使用条件边
from langgraph.graph import START
workflow.add_edge(START, "check_user")
workflow.add_conditional_edges(
"check_user",
lambda state: "known_user" if state.get("user_name") else "new_user"
)
进阶技巧:实现持久化状态存储
上面我们的状态存储在内存中,服务器重启就丢了。实际项目需要持久化存储。
# 使用Redis实现状态持久化
import redis
import json
redis_client = redis.Redis(host='localhost', port=6379, db=0)
def save_state(user_id: str, state: dict):
"""保存用户状态到Redis"""
# 序列化时需要转换消息对象
serializable_state = {
"user_name": state.get("user_name"),
"user_preferences": state.get("user_preferences"),
"current_task": state.get("current_task"),
"task_history": state.get("task_history", []),
"recent_entities": state.get("recent_entities", {}),
"context_summary": state.get("context_summary", "")
}
redis_client.setex(f"chat_state:{user_id}", 86400, json.dumps(serializable_state))
def load_state(user_id: str) -> dict:
"""从Redis加载用户状态"""
data = redis_client.get(f"chat_state:{user_id}")
if data:
return json.loads(data)
return None
在对话函数中使用
def chat_persistent(user_id: str, message: str):
# 尝试加载已有状态
saved_state = load_state(user_id)
if saved_state:
state = saved_state
else:
state = initialize_state({}, user_id)
state["messages"] = state.get("messages", []) + [HumanMessage(content=message)]
result = app.invoke(state)
# 保存更新后的状态
save_state(user_id, result)
return result["messages"][-1].content
总结与实战建议
我自己在生产环境中用这套架构跑了半年多,最大感受是:状态管理做好了,对话系统的体验能提升好几个档次。用户不再需要反复解释自己的背景,系统能记住三个月前的偏好。
关键经验总结:
- 状态设计要分层:长期记忆(用户画像)、中期记忆(当前会话)、短期记忆(最近几轮)分开管理
- 定期压缩上下文:用AI生成摘要比简单截断效果好太多
- 做好状态持久化:Redis或数据库都可以,关键是别让状态丢失
- 调试时打印完整状态:很多bug是因为状态流转不符合预期,加日志能省很多时间
最后再推荐一下HolyShehe AI,我用它替代OpenAI官方API后,成本直接降了85%以上。GPT-4o-mini这个模型性价比很高,响应速度快,对于状态管理这种高频调用场景特别合适。
👉 免费注册 HolyShehe AI,获取首月赠额度如果有任何问题,欢迎在评论区留言,我会尽力解答。下一期我们讲如何给对话系统添加工具调用能力,敬请期待!