掘金 人工智能 09月17日
理解大型语言模型的“记忆”:Chat Memory机制
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

大型语言模型(LLM)本身是无状态的,这限制了它们在多轮对话中保持上下文的能力。Chat Memory机制应运而生,它允许我们存储和检索LLM的交互历史,从而维持对话的连贯性。手动实现Chat Memory需要维护对话记录、将其拼接到prompt中,并管理历史记录窗口以防止Token超限。Spring AI框架通过ChatMemory抽象和默认的MessageWindowChatMemory实现,简化了这一过程,它能够根据预设策略(如保留最近N条消息)自动管理对话历史,并支持不同的存储库,解决了内存易失和分布式部署的挑战。

🧩 **Chat Memory的核心目标是解决LLM的无状态问题**:大型语言模型本身不保留先前交互的信息,这使得在多轮对话中维持上下文和状态变得困难。Chat Memory机制通过存储和检索历史交互信息,使得LLM能够“记住”之前的对话内容,从而提供更连贯、有意义的交互体验。

🟢 **手动实现Chat Memory的挑战与Spring AI的简化**:在没有框架帮助的情况下,开发者需要手动处理会话记录的保存、检索,以及将历史对话拼接进prompt,并管理对话窗口大小以防止Token超限。Spring AI通过ChatMemory抽象和默认的MessageWindowChatMemory实现,自动化了这些繁琐的工作,提供了开箱即用的解决方案。

🟡 **MessageWindowChatMemory的策略与优势**:Spring AI默认使用的MessageWindowChatMemory采用“滑动窗口”机制,它能够根据预设的窗口大小(如保留最近N条消息)自动管理对话历史。其特殊规则包括优先淘汰UserMessage和AssistantMessage,保留SystemMessage,并确保新的SystemMessage能够覆盖旧的,从而有效解决了上下文窗口限制和系统指令更新的问题。

啥是chat memory呢?

我们借用官方文档的描述:

Large language models (LLMs) are stateless, meaning they do not retain information about previous interactions. This can be a limitation when you want to maintain context or state across multiple interactions. To address this, Spring AI provides chat memory features that allow you to store and retrieve information across multiple interactions with the LLM.

大型语言模型(LLM)是无状态的,这意味着它们不会保留你们之间 之前交互的信息。如果你希望在多次交互中保持上下文或状态时,就要使用chat memory 机制

如何实现chat memory

我们抛开spring ai 框架来说,如果想要LLM记住我们之间的多轮对话,就需要有个地方去存储对话的记录,我们称之为上下文。 因此,我们需要手动维护每次请求的上下文,即 Context,然后把上一次请求过的内容手动加入到下一次请求中,让 LLM 大模型能正确看到此前我们都聊了什么。

在实际应用中,系统会将多轮对话按顺序拼接成一个长文本,作为模型的输入。通常会加入角色标记(如“用户:”、“助手:”)来帮助模型区分不同发言者。

格式示例:

用户:你好助手:你好!有什么我可以帮你的吗?用户:我想订一张去上海的机票助手:好的,您计划什么时候出发?用户:下周五

模型会基于这个完整的上下文生成下一句回复。

随着 chat 调用次数的不断增多,messages 列表的长度也在不断增加,这意味着每次请求所消耗的 Tokens 数量也在不断增加,并且最终会在某个时间点,messages 中的消息所占用的 Tokens 超过了大模型支持的上下文窗口大小。所以会有某种策略来保持 messages 列表的消息数量在一个可控的范围内,例如,每次只保留最新的 20 条消息作为本次请求的上下文。

也就是说如果我们不使用框架的帮助,需要手动的做很多工作,例如:

    手动实现保存会话记录手动实现检索历史记录手动实现将历史对话记录拼接到prompt中手动管理历史记录的窗口,防止窗口无限增长带来的问题

基于spring ai实现的chat memory

依旧借用官方文档的描述:

The ChatMemory abstraction allows you to implement various types of memory to support different use cases. The underlying storage of the messages is handled by the ChatMemoryRepository, whose sole responsibility is to store and retrieve messages. It’s up to the ChatMemory implementation to decide which messages to keep and when to remove them. Examples of strategies could include keeping the last N messages, keeping messages for a certain time period, or keeping messages up to a certain token limit.

大致的意思是说:ChatMemory是一个抽象规范,用来实现对message的保留和移除。但是底层依赖于ChatMemoryRepository来实现具体的功能,ChatMemoryRepository的唯一功能就是store and retrieve message

看一下ChatMemory的定义:

public interface ChatMemory {   String DEFAULT_CONVERSATION_ID = "default";   /**    * The key to retrieve the chat memory conversation id from the context.    */   String CONVERSATION_ID = "chat_memory_conversation_id";   /**    * Save the specified message in the chat memory for the specified conversation.    */   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));   }   /**    * Save the specified messages in the chat memory for the specified conversation.    */   void add(String conversationId, List<Message> messages);   /**    * Get the messages in the chat memory for the specified conversation.    */   List<Message> get(String conversationId);   /**    * Clear the chat memory for the specified conversation.    */   void clear(String conversationId);}

从上面的定义我们看出ChatMemory定义了三个接口,它不要求具体的实现,只是定义了规范。

    add(String conversationId, List<Message> messages) 给对话添加消息List<Message> get(String conversationId) 获取某个对话的消息void clear(String conversationId) 清空对话的消息

spring ai对chatMemory的默认实现

Spring AI auto-configures a ChatMemory bean that you can use directly in your application. By default, it uses an in-memory repository to store messages (InMemoryChatMemoryRepository) and a MessageWindowChatMemory implementation to manage the conversation history. If a different repository is already configured (e.g., Cassandra, JDBC, or Neo4j), Spring AI will use that instead.

@AutoConfiguration@ConditionalOnClass({ ChatMemory.class, ChatMemoryRepository.class })public class ChatMemoryAutoConfiguration {   @Bean   @ConditionalOnMissingBean   ChatMemoryRepository chatMemoryRepository() {      return new InMemoryChatMemoryRepository();   }   @Bean   @ConditionalOnMissingBean   ChatMemory chatMemory(ChatMemoryRepository chatMemoryRepository) {      return MessageWindowChatMemory.builder().chatMemoryRepository(chatMemoryRepository).build();   }}

根据官方文档说明spring ai 自动配置了MessageWindowChatMemory,并且它内部默认使用的是InMemoryChatMemoryRepository来管理对话历史。

大模型对话系统中用于管理多轮对话上下文记忆的实现类,名为 MessageWindowChatMemory。它实现了 “滑动窗口式对话记忆”(Message Window) 的机制,是实际工程中非常常见且高效的一种方式。

🧩 一、核心目标:解决什么问题?

在大模型多轮对话中,模型的输入受限于 上下文窗口长度(如 8K、32K tokens)。如果对话轮数太多,就会超出限制。

👉 所以需要一种机制:只保留“最近 N 轮”对话,丢弃太早的历史,这就是“消息窗口”的由来。

规则说明
🟢 新 SystemMessage 加入时删除所有旧的 SystemMessage(只保留最新的系统指令)
🟡 消息超限时淘汰策略优先淘汰 UserMessage 和 AssistantMessage保留 SystemMessage

它的整体思路是:

    消息总数不能超过窗口的总数。系统消息特殊处理:新的系统消息要覆盖旧的系统消息,此时如果超过总数,那么淘汰UserMessage 和 AssistantMessage。这么做好处 实现了系统消息的“覆盖”语义:新指令来了,旧指令作废。

举例:• 旧消息中有:SystemMessage("你是客服")

• 新消息中加入:SystemMessage("你是程序员") → 这是一个新的 SystemMessage

spring ai 中如何使用MessageWindowChatMemory

上面我们讲解过,spring ai默认的配置就是MessageWindowChatMemory,可以选择构造器注入的方式

方式1:直接注入

@RestControllerpublic class ChatWithMemoryController {    private final ChatClient chatClient;    private final ChatMemory chatMemory;    public ChatWithMemoryController(ChatClient.Builder builder,ChatMemory chatMemory) {        this.chatMemory = chatMemory;        MessageChatMemoryAdvisor memoryAdvisor = MessageChatMemoryAdvisor.builder(this.chatMemory)                .build();        this.chatClient = builder                .defaultSystem("你是一个非常有经验的知识助手")                .defaultAdvisors(memoryAdvisor,new SimpleLoggerAdvisor())                .build();    }    @GetMapping(value = "/v2/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)    public Flux<ChatResponse> chat(@RequestParam String question) {        return chatClient.prompt()                .user( question)                // 添加参数 会话ID ,先默认1,用于调试                .advisors(advisorSpec -> advisorSpec.param(CONVERSATION_ID,"1"))                .stream()                .chatResponse();    }

方式2:也可以选择自己构建,设计上大部分场景我们需要自定义,因为默认的聊天记忆是保存在内存中的,这么做第一个问题是宕机会消失,第二个问题是没法做分布式。

MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder()        .maxMessages(10)        .build();this.chatMemory = chatMemory;MessageChatMemoryAdvisor memoryAdvisor = MessageChatMemoryAdvisor.builder(chatMemory)        .build();this.chatClient = builder        .defaultSystem("你是一个非常有经验的知识助手")        .defaultAdvisors(new QuestionAnswerAdvisor(vectorStore), memoryAdvisor)        .build();

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

Chat Memory LLM Spring AI 对话管理 上下文记忆 MessageWindowChatMemory 大型语言模型
相关文章