尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
LangChain4j ChatMemory 实战指南:会话记忆抽象、淘汰策略与持久化
LangChain4j ChatMemory 实战指南会话记忆抽象、淘汰策略与持久化【免费下载链接】langchain4jLangChain4j is an idiomatic, open-source Java library for building LLM-powered applications on the JVM. It offers a unified API over popular LLM providers and vector stores, and makes implementing tool calling (including MCP support), agents and RAG easy. It integrates seamlessly with enterprise Java frameworks like Quarkus and Spring Boot.项目地址: https://gitcode.com/GitHub_Trending/la/langchain4j导读在多轮对话应用中大语言模型本身不保留任何会话状态每次交互都必须把此前全部消息重新发送给模型。LangChain4j 提供的ChatMemory抽象正是为了解决这一问题它以统一 API 管理ChatMessage列表内置消息淘汰Eviction、持久化Persistence以及对SystemMessage与工具Tool消息的特殊处理。读完本文你将掌握 LangChain4j 中记忆Memory与历史History的本质区别学会用MessageWindowChatMemory与TokenWindowChatMemory两种开箱即用的实现控制上下文窗口并能够通过实现ChatMemoryStore将会话记忆持久化到任意存储中。为什么需要 ChatMemoryMemory 与 History 的区别LangChain4j 官方文档明确指出memory 和 history 是两个相似但截然不同的概念History历史完整保留用户与 AI 之间的所有消息不做任何删改。它就是用户在 UI 中看到的内容代表实际说过的话。Memory记忆只保留部分信息并将其呈现给 LLM使其表现得仿佛记得这段对话。依据所选记忆算法Memory 可能以各种方式改造历史淘汰某些消息、将多条消息汇总成摘要、剥离消息中不重要的细节、向消息中注入额外信息如 RAG 检索结果或额外指令如结构化输出约束等。当前 LangChain4j 只提供 memory 而非 history。如果你需要保留完整历史需要自行管理例如在应用层把全部消息落库。这一设计在核心接口上有直接体现ChatMemory的messages()方法 Javadoc 明确写着取决于实现它可能不会返回所有先前添加的消息而是返回其子集、摘要或组合见 ChatMemory.java。也就是说ChatMemory天然是一个按需裁剪过的记忆视图而不是历史记录。ChatMemory 核心抽象接口全景ChatMemory是会话记忆的统一入口接口定义于 langchain4j-core/src/main/java/dev/langchain4j/memory/ChatMemory.java方法作用Object id()返回该记忆的唯一 ID用于区分多用户/多会话void add(ChatMessage message)/add(ChatMessage...)/add(IterableChatMessage)追加一条或多条消息void set(ChatMessage...)/set(IterableChatMessage)用指定消息整体替换当前记忆自 1.11.0 引入默认实现为clear()add(...)用于记忆压缩等场景LangChain4j 不会自动调用它ListChatMessage messages()返回当前记忆内容可能是子集/摘要void clear()清空记忆addAsync(ListChatMessage)/setAsync(...)/messagesAsync()1.20.0 起Experimental三个异步非阻塞变体供异步/响应式 AI Service 使用默认实现返回携带AsyncNotSupportedException的失败 future其中addAsync的设计值得注意它接收一个消息列表而非单条消息以便一次读-改-写原子完成持久化、减少往返次数同时其 Javadoc 明确警告不得对同一个 memory 并发调用该方法因为读与写之间隔着 future 组合和线程切换竞争窗口比同步add更宽。clear()会触发 store 的deleteMessages(memoryId)。ChatMemory既可以作为独立的低层组件直接使用也可以作为高层组件如 AI Services的一部分自动参与对话流程。淘汰策略Eviction Policy控制上下文窗口的两种实现引入淘汰策略的必要性有三点适配 LLM 上下文窗口模型单次能处理的 token 数有上限对话一旦超出限制就必须淘汰部分消息通常淘汰最旧的控制成本每个 token 都有费用剔除无关消息可以降低每次调用的开销控制延迟发送给模型的 token 越多处理耗时越长。LangChain4j 提供两种开箱即用的实现二者都位于 langchain4j/src/main/java/dev/langchain4j/memory/chat/ 下。MessageWindowChatMemory按条数滑动的快速原型方案MessageWindowChatMemory以滑动窗口方式保留最近N条消息超出即淘汰最旧者。由于每条消息包含的 token 数量不同按条数裁剪并不精确因此官方定位是适合快速原型验证。ChatMemory chatMemory MessageWindowChatMemory.builder() .id(12345) // 记忆 ID默认 defaultChatMemoryService.DEFAULT .maxMessages(10) // 最多保留 10 条消息超出淘汰最旧的 .build();Builder 支持以下配置项对应 MessageWindowChatMemory.java配置方法说明id(Object id)记忆 ID默认值为ChatMemoryService.DEFAULTdefault可用用户 ID / 会话 ID 区分多个对话maxMessages(Integer)静态上限最多保留的消息条数必须大于 0构造时通过ensureGreaterThanZero校验dynamicMaxMessages(FunctionObject, Integer)动态上限运行时可根据id实时返回窗口大小窗口行为始终遵循 provider 最新返回值chatMemoryStore(ChatMemoryStore)指定持久化 store默认使用SingleSlotChatMemoryStore纯内存alwaysKeepSystemMessageFirst(Boolean)新SystemMessage是否总是置于消息列表首位默认false从源码看maxMessages与dynamicMaxMessages最终都落为同一个maxMessagesProvider字段MessageWindowChatMemory.java前者是忽略 id、永远返回常量的特例。add()的调用链是读取当前消息 →appendMessage()执行 SystemMessage 规则与窗口裁剪ensureCapacity→ 若列表发生变化则调用store.updateMessages(id, messages)持久化MessageWindowChatMemory.java。ensureCapacity的裁剪逻辑若首位是SystemMessage则从索引 1 开始淘汰保证系统消息永远不被移除MessageWindowChatMemory.java。TokenWindowChatMemory按token 数滑动的精确方案TokenWindowChatMemory同样基于滑动窗口但以最近N个 token为准裁剪消息。消息不可分割某条消息装不下时会被整体淘汰即使只超出几个 token。由于 token 数因模型分词器而异它必须接收一个TokenCountEstimator来统计每条ChatMessage的 token 数可选用具体模型提供商的TokenCountEstimator实现如 OpenAI 的OpenAiTokenCountEstimator。ChatMemory chatMemory TokenWindowChatMemory.builder() .id(12345) .maxTokens(2000, tokenCountEstimator) // 保留最近 2000 个 token .build();Builder 配置项对应 TokenWindowChatMemory.java配置方法说明id(Object id)同MessageWindowChatMemorymaxTokens(Integer, TokenCountEstimator)静态上限保留的 token 总量溢出时从最旧消息起整体淘汰dynamicMaxTokens(FunctionObject, Integer, TokenCountEstimator)动态上限运行时根据 id 动态返回 token 限额chatMemoryStore(ChatMemoryStore)默认SingleSlotChatMemoryStorealwaysKeepSystemMessageFirst(Boolean)默认false其ensureCapacity算法TokenWindowChatMemory.java会先用estimateTokenCountInMessages统计总 token 数超出限额时逐条淘汰最旧消息并用estimateTokenCountInMessage递减计数若列表只剩一条SystemMessage则直接返回避免删掉系统消息。选型建议原型验证、对成本不敏感时用MessageWindowChatMemory生产环境需要精确控制上下文占用、追求更稳定的成本与延迟时用TokenWindowChatMemory。持久化实现 ChatMemoryStore 接入任意存储默认情况下两种ChatMemory实现都把消息存放在内存中——SingleSlotChatMemoryStoreSingleSlotChatMemoryStore.java是一个Internal的纯内存实现应用重启后数据即丢失。要实现持久化需要自行实现ChatMemoryStore接口定义于 langchain4j-core/src/main/java/dev/langchain4j/store/memory/chat/ChatMemoryStore.java它只有三个同步抽象方法ListChatMessage getMessages(Object memoryId)按记忆 ID 读取全部消息返回值不得为 nullvoid updateMessages(Object memoryId, ListChatMessage messages)按记忆 ID 覆盖写入全部消息代表ChatMemory的当前状态void deleteMessages(Object memoryId)按记忆 ID 删除全部消息。官方文档给出的示例实现骨架如下class PersistentChatMemoryStore implements ChatMemoryStore { Override public ListChatMessage getMessages(Object memoryId) { // TODO: 按 memoryId 从持久化存储中读取全部消息。 // 可用 ChatMessageDeserializer.messageFromJson(String) 和 // ChatMessageDeserializer.messagesFromJson(String) 方便地从 JSON 反序列化消息。 } Override public void updateMessages(Object memoryId, ListChatMessage messages) { // TODO: 按 memoryId 在持久化存储中更新全部消息。 // 可用 ChatMessageSerializer.messageToJson(ChatMessage) 和 // ChatMessageSerializer.messagesToJson(ListChatMessage) 方便地将消息序列化为 JSON。 } Override public void deleteMessages(Object memoryId) { // TODO: 按 memoryId 在持久化存储中删除全部消息。 } } ChatMemory chatMemory MessageWindowChatMemory.builder() .id(12345) .maxMessages(10) .chatMemoryStore(new PersistentChatMemoryStore()) .build();配合使用时需要注意ChatMemoryStore的三个生命周期语义updateMessages()每次有新消息加入ChatMemory时都会被调用。一次 LLM 交互中通常调用两次一次是加入新的UserMessage时一次是加入新的AiMessage时。该方法需要用给定 memoryId 关联的全部消息覆盖旧状态。消息可以逐条存储每条消息一个记录/行/对象也可以整体存储整个ChatMemory一个记录/行/对象。从ChatMemory淘汰的消息也会同步从ChatMemoryStore淘汰消息被淘汰时updateMessages()会收到一个不包含被淘汰消息的新列表持久化层必须整体覆盖而不是只追加。getMessages()在每次请求ChatMemory全部消息时被调用通常每次 LLM 交互一次。memoryId参数的值即创建ChatMemory时指定的id可用来区分多个用户和/或多个会话。deleteMessages()在调用ChatMemory.clear()时被触发如果不用清空功能可以让该方法留空。为了简化 JSON 序列化LangChain4j 核心模块提供了两个工具类ChatMessageSerializer.javamessageToJson(ChatMessage)与messagesToJson(ListChatMessage)ChatMessageDeserializer.javamessageFromJson(String)与messagesFromJson(String)。它们把UserMessage、AiMessage、SystemMessage、ToolExecutionResultMessage等各类型消息统一序列化/反序列化覆盖了持久化实现中大部分繁琐的类型分支。注意官方文档提示目前唯一的开箱即用实现是InMemoryChatMemoryStore并计划逐步加入 SQL 数据库、文档存储等集成。在仓库当前的 集成文档目录 中可以看到目前已收录的 chat memory store 集成索引其余存储请按上述接口自行接入。SystemMessage 的特殊处理规则SystemMessage是特殊消息类型两种ChatMemory实现对其一视同仁地执行以下规则源码在 MessageWindowChatMemory.java 与 TokenWindowChatMemory.java 中均有完整实现一旦加入SystemMessage永远被保留任何淘汰逻辑都不会删除它窗口裁剪时从索引 1 开始淘汰同一时刻只能持有一条SystemMessage加入一条内容相同的新SystemMessage会被直接忽略appendMessage返回false不触发持久化加入一条内容不同的新SystemMessage会替换旧的那条。默认情况下新消息被追加到列表末尾可以通过设置alwaysKeepSystemMessageFirst(true)让它总是插到列表首位索引 0。这对于系统提示词固定、随会话动态调整的场景非常有用例如切换系统指令时无需手动清理旧指令。工具Tool消息的特殊处理当包含ToolExecutionRequest的AiMessage被淘汰时其后续的孤儿ToolExecutionResultMessage工具执行结果消息也会被自动连带淘汰。这是因为 OpenAI 等部分 LLM 提供商明确禁止在请求中发送没有对应AiMessage的孤立ToolExecutionResultMessage。相关逻辑在两个实现的ensureCapacity中都有体现淘汰掉一条携带工具调用的AiMessage后会继续删除紧随其后的ToolExecutionResultMessage直至遇到非工具结果消息为止MessageWindowChatMemory.java。这保证了对话中工具调用链的完整性避免发送非法消息导致请求报错。非阻塞模式下的异步扩展如果ChatMemory或ChatMemoryStore涉及 I/O如数据库读写在 AI Service 以非阻塞CompletableFuture/Reactive模式运行时应实现异步对应方法避免阻塞线程ChatMemory侧addAsync(ListChatMessage)、setAsync(ListChatMessage)、messagesAsync()自 1.20.0 起提供标注ExperimentalChatMemoryStore侧getMessagesAsync(Object)、updateMessagesAsync(Object, ListChatMessage)、deleteMessagesAsync(Object)。两个接口的异步默认实现都返回携带AsyncNotSupportedException的失败 future即不会静默地把阻塞 I/O 丢到工作线程——这是刻意设计为的是暴露并非真正非阻塞的事实。若底层客户端本身是阻塞的实现方应显式将操作卸载到执行器。详细内容可参考 Non-blocking and Reactive 教程。与 AiServices 的配合使用ChatMemory最常见的使用场景是与 AI Services 结合在构建 AI Service 时传入ChatMemory框架会自动在每次调用前注入记忆、在调用后写入新消息。若要为每个用户维护独立的对话记忆只需为每个用户/会话创建带不同id的ChatMemory实例官方 langchain4j-examples 仓库中提供了ServiceWithMemoryExample、ServiceWithMemoryForEachUserExample、ServiceWithPersistentMemoryExample、ServiceWithPersistentMemoryForEachUserExample等完整示例结合自定义ChatMemoryStore即可实现每用户独立 持久化的生产级会话记忆方案。工具调用场景下ChatMemory对工具消息的特殊处理见上文与 tools 教程 配合可保证多轮工具调用在窗口裁剪后依然对 LLM 提供合法、完整的消息序列。小结LangChain4j 的ChatMemory用一套小而精的抽象解决了 LLM 应用中最常见的上下文管理难题两种现成实现覆盖了按条数快速原型与按 token精确控制两种淘汰需求且都支持静态或动态窗口大小ChatMemoryStore扩展点让持久化只需实现三个方法配合核心模块提供的 JSON 序列化工具即可接入数据库、缓存或对象存储SystemMessage 与工具消息的专属规则保证了系统指令的稳定性和工具调用链的合法性异步变体为非阻塞 AI Service 提供了无阻塞的记忆读写路径。从源码结构看MessageWindowChatMemory与TokenWindowChatMemory共享几乎完全一致的 SystemMessage/工具消息处理逻辑区别仅在窗口度量单位与裁剪算法这使你在两者之间迁移时几乎不需要改动业务代码只需更换 Builder 与估算器。【免费下载链接】langchain4jLangChain4j is an idiomatic, open-source Java library for building LLM-powered applications on the JVM. It offers a unified API over popular LLM providers and vector stores, and makes implementing tool calling (including MCP support), agents and RAG easy. It integrates seamlessly with enterprise Java frameworks like Quarkus and Spring Boot.项目地址: https://gitcode.com/GitHub_Trending/la/langchain4j创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED

相关推荐

Dart Skills CLI:面向交付流水线的AI协作者

Dart Skills CLI:面向交付流水线的AI协作者

1. 项目概述:这不是一个“CLI工具”,而是一套面向Dart工程师的AI协同交付工作流你有没有遇到过这样的场景:刚写完一段Dart代码,想立刻验证它在Flutter Web上的渲染行为,但本地dev server卡在热重载失败;或者…

📅 2026/9/15 16:05:17
98游戏发布站PHP源码解析:环境搭建、会员发布与下载分发

98游戏发布站PHP源码解析:环境搭建、会员发布与下载分发

简介:基于PHP构建的98游戏发布站程序,面向需要快速搭建游戏分享与下载平台的开发者,提供含会员注册、登录、上传、分类、下载、评论评分等功能的完整前后台源码。压缩包共242个文件,以84个PHP脚本为核心后端逻辑,配以4…

📅 2026/9/15 16:00:17
用 Instructor 实现 Demonstration Ensembling(DENSE):最大化利用少样本示例的集成提示技术

用 Instructor 实现 Demonstration Ensembling(DENSE):最大化利用少样本示例的集成提示技术

用 Instructor 实现 Demonstration Ensembling(DENSE):最大化利用少样本示例的集成提示技术 【免费下载链接】instructor structured outputs for llms 项目地址: https://gitcode.com/GitHub_Trending/in/instructor DENSE&#xff…

