上下文压缩技术全面深度测评:DeepAgents如何有效解决AI对话失忆难题

2026-06-06阅读 0热度 0
黑科技

开场:为什么“上下文”会变成负担?

大模型天然受限于“上下文窗口”,其单次能处理的token总量是固定的。 简短对话几乎没有影响。但当Agent需要执行规模较大的长期任务时,每一轮“调用工具 → 获取结果 → 继续推理”都会持续往历史记录中追加大量内容:读取的文件、运行的命令、命令返回的日志……几十轮交涉后,历史列表轻松膨胀到几十万token。 一旦触及窗口上限,会立刻引发三个典型问题: - **请求直接被拒**:provider返回context overflow错误; - **API开销飞涨**:每轮对话都需重新发送全部历史,token量线性推高成本; - **早期指令丢失**:粗暴截断会连带删除最初的系统目标和任务计划,Agent瞬间“失忆”。 因此,主打长链路任务的Agent必须配备一套**上下文压缩(Summarization / Compaction)机制**:将那些“陈旧的、当前无关紧要的”信息折叠成摘要,清出空间以维持后续执行。 这听起来不过是“把旧对话历史总结一下”,但真正要做到精准、稳定、无信息损耗,内里细节远多于表面想象。我们就以DeepAgents的具体实现入手,逐层剖析。 ---

一、一句话看懂 DeepAgents 的设计哲学

DeepAgents的压缩方案有一个非常“工程师”的标志:**它没有选择造轮子。** 它直接沿用了LangChain自带的压缩内核——那套“检测阈值 → 定位断点 → 切分 → 生成摘要”的核心逻辑,完全委托给上游组件。DeepAgents只在其外部封装了四项针对长任务Agent的**实用增强**: - “历史落盘,可回溯”——被压缩的消息并非删除,而是归档存储; - “压缩前先裁大参数”——增加一层低成本优化,往往截断后便无需触发LLM摘要; - “撞墙时有容错机制”——真正溢出报错时,自动切换至压缩后重试; - “非破坏式压缩”——原始对话内容一字不动,仅记录压缩事件的元数据。 外加一个「手动压缩工具」,使模型能够自主发起清理请求。 记住这个框架,接下来逐条展开。 ---

二、主流程:进模型前的三步流水线

DeepAgents将压缩逻辑挂载在“每次调用大模型之前”的节点上。每次模型调用前,都会执行一条三步流水线: **第 0 步|重建有效消息**:检查上一次是否执行过压缩,若是,则将“摘要 + 后续新消息”拼装成当前会话的有效视图。原始消息保持不变,这一点尤为重要,将在第四节详析。 **第 1 步|截断大工具参数**:这是成本最低的一步。将历史中过长的`write_file` / `edit_file`参数(例如一次性写入数千行代码内容)缩短为“前20字符 + (已截断)”。多数情况下,仅此一步省下的token就足够,根本无需劳烦大模型。 **第 2 步|阈值判断**:计算当前token总量,确认是否超出触发线。未超出则直接送入模型。此处巧妙之处在于:即便模型依然返回“上下文溢出”错误,系统不会直接抛错,而是“就地转入压缩流程重试”。这为阈值估算增加了一道保险。 **第 3 步|实际压缩**:超出阈值才执行完整压缩。顺序非常讲究——“先定位安全切点 → 将旧历史落盘 → 最后让LLM生成摘要”,然后以“摘要 + 近期消息”替代原始长序列,再发送给模型。 ---

三、魔鬼在细节①:切点不能乱切

“将旧消息总结掉”看似简单,但**“从哪个位置下刀”**是真正的技术难点。 Agent的历史记录中存在大量“工具调用对”:AI发出“我要调用bash执行一个命令”(AI消息),紧接着是“命令执行结果”(Tool消息)。二者紧密关联,无法分割。 如果切点恰好落在两者之间,就会留下一条“没有来源的答案”——一条孤儿Tool消息。绝大多数模型遇到这种不完整结构会直接报错。 DeepAgents(借助LangChain内核)的处理手法相当优雅:若检测到切点压在某条Tool消息上,即“向前回溯”,找到发起此调用的那条AI消息,将切点前移,确保这对消息要么一起进入摘要,要么一起保留。token预算采用二分查找精确逼近,但**绝不以此为代价切断工具对。** ---

