Agent 上下文太长怎么办:四类压缩策略、最近上下文怎么传与记忆保持的分层架构

核心结论 :Agent 上下文越长越难管,根源是三个增长源------多轮对话历史、工具返回的大量结果、模型自己的思考链 。塞满了会怎样?要么报错超限,要么「注意力涣散」(长上下文里关键信息被稀释,模型开始漏看、走神)。解决它靠的不是一招,而是四类策略的组合 :① 丢弃 (滑动窗口,只留最近 N 轮)→ ② 压缩 (把历史摘要成一段话)→ ③ 卸载 (offloading,全文存外部、模型只看预览)→ ④ 检索 (用 ID / 向量按需把相关记忆拉回来)。「最近上下文怎么传」的答案是:系统提示常驻 + 最近 N 轮原样保留 + 更早的压缩成摘要 + 关键事实存外部可检索------这就是「短期窗口 + 长期记忆」的分层架构。

目录

    1. 一句话结论
    1. 上下文为什么会「太长」:三个增长源
    1. 四类压缩策略总览:丢弃 / 压缩 / 卸载 / 检索
    1. 策略一:滑动窗口------最近上下文怎么传
    1. 策略二:摘要压缩------把历史「折叠」成一段话
    1. 策略三:卸载 offloading------模型只看预览
    1. 策略四:检索召回------按需把记忆拉回来
    1. 组合打法:短期窗口 + 长期记忆的分层架构
    1. 完整可运行代码:一个上下文压缩管理器
    1. 对照表:症状 → 原因 → 解法
    1. 判断收尾

1. 一句话结论

先建立一个直觉 🎭:Agent 的上下文像「一张书桌」 ------你能同时摊开的资料有限,摊太多就找不到东西、还会把水杯撞倒(关键信息被淹没)。聪明的做法不是「换一张无限大的桌子」(上下文窗口再大也会满),而是只把正在用的几份放桌上,其余的归档到抽屉(外部存储),需要时再取出来。

所以记住这套框架:

  • 三个增长源:对话历史、工具结果、思考链------它们是上下文膨胀的三大元凶。
  • 四类策略:丢弃(滑动窗口)、压缩(摘要)、卸载(offloading)、检索(召回)。
  • 最近上下文怎么传:系统提示常驻 + 最近 N 轮原样 + 更早的摘要 + 关键事实外部存。

一句话:上下文管理不是「删内容」,而是「分层」------最近的留在桌上,久远的归档到抽屉,需要时按编号取。

2. 上下文为什么会「太长」:三个增长源

先搞清楚是什么把上下文撑爆的。Agent 跑一个长任务,上下文只在三处膨胀:

增长源 典型表现 特点
多轮对话历史 用户和 Agent 来回几十轮 越滚越大,且大部分轮次已无关
工具返回结果 读了 500 行文件、查了 20 条搜索结果 单次就很大,是「尖峰」
模型思考链 每步推理几千 token 累积快,但对后续价值低
  • 对话历史是「慢增长」:一轮一轮往上爬。
  • 工具结果是「尖峰」:一次大返回就把上下文顶上去。
  • 思考链是「高消耗低价值」:占地方,但对后续决策帮助有限。

用类比 🎭:这就像「健身后堆在玄关的东西」 ------购物袋(工具结果)一次就占一大块,换下的衣服(对话)一件件累积,汗湿的毛巾(思考链)又占地方又没用。你要做的不是「买个大玄关」,而是分类处理。

💡 我在Agent规则管理:让 Memory 做索引、让 Skill 存规则------上下文上限实测与分工方案里实测过上下文上限的实际表现,本文更聚焦「膨胀了怎么压」的具体策略。

3. 四类压缩策略总览:丢弃 / 压缩 / 卸载 / 检索

针对三个增长源,业界沉淀出四类策略,从「简单粗暴」到「精细可控」:

