02、聊天会话记忆ChatMemory

一、ChatMemory

由于手动维护和管理ChatMessages很麻烦,LangChain4j提供了ChatMemory抽象以及多个开箱即用的实现。

ChatMemory可以作为独立的低级组件来使用,也可以作为高级组件(AiService)的一部分使用。

ChatMemory作为ChatMessages的容器,它有如下附加功能:

  • 逐出策略
  • 持久化
  • SystemMessage的特殊处理
  • 工具信息的特殊处理

二、记忆与历史

记忆与历史是两个不同的概念,我们要做好区分:

历史记录:它保留的是用户与AI之间的所有信息。用户可以在UI中看到的内容,代表了之前用户与AI之间所发生过的对话。

记忆:保存了一些信息,这些信息会呈现给LLM,使其看上去就像记住了之前对话一样,它与历史不同,会根据所使用的内存算法,可以以各种方式修改历史记录、汇总单独的消息、从消息中删除不重要的细节、向消息中注入额外的信息或指令。

LangChain4j当前只提供记忆而不会提供历史,如果我们要保存历史则需要我们自行实现对过对话信息的保存。

三、逐出策略

逐出策略非常重要也是必须的。原因如下:

  • LLM的上下文窗口一次可以处理的令牌长度是有限的。在有些情况下,会话超过这个限制则逐出一些消息,而逐出策略就是用来控制哪些消息要逐出。我们最为常用的策略是:最老的消息被逐出
  • 控制成本。每个令牌都是有一定的成本的,每次对LLM的调用逐渐会变得更加昂贵,为了降低这个成本可以考虑清除掉不必要的消息
  • 加快响应。发送给到LLM的令牌越多,处理它们所需要的时间则会越多

LangChain4j当前提供了两个开箱即用的实现

  • MessageWindowChatMemory:作为滑动窗口,保留最新的N条消息,并逐出不再合适的旧消息。
  • TokenWindowChatMemory:作为滑动窗口,保留N个最新的令牌,根据需要逐出旧消息。使用这个的话需要有一个Tokenizer来计算每个ChatMessage中的令牌

四、持久化

默认情况下,我们的记忆的消息会存储到内存当中,如果我们需要进行持久化的存储,则可以自定义ChatMemoryStore,把ChatMessage存储到我们自己的持化久设备当中。如下所示的实现方式

class PersistentChatMemoryStore implements ChatMemoryStore {

        @Override
        public List<ChatMessage> getMessages(Object memoryId) {
          // TODO: Implement getting all messages from the persistent store by memory ID.
          // ChatMessageDeserializer.messageFromJson(String) and 
          // ChatMessageDeserializer.messagesFromJson(String) helper methods can be used to
          // easily deserialize chat messages from JSON.
        }

        @Override
        public void updateMessages(Object memoryId, List<ChatMessage> messages) {
            // TODO: Implement updating all messages in the persistent store by memory ID.
            // ChatMessageSerializer.messageToJson(ChatMessage) and 
            // ChatMessageSerializer.messagesToJson(List<ChatMessage>) helper methods can be used to
            // easily serialize chat messages into JSON.
        }

