loading

Loading

探索与发现

首页 AI 工程化

上下文分段回收:Agent 长跑不掉线的工程做法

分类:AI 工程化
字数: (2478)
阅读: (4)
0
摘要:Agent 长任务在十几轮后崩掉,多因为上下文组织和回收没做好。本文聊分段的切法、触发点、不要回收的三类信息,以及卸载、隔离、压缩怎么配平,中间穿插 Shannon 三段式压缩、LangChain 四策略和 Context Rot 的具体工程取舍。

Agent 跑长任务时最典型的故障,不是模型不够聪明,而是它在第 40 轮之后开始胡来:重复调用同一个工具,忘记最初的目标,或者在自己删掉关键信息后给出一个看起来合理、实际跑不通的方案。Cognition AI 在谈单线架构时给过一个观察,一个 tool calling loop 连续跑 10~20 轮之后,很容易进入无法自我恢复的混乱状态。这里的混乱大多不是推理能力退化,而是上下文的组织和回收没做好。

“分段回收”是我在项目里常用的说法:不是等上下文撑到窗口上限再一刀切,而是在 Agent 循环中点状地识别哪些段落已经完成使命、哪些还需要留、哪些只需要留一个可回溯的钩子。

先承认物理限制:Context Rot 不是玄学

Chroma 的研究把这个现象叫 Context Rot,token 数量上升后,模型准确利用信息的能力持续下降。原因是自注意力在 token 数上是近似 O(n²)。窗口越大计算越贵,且关键信号会被噪声稀释。128K 到 200K 这个区间被不少人当成性能腐烂的阈值参考。

所以“上 1M 窗口就好了”是伪解。窗口换来的不是免费记忆,而是更贵的注意力分配。真正的问题是:哪些段落必须留在窗口里参与推理,哪些可以压缩成一次指针,哪些可以完全移出去、只在需要时拉回。

分段的第一刀:切静态,别切动态

合理的拆法不是按“第几轮到第几轮”切,而是按信息是否易变来切。System Prompt、工具定义、领域规则属于相对静态的部分,会话开始就定了,token 预算需要提前划走;对话历史、工具调用返回、检索片段属于动态增长部分,膨胀速度取决于任务类型。

静态部分适合做前缀缓存,动态部分适合按阶段注入。这条边界如果一开始不划清楚,后面所有回收动作都会变成“感觉这里差不多了删掉一点”,迟早删错。

回收的触发点:按预算放量,不按消息条数

比较可靠的触发条件是预算使用率,而不是消息数。用估算函数先把 token 算出来:文本按字符数除以 4、代码按除以 3、每条消息加 5 的结构开销,再乘 1.1 的安全边际,误差通常在 10~15%。在预算用到 75% 左右时启动压缩,目标压回 37% 附近,这个余量留给当前轮的输入输出和工具返回。

触发太早会浪费时间,触发太晚会导致这一轮已经被垃圾占满。按预算触发的好处是可以和 Prompt Cache 对齐:压缩本身会破坏前缀变化,频率必须控制。

三件事不能一起回收

压缩不能只交给一个 LLM 摘要了事。我自己踩过两类坑。

一是不可逆的截断

Shell 输出、长日志、MCP 返回值如果直接按行截断,最关键的报错往往就在末尾,或者藏在中间几行。Agent 拿到半截结果,会得出错误结论,然后花更多步数反复确认。较稳的做法是保留结构:错误类型、关键文件路径、URL、行号这类可回溯的指针必须留在摘要里。丢失细节可以接受,丢掉回源路径就是丢记忆。

二是错误路径被清空

失败的尝试是很有价值的上下文。Agent 删掉自己走过的那条死路,会很快再走一遍。失败分支值得用一条几十 token 的记录保留:试了什么、在哪一步挂了、看到什么报错。给信息打标记——冗余、关键、待确认——比按规则无条件删除可控得多,因为误删隐性关键信息的代价通常大于多留几行的代价。

Shannon 在压缩实现里用了一个三段式参考:Primers 留前 3 条消息,建立最初需求;SUMMARY 把中间几百条压成一段结构化摘要,保留关键决策、重要发现和未解决问题;RECENTS 留最后 20 条,找回“刚才那个方案”。这是最近这一侧连贯性的底线,删掉之后 Agent 就无法回答指代性的追问。

回收不等于只压:卸载和隔离是同一套账

LangChain 的四策略里,Compress 只是其中一个动作。Write 先把信息写到文件或 notepad 里,动态任务目标、已完成的检查项、等待解决的前提写成 todo 文件,需要时再读回上下文尾部——这还有一个额外好处,模型对上下文中间的信息关注度最低,回填到尾部能提高它被用上的概率。Select 强调的是 just-in-time,不预加载,按需从文件搜索或 RAG 取。Isolate 是在窗口容量之外做分区:子 Agent 在干净的窗口里探索几万 token,只返回一两千 token 的结论。

分段回收要配合这几件事才是完整的账。只压缩不卸载,摘要会越写越长、本身也要占空间;只卸载不隔离,超过单窗口容量的任务没有出口。

工程落地里真正麻烦的三件事

掩码成本不对称。没被采纳的探索历程不能一概删掉。给每条轨迹打上标记时要允许中性存在,给错误的尝试留下最小的结构记录。判断“这条能不能删”本身不值多少钱,判断错了的代价包括多走 10~20 步、多烧一轮 token,甚至模型在错误前提上给出一个看起来能跑的答案。

回收不能打断身份连续性。压缩之后,模型得认得出“我刚才提出这个方案”和“用户最初提的问题”之间的区别,对应的摘要方案就该保留话语主体和具体冲突点——两个决策的分歧不能合并。过于简化的摘要会制造出“虽然所有信息都提到了,但核心传达失败了”的结果。

需要在压缩前后保存缓存命中记录。不能绕开的情况是:一次会话里带了缓存命中,回收的边界一旦漂移,后面每轮的预填充成本都会变成全价。把缓存边界和分段边界放在同一次判断里,是一种会约束你决策的自由度。

一个可执行的最小策略

我目前的量尺是:

  • System Prompt、工具定义做前缀缓存,开工前先按预算一次划清;
  • 每条任务分支到完成就固化成一个“结论摘要+回源指针”,不上台堆在原位上;
  • 出错或放弃的步骤用几行记录保留,不进入原始历史;
  • 每到一个段边界处重新写一次任务目标,并定义清楚下一步边界——把叙述尾巴敲在这类可以被注意力覆盖的范围终点,比去评估中间哪些信号强可复用的姿态更好;
  • 压缩过程中校验无法回放的边界点:工具定义丢失、原始文件句柄、调试所需的标识符。

大部分场景最先被忽视的从来不是窗口容量,而是回收是否可逆、分段是否对齐缓存这些没出现在示意图里的具体判断。

转载请注明出处: 皮哈皮阿

本文的链接地址: https://blog.phpat.com/ai-engineering/662.html

手机扫码阅读

微信或手机浏览器扫一扫,随时随地随心阅读与分享

您可能对以下文章感兴趣
1 / 1
预览图