策略 做什么 优点 缺点
丢弃(滑动窗口) 只保留最近 N 轮,更早的删掉 极简单、成本低 丢长期信息
压缩(摘要) 把历史总结成一段摘要 保留语义、省空间 有信息损失、要额外 LLM 调用
卸载(offloading) 全文存外部,上下文只放短预览 止住尖峰、可回溯 要管理外部存储
检索(召回) 按需把相关记忆拉回上下文 精准、可无限扩展 依赖检索质量
  • 丢弃是基础款:先保证不爆,再谈质量。
  • 压缩是性价比款:一段摘要替掉十轮对话。
  • 卸载是对付「工具尖峰」的利器。
  • 检索是长期记忆的入口:把「无限记忆」变成可能。

💡 记住一个判断顺序:先用滑动窗口兜底 → 再用摘要压缩 → 大结果用 offloading → 长期事实用检索。四招组合,不是四选一。

4. 策略一:滑动窗口------最近上下文怎么传

「最近上下文怎么传」这个问题,最直接的答案就是滑动窗口(sliding window)。

  • 原理:只保留最近 N 轮对话(或最近 K 个时间步),更早的直接丢弃。
  • 实现:维护一个固定长度的消息队列,超过 N 就弹出最老的。
  • 优点:实现极简,成本几乎为零,适合短期任务(客服对话、单轮调试)。
  • 缺陷 :丢失所有长期信息------用户问「昨天你提到的那个方案」,如果被滑出窗口,模型就答不上来。

滚动窗口的典型实现,就是「传最近 N 轮」:

python 复制代码
def keep_recent(messages, max_turns=10):
    """只保留最近 N 轮消息(滑动窗口)"""
    return messages[-max_turns:]

⚠️ 纯滑动窗口是不够的------它解决了「不爆」,但解决了「记不住早期信息」。所以真实系统通常滑动窗口 + 摘要一起用(第 8 节讲)。

参考资料:

5. 策略二:摘要压缩------把历史「折叠」成一段话

滑动窗口会丢信息,摘要(summarization) 就是来补这个洞的:

  • 原理:把较早的历史(或整段上下文)交给 LLM,压缩成一段「到目前为止发生了什么」的摘要。
  • 做法:保留最近 N 轮原样 + 把更早的压成一段摘要放在最前面。
  • 优点 :保留语义,十轮对话能压成两三句,信息密度高。
  • 代价:有信息损失(细节会丢),且摘要本身要一次 LLM 调用。

业界把这类做法叫 compaction(压缩) ------LangChain 的说法很形象:offloading 止住小尖峰,summarization 把整体上升的曲线「重置」下来。

用类比 🎭:摘要像「会议纪要」------两个小时的会压成一页要点,你不需要逐字回忆,只要记住「决定了什么、下一步谁做什么」。

参考资料:

6. 策略三:卸载 offloading------模型只看预览

摘要解决「历史太多」,卸载(offloading) 解决「单次结果太大」:

  • 原理 :把大块内容(长文件、长搜索结果、完整命令输出)存到外部 (文件 / 数据库),上下文里只放一个短预览 + 引用。

  • 效果:模型看到的是「已读取 config.py(128 行,摘要:端口是 8080)」而不是 128 行全文。

  • 好处 :瞬间止住上下文尖峰;需要全文时再按引用取回。

  • 代价:要管理外部存储,且模型要「知道去哪取」。

  • 参考 ARC(Addressable Recall Compaction)的做法:把工具观察存进append-only、ID 可寻址 的日志,压缩时把旧观察替换成紧凑引用,Agent 之后可以用这些 ID 请求原内容,而不用重新执行工具。

用类比 🎭:offloading 像「把大衣存到衣帽间」------你手上只拿一张号码牌(引用),需要穿时凭号码去取,而不是一直抱着大衣逛商场。

参考资料:

7. 策略四:检索召回------按需把记忆拉回来

前三种策略都在「省」,检索(retrieval) 在「按需取回」,这是长期记忆的关键:

  • 原理:把重要信息存进外部记忆库(向量库 / 结构化存储 / ID 日志),需要时按相关性检索,把 top-k 拉回上下文。
  • 两种召回方式 :
    • 向量检索:按语义相似度找(问「上次说的部署方案」→ 拉回相关片段)。
    • ID 可寻址召回:按明确 ID 取(ARC 的做法,比相似度更精准、无歧义)。
  • 优势:记忆容量「无限」------上下文只放当前需要的那部分,剩下都在库里。

