11. LangChain4j + Tools(Function Calling)的使用详细说明

10. LangChain4j + 持久化实操详细说明

@[toc]

**实操:**将客户和大模型的对话问答保存进Redis进行持久化记忆留存

  1. 创建对应项目的 module 模块内容:
  2. 导入相关的 pom.xml 的依赖,这里我们采用流式输出的方式,导入langchain4j-open-ai + langchain4j + langchain4j-reactor 这三件必须存在,持久化存储就需要导入`spring-boot-starter-data-redis Redis 的依赖,来存储我们的用户对话信息。 这里我们不指定版本,而是通过继承的 pom.xml 当中获取。
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>
  1. 设置 applcation.yaml / properties 配置文件,其中指明我们的输出响应的编码格式,因为如果不指定的话,存在返回的中文,就是乱码了。同时加上配置我们的 Redis 的配置。
properties 复制代码
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
  1. 新建高阶接口ChatPersistenceAssistant,我们操作大模型对话的接口类
java 复制代码
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);
}
  1. 编写 Redis 的序列化的配置类,防止存储时数据的序列化,反序列乱码
java 复制代码
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;
    }
}
  1. 想要实现 LangChain4j 的持久化,就需要实现 ChatMemoryStore 接口,同时重写其中的三个人方法,通过这三个方法操作我们的 Redis ,让数据存储到 Redis 当中。

注意:必须要加入到 IOC容器当中管理,官方漏了这一点

java 复制代码
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);
    }
}
  1. 编写大模型三件套(大模型 key,大模型 name,大模型 url) 三件套的大模型配置类,同时将我们的编写的 ChatMemoryStore 实现类,配置加入到我们操作接口类的实现类当中,进行持久化
java 复制代码
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();
    }

}
  1. 编写对外访问的 cutroller 层
java 复制代码
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();
    }

}
  1. 运行测试:

如果使用的是命令行的方式查看 Redis 使用如下,命令启动 Redis ,就不会出现中文乱码了

bash 复制代码
redis-cli -p 6379 -raw

最后:

"在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。"

相关推荐
玩毛线的包子1 分钟前
Android Gradle学习(十二)- ASM对于字节码的增删改查
java
玲小珑16 分钟前
LangChain.js 完全开发手册(十三)AI Agent 生态系统与工具集成
前端·langchain·ai编程
晓宜32 分钟前
Java25 新特性介绍
java·python·算法
Seven9744 分钟前
SpringIOC、DI及Bean线程安全面试题解析
java
TitosZhang1 小时前
BIO、NIO、AIO详解
java·redis·nio
Arva .1 小时前
Spring Boot 配置文件
java·spring boot·后端
IT_Octopus1 小时前
https私人证书 PKIX path building failed 报错解决
java·spring boot·网络协议·https
程序员清风1 小时前
网易三面:Java中默认使用的垃圾回收器及特点分版本说说?
java·后端·面试
这周也會开心1 小时前
本地部署javaweb项目到Tomcat的三种方法
java·tomcat
飞哥数智坊2 小时前
AI 写代码总跑偏?试试费曼学习法:让它先复述一遍!
人工智能·ai编程