Spring AI 教程

内存存储(InMemoryChatMemoryRepository)

提示:如果不能访问 OpenAI,请点击 AiCode API 注册账号,通过代理访问。  

在 Spring AI 1.0.0 中,InMemoryChatMemoryRepository 是 ChatMemoryRepository 接口的一个默认实现,用于在内存中存储聊天消息和检索聊天消息。它可以获取所有会话 ID、根据会话 ID 获取聊天消息、存储整个会话的历史消息以及清理指定会话 ID 中的聊天消息。

InMemoryChatMemoryRepository 存储库使用 ConcurrentHashMap 来存储消息,键为会话 ID(conversationId),值为该会话的消息列表(List<Message>)。这种结构保证了在多线程环境下的并发安全性,能够高效地进行消息的添加、查询和删除操作。

默认情况下,若未配置其他 Repository 实现,Spring AI 将自动配置 InMemoryChatMemoryRepository 类型的 ChatMemoryRepository Bean 供直接使用。

使用方式如下:

@Autowired
ChatMemoryRepository chatMemoryRepository;

直接通过 @Autowired 注解将 InMemoryChatMemoryRepository 注入到程序中即可。

如果需要手动创建 InMemoryChatMemoryRepository,可以这样做:

ChatMemoryRepository repository = new InMemoryChatMemoryRepository();

  

源码分析

查看源码是最直接了解 InMemoryChatMemoryRepository 底层实现原理的方式,源码如下:

package org.springframework.ai.chat.memory;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.ai.chat.messages.Message;
import org.springframework.util.Assert;

/**
 * ChatMemoryRepository 接口的内存实现类,用于在内存中存储和管理聊天会话消息。
 * 基于 ConcurrentHashMap 实现,支持多线程安全操作,适合开发测试或简单场景。
 */
public final class InMemoryChatMemoryRepository implements ChatMemoryRepository {

    /**
     * 底层存储结构,键为会话ID(conversationId),值为该会话的消息列表
     * 使用 ConcurrentHashMap 保证多线程环境下的并发安全性
     */
    Map<String, List<Message>> chatMemoryStore = new ConcurrentHashMap();

    /**
     * 默认构造方法,初始化内存存储结构
     */
    public InMemoryChatMemoryRepository() {
    }

    /**
     * 获取所有会话ID的列表
     *
     * @return 包含所有会话ID的 ArrayList,无会话时返回空列表
     */
    public List<String> findConversationIds() {
        return new ArrayList(this.chatMemoryStore.keySet());
    }

    /**
     * 根据会话ID查询对应的消息列表
     *
     * @param conversationId 会话唯一标识,不可为null或空字符串
     * @return 该会话的消息列表副本(避免外部修改内部存储),会话不存在时返回空列表
     * @throws IllegalArgumentException 若 conversationId 为null或空
     */
    public List<Message> findByConversationId(String conversationId) {
        // 验证会话ID合法性
        Assert.hasText(conversationId, "conversationId cannot be null or empty");
        // 从存储中获取消息列表
        List<Message> messages = (List)this.chatMemoryStore.get(conversationId);
        // 若消息存在则返回副本,否则返回空列表
        return (List)(messages != null ? new ArrayList(messages) : List.of());
    }

    /**
     * 保存指定会话的消息列表(覆盖式保存)
     *
     * @param conversationId 会话唯一标识,不可为null或空字符串
     * @param messages 待保存的消息列表,不可为null且不能包含null元素
     * @throws IllegalArgumentException 若参数不满足合法性要求
     */
    public void saveAll(String conversationId, List<Message> messages) {
        // 验证会话ID和消息列表合法性
        Assert.hasText(conversationId, "conversationId cannot be null or empty");
        Assert.notNull(messages, "messages cannot be null");
        Assert.noNullElements(messages, "messages cannot contain null elements");
        // 将消息列表存入存储结构(覆盖原有值)
        this.chatMemoryStore.put(conversationId, messages);
    }

    /**
     * 根据会话ID删除对应的所有消息
     *
     * @param conversationId 会话唯一标识,不可为null或空字符串
     * @throws IllegalArgumentException 若 conversationId 为null或空
     */
    public void deleteByConversationId(String conversationId) {
        // 验证会话ID合法性
        Assert.hasText(conversationId, "conversationId cannot be null or empty");
        // 从存储中移除该会话的消息
        this.chatMemoryStore.remove(conversationId);
    }
}

简单示例

下面通过一个简单的例子来使用 InMemoryChatMemoryRepository,通过 InMemoryChatMemoryRepository 实现记忆存储。代码如下:

package com.hxstrive.springai.springai_openai.chatMemoryRepository.chatMemoryRepository1;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.ChatMemoryRepository;
import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RestController;

@RestController
@SpringBootApplication
public class SpringAiDemoApplication implements CommandLineRunner {

	@Autowired
	private ChatModel chatModel;

	private static ChatMemory chatMemory;

	static {
		// 创建 ChatMemory 实现
		ChatMemoryRepository chatMemoryRepository = new InMemoryChatMemoryRepository();
		chatMemory = MessageWindowChatMemory.builder()
				.chatMemoryRepository(chatMemoryRepository) // 使用InMemoryChatMemoryRepository作为存储仓库
				.maxMessages(20) // 最多保存20条消息
				.build();
	}

	public static void main(String[] args) {
		SpringApplication.run(SpringAiDemoApplication.class, args);
	}

	@Override
	public void run(String... args) throws Exception {
		StringBuilder builder = new StringBuilder();

		// 唯一会话ID,同一个会话上下文中保持一致
		String sessionId = "USER-251017100313";

		String question = "四川大学是985吗?";
		String ai = chat(sessionId, question);
		builder.append(String.format("用户问题:%s\n", question));
		builder.append(String.format("机器回复:%s\n", ai));

		question = "是211吗?";
		ai = chat(sessionId, question);
		builder.append(String.format("用户问题:%s\n", question));
		builder.append(String.format("机器回复:%s\n", ai));

		System.out.println(builder.toString());
	}

	private String chat(String sessionId, String question) {
		ChatClient chatClient = ChatClient.builder(chatModel)
				.defaultSystem("你是AI助手小粒,所有回复均使用中文。")
				.defaultAdvisors(
						new SimpleLoggerAdvisor() // 输出聊天日志
				).build();

		// 1. 将用户输入添加到记忆
		UserMessage userMessage = new UserMessage(question);
		chatMemory.add(sessionId, userMessage);

		// 2. 获取记忆中的所有消息(上下文),传递给 AI 模型
		ChatClient.CallResponseSpec responseSpec = chatClient.prompt().messages(chatMemory.get(sessionId)).call();

		// 3. 将 AI 回复添加到记忆,更新上下文
		AssistantMessage assistantMessage = responseSpec.chatResponse().getResult().getOutput();
		chatMemory.add(sessionId, assistantMessage);

		// 4. 返回 AI 回复内容
		return assistantMessage.getText();
	}

}

配置可以参考 Spring AI 快速聊天示例,运行上述示例输出如下:

用户问题:四川大学是985吗?
机器回复:是的,四川大学是“985工程”大学之一。
用户问题:是211吗?
机器回复:是的,四川大学同时也是“211工程”大学之一。

通过一问一答,可以看出是具备记忆功能的。否则,问 AI“是211吗?”时,它怎么知道我问的是四川大学。

下面是打断点查看 InMemoryChatMemoryRepository 内部数据的截图:

内存存储(InMemoryChatMemoryRepository)

更多信息,请读者自行尝试。

  

提示:如果不能访问 OpenAI,请点击 AiCode API 注册账号,通过代理访问。  

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号