当前位置: 首页 > news >正文

LangChain4j-03 ChatMemory 详解:告别“金鱼脑”,实现多轮对话记忆

LangChain4j-03 ChatMemory 详解:告别“金鱼脑”,实现多轮对话记忆

本文基于 LangChain4j 官方文档,结合实战经验,系统讲解如何利用ChatMemory机制让大模型记住对话上下文。


🙋‍♂️ 让我们保持联系
觉得这篇文章有帮助吗?​ 一个小小的互动对我意义重大:
点赞​ 👍 - 让我知道这篇文章对你有价值
收藏​ 🔖 - 遇到问题时能快速找到解决方案
关注​ 📌 - 不错过后续的深度技术分享

🧠 一、 为什么需要 ChatMemory?

手动维护List<ChatMessage>来记录聊天记录非常繁琐,且面临三大难题:

  1. 上下文溢出:LLM 有 Token 限制,历史记录不能无限增长。
  2. 状态丢失:默认内存存储,服务重启后对话记忆清零。
  3. 逻辑复杂:需要手动处理系统消息、工具消息的关联性。

解决方案:LangChain4j 提供了ChatMemory抽象层,它本质上是一个智能的、带自动清理策略的聊天消息容器


📚 二、 核心概念与类图

1. 核心类关系图

«interface»

ChatMemory

+id() : : Object

+add(message) : : void

+messages() : : List<ChatMessage>

+clear() : : void

MessageWindowChatMemory

-maxMessages: int

-chatMemoryStore: ChatMemoryStore

+builder()

TokenWindowChatMemory

-maxTokens: int

-tokenizer: Tokenizer

+builder()

«interface»

ChatMemoryStore

+getMessages(memoryId)

+updateMessages(memoryId, messages)

+deleteMessages(memoryId)

2. “记忆” vs “历史”

维度Memory (记忆)History (历史)
目的给 LLM 提供上下文给用户查看完整记录
内容部分消息(按策略筛选)所有原始消息
现状✅ LangChain4j 内置❌ 需手动实现(如存DB)

⚙️ 三、 开箱即用的实现

1. MessageWindowChatMemory(按条数限制)

基于滑动窗口算法,保留最近的 N 条消息(User 和 AI 各算一条)。

// 示例:只保留最近2条对话ChatMemorychatMemory=MessageWindowChatMemory.builder().maxMessages(2).build();// 使用:通常在 AI Service 中绑定OpenAiChatModelmodel=OpenAiChatModel.builder().baseUrl("http://langchain4j.dev/demo/openai/v1").apiKey("demo")// 固定演示密钥.modelName("gpt-4o-mini").build();MyServiceaiService=AiServices.builder(MyService.class).chatModel(model).chatMemory(chatMemory)// 绑定记忆.build();System.out.println(aiService.chat("我是 zzn"));System.out.println(aiService.chat("我是java工程师"));//System.out.println(aiService.chat("我是谁?")); // 只保留最近2条对话,它已经不知道我是谁了!!!但是它知道我的职业//System.out.println(aiService.chat("我是什么职业?"));

适用场景:快速原型开发,简单对话。

2. TokenWindowChatMemory(按Token限制)

更科学的实现,保留最近的 N 个 Token(需配合 Tokenizer)。

TokenCountEstimatorestimator=newOpenAiTokenCountEstimator("gpt-4o-mini");// 2. 构建 TokenWindowChatMemoryTokenWindowChatMemorychatMemory=TokenWindowChatMemory.builder().maxTokens(100,estimator)// ✅ 传入估算器.build();OpenAiChatModelmodel=OpenAiChatModel.builder().baseUrl("http://langchain4j.dev/demo/openai/v1").apiKey("demo")// 固定演示密钥.modelName("gpt-4o-mini").build();MyServiceaiService=AiServices.builder(MyService.class).chatModel(model).chatMemory(chatMemory)// 绑定记忆.build();System.out.println(aiService.chat("我是 zzn,是一名java工程师,我主攻Java领域,擅长高并发的分布式架构,帮我给出职业生涯规划!"));System.out.println(aiService.chat("我是谁,我的职业是什么?"));//因为一轮会话已经超过100个token,所以它已经不知道我是谁了. 如果把上面的maxtoken改成更大,如10000就能记住你是谁

适用场景:生产环境,精确控制成本与上下文长度。


💾 四、 进阶实战:自定义持久化 (ChatMemoryStore)

默认内存存储重启即失,要实现跨会话记忆(如存Redis/MySQL),需实现ChatMemoryStore接口。

