ARTICLE · 1248848
AI记忆系统设计原理
不知道你有没有过这种体验。
隔了几天重新打开 AI 对话,继续上次的话题,AI 无缝接得上之前的聊天。
很多人会以为,是大模型自己记住了对话内容。但真相很反直觉:模型本身没有记忆,记忆全部来自外部系统。
模型是一个无状态函数。每一次调用,它只处理当下给到的 token 序列,预测下一个 token。训练完成之后,模型权重就固定不变。你和它说的任何话,都不会修改模型本身。
底层真相:所有记忆技术本质上都在回答同一个问题,这一次调用,到底哪些内容应该放进上下文?
模型只是无状态函数
简单看 API 调用逻辑就会一目了然。
调用 1:用户说我的名字是 Shayan。模型给出回复。
调用 2,如果不带上历史,直接问"我叫什么名字?",模型会回复不知道。
调用 2 带上全部历史消息,模型才可以正确说出你的名字。
不是模型记住,是后端把完整历史,再次塞进本次请求传给模型。
后端数据库存储全部聊天记录。用户发送新消息,后端读取完整会话历史,拼接上新提问,整体提交给模型。模型相当于把全部对话从头再读一遍。
随之而来一个现实问题。每一轮对话新增几百 token,对话越长,提交的 token 总量越大,算力开销会随对话长度快速上涨。缓存、摘要压缩,都是为了解决这个痛点诞生。
记忆存在的三个位置
记忆可以划分成三层,非常好理解。
类比电影《记忆碎片》主角 Leonard,大脑无法生成新记忆,只能靠写笔记、纹身,每次做事重新阅读笔记。LLM 应用就相当于这个主角,外加一本笔记本。
所有记忆方案,滑动窗口、摘要、RAG、记忆工具,全部都是在解决一件事:哪些外部记忆,需要放进这一轮的上下文。
KV 缓存不在这张表里,缓存不等于记忆。
KV 缓存到底是什么
这是很多人最容易混淆的知识点。
模型读取每一个 token,会生成 Key(K) 和 Value(V) 向量。
Key:相当于索引标签,标记这个 token 是什么
Value:承载 token 携带的信息
做注意力计算的时候,新 token 生成 Query(Q),去匹配历史全部 token 的 Key,调取对应的 Value,完成指代理解。
KV 缓存,就是 GPU 中保存 K、V 向量的表格。
每一个 token 的 K、V 只依赖它前面的内容,不会被后续 token 改变。所以不用每一轮全部重新计算,直接复用表格内容。
举个对比,1000 token 提示词,生成 100 token 输出:
不使用缓存:要计算约 105000 行向量
使用 KV 缓存:预填充 1000 token 一次,之后每一步新增一行,约 1100 行
大幅减少算力消耗。但缓存只是计算加速手段,不新增任何信息。删掉缓存,可以基于原始 token 完整重建。丢失缓存只会多花钱、变慢,不会丢失业务记忆。它不等于业务侧的回答缓存。
提示缓存,跨请求复用 KV 表
普通 KV 缓存,单次请求结束就销毁。
提示缓存 Prompt Caching,就是服务端短暂保存这份 KV 表格。下一次请求开头 token 序列完全一模一样,就可以跳过重复计算。
这里有一条硬性规则:缓存生效,必须从第一个 token 开始,token 序列完全一致的前缀。只要中间任意一处 token 改动,这之后全部缓存失效。
很多人踩坑,把会变化的时间戳放在 system prompt 最开头,每一次请求前缀都变,缓存完全无法命中。
✅ 正确的排布思路:把稳定不变内容放前面,频繁变动内容放在最后。
工具定义(几乎不变)
系统提示词
静态文档
对话历史(只会在末尾追加)
用户最新消息(每次变化)
变化的时间、动态信息,应该放到最新用户消息内部,而不是放在 system 头部。
善用提示缓存,可以极大降低长文档反复问答场景的 token 开销,同时降低首字延迟。但提示缓存生命周期只有数分钟,会话隔几天再打开,缓存早就过期。能继续对话,依靠的是数据库保存原始消息,重新预填充。
上下文装不下怎么办
上下文窗口有三重限制。
硬限制:超过窗口大小直接 API 报错
成本限制:token 越多,耗时、开销越高
质量限制:长文本中间内容模型利用率会下降,也就是论文提到的"Lost in the Middle"现象。实际有效上下文,小于宣传的最大窗口。
滑动窗口以及隐藏陷阱
最简单方案,只保留最近 N 条消息。成本低,但更早的信息直接彻底消失。
还有一个极易踩坑的缓存陷阱。如果每一轮直接丢掉最老一条消息,整个对话历史的开头 token 一直在变化。缓存会每一轮全部失效。
优化方案:批量驱逐。窗口涨到 40 条消息,一次性裁剪回 20 条。两次裁剪之间,历史只在末尾追加,维持前缀不变,保障缓存命中率。
上下文压缩摘要
当对话消耗达到预算 80% 左右,调用 LLM 把早期对话总结成摘要,保留最近消息原文。
注意摘要是有损操作。摘要之上再做摘要,信息会持续衰减,类似传话游戏。必须明确告诉摘要模型,哪些信息强制保留:用户偏好、决策、ID、路径数字,丢弃已经解决的内容。
Agent 还可以清理旧的工具返回结果,这类内容往往占大量 token,可以替换成简短占位标记。
生产系统通用排布
【工具定义+系统提示词】(缓存断点,高度稳定)用户固定事实快照(很少变动)历史对话摘要(部分场景)最近 N 条原始对话(仅末尾增长)最新用户消息+动态信息(每次变化)稳定内容前置,变动内容后置,最大化缓存收益。
跨会话的长期记忆
新开启一轮全新聊天,历史完全为空。想要记住几周前用户说过的偏好,滑动窗口、摘要都解决不了。
典型场景:外卖助手,用户之前说披萨不要洋葱。三周新开会话,依旧要记住这条偏好。
完整流水线分为四步:
提取:单独调用轻量 LLM,从对话中抽取出长期有效的事实:身份、偏好、待办约束。临时情绪忽略。
合并整理:这是最难的环节。新增事实要和已有记忆做比对,判断新增、更新、删除还是不处理,处理新旧事实矛盾。
持久存储:存入数据表,带上来源会话 ID、时间戳,方便溯源。
注入上下文:新会话开启,把记忆送入上下文;记忆量大,则靠检索挑选相关片段。
工程最佳实践:
提取记忆放到后台异步执行,不要阻塞用户响应。长期记忆是为下一次会话服务。
会话启动时刻做一次记忆快照。会话中途更新记忆,会破坏整个前缀,让缓存全部失效。
长期记忆会带来一系列风险:生成错误事实、内容过时、记忆注入攻击、隐私合规问题,都需要提前设防。
RAG 检索,从海量记忆挑出相关内容
当用户记忆数据量很大,不可能全部塞进上下文。就需要检索,只挑选当前问题相关片段。
向量嵌入把文本转为向量,语义相近文本向量靠近,配合余弦相似度做匹配。
只靠向量检索有三类典型失效场景,需要配套手段修复:
编号、订单 ID 这类精确字符,向量很难识别,搭配 BM25 关键词混合检索;
指代模糊问句(另外那个呢),先让小模型把问句补全为独立完整问题;
时间语义(上周那个 bug),把时间解析成过滤条件,不要指望向量理解时间。
检索返回的内容属于每次提问都会变化,根据缓存前缀规则,要放在靠近用户新消息的末尾,不能放到 system 提示头部。
Agent 自主管理记忆
前面的模式,全部是业务代码决定哪些内容送入上下文。
Agent 记忆模式反过来:给模型提供工具,由模型自主决定什么时候读取、写入记忆。
提供 remember 保存记忆、recall 查询记忆的工具。模型发起工具调用,真正读写数据库的依旧是后端代码,模型不能直碰存储。
流水线驱动记忆:代码控制,行为稳定可控;缺点,全部请求都会执行逻辑。
Agent 驱动记忆:按需调用工具,存在模型忘记调用工具的风险;工具返回追加到上下文末尾,天然不会破坏前缀缓存。
多数实际项目,会采用两者混合。必须永远把 user_id 由会话传入,不接收模型输入,做好权限隔离,防止记忆泄露、注入攻击。
一次完整请求全流程复盘
用户打开全新会话,发送"帮我推荐一款披萨"。
后端加载系统提示、工具、该用户会话快照,会话历史为空。
按照稳定到变动的顺序组装 prompt,设置缓存断点。
服务端命中缓存,静态部分不用重复计算。
模型预填充剩余 token,构建 KV 缓存。
模型调用 recall 工具,检索用户饮食偏好,检索结果追加上下文末尾。
模型逐 token 输出回答,复用 KV 缓存。
后端把本轮对话存入数据库。
后台异步:提取对话中新的长期事实,合并更新用户记忆,留给下一次会话使用。
模型输出结束那一刻,就已经把一切忘掉。整套记忆能力,全部是周边系统完成。
这套体系解决不了什么
检索会漏,漏检就会产生幻觉;并不是记忆存得越多效果越好,无关信息反而干扰模型。
摘要、抽取记忆属于有损,会出错,需要评估测试。
每多一层机制,就多一层成本、延迟、故障点。很多简单场景,仅仅依靠提示缓存,完全不需要复杂长期记忆。
持久记忆属于个人数据,必须处理查看、编辑、删除的隐私合规。
很多人以为大模型自带记忆能力。真相恰恰相反。
记忆不属于模型,记忆是状态。和软件系统里所有状态一样,它要有存储位置、负责人,还要有过期、淘汰策略。
所谓 AI 记住了你,本质是你的系统记住了你。















































































