跳到主要内容

上下文窗口悬崖:当你的 Agent 在任务中途触及限制时究竟发生了什么

· 阅读需 9 分钟
Tian Pan
Software Engineer

你的 Agent 完美地完成了第一步到第六步。第七步与第二步矛盾。第八步幻觉出一个不存在的工具。第九步自信地提交了垃圾结果。没有任何崩溃。没有抛出任何错误。Agent 只是忘记了自己在做什么——然后继续执行。

这就是上下文窗口悬崖:AI Agent 累积的上下文超过其有效推理能力的那一刻。它不会优雅地失败。它不会寻求帮助。它会基于不完整的信息自信地做出错误决策,而你直到损害造成后才会知道。

宣传容量与有效容量之间的差距

每个模型都宣传一个上下文窗口——128K token、200K,甚至一百万。这些数字是营销上限,而非工程保证。

研究人员对 13 个前沿模型进行了需要真正推理(而非表面模式匹配)的任务测试,发现其中 11 个在 32K token 时准确率降至基线的 50% 以下。GPT-4o 从 99.3% 的基线准确率降至 69.7%。这些模型在技术上可以接受更多 token。只是无法对它们进行推理。

这一差距对 Agent 至关重要。一个代码 Agent 在做出第一个真正决策之前,可能已经累积了 50K token 的文件内容、工具输出和推理轨迹。当它到达关键步骤时,已经在退化区间运行——不是因为窗口满了,而是因为其注意力已经变得不可靠。

"中间遗忘"效应加剧了这个问题。模型强烈关注上下文的开头和结尾,而实际上忽略了中间部分。一个累积了 150K token 上下文的 Agent 可能实际上忽略了位于窗口中间的 100K token 的工具响应。信息存在于输入中,但缺席于推理过程。

上下文溢出的三种故障特征

上下文溢出不会产生单一的故障模式。它产生三种不同的特征,每种由不同的截断策略引起——每种都以自己的方式构成危险。

静默截断是最古老也最危险的模式。当上下文超过限制时,框架悄悄丢弃最旧的消息。Agent 失去了原始指令、任务定义和给予它的约束条件。它继续执行——现在已经脱离了目标。系统提示词蒸发。安全护栏消失。Agent 自由地产生幻觉,因为本应约束它的上下文不再存在。

摘要失真用压缩摘要替换原始历史。这听起来很合理,直到你意识到摘要的有损性是不可预测的。第二步中提到的数值约束——"永远不要超过每秒 500 个请求"——可能被摘要为"存在速率限制方面的考虑"。使约束可操作的具体性消失了。Agent 带着速率限制存在的模糊感觉继续前进,但不知道具体数字是什么,做出的决策在技术上承认了这个概念,同时违反了实际数字。

滑动窗口遗忘保留最近的 N 个 token 并丢弃之前的所有内容。这创造了只能记住近期过去的 Agent。第七步与第二步矛盾,不是因为 Agent 决定改变方向,而是因为第二步在它的世界中不再存在。每个决策在局部上是合理的,但在全局上是不连贯的。产出的工作在任何给定快照中看起来都很合理,但当你审视完整轨迹时就会崩塌。

为什么 Agent 不会优雅退化

根本问题在于 LLM 不知道自己不知道什么。一个忘记了需求的人类工程师知道自己可能遗忘了什么。他们会感到不确定。他们会查看笔记。

丢失了上下文的 LLM 不会对丢失的信息体验到不确定性——它只是不知道这些信息曾经存在。它基于剩余的上下文以完全的信心生成内容。这就是为什么上下文溢出产生的是自信的错误,而非谨慎的错误。Agent 不会随着上下文退化而放慢速度或提出澄清问题。它的信心保持不变,而准确率崩塌。

Databricks Mosaic 的研究发现,在 32K token 之后,Agent 开始倾向于从不断增长的历史中重复操作。Agent 进入循环不是因为卡住了,而是因为上下文中最近的操作是它拥有的最强信号。原始目标、未探索的替代方案、早期步骤的约束——所有这些都已被推出或淹没在最近工具输出的累积权重中。

在多步骤工作流中,这创造了一个特征性的失败模式:前几步非常出色,中间步骤逐渐偏移,最后的步骤与最近的上下文一致但与原始目标脱节。工作成果看起来很专业。但它是错误的。

上下文溢出是容量规划问题

大多数团队将上下文溢出视为提示工程问题。他们试图写更短的提示、使用更简洁的工具输出,或切换到更大窗口的模型。这就像通过购买更多内存来处理内存泄漏——推迟了崩溃但没有修复原因。

