Harness 解剖、上下文腐烂,和那个铰链

测过的 18 个前沿模型,无一例外随输入变长而变差。而压缩会悄悄删掉你写在上下文里的安全约束——因为它优化的是任务连续性,不是规矩。

位置
第 03 篇 / 共 5 篇
预计
7 分钟

上一篇讲了循环的骨架。这一篇往下一层,看单次运行是怎么被武装的 —— 以及它会怎么烂掉。

Harness 的解剖:骨架、错误处理、护栏

harness 是四层栈里的第三层,管的是「单次运行需要随身带什么」。拆开看是三块。

骨架 —— 有哪些工具、允许哪些动作、什么状态算做完。这块决定了 agent 的能力边界,也决定了它的搜索空间:工具描述写得详不详细,本身就能带来几十个百分点的差别,这一点在别处量过。

错误处理 —— 一个动作失败了怎么办。关键不是「要不要重试」,是分类

错误类别 该怎么办 为什么
认证失败、参数写错 立刻失败 重试一百次输入都一样,结果也一样
限流、超时、5xx 退避后重试 是暂时的
现场已经脏了 重试单步没用,得回退整段 前置条件已经变了

判据不是「失败了没有」,是「重试有没有可能成功」。 一个打错的 key 让每个任务白白试三次,最后报同一句话 —— 这是最常见的浪费,而且我自己就写过这个 bug:我的错误分类一开始把 401 也归进「可重试」,直到有一轮跑完发现每个任务都试满三次、报的是同一句认证失败。

护栏 —— 在动手之前拦住不该做的事。有两个位置容易被忽略:

  • 输入端:agent 读进来的材料(文档、代码、网页)里可能藏着指令。这是提示词注入,第五篇细讲。
  • 输出端:不可逆的动作(删除、支付、发布)该不该做,得有一道显式的门。

护栏拦下来之后不能静默跳过 —— 「验过了」「验失败了」「没验」是三个状态,混成两个就会出现假绿灯。

上下文腐烂:agent 为什么跑着跑着忘了自己在干嘛

这是长时程循环最反直觉的失败。

证据很硬:Chroma 那份研究测了 18 个前沿模型,结论是每一个都随着输入变长而变差。不是「有些模型有这个问题」,是全部。

注意这和「窗口装不下」是两回事。窗口没满,性能也已经在掉了 —— 有效信息被稀释在噪音里。这就是 context rot 这个词的意思。

而循环把这个问题放大了,因为循环天生会让上下文越滚越长:这一轮的产出喂进下一轮,一轮轮累加。

压缩,以及它悄悄删掉的东西

对付上下文变长的常规手段是压缩(compaction):把早期的对话摘要掉或者驱逐掉,腾出预算。

这里有一个非常值得警惕的发现。有研究把它叫做 Governance Decay(治理衰减):

你写在上下文里的那些长期约束 —— 运行时策略、记忆条目、常驻指令 —— 在它们可见的时候 agent 会老老实实遵守;而 harness 一压缩历史,它们就被丢掉了。

原因不是压缩算法有 bug,是它的优化目标不对:压缩优化的是任务连续性,于是它把「常驻策略」当成低显著度的内容处理掉了。最近做了什么被留下,不许做什么被扔掉。

后果是具体的:约束消失之后,下游出现工具调用层面的违规 —— 而且没有任何报警,因为从系统的角度看,它只是压缩了一次历史。

还有一个更麻烦的性质:压缩是有损操作,而且会叠加。一个跑七天的会话可能压缩五十到一百次,每一次压缩都是作用在上一次压缩的输出上。而目前没有公开基准去系统地量「连续多次压缩之后准确率掉了多少」。

该怎么办

三条,按性价比排:

一、有损的是喂给模型的那一份,存下来的那份必须完整。 压缩照做,但完整的原文要落到磁盘上、能调回来。只在控制台 warn 一句「截掉了 3000 token」没有用 —— 事后想知道截掉的是不是关键信息,得能把原文调出来。

二、约束不能只活在上下文里。 既然压缩会删掉它们,那真正要紧的规矩就该放在代码里,而不是提示词里。提示词是请求,代码才是约束。

三、砍了什么要记下来。 不只记「砍了多少 token」,要记砍的是哪几段。否则「覆盖度低」到底是模型不行还是你砍错了,分不清。

这三条我的经验里第一条最容易被跳过,因为它当下看不出好处。我最初只在控制台 warn 一句「截掉了约 3000 token」,直到要复盘一次覆盖不全的运行 —— 那时候我答不上来「被砍掉的里面有没有关键规则」,因为原文已经不在了。

验证:整个循环转动所依赖的铰链

前面两节都是在武装单次运行。这一节是那个让循环能转的东西。

铰链的意思是:验证的结果决定循环下一步往哪儿走 —— 完成、重试、还是停下。

验证结果 循环怎么走
通过 收工,写状态,等下一轮
不通过、但可修 带着具体的失败原因再跑一轮
不通过、且不可修 停下来,交给人

第三行最容易被漏掉。一个只有「通过」和「重试」两个出口的循环,遇到修不好的东西会一直转 —— 有案例记录到连续 113 份报告、每份都跑满 16 次重试上限,一个可执行产物都没产出。

验证的可信度分三档,用哪档取决于你能拿到什么:

  • 确定性检查(测试、编译、lint)—— 最可信,最便宜,能用就用
  • 错误日志(真跑一遍看报什么错)—— 比读代码强,因为它基于执行而不是猜测
  • 模型判定 —— 最后的手段,而且必须换一个没参与干活的模型

「能用死规矩做对的事,就别交给会瞎猜的东西。」 这条线画在哪儿,直接决定整个循环可不可靠。

边界与代价

上下文腐烂的证据来自特定的测试方式。 那 18 个模型是在「输入变长」这个维度上被测的,具体的任务形态会影响掉多少。可以拿它论证「长上下文要当心」,不能拿它推算你的场景会掉几个点。

「完整副本落盘」是有代价的。 每次请求多一次写盘,而且日志里会有提示词全文的引用 —— 这意味着那个目录里有产品文档、代码片段、有时候还有测试账号。它不能提交进仓库,这是安全边界不是洁癖。

验证做得越严,跑得越慢。 每一轮多一次独立判定,就是多一次调用、多一份延迟。这笔钱该花,但要算进循环经济学里 —— 尤其当验证要驱动浏览器、每步几十秒的时候。

下一篇

前三篇都是原理。下一篇动手:选一个项目,从零把一个真的循环搭起来。

选题本身有讲究 —— 为什么「客服 agent」和「bug 分诊循环」是两个特别适合入门的题,以及第一个循环该做多小。