Spring AI(3)——Chat Memory

Chat Memory介绍

大型语言模型(LLM)是无状态的,这意味着它们不保留关于以前互动的信息。为了解决这个问题,Spring AI提供了Chat Memory(聊天记忆)功能。通过Chat Memory,用户可以实现在与LLM的多次交互中存储和检索信息。

聊天记忆的底层存储由ChatMemoryRepository处理,其唯一责任是存储和检索消息。决定保留哪些消息及何时删除这些消息的权利在于ChatMemory的实现。策略可能包括保留最近的N条消息,保留一定时间段的消息,或者保留指定最大令牌数的消息。

ChatMemory当前有一种实现:MessageWindowChatMemory。MessageWindowChatMemory 维护一个最多可达到指定最大大小(默认:20 条消息)的消息窗口。当消息数量超过此限制时,旧消息会被驱逐,但系统消息会被保留。如果添加了一条新的系统消息,则会从聊天记忆中删除所有以前的系统消息。这确保了最新的上下文始终可用于对话,同时保持聊天记忆使用在可控范围内。

ChatModel对象使用Chat Memory

不使用Chat Memory的现象

    @GetMapping("/chat")
    public String chat() {
        String answer = this.chatModel.call("你好,我是老任与码");
        System.out.println(answer);

        String answer2 = this.chatModel.call("我是谁");
        System.out.println(answer2);
        return "success";
    }

输出结果:

根据输出结果可以看到,第一次提问时,虽然已经告知大模型自己的名字,但是第二次提问时,大模型并不能回答出正确答案。这就说明本例中,两次提问是相互独立的,大模型是无状态的。

使用Chat Memory

不需要任何配置,即可直接注入ChatMemory对象:

@RestController
@RequestMapping("/memory")
public class MemoryController {

    @Resource
    private ZhiPuAiChatModel chatModel;

    @Resource
    private ChatClient chatClient;

    // 可以直接注入,也可以自定义创建
    @Resource
    private ChatMemory chatMemory;

    ......

}

也可以在配置类中创建ChatMemory对象后,再注入:

    @Bean
    public ChatMemory chatMemory() {
        MessageWindowChatMemory memory = MessageWindowChatMemory.builder()
                .maxMessages(10)
                .chatMemoryRepository(new InMemoryChatMemoryRepository())
                .build();
        return memory;
    }

 本例根据MessageWindowChatMemory创建ChatMemory对象,通过maxMessage(10)方法指定存储的最大的消息条数是10条,通过chatMemoryRepository(new InMemoryChatMemoryRepository())方法,表示聊天的上下文消息存储在内存中。

测试代码:

    
    @GetMapping("/chat3")
    public String chat3(String conversationId) {
        UserMessage userMessage1 = new UserMessage("你好,我是老任与码" + conversationId);
        // 将消息添加到ChatMemory中
        // conversationId表示和大模型的会话id,一般可以使用用户id表示,用于区分不同的用户的聊天上下文信息
        chatMemory.add(conversationId, userMessage1);
        // 提问
        ChatResponse response1 = this.chatModel.call(new Prompt(chatMemory.get(conversationId)));
        System.out.println("answer1:" + response1.getResult().getOutput().getText());
        // 将回答也添加到ChatMemory中
        chatMemory.add(conversationId, response1.getResult().getOutput());

        // 第二次提问,也需要将消息添加到ChatMemory
        UserMessage userMessage2 = new UserMessage("我是谁");
        chatMemory.add(conversationId, userMessage2);
        System.out.println(chatMemory.get(conversationId));
        ChatResponse response2 = this.chatModel.call(new Prompt(chatMemory.get(conversationId)));
        System.out.println("answer2:" + response2.getResult().getOutput().getText());
        return "success";
    }

输出结果:

根据输出结果,大模型根据上下文信息,给出了可能的答案。

通过调试查看ChatMemory对象中的信息:

根据调试可以看到,ChatMemory对象中的聊天上下文数据存储在ConcurrentHashMap对象中,key值表示conversationId,value值是我们聊天过程中的提问和回答的消息。

ChatMemory支持的存储方式

  • InMemoryChatMemoryRepository表示上下文消息存储在内存中
  • JdbcChatMemoryRepository表示使用 JDBC 在关系数据库中存储消息,支持PostgreSQL、MySQL / MariaDB、SQL Server、HSQLDB等数据库
  • CassandraChatMemoryRepository表示使用 Apache Cassandra 分布式数据库存储消息
  • Neo4jChatMemoryRepository表示将聊天消息作为节点和关系存储在 Neo4j 图数据库

ChatClient对象使用Chat Memory

在ChatClient中使用ChatMemory,需要指定对应的Advisor,支持的Advisor包括:

  • MessageChatMemoryAdvisor:此Advisor使用提供的实现类来管理对话记忆。在每次互动中,它从记忆中检索对话历史,并将其作为消息的集合包含在提示中。
  • ChatMemoryPromptChatMemoryAdvisor:此Advisor使用提供的实现来管理对话记忆。在每次互动中,它从记忆中检索对话历史,并将其作为普通文本附加到系统提示(SystemMessage)中。
  • ChatMemoryVectorStoreChatMemoryAdvisor:此Advisor使用提供的实现来管理对话记忆。在每次互动中,它从向量存储中检索对话历史,并将其作为普通文本附加到系统消息中。

