7. LangChain4j + 记忆缓存详细说明
@[toc]

记忆缓存是聊天系统中的一个重要组件,用于存储和管理对话的上下文信息。它的主要作用是让AI助手能够"记住"之前的对话内容,从而提供连贯和个性化的回复。
我们随便打开一个大模型对话聊天就明白了。

简单的理解就是:一个让大模型可以记住我们上面一段历史内容上对它的提问,和索引需要的内容:


官方解释:LangChain4j提供了2种开箱即用的记忆缓存的实现方式如下:
- 一种是:通过提问的条数,记忆几条提问记录信息。比如:记忆存储你提问的前 100 条的记录内容。推荐
- 第二种:则是通过记忆存储你的 token 数目,这种方式不推荐,因为不同的大模型的 token 计算的方式不同。


LangChain4j + 记忆缓存实战操作
- 创建对应项目的 module 模块内容:
- 导入相关的 pom.xml 的依赖,这里我们采用流式输出的方式,导入
langchain4j-open-ai + langchain4j + langchain4j-reactor
这三件必须存在,这里我们不指定版本,而是通过继承的 pom.xml 当中获取。_
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--langchain4j-open-ai + langchain4j + langchain4j-reactor-->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-reactor</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.22</version>
</dependency>
- 配置 applcation.yaml / properties 配置文件,其中指明我们的输出响应的编码格式,因为如果不指定的话,存在返回的中文,就是乱码了。
properties
server.port=9006
spring.application.name=langchain4j-06chat-memory
# 设置响应的字符编码,避免流式返回输出乱码
server.servlet.encoding.charset=utf-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true

- 编写操作大模型的 Chan**Assistant 接口类**。

注意:需要加上@MemoryId 和 @UserMesage 两个注解 这个很好理解,你想让大模型记录到你的对话历史信息。你自然就一定要提供UserId用户ID,以及用户的历史记录信息了
java
package com.rainbowsea.langchain4j06chatmemory.service;
import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.UserMessage;
/**
*/
public interface ChatMemoryAssistant
{
/**
* 聊天带记忆缓存功能
*
* @param userId 用户 ID
* @param prompt 消息
* @return {@link String }
*/
String chatWithChatMemory(@MemoryId Long userId, @UserMessage String prompt);
}
- 编写大模型三件套(大模型 key,大模型 name,大模型 url) 三件套的大模型配置类。
注意:这里我们要记忆缓存的话,就需要用到我们的通义千问的长对话大模型了。



java
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.memory.chat.TokenWindowChatMemory;
import dev.langchain4j.model.TokenCountEstimator;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.openai.OpenAiTokenCountEstimator;
import dev.langchain4j.service.AiServices;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.rainbowsea.langchain4j06chatmemory.service.ChatMemoryAssistant;
/**
* @Description: 知识出处,https://docs.langchain4j.dev/tutorials/chat-memory/#eviction-policy
*/
@Configuration
public class LLMConfig
{
@Bean
public ChatModel chatModel()
{
return OpenAiChatModel.builder()
.apiKey(System.getenv("aliQwen_api")) //你自己配置到系统变量当中的值
.modelName("qwen-long")
.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
.build();
}
/**
* @Description: 按照MessageWindowChatMemory
*知识出处,https://docs.langchain4j.dev/tutorials/chat-memory/#eviction-policy
*/
@Bean(name = "chatMessageWindowChatMemory")
public ChatMemoryAssistant chatMessageWindowChatMemory(ChatModel chatModel)
{
return AiServices.builder(ChatMemoryAssistant.class)
.chatModel(chatModel)
//按照memoryId对应创建了一个chatMemory
.chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(100))
.build();
}
}
为了保证知识内容上的完整性,这里我也附上通过 token 数记录历史对话的代码

java
package com.rainbowsea.langchain4j06chatmemory.config;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.memory.chat.TokenWindowChatMemory;
import dev.langchain4j.model.TokenCountEstimator;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.openai.OpenAiTokenCountEstimator;
import dev.langchain4j.service.AiServices;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.rainbowsea.langchain4j06chatmemory.service.ChatMemoryAssistant;
/**
* @Description: 知识出处,https://docs.langchain4j.dev/tutorials/chat-memory/#eviction-policy
*/
@Configuration
public class LLMConfig
{
@Bean
public ChatModel chatModel()
{
return OpenAiChatModel.builder()
.apiKey(System.getenv("aliQwen_api")) //你自己配置到系统变量当中的值
.modelName("qwen-long")
.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
.build();
}
/**
* @Description: 按照 TokenWindowChatMemory,
* 知识出处,https://docs.langchain4j.dev/tutorials/chat-memory/#eviction-policy
*/
@Bean(name = "chatTokenWindowChatMemory")
public ChatMemoryAssistant chatTokenWindowChatMemory(ChatModel chatModel)
{
//1 TokenCountEstimator默认的token分词器,需要结合Tokenizer计算ChatMessage的token数量
TokenCountEstimator openAiTokenCountEstimator = new OpenAiTokenCountEstimator("gpt-4");
return AiServices.builder(ChatMemoryAssistant.class)
.chatModel(chatModel)
.chatMemoryProvider(memoryId -> TokenWindowChatMemory.withMaxTokens(1000,openAiTokenCountEstimator))
.build();
}
}
- 编写聊天记忆缓存调用的 cutroller

运行测试:

为了保证知识内容上的完整性,这里我也附上通过 token 数记录历史对话的代码
java
package com.rainbowsea.langchain4j06chatmemory.controller;
import cn.hutool.core.date.DateUtil;
import com.rainbowsea.langchain4j06chatmemory.service.ChatMemoryAssistant;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.model.chat.response.ChatResponse;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
/**
* @auther zzyybs@126.com
* @Date 2025-05-30 19:16
* @Description: TODO
*/
@RestController
@Slf4j
public class ChatMemoryController
{
@Resource(name = "chatTokenWindowChatMemory")
private ChatMemoryAssistant chatTokenWindowChatMemory;
/**
* @Description: TokenWindowChatMemory实现聊天功能
* @Auther: zzyybs@126.com
*/
@GetMapping(value = "/chatmemory/test3")
public String chatTokenWindowChatMemory()
{
chatTokenWindowChatMemory.chatWithChatMemory(1L, "你好!我的名字是mysql");
String answer01 = chatTokenWindowChatMemory.chatWithChatMemory(1L, "我的名字是什么");
System.out.println("answer01返回结果:"+answer01);
chatTokenWindowChatMemory.chatWithChatMemory(3L, "你好!我的名字是oracle");
String answer02 = chatTokenWindowChatMemory.chatWithChatMemory(3L, "我的名字是什么");
System.out.println("answer02返回结果:"+answer02);
return "chatTokenWindowChatMemory success : "
+ DateUtil.now()+"<br> \n\n answer01: "+answer01+"<br> \n\n answer02: "+answer02;
}
}
最后:
"在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。"