架构层面的洞察是,上下文溢出是一个容量规划问题。你需要在 Agent 开始之前大致知道每个步骤将消耗多少上下文,以及总量是否适合你的有效(而非宣传的)上下文预算。

一个材料科学工作流清楚地展示了这一点:传统方法消耗了 2080 万个 token 并失败了。同样的工作流用内存指针重新设计后使用了 1,234 个 token 并成功了。差异不在于更好的提示。而在于更好的架构——Agent 操作的是对数据的引用而非数据本身。

容量规划方法意味着回答这样的问题:这个工作流会进行多少次工具调用?平均响应大小是多少?最坏情况是什么?如果三次文件读取平均各 15K token,你在任何推理发生之前就消耗了 45K token 的上下文。加上工具定义、系统提示和对话历史,你可能在 Agent 做出第一个有意义的决策之前就达到了 100K token。

主动上下文预算模式

一旦你将上下文视为需要预算的有限资源,几种架构模式就会浮现。

子 Agent 隔离将高风险或上下文密集的操作委托给隔离的子 Agent。50K token 的探索可以在进入父 Agent 上下文之前被压缩为 2K token 的摘要。父 Agent 对精心策划的摘要而非原始工具输出进行推理。如果子 Agent 失败,它不会用 50K token 的失败探索污染父 Agent 的上下文。

观察遮蔽用占位符替换较旧的工具输出,同时保留 Agent 的推理和操作历史。JetBrains 的研究发现,这在匹配或超过全上下文 Agent 的问题解决性能的同时,实现了超过 50% 的成本降低。关键洞察:Agent 自身的推理轨迹比它推理所用的原始数据更有价值。你可以遮蔽数据并保留结论。

预算感知压缩将上下文管理公式化为序列决策问题。Agent 监控其剩余上下文预算并动态调整压缩强度。这种方法的研究表明,一个具有 8K token 预算的 300 亿参数模型可以超越具有 128K 上下文的 2350 亿参数模型——因为较小模型通过有纪律的上下文管理对更高质量的信息进行推理,而较大模型则淹没在累积的噪音中。

内存指针用短标识符替换大数据负载。Agent 不是将完整的 API 响应倒入上下文,而是将其存储在外部并通过 ID 引用。当它需要特定字段时,只检索那些字段。这在网络搜索评估中实现了 84% 的 token 减少,在分子网格检索任务中实现了 17,000 倍的减少。

基于文件的迭代使用文件系统作为上下文缓冲区。Agent 不是将整个文件加载到上下文中,而是读取有针对性的行范围、处理它们、将中间结果写入磁盘,然后继续。文件系统成为具有无限容量的外部内存,Agent 的上下文保持精简。

构建上下文感知的 Agent 架构

实际的前进路径有三层。

首先,检测你的上下文消耗。你无法为你不测量的东西做预算。记录 Agent 工作流每一步的 token 数量。识别哪些工具调用是最大的贡献者。许多团队发现,一个冗长的 API 响应或一次贪婪的文件读取就占了他们上下文消耗的一半。

其次,为每个工作流阶段设置明确的预算。如果你的有效上下文窗口是 32K token(推理仍然可靠的地方,而非模型技术上接受输入的地方),而你的工作流有八个步骤,每个步骤大约获得 4K token 的净新上下文。需要更多的步骤必须压缩或外部化。需要更少的步骤可以将其预算捐给后续阶段。

第三,为上下文失败而设计。假设尽管你做了预算,某些运行仍会接近限制。建立检查点,Agent 在检查点总结其进展并可以从压缩状态恢复。像对待传统软件中的内存溢出错误一样对待上下文溢出:它应该是一个有恢复路径的已处理异常,而非结果的静默损坏。

上下文预算即架构

200K token 的上下文窗口不是 200K token 的推理引擎。它更接近于一个 32K token 的推理引擎加上 168K token 的可靠性递减的存储。构建可靠工作的 Agent 意味着为有效容量而非理论最大值进行设计。

从 AI Agent 中获得最佳结果的团队不是使用最大上下文窗口的团队。他们是将上下文视为其实际稀缺资源的团队——仔细预算、积极压缩,并设计架构使 Agent 始终对高质量、相关的信息进行推理,而非对不断增长的累积噪音堆进行推理。

上下文溢出不是模型的 bug。它是忽视模型真实约束的架构的 bug。

References:Let's stay in touch and Follow me for more thoughts and updates