秋潭之水 發表於 2025-9-9 21:59:00

7. LangChain4j + 记忆缓存详细说明

<h1 id="7-langchain4j--记忆缓存详细说明">7. LangChain4j + 记忆缓存详细说明</h1>
<p>@</p><div class="toc"><div class="toc-container-header">目录</div><ul><li>7. LangChain4j + 记忆缓存详细说明<ul><li>LangChain4j + 记忆缓存实战操作</li></ul></li><li>最后:</li></ul></div><p></p>
<hr>
<ul>
<li>https://docs.langchain4j.dev/tutorials/chat-memory/</li>
</ul>
<p><img alt="" loading="lazy" src="https://img2024.cnblogs.com/blog/3084824/202509/3084824-20250909215828142-1863809969.png" class="lazyload"></p>
<p>记忆缓存是聊天系统中的一个重要组件,用于存储和管理对话的上下文信息。它的主要作用是让AI助手能够"记住"之前的对话内容,从而提供连贯和个性化的回复。</p>
<p>我们随便打开一个大模型对话聊天就明白了。</p>
<p><img alt="" loading="lazy" src="https://img2024.cnblogs.com/blog/3084824/202509/3084824-20250909215828158-1863972296.png" class="lazyload"></p>
<p><strong>简单的理解就是:一个让大模型可以记住我们上面一段历史内容上对它的提问,和索引需要的内容:</strong></p>
<p><img alt="" loading="lazy" src="https://img2024.cnblogs.com/blog/3084824/202509/3084824-20250909215828176-1195156660.png" class="lazyload"></p>
<p><img alt="" loading="lazy" src="https://img2024.cnblogs.com/blog/3084824/202509/3084824-20250909215828142-377169521.png" class="lazyload"></p>
<p>官方解释:<font style="color: rgba(28, 30, 33, 1)">LangChain4j提供了2种开箱即用的记忆缓存的实现方式如下:</font></p>
<ul>
<li>一种是:通过提问的条数,记忆几条提问记录信息。比如:记忆存储你提问的前 100 条的记录内容。推荐</li>
<li>第二种:则是通过记忆存储你的 token 数目,这种方式不推荐,因为不同的大模型的 token 计算的方式不同。</li>
</ul>
<p><img alt="" loading="lazy" src="https://img2024.cnblogs.com/blog/3084824/202509/3084824-20250909215828000-526650568.png" class="lazyload"></p>
<p><img alt="" loading="lazy" src="https://img2024.cnblogs.com/blog/3084824/202509/3084824-20250909215828121-1203356631.png" class="lazyload"></p>
<h2 id="langchain4j--记忆缓存实战操作">LangChain4j + 记忆缓存实战操作</h2>
<ol>
<li><strong>创建对应项目的 module 模块内容:</strong></li>
<li>导入相关的 pom.xml 的依赖,这里我们采用流式输出的方式,导入<code>langchain4j-open-ai + langchain4j + langchain4j-reactor </code>这三件必须存在,这里我们不指定版本,而是通过继承的 pom.xml 当中获取。_</li>
</ol>
<pre><code class="language-xml">&lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
      &lt;/dependency&gt;
      &lt;!--langchain4j-open-ai + langchain4j + langchain4j-reactor--&gt;
      &lt;dependency&gt;
            &lt;groupId&gt;dev.langchain4j&lt;/groupId&gt;
            &lt;artifactId&gt;langchain4j-open-ai&lt;/artifactId&gt;
      &lt;/dependency&gt;
      &lt;dependency&gt;
            &lt;groupId&gt;dev.langchain4j&lt;/groupId&gt;
            &lt;artifactId&gt;langchain4j&lt;/artifactId&gt;
      &lt;/dependency&gt;
      &lt;dependency&gt;
            &lt;groupId&gt;dev.langchain4j&lt;/groupId&gt;
            &lt;artifactId&gt;langchain4j-reactor&lt;/artifactId&gt;
      &lt;/dependency&gt;
      &lt;!--lombok--&gt;
      &lt;dependency&gt;
            &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
            &lt;artifactId&gt;lombok&lt;/artifactId&gt;
            &lt;optional&gt;true&lt;/optional&gt;
      &lt;/dependency&gt;
      &lt;!--hutool--&gt;
      &lt;dependency&gt;
            &lt;groupId&gt;cn.hutool&lt;/groupId&gt;
            &lt;artifactId&gt;hutool-all&lt;/artifactId&gt;
            &lt;version&gt;5.8.22&lt;/version&gt;
      &lt;/dependency&gt;
