跳到主要内容

压缩陷阱:为什么长时运行的智能体会忘记已经尝试过的事情

· 阅读需 10 分钟
Tian Pan
Software Engineer

智能体调用文件写入工具,工具因权限错误而失败。智能体记录此事,转而采用其他方案。任务运行足够长时间后,运行时触发上下文压缩,生成摘要:"智能体一直在尝试写入输出文件。"被丢弃的内容:权限错误曾经发生过,以及为什么最初的方案被放弃。三百个 token 之后,智能体再次尝试同样的写入操作。

这种模式——姑且称之为压缩陷阱——是生产智能体系统中最顽固的可靠性故障之一。这不是模型 bug,而是压缩机制的工作方式与智能体在长时会话中保持连贯性所需之间的架构失配。

压缩究竟丢弃了什么

上下文压缩的存在有其必然性:智能体任务经常超出上下文窗口的承载范围。代码审查智能体、多步数据管道或自主研究任务,可能在完成之前就产生数百次工具调用。总得有所取舍。

主流方案是摘要化(由 LLM 压缩旧的对话轮次)、截断(丢弃旧消息),或两者结合。这些方法对会话状态的处理效果不错——能保留目标、近期进展和大致方向。但它们在一类特定信息上表现不佳:否定约束和失败记录

工具调用失败时,智能体需要记住三件事:失败了、具体出了什么问题,以及这对未来行动意味着什么。摘要可能会记录"智能体遇到了工具错误",但必然丢失因果链——是哪个约束导致了错误、为什么重试会得到同样结果、哪些路径现在已经封闭。这些"否定记录"是智能体向前推进与反复循环之间的分水岭。

一项针对代码中心任务中上下文管理智能体的基准评估发现,在压缩上下文下运行的智能体得分约为 10 分中的 4.0 分,而上下文完整的智能体得分约为 6.0 分。差距并非来自失去高层目标意识——而是来自重新读取已处理过的文件,以及重新尝试已经失败过的操作。

时机问题让情况更糟

还有一个结构性问题:压缩通常只在轮次之间运行,而非在轮次中间。一个以 170,000 个 token 上下文开始某轮的智能体,可能通过连续工具调用扩展到 400,000 个或更多,整个过程没有任何压缩。等到轮次结束、压缩运行时,上下文已经如此庞大,单次摘要需要覆盖大量历史——而大规模有损压缩比渐进式有损压缩损失更多。

这也意味着智能体没有可靠的时机检测到"我即将丢失状态——让我先把它外化"。压缩发生在事后,作用于智能体已不再主动管理的内容。

"迷失在中间"效应进一步加剧了这一问题。当上下文被摘要或截断时,剩余内容会重新定位——压缩前接近末尾的内容,在新上下文中变成了中间内容。LLM 有着经过充分记录的 U 型注意力模式,对开头和结尾附近内容的回忆更准确。压缩前新鲜且显著的状态,在压缩后变成了检索准确性降低的中间内容,即便它在技术上"活过"了压缩。

工程师在实践中观察到的三种失效模式

无限重试循环。 智能体调用工具,收到错误或模糊结果,再次调用。没有全局的尝试次数表示,也没有错误条件的记录,每个局部上下文状态看起来都像是"我应该再试一次"。循环持续到上下文填满、进程被终止或人工介入。这在结构上与 CPU 抖动相同——反复尝试访问某个资源,却没有元数据来识别该尝试是徒劳的。

重复已完成的工作。 智能体追踪修改过的文件、调用过的 API 以及已完成的子任务。压缩后,摘要保留了目标("更新这五个文件"),但丢弃了完成记录。智能体重新读取已处理的文件,重新运行已应用的转换。除了浪费算力,这还造成正确性问题:两次应用的转换、重复写入的记录、重复发送的通知。

约束失忆。 智能体在任务中途发现某个方案被阻断——速率限制、缺少权限、排除了一类查询的模式约束。这个约束是通过探索学得的,并不存在于原始系统提示中。压缩后,约束消失了。智能体退回到把被阻断的方案视为可行,探索成本被再次支付。

真正有效的方法

核心洞察是:上下文窗口不是可靠的智能体状态记忆系统——它是一个计算表面。任何需要存活足够长时间以防止重复工作的内容,都需要存在于上下文窗口之外。

将关键状态外化到持久存储。 工具调用历史、重试次数、发现的约束和操作状态,应该存储在键值存储(Redis、DynamoDB)或结构化日志中,智能体在每轮开始时查询。"我之前是否已经尝试过这个?"变成一次查询,而不是一次回忆任务。智能体不需要记住自己尝试过什么;它需要知道如何向一个永不遗忘的系统提问。