1. 核心接口方法

  • updateMessages(Object memoryId, List<ChatMessage> messages)最关键。当新增或驱逐消息时调用,用于全量更新存储。
  • getMessages(Object memoryId):加载某次对话的所有消息。
  • deleteMessages(Object memoryId):清除对话记录。

2. 基于 Redis 的持久化实现

Java 源码:RedisChatMemoryStore.java

/** * 基于RedisTemplate的ChatMemoryStore实现 */publicclassRedisChatMemoryStoreimplementsChatMemoryStore{privatestaticfinalStringKEY_PREFIX="chat:memory:";privatefinalRedisTemplate<String,String>redisTemplate;privatefinalLongttlSeconds;publicRedisChatMemoryStore(RedisTemplate<String,String>redisTemplate){this(redisTemplate,null);}publicRedisChatMemoryStore(RedisTemplate<String,String>redisTemplate,LongttlSeconds){this.redisTemplate=redisTemplate;this.ttlSeconds=ttlSeconds;}@OverridepublicList<ChatMessage>getMessages(ObjectmemoryId){Stringkey=KEY_PREFIX+memoryId;Stringjson=redisTemplate.opsForValue().get(key);if(json==null||json.isEmpty()){returnnewArrayList<>();}try{// ✅ 正确用法:使用ChatMessageDeserializerreturnChatMessageDeserializer.messagesFromJson(json);}catch(Exceptione){thrownewRuntimeException("Failed to deserialize chat messages from Redis",e);}}@OverridepublicvoidupdateMessages(ObjectmemoryId,List<ChatMessage>messages){Stringkey=KEY_PREFIX+memoryId;// ✅ 序列化:使用ChatMessageSerializerStringjson=ChatMessageSerializer.messagesToJson(messages);if(ttlSeconds!=null){redisTemplate.opsForValue().set(key,json,ttlSeconds,TimeUnit.SECONDS);}else{redisTemplate.opsForValue().set(key,json);}}@OverridepublicvoiddeleteMessages(ObjectmemoryId){Stringkey=KEY_PREFIX+memoryId;redisTemplate.delete(key);}/** * 清理测试数据 */publicvoidclearAll(){Stringpattern=KEY_PREFIX+"*";redisTemplate.delete(redisTemplate.keys(pattern));}}

使用方式

RedisChatMemoryStorechatMemoryStore=newRedisChatMemoryStore(redisTemplate,1800L);// 示例:只保留最近2条对话ChatMemorychatMemory=MessageWindowChatMemory.builder().maxMessages(2).chatMemoryStore(chatMemoryStore).id("zzn").build();// 使用:通常在 AI Service 中绑定OpenAiChatModelmodel=OpenAiChatModel.builder().baseUrl("http://langchain4j.dev/demo/openai/v1").apiKey("demo")// 固定演示密钥.modelName("gpt-4o-mini").build();MyServiceaiService=AiServices.builder(MyService.class).chatModel(model).chatMemory(chatMemory)// 绑定记忆.build();System.out.println(aiService.chat("我是 zzn"));System.out.println(aiService.chat("我是java工程师"));System.out.println(redisTemplate.opsForValue().get("chat:memory:zzn"));//只有“我是java工程师”和Ai回复在redis中

使用方式-ChatMemoryProvider&@MemoryId

RedisChatMemoryStorechatMemoryStore=newRedisChatMemoryStore(redisTemplate,1800L);// 示例:只保留最近2条对话ChatMemoryProviderchatMemoryProvider=memoryId->MessageWindowChatMemory.builder().id(memoryId).maxMessages(2).chatMemoryStore(chatMemoryStore).build();// 使用:通常在 AI Service 中绑定OpenAiChatModelmodel=OpenAiChatModel.builder().baseUrl("http://langchain4j.dev/demo/openai/v1").apiKey("demo")// 固定演示密钥.modelName("gpt-4o-mini").build();MyServiceaiService=AiServices.builder(MyService.class).chatModel(model).chatMemoryProvider(chatMemoryProvider)// 绑定记忆.build();System.out.println(aiService.chat("zzn","我是 zzn"));System.out.println(aiService.chat("zzn","我是java工程师"));System.out.println(redisTemplate.opsForValue().get("chat:memory:zzn"));//SystemMessage特权:只有SystemMessage和Ai回复在redis中

⚠️ 五、 特殊消息处理机制

1. 系统消息 (SystemMessage)

SystemMessage拥有**“特权”。在MessageWindowChatMemory中,系统消息不会被自动驱逐**(除非手动清除),确保 LLM 始终遵循初始指令。

2. 工具消息 (Tool Messages)

为了防止孤儿消息(Orphaned ToolExecutionResultMessage)错误,框架做了特殊处理:

  • 当一条包含ToolExecutionRequestAiMessage被驱逐时,其对应的ToolExecutionResultMessage也会被自动连带驱逐
  • 这是因为某些 LLM(如 OpenAI)严格禁止在请求中发送没有对应请求的执行结果。

🎯 六、 总结与最佳实践

  1. 选型建议
    • 开发/测试:直接用MessageWindowChatMemory,简单粗暴。
    • 生产环境:优先TokenWindowChatMemory+RedisChatMemoryStore
  2. ID 设计memoryId建议使用userId:conversationId格式,实现多租户隔离。
  3. 性能注意updateMessages会在每次交互时调用两次(用户输入+AI回复),高频场景建议使用批量写入或异步存储优化。

通过ChatMemory,你可以轻松构建出能进行连贯多轮对话的智能应用,而无需关心底层的消息管理与淘汰逻辑。

🙋‍♂️ 让我们保持联系
觉得这篇文章有帮助吗?​ 一个小小的互动对我意义重大:
点赞​ 👍 - 让我知道这篇文章对你有价值
收藏​ 🔖 - 遇到问题时能快速找到解决方案
关注​ 📌 - 不错过后续的深度技术分享

http://www.jsqmd.com/news/720472/

相关文章:

  • 从无人机编队到智能集群:纯方位无源定位技术的应用场景与未来展望
  • 化工泵选型技术要点 合规厂家资质与性能解析 - 奔跑123
  • 别再怪Win11了!任务栏QQ闪动弹窗,可能是你这个设置没关(附新旧版QQ对比)
  • 告别手动上传!用Python+SAP OData实现OA审批后自动同步请求号(保姆级避坑指南)
  • Rust Trait 泛型结合使用技巧
  • 示波器traces采集
  • 驾校培训办公管理系统 专属驾校的OA系统 驾培管理行业
  • Win11Debloat:让Windows系统重获清爽体验的专业优化工具
  • DeepSeek V4 深度测评:基于工程化协同方法论的 5 维实验验证
  • 躲开跨国文化陷阱:英美澳企业全英文面试中的“红牌”行为与高情商沟通术
  • Mac 访问 Linux 共享文件夹:Samba 配置完整指南
  • OpenAI 从模型研发到算力霸权的史诗跃迁
  • 2026 年无刷电机厂家口碑推荐榜:无人机电机、机器人电机、空心杯电机、无框力矩电机厂家选择指南 - 海棠依旧大
  • AI应用的可观测性工程2026:让LLM系统从黑盒变白盒
  • Paperxie AI PPT 生成器:毕业答辩 PPT 的 “懒人救星”,让你告别熬夜改模板
  • Windows Cleaner终极指南:3分钟彻底解决C盘爆红问题
  • 5分钟上手:ComfyUI-BiRefNet-ZHO智能AI图像背景去除与视频抠图工具终极指南
  • 从gethostbyname到getaddrinfo:现代Linux网络编程为何要升级你的DNS查询代码?
  • 2026年立式开装封一体机厂家推荐排行榜/开装封一体机,卧式开装封一体机 - 品牌策略师
  • 别再被PyTorch的Tensor布尔值搞晕了!手把手教你用.all()和.any()的正确姿势
  • VSCode新手必看:CodeGeeX插件安装到实战避坑全指南(2024最新版)
  • Xenia Canary终极指南:在现代PC上完美运行Xbox 360游戏的完整解决方案
  • 【触想智能】嵌入式工业一体机在智能化设备上应用产生的意义
  • UniApp动态渲染头像避坑指南:为什么你的`userInfo.avatar`在微信小程序里总变成‘/pages/index/undefined’?
  • 知识竞赛的类型与特点全面解析
  • Axure RP中文语言包:3分钟搞定专业界面本地化,告别英文烦恼!
  • PHP 8.9类型严格模式上线倒计时:3类遗留项目(Laravel 9、Symfony 6、WordPress插件)紧急适配清单
  • 【2026 Turnitin对策】英文文章AI率95%降至0%实测:掌握这4个高阶修改法
  • 如何使用MouseClick跨平台鼠标连点工具提升工作效率
  • 告别深拷贝的痛:在鸿蒙PC与ArkTS中玩转 `@ObservedV2` 装饰器