1. 核心抽象:ChatMemory 接口
在 LangChain4j 中,记忆系统的核心接口是 ChatMemory。它的职责非常纯粹:维护对话历史的顺序,并根据策略进行截断。
1.1 两种主流实现(短期记忆的落地)
正如你笔记中提到的“滑动窗口”策略,LangChain4j 直接提供了现成的类:
-
MessageWindowChatMemory(按消息数量滑动):保留最近的 条消息。比如设置 ,那么第 11 条消息进来时,第 1 条就会被丢弃。
-
TokenWindowChatMemory(按 Token 数量滑动):这是最推荐的方式。它会根据指定的
Tokenizer(分词器)计算历史记录的总 Token 数。如果超过限制(如 2000 Tokens),它会逐条删除最早的消息,直到余下的内容符合长度限制。
2. 记忆的持久化:ChatMemoryStore
默认情况下,ChatMemory 是存储在内存里的(InMemoryChatMemoryStore)。这在生产环境下显然不够,因为服务器重启记忆就丢了。
ChatMemoryStore 就是为了解决这个问题。它是一个简单的接口,你只需要实现 getMessages(Object memoryId) 和 updateMessages(Object memoryId, List<ChatMessage> messages) 两个方法,就可以把记忆对接到:
- Redis: 适合高性能、高频次的短期记忆。
- JDBC (MySQL/PostgreSQL): 适合需要持久化存储的场景。
- MongoDB: 适合存储非结构化的对话数据。
面试加分点: LangChain4j 允许通过
memoryId来区分不同用户或不同会话。在你的代码逻辑里,这个memoryId通常就是userId。
3. 实战代码实现
我们可以通过 LangChain4j 的 AiServices 快速集成记忆功能。以下是一个结合了 Token 滑动窗口 和 自定义存储 的标准实现:
4. 如何融入你的“长期记忆(主动录入)”设计?
LangChain4j 的 ChatMemory 主要解决的是对话连贯性(短期) 。对于你提到的长期记忆(主动录入+向量检索) ,在 LangChain4j 中是通过 ContentRetriever (RAG) 来实现的。
联合运作流程:
- 用户输入: “帮我写一段 Java 代码”。
- 长期记忆检索: 系统通过
ContentRetriever到向量数据库里搜一下用户的“个人标签”。 - 命中: 搜到“用户是 Java 后端开发,偏好 Spring Boot”。
- 注入: 将这段信息作为
SystemMessage注入。 - 短期记忆拼接:
ChatMemory自动把最近 5 轮对话记录拼在后面。 - 发送给 LLM: 最终的 Prompt 包含了:
系统指令(长期记忆) + 历史对话(短期记忆) + 当前问题。
5. 总结与建议
LangChain4j 的设计非常符合 “高内聚、低耦合” 的原则。
- 如果你追求简单稳定,直接用
MessageWindowChatMemory。 - 如果你追求成本精控,一定要用
TokenWindowChatMemory。 - 如果你需要多端同步,必须实现
ChatMemoryStore对接到 Redis。