大模型:RunnableWithMessageHistory 短期记忆案例

发布时间:2026/7/6 3:23:13
大模型:RunnableWithMessageHistory 短期记忆案例 代码分析基于 LangChain 的多轮对话记忆实现这段代码演示了如何使用 LangChain 的RunnableWithMessageHistory组件结合通义千问模型ChatTongyi构建一个具备多轮对话记忆能力的聊天链。它通过InMemoryChatMessageHistory在内存中按会话 IDsession_id存储对话历史并自动在每次请求时将历史消息注入提示模板。 代码结构与关键组件1. 模型与提示模板modelChatTongyi(modelqwen3-max)promptChatPromptTemplate.from_messages([(system,你需要根据会话历史回应用户问题。对话历史),MessagesPlaceholder(chat_history),(human,请回答如下问题{input})])使用ChatPromptTemplate构建聊天消息模板包含三个部分系统提示固定角色设定。MessagesPlaceholder(chat_history)这是一个占位符运行时会被传入的历史消息列表替换。它是实现记忆注入的关键。用户问题{input}。2. 基础链base_chainstr_parserStrOutputParser()defprint_prompt(full_prompt):print(*20,full_prompt.to_string(),*20)returnfull_prompt base_chainprompt|print_prompt|model|str_parser使用 LCELLangChain Expression Language将prompt、调试函数、模型、输出解析器串联成一个Runnable链。print_prompt用于在控制台打印最终生成的提示方便调试。3. 历史存储与RunnableWithMessageHistorystore{}# session_id - InMemoryChatMessageHistorydefget_history(session_id):ifsession_idnotinstore:store[session_id]InMemoryChatMessageHistory()returnstore[session_id]conversation_chainRunnableWithMessageHistory(base_chain,get_history,input_messages_keyinput,history_messages_keychat_history)store是内存字典键为session_id值为InMemoryChatMessageHistory实例。RunnableWithMessageHistory是一个包装器它接收一个基础链和一个“历史获取函数”并在每次调用时自动完成根据session_id从store中获取对应历史消息列表。将历史消息注入到base_chain输入中的chat_history字段。执行base_chain。将本轮的用户输入和模型输出追加到历史存储中通过add_user_message和add_ai_message。 运行时流程以三次执行为例第一次执行用户说“小明有2个猫”conversation_chain.invoke({input: 小明有2个猫}, session_config)从store[user_001]获取空历史首次。传入base_chain的输入为{input: ..., chat_history: []}。prompt生成的消息列表中chat_history为空只有系统消息和当前用户提问。模型生成回复如“好的记住了”。执行后RunnableWithMessageHistory自动将HumanMessage(小明有2个猫)和AIMessage(...)追加到store[user_001]。第二次执行“小刚有1只狗”历史已有两轮用户AI。chat_history注入完整历史模型看到上下文生成新回复。历史再次扩展。第三次执行“总共有几个宠物”历史包含前两轮所有消息。模型能从历史中提取“小明有2个猫小刚有1只狗”正确回答“3个”。 关键知识点组件作用MessagesPlaceholder在ChatPromptTemplate中预留一个位置运行时注入完整的消息列表而非单个字符串。这保留了消息角色human/ai的结构。RunnableWithMessageHistory这是一个Runnable 包装器它不影响原链的逻辑仅在其前后插入“加载历史”和“保存历史”的操作。适用于任何Runnable不限于Chain。InMemoryChatMessageHistory一个简单的内存存储实现支持add_messages、messages属性返回BaseMessage列表和clear。适合开发测试但生产环境可用RedisChatMessageHistory或PostgresChatMessageHistory。input_messages_key/history_messages_key指定在invoke输入字典中哪两个键分别代表当前用户输入和历史消息列表。这些键必须与提示模板中的占位符名称匹配。✅ 优点简单只需几行代码即可为任何链添加记忆。灵活历史存储可插拔内存、Redis、数据库。无需修改模型通过提示工程注入历史不依赖模型的特殊记忆功能。⚠️ 局限与改进方向局限解决方案历史无限增长超出上下文窗口限制结合ConversationBufferWindowMemory或ConversationSummaryMemory进行裁剪/摘要。内存存储程序重启丢失改用持久化存储如RedisChatMessageHistory或自定义FileChatMessageHistory。无法跨会话共享信息需额外实现“长期记忆”组件如PostgresStore手动读写用户画像。高并发下store字典可能线程不安全使用线程安全的存储或外部缓存如 Redis。 面试考点速查RunnableWithMessageHistory和直接手动维护messages列表的区别手动维护需要自己在每次调用时处理历史注入和更新代码分散且易错RunnableWithMessageHistory将这些逻辑封装成标准组件便于复用和测试。MessagesPlaceholder与普通字符串占位符{var}有何不同普通占位符只能传入单个字符串MessagesPlaceholder传入的是BaseMessage序列能保留角色和结构信息更符合聊天模型的输入格式。如何在生产环境中实现多轮对话记忆使用持久化历史存储如 Redis替换InMemoryChatMessageHistory结合RunnableWithMessageHistory并配合消息裁剪/摘要策略以控制 Token 消耗。这段代码清晰地展示了 LangChain 处理对话记忆的标准范式是面试中常见的实战示例。掌握其原理和扩展方式能够体现你对 LangChain 核心组件的深度理解。