SimpleMem 的做法更进一步,用三段式处理长期记忆:① 语义结构化压缩 (把原始对话提炼成独立记忆单元)→ ② 递归整合 (把相关单元组织成更高层抽象)→ ③ 自适应检索(按查询动态调整召回范围)。

用类比 🎭:检索像「图书馆查书」------书架(外部存储)能装无数本书,你不需要把它们全搬到桌上,只要按索引(向量/ID)借出你要的那几本。

参考资料:

8. 组合打法:短期窗口 + 长期记忆的分层架构

四类策略不是四选一,而是分层组合。这直接回答了「最近上下文怎么传 + 怎么保持记忆」:

短期层(最近上下文怎么传):

  • 系统提示常驻:永远放在最前面,不被裁剪。
  • 最近 N 轮原样保留:滑动窗口,保证「刚才聊了什么」不丢。
  • 关键状态摘要常驻:当前任务目标、已完成的步骤,压成一段放在窗口前。

长期层(怎么保持记忆):

  • 工具大结果 → offloading:全文存外部,上下文只留预览 + 引用。
  • 久远对话 → 摘要 compaction:压成纪要,替换掉原始轮次。
  • 关键事实 → 检索库:存进向量库 / ID 日志,按需召回。

一个可用的「分段上下文」结构长这样:

位置 内容 策略
最前 系统提示 + 角色 常驻,永不裁剪
次前 任务目标 + 进展摘要 compaction,定期更新
中间 检索回来的相关记忆 retrieval,按需注入
最新 最近 N 轮对话 sliding window,原样保留

💡 一句话记住这套分层:最前永远在(系统提示)+ 目标压成段(摘要)+ 旧事存抽屉(外部)+ 最近原样带(窗口)+ 需要再召回(检索)。

9. 完整可运行代码:一个上下文压缩管理器

下面是一段复制粘贴就能跑的 Python 代码,实现一个「四策略合体」的上下文管理器:滑动窗口保留最近 N 轮、超长内容 offloading 到外部存储、把更早的对话摘要成一段、并支持按 ID 召回。运行无需第三方库。

python 复制代码
# 上下文压缩管理器:滑动窗口 + offloading + 摘要 + 召回
class ContextManager:
    def __init__(self, max_turns=5, max_content_len=100):
        self.system_prompt = "你是一个助手。"          # 系统提示:常驻不裁
        self.recent = []                              # 最近 N 轮(滑动窗口)
        self.max_turns = max_turns
        self.max_content_len = max_content_len
        self.summary = ""                             # 历史摘要
        self.archive = {}                             # 外部存储(offloading)
        self._id = 0                                  # 归档 ID 计数器

    def offload(self, content):
        """卸载:超长内容存外部,返回引用预览"""
        if len(content) <= self.max_content_len:
            return content                            # 不长,直接保留
        self._id += 1
        ref = f"<archive#{self._id}>"                 # 生成可寻址引用
        self.archive[self._id] = content              # 全文存外部
        return f"{ref}(预览:{content[:30]}...,共 {len(content)} 字)"

    def add(self, role, content):
        """加一条消息:先 offloading,再滑窗,超窗则摘要"""
        content = self.offload(content)               # 大内容先卸载
        self.recent.append({"role": role, "content": content})
        if len(self.recent) > self.max_turns:         # 超过窗口
            oldest = self.recent.pop(0)               # 弹出最老的
            # 摘要:把弹出的内容压进 summary(真实场景用 LLM 生成)
            self.summary += f"[{oldest['role']}] {oldest['content'][:20]};"

    def recall(self, ref_id):
        """召回:按 ID 从外部存储取回全文"""
        return self.archive.get(ref_id, "未找到")

    def build_context(self):
        """组装最终传给模型的分段上下文"""
        return [self.system_prompt] + \
               ([f"历史摘要:{self.summary}"] if self.summary else []) + \
               [f"{m['role']}: {m['content']}" for m in self.recent]