模式是:在调用任何破坏性或对幂等性敏感的工具之前,智能体检查该操作键的外部日志,查看是否有过往尝试。每次工具调用后,写入结果。压缩可以对上下文窗口做任何事——过往尝试记录持续存在。

为不变约束使用固定上下文槽。 基础设施层面支持一小块上下文(约 500 个 token),在每次压缩中都存活并在每轮重新注入。这是系统级约束的正确位置:"永不重试操作 X"、"检测到 Y 条件时停止"、"这些文件不得修改"。目标是让不变量在字面意义上不可压缩,而不是依赖摘要来保留它们。

在任务边界处检查点行为状态。 每当智能体完成一个有意义的子任务——处理完一批数据、完成研究阶段、完成一组文件——写入结构化检查点:已达成的目标、已完成的步骤、发现的约束、剩余内容。如果检测到压缩后的漂移(智能体开始重做已检查点的工作),从最后一个已知良好的检查点恢复,重新注入完整的检查点块。

这与压缩产生的 LLM 生成摘要不同。检查点数据是结构化的、由智能体控制的;它包含智能体正确恢复所需的确切字段。压缩摘要是通用文本,可能捕获也可能不捕获操作相关状态。

将步骤数量限制作为硬性基础设施控制。 这是对抗无限循环最简单也最可靠的防御:在运行终止前对总行动数设置硬性上限。合适的数字取决于任务,但没有上限是错误的答案。智能体不会因为认识到自己陷入循环而自我终止——它们从每个上下文状态出发进行局部推理,得出"再走一步是合理的"结论。步骤限制强制实施了一个不依赖智能体自我意识的断路器。

将此与通过操作指纹识别的循环检测配合使用:对(工具名 + 输入 + 近期输出)进行哈希,如果同一指纹出现三次则停止。这能捕获非明显的循环,即智能体在做出略微不同但同样徒劳的尝试。

将工具输出设计为紧凑且基于引用的形式。 如果工具返回 50,000 个 token 的日志输出并被包含在上下文中,它会主导上下文窗口并产生巨大的压缩压力。将大型输出存储在外部状态中,返回简短引用("log_ref_abc123:50k 行,关键发现:X、Y、Z")。智能体可以在需要时检索完整输出;上下文窗口只看到摘要。这降低了上下文填充速率,减少了积极压缩的压力。

内存层次结构,而非魔法模型

斯坦福的 MemGPT/Letta 架构通过一个受操作系统启发的类比使这一点具体化:将上下文窗口视为 RAM,将外部存储视为磁盘。智能体明确管理两者之间的移动。核心记忆(上下文内)保存热工作集;归档记忆(外部数据库)保存其余一切。当上下文填满时,内容以类似 LRU 的策略被逐出并带摘要存储。当智能体需要被逐出的内容时,它显式检索。

这比大多数团队想从头构建的基础设施要多,但它指向了正确的心智模型:上下文窗口是被管理的缓存,而非事实来源。生产智能体需要工作状态与持久状态之间的分离,就像任何其他有状态系统所需要的一样。

各框架正在向类似模式汇聚。LangGraph 的检查点层处理每轮状态持久化。LangMem 为跨会话的长期记忆添加了语义检索。Google 的 ADK 和 Anthropic 的托管智能体基础设施都将压缩管理作为一等关注点。工具支持已经存在;差距通常是工程师把上下文当作持久存储来对待,而它并不是。

基础设施层面的论点

一个有助于获得组织认同的框架:上下文压缩失败不是模型质量问题。改进基础模型并不能修复一个在压缩丢弃失败记录后重试失败操作的智能体。修复需要外部状态,而不是更好的摘要。

这很重要,因为"修复模型"或"改进提示"是对智能体可靠性失败的常见第一反应。对于压缩类问题,它们是错误的干预措施。一个在压缩期间丢失否定记录的智能体,无论模型能力如何,都会继续丢失它们,因为损失是结构性的。信息是真实地消失了。

2025 年成功发布可靠长时运行智能体的团队,无一例外地汇聚于同一套基础设施决策:硬性步骤限制、用于工具调用历史的外部状态、用于不变约束的固定上下文,以及任务边界处的结构化检查点。这些是工程决策,不是 AI 决策,它们属于与任何其他有状态分布式系统相同的架构审查范畴。

那些在生产中失败的智能体——确实有不少值得关注的案例——通常是因为有人假设上下文窗口会处理好状态问题。它不会。它从来就不会。只是任务足够长之后,这一点才变得显而易见。

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