四、魔鬼在细节②:压缩后,原稿一字不删

这是DeepAgents与LangChain原生实现“最核心的分歧点”,也是最值得记录的设计决策。 LangChain原生的做法简单直接:在压缩节点中“直接改写对话状态”——彻底删除原始消息(`RemoveMessage(REMOVE_ALL_MESSAGES)`),替换为“摘要 + 近期消息”。结果干净,但原始记录丢失了。 DeepAgents选择截然相反的路径:它**不对原始消息进行任何删除**,仅在私有字段`_summarization_event`中记录三项内容:“切点位置、摘要内容、旧历史归档文件路径”。每次模型调用前,根据这个“事件”动态计算出精简后的视图再传递。 为什么坚持这么做?因为保留原稿能带来多重显著优势: - **支持回放(replay)**:原始对话始终可用,随时重跑、复盘; - **支持评测(evals)**:以真实完整轨迹进行评估,结果不失真; - **支持与手动压缩工具共享状态**:两条压缩路径共读写同一份原稿,不会产生冲突(见第六节)。 代价也确实存在:每次调用需要“临时重建视图”,并处理切点坐标的转换。但针对一个追求长期稳定、高度可观测、支持深度调试的Agent平台来说,**“保留原始记录的价值远远超出这部分开销。”** ---

五、魔鬼在细节③:压缩 = 折叠 + 归档,不是删除

许多人将“压缩”等同于“丢弃旧数据,只留一份摘要”。DeepAgents的理解更深入一层:**“压缩是折叠操作,旧内容必须归档,且Agent应能够回溯查阅。”** 正式压缩前,它先以“追加写入”的方式将待丢弃的那批消息存入一个Markdown存档文件(每线程独立文件,每次压缩追加一个带时间戳的区块)。紧接着——这一步最为精妙——**“在摘要消息中嵌入该存档文件的路径。”** 模型最终收到的摘要类似于(大意): > “文件 `thread_xxx_archive.md` 中保存了第 1 至 20 轮对话的完整历史记录。本次会话目标是……已完成操作包括……后续计划是……” 如此,上下文内容体量大幅缩减,但**任何被折叠的细节,Agent均能通过 `read_file` 操作反向查询。** 摘要遗漏了某个关键命令的输出?直接查阅存档即可。这一机制将“有损压缩”升级为“无损归档 + 有损视图”,是整套方案中最实用的增强。 补充一点:摘要本身同样经过精心设计。提示词强制LLM按四段checklist输出:「SESSION INTENT(本次任务的终极目标) / SUMMARY(关键决策与结论) / ARTIFACTS(涉及哪些文件变动) / NEXT STEPS(下一步执行清单)」——专门防止压缩后Agent重复执行已完成操作,或忘记已生成的产出物。 ---

六、自动 + 手动:两层触发,一份状态

DeepAgents为压缩机制配置了“两套触发方式”,覆盖不同使用场景: - **自动层**(`SummarizationMiddleware`):后台兜底机制。token一旦超过触发线(使用模型profile时,默认设为窗口上限的**85%**)即自动执行压缩,全程无需模型或用户介入。 - **手动层**(`SummarizationToolMiddleware`):为Agent装配一个`compact_conversation`工具,并在系统提示中悄悄告知——“当你切换到全新任务、或已得出结果、先前的上下文不再需要时,可主动调用它进行清理。” 手动层配备一条防呆规则:**“50% 资格闸门”。** 若上下文容量尚未达到自动触发线的一半,模型即使调用了`compact_conversation`也会被礼貌拒绝(“尚未到压缩时机”),以防过早压缩浪费一次LLM调用。 两层设计最巧妙之处在于:**它们共用一个 `_summarization_event` 状态字段。** 自动压缩和手动压缩写入同一份记录,状态互通、配置协同,绝无“压缩两遍”“坐标错乱”的风险。 ---

七、把设计权衡列成一张表

