10. LangChain4j + 持久化实操详细说明
本文介绍了如何使用LangChain4j框架实现对话系统的持久化存储功能。通过集成Redis数据库,将用户与大模型的对话记录进行持久化保存。文章详细说明了实现步骤:1)创建项目模块并导入必要的依赖包;2)配置application.properties文件设置Redis连接参数;3)定义高阶接口ChatPersistenceAssistant处理对话逻辑;4)编写Redis序列化配置类防止数据乱码
·
10. LangChain4j + 持久化实操详细说明
**实操:**将客户和大模型的对话问答保存进Redis进行持久化记忆留存
- 创建对应项目的 module 模块内容:
- 导入相关的 pom.xml 的依赖,这里我们采用流式输出的方式,导入langchain4j-open-ai + langchain4j + langchain4j-reactor 这三件必须存在,持久化存储就需要导入`spring-boot-starter-data-redis Redis 的依赖,来存储我们的用户对话信息。 这里我们不指定版本,而是通过继承的 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>
<!--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>
- 设置 applcation.yaml / properties 配置文件,其中指明我们的输出响应的编码格式,因为如果不指定的话,存在返回的中文,就是乱码了。同时加上配置我们的 Redis 的配置。
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
- 新建高阶接口ChatPersistenceAssistant,我们操作大模型对话的接口类
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);
}
- 编写 Redis 的序列化的配置类,防止存储时数据的序列化,反序列乱码
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) "\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;
}
}
- 想要实现 LangChain4j 的持久化,就需要实现 ChatMemoryStore 接口,同时重写其中的三个人方法,通过这三个方法操作我们的 Redis ,让数据存储到 Redis 当中。
注意:必须要加入到 IOC容器当中管理,官方漏了这一点
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);
}
}
- 编写大模型三件套(大模型 key,大模型 name,大模型 url) 三件套的大模型配置类,同时将我们的编写的 ChatMemoryStore 实现类,配置加入到我们操作接口类的实现类当中,进行持久化
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 zzyybs@126.com
* @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();
}
}
- 编写对外访问的 cutroller 层
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
最后:
“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”
更多推荐
所有评论(0)