langchain4j笔记-03

Chat Memory

自定义实现多轮对话

java 复制代码
    public static void main(String[] args) {

        List<ChatMessage> chatMessageList = new ArrayList<>();

        List<String> questions = new ArrayList<>();
        questions.add("北京大学是211么?");
        questions.add("是985么?");
        questions.add("是双一流么?");

        for (String userMessage : questions) {
            SystemMessage systemMessage = SystemMessage.from("你是一个智能助手!");
            if (!chatMessageList.isEmpty()) {
                ChatMessage chatMessage = chatMessageList.get(0);
                if (chatMessage instanceof SystemMessage) {
                    
                } else {
                    chatMessageList.add(systemMessage);
                }
            } else {
                chatMessageList.add(systemMessage);
            }

            chatMessageList.add(UserMessage.from(userMessage));
            ChatRequest request = ChatRequest.builder().messages(chatMessageList).build();
            ChatRequestOptions options = ChatRequestOptions.EMPTY;
            ChatResponse chat = BASE_MODEL.chat(request, options);
            AiMessage aiMessage = chat.aiMessage();
            chatMessageList.add(aiMessage);
            System.out.println(aiMessage.text());
        }
        
    }

以上有个问题,所有的对话都保存在一个集合里面,如何解决,为每个对话分配一个List集合

ChatMemory

java 复制代码
    public static void main(String[] args) {

        ChatMemory chatMemory = MessageWindowChatMemory.builder().maxMessages(10).build();

        List<String> questions = new ArrayList<>();
        questions.add("北京大学是211么?");
        questions.add("是985么?");
        questions.add("是双一流么?");

        for (String userMessage : questions) {
            SystemMessage systemMessage = SystemMessage.from("你是一个智能助手!");
            chatMemory.add(systemMessage);
            chatMemory.add(UserMessage.from(userMessage));
            ChatRequest request = ChatRequest.builder().messages(chatMemory.messages()).build();
            ChatRequestOptions options = ChatRequestOptions.EMPTY;
            ChatResponse chat = BASE_MODEL.chat(request, options);
            AiMessage aiMessage = chat.aiMessage();
            chatMemory.add(aiMessage);
            System.out.println(aiMessage.text());
        }

    }

追踪源码发现:MessageWindowChatMemory 底层提供了 chatMemoryStore(默认是:SingleSlotChatMemoryStore),里面维护的就是一个List。

使用系统的 InMemoryChatMemoryStore

java 复制代码
ChatMemory chatMemory = MessageWindowChatMemory.builder()
                .chatMemoryStore(new InMemoryChatMemoryStore())
                .maxMessages(10).build();

分析 InMemoryChatMemoryStore 的源码

java 复制代码
public class InMemoryChatMemoryStore implements ChatMemoryStore {

    private final Map<Object, List<ChatMessage>> messagesByMemoryId = new ConcurrentHashMap<>();

    /**
     * Constructs a new {@link InMemoryChatMemoryStore}.
     */
    public InMemoryChatMemoryStore() {}

    @Override
    public List<ChatMessage> getMessages(Object memoryId) {
        return messagesByMemoryId.computeIfAbsent(memoryId, ignored -> new ArrayList<>());
    }

    @Override
    public void updateMessages(Object memoryId, List<ChatMessage> messages) {
        messagesByMemoryId.put(memoryId, messages);
    }

    @Override
    public void deleteMessages(Object memoryId) {
        messagesByMemoryId.remove(memoryId);
    }
}

自定义 chatMemoryStore ,这里使用redis

具体支持参考:

引入redis依赖:

xml 复制代码
<!-- Source: https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>7.5.0</version>
    <scope>compile</scope>
</dependency>

使用redis来存储:

java 复制代码
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 redis.clients.jedis.*;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import static dev.langchain4j.internal.ValidationUtils.*;

public class RedisChatMemoryStore implements ChatMemoryStore {

    /**
     * Redis client for database operations.
     */
    private final RedisClient client;

    /**
     * Prefix to be added to all Redis keys.
     */
    private final String keyPrefix;

    /**
     * Time-to-live value for Redis keys in seconds.
     * Keys will automatically expire after this duration.
     * A value of 0 or less means keys will not expire.
     */
    private final Long ttl;

    
    public RedisChatMemoryStore(String host, Integer port,Integer database, String user, String password) {
        this(host, port, database, user, password, "", 0L);
    }
    
    public RedisChatMemoryStore(
            String host, Integer port, Integer database, String user, String password, String prefix, Long ttl) {
        String rawPassword = password;
        String encodedPassword = URLEncoder.encode(rawPassword, StandardCharsets.UTF_8);
        this.client = RedisClient.create("redis://:"+ encodedPassword +"@192.168.6.15:6379");
        this.keyPrefix = ensureNotNull(prefix, "prefix");
        this.ttl = ensureNotNull(ttl, "ttl");
    }
    