if __name__ == "__main__":
    cm = ContextManager(max_turns=3, max_content_len=50)
    # 加 5 条消息(其中一条是超长工具结果)
    cm.add("user", "帮我检查配置文件")
    cm.add("tool", "port = 8080\n" * 30)              # 超长结果 → 卸载
    cm.add("assistant", "已读取配置,端口是 8080")
    cm.add("user", "改成 9090")
    cm.add("assistant", "已修改为 9090")
    for line in cm.build_context():                   # 打印组装后的上下文
        print(line)
    print("召回 #1 长度:", len(cm.recall(1)))         # 按 ID 取回全文

怎么读这段代码 :offload() 是第 6 节「卸载」------超过 50 字的内容存进 archive,上下文只留 <archive#1> 引用 + 预览,止住尖峰;add() 里「超过 max_turns 就弹出最老的并压进 summary」是滑动窗口 + 摘要的组合(第 4、5 节);build_context() 组装出的正是第 8 节的分段结构:系统提示 → 历史摘要 → 最近 N 轮;recall() 是第 7 节「按 ID 召回」。把「用规则压摘要」换成「调 LLM 生成摘要」,就是一套能上生产的上下文管理器。

10. 对照表:症状 → 原因 → 解法

症状 原因 解法
请求报错「超出上下文上限」 历史 + 工具结果总长超窗口 滑动窗口兜底 + 摘要压缩
长对话里模型「忘了」早期信息 滑动窗口把早期轮次滑出去了 早期内容摘要成纪要常驻
读一个大文件后上下文爆满 单次工具结果太大(尖峰) offloading:全文存外部,只留预览
关键事实隔几轮就「想不起来」 没有长期记忆层 关键事实存检索库,按需召回
模型在长上下文里注意力涣散 无关信息太多稀释了关键信息 精简上下文,只留当前需要的
摘要后细节丢了、答不准 摘要压缩有信息损失 细节用 ID 归档、按需召回原文

11. 判断收尾

什么时候该上这套分层方案 :你的 Agent 要跑长任务、多轮对话、或频繁读大文件,上下文必然会膨胀------这时「滑动窗口 + 摘要 + offloading + 检索」的组合几乎是刚需,否则迟早撞上上限或注意力涣散。什么时候可以简化 :任务很短(几轮就结束)、或上下文远没到上限------只用一个滑动窗口就够,别为没发生的问题过度设计。判断标准只有一条:你的 Agent 会不会长期跑、且信息量持续增长------会,就上分层;不会,一个滑动窗口足矣。

本专栏《AI-Agent 工具》从 Agent 记忆、上下文管理、工具调用一路讲到架构选型与评测,每篇配可直接运行的代码,持续更新。想在 Agent 实战里少踩坑,可在专栏页订阅。

参考资料(综合):

相关推荐
风雨中的小七20 分钟前
解密Prompt系列73. 爆改对话框:揭秘 Intelligent UI 的动态界面魔法
llm·agent·mcp
许雪里9 小时前
XXL-AI v1.5.0 发布|AI应用开发平台
agent·ai编程
ZhengEnCi11 小时前
Agent 工具怎么折叠海量输出才「爽」:Trae、Claude Code 的渐进式披露设计原理
前端·agent
ZhengEnCi11 小时前
Agent 面试为什么几乎不聊高并发:从瓶颈转移到考察维度的真相
agent
七夜zippoe12 小时前
Agent 优雅降级实战:模型不可用时的后备方案设计
ai·agent·模型·降级·后备方案
用户20187928316714 小时前
大模型是怎么"看"图、跑命令、调 MCP 的?我把 gemini-cli 源码扒了个底朝天
agent
Flynt14 小时前
一夜涨近2000星的"开源Grok Bot",我在1核2GB小机上装了一遍:壳是真的,脑子得自备
开源·agent·claude
用户9432547583714 小时前
你的 Agent 会重复扣钱吗?我给 LangGraph 加了一层"步骤日志"
agent
小田学Python16 小时前
DeepSeek Harness 完整上手指南:安装部署 + 必装插件 + 用一周就回不去的功能
大数据·大模型·agent·ai工具·deepseek