Spring AI 聊天记忆管理:MessageWindowChatMemory 与 MessageChatMemoryAdvisor 详解

个人名片
在这里插入图片描述
🎓作者简介:java领域优质创作者
🌐个人主页码农阿豪
📞工作室:新空间代码工作室(提供各种软件服务)
💌个人邮箱:[2435024119@qq.com]
📱个人微信:15279484656
🌐个人导航网站www.forff.top
💡座右铭:总有人要赢。为什么不能是我呢?

  • 专栏导航:

码农阿豪系列专栏导航
面试专栏:收集了java相关高频面试题,面试实战总结🍻🎉🖥️
Spring5系列专栏:整理了Spring5重要知识点与实战演练,有案例可直接使用🚀🔧💻
Redis专栏:Redis从零到一学习分享,经验总结,案例实战💐📝💡
全栈系列专栏:海纳百川有容乃大,可能你想要的东西里面都有🤸🌱🚀

Spring AI 聊天记忆管理:MessageWindowChatMemory 与 MessageChatMemoryAdvisor 详解

引言

在现代对话系统开发中,维护对话上下文是构建智能交互体验的关键。Spring AI 框架提供了强大的聊天记忆管理功能,其中 MessageWindowChatMemoryMessageChatMemoryAdvisor 是两个核心组件。本文将深入探讨这两个组件的正确使用方法,分析常见错误原因,并提供完整的配置示例。

一、Spring AI 聊天记忆核心概念

1.1 ChatMemory 接口

ChatMemory 是 Spring AI 中定义聊天记忆行为的核心接口:

public interface ChatMemory {
    String DEFAULT_CONVERSATION_ID = "default";
    String CONVERSATION_ID = "chat_memory_conversation_id";

    default void add(String conversationId, Message message) {
        Assert.hasText(conversationId, "conversationId cannot be null or empty");
        Assert.notNull(message, "message cannot be null");
        this.add(conversationId, List.of(message));
    }

    void add(String conversationId, List<Message> messages);
    List<Message> get(String conversationId);
    void clear(String conversationId);
}

该接口定义了三个核心操作:

  • add() - 添加消息到记忆
  • get() - 获取对话历史
  • clear() - 清除对话记忆

1.2 ChatMemoryRepository

ChatMemoryRepository 是记忆存储的抽象接口:

public interface ChatMemoryRepository {
    List<String> findConversationIds();
    List<Message> findByConversationId(String conversationId);
    void saveAll(String conversationId, List<Message> messages);
    void deleteByConversationId(String conversationId);
}

默认实现 InMemoryChatMemoryRepository 使用内存存储对话记录。

二、MessageWindowChatMemory 详解

2.1 核心特性

MessageWindowChatMemoryChatMemory 的主要实现类,具有以下特点:

  • 维护一个固定大小的消息窗口
  • 自动移除旧消息,保留最新消息
  • 默认保留20条消息(可配置)
  • 特殊处理系统消息(不会被自动移除)

2.2 正确创建方式

错误方式(会导致编译错误):

// 错误!构造函数是私有的
ChatMemory memory = new MessageWindowChatMemory(repository, 20);

正确方式 - 使用 Builder 模式

@Bean
public ChatMemory chatMemory(ChatMemoryRepository chatMemoryRepository) {
    return MessageWindowChatMemory.builder()
            .chatMemoryRepository(chatMemoryRepository)
            .maxMessages(20) // 可选,默认20
            .build();
}

2.3 完整配置示例

@Configuration
public class ChatMemoryConfig {
    
    @Bean
    public ChatMemoryRepository chatMemoryRepository() {
        return new InMemoryChatMemoryRepository();
    }
    
    @Bean
    public ChatMemory chatMemory(ChatMemoryRepository chatMemoryRepository) {
        return MessageWindowChatMemory.builder()
                .chatMemoryRepository(chatMemoryRepository)
                .maxMessages(30) // 自定义消息窗口大小
                .build();
    }
}

三、MessageChatMemoryAdvisor 详解

3.1 作用与功能

MessageChatMemoryAdvisor 是一个聊天顾问,负责:

  • 自动将对话历史添加到请求中
  • 管理不同会话的记忆
  • ChatMemory 协同工作

3.2 正确创建方式

错误方式

// 错误!构造函数是私有的
new MessageChatMemoryAdvisor(chatMemory);

正确方式 - 使用 Builder

MessageChatMemoryAdvisor.builder(chatMemory)
        .conversationId("custom-id") // 可选
        .build();

3.3 完整聊天客户端配置

@Bean
public ChatClient chatClient(OllamaChatModel chatModel, ChatMemory chatMemory) {
    return ChatClient.builder(chatModel)
            .defaultSystem("你是一个法律咨询助手")
            .defaultAdvisors(
                new SimpleLoggerAdvisor(),
                MessageChatMemoryAdvisor.builder(chatMemory)
                    .conversationId("legal-consultation")
                    .build()
            )
            .build();
}

