在这里插入图片描述

记忆缓存


记忆缓存是聊天系统中的一个重要组件,用于存储和管理对话的上下文信息。它的主要作用是让AI助手能够 “记住” 之前的对话内容,从而提供连贯和个性化的回复。

两种淘汰策略

  • MessageWindowChatMemory: 基于消息数量的简单实现,它采用滑动窗口的方式,保留最新的N条消息并淘汰旧消息。
  • TokenWindowChatMemory: 基于令牌数量限制,确保模型处理的上下文保持在指定范围内,需要结合TokenCountEstimator计算ChatMessage的token数量。

1. 引入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- LangChain4j原生 基础-->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-open-ai</artifactId>
    </dependency>
    <!-- LangChain4j原生 高阶-->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

2. yml配置

server:
  port: 9006

spring:
  application:
    name: langchain4j-memory

3. 主启动类

@SpringBootApplication
public class MemoryLangChain4jApp {

    public static void main(String[] args) {
        SpringApplication.run(MemoryLangChain4jApp.class, args);
    }
}

4. 自定义 ChatMemoryAssistant 接口

public interface ChatMemoryAssistant {

    /**
     * 聊天带记忆缓存
     * @param userId 用户ID
     * @param msg  消息
     * @return
     */
    String chatMemory(@MemoryId Long userId, @UserMessage String msg);
}

5. 配置类 LLMConfig

@Configuration
public class LLMConfig {

    @Bean(name = "qwen-long")
    public ChatModel chatModelQwen() {
        return OpenAiChatModel.builder()
                .apiKey(System.getenv("ALIYUN_KEY"))
                .modelName("qwen-long")
                .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
                .build();
    }

    // 按照 MessageWindowChatMemory 基于滑动窗口消息数量的淘汰策略
    @Bean(name = "messageWindowChatMemory")
    public ChatMemoryAssistant chatMessageWindowChatMemory(@Qualifier("qwen-long") ChatModel chatModelQwen) {
        return AiServices.builder(ChatMemoryAssistant.class)
                .chatModel(chatModelQwen)
                .chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(100))
                .build();
    }

    // 按照 TokenWindowChatMemory 基于令牌数量的淘汰策略
    @Bean(name = "tokenWindowChatMemory")
    public ChatMemoryAssistant chatTokenWindowChatMemory(@Qualifier("qwen-long") ChatModel chatModelQwen) {
        // 使用 TokenCountEstimator 默认的token分词器
        TokenCountEstimator tokenCountEstimator = new OpenAiTokenCountEstimator("gpt-4");
        return AiServices.builder(ChatMemoryAssistant.class)
                .chatModel(chatModelQwen)
                .chatMemoryProvider(memoryId -> TokenWindowChatMemory.withMaxTokens(1000, tokenCountEstimator))
                .build();
    }
}

6. 控制类 MemoryLangChain4jController

@RestController
public class MemoryLangChain4jController {

    @Resource(name = "messageWindowChatMemory")
    private ChatMemoryAssistant chatMessageWindowChatMemory;

    @Resource(name = "tokenWindowChatMemory")
    private ChatMemoryAssistant chatTokenWindowChatMemory;

    // MessageWindowChatMemory 实现聊天功能
    // http://localhost:9006/chatMemory/test1
    @GetMapping("/chatMemory/test1")
    public String chatMessageWindowChatMemory() {
        // 1号用户
        chatMessageWindowChatMemory.chatMemory(1L, "你好,我的名字叫jack");
        String result1 = chatMessageWindowChatMemory.chatMemory(1L, "我的名字叫什么");
        // 3号用户
        chatMessageWindowChatMemory.chatMemory(3L, "你好,我的名字叫tom");
        String result3 = chatMessageWindowChatMemory.chatMemory(3L, "我的名字叫什么");
        
        return "result1:" + result1 + "<br> result3:" + result3;
    }

    // TokenWindowChatMemory 实现聊天功能
    // http://localhost:9006/chatMemory/test2
    @GetMapping("/chatMemory/test2")
    public String chatTokenWindowChatMemory() {
        // 1号用户
        chatTokenWindowChatMemory.chatMemory(1L, "你好,我的名字叫java");
        String result1 = chatTokenWindowChatMemory.chatMemory(1L, "我的名字叫什么");
        // 3号用户
        chatTokenWindowChatMemory.chatMemory(3L, "你好,我的名字叫python");
        String result3 = chatTokenWindowChatMemory.chatMemory(3L, "我的名字叫什么");

        return "result1:" + result1 + "<br> result3:" + result3;
    }
}

7. 测试

访问 http://localhost:9006/chatMemory/test1
在这里插入图片描述
访问 http://localhost:9006/chatMemory/test2
在这里插入图片描述

持久化


1. 引入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- LangChain4j原生 基础-->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-open-ai</artifactId>
    </dependency>
    <!-- LangChain4j原生 高阶-->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j</artifactId>
    </dependency>
    <!-- SpringBoot-redis 记忆缓存持久化-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

