1. “记忆”到底是什么

大语言模型本身通常是“无状态”的:每次请求只看得到你这一次发给它的内容。

之所以看起来“记得之前说过什么”,本质是应用在每次请求时,把一段历史对话一起带给模型

一个直观例子:

  • 第 1 轮
    • 问:10 个苹果分给 2 个人,每人多少个?
    • 模型看到:10 个苹果分给 2 个人,每人多少个?
    • 模型答:5 个
  • 第 2 轮
    • 问:如果有 5 个人呢?
    • 模型看到:10 个苹果分给 2 个人... + 模型答 5 个 + 如果有 5 个人呢?
    • 模型答:2 个

所以:把所有问题都塞在同一个对话里,历史越长,token 消耗越快,成本也越高。

2. Spring AI 的“记忆”怎么做

Spring AI 通常把“记忆”拆成三件事:

  • 存放聊天记录的地方:内存 / 数据库 / 其他存储
  • 控制取多少历史:常见是“只取最近 N 条”
  • 自动读写历史:每次调用模型前取历史、调用后把新消息写回去

需要特别记住一个概念:会话 ID(conversationId)

  • 这是用来区分“这一段对话属于谁/属于哪个聊天窗口”的标识
  • 每次请求都必须传同一个 conversationId,才能续上上一轮的历史

3. 存到内存(适合学习 / 开发)

3.1 手动管理(更直观,但不够优雅)

import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;

public class MemoryDemo {

    private final ChatModel chatModel;

    public MemoryDemo(ChatModel chatModel) {
        this.chatModel = chatModel;
    }

    public void run() {
        ChatMemory chatMemory = MessageWindowChatMemory.builder()
                .maxMessages(10)
                .build();

        String conversationId = "007";

        chatMemory.add(conversationId, new UserMessage("My name is James Bond"));
        ChatResponse res1 = chatModel.call(new Prompt(chatMemory.get(conversationId)));
        chatMemory.add(conversationId, res1.getResult().getOutput());

        chatMemory.add(conversationId, new UserMessage("What is my name?"));
        ChatResponse res2 = chatModel.call(new Prompt(chatMemory.get(conversationId)));
        chatMemory.add(conversationId, res2.getResult().getOutput());
    }
}

3.2 自动管理(更常用、更优雅)

核心思路:给 ChatClient 加一个“记忆增强器”,让它自动做:

  • 调用前:按 conversationId 取历史
  • 调用后:把新消息写回去
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.memory.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.model.ChatModel;

public class ChatService {

    private final ChatClient chatClient;
    private final String conversationId = "007";

    public ChatService(ChatModel chatModel) {
        ChatMemory chatMemory = MessageWindowChatMemory.builder()
                .maxMessages(10)
                .build();

        this.chatClient = ChatClient.builder(chatModel)
                .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
                .build();
    }
		
	@GetMapping("/ai")
    public String chat(String userInput) {
        return chatClient.prompt()
                .user(userInput)
                .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId))
                .call()
                .content();
    }
}

4. 存到数据库(JDBC,适合生产)

当你希望“重启不丢”“多实例共享”“可追溯”,就要把聊天记录存到数据库。

4.1 引入依赖

Maven:

<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId>
</dependency>

4.2 建表(很关键)

JDBC 方式会用一张表保存聊天记录(默认表名 SPRING_AI_CHAT_MEMORY)。

默认情况下,只有“内嵌数据库”才会自动建表。对于 MySQL / PostgreSQL 这类常见数据库,一般需要显式开启:

spring.ai.chat.memory.repository.jdbc.initialize-schema=always

4.3 配置代码:把“窗口记忆”绑定到“JDBC 存储”

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.memory.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.memory.repository.ChatMemoryRepository;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AiMemoryConfig {

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

    @Bean
    public ChatClient chatClient(ChatModel chatModel, ChatMemory chatMemory) {
        return ChatClient.builder(chatModel)
                .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
                .build();
    }
}

说明:

  • ChatMemoryRepository 会由 JDBC starter 自动装配成数据库实现
  • MessageWindowChatMemory 负责“只拿最近 N 条”,避免 token 失控

4.4 Controller 调用:每次都带 conversationId

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AIController {

    private final ChatClient chatClient;

    public AIController(ChatClient chatClient) {
        this.chatClient = chatClient;
    }

    @GetMapping("/ai")
    public String generation(@RequestParam String userInput,
                             @RequestParam String conversationId) {
        return this.chatClient.prompt()
                .user(userInput)
                .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId))
                .call()
                .content();
    }
}

PS:由于本人学习的不够深刻可能会出现错误,请多多包涵

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