Agent 的长短期记忆系统怎么做的?记忆怎么存?粒度是多少?怎么用?
一则或许对你有用的小广告
欢迎加入小哈的星球,你将获得:专属的实战项目(4个项目都能学) / 1v1 提问 / 简历修改 / Java 学习路线 / 社群讨论 / 学习打卡 / 每月赠书
《Spring AI 项目实战(问答机器人、RAG 智能客服、联网搜索)》已完结,基于
Spring AI + Spring Boot 3.x + JDK 21...,查看介绍《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...,查看介绍;演示链接:http://116.62.199.48:7070/《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接:http://116.62.199.48/
新开坑项目:《从零手撸:秒杀系统高并发优化实战》 正在更新中...,查看介绍
截止目前,星球内专栏累计输出 150w+ 字,讲解图 5110+ 张,还在持续爆肝中.. 后续还会上新更多项目,已有 4700+ 小伙伴加入学习,欢迎点击围观
面试考察点
-
概念深度:看你脑子里的记忆分类是不是还停留在 "短 = 上下文窗口、长 = 向量库" 这种老说法。2026 年业界已经普遍换成 语义 / 情景 / 程序性 的细分类,答得出来说明你一直跟着社区走。
-
工程落地:记忆存哪?粒度怎么选?什么时候写入、什么时候淘汰?这些是没真搭过 Agent 的人答不细的。
-
框架熟练度:能不能结合
LangChain4j、Spring AI这类 Java AI 框架讲清楚实现机制,而不是含糊地说一句 "用向量库"。
核心答案
先说本质:大模型本身是纯无状态的,所谓 "记忆" 全靠应用层在每次请求时把历史信息塞回 Prompt。Agent 记忆系统干的就是这件事——让 LLM 在无状态的前提下,看起来像 "记得住事" 一样。
记忆系统可以拆成两层:
| 层次 | 作用 | 典型实现 | 生命周期 |
|---|---|---|---|
| 短期记忆(Short-Term / Working Memory) | 维持当前对话的上下文连贯 | 消息列表 + 窗口淘汰策略 | 单会话,重启即失 |
| 长期记忆(Long-Term Memory) | 跨会话积累用户画像、事件、技能 | 向量库 + 关系库 + 记忆引擎(如 Mem0) |
跨会话持久化 |
长期记忆再往下细分,是借了认知科学的三分法:
- 语义记忆(Semantic):事实/知识,比如 "用户偏好 Java、家有三只猫"
- 情景记忆(Episodic):事件/经历,比如 "6 月 3 日用户申请过退款"
- 程序性记忆(Procedural):技能/工作流,比如 "部署流程:先 build 再 push 到 staging"
深度解析
一、整体架构:先看一张全景图
这张图把 Agent 记忆的流转路径画出来了:
- 短期记忆是 Agent 的工作台:每次对话的实时上下文都放这里。LLM 上下文窗口有限,即便
Claude 4.6给到了 1M context,也不能无限往里塞,所以淘汰和压缩是必须的 - 长期记忆是 Agent 的记忆库:会话结束后,系统从短期记忆里挑出有价值的信息写入长期记忆,下次会话再按需召回
- 难点不在存,而在 "抽取" 和 "召回" 这两步:存什么?什么时候存?怎么召回相关的?这才是真正费脑筋的地方
二、短期记忆:窗口策略是灵魂
短期记忆看着简单,不就是把消息塞进 List 里吗?但窗口策略直接决定了对话连贯性和 Token 成本。
主流就三种策略:
| 策略 | 机制 | 优点 | 缺点 |
|---|---|---|---|
| 固定消息数(Message Window) | 保留最近 N 条消息 | 实现简单 | 长消息容易爆 Token |
| 固定 Token 数(Token Window) | 保留最近 N 个 Token 内的消息 | Token 成本可控 | 短消息可能切太狠 |
| 摘要窗口(Summarization) | 老消息压缩成摘要 + 保留最近若干条 | 兼顾上下文与成本 | 摘要质量依赖模型 |
LangChain4j 里的 SummarizingTokenWindowChatMemory 就是第三种。老消息超阈值时触发 LLM 生成摘要,比单纯淘汰聪明不少。
三、长期记忆:三分天下
为什么长期记忆要拆三种?因为它们的存储方式、检索方式、更新方式都不一样。
1. 语义记忆 —— 用户画像 / 事实库
- 存什么:用户的偏好、属性、长期有效的客观事实
- 粒度:实体级(Entity-level)/ 事实级(Fact-level),一句话或一个 KV
- 怎么存:向量化后存向量库(
Milvus、PGVector、Redis Vector),或用专门的记忆引擎如Mem0 - 怎么用:当前问题向量化,去检索 Top-K 相关事实,再注入 Prompt
举个例子,用户说 "我家猫又吐毛球了"。语义记忆应该抽出:{entity: "user", attribute: "has_pets", value: "cat"},下次再聊宠物话题就主动召回。
2. 情景记忆 —— 事件流水
- 存什么:具体发生了什么事、用户做过什么操作
- 粒度:事件级(Event-level),带时间戳
- 怎么存:时序数据库 / 关系库(如
PostgreSQL)+ 向量索引做语义检索 - 怎么用:用户问 "上周我跟你说过什么来着",就按时间和语义双维度召回
3. 程序性记忆 —— 工作流 / SOP
- 存什么:用户教过 Agent 的操作流程、项目特定的处理规则
- 粒度:规则级(Rule-level)/ 流程级(Workflow-level)
- 怎么存:结构化规则库、代码片段、Few-shot 示例
- 怎么用:用户说 "按上次那个流程来",就召回对应的工作流执行
四、记忆的粒度:从粗到细
面试官问 "粒度是多少",就是想听你把 存储单元的颗粒度 说清楚。下面是从粗到细的对比:
| 粒度 | 示例 | 适用场景 | 问题 |
|---|---|---|---|
| 会话级(Session) | 整个对话历史 | 简单客服 | 信息冗余大,召回精度低 |
| 轮次级(Turn) | 一问一答对 | 多轮对话回放 | 颗粒太细,噪声多 |
| 消息级(Message) | 单条 ChatMessage | 上下文拼接 | 短期记忆常用粒度 |
| 摘要级(Summary) | 一段对话压缩后的一句话 | 长对话归档 | 可能丢细节 |
| 实体/事实级(Entity/Fact) | "用户喜欢 Java" | 语义记忆首选 | 抽取质量依赖 LLM |
| 事件级(Event) | 带 timestamp 的动作记录 | 情景记忆 | 需要时序存储 |
生产环境的黄金组合:短期记忆用消息级 + 摘要级混合,长期记忆的语义部分用实体/事实级,情景部分用事件级。这也是 Mem0 这类记忆引擎在 2026 年事实上的标准做法。
五、代码示例(LangChain4j 实现)
先用 LangChain4j 看短期记忆怎么搞:
import dev.langchain4j.memory.chat.TokenWindowChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.model.openai.OpenAiTokenizer;
import dev.langchain4j.service.AiServices;
// 短期记忆方案一:基于消息条数的滑动窗口
ChatMemory messageWindow = MessageWindowChatMemory.builder()
.maxMessages(20) // 保留最近 20 条消息
.id("user-123") // 按用户/会话隔离
.build();
// 短期记忆方案二:基于 Token 数的滑动窗口(推荐,更可控)
OpenAiTokenizer tokenizer = new OpenAiTokenizer("gpt-4o");
ChatMemory tokenWindow = TokenWindowChatMemory.builder()
.maxTokens(2000, tokenizer) // 控制在 2000 token 以内
.id("user-123")
.build();
// 组装成带记忆的 AI Service
Assistant agent = AiServices.builder(Assistant.class)
.chatLanguageModel(model)
.chatMemoryProvider(memoryId ->
TokenWindowChatMemory.builder()
.maxTokens(2000, tokenizer)
.id(memoryId)
.build())
.build();
关键点有两个:
chatMemoryProvider会按需给每个memoryId(通常是userId或sessionId)开独立的记忆空间,多用户隔离就靠它TokenWindowChatMemory比按消息数更稳,生产环境推荐这种,避免几条长消息就把上下文打爆
长期记忆这块,LangChain4j 没有内置 "记忆抽取" 机制,得自己组合 VectorStore:
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.data.segment.TextSegment;
// 长期记忆:用向量库存放从对话中抽取的事实
EmbeddingStore<TextSegment> longTermMemory = new InMemoryEmbeddingStore<>();
// 每轮对话结束后,调用 LLM 抽取 "值得长期记住的事实"
public void extractAndPersist(String conversation, String userId) {
List<String> facts = factExtractor.extract(conversation); // 自定义 Prompt 让 LLM 输出事实
for (String fact : facts) {
TextSegment seg = TextSegment.from("[" + userId + "] " + fact);
embeddingStore.add(embeddingModel.embed(seg).content(), seg);
}
}
// 下次对话开始时,按当前问题召回相关长期记忆
public List<String> recallRelevant(String query, int topK) {
return embeddingStore.findRelevant(
embeddingModel.embed(query).content(),
topK
).matches().stream()
.map(m -> m.embedded().text())
.toList();
}
六、代码示例(Spring AI 实现)
Spring AI 1.x 的写法略有不同,核心是 ChatMemory + Advisor 机制:
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.VectorStoreChatMemoryAdvisor;
import org.springframework.ai.vectorstore.VectorStore;
@Configuration
public class AgentMemoryConfig {
@Bean
public ChatMemory chatMemory() {
// 短期记忆:窗口 20 条
return MessageWindowChatMemory.builder()
.maxMessages(20)
.build();
}
@Bean
public ChatClient agentClient(ChatClient.Builder builder,
ChatMemory chatMemory,
VectorStore vectorStore) {
return builder
.defaultAdvisors(
// 短期记忆 Advisor:自动注入对话历史
MessageChatMemoryAdvisor.builder(chatMemory)
.build(),
// 长期记忆 Advisor:把记忆写入向量库
VectorStoreChatMemoryAdvisor.builder(vectorStore)
.build()
)
.build();
}
}
几个要点:
MessageChatMemoryAdvisor负责短期记忆的自动注入和淘汰,靠conversationId区分不同会话VectorStoreChatMemoryAdvisor把消息持久化到向量库,适合长期记忆落地- 有个坑:流式响应下
VectorStoreChatMemoryAdvisor存在已知问题(参见 Spring AI GitHub Issue #3152),生产环境要注意兜底
七、记忆的 "使用时机":别只想着存
很多人答这题只讲 "怎么存",忘了讲 什么时候用。但记忆系统的读写时机才是真正的工程精髓:
整个流程拆解:
- 召回(Recall):每次回答前,用当前问题去长期记忆里语义检索 Top-K 相关条目。这一步别图省事召回一堆,3~5 条足够,多了反而稀释 Prompt
- 写入(Write):别把每条消息都写进长期记忆,那样噪声爆炸。正确做法是会话结束或定期触发记忆抽取,让 LLM 总结出有价值的事实再入库
- 淘汰(Eviction):长期记忆也得定期清理,过时的、错误的、低权重要淘汰。比如用户半年前喜欢的东西现在可能变了,
Mem0这类引擎会基于 "decay score" 自动降权
八、2026 年的前沿:记忆引擎成为独立赛道
最后聊聊行业趋势。2026 年 Agent 记忆领域最大的变化,是专门做记忆的引擎开始崛起,典型代表:
| 引擎 | 特点 |
|---|---|
Mem0 |
业界事实标准,统一管理语义/情景/程序性记忆,支持 LoCoMo、LongMemEval 基准 |
Zep |
基于时序知识图谱,擅长情景记忆 |
Letta(原 MemGPT) |
模仿操作系统虚拟内存的思路,自动分页 |
Cognee |
用知识图谱 + 向量做混合记忆 |
行业共识:记忆系统已经成了 Agent Stack 的独立一层,跟 LLM、向量库、工具调用并列。很多人把 2026 年叫作 "Agent Memory 元年"。
面试高频追问
-
记忆越来越多,召回精度怎么保证?
三板斧:Rerank 重排(先召回 Top-20,再用 Cross-Encoder 精排到 Top-5)、decay 衰减(按时间和使用频率降权)、记忆合并去重(相似事实自动合并)。
-
怎么防止 Agent 记住 "错的事"?
- 记忆写入前做事实校验,跟已有记忆做一致性检查
- 提供用户纠正机制,用户说一句 "不对,我其实是..." 就触发记忆覆盖
- 关键决策不依赖记忆,走 RAG 实时检索
-
长期记忆和 RAG 知识库啥区别?
- RAG 知识库:组织级的、共享的、相对确定的文档知识
- 长期记忆:个体级的、私有的、动态演化的个人经验
- 一个像 "公司 Wiki",一个像 "你的私人日记"
-
多用户场景下记忆怎么隔离?
每条记忆都带上
userId/sessionId元数据,存储和检索时都按这个字段过滤。ChatMemoryProvider里的memoryId就是干这个的。
常见面试变体
- "ChatGPT 的 Memory 功能是怎么实现的?"
- "你的 Agent 怎么记住用户的偏好?"
- "长期记忆存什么?怎么避免噪声?"
- "多轮对话的上下文怎么管理?爆 Token 怎么办?"
- "讲讲
Mem0这类记忆引擎的核心架构"
记忆口诀
短期靠窗口、长期分三类、粒度选实体、读写有时机、Rerank 保精度。
一句话解释:短期记忆拼的是窗口和摘要策略,长期记忆拼的是语义/情景/程序性三分法,粒度优先选实体/事实级,写入要克制、召回要重排。
总结
Agent 记忆系统的问题,重点从来不是 "用什么数据库",而是 记忆分类、粒度选择、读写时机 这三件事。面试时按 "短期(窗口策略)→ 长期(三种类型)→ 粒度(实体级黄金组合)→ 落地(LangChain4j / Spring AI 代码)→ 趋势(Mem0 记忆引擎)" 的顺序讲下来,既有深度也有广度。收尾补一句 "记忆引擎已经成为 Agent Stack 独立一层",基本能稳住整场。