2. yml配置

server:
  port: 9008

spring:
  application:
    name: langchain4j-memory
  # redis配置
  data:
    redis:
      host: 192.168.195.135
      port: 6379
      database: 0
      connect-timeout: 3s
      timeout: 2s

3. 主启动类

@SpringBootApplication
public class PersistenceLangChain4jApp {

    public static void main(String[] args) {
        SpringApplication.run(PersistenceLangChain4jApp.class, args);
    }
}

4. 自定义 ChatPersistenceAssistant 接口

public interface ChatPersistenceAssistant {

    /**
     * 聊天带记忆缓存
     * @param userId 用户ID
     * @param msg  消息
     * @return
     */
    String chat(@MemoryId Long userId, @UserMessage String msg);
}

5. 配置类

RedisTemplate配置类

@Configuration
public class RedisConfig
{
    /**
     * RedisTemplate配置
     * redis序列化的工具配置类,下面这个请一定开启配置
     * 127.0.0.1:6379> keys *
     * 1) "ord:102"  序列化过
     * 2) "\xac\xed\x00\x05t\x00\aord:102"   野生,没有序列化过
     * this.redisTemplate.opsForValue(); //提供了操作string类型的所有方法
     * this.redisTemplate.opsForList(); // 提供了操作list类型的所有方法
     * this.redisTemplate.opsForSet(); //提供了操作set的所有方法
     * this.redisTemplate.opsForHash(); //提供了操作hash表的所有方法
     * this.redisTemplate.opsForZSet(); //提供了操作zset的所有方法
     * @param redisConnectionFactor
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactor)
    {
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();

        redisTemplate.setConnectionFactory(redisConnectionFactor);
        //设置key序列化方式string
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        //设置value的序列化方式json,使用GenericJackson2JsonRedisSerializer替换默认序列化
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }
}

RedisChatMemoryStore配置类,实现ChatMemoryStore接口

@Component
public class RedisChatMemoryStore implements ChatMemoryStore {

    public static final String CHAT_MEMORY_PREFIX = "CHAT_MEMORY:";

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public List<ChatMessage> getMessages(Object memoryId) {
        String value = redisTemplate.opsForValue().get(CHAT_MEMORY_PREFIX + memoryId);
        return ChatMessageDeserializer.messagesFromJson(value);
    }

    @Override
    public void updateMessages(Object memoryId, List<ChatMessage> messages) {
        redisTemplate.opsForValue().set(CHAT_MEMORY_PREFIX + memoryId, ChatMessageSerializer.messagesToJson(messages));
    }

    @Override
    public void deleteMessages(Object memoryId) {
        redisTemplate.delete(CHAT_MEMORY_PREFIX + memoryId);

    }
}

大模型配置类LLMConfig

@Configuration
public class LLMConfig {

    @Resource
    private RedisChatMemoryStore redisChatMemoryStore;

    @Bean(name = "qwen")
    public ChatModel chatModelQwen() {
        return OpenAiChatModel.builder()
                .apiKey(System.getenv("ALIYUN_KEY"))
                .modelName("qwen-plus")
                .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
                .build();
    }

    @Bean
    public ChatPersistenceAssistant chatPersistenceAssistant(@Qualifier("qwen") ChatModel chatModelQwen) {
        ChatMemoryProvider chatMemoryProvider = memoryId -> MessageWindowChatMemory.builder()
                .id(memoryId)
                .maxMessages(1000)
                .chatMemoryStore(redisChatMemoryStore)
                .build();

        return AiServices.builder(ChatPersistenceAssistant.class)
                .chatModel(chatModelQwen)
                .chatMemoryProvider(chatMemoryProvider)
                .build();
    }
}

6. 控制类 PersistenceLangChain4jController

@RestController
public class PersistenceLangChain4jController {

    @Resource
    private ChatPersistenceAssistant chatPersistenceAssistant;

    // http://localhost:9008/chatPersistence/redis/chat
    @GetMapping("/chatPersistence/redis/chat")
    public String chat() {
        chatPersistenceAssistant.chat(1L, "你好,我叫redis");
        chatPersistenceAssistant.chat(2L, "你好,我的名字叫mysql");
        String result1 = chatPersistenceAssistant.chat(1L, "我的名字叫什么");
        String result2 = chatPersistenceAssistant.chat(2L, "我的名字叫什么");
        return "result1:" + result1 + "<br> result2:" + result2;
    }
}

7. 测试

访问 http://localhost:9008/chatPersistence/redis/chat
在这里插入图片描述
连接上redis,我们看到对话信息已经存储到redis中
注意:这里连接redis客户端,一定要加 --raw,否则中文乱码
在这里插入图片描述

总结


以上主要介绍了 LangChain4j 记忆缓存、持久化相关知识,想了解更多 LangChain4j 知识的小伙伴请参考 LangChain4j 官网 进行学习,学习更多 LangChain4j 实战实用技巧的小伙伴,请关注后期发布的文章,认真看完一定能让你有所收获。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