四、常见问题解决方案

4.1 构造函数访问错误

问题

'MessageWindowChatMemory(...)' has private access

原因:试图直接实例化私有构造的类

解决:使用 Builder 模式

4.2 Builder 访问错误

问题

'Builder()' has private access

原因:错误使用 Builder 构造函数

解决:使用静态 builder() 方法

// 正确
MessageWindowChatMemory.builder()...

// 错误
new MessageWindowChatMemory.Builder()...

4.3 依赖注入问题

推荐做法:通过方法参数注入依赖

@Bean
public ChatMemory chatMemory(ChatMemoryRepository repo) {
    return MessageWindowChatMemory.builder()
            .chatMemoryRepository(repo)
            .build();
}

五、最佳实践

  1. 合理设置消息窗口大小

    • 一般对话:20-30条
    • 长对话场景:50-100条
    • 注意内存消耗
  2. 会话管理策略

    // 按用户ID区分会话
    MessageChatMemoryAdvisor.builder(chatMemory)
            .conversationId(userId)
            .build();
    
  3. 组合使用多个 Advisor

    .defaultAdvisors(
        new SimpleLoggerAdvisor(),
        new RetryAdvisor(),
        MessageChatMemoryAdvisor.builder(chatMemory).build()
    )
    
  4. 自定义记忆处理逻辑

    • 继承 MessageWindowChatMemory
    • 重写 process() 方法

六、扩展应用

6.1 Redis 记忆存储

@Bean
public ChatMemoryRepository redisChatMemoryRepository(RedisTemplate<String, Object> redisTemplate) {
    return new RedisChatMemoryRepository(redisTemplate);
}

6.2 自定义记忆策略

public class PriorityMessageChatMemory implements ChatMemory {
    // 实现优先保留重要消息的逻辑
}

结论

Spring AI 的记忆管理功能通过 MessageWindowChatMemoryMessageChatMemoryAdvisor 提供了强大而灵活的对话上下文管理能力。关键在于正确使用 Builder 模式创建这些组件,并合理配置记忆策略。本文介绍的模式和最佳实践可以帮助开发者构建更智能、更自然的对话系统。

通过遵循这些指导原则,你可以避免常见的配置错误,并充分利用 Spring AI 提供的记忆管理功能,为用户创造更加连贯和个性化的对话体验。