📅 2026/9/15 16:00:17
MORE NEWS

更多资讯

📰

Friend 项目内存权限管控:app/key 级内存授权(Memory App/Key Grants)架构与实践

Friend 项目内存权限管控:app/key 级内存授权(Memory App/Key Grants)架构与实践 【免费下载链接】Friend AI that sees your screen, listens to your conversations and tells you what to do 项目地址: https://gitcode.com/GitHub_Tren…

📰

Nightingale 集成中心实战:基于 Categraf 的 BIND DNS 服务器监控与告警方案

Nightingale 集成中心实战:基于 Categraf 的 BIND DNS 服务器监控与告警方案 【免费下载链接】nightingale Nightingale is to monitoring and alerting what Grafana is to visualization. 项目地址: https://gitcode.com/GitHub_Trending/ni/nightingale 本…

📰

douyin-downloader完整教程:快速掌握无水印视频下载与主页批量归档

douyin-downloader完整教程:快速掌握无水印视频下载与主页批量归档 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fa…

📰

仿抖音用户作品数据解密:douyin 项目中 user_video_list/*.md 的数据结构、加载链路与生成脚本

仿抖音用户作品数据解密:douyin 项目中 user_video_list/*.md 的数据结构、加载链路与生成脚本 【免费下载链接】douyin Vue3 Pinia 仿抖音,Vue 在移动端的最佳实践 . Imitate TikTok ,Vue Best practices on Mobile 项目地址: https://g…

📰

BG/NBD模型实战:用Python模拟验证客户终身价值(CLV)预测

我还在整理这篇关于CLV与BG/NBD模型的实现细节,先给你一个核心提要:这不是纯理论文章,而是一次完整的Python模拟实验——从生成一份"已知真实答案"的交易数据开始,反推模型能否把参数和未来购买行为还原出来。做用户增长…

📰

电磁-热耦合仿真:Maxwell与Steady-State Thermal联合求解完整指南

1. 电磁设备发热这档事,为什么必须把两个求解器绑在一起干过电磁仿真的人基本都遇到过这种场景:电磁阀持续通电半小时,外壳烫得不敢摸;电机堵转状态下绕组温度直线飙升;高频变压器满载运行时,磁芯热到失去磁…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