news 2026/10/14 16:32:34

Agent 上下文管理彻底搞懂:滑动窗口、摘要压缩、分层记忆的底层原理 + 本机实测

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Agent 上下文管理彻底搞懂:滑动窗口、摘要压缩、分层记忆的底层原理 + 本机实测

👋 我是 Yulia,一线 AI 应用开发工程师:4 年工程经验,在职做大模型应用落地。
只写一件事——把最新 AI 技术讲成人话 + 给你能直接跑通的代码(全部本机实测,不搬运不注水)。
有用就点个关注,持续更新大模型应用的前沿实战。

网上讲 Agent 记忆的文章,大多是罗列"8 大记忆策略",要么就是丢一个 LangChain 的ConversationSummaryBufferMemory让你照抄。这篇不一样:我先讲清楚为什么上下文会有限(token / 注意力复杂度 / KV Cache 的底层原因),再纯手写三种上下文管理策略(不依赖任何框架),最后本机实测,把"谁省 token、谁失忆"的真实数字摆给你看。

一、Agent 为什么聊着聊着就"失忆"了

你肯定遇到过:一个 Agent 跟你聊了几十轮,前面明明说过"我预算 5000 元"“我们 10 个人”,结果聊到后面它问"那你们预算多少?"——它把前面的话忘了。

这不是 Agent 笨,是上下文窗口有限这个硬约束导致的。Agent 的每一轮对话,都要作为文本重新喂给大模型;但大模型一次能"看"的文本量是有限的(比如 4K、32K、128K token)。多轮对话越积越多,总有一天塞不下,就必须"取舍"——怎么取舍,就是**上下文管理(Context Management)**要解决的问题。

这也是面试里的高频追问:“上下文超限了怎么处理?摘要压缩丢失关键信息怎么办?”

二、本文能给你什么

  1. 搞懂为什么上下文会有限——token、注意力 O(n²)、KV Cache 三个底层原因;
  2. 掌握三种策略的原理 + 纯手写实现(滑动窗口 / 摘要压缩 / 分层记忆),不依赖 LangChain;
  3. 拿到一份本机实测的真实数据:每种策略占多少 token、还记不记得关键信息;
  4. 拿到一张"什么时候用哪种"的选型决策表。

先看全景图:

对话历史

上下文管理

滑动窗口
只留最近 N 轮

摘要压缩
旧内容压成摘要

分层记忆
短期 + 长期检索

喂给 LLM

三、底层铺垫:为什么上下文会"有限"

在讲策略之前,必须先回答一个问题:为什么不能把所有历史都塞进去?这背后是三个成本。

3.1 token 是什么

大模型不认"字",只认token。分词器(Tokenizer)会把文本切成 token,再喂给模型。以 GPT 系用的 cl100k_base 分词器为例:

importtiktoken enc=tiktoken.get_encoding("cl100k_base")print(enc.encode("你好世界"))# 5 个 tokenprint(enc.encode("hello"))# 1 个 token

一个英文单词通常 1~2 个 token,一个中文字通常 1~3 个 token。所谓"上下文窗口 32K",意思就是模型一次最多吃 32768 个 token。你聊得越多,累计 token 越多,总有撞到上限的时候。

3.2 注意力计算是 O(n²)

大模型的核心是Self-Attention(自注意力)。它的原理是:让序列里的每个 token 都去"看"其他所有 token,算一个注意力权重。

假设序列长度是 n,那么每个 token 都要和另外 n 个 token 算一遍注意力,整个注意力矩阵是n × n。所以计算量是 O(n²)。

这意味着什么?序列长度翻一倍,计算量变成 4 倍。你把上下文从 4K 拉到 8K,不是贵一倍,是贵 4 倍。这是上下文不能无限长的最根本原因。

3.3 KV Cache 显存随长度线性增长

推理时,为了不重复计算,模型会把每一层算出来的K(Key)和 V(Value)矩阵缓存起来,这就是 KV Cache。

它的显存占用跟层数 × 序列长度 × 每 token 维度 × 头的数量成正比。序列越长,KV Cache 显存越大——而且是每一层、每一个 token、每一个注意力头都各存一份。

所以即便算力扛得住 O(n²),显存也会先爆。上下文有限,本质是"成本"决定的,不是模型的意愿。

一句话记住:token 是"计量单位",O(n²) 是"算力成本",KV Cache 是"显存成本"——三者一起决定了上下文窗口不可能无限大,于是有了"管理"的必要。

四、三种策略:原理 + 纯手写实现

下面用一个真实的 6 轮对话做例子。这段对话里埋了 5 个关键事实(10 人、5000 元、移动端、私有化、钉钉),它们只出现一次、都在早期轮次——正好用来测"哪种策略会失忆"。

