在现代对话系统和聊天机器人应用中,保持连贯的对话记忆至关重要。ChatMemory 作为对话系统的记忆核心,负责维护和检索历史对话信息。然而,默认的内存实现往往无法满足生产环境的需求,特别是在持久化、扩展性和性能方面。本文将深入探讨如何实现 ChatMemory 的自定义持久化方案。

什么是 ChatMemory?

ChatMemory 是对话系统中的关键组件,它承担着以下职责:

  • 存储对话历史和上下文信息

  • 维护用户偏好和个性化设置

  • 跟踪对话状态和流程

  • 支持长期记忆和短期记忆的分离

为什么需要自定义持久化?

默认实现的局限性

大多数对话框架提供的默认内存实现存在以下问题:

  1. 内存限制:基于 RAM 的存储,重启后数据丢失

  2. 扩展性差:单机存储,无法分布式部署

  3. 性能瓶颈:大量对话数据时响应变慢

  4. 缺乏备份:数据丢失风险高

自定义持久化的优势

  • 数据持久性:确保对话历史不丢失

  • 可扩展性:支持水平扩展

  • 性能优化:根据业务需求定制存储策略

  • 数据安全:完善的备份和恢复机制

本文将基于 MapDB 的 ChatMemory 聊天记忆持久化实现

MapDB简介

MapDB是一个轻量级的Java嵌入式数据库引擎,它支持在JVM中直接运行,无需外部服务器。MapDB提供了基于磁盘或堆外存储的并发的Maps、Sets、Lists、Queues等数据结构,使得开发者可以像使用Java集合一样轻松地使用MapDB。此外,MapDB还支持ACID事务、MVCC(多版本并发控制)等特性,确保数据的完整性和一致性。

MapDB的特点

高性能:MapDB经过优化和重写,性能出色,可以在多核环境中实现线性扩展。

轻量级:MapDB的jar包体积较小,且没有其他依赖项,非常适合嵌入式系统或内存数据库的应用场景。

易用性:MapDB提供了基于Java集合的API,使得开发者可以轻松地进行数据存储和检索操作。

ACID事务支持:MapDB支持ACID事务,确保数据的一致性和隔离性。

模块化设计:MapDB采用模块化的架构设计,易于扩展和定制。

如何引入 MapDB

要开始使用 MapDB,首先需要将其添加到你的项目中。如果你使用的是 Maven,可以在项目的 pom.xml 文件中添加以下依赖:

<dependency>
    <groupId>org.mapdb</groupId>
    <artifactId>mapdb</artifactId>
    <version>3.0.9</version>
</dependency>

实现自定义的聊天记忆存储器

package com.springai.config;


import dev.langchain4j.data.message.ChatMessage;
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.messageToJson;
import static dev.langchain4j.data.message.ChatMessageSerializer.messagesToJson;
import static org.mapdb.Serializer.INTEGER;
import static org.mapdb.Serializer.STRING;


/**
 * 自定义的持久化聊天存储器
 */
public class PersistentChatMemoryStore implements ChatMemoryStore {
    // 创建一个MapDB数据库实例
    private final DB db = DBMaker.fileDB("./chat-memory1.db").transactionEnable().make();
    // 创建一个MapDB的HashMap,用于存储聊天消息
    private final Map<Integer, String> map = db.hashMap("messages", INTEGER, STRING).createOrOpen();

    // 根据指定的 memoryId 从存储中获取对应的聊天消息
    //返回指定记忆ID对应的聊天消息列表
    public List<ChatMessage> getMessages(Object memoryId) {
        String json = map.get((Integer) memoryId);
        return messagesFromJson(json);
    }

    // 更新指定memoryId的聊天消息
    public void updateMessages(Object memoryId, List<ChatMessage> messages) {
        // 将聊天消息列表转换为JSON字符串
        String json = messagesToJson(messages);
        // 将JSON字符串存储到指定的memoryId对应的键中
        map.put((Integer) memoryId, json);
        //通过 db.commit() 确保数据被写入磁盘,实现持久化存储
        db.commit();
    }

    // 从存储中移除指定 memoryId 对应的所有聊天消息
    public void deleteMessages(Object memoryId) {
        map.remove((int) memoryId);
        db.commit();
    }
}

模型创建并实现记忆持久化

service接口

package com.springai.service;

import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.UserMessage;
import reactor.core.publisher.Flux;

public interface ChatAssistant {


    /*
    * @Param userId 用户id
    * @Param message 用户消息
    * */
    Flux<String> chat(@MemoryId Integer userId, @UserMessage String message);
}

配置类实现模型信息
package com.springai.config;

import com.springai.config.PersistentChatMemoryStore;
import com.springai.service.ChatAssistant;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.StreamingChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class LLMConfig {

    // 创建一个流式模型
    @Bean
    public StreamingChatLanguageModel streamingChatLanguageModel() {
        return OpenAiStreamingChatModel.builder()
                .apiKey("sk-3dbcd881d8db42ddb24e1b095539f72e")
                .modelName("qwen-max")
                .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
                .logRequests(true)
                .logResponses(true)
                .build();
    }

    //创建一个持久化聊天存储器容器
    @Bean
    public ChatMemoryStore chatMemoryStore() {
        return new PersistentChatMemoryStore();
    }

    //创建一个聊天存储器提供者
    @Bean
    public ChatMemoryProvider chatMemoryProvider(ChatMemoryStore chatMemoryStore) {
        return memoryId -> MessageWindowChatMemory.builder()
                .id(memoryId)
                .maxMessages(10)
                .chatMemoryStore(chatMemoryStore)
                .build();
    }

    //创建一个聊天助手
    @Bean
    public ChatAssistant chatAssistant(StreamingChatLanguageModel streamingChatLanguageModel,
                                       ChatMemoryProvider chatMemoryProvider) {
        return AiServices.builder(ChatAssistant.class)
                // 使用注入的流式模型
                .streamingChatLanguageModel(streamingChatLanguageModel)
                // 使用注入的 chatMemoryProvider
                .chatMemoryProvider(chatMemoryProvider)
                .build();
    }
}

接口实现

package com.springai.controller;

import com.springai.service.ChatAssistant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

@RestController
public class ChatContrnller {
    @Autowired
    ChatAssistant chatAssistant;

    @GetMapping("/chat")
    public Flux<String> chat(Integer userId, String message) {
        Flux<String> chat = chatAssistant.chat(userId, message);
        return chat;
    }
}
本文使用的流式输出模型,也可以换为普通模型

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