当Agent运行超过40分钟、上千轮请求后,上下文窗口触达上限时,历史数据按"长短和新旧"驱逐,导致刚读过的堆栈信息被丢弃,下个回合被迫重新跑测试。Favur团队实测单次任务消耗上万次模型请求,记录可公开查阅。
一个 Agent 在任务进行到四十分钟时,已经读取了三个文件、运行了两次测试套件、捕获了一个栈追踪,并写出了一份修复计划。此时上下文窗口填满了,紧缩(compaction)运行以驱逐历史记录来腾出空间,而栈追踪因其在长度上靠前且时间上靠后而被清除。下一轮就需要重新运行测试来恢复 Agent 原本已经拥有的信息。
Favur 是一个 Agent 团队,它接收一份书面规范并从中构建软件,自主进行规划、编码、审查和测试,无需任何人驱动。任务运行时间足够长,以至于这成了常态而非边缘情况。本月有一个任务在完成前发出了超过一万次模型请求,其完整公开记录在此。

当紧缩触发时,它面对的是四百条毫无区分的文本。有些是 Agent 一次性消费过的工具结果。有些是它写下的计划。有些是某人输入的指令。没有任何东西能把它们区分开来。
唯一剩下的属性是长度和最近程度,而这两者都与真正重要的东西负相关。栈追踪既长又旧,而下一轮正需要它;目录列表既短又新,却已经被消费过了。
这个领域的大部分工作都集中在让那个判断更加精准。给模型一个工具来剪掉它自己的历史。训练一个小模型来给相关性打分。分层摘要。所有这些都是真实的工程,而且都接受了一个前提:判断发生在窗口已满的时刻——而这正是系统对自己历史了解最少的时刻。
每条消息都通过特定的调用点到达,出于特定的原因。在那个瞬间,没有任何东西是模糊的。代码知道它正在记录一个供即时消费的工具结果,或者持久化一个计划,或者记录一条指令。然后它保留了文本而丢弃了原因,一小时后不得不从毫无区分的文本中重建它。
所以 Favur 在追加时就给消息分配一个保留类别。有三个类别,名字本身就承载了整套方案。Forgettable(可遗忘)、Fuzzy(模糊)、Strict(严格)。调用点选择其中一个,因为调用点是系统中唯一知道的那个部分。
进度报告——Agent 写给自己的日志,然后读回来以恢复自己的状态——以 strict 追加。这使得日志在早期的压缩轮次中仍然可用,而这正是 Agent 最可能需要它的时候。但 strict 不是豁免。在最高层级,日志同样会被丢弃。注入到运行中 Agent 的指令也是以 strict 追加的,原因相同。
这样紧缩就不需要任何判断了。在压力下,它不是读取消息,而是读取消息分类。
第一层(通常占用 5%)对 forgettable 和 fuzzy 素材进行摘要,不删除任何东西。历史变短了;所发生事情的整体形状以压缩形式保留下来。
第二层(10% 占用)丢弃那些素材,并开始对 strict 进行摘要。日志和指令仍然存在,只是变小了。
第三层(25% 占用)丢弃所有三个类别。我们通常将窗口占用限制在 50%——超过这个比例 Agent 就会失败。
回到栈追踪的例子——它以 strict 追加,所以下一轮需要它时栈追踪仍然在那里,因为 strict 在第二层之前不会被摘要,到第三层才会被丢弃。而目录列表以它应得的方式追加,在第一层之后变成摘要,在第二层之后消失,这对目录列表是正确的,对这里却是错误的。两种结果都不是紧缩的判断。都是在追加时决定的。
各层级的阈值是配置项而非常量。固化在客户端中的触发器只是关于对话何时运行足够长的一声意见,适用于该客户端将运行的所有任务,而两小时重构任务的正确值并非六轮问答任务的正确值。
把决策移到信息最充分的时刻,也意味着把它移到调用点最多的时刻。
一个紧缩例程是一段代码,错在一个可以找到的地方。保留类别是截然相反的权衡。每一条追加消息的代码现在都要做一个小区分,而一个错误的区分是静默的——因为一条以 forgettable 追加的消息不会宣告它本应是 strict。它在一个没人监视的任务中比预期更早地离开某一层级,而很久以后才浮出水面的,是一个看起来忘记了某些东西的 Agent。
这个方案的效果也取决于其最不经心的调用点。层级按意图分类,但前提是每条消息都携带意图,而一条没有类别的消息又会回到按长度和最近程度分类的状态,尽管周围的一切都分类得当。这不是一种可见的失败。它是历史中某个区域悄然回归到本设计所要取代的行为。

紧缩被当作摘要问题来处理,而它实际表现像个标注问题。那些标注在有任何东西去查找它们的时候根本不存在。
把判断移到追加阶段,那么在压力下运行的进程就完全不需要智能。这样产生的结果不是一个拥有更大窗口的 Agent。而是一个其窗口按某人选择的顺序清空的 Agent。
在我们的产品网站上阅读更多内容。
Favur Evals,公开基准,这些运行在此评分
Favur 本身目前是邀请制。如果你想给它一个你自己的规范,等待名单在此