ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

大模型记忆机制全解析:从KV Cache到RAG的优化实践

大模型记忆机制全解析:从KV Cache到RAG的优化实践 大模型说到底是“记东西的机器”训练阶段把知识写进权重推理阶段把对话内容写进缓存要用业务资料时再从外部数据库里捞。只要把记忆这件事想清楚上下文长度、显存占用、RAG 效果、长文本崩坏这些问题基本都能找到答案。这次清华唐杰团队把视线聚焦在“大模型记忆全景”上本身就是一件值得展开聊的事。唐杰团队长期做 GLM 系列大模型从基座训练到对齐部署都有完整链路这次从记忆机制入手等于把模型的上下文利用、参数存储、外部检索这些能力放到同一张图里重新审视。本文不重复论文细节而是顺着“记忆全景”这条主线把大模型到底靠什么记住东西、记忆为什么吃显存、长上下文怎么扩展、RAG 为什么是外挂硬盘以及本地部署时怎么调优全部拆开讲清楚。如果你关心大模型部署、长上下文调参、显存优化、RAG 应用开发或者想搞明白“为什么模型答着答着就忘了前面说的话”这篇文章可以直接收藏。1. 核心信息速览先把这篇文章涉及的记忆机制和工程影响整理成一张表后面逐项展开。项目说明研究对象大模型记忆机制全景记忆三大形态参数记忆、上下文记忆、外部记忆上下文记忆核心KV Cache直接决定推理显存占用长上下文扩展位置编码、滑动窗口、局部注意力、上下文压缩外部记忆RAG、向量库、业务数据库、缓存系统典型硬件影响上下文越长KV Cache 显存占用越高关键优化方向GQA、KV Cache 量化、PagedAttention、前缀缓存评估维度记忆容量、检索准确性、遗忘程度、长文本推理一致性适合读者大模型应用开发、本地部署、RAG 工程化、LLM 推理优化2. 大模型记忆的三种形态要理解“记忆全景”先要把记忆分类。大模型不是只有一种记忆而是同时存在三层相互配合的记忆系统。2.1 参数记忆参数记忆是模型在预训练阶段写入权重里的知识。模型读完海量语料把统计规律压缩进几十亿到几千亿个参数中。Qwen、GLM、Llama、DeepSeek 这些模型最终交付给用户的safetensors权重文件就是参数记忆的载体。参数记忆有几个特点容量有限但覆盖面广可以回答“中国的首都是哪里”“Transformer 的结构是什么”这类通用知识。训练完成后基本冻结想要更新它只能做微调或继续预训练。每一条知识都没有明确的地址你不能像查数据库一样按主键去取。所以参数记忆适合存“稳定、通用、不需要实时更新”的知识。如果你想让它记住某个私有文档里的内容直接微调成本高、周期长、还可能遗忘旧知识这不是最优解。2.2 上下文记忆上下文记忆是模型在一次推理过程中临时保存的对话状态。实现载体就是注意力机制里的 Key 和 Value也就是常说的 KV Cache。你每多输入一个 token模型都要为每一层、每一个注意力头缓存一份 K 和 V 向量。上下文记忆的特点写入速度极快随推理过程实时生成。显存占用线性增长上下文越长越贵。对话结束即释放不具备跨会话持久性。容量受限于模型的训练长度和推理长度。这是所有大模型开发者最先遇到的记忆瓶颈。批处理的时候上下文长度直接决定并发上限和显存上限。2.3 外部记忆外部记忆是指在模型参数和上下文之外的存储系统最典型的就是 RAG 里的向量数据库也包括业务数据库、搜索引擎、文档索引、缓存服务。外部记忆的特点容量几乎无限可以存整个企业知识库。更新灵活增删改查完全可控。需要额外开发链路文档切分、向量化、召回、重排。与模型配合时存在检索噪声召回质量直接决定回答质量。这三层记忆不是互相替代的关系而是互补关系。参数记忆负责通用知识上下文记忆负责当前任务外部记忆负责私域知识。理解这一点你就明白为什么单纯调大上下文窗口不能解决所有问题。3. 上下文记忆KV Cache 到底吃多少显存KV Cache 是大模型推理显存占用的大头。很多人只关注模型权重占了多少显存却忽略了上下文长度对显存的放大效果。3.1 KV Cache 显存估算公式KV Cache 的显存占用可以近似用下面的公式计算KV Cache 显存 2 × batch_size × num_layers × num_kv_heads × head_dim × seq_len × 单元素字节数公式里乘以 2是因为每个 token 要同时缓存 K 和 V 两组向量。单元素字节数取决于精度FP16 是 2 字节INT8 是 1 字节INT4 是 0.5 字节。下面这个脚本可以直接估算一个模型在给定上下文长度下的 KV Cache 占用def estimate_kv_cache_gb( layers: int, kv_heads: int, head_dim: int, seq_len: int, batch_size: int 1, bits: int 16, ) - float: 估算 KV Cache 显存占用。 bits 可选 16(FP16), 8(INT8), 4(INT4)。 bytes_per_elem bits // 8 total_elements 2 * batch_size * layers * kv_heads * head_dim * seq_len return total_elements * bytes_per_elem / (1024 ** 3) # 7B 级别模型常见配置32 层、8 个 KV 头、每个头 128 维 for seq_len in [2048, 4096, 8192, 16384, 32768]: gb estimate_kv_cache_gb( layers32, kv_heads8, head_dim128, seq_lenseq_len, batch_size1, bits16, ) print(fseq_len{seq_len:6}, KV Cache ≈ {gb:.2f} GB)从计算结果可以直观看到序列长度翻倍KV Cache 显存也翻倍。这还没考虑多 batch 并发如果 batch_size 是 164K 上下文下的 KV Cache 会进一步放大 16 倍。这也是为什么高并发服务经常在长上下文场景下直接 OOM。3.2 为什么显存看起来“不够用”一个 7B 模型权重按 FP16 算大约占 14 GB很多人的理解是“14GB 显存就能跑”。实际上一旦把上下文拉长到 32KKV Cache 可能再吃掉好几个 GB如果再用批处理显存压力会明显上升。模型加载和推理时显存分配主要包括模型权重。优化器状态微调任务才需要。激活值前向计算过程中的中间张量。KV Cache。CUDA context 以及其他框架开销。所以实际部署时不能只看权重大小还要把 KV Cache 和激活值考虑进去。3.3 常见 KV Cache 优化手段GQA分组查询注意力是最有效的优化之一。它让多个查询头共享同一组 Key 和 Value 头从而把 KV Cache 降到原来的 1/8 甚至更低。如今的新模型基本都采用 GQA 或 MQA。其他优化手段包括KV Cache 量化将 K 和 V 从 FP16 压到 INT8 或 INT4以精度损失换显存。PagedAttention把 KV Cache 分页管理类似操作系统内存分页减少碎片化浪费vLLM 的显存管理核心就是它。滑动窗口注意力只保留最近 N 个 token 的 KV超出窗口的旧信息被丢弃。前缀缓存多个请求共享相同前缀时复用 KV Cache典型场景是系统提示词不变的多轮对话。这些优化手段对本地部署尤其重要。4GB、6GB 显存能跑多大上下文、多少并发很大程度上取决于 KV Cache 被压缩到多狠。4. 长上下文扩展位置编码、滑动窗口与遗忘压缩“记性不好”在工程上的表现就是上下文一长模型就开始忘事、答非所问、内容崩坏。提升长上下文记忆能力业界主要走四条技术路线。4.1 位置编码扩展Transformer 本身不考虑 token 顺序全靠位置编码注入位置信息。RoPE旋转位置编码是当前主流方案通过旋转矩阵把位置信息编码进 Q 和 K 向量。问题在于模型训练时没有见过特别长的序列推理时突然拉到 32K、128K位置编码会产生分布外问题。解决办法有两类位置插值把较长的位置范围压缩到训练时见过的范围内。高频外推对高频和低频位置信息做不同处理让模型在训练长度之外也能外推。实际部署中如果本地用 vLLM 或 Ollama 跑模型要注意max_model_len和num_ctx的设置。强行超过模型支持的上下文长度可能不是简单地变慢而是直接输出乱码。4.2 滑动窗口与局部注意力滑动窗口注意力的思路是每个 token 只关注最近 W 个 token而不是全部历史。这样 KV Cache 上限就是 W不再随序列长度无限增长。StreamingLLM 进一步发现只要保留最开始的几个 token 作为注意力锚点再配合滑动窗口模型就可以在任意长文本上保持相对稳定的输出。这相当于让模型在“只有有限工作记忆”的前提下也能处理很长的输入。这种方案适合流式处理长文档、超长对话、日志分析等场景。代价是中间过程的信息会丢失需要回溯早期内容时不方便。4.3 重要 Token 保留与记忆压缩另一条路线是“选择性遗忘”不缓存所有 token而是只保留注意力得分高的重要 token。H2O 的思路是每步淘汰注意力得分较低的 tokenSnapKV 则通过检测 Prompt 中的关键 token 位置来保留候选集。这些方法本质上是在做记忆压缩。模型不需要记住整篇文章的每一个字只需要记住关键信息。这与人类阅读时做摘要的行为类似。实际效果取决于任务类型。对于长文档问答关键信息往往集中在几个片段这类方法效果不错。对于需要全文细节的推理任务比如“第 37 页第三段的证据是什么”激进压缩会掉点。4.4 上下文压缩与摘要记忆还有一种方法是在多轮对话中自动生成历史摘要然后把摘要作为新的上下文输入。这种方案与 LangChain 的ConversationSummaryMemory思路一致。优点是对显存友好适合长期会话。缺点是摘要会丢失细节而且摘要本身的生成质量会影响后续回答。工程上可以结合“摘要 关键片段”双轨方案既保留细节又压缩长度。5. 外部记忆RAG 是大模型的“持久化硬盘”如果说 KV Cache 是 RAM那 RAG 就是硬盘。上下文记忆再好对话一结束就没了RAG 可以长期保存业务知识并且随时更新。5.1 RAG 的基本链路一个标准的 RAG 流程分两段离线索引和在线检索。离线索引阶段把文档切分成固定大小的 chunk然后通过 embedding 模型转成向量写入向量数据库。在线检索阶段用户提问先转成 query 向量再到向量库里做相似度检索取 top-k 个 chunk拼进 prompt交给大模型生成答案。下面是一段简化示例展示核心流程from sentence_transformers import SentenceTransformer import chromadb # 1. 加载 embedding 模型 embedding_model SentenceTransformer(BAAI/bge-m3) # 2. 初始化持久化向量库 client chromadb.PersistentClient(path./kb) collection client.get_or_create_collection(docs) # 3. 添加文档 documents [ 公司的报销流程是先提交申请然后财务审核。, 报销发票需要在当月月底前提交。, 如果发票丢失需要填写丢失说明并找主管签字。, ] ids [fdoc_{i} for i in range(len(documents))] collection.add( documentsdocuments, idsids, embeddingsembedding_model.encode(documents).tolist(), ) # 4. 在线检索 query 发票丢了怎么办 query_vec embedding_model.encode([query]).tolist() results collection.query(query_embeddingsquery_vec, n_results2) print(召回结果, results[documents][0])这段代码演示了最基础的 RAG 链路实际项目中还需要处理 PDF 解析、表格识别、文档切分、重排序、权限过滤等环节。5.2 RAG 的记忆管理问题RAG 不是简单把一堆文档扔进向量库就完事它有自己的“记忆管理”问题。chunk 粒度直接决定召回质量。切得太小语义不完整切得太大噪声太多、向量检索不准确。实践中要结合 embedding 模型能力、文档结构、任务类型做评估。检索召回之后还有一个关键环节是重排。向量检索拿到 top-50再用 rerank 模型重新打分取 top-5可以明显提升答案准确性。很多 RAG 项目效果不好不是大模型的问题而是召回排序的问题。更新策略也很重要。知识库什么时候会新增数据旧文档被删除后向量库里对应向量如何清理如果用户已经进入多轮对话是否需要用对话历史改写 query 后再检索这些都是外部记忆生命周期管理的常见问题。5.3 混合记忆上下文 RAG实际工程里最稳妥的做法是混合记忆系统提示词承载固定身份信息上下文记忆承载多轮对话状态RAG 承载动态业务知识。比如在部署一个私有知识库问答机器人时完整的 prompt 结构可能是[系统提示词] 你是一个企业智能助手只能根据提供的资料回答。 [检索到的资料] chunk-1... chunk-2... [最近 5 轮对话] 用户... 助手... [当前问题] 用户...先检索外部记忆再把它和上下文记忆拼接到一起最后交给模型生成。这一步顺序很关键检索前置能让模型在生成时“有据可依”而不是先凭参数记忆瞎猜。6. 从记忆机制看本地部署优化回到最实际的场景本地部署大模型时记忆机制直接影响两个决策——显存够不够、上下文能开多大。6.1 vLLM 部署中的记忆相关配置vLLM 是目前最常用的高吞吐推理框架之一。它用 PagedAttention 管理 KV Cache显存利用率比传统方案高很多。部署时与记忆直接相关的参数包括python -m vllm.entrypoints.openai.api_server \ --model /path/to/your/model \ --max-model-len 8192 \ --gpu-memory-utilization 0.9 \ --enable-prefix-caching说明几点--max-model-len限制最大序列长度。设得越大KV Cache 预留越多能跑的并发就越少。--gpu-memory-utilization控制 vLLM 最多使用多少显存。设得太小KV Cache 空间不足设得太大可能没有余量给 CUDA context 和其他进程。--enable-prefix-caching开启前缀缓存多个请求共享相同 system prompt 时能复用 KV Cache适合多轮对话和 RAG 应用。实际数字以你本机显卡为准。可以先跑一个短并发压测再逐步调大max-model-len直到 OOM 边界线。6.2 Ollama 部署中的上下文设置Ollama 的优势是简单一条命令就能跑起来。默认的上下文长度不一定适合你的任务需要通过Modelfile控制FROM qwen3:8b PARAMETER num_ctx 8192然后在模型目录下执行ollama create qwen3-8b-8k -f Modelfile ollama run qwen3-8b-8knum_ctx就是 KV Cache 的长度上限。如果显存不够优先调低它如果任务需要长文档分析再往上加。6.3 显存不足时的取舍顺序如果本地显存有限比如 6GB 或 8GB建议按下面的顺序取舍先确定任务需要的最小上下文长度不要盲目开 32K。优先开启 KV Cache 量化用 INT8 或 INT4 换显存。选择 kv_heads 更少的模型GQA 会让 KV Cache 更小。批处理时减少 batch_size或把长序列任务拆成多个短任务。如果模型权重占用已经很高再考虑 AWQ/GPTQ 量化权重。这些方法都不是提升模型上限而是减少“记忆占用的空间”让有限显存能跑更长的上下文、更高的并发。6.4 多轮对话的累积记忆问题本地部署服务在持续对话时上下文会越变越长。对用户是“记忆变长”对服务是“显存持续上涨”。解决方案有几种对话长度上限达到阈值后触发截断只保留最近 N 轮。历史摘要让模型把早期对话压缩成摘要再放入上下文。关键信息抽取从对话历史中提取结构化信息例如用户偏好、任务状态后续只保留结构化字段。从记忆全景的视角看这相当于主动做“记忆归档”把工作记忆压缩成长期记忆避免 RAM 被占满。7. 记忆能力评估怎么测一个模型“记不记得住”部署完成之后不能只看 Loss 或困惑度要针对记忆能力做专门验证。7.1 测试维度我建议从五个维度建立记忆测试集短期记忆多轮对话中模型能否准确记住前三轮提到的具体信息比如数字、人名、地点。长文本检索给定一篇长文档问题答案埋在中间位置模型能否正确找到并回答。多跳推理答案需要综合文档中两个不同位置的信息才能得出。遗忘测试输入非常长的上下文后模型是否还记得开头的关键内容。稳定性测试同一个问题在多轮重复提问下答案是否一致。7.2 常见基准公开基准方面可以关注 LongBench、L-Eval、RULER 这些长文本评测集。它们覆盖了单文档问答、多文档问答、摘要、代码补全等任务适合量化评估长上下文能力。实际项目里如果不想依赖标准基准可以构造自己的“金标测试集”从业务文档中人工标注 20 到 50 个问题每个问题记录标准答案和答案所在位置然后用统一脚本批量跑。这样比刷公开榜更贴近真实业务。7.3 一个简单的自测脚本可以用一个本地脚本快速验证模型的记忆能力from openai import OpenAI client OpenAI( base_urlhttp://127.0.0.1:8000/v1, api_keyEMPTY, ) def test_memory(question: str, context: str) - str: response client.chat.completions.create( modelyour-model, messages[ {role: system, content: 你是一个严谨的助手请严格根据上下文回答问题。}, {role: user, content: f背景资料\n{context}\n\n问题{question}}, ], temperature0, ) return response.choices[0].message.content context 项目计划在3月25日发布v1.0版本。 核心功能包括用户注册、数据看板、报表导出。 数据库使用PostgreSQL 15部署环境为内网服务器。 question 项目什么时候发布数据库使用什么版本 print(test_memory(question, context))如果模型完全答错就要检查上下文是否被截断、检索是否命中、提示词是否补充了足够的位置信息。8. 常见问题与排查方法问题现象可能原因排查方式解决方案上下文调长后输出乱码超过模型训练长度位置编码外推失效检查 max_model_len 与模型支持长度降低上下文长度或换支持更长上下文的模型推理时显存不足 OOMKV Cache 过大或 batch_size 过大观察显存曲线计算 KV Cache 占用调低 max_model_len、开启量化、减小并发模型忘了前面的对话内容上下文被截断或丢弃查看请求日志中实际发送的 prompt 长度使用摘要记忆或关键信息抽取RAG 检索不到答案chunk 切分不合理或 embedding 模型不匹配打印检索结果检查 top-k 命中率调整 chunk 大小、增加 rerank、更换 embedding 模型长文档问答答案前后矛盾模型只看到部分片段判断是否命中关键片段增大召回数或把问题拆成子问题接口调用超时上下文太长推理时间指数级增加查看请求耗时与 token 数压缩输入、限制 max_tokens、加超时重试多轮对话显存持续上涨KV Cache 未复用时反复重建观察每轮推理后显存是否回落开启 prefix caching或定期清理会话9. 最佳实践搭一套可用的记忆系统把前面的内容落到工程上我建议按下面三步走。第一步明确记忆类型。先问自己任务需要的是通用知识、单次对话状态还是持续更新的业务知识。通用知识选参数记忆单次对话靠上下文记忆业务知识用 RAG。不要一上来就堆长上下文。第二步做显存与精度的取舍。部署前先估算 KV Cache 占用再决定上下文长度、量化精度和并发数。以「先跑通再压测最后调优」的顺序推进避免一开始就追求极限参数。第三步建立记忆评估闭环。每调整一次记忆相关参数就用同一套金标测试集跑一遍记录命中率、答案完整性和显存峰值。合规层面要特别注意RAG 外部记忆涉及用户隐私或业务敏感数据时需要做好权限隔离和数据脱敏。声音、人脸、图文等数据的离线记忆和调用必须在明确授权范围内进行。涉及版权资料时要确认是否有权对内容进行存储、切分和向量化后再使用避免侵权风险。10. 总结大模型记忆全景的本质是把“模型知道什么”和“模型记住了什么”分开看。参数记忆决定模型的底子上下文记忆决定当前任务的连贯性外部记忆决定业务知识能否长期沉淀。三者叠加起来才是大模型真正的记忆能力。这篇文章值得带走的几个点KV Cache 是显存占用的核心变量部署前先估算。长上下文不是越长越好超出训练长度会崩坏。RAG 是外部记忆的关键工程化方案。记忆能力必须通过金标测试集持续评估。少用“通通塞进上下文”的思路先分清记忆类型再设计方案。如果你想继续深入建议从这三件事开始先跑一次 KV Cache 显存估算再给你的模型配置一套合适的上下文长度最后用 20 个业务问题建立记忆评测集。这套流程跑通你对大模型记忆机制的理解会比单纯读论文更扎实。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进