LangChain4j 记忆功能
LangChain4j 记忆功能——从内存存储到分布式持久化
一、AI的”记忆”到底是什么?
你有没有遇到过这种情况:跟AI聊了几句后,问它”我刚才说了什么?”,它一脸茫然地回答”抱歉,我没有之前的对话记录”。
这就是AI的”健忘症”——每一次对话请求本质上都是独立的,AI不会天然”记住”你之前说过什么。
在LangChain4j中,ChatMemory就是解决这个问题的核心组件。它不是简单地保存所有对话,而是一套智能的记忆管理机制,包括:
- 淘汰策略:防止对话过长超出Token限制
- 持久化:让记忆在服务重启后依然存在
- 多用户隔离:不同用户的对话互不干扰
官方文档明确指出:”记忆”和”历史”是两个不同概念:
- 历史:完整的对话记录,用户能在UI上看到的内容
- 记忆:经过处理后的信息,只保留对AI有用的部分
LangChain4j目前只提供”记忆”功能,如果需要完整历史记录,需要自己实现。
二、三种记忆策略:选对方案很重要
2.1 MessageWindowChatMemory:按消息数量滑动窗口
这是最简单的实现,像一个固定大小的队列,只保留最近N条消息。
1 | // 创建保留最近10条消息的记忆 |
优点:实现简单、性能好
缺点:不考虑Token数量,可能导致超限
适用场景:快速原型开发、短会话场景
2.2 TokenWindowChatMemory:按Token精确控制(推荐)
这是更专业的选择,基于Token数量进行淘汰,精确控制上下文长度。
1 | // 创建保留最近2000个Token的记忆 |
优点:精确控制Token使用,不会超限
缺点:需要Tokenizer,有额外计算开销
适用场景:生产环境、长对话场景
2.3 摘要记忆:让AI”记住”更久远的事
这是一个进阶策略,当对话超过阈值时,自动调用LLM对历史对话进行摘要压缩。
1 | // 需要自己实现摘要逻辑 |
这种方式的优势是可以在有限Token内保留更多有效信息,适合需要长期记忆的场景。
2.4 三种策略对比
| 策略 | 淘汰依据 | 精确度 | 复杂度 | 推荐场景 |
|---|---|---|---|---|
| MessageWindowChatMemory | 消息条数 | 低 | 低 | 原型开发 |
| TokenWindowChatMemory | Token数量 | 高 | 中 | 生产环境首选 |
| 摘要记忆 | 语义压缩 | 高 | 高 | 长对话、知识问答 |
三、开箱即用的内存存储
最简单的使用方式——直接创建ChatMemory并接入AiServices:
java
1 | // 1. 创建ChatMemory |
这是共享记忆模式,所有用户共用同一个上下文。如果你的应用需要区分用户,往下看。
四、多会话记忆隔离:每次回话都由自己的记忆
生产环境必须区分用户的每次对话。LangChain4j提供了@MemoryId注解,优雅地解决这个问题:
java
1 |
|
框架内部会根据@MemoryId的值,自动从ChatMemoryStore中获取对应的记忆。
五、持久化:让记忆”断电不丢”
默认的ChatMemory存储在内存中,服务重启后记忆就没了。生产环境必须持久化。
5.1 ChatMemoryStore接口
LangChain4j提供了ChatMemoryStore接口,实现它就能接入任何存储:
java
1 | public interface ChatMemoryStore { |
LangChain4j提供了序列化工具类,方便将消息转为JSON:
ChatMessageSerializer.messagesToJson():序列化ChatMessageDeserializer.messagesFromJson():反序列化
5.2 Redis存储实现(重点)
Redis是分布式场景的首选,高性能+天然支持过期。
java
1 |
|
使用方式:
java
1 | ChatMemoryStore store = new RedisChatMemoryStore(); |
5.3 MySQL存储实现
适合需要审计、长期归档的场景
关键设计:messagesJson字段存储整个对话序列化后的JSON,查询简单但更新时需整体替换。
5.4 各存储方案对比
| 存储方案 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| Redis | 高性能、支持TTL | 数据可能丢失 | 高并发、分布式 |
| MySQL | 数据安全、支持事务 | 性能相对较低 | 审计、长期归档 |
六、分布式场景最佳实践
6.1 为什么需要分布式存储?
单机内存存储的三大痛点:
- 服务重启丢失:重启后所有对话记忆消失
- 无法水平扩展:多节点部署时,用户请求分发到不同节点,记忆不共享
- 内存压力:高并发下内存占用爆炸
6.3 会话隔离的关键设计
在分布式环境中,必须确保会话标识全局唯一:
java
1 | // 推荐:用户ID + 会话ID 组合 |
这样既能区分用户,又能支持一个用户多个会话(如不同设备、不同话题)。
6.4 淘汰策略的特别说明
当消息被ChatMemory淘汰时,updateMessages()方法会被调用,传入的消息列表不包含被淘汰的消息。这意味着存储层会自动同步淘汰,无需额外处理。
七、高级特性
7.1 SystemMessage的特殊处理
SystemMessage在ChatMemory中有特殊行为:
- 一旦添加,永远保留
- 同时只能存在一条
- 相同内容的新消息会被忽略
- 不同内容的新消息会替换旧的
7.2 工具消息的自动清理
如果包含ToolExecutionRequest的AiMessage被淘汰,后续孤立的ToolExecutionResultMessage也会自动被淘汰,避免某些LLM提供商的兼容性问题。
7.3 AiServices的并发限制
官方文档特别提醒:
AI Service should not be called concurrently for the same @MemoryId, as it can lead to corrupted ChatMemory.
同一个@MemoryId的并发调用可能导致记忆损坏,需要在业务层做好同步控制。
7.4 记忆与RAG的结合
你可以将RAG检索到的内容也存储到ChatMemory中:
java
1 | AiServices.builder(Assistant.class) |
这样AI可以”记住”它从知识库中检索到的信息。
八、实战:完整的分布式记忆服务
结合以上所有知识点,给出一个可直接使用的完整示例:
java
1 | // 1. 定义AI服务接口 |
九、总结
| 需求场景 | 推荐方案 |
|---|---|
| 本地测试/原型 | MessageWindowChatMemory + 内存存储 |
| 生产环境单机 | TokenWindowChatMemory + 内存存储 |
| 分布式部署 | TokenWindowChatMemory + Redis存储 |
| 高并发大流量 | 二级缓存(Caffeine + Redis)+ 会话分片 |
| 需要长期审计 | MySQL持久化 + 定时清理策略 |
- Title: LangChain4j 记忆功能
- Author: 薛定谔的汪
- Created at : 2025-04-15 18:01:54
- Updated at : 2026-04-07 19:27:15
- Link: https://www.zhengyk.cn/2025/04/15/ai/langchain4j_03_memory/
- License: This work is licensed under CC BY-NC-SA 4.0.