DeepAgents的每一个选择背后,都是一组明确的取舍。通过一张表格可一览全局: | 权衡点 | DeepAgents 的选择 | 换来什么 / 代价 | | --- | --- | --- | | 状态可变性 | “非破坏式”:保留原稿,压缩信息写入私有字段 | 支持回放/评测/工具协同;代价是每次需重建视图 | | 复用还是重写 | 复用LangChain内核,自制外层增强 | 逻辑零冗余、同步上游更新;代价是与上游私有方法耦合 | | 旧历史去向 | 压缩前“先落盘”,摘要内含路径 | 细节可通过read_file回查、artifact不丢失;代价是多一次文件I/O | | 压缩成本 | 先做“参数截断”这一廉价优化 | 常可免去一次LLM摘要调用;代价是截断导致部分信息丢失(已落盘) | | 撞墙处理 | 不超阈值也尝试提交,溢出则压缩重试 | 阈值估算宽松也不影响运行;代价是极端场景下多一次失败往返 | | 触发方式 | “自动 + 手动”两层,共享状态 | 兼具兜底能力与主动清理;代价是需50%闸门防止过早压缩 | | 默认阈值 | 有profile按比例(85%)、无则保守固定值 | 开箱即用、跨模型自适应;代价是依赖profile数据精度 | 一句话总结这张表:**DeepAgents 的压缩方案,是一次务实、克制、强调可观测性的系统设计。** ---

八、几个还值得琢磨的问题

阅读源码至结尾处,有几个点我个人仍存有疑惑,记录于此供同好探讨: - **“多子Agent归档文件是否可能互相污染?”** 主Agent与每个子Agent各自安装了一份压缩中间件,归档文件以`thread_id`命名。子Agent是否复用了父线程ID?若复用,多个Agent的历史是否会追加至同一文件中导致数据混乱? - **“截断逻辑仅覆盖 `write_file` / `edit_file`”**:那些可能产生超长输出的自定义工具(例如某些`execute`函数)的大参数并未被截断。对于长输出工具较多的场景,本层优化收益有限。是否应开放一个工具名白名单供开发者配置? - **“token计数的两套处理逻辑”**:内部针对“是否将工具定义一并统计入token”做了try/except兼容处理,部分计数器下工具占用未纳入阈值计算。工具密集场景下,是否会低估总token量,导致压缩触发偏晚? 这些问题算不上bug,更多是“在通用性与精确性之间如何权衡”的开放性话题。欢迎研读过源码的同行一起探讨。 ---

结语:好的压缩,是“忘得优雅”

归结而言,DeepAgents的上下文压缩方案带给我最大的启发是:**“高效的压缩,不是删得彻底,而是忘得恰到好处。”** 它给出的答案是一套克制而严谨的工程选型:复用成熟组件不强行炫技、优先采用低成本的token节省手段、切点严格守护工具调用对、原始数据一律备份、旧历史归档并支持回溯、自动与手动双保险互为支援。每一项单独拎出来并不惊艳,但组合在一起,便构成了一套“能扛住长任务、同时不丢失关键信息”的压缩体系。 未来当你设计任何“需要长期运行、上下文会持续膨胀”的AI系统时,不妨拿来这套设计思路——**“遗忘,也可以做到很优雅。”** ---

延伸阅读

- 本文的完整技术深读(含逐段源码引用、设计权衡、待验证清单):`notes/research/agent-frameworks/2026-06-04-deepagents-context-compaction-深读.md` - 配套对比:`notes/research/agent-frameworks/2026-05-19-langchain-1.0-upgrade-深读.md`(LangChain 中间件机制总览) 涉及代码版本: - DeepAgents `2ac7d415`(`langchain-ai/deepagents`,`middleware/summarization.py`) - LangChain v1(压缩内核 `SummarizationMiddleware` 来源)
免责声明

本网站新闻资讯均来自公开渠道,力求准确但不保证绝对无误,内容观点仅代表作者本人,与本站无关。若涉及侵权,请联系我们处理。本站保留对声明的修改权,最终解释权归本站所有。

相关阅读

更多
欢迎回来 登录或注册后,可保存提示词和历史记录
登录后可同步收藏、历史记录和常用模板
注册即表示同意服务条款与隐私政策