7. LangChain4j + 記憶緩存詳細說明
7. LangChain4j + 記憶緩存詳細說明
@

記憶緩存是聊天系統中的一個重要組件,用于存儲和管理對話的上下文信息。它的主要作用是讓AI助手能夠"記住"之前的對話內容,從而提供連貫和個性化的回復。
我們隨便打開一個大模型對話聊天就明白了。

簡單的理解就是:一個讓大模型可以記住我們上面一段歷史內容上對它的提問,和索引需要的內容:


官方解釋:LangChain4j提供了2種開箱即用的記憶緩存的實現方式如下:
- 一種是:通過提問的條數,記憶幾條提問記錄信息。比如:記憶存儲你提問的前 100 條的記錄內容。推薦
- 第二種:則是通過記憶存儲你的 token 數目,這種方式不推薦,因為不同的大模型的 token 計算的方式不同。


LangChain4j + 記憶緩存實戰操作
- 創建對應項目的 module 模塊內容:
- 導入相關的 pom.xml 的依賴,這里我們采用流式輸出的方式,導入
langchain4j-open-ai + langchain4j + langchain4j-reactor這三件必須存在,這里我們不指定版本,而是通過繼承的 pom.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 配置文件,其中指明我們的輸出響應的編碼格式,因為如果不指定的話,存在返回的中文,就是亂碼了。
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,以及用戶的歷史記錄信息了
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) 三件套的大模型配置類。
注意:這里我們要記憶緩存的話,就需要用到我們的通義千問的長對話大模型了。



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 數記錄歷史對話的代碼

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 數記錄歷史對話的代碼
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;
}
}
最后:
“在這個最后的篇章中,我要表達我對每一位讀者的感激之情。你們的關注和回復是我創作的動力源泉,我從你們身上吸取了無盡的靈感與勇氣。我會將你們的鼓勵留在心底,繼續在其他的領域奮斗。感謝你們,我們總會在某個時刻再次相遇。”


浙公網安備 33010602011771號