CONV=[("用户","我们团队想选一个项目管理工具,帮我参考一下"),("助手","好的,先了解一下需求。团队多少人?预算多少?"),("用户","我们 10 个人,预算一年 5000 元以内"),# 事实: 10人 + 5000元("助手","记下了。还有什么硬性要求吗?"),("用户","最好支持移动端,大家经常出差"),# 事实: 移动端("助手","明白。数据方面有偏好吗?"),("用户","数据要能私有化部署,不能存第三方云上"),# 事实: 私有化部署("助手","好的。还有别的吗?"),("用户","对了,还要能和我们钉钉打通"),# 事实: 钉钉("助手","都记下了。你确认一下需求我就推荐"),("用户","就这些,你推荐一个吧"),("助手","根据你的需求,我推荐……"),# 这里需要早期事实]defto_text(msgs):return"\n".join(f"{role}:{text}"forrole,textinmsgs)

4.1 滑动窗口(Sliding Window):最简单,也最"健忘"

原理:只保留最近 N 轮,更早的直接丢弃。就像微信聊天窗口往上翻——太久之前的消息你翻不到了。

实现:一句话的事。

defsliding_window(keep_turns=3):# 一轮 = 用户 + 助手 = 2 条消息,所以保留最近 3 轮 = 最后 6 条returnto_text(CONV[-keep_turns*2:])

优点:实现零成本,token 占用完全可控(永远不超过最近 N 轮的量)。

缺点:早期关键信息被硬删。上面例子里,最近 3 轮只剩"私有化部署"“钉钉”“就这些”,"10 个人 / 5000 元 / 移动端"全丢了——Agent 后面问"预算多少"就露馅了。

适用:短对话、一次性任务,历史信息不重要的场景。

4.2 摘要压缩(Summary Compression):把旧内容"压扁"

原理:与其直接丢弃旧内容,不如先用一个 LLM 把旧对话总结成一段摘要,把摘要 + 最近 N 轮一起放进上下文。这样既省 token,又保留了旧内容的"大意"。

defsummarize(keep_turns=3,summary=""):recent=to_text(CONV[-keep_turns*2:])# 最近 N 轮原样保留returnsummary+"\n"+recent# 摘要 + 最近 N 轮

关键坑:摘要会丢关键信息。这是面试必问的点。看两种摘要的差别:

# 朴素摘要:只说了个大概,关键细节全没了summary_naive="【历史摘要】用户之前咨询了项目管理工具选型。"# 结构化摘要:按字段把关键信息一条条抽出来summary_structured="【历史摘要】10 人团队;年预算 5000 元;需要移动端;私有化部署;钉钉集成。"

朴素摘要丢掉了"10 人、5000 元、移动端",Agent 还是失忆;结构化摘要把这些关键字段都留住了。

怎么缓解"摘要丢信息"?核心是别让 LLM 自由发挥,用结构化提示词逼它按字段抽取:

请把下面的对话总结成结构化摘要,必须包含以下字段(没有就写"无"): - 团队人数: - 预算: - 硬性要求(移动端/私有化/集成等):

优点:比滑动窗口省 token 的同时,能保留旧内容的大意。

缺点:① 摘要本身就是一次额外的 LLM 调用(有成本和延迟);② 摘要永远会损失细节(“大意"不等于"原文”),需要精确引用原文的场景不适合;③ 摘要会有"漂移"——多轮摘要套摘要,信息会越来越歪。

4.3 分层记忆(Hierarchical Memory):短期 + 长期,最像人

原理:借鉴人类记忆——短期记忆只记最近的事(窗口内完整保留),长期记忆存到外部的"笔记本"(向量数据库),需要的时候再"回忆"(检索)回来。

全部历史

短期记忆
最近 N 轮

长期记忆
向量库

当前 query

检索相关历史

合并上下文

喂给 LLM

实现:短期 = 窗口;长期 = 把每条历史向量化存起来,查询时用当前 query 去检索最相关的几条,拼回上下文。

fromfastembedimportTextEmbeddingimportnumpyasnp model=TextEmbedding(model_name="BAAI/bge-small-zh-v1.5")all_texts=[textfor_,textinCONV]mem_vecs=np.array(list(model.embed(all_texts)))# 每条历史 -> 向量mem_vecs=mem_vecs/np.linalg.norm(mem_vecs,axis=1,keepdims=True)# 归一化defhierarchical(keep_turns=3,query="预算 团队人数 移动端 私有化部署 钉钉"):recent=to_text(CONV[-keep_turns*2:])# 短期:最近 3 轮qv=np.array(list(model.embed([query])))[0]qv=qv/np.linalg.norm(qv)sims=mem_vecs @ qv# 余弦相似度(归一化后=点积)top=sorted(np.argsort(sims)[::-1][:6].tolist())# 检索最相关的 6 条retrieved="【长期记忆检索】"+";".join(all_texts[i]foriintop)+"。"returnretrieved+"\n"+recent# 长期 + 短期 拼一起

逐行说明:

  • model.embed把每条历史文本变成一个向量,mem_vecs就是"长期记忆库";
  • 查询时,把当前 query 也向量化,和记忆库里的向量算余弦相似度(归一化后就是点积);
  • 取相似度最高的 top-6 条历史,作为"回忆"出来的内容,和短期窗口拼在一起喂给模型。

优点:① 长期记忆不占上下文(存在外部向量库);② 按需检索,只把"相关的"历史拿回来;③ 能跨会话、跨天持久化。

缺点:① 要维护向量库(索引、存储成本);② 检索质量依赖 embedding 和 query 的写法;③ 比前两种复杂得多。

五、本机实测:谁省 token,谁失忆

原理讲完,直接跑一遍。我在上面这段 6 轮对话上,实测了 5 种做法的token 占用和关键信息保留(5 个事实还剩几个):

策略token 占用保留事实丢失的事实
全量(不管理)2445/5无
滑动窗口(最近 3 轮)1082/510 个人、5000 元、移动端
摘要压缩 · 朴素摘要1312/510 个人、5000 元、移动端
摘要压缩 · 结构化摘要1514/510 个人
分层记忆(短期 + 长期检索)2315/5无

三个结论:

  1. 省 token 的代价是失忆。滑动窗口把 token 从 244 压到 108,但丢了 3 个关键事实;朴素摘要同样丢——因为它没把细节抽对。
  2. 结构化摘要用 151 token 保住了 4/5 的事实,是"省 token + 保留大意"的性价比之选(“10 个人"在结构化摘要里被写成了"10 人团队”,措辞变了但语义在——这也是摘要的一个真实副作用:改写会导致措辞漂移,下游若靠精确词面匹配就会漏)。
  3. 分层记忆 231 token 保住 5/5,最稳,但 token 也接近全量(因为它把检索到的历史原文又塞了回来)。

⚠️ 诚实说明:本实测是 6 轮玩具对话 + 简单字符串匹配,用来把"机制"讲清楚,不代表生产。生产里摘要质量取决于 LLM、分层记忆的检索质量取决于 embedding,但"省 token 会失忆、摘要会丢细节"这两个结论是普遍成立的。

六、什么时候用哪种:选型决策表

场景建议
短对话、一次性任务、历史不重要滑动窗口,最简单
需要记住早期关键事实,但能接受"大意"结构化摘要压缩
需要精确引用原文(如"你之前说的预算是多少")分层记忆(检索原文,不靠摘要)
跨会话、跨天、长期积累的 Agent分层记忆(向量库持久化)
团队/生产环境、可观测可评测分层记忆 + 摘要组合,按预算权衡

一句话总结:滑动窗口是"穷省",摘要压缩是"记大意",分层记忆是"记笔记"——越往后越像人,也越复杂越贵。选哪种,取决于你的任务需要"记住多少"、以及愿意付多少成本。

七、总结与避坑

核心要点回顾成一张图:

常见坑速查:

坑原因解法
滑动窗口直接丢早期关键信息硬截断早期关键信息提前抽出来存着
摘要自由发挥丢细节没约束 LLM用结构化提示词逼它按字段抽取
摘要多次套摘要越来越歪信息漂移摘要尽量基于原文,少嵌套
分层记忆检索不到query 写法不好检索 query 写清关键词,必要时多路召回

如果这篇对你有帮助,点个赞、收藏一下,下次做 Agent 记忆设计直接对照第六节的决策表。

关注我,这个系列我会拆完大模型应用落地全链路——RAG 混合检索、Agent 上下文管理、KV Cache、LoRA 微调,每篇都带能直接跑的代码。

你的 Agent 项目里,上下文管理用的是哪种?有没有踩过"聊到一半它忘了预算"的坑?评论区聊聊,我挨个回。

(本文数据截至 2026 年 10 月,实测环境 Python 3.10 + tiktoken cl100k_base + bge-small-zh-v1.5,有更新评论区提醒我。)

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 16:32:09

区域回波识别:用声学与射频信号感知空间状态变化

我在某实验室的配电间里捣鼓超声波洗手器时意识到,回波能做的不只是“感应到人后出水”这种开关逻辑。墙壁反射回来的声音、楼层板传来的振动、房间角落的驻波,全都带着空间状态的信息,只是没人把它们当作观测信号用。Z.E.R.O. (Zonal Echo R…

作者头像 李华
网站建设 2026/10/14 16:32:08

OpenTPU:基于FPGA的开放语言模型推理加速器全链路解析

先说一个反直觉的结论:如果只看数据中心大batch推理的跑分,FPGA在这条赛道上确实不是GPU的对手。但在端侧部署、小batch低延迟推理、隐私敏感场景,以及教学科研领域,GPU反而常常是最不合适的那个加速器。OpenTPU就是在这种矛盾里被…

作者头像 李华
网站建设 2026/10/14 16:31:52

第三方Zynq板卡移植PYNQ实战:设备树与Overlay配置

折腾了两周,终于让PYNQ 4.0在这套“snickerdoodle black piSmasher”的组合上稳定跑起来了。这块板子用的是Zynq-7020,双核Cortex-A9,FPGA逻辑资源做中低复杂度设计够用,但最大的问题是官方没有放出对应的PYNQ镜像,pi…

作者头像 李华