Agent 一接骨架屏页面就开始误判完成态:从 Readiness Signal 到 DOM Stabilization 的工程实战
浏览器 Agent 一进企业后台,最容易踩的坑往往不是页面太慢,而是页面看起来已经“加载好了”,实际仍停留在骨架屏、占位卡片和半成品 DOM。⚠️ 人类会等列表真实出现再点,Agent 如果只看到按钮可见、节点已挂载,就可能提前触发搜索、提交或翻页,直接把抖动放大成流程事故。
骨架屏最危险的地方,在于它提供了和真实页面极其相似的视觉信号。🧠 对执行器来说,标题、按钮和容器都已经存在,可真正决定动作是否安全的数据和事件绑定往往还没落稳。观察与动作就在这里脱节。🔍
[外链图片转存中…(img-SLJ051ra-1777778322218)]
骨架屏为什么会把完成态判断带偏
很多自动化框架默认把element visible、DOMContentLoaded或network idle当成可执行信号。📌 这在静态页里够用,但现代后台依赖异步数据和 hydration。列表骨架还没替换成真实行,按钮虽然能点,回调却还没注册完成,于是 Agent 会在“结构已到位、语义未就绪”的窗口里误出手。🧩
更棘手的是,这类问题常常难以复盘。🧪 回放截图里,页面最后看起来完全正常;日志里也能看到目标元素确实存在。真正缺失的是进入稳定态前的那几百毫秒:DOM、数据和可交互状态并不同步。没有显式的 readiness contract,模型就会反复把占位内容当成真实世界。🚨
一组 Readiness Signal 对比实验把问题看清
这次回放了58条真实后台任务,覆盖搜索、审批、工单跳转和报表筛选。📊 基线方案只检查目标节点可见;方案二加入network idle;方案三再补上骨架消失、关键数据行数达标和短暂 DOM 稳定窗口。提前点击的核心问题不是模型不会找按钮,而是执行器没有区分“页面可见”和“页面可用”。✅
| 方案 | 任务成功率 | 提前点击率 | 平均重试次数 | 误提交率 |
|---|---|---|---|---|
| 仅检查元素可见 | 61% | 19% | 3.6 | 11% |
元素可见 +network idle | 73% | 10% | 2.4 | 7% |
| 多信号就绪判定 | 91% | 2% | 1.2 | 1% |
真正有效的,不是等更久,而是等对信号。🛠️ 一旦把骨架节点、关键字段回填和 DOM 短稳窗口纳入门槛,许多“偶发”误操作就会立刻收敛,说明问题根本不在推理,而在执行前缺少页面语义校验。📈
defis_page_ready(snapshot):return(notsnapshot.has_skeletonandsnapshot.data_row_count>=snapshot.min_rowsandsnapshot.bound_actions_readyandsnapshot.dom_stable_ms>=300)defshould_execute(action,snapshot):returnis_page_ready(snapshot)andaction.targetinsnapshot.enabled_targets工程上真正该补的是 DOM Stabilization 契约
更稳的做法,是把“可以动作”定义成系统契约,而不是浏览器默认状态。🛡️ 每次观察后,执行器都要同时记录页面是否仍有骨架、关键数据是否回填、目标控件是否可交互,以及最近一段时间 DOM 是否持续抖动。只有这些条件同时满足,点击、输入和提交才允许放行。这样做的价值,是把等待变成可审计的工程规则。📦
另一层常被忽略的是回压与取消。⏱️ 页面长期停在骨架态时,系统不能无上限重试“再看一眼”,而要及时触发重载、降级路径或人工接管。笔者认为,未来真正稳定的浏览器 Agent 都会把 readiness、重试预算和失败回退收敛成同一条状态机,否则骨架屏会持续制造“元素在、结果错”的事故。⭐
未来 3 到 6 个月 页面自动化会更依赖语义就绪信号
一句话总结:骨架屏不会直接让 Agent 失明,它破坏的是完成态判断。📌 把Readiness Signal和DOM Stabilization做成显式契约后,系统才能分清“页面已出现”和“页面已可执行”这两个阶段。浏览器 Agent,会在点击前确认骨架是否消失、数据是否回填、DOM 是否稳定吗?
