让浏览器智能体只使用最新页面引用
多页任务中,历史页面快照里的元素引用可能被模型再次使用,造成误点或重复提交。作者在每次模型请求前压缩浏览器历史,仅保留最新观察的完整内容,将旧快照缩为操作回执。
AI 深度解读
AI 操作浏览器时,点错按钮未必是没看懂页面,也可能是记住了太多旧页面。作者的桌面 AI 助手 Ankita 就遇到了这个问题:对话历史里的旧控件引用仍被模型当成可用目标,甚至可能导致重复提交表单。
作者的处理很直接:每次请求模型时,只保留最新页面的完整观察,旧页面压缩成操作回执,有用的原文证据另外保存。这个方案值得看的地方,是它把「过去读到了什么」和「现在能操作什么」分开了。
旧页面为什么会变成新操作的陷阱
Ankita 是一个采用 MIT 许可证、以本地优先为原则的开源桌面 AI 助手,能够在聊天过程中操作真实的浏览器标签页。
问题出现在跨页面任务中。助手打开表单、读取控件、点击进入确认页,再读取一次。每份页面快照都留在对话历史里,包含 [ref=e12] 这样的控件引用。模型看到这些内容,却未必知道哪个引用已经失效。
它可能拿着三页之前的 ref 去点击。这个 ref 此时可能指向其他控件,也可能什么都指不到。对于提交表单的流程,重复执行旧页面上的动作,还可能再次提交已经完成的操作。
页面历史可以作为记录,但不能默认继续充当操作入口。
作者据此定下规则:每次调用模型前,只有最新的浏览器观察保留完整内容,较早的观察都压缩成回执。
压缩的是模型输入,原始记录仍然保留
实现位于 src/core/browser-context.mjs 的 compactBrowserHistory,由 src/core/agent.mjs 在请求时调用。
这里的「投影」可以理解为:从完整聊天记录生成一份本次发给模型的视图。存储的原始对话不改,工具 ID 和工具调用配对关系也保留,避免下游把这份视图误当成被重写过的会话。
函数先收集浏览器工具调用,再识别携带页面观察的工具回复:包含 [ref=...] 行,或者包含 Current snapshot: 分隔符的消息。除最后一份外,其余观察被替换成三部分:
- 保留快照分隔符之前的回执,最多 512 bytes,用来留下动作和页面身份。
- 明确告知模型,旧观察因 refs 失效而省略,当前控件应以最新快照为准。
- 保留原消息里的
[verified:或[UNVERIFIED行,让验证结果继续可见。
失败的工具结果不参与这种压缩。尤其是部分执行失败的批量操作,其完整回执会留下,因为失败细节正是下一步判断需要的内容。
代价也很明确:旧控件树从模型输入里消失后,模型就不能再检查它。如果最新页面与记忆中的操作冲突,应该重新读取页面。512 bytes 的回执加验证行只能充当简短凭据,完整审计记录仍在存储的对话中,模型在当前轮次看不到。
页面事实怎么留下,又不带回过期按钮
删除旧页面树还有另一个风险:价格、确认编号、回答所需的段落也可能一起消失。作者为此在 src/core/browser-progress.mjs 中单独保存页面证据。
captureBrowserEvidence 只接收原文引文,不接收模型总结出来的事实或提炼出的结论。保存前,引文文本、来源 URL、观察 ID 必须与浏览器实际返回的上下文块匹配,再通过 URL 与文本的 SHA-256 哈希去重。
容量限制也写得很具体:总计 16 KB、最多 16 条、每块最多 2 KB。过长引文会被拒绝,并给模型可见的提示;请求预算不足时,则整条省略并提示,不会截出半句话,伪装成完整证据。
保留的引文会附加到最新工具消息下,明确标记为「已观察证据、不可信页面数据、不含可操作 refs」。模型可以引用这些文本,但不能把它们当成点击目标。
事实需要跨页面保留,操作权限需要跟着当前页面更新。
作者还通过 recordBrowserProgress 判断任务有没有推进。页面指纹由标签页、URL、文档 ID、控件和上下文构成,忽略临时 refs 与观察 ID。因此,仅仅给控件重新编号,不会被算成页面发生变化。
页面持续不变时,无进展计数器会递增,随后给出引导:读回操作结果、换一种方法,或者由用户接管,不要重复可能造成再次提交的动作。原文没有给出触发引导的具体阈值,也没有展示这些引导能否稳定阻止重复操作。
我的判断
我认同作者把证据和操作目标拆开的方向。浏览器页面会变化,把旧控件树长期放在模型面前,却期待模型始终记清引用的有效期,对上下文判断能力要求太高。直接缩小可见的操作范围,是一个清楚、容易理解的工程取舍。
但效果还只能按作者的本地验证来评估。src/core/config.mjs 中的 browserHistoryCompaction 默认是 false,需要通过 BROWSER_HISTORY_COMPACTION 开启。作者报告,browser-progress 和 browser-projection-status 两组测试合计六项通过;配置注释同时承认,混合页面的证据保留仍需真实运行中的对照验证。
因此,「消灭整个错误类别」更接近作者对设计效果的判断,原文没有给出长期浏览会话数据或错误率对照。删除旧观察可以减少模型选择过期 ref 的机会,但它也不等于给提交操作加上了幂等保护。
另一条路线是保留带版本号、只读且明确标为过期的快照。它有利于回查操作依据,却也把辨别快照状态的负担重新交给模型。两者之间,我更倾向于默认只暴露最新操作目标,再按需提供历史证据查询。不过,这个组合并不是原文已经验证过的实现。
能拿来做什么
- 给浏览器 AI 助手整理上下文时,可以保留完整存档,再单独生成本次模型输入视图,同时保住工具调用的 ID 和配对关系。
- 处理表单、付款确认或其他跨页流程时,可以检查旧快照是否仍携带可操作 refs,并在页面没有推进时引导助手先核对结果,避免反复执行同一动作。
- 保存跨页资料时,可以借鉴「原文、URL、观察 ID」的校验方式,同时明确区分页面数据与指令,预算不足时整条省略证据并提示。
- 如果准备启用类似压缩策略,可以重点验证混合页面证据、部分批次失败和重复提交场景;作者的六项测试通过记录,还不足以替代这些真实流程验证。