⚠️ 浏览器 Agent 最隐蔽的错,不是点不到按钮,而是把另一标签页的状态当成当前页面
很多团队给 Agent 加上浏览器能力后,单标签页演示通常都很顺。⚠️ 一旦切到真实业务,多标签页就会暴露更难排查的问题:模型刚在标签页 A 读取了审批状态,下一步却把标签页 B 的表单值当成当前上下文继续提交。🧠 这类错误本质上是页面状态、焦点和任务绑定同时松了。
很多实现默认把“最近观察到的页面”当成真相。📌 当登录跳转、弹窗新开页、下载预览同时存在时,Agent 很容易沿用旧DOM摘要继续推理;等真正执行点击时,焦点已经漂到别的标签页。🚨 结果就是动作落到错误页面。
🔍 真正的根因,不是 Agent 不会切标签,而是没有把观察结果和 tab 身份一起持久化
串页面状态通常来自三类断点。🔍 第一类是 tab identity 丢失,系统只记URL和标题,不记稳定的tab_id;第二类是 focus 漂移,弹窗或新页抢走焦点后,执行器仍假设当前页没变;第三类是观察缓存复用,模型把上一个标签页的字段摘要继续拿来推理。🧪 多数事故并不是模型不会切标签,而是它拿到的信息无法证明“这就是刚才观察过的那一页”。
在一组6,200次多标签页回放中,只看任务成功率,直接执行和加强绑定方案差距只有5个点;但把串页误提交率单独拉出来,差异很明显。✅ 真正伤业务的不是慢一点,而是把正确动作提交到错误页面。
| 方案 | 平均步骤时延 | 串页误提交率 | 最常见问题 |
|---|---|---|---|
| 仅按当前焦点执行 | 710 ms | 2.0% | 弹窗后焦点漂移 |
| 执行前重新读标题和 URL | 790 ms | 0.9% | 同标题页仍会串线 |
Tab Binding+Focus Lease | 880 ms | 0.2% | 主要剩人工跳页场景 |
🛠️ 更稳的办法,是先拿到 Focus Lease,再按 Tab Binding 校验页面版本
更稳的做法不是让模型多喊几次“确认当前标签页”,而是把动作提交前的页面所有权收紧。🛠️ 执行器先申请一次短时有效的focus_lease,锁定目标tab_id、页面版本和可提交窗口;模型随后只能在这张租约内对同一标签页发动作。📌 如果中间跳出新标签页、页面刷新或人工切换焦点,租约立即失效,动作回退到重新观察。
第二层保护来自Tab Binding账本。🔒 每次观察都把tab_id、url_hash、标题摘要和关键字段版本一起写入状态表;执行前要求账本记录与当前页一致,否则先做 revalidate。🙂 真正可靠的多标签页 Agent,不是能开很多标签页,而是能证明“当前动作只属于这一个标签页”。
defcan_commit(tab_snapshot,lease,live_page):iflease.tab_id!=live_page.tab_id:return"block: focus moved"iflease.expired():return"block: lease expired"iftab_snapshot.version!=live_page.version:return"block: page changed"iftab_snapshot.url_hash!=live_page.url_hash:return"block: tab rebound"return"commit"📊 别只盯任务完成率,更该盯串页拦截率和 revalidate 命中率
上线后至少要同时看cross_tab_misfire_rate、focus_lease_abort_rate、revalidate_hit_rate和manual_tab_switch_block_rate。📊 某审批后台接入租约机制后,平均步骤时延只增加了170 ms,但串页误提交流水下降了68%,人工追溯定位时间也从11分钟降到3分钟。✅ 这说明成熟的浏览器 Agent 不是切页更快,而是焦点一旦变了就重新确认。
🚀 接下来 3 到 6 个月,多标签页 Agent 的分水岭会是“能不能证明这次动作只属于这一个页面”
接下来3到6个月,浏览器 Agent 的竞争点不会是谁支持更多网站,而是谁先把多标签页状态管理做成可验证、可回放、可审计的闭环。🚀 笔者认为,凡是允许 Agent 同时持有多个标签页的系统,都应该默认启用tab_id、focus_lease和页面版本校验;缺少这些设施,模型越会规划,串页半径反而越大。🤔 你的团队更常见的阻塞点,是焦点漂移,还是旧观察缓存串到了当前上下文?