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();
相关推荐
问心无愧05131 小时前
CTF show web入门45
android·前端·笔记
nnsix1 小时前
设计模式 - 单例模式 笔记
笔记·单例模式·设计模式
AOwhisky1 小时前
Docker 学习笔记:网络篇
linux·运维·网络·笔记·学习·docker·容器
24白菜头1 小时前
MySQL学习笔记
数据库·笔记·学习·mysql
问心无愧05132 小时前
ctf show web入门54
前端·笔记
小陈phd2 小时前
多模态大模型学习笔记(三十九)——生成式与Transformer式OCR:从“像素抄录“到“文档智能“的完整演进
笔记·学习·transformer
泡泡以安2 小时前
Unidbg学习笔记(一):为什么需要用户态模拟器
笔记·学习
暖馒2 小时前
WPF绑定由简到繁深入笔记
笔记·wpf
问心无愧05132 小时前
ctf show web 入门46
android·前端·笔记