【LangChain】P6 对话记忆完全指南:从原理到实战(中)
目录
- 第四部分:实战案例 - 构建智能客服机器人
- 4.1 需求分析
- 功能需求:
- 非功能需求:
- 4.2 核心实现:多用户会话管理
- 4.3 完整的使用示例
- 4.4 功能亮点分析
- 亮点 1:完美的会话隔离
- 亮点 2:智能的系统提示
- 亮点 3:动态历史管理
本篇博文为 【LangChain】系列第五篇博文 —— 对话记忆完全指南:从原理到实战(上)的第二篇博文。如无法衔接,请访问 【LangChain】P5 对话记忆完全指南:从原理到实战(上)
第四部分:实战案例 - 构建智能客服机器人
4.1 需求分析
假设我们要为一家酒店开发客服机器人,需求如下:
功能需求:
✅ 记住客户的姓名、房间号等信息
✅ 理解多轮对话的上下文关联
✅ 支持多个客户同时咨询(会话隔离)
✅ 对话历史持久化(程序重启后仍可继续)
非功能需求:
- 响应速度快(控制 token 消耗)
- 对话质量高(不能答非所问)
- 可追溯(记录所有对话以便分析)
4.2 核心实现:多用户会话管理
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
from typing import Dict, Listclass HotelServiceBot:"""酒店客服机器人 - 支持多用户并发"""def __init__(self, llm):self.llm = llmself.sessions: Dict[str, List] = {} # 存储每个用户的会话# 定义客服的系统提示self.system_prompt = """你是【悦居酒店】的智能客服助手"小悦"。你的职责:
1. 热情、专业地解答客人的问题
2. 记住客人提供的重要信息(姓名、房间号、需求等)
3. 主动提供帮助建议
4. 对于设施故障等问题,及时安排处理回复风格:
- 亲切有礼,但不过度热情
- 简洁明了,避免长篇大论
- 遇到无法解决的问题,及时转人工重要提示:
- 客人的房间号格式为 XXX(三位数字)
- 工程维修通常 15-30 分钟到达
- 前台电话:8888"""def get_or_create_session(self, user_id: str) -> List:"""获取或创建用户会话设计要点:- 每个用户有独立的消息历史- 首次访问时自动创建会话- 会话包含系统提示 + 对话历史"""if user_id not in self.sessions:self.sessions[user_id] = [SystemMessage(content=self.system_prompt)]print(f"[系统] 为用户 {user_id} 创建新会话")return self.sessions[user_id]def chat(self, user_id: str, message: str) -> str:"""处理用户消息流程:1. 获取该用户的历史记录2. 添加新消息3. 调用模型4. 保存回复5. 管理历史长度"""# 获取用户的会话历史messages = self.get_or_create_session(user_id)# 添加用户消息messages.append(HumanMessage(content=message))# 调用模型生成回复response = self.llm.invoke(messages)# 保存 AI 回复messages.append(AIMessage(content=response.content))# 控制历史长度(避免 token 超限)self._trim_history(user_id, max_turns=10)return response.contentdef _trim_history(self, user_id: str, max_turns: int = 10):"""修剪单个用户的对话历史"""messages = self.sessions[user_id]# 分离系统消息和对话system_msg = [m for m in messages if isinstance(m, SystemMessage)]conversation = [m for m in messages if not isinstance(m, SystemMessage)]# 只保留最近 N 轮if len(conversation) > max_turns * 2:conversation = conversation[-(max_turns * 2):]print(f"[系统] 用户 {user_id} 的历史已修剪")self.sessions[user_id] = system_msg + conversationdef get_history(self, user_id: str) -> List:"""获取用户的对话历史"""return self.get_or_create_session(user_id)def clear_session(self, user_id: str):"""清空用户会话"""if user_id in self.sessions:del self.sessions[user_id]print(f"[系统] 用户 {user_id} 的会话已清空")def get_active_users(self) -> List[str]:"""获取当前活跃用户列表"""return list(self.sessions.keys())
4.3 完整的使用示例
# 创建机器人实例
bot = HotelServiceBot(chat_model)print("=" * 50)
print(" 悦居酒店智能客服系统启动")
print("=" * 50)# ===== 用户A的对话 =====
print("\n【客人A上线】")
print("-" * 50)response1 = bot.chat("user_a", "你好,我是 301 房间的客人,我叫李明")
print(f"小悦: {response1}")response2 = bot.chat("user_a", "我房间的空调不制冷,能帮我看看吗?")
print(f"小悦: {response2}")response3 = bot.chat("user_a", "大概多久能来?对了,你还记得我是哪个房间的吗?")
print(f"小悦: {response3}")# ===== 用户B的对话(同时进行)=====
print("\n【客人B上线】")
print("-" * 50)response4 = bot.chat("user_b", "你好,我是 505 房间的客人")
print(f"小悦: {response4}")response5 = bot.chat("user_b", "附近有什么好吃的餐厅推荐吗?")
print(f"小悦: {response5}")# ===== 验证会话隔离 =====
print("\n【系统检查】")
print("-" * 50)
# 验证用户A的信息没有泄露给用户B
response6 = bot.chat("user_b", "李明是谁?")
print(f"小悦(对B): {response6}")
# 验证用户A仍然能被记住
response7 = bot.chat("user_a", "我想换个房间,可以吗?")
print(f"小悦(对A): {response7}")# ===== 查看系统状态 =====
print("\n【系统统计】")
print("=" * 50)
print(f"当前活跃用户: {bot.get_active_users()}")
print(f"用户A的对话轮数: {len([m for m in bot.get_history('user_a') if not isinstance(m, SystemMessage)]) // 2}")
print(f"用户B的对话轮数: {len([m for m in bot.get_history('user_b') if not isinstance(m, SystemMessage)]) // 2}")# ===== 查看详细历史 =====
print("\n【用户A的完整对话历史】")
print("-" * 50)
for i, msg in enumerate(bot.get_history('user_a'), 1):role = "系统" if isinstance(msg, SystemMessage) else ("客人" if isinstance(msg, HumanMessage) else "小悦")content = msg.content[:60] + "..." if len(msg.content) > 60 else msg.contentprint(f"{i}. {role}: {content}")
运行效果示例展示:
==================================================悦居酒店智能客服系统启动
==================================================【客人A上线】
--------------------------------------------------
[系统] 为用户 user_a 创建新会话
小悦: 您好,李先生!很高兴为您服务。请问有什么可以帮您的吗?
小悦: 好的李先生,我马上为您安排工程维修。维修师傅会在15-30分钟内到301房间检查空调。请问您方便在房间等候吗?
小悦: 当然记得,您是301房间的李先生。维修师傅会在15-30分钟内到达,请您稍等片刻。需要我帮您催一下进度吗?【客人B上线】
--------------------------------------------------
[系统] 为用户 user_b 创建新会话
小悦: 您好!很高兴为您服务。请问有什么可以帮您的吗?
小悦: 当然!酒店附近有几家不错的餐厅:- **悦居中餐厅**(酒店2楼):主打粤菜和本地特色,环境优雅
- **星光西餐吧**(步行5分钟):牛排和意面很受欢迎
- **巷子小馆**(步行8分钟):性价比高的本地小吃 需要我帮您预订座位或提供具体路线吗?【系统检查】
--------------------------------------------------
小悦(对B): 抱歉,我无法查询到酒店客人或员工的个人信息。如果您需要联系特定客人或工作人员,建议您联系前台(电话:8888),他们会为您提供帮助。
小悦(对A): 李先生,我理解您想换房间的需求。由于换房需要前台协调,我建议您:1. 先让师傅检查空调情况
2. 如需换房,可拨打前台电话8888直接沟通需要我帮您转接前台吗?【系统统计】
==================================================
当前活跃用户: ['user_a', 'user_b']
用户A的对话轮数: 4
用户B的对话轮数: 3【用户A的完整对话历史】
--------------------------------------------------
1. 系统: 你是【悦居酒店】的智能客服助手"小悦"。你的职责:
1. 热情、专业地解答客人的问题
2. 记住客人提供的重要信息(...
2. 客人: 你好,我是 301 房间的客人,我叫李明
3. 小悦: 您好,李先生!很高兴为您服务。请问有什么可以帮您的吗?
4. 客人: 我房间的空调不制冷,能帮我看看吗?
5. 小悦: 好的李先生,我马上为您安排工程维修。维修师傅会在15-30分钟内到301房间检查空调。请问您方便在房间等候吗?
6. 客人: 大概多久能来?对了,你还记得我是哪个房间的吗?
7. 小悦: 当然记得,您是301房间的李先生。维修师傅会在15-30分钟内到达,请您稍等片刻。需要我帮您催一下进度吗?
8. 客人: 我想换个房间,可以吗?
9. 小悦: 李先生,我理解您想换房间的需求。由于换房需要前台协调,我建议您:1. 先让师傅检查空调情况
2. 如需换房,可拨打前...
4.4 功能亮点分析
让我们分析这个客服机器人的设计亮点:
亮点 1:完美的会话隔离
# 数据结构设计
self.sessions = {"user_a": [SystemMessage(...), HumanMessage(...), AIMessage(...)],"user_b": [SystemMessage(...), HumanMessage(...), AIMessage(...)],"user_c": [...]
}
为什么这样设计?
- 每个用户有独立的"记忆空间"
- 用户A的信息不会泄露给用户B
- 支持大量用户并发使用
类比理解: 就像医院的病历系统,每个病人有独立的病历档案,医生不会把张三的病情记到李四的档案里。
亮点 2:智能的系统提示
self.system_prompt = """你是【悦居酒店】的智能客服助手"小悦"。你的职责:
1. 热情、专业地解答客人的问题
2. 记住客人提供的重要信息(姓名、房间号、需求等)
...
系统提示的作用:
作用 | 说明 | 示例 |
---|---|---|
定义角色 | 告诉 AI 它是谁 | “你是酒店客服” vs “你是医生” |
设定规则 | 约束 AI 的行为 | “记住客人信息”、“及时转人工” |
统一风格 | 保持回复一致性 | “亲切有礼,简洁明了” |
提供知识 | 补充背景信息 | “前台电话8888” |
亮点 3:动态历史管理
def _trim_history(self, user_id: str, max_turns: int = 10):# 只保留最近 10 轮对话if len(conversation) > max_turns * 2:conversation = conversation[-(max_turns * 2):]
为什么要限制历史长度?
问题场景:
第 1 轮:客人问早餐时间
第 2 轮:客人问 WiFi 密码
...
第 50 轮:客人问洗衣服务如果保留所有 50 轮对话:
- Token 消耗:~5000 tokens(约 $0.01/次)
- 响应时间:~3 秒
- 信息噪音:前 40 轮的内容可能已经不相关
解决方案:
只保留最近 10 轮:
- Token 消耗:~1000 tokens(节省 80%)
- 响应时间:~1 秒(快 3 倍)
- 信息质量:更聚焦当前话题
本篇博文为 【LangChain】系列,对话记忆完全指南:从原理到实战 的第二篇文章,围绕一个简答的酒店服务示例展开。第三部分(下)部分内容将对这个示例进一步展开,讨论进一步优化的方案。
2025.10.01 祖国母亲节日快乐!祝愿我的大家庭一直幸福,顺利!
中国·吉林长春