MessageHistory

聊天机器人要记住上一轮用户说了什么,否则每句都当新会话。把 messages append 到 list 可以,但多 session、多用户时要自己管 dict 键。ChatMessageHistory 抽象「按 session 读写 message 列表」,为 RunnableWithMessageHistory 提供后端。

段末注释ChatMessageHistory = 按会话存储 BaseMessage 列表的可插拔后端;Memory(记忆)在 LangChain 中指对话上下文持久化机制。


1. 一句话定位

维度 内容
角色 能力层 会话存储:session → list[BaseMessage]
输入 → 输出 add_message / add_user_message;读取 messages 属性
典型调用入口 history.add_message(HumanMessage(...))history.messages
与 LangGraph 长期记忆用 checkpointer + store;短会话 history 仍可用于简单链

2. 实现逻辑

1
2
3
4
5
6
1. history = InMemoryChatMessageHistory()
2. history.add_user_message("你好")
3. model.invoke(history.messages) → AIMessage
4. history.add_ai_message(ai.content) 或 add_message(ai)
5. 下一轮 history.messages 已含完整多轮
6. 多 session:dict[session_id, ChatMessageHistory]

字段级变形

1
2
3
4
5
messages = []
→ add_user_message("北京天气")
→ [HumanMessage("北京天气")]
→ add_ai_message("晴")
→ [HumanMessage(...), AIMessage("晴")]

3. 原理说明

3.1 BaseChatMessageHistory

BaseChatMessageHistory(抽象类,langchain_core.chat_history
功能:按会话读写 list[BaseMessage]。实现有内存、Redis、Postgres 等。

add_message(message)(方法)
最小输入:1 条 BaseMessage

add_user_message(message: str) / add_ai_message(message: str)(方法)
内部包成 Human/AIMessage。message 建议非空。

clear()(方法)
清空当前会话。无参数。清空后 messages 长度为 0。

messages(属性)
类型 list[BaseMessage],默认 []。最小可读:0 条;多轮对话至少 2 条(human+ai)。

1
2
history.add_message(HumanMessage(content="你好"))
history.messages # len=1

勿直接 mutate 该 list,用 API。

3.2 InMemoryChatMessageHistory

InMemoryChatMessageHistory(数据类)
功能:进程内 list,重启丢失,适合测试。构造无必填参数。
数据结构:内部 messages: list[BaseMessage] = []
最小维度:空 history 合法。

1
2
3
4
from langchain_core.chat_history import InMemoryChatMessageHistory
h = InMemoryChatMessageHistory()
h.add_user_message("我叫小明。")
h.add_ai_message("你好,小明。")

多 session:dict[session_id, ChatMessageHistory]session_id 非空 str。

3.3 与 v1「Memory」类

旧版 ConversationBufferMemorylangchain-classic。挂到 LCEL 上的 RunnableWithMessageHistory 已弃用(≥1.3.3);新项目用 LangGraph checkpointer(thread_id)。BaseChatMessageHistory 本身仍可用作存储抽象。

3.4 工具轮次

带工具的历史须完整保存 AIMessage(tool_calls)ToolMessage。最小合法工具轮:human + ai(tool_calls len≥1) + 等量 ToolMessage。缺 id 则下轮 invoke 非法。


4. 最小可运行示例

1
pip install -U langchain-core
1
2
3
4
5
6
7
8
9
10
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.messages import HumanMessage, AIMessage

history = InMemoryChatMessageHistory()
history.add_message(HumanMessage(content="我叫小明。"))
history.add_message(AIMessage(content="你好,小明。"))
history.add_message(HumanMessage(content="我叫什么?"))

print(len(history.messages), history.messages[0].content)
# 预期:3 我叫小明。

重要配置参数

参数(API 名) 类型 / 默认值 功能说明 作用与影响 参考起点 配置指导
session_id str(外层 dict 键) 把不同用户/会话的消息列表隔开 共用一个 id 会串话;多 worker 内存后端会丢 UUID 多租户用 tenant:user
messages list,只读属性,默认 [] 读取当前会话已保存的 BaseMessage 序列 直接 mutate 可能不同步 增删一律走 add_message / clear
持久化后端 实现类 历史存在内存、Redis 还是 SQL InMemory 重启即丢、多进程不一致 生产选型 InMemory 仅 dev
clear() 方法 清空该会话全部消息 不可恢复;合规「忘记我」用 用户触发 GDPR 场景必暴露
历史长度上限 应用层 送进模型前截断或摘要到最近 N 轮 防超窗;截太狠丢上下文 最近 N 轮 可截断或摘要
TTL Redis 等 会话键自动过期时间 过短用户感觉失忆;过长占存储 7~30 天 按合规要求

5. 易踩坑

  1. 只存 user/ai 不存 tool 消息:Agent 多轮必崩。
  2. 多 worker 共享 InMemory:请求打到不同进程,历史不一致;生产用 Redis/DB。
  3. 直接修改 history.messages list 引用:部分实现不保证同步,用 API 方法。

小结

  • ChatMessageHistory 管 session 级 messages 列表
  • 原型用 InMemoryChatMessageHistory;生产换持久化实现。
  • v1 与 RunnableWithMessageHistory 组合挂到链上。
  • 工具对话须存 完整 message 类型序列

参考链接

-------------本文结束感谢您的阅读-------------