06 记忆系统面试题
1、什么是短期记忆?
短期记忆指的是 Agent 在一次会话或一次任务执行过程中保存的上下文信息。
比如用户刚才问了什么、模型已经回答了什么、调用过哪些工具、工具返回了什么结果,这些都属于短期记忆。
它的作用是让 Agent 能够理解当前对话的上下文,而不是每一轮都像第一次见到用户一样。
举个例子:
用户:帮我查一下北京今天的天气
助手:北京今天晴,最高温 28 度
用户:那明天呢?第二句话里的“那”指的是北京天气,如果没有短期记忆,模型就不知道用户在问什么。
在 LangGraph v1.0 里,短期记忆通常通过 checkpointer 实现。每个线程都有自己的 thread_id,LangGraph 会把状态保存下来,下一轮调用时再恢复出来。
简单理解就是:
thread_id = 一段会话
state = 当前会话状态
checkpointer = 保存和恢复 state 的组件LangChain v1.0 的 create_agent 底层也是基于 LangGraph 构建的,所以它的会话记忆也是依赖 LangGraph 的状态持久化能力。
2、什么是长期记忆?
长期记忆指的是可以跨会话、跨任务长期保存的信息。
它不是为了记录每一句对话,而是保存对以后有价值的信息,例如:
- 用户偏好
- 用户画像
- 常用配置
- 历史决策
- 项目背景
- 重要事实
比如用户多次提到“我喜欢回答简洁一点”,这个信息就适合进入长期记忆。下次用户重新打开一个新会话,系统仍然可以根据这个偏好调整回答方式。
短期记忆更像“这次对话的上下文”,长期记忆更像“对这个用户或业务的长期了解”。
在 LangGraph v1.0 里,长期记忆通常通过 Store 实现。Checkpointer 负责保存线程内的状态,Store 负责保存跨线程、跨会话的数据。
可以把两者区分成:
短期记忆:thread_id 维度,跟当前会话绑定
长期记忆:user_id / namespace 维度,跟用户或业务对象绑定3、为什么需要Memory?
Memory 的核心作用是让 Agent 有连续性。
没有 Memory 的系统,每次请求都是独立的。用户刚刚说过的信息,下一轮就丢了;用户长期形成的偏好,也无法沉淀下来。这样的 Agent 很难做复杂任务,只适合一次性问答。
有了 Memory 后,Agent 可以做到几件事:
第一,理解上下文。
用户说“继续”“换一种方式”“刚才那个方案”,系统能知道指的是什么。
第二,减少重复提问。
如果系统已经知道用户的技术栈、语言偏好、业务背景,就不需要每次都重新问。
第三,支持复杂任务。
很多 Agent 任务不是一轮完成的,比如写代码、查资料、做分析、执行多步流程,都需要保存中间状态。
第四,个性化回答。
不同用户关注点不同,有的人要原理,有的人要代码,有的人要结论。长期记忆可以让系统逐渐适应用户。
所以 Memory 不是简单地“保存聊天记录”,而是 Agent 从一次性工具变成长期助手的基础能力。
4、为什么不能直接把所有历史记录放进Prompt?
最直接的原因是上下文窗口有限。
大模型能接收的 Prompt 长度是有限的。历史记录越来越多后,不可能无限塞进去。即使模型支持很长上下文,也不代表应该全部放进去。
主要问题有几个。
第一,成本高。
Prompt 越长,调用成本越高,响应也越慢。很多历史内容其实对当前问题没有帮助,全部传进去是在浪费。
第二,噪声多。
历史记录里有大量无关信息,模型可能被干扰,反而抓不住当前问题的重点。
第三,容易产生冲突。
用户早期说过的话可能已经过期,比如以前用 MySQL,现在改成 PostgreSQL。如果全部放进去,模型可能不知道应该信哪一条。
第四,隐私和安全风险更高。
不是所有历史内容都应该反复进入模型上下文,尤其是包含敏感信息时。
更合理的做法是:
完整历史记录可以存起来
但每次只召回当前任务真正需要的部分也就是通过摘要、检索、过滤和排序,把相关记忆放进 Prompt,而不是把所有历史原样拼进去。
5、如何设计Memory架构?
一个比较实用的 Memory 架构,一般会分成四层。
第一层是会话状态层。
保存当前对话的消息、工具调用结果、中间变量等。它解决的是“当前任务进行到哪里了”。在 LangGraph v1.0 里,这一层通常用 checkpointer。
第二层是长期存储层。
保存跨会话的信息,比如用户偏好、用户画像、项目背景、业务事实等。在 LangGraph v1.0 里,这一层通常用 Store。
第三层是检索层。
当用户发起新请求时,系统需要从长期记忆中找出相关内容。这里可以用关键词检索、向量检索、标签过滤,也可以组合使用。
第四层是写入和治理层。
不是所有内容都应该写入记忆。系统要判断哪些信息值得保存,哪些信息应该更新,哪些信息已经过期,哪些信息不能保存。
整体流程可以这样理解:
用户输入
↓
读取短期记忆(当前会话状态)
↓
召回长期记忆(用户偏好、历史事实)
↓
组装 Prompt
↓
模型生成回答 / 调用工具
↓
更新短期记忆
↓
必要时写入长期记忆这里最重要的是分清边界:
短期记忆解决当前会话连续性
长期记忆解决跨会话连续性
检索层解决用什么记忆
治理层解决什么该记、什么不该记6、如何实现会话记忆?
会话记忆的重点是保存每一轮对话后的状态,并在下一轮恢复出来。
如果自己实现,可以用 session_id 或 thread_id 作为 key,把消息列表保存到 Redis、数据库或本地存储中。
简单结构如下:
{
"thread_id": "user-001-chat-001",
"messages": [
{"role": "user", "content": "帮我查北京天气"},
{"role": "assistant", "content": "北京今天晴"}
]
}下一轮用户继续提问时,系统根据 thread_id 取出历史消息,再和新问题一起传给模型。
在 LangGraph v1.0 中,推荐使用 checkpointer 来做这件事。开发时可以用内存版本,生产环境一般会换成数据库版本。
示例思路:
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import StateGraph
checkpointer = InMemorySaver()
app = graph.compile(checkpointer=checkpointer)
config = {
"configurable": {
"thread_id": "user-001-chat-001"
}
}
app.invoke(
{"messages": [{"role": "user", "content": "你好"}]},
config=config,
)同一个 thread_id 再次调用时,LangGraph 会自动恢复之前的状态。
如果用 LangChain v1.0 的 create_agent,也是类似思路:
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver
checkpointer = InMemorySaver()
agent = create_agent(
model="anthropic:claude-sonnet-4-5",
tools=[],
checkpointer=checkpointer,
)
agent.invoke(
{"messages": [{"role": "user", "content": "我叫张三"}]},
config={"configurable": {"thread_id": "chat-001"}},
)会话记忆实现时还要注意控制长度。不能无限保存所有消息给模型,通常会配合摘要或窗口截断,只保留最近几轮和关键信息。
7、如何实现跨会话记忆?
跨会话记忆不能只依赖 thread_id,因为 thread_id 通常只代表一段会话。用户开启新会话后,线程就变了,但用户本身没有变。
所以跨会话记忆一般要绑定到 user_id、tenant_id 或业务对象 ID 上。
例如:
user_id = u_001
namespace = memories/u_001保存的信息可能是:
{
"preferred_language": "中文",
"answer_style": "简洁直接",
"tech_stack": ["Python", "LangGraph", "Redis"]
}在 LangGraph v1.0 里,跨会话记忆通常用 Store。Checkpointer 管当前线程状态,Store 管长期数据。
大致流程是:
新会话开始
↓
根据 user_id 从 Store 查询长期记忆
↓
把相关记忆加入上下文
↓
完成回答
↓
如果发现新的长期偏好,再写回 Store示例思路:
from langgraph.store.memory import InMemoryStore
store = InMemoryStore()
namespace = ("memories", "user-001")
store.put(
namespace,
"preference",
{"answer_style": "简洁直接"},
)
memory = store.get(namespace, "preference")生产环境里,Store 后面一般会接数据库,比如 PostgreSQL、Redis、ElasticSearch 或向量数据库,具体取决于记忆类型和检索方式。
8、如何实现用户画像?
用户画像不是把用户所有聊天记录堆起来,而是把稳定、有用的信息结构化保存。
常见画像字段包括:
{
"user_id": "u_001",
"role": "后端开发工程师",
"language": "中文",
"tech_stack": ["Python", "FastAPI", "LangGraph"],
"answer_preference": "先给结论,再解释原因",
"business_context": "正在做企业知识库问答系统"
}画像的来源一般有三种。
第一,用户明确告诉系统。
例如:“以后都用中文回答”“我是 Java 后端”。这种信息可信度比较高,可以直接写入或经过确认后写入。
第二,从多轮对话中归纳。
比如用户多次要求“别写太复杂”,系统可以归纳出用户偏好简洁回答。但这种信息最好不要太武断,需要保留证据或置信度。
第三,从业务系统同步。
例如用户的角色、部门、权限、购买套餐等,这类信息应该来自业务数据库,而不是让模型猜。
实现时要注意两点。
第一,画像要结构化。
不要只保存一段长文本,否则后面很难检索、更新和判断冲突。
第二,画像要可更新。
用户偏好会变,技术栈也会变。画像最好带上更新时间和来源。
{
"key": "answer_preference",
"value": "简洁直接",
"source": "user_explicit",
"updated_at": "2026-06-16"
}这样后续如果出现冲突,可以优先相信更新、更可靠的信息。
9、如何实现长期记忆存储?
长期记忆存储要先看记忆类型,不同类型适合不同存储方式。
如果是用户偏好、用户画像、配置项,适合用关系型数据库或 KV 存储,因为它们结构清晰,经常按 key 查询。
例如:
user_id + memory_key -> memory_value如果是历史经验、文档片段、对话摘要这类非结构化内容,适合用向量数据库,因为后续通常是按语义相似度召回。
如果是短期状态或高频读写的缓存,可以用 Redis。
一个常见设计是:
PostgreSQL:保存结构化长期记忆
Redis:保存会话缓存和热点记忆
Vector DB:保存可语义检索的文本记忆
Object Storage:保存大文件或原始材料长期记忆表可以设计成这样:
CREATE TABLE agent_memories (
id BIGSERIAL PRIMARY KEY,
user_id TEXT NOT NULL,
memory_type TEXT NOT NULL,
memory_key TEXT,
content TEXT NOT NULL,
metadata JSONB,
importance INT DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);如果需要向量检索,可以增加 embedding 字段,或者同步到专门的向量库。
长期记忆存储还要考虑删除能力。用户要求删除记忆时,系统必须能找到并删除,而不是散落在各处无法清理。
10、如何实现记忆召回?
记忆召回就是在用户发起请求时,从记忆库里找出当前问题真正需要的内容。
常见做法有三种。
第一,规则召回。
根据 user_id、memory_type、标签等条件直接查询。
例如用户画像、语言偏好、权限信息,这类不需要向量检索,直接按 key 查就行。
第二,关键词召回。
适合查找包含明确关键词的记忆,比如项目名、产品名、错误码。
第三,向量召回。
适合语义相似的场景。比如用户问“上次那个接口超时的问题”,不一定包含原始记忆里的关键词,但语义接近,可以用 embedding 检索。
实际项目中通常会混合使用:
用户问题
↓
查用户画像和固定偏好
↓
做关键词召回
↓
做向量召回
↓
合并结果
↓
过滤、排序、去重
↓
放入 Prompt召回后不要直接全部塞给模型,还要做排序。一般会考虑:
- 相关性
- 重要性
- 新旧程度
- 可信来源
- 是否和当前任务冲突
最终只选少量最有用的记忆进入上下文。
11、如何避免无关记忆被召回?
避免无关记忆被召回,关键不是只靠向量相似度。
向量检索会找“语义相似”的内容,但语义相似不代表当前任务需要。比如用户问“Python 内存优化”,可能召回到“用户喜欢简洁回答”之外的一堆历史 Python 讨论,但很多都没用。
常见处理方式有几个。
第一,加过滤条件。
召回前先按用户、租户、项目、记忆类型过滤,避免跨用户、跨项目召回。
user_id = 当前用户
project_id = 当前项目
memory_type in [preference, fact, summary]第二,给记忆打标签。
保存时就标好类型,例如:
preference:用户偏好
profile:用户画像
project:项目背景
task:任务摘要
fact:事实信息召回时按类型取,不要混在一起。
第三,设置相似度阈值。
低于阈值的向量结果直接丢弃,避免硬凑结果。
第四,做二次重排。
先召回一批候选,再用 reranker 或 LLM 判断哪些真的和当前问题相关。
第五,限制数量。
即使召回了很多,也只取最相关的几条。记忆不是越多越好,放太多反而影响模型判断。
最后还要处理过期记忆。比如用户以前说用 Vue,后来项目改成 React,旧记忆如果不更新,就会误导模型。
12、如何做Memory压缩?
Memory 压缩的目的,是在不丢掉关键信息的前提下,减少上下文长度和存储成本。
常见压缩方式有几种。
第一,窗口截断。
只保留最近 N 轮对话,适合简单聊天场景。
只保留最近 10 条消息
更早的消息不直接进入 Prompt第二,摘要压缩。
把早期对话总结成一段摘要,替代完整消息。
前 30 轮对话 -> 一段会话摘要
最近 5 轮对话 -> 原文保留第三,结构化提取。
从对话中提取稳定信息,比如用户偏好、项目背景、关键决策,然后保存成结构化字段。
{
"preference": "回答要简洁",
"project": "企业知识库问答系统",
"decision": "使用 LangGraph 编排 Agent 流程"
}第四,去重合并。
如果多条记忆表达的是同一件事,就合并成一条,避免重复召回。
第五,按重要性保留。
不是所有内容都同等重要。临时闲聊可以丢弃,明确偏好、关键业务事实、长期决策应该保留。
在 LangGraph 里,常见做法是把完整状态交给 checkpointer 保存,但进入模型上下文前做裁剪或摘要,而不是把 checkpoint 里的所有内容都传给模型。
13、如何做Memory摘要?
Memory 摘要就是把一段长对话压缩成短文本,同时保留对后续有用的信息。
一个好的摘要不应该只是流水账,而应该提取这些内容:
- 用户目标
- 已经确认的事实
- 已经做过的操作
- 关键结论
- 未完成事项
- 用户偏好
例如一段对话很长,摘要可以写成:
用户正在设计一个基于 LangGraph 的客服 Agent。
已确认使用 PostgreSQL 保存结构化记忆,使用向量库保存历史问题摘要。
用户偏好中文回答,要求示例代码尽量简单。
当前还没有决定是否引入 Redis。实现时通常有两种方式。
第一,定期摘要。
每隔固定轮数,比如 10 轮,对之前消息做一次摘要。
第二,超过阈值后摘要。
当上下文长度超过限制时,把较早的消息压缩成摘要。
摘要时要注意不要过度加工。模型不能把不确定的信息写成确定事实,也不能把用户没有表达过的偏好强行总结出来。
比较稳妥的 Prompt 可以这样写:
请总结以下对话中对后续任务有用的信息。
只保留明确出现的信息,不要推测。
按:用户目标、已确认事实、待办事项、用户偏好 四类输出。摘要生成后,可以替换旧消息,也可以作为长期记忆的一部分保存。
14、Redis在Memory体系中的作用是什么?
Redis 在 Memory 体系里主要适合做高频、短期、低延迟的存储。
常见用途有几个。
第一,保存会话缓存。
比如当前会话的最近消息、临时状态、中间结果。Redis 读写快,适合频繁访问。
第二,保存热点记忆。
有些长期记忆虽然存放在数据库里,但访问非常频繁,可以缓存到 Redis,减少数据库压力。
第三,做过期控制。
Redis 天然支持 TTL,适合保存有生命周期的记忆。
临时验证码:5分钟过期
会话状态:24小时过期
任务中间结果:1小时过期第四,做分布式锁或任务状态。
多个 Agent 实例同时处理同一个用户或任务时,可以用 Redis 控制并发,避免状态被覆盖。
但 Redis 不适合保存所有长期记忆。原因是它主要是内存型存储,成本较高,而且复杂检索能力有限。
比较合理的分工是:
Redis:短期状态、热点缓存、临时结果
数据库:结构化长期记忆
向量数据库:语义检索记忆所以 Redis 在 Memory 系统里更像加速层和临时状态层,而不是唯一的记忆库。
15、向量数据库在Memory体系中的作用是什么?
向量数据库的主要作用是做语义检索。
普通数据库擅长按字段查,比如:
user_id = u_001
memory_type = preference但很多记忆不是靠精确字段能查出来的。
比如用户问:
上次我们讨论的那个登录超时问题,后来怎么定的?这句话里可能没有出现原始记忆中的关键词。如果只靠关键词搜索,可能查不到。但向量检索可以根据语义相似度,把相关的历史摘要找出来。
向量数据库一般会保存三类信息:
{
"id": "mem_001",
"content": "用户之前讨论过登录接口超时,决定先增加超时日志,再排查数据库慢查询。",
"embedding": [0.12, 0.34, ...],
"metadata": {
"user_id": "u_001",
"project_id": "p_001",
"memory_type": "task_summary",
"created_at": "2026-06-16"
}
}检索流程通常是:
用户问题
↓
生成 query embedding
↓
向量库相似度搜索
↓
按 metadata 过滤
↓
取 TopK 结果
↓
必要时重排
↓
放入 Prompt向量数据库适合存对话摘要、历史问题、文档片段、经验记录等非结构化内容。
但它不适合替代所有存储。像用户语言偏好、权限、开关配置这些精确数据,还是应该放在关系型数据库或 KV 存储中。
一句话总结:向量数据库解决的是“意思相近但关键词不一定相同”的记忆召回问题。