本例使用MessageChatMemoryAdvisor:

    @Bean
    public ChatClient chatClient(ZhiPuAiChatModel chatModel) {
        return ChatClient
                .builder(chatModel)
                // MessageChatMemoryAdvisor 聊天记忆的advisor
                .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory()).build())
                .build();
    }

测试代码:

    @GetMapping("/chat4")
    public String chat4(String conversationId) {
        String answer1 = chatClient.prompt()
                .user("我叫老任与码")
                .advisors(a -> a.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, conversationId))
                .call()
                .content();

        System.out.println(answer1);

        String answer2 = chatClient.prompt()
                .user("我叫什么名字")
                .advisors(a -> a.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, conversationId))
                .call()
                .content();

        System.out.println(answer2);

        System.out.println(chatMemory.get(conversationId));

        return "success";
    }

上述代码中,必须指定:

advisors(a -> a.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, conversationId))

表示根据conversationId区分不同会话的上下文。

Spring AI 提供了不同类型的聊天记忆存储库,包括基于 JDBC、Cassandra 和 Neo4j 的存储库,以下是对它们的详细介绍: ### 基于 JDBC 的聊天记忆存储库 JDBC 扩展包(如 `spring-ai-alibaba-starter-memory-jdbc`)主要包含抽象类 `JdbcChatMemoryRepository` 和 5 个数据库的子类。`JdbcChatMemoryRepository` 实现了 `ChatMemoryRepository` 接口,其构造函数会查询是否存在聊天记忆表,若不存在则会创建。子类则包含各个数据库的特定语句,如判断表是否存在、创建表、保存聊天记忆、查询聊天记忆、删除聊天记忆等操作 [^2]。 以下是一个简单示例,展示如何使用 JDBC 存储聊天历史信息(以 MySQL 为例): ```java import org.springframework.ai.memory.jdbc.JdbcChatMemoryRepository; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; import javax.sql.DataSource; @Configuration public class ChatMemoryConfig { @Bean public JdbcChatMemoryRepository jdbcChatMemoryRepository(DataSource dataSource) { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); return new JdbcChatMemoryRepository(jdbcTemplate); } } ``` ### 基于 Cassandra 的聊天记忆存储库 Cassandra 是一个高度可扩展的分布式 NoSQL 数据库,基于 Cassandra 的 Spring AI 聊天记忆存储库利用了其高可用性、可扩展性和容错性等特点,适合处理大量的聊天消息数据。当配置了 Cassandra 存储库后,Spring AI 会使用它来存储聊天消息,替代默认的内存存储库 [^1]。 使用时,需要添加 Cassandra 和 Spring AI 相关依赖,配置 Cassandra 连接信息,创建表示聊天消息的实体类,以及创建继承 `CassandraRepository` 的接口来操作聊天消息。以下是一个简化示例: ```java import com.datastax.oss.driver.api.mapper.annotations.Entity; import com.datastax.oss.driver.api.mapper.annotations.PartitionKey; import org.springframework.data.cassandra.repository.CassandraRepository; @Entity public class ChatMessage { @PartitionKey private String sessionId; private String message; private long timestamp; // 构造函数、Getter 和 Setter 方法 } public interface CassandraChatMemoryRepository extends CassandraRepository<ChatMessage, String> { // 可以添加自定义查询方法 } ``` ### 基于 Neo4j 的聊天记忆存储库 Neo4j 是一个图数据库,非常适合处理具有复杂关系的数据。基于 Neo4j 的 Spring AI 聊天记忆存储库可以利用图数据库的特性,更好地存储和查询聊天消息之间的关系。 要使用 Neo4j 存储聊天记忆,需要添加 Neo4j 和 Spring AI 相关依赖,配置 Neo4j 连接信息,创建表示聊天消息的实体类,以及创建继承 `Neo4jRepository` 的接口来操作聊天消息。以下是一个简单示例: ```java import org.springframework.data.neo4j.core.schema.Node; import org.springframework.data.neo4j.core.schema.Property; import org.springframework.data.neo4j.repository.Neo4jRepository; @Node public class ChatMessage { @Property private String sessionId; @Property private String message; @Property private long timestamp; // 构造函数、Getter 和 Setter 方法 } public interface Neo4jChatMemoryRepository extends Neo4jRepository<ChatMessage, Long> { // 可以添加自定义查询方法 } ``` ### 总结 Spring AI 支持多种类型的聊天记忆存储库,用户可以根据具体需求选择合适的存储方案。JDBC 适用于关系型数据库,Cassandra 适用于高并发、大规模数据存储,而 Neo4j 则适用于需要处理复杂关系数据的场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值