<think>我们正在使用Spring AI 1.0.0版本,并且发现`defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory))`已经被弃用。我们需要找到替代方案。 在Spring AI 1.0.0中,`MessageChatMemoryAdvisor`已经被移除,取而代之的是使用`ChatMemory`接口的实现,并直接将其注入到`PromptTemplate`中,或者通过`ChatMemoryManager`来管理。 根据Spring AI的更新日志和文档,我们可以采用以下步骤来替代: 1. 创建一个`ChatMemory`的bean,例如`InMemoryChatMemory`(这是Spring AI提供的一个基于内存的实现)。 2. 在创建`ChatClient`(如`OpenAiChatClient`)时,不再使用`defaultAdvisors`,而是使用`ChatMemoryManager`来管理聊天内存。 具体步骤: 步骤1:配置`ChatMemory`和`ChatMemoryManager` 在Spring AI 1.0.0中,`ChatMemoryManager`用于管理不同会话的`ChatMemory`。我们可以使用一个简单的基于内存的`ChatMemoryManager`,例如`SimpleChatMemoryManager`。 步骤2:在需要的地方注入`ChatMemoryManager`,并在每次请求时设置当前会话的`ChatMemory`。 示例代码: 首先,添加必要的依赖(假设已经配置了Spring AI和OpenAI的starter)。 然后,配置`ChatMemoryManager`和`ChatMemory`: ```java @Configuration public class AiConfig { @Bean public ChatMemoryManager chatMemoryManager() { return new SimpleChatMemoryManager(); } // 如果你需要每个会话都有一个独立的聊天内存,你可以在每次请求时从ChatMemoryManager获取 // 这里我们定义一个作用域为会话的ChatMemory bean(如果是在Web应用中) // 注意:如果不是Web应用,你可能需要其他方式管理会话 @Bean @Scope(scopeName = "session", proxyMode = ScopedProxyMode.TARGET_CLASS) public ChatMemory chatMemory() { return new InMemoryChatMemory(); // 默认保留最近的10条消息,可以通过构造函数指定 } } ``` 步骤3:在服务层使用`ChatMemoryManager`和`ChatClient`进行交互。 但是,请注意,在Spring AI 1.0.0中,`ChatClient`不再直接支持`ChatMemory`。相反,我们需要在每次调用时构建一个包含聊天历史的`Prompt`。 我们可以通过以下方式实现: 1. 创建一个服务类,用于处理AI的交互。 2. 在该服务类中,我们使用`ChatMemory`来存储和获取历史消息。 3. 在调用`ChatClient`之前,将`ChatMemory`中的消息添加到当前提示中。 示例服务类: ```java @Service public class ChatService { private final ChatClient chatClient; private final ChatMemoryManager chatMemoryManager; public ChatService(ChatClient chatClient, ChatMemoryManager chatMemoryManager) { this.chatClient = chatClient; this.chatMemoryManager = chatMemoryManager; } public String chat(String sessionId, String message) { // 根据sessionId获取或创建ChatMemory ChatMemory chatMemory = chatMemoryManager.get(sessionId) .orElseGet(() -> { ChatMemory newChatMemory = new InMemoryChatMemory(); chatMemoryManager.put(sessionId, newChatMemory); return newChatMemory; }); // 将用户输入添加到聊天内存 chatMemory.add(new UserMessage(message)); // 从聊天内存中获取所有消息 List<Message> messages = chatMemory.getMessages(); // 构建一个Prompt,包含所有消息 Prompt prompt = new Prompt(messages); // 调用ChatClient ChatResponse response = chatClient.call(prompt); // 获取AI的回复 String aiResponse = response.getResult().getOutput().getContent(); // 将AI的回复添加到聊天内存 chatMemory.add(new AssistantMessage(aiResponse)); return aiResponse; } } ``` 在这个示例中,我们使用`ChatMemoryManager`来管理每个会话(通过sessionId标识)的聊天内存。每次用户发送消息时,我们获取该会话的聊天内存,将用户消息添加进去,然后构建一个包含所有历史消息的`Prompt`,再调用`ChatClient`。得到AI回复后,将其添加到聊天内存中并返回。 注意:上述代码中,`ChatClient`是通过`ChatClient`接口注入的,你需要确保已经配置了一个`ChatClient`的bean(例如`OpenAiChatClient`)。 另外,在Web应用中,你可以从HttpSession中获取sessionId,或者使用其他方式生成会话标识。 总结:在Spring AI 1.0.0中,不再使用`defaultAdvisors`,而是通过显式地管理`ChatMemory`来维护聊天历史。 参考:Spring AI 1.0.0的官方文档和更新说明[^1]。 §§相关问题§§ 1. 如何在Spring AI中配置OpenAI的API密钥? 2. 除了内存存储,Spring AI支持哪些其他类型的`ChatMemory`实现? 3. 如何控制`ChatMemory`中保留的消息数量?</think>在 Spring AI 1.0.0 中,`defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory))` 已被弃用。以下是替代方案和迁移步骤: ### 替代方案(使用 `ChatMemoryManager`) ```java @Bean public ChatMemoryManager chatMemoryManager() { return new SimpleChatMemoryManager( new InMemoryChatMemory() // 默认实现 ); } @Bean public PromptTemplate promptTemplate(ChatMemoryManager chatMemoryManager) { return new PromptTemplate(chatMemoryManager); } ``` ### 完整迁移示例 ```java @Configuration public class AiConfig { // 1. 创建 ChatMemoryManager @Bean public ChatMemoryManager chatMemoryManager() { return new SimpleChatMemoryManager( MessageWindowChatMemory.builder() .maxMessages(10) // 保留最近10条消息 .build() ); } // 2. 注入到 PromptTemplate @Bean public PromptTemplate chatPromptTemplate(ChatMemoryManager chatMemoryManager) { return new PromptTemplate(chatMemoryManager); } // 3. 使用示例 @Bean ApplicationRunner applicationRunner(ChatClient chatClient, PromptTemplate promptTemplate) { return args -> { // 创建带上下文的提示 Prompt prompt = promptTemplate.create("用户问题: {query}", Map.of("query", "Spring AI是什么?")); // 调用 AI ChatResponse response = chatClient.call(prompt); System.out.println(response.getResult().getOutput().getContent()); }; } } ``` ### 关键变更说明 1. **弃用替代** `MessageChatMemoryAdvisor` → 改用 `ChatMemoryManager` + `PromptTemplate` 组合 2. **内存管理** - `InMemoryChatMemory`:基础实现(默认保留10条消息) - `MessageWindowChatMemory`:可配置消息窗口大小 ```java MessageWindowChatMemory.builder() .maxMessages(20) // 自定义消息数量 .build() ``` 3. **会话管理** 通过 `@SessionId` 注解区分会话: ```java public String handleChat(@RequestParam String query, @SessionId UUID sessionId) { Prompt prompt = promptTemplate.create(query, sessionId); // ... } ``` ### 注意事项 1. **依赖更新** 确保 `pom.xml` 使用最新依赖: ```xml <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-core</artifactId> <version>1.0.0</version> </dependency> ``` 2. **历史记录访问** 通过 `ChatMemory` 获取完整上下文: ```java chatMemoryManager.get(sessionId).getMessages(); ``` 3. **持久化扩展** 实现 `ChatMemoryStore` 接口可接入 Redis 等存储[^1]。 [^1]: 参考 Spring AI 官方文档:Chat Memory Management
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农阿豪@新空间

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值