保卫萝卜2挑战模式
140.17MB · 2025-10-01
@[toc]
**实操:**将客户和大模型的对话问答保存进Redis进行持久化记忆留存
<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>
<!--spring-boot-starter-data-redis
https://docs.langchain4j.dev/tutorials/chat-memory#persistence
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</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>
server.port=9009
spring.application.name=langchain4j-09chat-persistence
# 设置响应的字符编码
server.servlet.encoding.charset=utf-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
# ==========config redis===============
spring.data.redis.host=localhost
spring.data.redis.port=6379
spring.data.redis.database=0
spring.data.redis.connect-timeout=3s
spring.data.redis.timeout=2s
package com.rainbowsea.langchain4j09chatpersistence.service;
import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.UserMessage;
/**
*/
public interface ChatPersistenceAssistant
{
/**
* 聊天
*
* @param userId 用户 ID
* @param message 消息
* @return {@link String }
*/
String chat(@MemoryId Long userId, @UserMessage String message);
}
package com.rainbowsea.langchain4j09chatpersistence.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
*/
@Configuration
@Slf4j
public class RedisConfig
{
/**
* RedisTemplate配置
* redis序列化的工具配置类,下面这个请一定开启配置
* 127.0.0.1:6379> keys *
* 1) "ord:102" 序列化过
* 2) "xacxedx00x05tx00aord: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;
}
}
package com.rainbowsea.langchain4j09chatpersistence.config;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.ChatMessageDeserializer;
import dev.langchain4j.data.message.ChatMessageSerializer;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @Description: https://docs.langchain4j.dev/tutorials/chat-memory#persistence
*/
@Component
public class RedisChatMemoryStore implements ChatMemoryStore
{
// 存储到 Redis 的 key的一个标识前缀
public static final String CHAT_MEMORY_PREFIX = "CHAT_MEMORY:";
@Resource
private RedisTemplate<String,String> redisTemplate;
@Override
public List<ChatMessage> getMessages(Object memoryId)
{
// 从 Redis 当中获取数据
String retValue = redisTemplate.opsForValue().get(CHAT_MEMORY_PREFIX + memoryId);
//将 JSON 格式的内容转换为 对象
return ChatMessageDeserializer.messagesFromJson(retValue);
}
@Override
public void updateMessages(Object memoryId, List<ChatMessage> messages)
{
// 添加数据到 Redis 当中 messagesToJson将信息已JSON格式存储
redisTemplate.opsForValue()
.set(CHAT_MEMORY_PREFIX + memoryId, ChatMessageSerializer.messagesToJson(messages));
}
@Override
public void deleteMessages(Object memoryId)
{
// 删除 Redis 数据
redisTemplate.delete(CHAT_MEMORY_PREFIX + memoryId);
}
}
package com.rainbowsea.langchain4j09chatpersistence.config;
import com.rainbowsea.langchain4j09chatpersistence.service.ChatPersistenceAssistant;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @auther [email protected]
* @Date 2025-06-02 16:08
* @Description: TODO
*/
@Configuration
public class LLMConfig
{
@Resource
private RedisChatMemoryStore redisChatMemoryStore;
@Bean
public ChatModel chatModel()
{
return OpenAiChatModel.builder()
.apiKey(System.getenv("aliQwen_api"))
.modelName("qwen-plus")
.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
.build();
}
@Bean
public ChatPersistenceAssistant chatMemoryAssistant(ChatModel chatModel)
{
ChatMemoryProvider chatMemoryProvider = memoryId -> MessageWindowChatMemory.builder()
.id(memoryId)
.maxMessages(1000)
.chatMemoryStore(redisChatMemoryStore)
.build();
return AiServices.builder(ChatPersistenceAssistant.class)
.chatModel(chatModel)
.chatMemoryProvider(chatMemoryProvider)
.build();
}
}
package com.rainbowsea.langchain4j09chatpersistence.controller;
import cn.hutool.core.date.DateUtil;;
import com.rainbowsea.langchain4j09chatpersistence.service.ChatPersistenceAssistant;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
*/
@RestController
@Slf4j
public class ChatPersistenceController
{
@Resource
private ChatPersistenceAssistant chatPersistenceAssistant;
// http://localhost:9009/chatpersistence/redis
@GetMapping(value = "/chatpersistence/redis")
public String testChatPersistence()
{
chatPersistenceAssistant.chat(1L, "你好!我的名字是redis");
chatPersistenceAssistant.chat(2L, "你好!我的名字是nacos");
String chat = chatPersistenceAssistant.chat(1L, "我的名字是什么");
System.out.println(chat);
chat = chatPersistenceAssistant.chat(2L, "我的名字是什么");
System.out.println(chat);
return "testChatPersistence success : "+ DateUtil.now();
}
}
如果使用的是命令行的方式查看 Redis 使用如下,命令启动 Redis ,就不会出现中文乱码了
redis-cli -p 6379 -raw