    @Override
    public List<ChatMessage> getMessages(Object memoryId) {
        String json = client.get(toRedisKey(memoryId));
        List<ChatMessage> chatMessages = ChatMessageDeserializer.messagesFromJson(json);
        return chatMessages;
    }
    
    @Override
    public void updateMessages(Object memoryId, List<ChatMessage> messages) {
        String json = ChatMessageSerializer.messagesToJson(ensureNotEmpty(messages, "messages"));
        client.set(toRedisKey(memoryId), json);
    }

  
    @Override
    public void deleteMessages(Object memoryId) {
        client.del(toRedisKey(memoryId));
    }
  
    private String toMemoryIdString(Object memoryId) {
        boolean isNullOrEmpty = memoryId == null || memoryId.toString().trim().isEmpty();
        if (isNullOrEmpty) {
            throw new IllegalArgumentException("memoryId cannot be null or empty");
        }
        return memoryId.toString();
    }
    
    private String toRedisKey(Object memoryId) {
        return keyPrefix + toMemoryIdString(memoryId);
    }

}

测试

java 复制代码
    public static void main(String[] args) {
        RedisChatMemoryStore chatMemoryStore = new RedisChatMemoryStore("192.168.6.15:6379", 6349, 11, userId, "redis@2025");
        ChatMemory chatMemory = MessageWindowChatMemory.builder()
                .chatMemoryStore(chatMemoryStore)
                .maxMessages(10).build();
        List<String> questions = new ArrayList<>();
        questions.add("北京大学是211么?");
        questions.add("是985么?");
        questions.add("是双一流么?");

        for (String userMessage : questions) {
            SystemMessage systemMessage = SystemMessage.from("你是一个智能助手!");
            chatMemory.add(systemMessage);
            chatMemory.add(UserMessage.from(userMessage));
            ChatRequest request = ChatRequest.builder().messages(chatMemory.messages()).build();
            ChatRequestOptions options = ChatRequestOptions.EMPTY;
            ChatResponse chat = BASE_MODEL.chat(request, options);
            AiMessage aiMessage = chat.aiMessage();
            chatMemory.add(aiMessage);
            System.out.println(aiMessage.text());
        }
    }

解释: chatMemoryId, 默认值:default

java 复制代码
    @Test
    void test2() {
        RedisChatMemoryStore chatMemoryStore = new RedisChatMemoryStore("192.168.6.15:6379", 6349, 11, userId, "redis@2025");
        ChatMemory chatMemory = MessageWindowChatMemory.builder()
                //.id(chatMemoryId)
                .chatMemoryStore(chatMemoryStore)
                .maxMessages(10).build();
        chatMemory.add(UserMessage.from("帮我总结刚才的问题"));
        ChatRequest request = ChatRequest.builder().messages(chatMemory.messages()).build();
        ChatRequestOptions options = ChatRequestOptions.EMPTY;
        ChatResponse chat = BASE_MODEL.chat(request, options);
        AiMessage aiMessage = chat.aiMessage();
        chatMemory.add(aiMessage);
        System.out.println(aiMessage.text());
    }

结果是:获取到了另一个的会话内容,如何区分开?

chatMemoryId 应该设计为:userid + 会话id + 其他的id + 时间戳等。

java 复制代码
String chatMemoryId = userId + UUID.randomUUID().toString() + System.currentTimeMillis();
ChatMemory chatMemory = MessageWindowChatMemory.builder()
        .id(chatMemoryId)
        .chatMemoryStore(chatMemoryStore)
        .maxMessages(10).build();
相关推荐
sulikey6 小时前
个人Linux操作系统学习笔记6 - 操作系统与进程初识
linux·笔记·学习·操作系统·进程
XGeFei7 小时前
【Fastapi学习笔记(3)】——资源的层级关系、安全性-幂等性、Field、工厂函数
笔记·学习·fastapi
星恒随风8 小时前
Python 基础语法详解(一):从表达式、变量到数据类型
开发语言·笔记·python·学习
暴躁小师兄数据学院9 小时前
【AI大数据工程师特训笔记】第14讲:Linux操作系统与shell脚本
大数据·人工智能·笔记
土狗TuGou10 小时前
SQL内功笔记 · 第8篇:事务的四大特性与隔离级别
数据库·笔记·后端·sql·mysql·oracle
智者知已应修善业10 小时前
【51单片机用T0定时器方式1,实现0.5S的时间间隔实现第一次一个灯亮、第二次二个灯亮,直到全部灯亮,然后重复整个过程】2023-12-29
c++·经验分享·笔记·算法·51单片机
智者知已应修善业11 小时前
【51单片机4位静态数码管显示1234】2023-11-14
c++·经验分享·笔记·算法·51单片机
whyTeaFo11 小时前
MIT6.1810: xv6 book Chapter4: Traps and system calls 笔记
笔记
jimbo_lee12 小时前
yocto 用法(随手笔记,记录以备不时之需)
笔记·yocto
胡图图不糊涂^_^14 小时前
测试用例篇——设计测试用例的方法
笔记·学习·测试用例·判定表法·正交法生成用例测试·等价类·边界值