AI超级智能开发系列从入门到上天第六篇:自定义AI记忆持久化

AI超级智能开发系列从入门到上天第六篇:自定义AI记忆持久化 一当前现状对话记忆持久化背景提取之前使用的是基于内存的对话记忆来保存对话上下文但这种方式存在缺陷服务器重启后内存中的对话记录会全部丢失。因此实际业务中可能需要将对话记忆持久化把数据保存到文件数据库Redis其他对象存储核心问题如何实现对话记忆的持久化存储与恢复。二利用现有依赖实现Spring AI 对话记忆持久化支持提取Spring AI 官方提供了第三方数据库整合的对话记忆实现可将对话保存到不同数据源InMemoryChatMemory基于内存的临时存储默认实现重启丢失CassandraChatMemory在 Cassandra 中实现带过期时间的持久化存储Neo4jChatMemory在 Neo4j 中实现无过期时间限制的持久化存储JdbcChatMemory在 JDBC 兼容数据库中实现无过期时间限制的持久化存储关于 JdbcChatMemory 的使用提示若需将对话持久化到传统关系型数据库理论上可使用JdbcChatMemory但不推荐依赖spring-ai-starter-model-chat-memory-jdbc版本稀少官方文档 / 介绍匮乏Maven 中央仓库无法检索到该依赖生产环境稳定性与兼容性存疑三自定义ChatMemory1. 核心设计思想Spring AI 的对话记忆实现解耦了「存储」和「记忆算法」可以独立修改ChatMemory的存储层比如从内存换成数据库 / Redis无需改动上层保存对话记忆的业务流程2. 自定义实现方式官方文档未提供自定义ChatMemory示例但可以参考默认实现类InMemoryChatMemory的源码进行仿写。InMemoryChatMemory源码如下3.ChatMemory接口核心职责接口方法较少核心是实现对话消息的增、查、删操作新增添加新的对话消息到记忆中查询根据会话 ID 获取历史消息删除清除指定会话的记忆或过期消息四自定义文件持久化1引入依赖dependency groupIdcom.esotericsoftware/groupId artifactIdkryo/artifactId version5.6.2/version /dependency2编写代码/** * 基于文件持久化的对话记忆 */ public class FileBasedChatMemory implements ChatMemory { private final String BASE_DIR; private static final Kryo kryo new Kryo(); static { kryo.setRegistrationRequired(false); // 设置实例化策略 kryo.setInstantiatorStrategy(new StdInstantiatorStrategy()); } // 构造对象时指定文件保存目录 public FileBasedChatMemory(String dir) { this.BASE_DIR dir; File baseDir new File(dir); if (!baseDir.exists()) { baseDir.mkdirs(); } } Override public void add(String conversationId, ListMessage messages) { ListMessage conversationMessages getOrCreateConversation(conversationId); conversationMessages.addAll(messages); saveConversation(conversationId, conversationMessages); } Override public ListMessage get(String conversationId, int lastN) { ListMessage allMessages getOrCreateConversation(conversationId); return allMessages.stream() .skip(Math.max(0, allMessages.size() - lastN)) .toList(); } Override public void clear(String conversationId) { File file getConversationFile(conversationId); if (file.exists()) { file.delete(); } } private ListMessage getOrCreateConversation(String conversationId) { File file getConversationFile(conversationId); ListMessage messages new ArrayList(); if (file.exists()) { try (Input input new Input(new FileInputStream(file))) { messages kryo.readObject(input, ArrayList.class); } catch (IOException e) { e.printStackTrace(); } } return messages; } private void saveConversation(String conversationId, ListMessage messages) { File file getConversationFile(conversationId); try (Output output new Output(new FileOutputStream(file))) { kryo.writeObject(output, messages); } catch (IOException e) { e.printStackTrace(); } } private File getConversationFile(String conversationId) { return new File(BASE_DIR, conversationId .kryo); } }3修改MessageChatMemoryAdvisor对象的上下文存储类public LoveApp(ChatModel dashscopeChatModel) { // 初始化基于文件的对话记忆 String fileDir System.getProperty(user.dir) /chat-memory; ChatMemory chatMemory new FileBasedChatMemory(fileDir); chatClient ChatClient.builder(dashscopeChatModel) .defaultSystem(SYSTEM_PROMPT) .defaultAdvisors( new MessageChatMemoryAdvisor(chatMemory) ) .build(); }4测试效果