</code></pre>
<ol start="3">
<li><strong>配置 applcation.yaml / properties 配置文件,其中指明我们的输出响应的编码格式,因为如果不指定的话,存在返回的中文,就是乱码了。</strong></li>
</ol>
<pre><code class="language-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

</code></pre>
<p><img alt="" loading="lazy" src="https://img2024.cnblogs.com/blog/3084824/202509/3084824-20250909215828057-2055211341.png" class="lazyload"></p>
<ol start="4">
<li><strong>编写操作大模型的 Chan****<font style="color: rgba(8, 8, 8, 1); background-color: rgba(255, 255, 255, 1)">Assistant 接口类</font></strong><font style="color: rgba(8, 8, 8, 1); background-color: rgba(255, 255, 255, 1)">。</font></li>
</ol>
<p><img alt="" loading="lazy" src="https://img2024.cnblogs.com/blog/3084824/202509/3084824-20250909215828153-2007584024.png" class="lazyload"></p>
<blockquote>
<p><strong>注意:需要加上@MemoryId 和 @UserMesage 两个注解
这个很好理解,你想让大模型记录到你的对话历史信息。你自然就一定要提供UserId用户ID,以及用户的历史记录信息了</strong></p>
</blockquote>
<pre><code class="language-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);
}

</code></pre>
<ol start="5">
<li><strong>编写大模型三件套(大模型 key,大模型 name,大模型 url) 三件套的大模型配置类。</strong></li>
</ol>
<hr>
<p><strong>注意:这里我们要记忆缓存的话,就需要用到我们的通义千问的长对话大模型了。</strong></p>
<p><img alt="" loading="lazy" src="https://img2024.cnblogs.com/blog/3084824/202509/3084824-20250909215828154-1862653934.png" class="lazyload"></p>
<p><img alt="" loading="lazy" src="https://img2024.cnblogs.com/blog/3084824/202509/3084824-20250909215828183-1583262940.png" class="lazyload"></p>
<p><img alt="" loading="lazy" src="https://img2024.cnblogs.com/blog/3084824/202509/3084824-20250909215828079-1995214879.png" class="lazyload"></p>
<pre><code class="language-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 -&gt; MessageWindowChatMemory.withMaxMessages(100))
                .build();
    }

}
</code></pre>
<p>为了保证知识内容上的完整性,这里我也附上通过 token 数记录历史对话的代码</p>
<p><img alt="" loading="lazy" src="https://img2024.cnblogs.com/blog/3084824/202509/3084824-20250909215828149-1661818607.png" class="lazyload"></p>
<pre><code class="language-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 -&gt; TokenWindowChatMemory.withMaxTokens(1000,openAiTokenCountEstimator))
                .build();
    }
}

</code></pre>
<ol start="6">
<li><strong>编写聊天记忆缓存调用的 cutroller</strong></li>
</ol>
<p><img alt="" loading="lazy" src="https://img2024.cnblogs.com/blog/3084824/202509/3084824-20250909215828122-1403106536.png" class="lazyload"></p>
<p><strong>运行测试:</strong></p>
<p><img alt="" loading="lazy" src="https://img2024.cnblogs.com/blog/3084824/202509/3084824-20250909215828179-1247159475.png" class="lazyload"></p>
<p>为了保证知识内容上的完整性,这里我也附上通过 token 数记录历史对话的代码</p>
<pre><code class="language-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()+"&lt;br&gt; \n\n answer01: "+answer01+"&lt;br&gt; \n\n answer02: "+answer02;
    }
}

</code></pre>
<h1 id="最后">最后:</h1>
<blockquote>
<p>“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”</p>
<p><img alt="在这里插入图片描述" loading="lazy" src="https://img2024.cnblogs.com/blog/3084824/202509/3084824-20250909215828199-1322973247.gif" class="lazyload"></p>
</blockquote><br><br>
来源:https://www.cnblogs.com/TheMagicalRainbowSea/p/19082684
頁: [1]
查看完整版本: 7. LangChain4j + 记忆缓存详细说明