LangChain4j AiServices 实现聊天记忆

ChatMemory

上次说到ChatMessage手动的维护和管理是比较麻烦的,因此LangChain4j提出了ChatMemory的概念。它的本质是ChatMessage的容器。

可以看下ChatMemory这个接口的方法,它内部封装了一个List的ChatMessage。

要注意的是:

  • LLM的上下文窗口都是有一定限制的,针对不同的LLM可能限制的令牌数量有所不同,这意味着他们在任何给定时间可以处理的令牌数量都有上限,这可能会导致整个窗口对话超出限制。
  • 每个令牌都有成本,所以每次调用LLM的成本会逐渐增加。

    通过ChatMemory可以结合ApiServices组件可以实现聊天记忆功能。

    以下是官方提供的几个示例:

    简单的聊天记忆

    package com.chatglm.demo;
    import dev.langchain4j.memory.ChatMemory;
    import dev.langchain4j.memory.chat.MessageWindowChatMemory;
    import dev.langchain4j.model.openai.OpenAiChatModel;
    import dev.langchain4j.service.AiServices;
    /**
     * 聊天记忆
     */
    public class ServiceWithMemoryExample {
        /**
         * 定义一个带有单个方法的接口
         */
        interface Assistant {
            String chat(String message);
        }
        public static void main(String[] args) {
            // 创建ChatLanguageModel组件作为AiServices的基础组件
            OpenAiChatModel openAiChatModel = OpenAiChatModel.withApiKey("demo");
            // 创建MessageWindowChatMemory充当滑动窗口
            ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
            // 利用Apiservices组件实现Assistant接口并接入chatMemory
            Assistant assistant = AiServices.builder(Assistant.class)
                    .chatLanguageModel(openAiChatModel)
                    .chatMemory(chatMemory)
                    .build();
            String answer = assistant.chat("你好,我叫小A.");
            System.out.println(answer);
            String answerWithName = assistant.chat("我叫什么名字?");
            System.out.println(answerWithName); 
        }
    }

    AI回答:

    多用户聊天记忆

    package com.chatglm.demo;
    import dev.langchain4j.memory.chat.MessageWindowChatMemory;
    import dev.langchain4j.model.openai.OpenAiChatModel;
    import dev.langchain4j.service.AiServices;
    import dev.langchain4j.service.MemoryId;
    import dev.langchain4j.service.UserMessage;
    /**
     * 每个用户单独的聊天内存
     */
    public class ServiceWithMemoryForEachUserExample {
        interface Assistant {
            String chat(@MemoryId int memoryId, @UserMessage String userMessage);
        }
        public static void main(String[] args) {
            Assistant assistant = AiServices.builder(Assistant.class)
                    .chatLanguageModel(OpenAiChatModel.withApiKey(""))
                    .chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(10))
                    .build();
            System.out.println(assistant.chat(1, "Hello, my name is Klaus"));
            // Hi Klaus! How can I assist you today?
            System.out.println(assistant.chat(2, "Hello, my name is Francine"));
            // Hello Francine! How can I assist you today?
            System.out.println(assistant.chat(1, "What is my name?"));
            // Your name is Klaus.
            System.out.println(assistant.chat(2, "What is my name?"));
            // Your name is Francine.
        }
    }

    聊天记忆持久化

    依赖:

      org.mapdb mapdb 3.0.9   org.jetbrains.kotlin kotlin-stdlib   
    package com.chatglm.demo;
    import dev.langchain4j.data.message.ChatMessage;
    import dev.langchain4j.memory.ChatMemory;
    import dev.langchain4j.memory.chat.MessageWindowChatMemory;
    import dev.langchain4j.model.openai.OpenAiChatModel;
    import dev.langchain4j.service.AiServices;
    import dev.langchain4j.store.memory.chat.ChatMemoryStore;
    import org.mapdb.DB;
    import org.mapdb.DBMaker;
    import java.util.List;
    import java.util.Map;
    import static dev.langchain4j.data.message.ChatMessageDeserializer.messagesFromJson;
    import static dev.langchain4j.data.message.ChatMessageSerializer.messagesToJson;
    import static org.mapdb.Serializer.STRING;
    /**
     * 持久的聊天记忆
     */
    public class ServiceWithPersistentMemoryExample {
        interface Assistant {
            String chat(String message);
        }
        public static void main(String[] args) {
            ChatMemory chatMemory = MessageWindowChatMemory.builder()
                    .maxMessages(10)
                    .chatMemoryStore(new PersistentChatMemoryStore())
                    .build();
            Assistant assistant = AiServices.builder(Assistant.class)
                    .chatLanguageModel(OpenAiChatModel.withApiKey("demo"))
                    .chatMemory(chatMemory)
                    .build();
            String answer = assistant.chat("你是什么智能");
            System.out.println(answer); // Hello Klaus! How can I assist you today?
            // Now, comment out the two lines above, uncomment the two lines below, and run again.
            // String answerWithName = assistant.chat("What is my name?");
            // System.out.println(answerWithName); // Your name is Klaus.
        }
        // You can create your own implementation of ChatMemoryStore and store chat memory whenever you'd like
        static class PersistentChatMemoryStore implements ChatMemoryStore {
            private final DB db = DBMaker.fileDB("chat-memory.db").transactionEnable().make();
            private final Map map = db.hashMap("messages", STRING, STRING).createOrOpen();
            @Override
            public List getMessages(Object memoryId) {
                String json = map.get((String) memoryId);
                return messagesFromJson(json);
            }
            @Override
            public void updateMessages(Object memoryId, List messages) {
                String json = messagesToJson(messages);
                map.put((String) memoryId, json);
                db.commit();
            }
            @Override
            public void deleteMessages(Object memoryId) {
                map.remove((String) memoryId);
                db.commit();
            }
        }
    }

    多用户聊天记忆持久化

    package com.chatglm.demo;
    import dev.langchain4j.data.message.ChatMessage;
    import dev.langchain4j.memory.chat.ChatMemoryProvider;
    import dev.langchain4j.memory.chat.MessageWindowChatMemory;
    import dev.langchain4j.model.openai.OpenAiChatModel;
    import dev.langchain4j.service.AiServices;
    import dev.langchain4j.service.MemoryId;
    import dev.langchain4j.service.UserMessage;
    import dev.langchain4j.store.memory.chat.ChatMemoryStore;
    import org.mapdb.DB;
    import org.mapdb.DBMaker;
    import java.util.List;
    import java.util.Map;
    import static dev.langchain4j.data.message.ChatMessageDeserializer.messagesFromJson;
    import static dev.langchain4j.data.message.ChatMessageSerializer.messagesToJson;
    import static org.mapdb.Serializer.INTEGER;
    import static org.mapdb.Serializer.STRING;
    /**
     * 每个用户持久的聊天记忆
     */
    public class ServiceWithPersistentMemoryForEachUserExample {
        interface Assistant {
            String chat(@MemoryId int memoryId, @UserMessage String userMessage);
        }
        public static void main(String[] args) {
            PersistentChatMemoryStore store = new PersistentChatMemoryStore();
            ChatMemoryProvider chatMemoryProvider = memoryId -> MessageWindowChatMemory.builder()
                    .id(memoryId)
                    .maxMessages(10)
                    .chatMemoryStore(store)
                    .build();
            Assistant assistant = AiServices.builder(Assistant.class)
                    .chatLanguageModel(OpenAiChatModel.withApiKey("demo"))
                    .chatMemoryProvider(chatMemoryProvider)
                    .build();
            System.out.println(assistant.chat(1, "Hello, my name is Klaus"));
            System.out.println(assistant.chat(2, "Hi, my name is Francine"));
            // Now, comment out the two lines above, uncomment the two lines below, and run again.
            // System.out.println(assistant.chat(1, "What is my name?"));
            // System.out.println(assistant.chat(2, "What is my name?"));
        }
        // You can create your own implementation of ChatMemoryStore and store chat memory whenever you'd like
        static class PersistentChatMemoryStore implements ChatMemoryStore {
            private final DB db = DBMaker.fileDB("multi-user-chat-memory.db").transactionEnable().make();
            private final Map map = db.hashMap("messages", INTEGER, STRING).createOrOpen();
            @Override
            public List getMessages(Object memoryId) {
                String json = map.get((int) memoryId);
                return messagesFromJson(json);
            }
            @Override
            public void updateMessages(Object memoryId, List messages) {
                String json = messagesToJson(messages);
                map.put((int) memoryId, json);
                db.commit();
            }
            @Override
            public void deleteMessages(Object memoryId) {
                map.remove((int) memoryId);
                db.commit();
            }
        }
    }