        @Override
        public void deleteMessages(Object memoryId) {
          //
阿里巴巴通义千问AI PaaS大模型、炼丹炉大模型、ChatMemo接入、模型训练、VUE页面部署、后端部署、对接阿里云通义千问、语音转文本、简易搭建通义千问大模型聊天功能(支持PC端、移动端) 个人深耕AI大模型应用领域积累的成果,希望对您有所帮助。有大模型账号、环境问题、AI大模型技术应用落地方案等相关问题,欢迎详聊,能为您解决问题是我的荣幸! 个人深耕AI大模型应用领域积累的成果,希望对您有所帮助。有大模型账号、环境问题、AI大模型技术应用落地方案等相关问题,欢迎详聊,能为您解决问题是我的荣幸! 个人深耕AI大模型应用领域积累的成果,希望对您有所帮助。有大模型账号、环境问题、AI大模型技术应用落地方案等相关问题,欢迎详聊,能为您解决问题是我的荣幸! 个人深耕AI大模型应用领域积累的成果,希望对您有所帮助。有大模型账号、环境问题、AI大模型技术应用落地方案等相关问题,欢迎详聊,能为您解决问题是我的荣幸! 个人深耕AI大模型应用领域积累的成果,希望对您有所帮助。有大模型账号、环境问题、AI大模型技术应用落地方案等相关问题,欢迎详聊,能为您解决问题是我的荣幸! 个人深耕AI大模型应用领域积累的成果,希望对您有所帮助。有大模型账号、环境问题、AI大模型技术应用落地方案等相关问题,欢迎详聊,能为您解决问题是我的荣幸!
<think>我们正在使用LangChain4j,并希望将会话记忆存储到MongoDB中。 根据LangChain4j的文档,我们可以通过实现`ChatMemoryStore`接口来创建自定义的存储。 然后,我们可以将这个自定义的存储设置到`ChatMemory`中。 步骤: 1. 实现`ChatMemoryStore`接口,使用MongoDB作为后端存储。 2. 在创建`ChatMemory`时,使用这个自定义的存储。 但是,请注意,LangChain4j目前并没有直接提供MongoDB的存储实现,所以我们需要自己编写。 下面是一个大致的步骤和代码示例: 首先,我们需要添加MongoDB的Java驱动依赖(例如,在Maven项目中): ```xml <dependency> <groupId>org.mongodb</groupId> <artifactId>mongodb-driver-sync</artifactId> <version>4.10.0</version> </dependency> ``` 然后,创建一个类,比如`MongoDBChatMemoryStore`,实现`ChatMemoryStore`接口。 `ChatMemoryStore`接口有两个方法: - `List<ChatMessage> getMessages(Object memoryId)` - `void updateMessages(Object memoryId, List<ChatMessage> messages)` - `void deleteMessages(Object memoryId)` 我们可以使用MongoDB的集合来存储每个会话记忆。每个会话可以用一个唯一的ID(memoryId)来标识。 示例代码: ```java import dev.langchain4j.data.message.ChatMessage; import dev.langchain4j.store.memory.chat.ChatMemoryStore; import java.util.List; public class MongoDBChatMemoryStore implements ChatMemoryStore { private final MongoCollection<Document> collection; public MongoDBChatMemoryStore(MongoDatabase database) { this.collection = database.getCollection("chat_memories"); } @Override public List<ChatMessage> getMessages(Object memoryId) { Document doc = collection.find(new Document("memoryId", memoryId)).first(); if (doc == null) { return new ArrayList<>(); } // 假设我们将消息列表存储为Document的列表,然后转换为ChatMessage List<Document> messageDocs = doc.getList("messages", Document.class); return messageDocs.stream() .map(this::toChatMessage) .collect(Collectors.toList()); } @Override public void updateMessages(Object memoryId, List<ChatMessage> messages) { // 将消息列表转换为Document列表 List<Document> messageDocs = messages.stream() .map(this::toDocument) .collect(Collectors.toList()); Document doc = new Document("memoryId", memoryId) .append("messages", messageDocs); // 使用upsert:如果存在则更新,不存在则插入 collection.replaceOne( new Document("memoryId", memoryId), doc, new ReplaceOptions().upsert(true) ); } @Override public void deleteMessages(Object memoryId) { collection.deleteOne(new Document("memoryId", memoryId)); } private Document toDocument(ChatMessage message) { // 将ChatMessage转换为Document,这里需要根据ChatMessage的具体结构进行转换 // 例如,假设ChatMessage有类型和内容,我们可以这样转换: return new Document() .append("type", message.type().name()) .append("text", message.text()); } private ChatMessage toChatMessage(Document doc) { // 将Document转换回ChatMessage String type = doc.getString("type"); String text = doc.getString("text"); // 根据类型创建具体的ChatMessage子类实例,例如UserMessage, AiMessage等 // 这里需要根据实际类型来处理 if ("USER".equals(type)) { return new UserMessage(text); } else if ("AI".equals(type)) { return new AiMessage(text); } // ... 其他类型 throw new IllegalArgumentException("Unknown message type: " + type); } } ``` 注意:上面的转换方法`toDocument`和`toChatMessage`需要根据实际使用的`ChatMessage`类型来完善。在LangChain4j中,`ChatMessage`是一个接口,具体有`UserMessage`、`AiMessage`等实现。 然后,在创建`ChatMemory`时,我们可以使用这个存储: ```java MongoClient mongoClient = MongoClients.create("mongodb://localhost:27017"); MongoDatabase database = mongoClient.getDatabase("your_db_name"); ChatMemoryStore store = new MongoDBChatMemoryStore(database); ChatMemory chatMemory = MessageWindowChatMemory.builder() .chatMemoryStore(store) .maxMessages(10) // 保留最近10条消息 .id("some-session-id") // 会话ID .build(); ``` 这样,当与AI交互时,会话记忆就会被存储到MongoDB中,并在需要时从MongoDB中读取。 注意:在实际应用中,我们可能需要处理更复杂的转换,比如消息可能包含多个内容(文本、图像等)以及元数据。此外,我们还需要考虑序列化和反序列化的性能。 另外,我们也可以使用LangChain4j的`SerializedChatMemory`来将整个`ChatMemory`序列化为字符串,然后存储到MongoDB的一个字符串字段中。但是这样每次读取都需要反序列化整个对象,更新时也需要序列化整个对象。而上面的方法可以只更新消息列表,更灵活。 但是,请注意,`ChatMemoryStore`接口只要求存储消息列表,而不是整个`ChatMemory`对象。所以上述方式更符合接口设计。 引用: 关于MongoDB的Java驱动使用,可以参考官方文档:https://mongodb.github.io/mongo-java-driver/ 关于LangChain4j的`ChatMemoryStore`,可以参考其源码和文档:https://github.com/langchain4j/langchain4j 由于我们是在自定义存储,所以需要确保对消息的转换正确无误。 另外,我们也可以考虑使用LangChain4j的`ChatMemory`的持久化机制,例如使用`PersistentChatMemory`,但这里我们直接使用了`ChatMemoryStore`。 如果使用Spring Boot,我们可以将`MongoDBChatMemoryStore`声明为一个Bean,然后注入使用。 以上是一个基本的实现方案,根据实际需求可能需要调整。 相关问题: 1. 如何在Spring Boot项目中集成LangChain4j和MongoDB? 2. 如何优化MongoDB中存储的聊天消息结构以提高查询效率? 3. LangChain4j中还有哪些其他类型的记忆存储实现?</think>要在 LangChain4j 中将会话记忆存储到 MongoDB,需要通过自定义 `ChatMemoryStore` 实现 MongoDB 集成。以下是详细步骤: ### 1. **添加 MongoDB 依赖** 在 `pom.xml` 中添加 MongoDB Java 驱动: ```xml <dependency> <groupId>org.mongodb</groupId> <artifactId>mongodb-driver-sync</artifactId> <version>4.10.0</version> </dependency> ``` ### 2. **实现自定义 ChatMemoryStore** 创建 `MongoDBChatMemoryStore` 类实现 `ChatMemoryStore` 接口: ```java import dev.langchain4j.data.message.ChatMessage; import dev.langchain4j.store.memory.chat.ChatMemoryStore; import org.bson.Document; import com.mongodb.client.MongoClient; import com.mongodb.client.MongoCollection; import com.mongodb.client.model.Filters; import java.util.ArrayList; import java.util.List; public class MongoDBChatMemoryStore implements ChatMemoryStore { private final MongoCollection<Document> collection; public MongoDBChatMemoryStore(MongoClient mongoClient, String dbName, String collectionName) { this.collection = mongoClient.getDatabase(dbName).getCollection(collectionName); } @Override public List<ChatMessage> getMessages(Object memoryId) { Document doc = collection.find(Filters.eq("memoryId", memoryId)).first(); if (doc == null) return new ArrayList<>(); return doc.getList("messages", ChatMessage.class); } @Override public void updateMessages(Object memoryId, List<ChatMessage> messages) { Document doc = new Document() .append("memoryId", memoryId) .append("messages", messages); collection.replaceOne( Filters.eq("memoryId", memoryId), doc, new ReplaceOptions().upsert(true) ); } @Override public void deleteMessages(Object memoryId) { collection.deleteOne(Filters.eq("memoryId", memoryId)); } } ``` ### 3. **配置 MongoDB 连接** 初始化 MongoDB 客户端: ```java import com.mongodb.client.MongoClients; MongoClient mongoClient = MongoClients.create("mongodb://localhost:27017"); ``` ### 4. **集成到 LangChain4j** 将自定义存储绑定到 `ChatMemory`: ```java import dev.langchain4j.memory.chat.MessageWindowChatMemory; ChatMemoryStore mongoStore = new MongoDBChatMemoryStore( mongoClient, "langchain_db", "chat_memories" ); ChatMemory chatMemory = MessageWindowChatMemory.builder() .chatMemoryStore(mongoStore) // 关键:注入自定义存储 .maxMessages(20) // 保留最近20条消息 .id("user-session-id") // 唯一会话ID .build(); ``` ### 5. **使用示例** ```java // 初始化AI模型 ChatLanguageModel model = OpenAiChatModel.builder().apiKey("sk-...").build(); // 创建带记忆的AI服务 Assistant assistant = AiServices.builder(Assistant.class) .chatLanguageModel(model) .chatMemory(chatMemory) // 绑定MongoDB记忆 .build(); // 交互时自动存储/读取记忆 String userMessage = "1+1等于多少?"; String response = assistant.chat(userMessage); // 响应及记忆自动存入MongoDB ``` ### 关键说明 1. **会话标识**:`memoryId` 需唯一(如用户ID+会话ID),确保隔离不同会话 2. **数据结构**:MongoDB 文档结构: ```json { "memoryId": "user-session-id", "messages": [ {"type": "USER", "text": "1+1等于多少?"}, {"type": "AI", "text": "1+1等于2"} ] } ``` 3. **扩展性**:可增加字段如 `timestamp` 用于记忆过期管理[^1] ### 配置 MongoDB 服务器(参考) 若需配置 MongoDB 仓库(如 RedHat): ```conf [mongodb-org-4.0] name=MongoDB Repository baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.0/x86_64/ gpgcheck=1 enabled=1 gpgkey=https://www.mongodb.org/static/pgp/server-4.0.asc ``` [^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值