3.3 渐进式固化:从 Agent 调用到选择器代码


3.3 渐进式固化:从 Agent 调用到选择器代码

本节摘要:本节给出 Stagehand 路线的核心工作流:让系统随运行时间自己变便宜。固化四步法:第零步全用 act 起步(先跑通再优化);第一步 observe 检视高频指令的候选(收集固化资格证据);第二步把 observe 产物按指令键缓存落盘,运行时先查缓存、命中则直接执行(跳过模型裁决);第三步对长期稳定的指令换成 Playwright 原生调用(零模型成本,进入普通代码维护节奏)。缓存机制展开讲两件事:observe 结果怎么变成缓存(含完整代码),以及失效策略——命中失败立即回退 act 并刷新缓存,保证韧性来自兜底而非运气。收益度量给三指标(模型调用次数、耗时、成功率)与「固化覆盖率」的定义。最后是不固化清单:A/B 页面、动态内容、低频长尾路径——把适应性优化掉是本路线唯一的翻车方式。

学习目标

  • 按四步法把一段 act 流程逐步固化到纯代码。
  • 实现带失效回退的 observe 缓存,说出缓存键的选择依据。
  • 用三指标度量固化收益,定义固化覆盖率。
  • 对照不固化清单,判断某步骤是否具备固化资格。

一、固化四步法

第 3.1 节判定树说过:判定的证据是运行轨迹。固化四步法就是把取证与改造流程化:

第 0 步 全 act 起步 第 1 步 observe 检视 第 2 步 缓存命中 第 3 步 换纯代码 ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ 所有步骤先用 │ ───▶ │ 高频指令跑 │ ───▶ │ 运行时先查 │ ───▶ │ 长期稳定的 │ │ act 指令跑通 │ │ observe,看 │ │ 缓存,命中即 │ │ 指令改写为 │ │ (先跑通) │ │ 候选是否稳定 │ │ 执行,未命中 │ │ Playwright │ │ │ │ (收证据) │ │ 走 act 并刷新 │ │ 原生调用 │ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ 每步 1 次模型调用 一次性检视成本 大部分执行 0 调用 该步骤 0 调用 ​

四步的节奏建议:第 0 到第 1 步在开发期完成(几轮干跑即可);第 2 步在上线时启用;第 3 步按月度回顾,从缓存命中日志里挑「命中率最高且从未失效」的指令动刀。核心原则一句话:每一步都保留回退到上一步的能力——缓存未命中回退 act(第二节),纯代码失败也回退 act(保险层)——系统的确定性来自固化,韧性来自兜底,两者不交换。

顺带回答一个常见疑问:为什么第 0 步从全 act 起步,而不是直接照着页面写代码?因为「哪些步骤值得固化」这个信息本身要靠运行来产生——先跑通、再观察、后固化,每一步的改造都有证据支撑;反过来先验地写一整套代码,就回到了第 0.1 节确定性脚本的老路(凭一次观察写死全部路径)。这就是「渐进式」三个字的含义:固化的顺序跟着证据走,而不是跟着猜测走。

二、observe 结果如何变成缓存

observe 的候选携带定位信息(方法、目标描述等,形态以官方文档为准),这让它可以脱离模型被代码复用。缓存实现三件套:键用指令文本(同一指令对应同一目标);值是选中的候选对象;失效策略是命中后执行失败即删除该键并走 act 重新裁决、写回新值:

// cache_observe.mjs —— observe 结果缓存与失效回退(写法示意,以官方文档为准) import { existsSync, readFileSync, writeFileSync } from "node:fs"; import { Stagehand } from "@browserbasehq/stagehand"; const CACHE_FILE = "observe_cache.json"; /** 读取缓存文件 @returns {Record<string, object>} 指令到候选的映射 */ function loadCache() { if (existsSync(CACHE_FILE)) { return JSON.parse(readFileSync(CACHE_FILE, "utf8")); } return {}; } /** 写回缓存文件 @param {Record<string, object>} cache 缓存映射 */ function saveCache(cache) { writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2), "utf8"); } /** 带缓存的 act:命中则直接执行,未命中或执行失败则走模型并刷新 @returns {Promise} 成功 resolve 为 true,失败为 false */ async function cachedAct(page, instruction) { const cache = loadCache(); try { if (cache[instruction]) { await page.act(cache[instruction]); return true; } } catch (err) { console.warn("缓存失效,回退模型裁决:", err.message); delete cache[instruction]; } const candidates = await page.observe(instruction); if (candidates.length === 0) { console.warn("observe 无候选:", instruction); return false; } cache[instruction] = candidates[0]; saveCache(cache); await page.act(candidates[0]); return true; } const stagehand = new Stagehand({ env: "LOCAL", modelName: "gpt-4o" }); await stagehand.init(); const page = stagehand.page; await page.goto("http://127.0.0.1:8080/demo_todo.html"); await cachedAct(page, "点击「添加」按钮"); // 首次走模型并落缓存 await cachedAct(page, "点击「添加」按钮"); // 第二次起命中缓存,零模型调用 await stagehand.close(); ​

三处工程细节:键的选择——指令文本做键要求团队对指令有命名纪律(同一意图别写两种句式),进阶做法是给指令起稳定的业务名(add-todo)做键、指令文本做值,避免措辞漂移让缓存失联;缓存是部署物——像配置一样纳入版本管理(提交到仓库或配置中心),而不是运行环境的临时文件,这样新环境冷启动也能享受固化成果;命中失败必须回退——这是韧性的来源,页面改版导致候选定位失效时,系统自动退回模型裁决并学会新的定位,相当于给固化层装了自愈。

缓存治理还有两条团队纪律值得写进规范:其一,缓存要有主人——每条缓存关联到具体流程与责任人,页面改版时才知道通知谁刷新;其二,失效率是健康指标——统计每条缓存的命中与失效次数,失效率持续走高的缓存说明对应页面在漂移,要么升级该步骤的语义层写法,要么承认它不该固化(对照第四节清单)。把这两条接进监控(第 11 章),固化层就从「能跑」变成「可运维」。

三、收益度量:三指标与固化覆盖率

固化不是信仰活动,要拿数字说话。三个基础指标,按周采样对比:

指标 定义 固化的预期效果
模型调用次数 每百次任务的模型调用总数 斜率持续下降
端到端耗时 任务从发起到完成的时长分布 中位数下降,长尾变短
成功率 任务成功数除以任务总数 不降(降了说明固化过头,见第四节)

在三者之上定义固化覆盖率:固化步骤的执行次数除以总步骤执行次数。经验曲线:上线首周覆盖率接近零(全 act 跑证据),一个月后常见六到九成——具体数字取决于页面稳定度,不必攀比。第 11 章生产化会把覆盖率接进监控面板,作为「系统在变便宜还是变贵」的体征指标。

一个容易忽略的联动:固化覆盖率上升后,第 2.2 节的信任边界要重新评审——纯代码路径上的提交类动作不再经过模型,确认门的挂点从「模型动作」移到「代码函数」,闸门逻辑不变,挂载位置变了。

四、什么时候不要固化

固化把适应性换成确定性,有些步骤的适应性恰恰是价值所在:

不固化对象 特征 处理
A/B 实验页面 同一指令在不同变体间摆动 保持 act,让模型适应变体
动态内容区 列表、推荐流每次不同 保持 act 或 extract 指令
低频长尾路径 一年跑不了几次,缓存收益薄 保持 act,固化成本收不回
会变动的第三方页面 不由你控制的外部站点 保持 act 加验证(第 6 章),别用缓存锁死别人的页面

判定口诀:固化的前提是「你拥有这个稳定」——自家页面的稳定是承诺,别人页面的稳定是错觉。对第三方页面,缓存命中的定位随时可能指向被改掉的元素,命中失败回退机制虽能兜住,但反复失效的缓存本身就该清理。第 8 章会从合规角度再加一条:对他人站点的自动化要克制,固化成高频代码路径等于把访问压力焊死,速率礼仪(第 8 章)优先于固化收益。

还有一条团队层面的隐性收益值得记录:固化过程产出的缓存与覆盖率数据,本身就是页面变更的探测器——某段缓存失效率突然升高,往往意味着对应页面刚经历改版。把「失效率异动」接入告警(第 11 章),固化层就从成本优化资产兼职成了变更监测资产,一份维护两份回报。

本节要点回顾

  • 固化四步法:全 act 起步、observe 检视、缓存命中、换纯代码;每步保留回退能力;固化顺序跟着证据走。
  • 缓存三件套:指令做键、候选做值、失效即回退刷新;缓存是部署物,纳入版本管理,还要有主人与失效率监控。
  • 收益三指标(模型调用、耗时、成功率)加固化覆盖率;成功率下降是固化过头的警报。
  • 不固化清单:A/B 页面、动态内容、低频长尾、他人页面——固化的前提是你拥有这个稳定。
  • 失效率异动可兼作页面变更探测:一份缓存,两份回报(成本优化与变更监测)。

到本章为止,前三种看页面的方式里已经讲了两种(DOM 原语与其代码优先变体)。下一章换眼睛:不读 DOM、不看截图,读浏览器为无障碍准备的那棵语义树——结构化快照流派,以及把这个能力交给编码 Agent 的两个官方 MCP 服务器。

作者与出处
原作者:灏天文库
整理人: 灏天文库整理
本站整理收录,版权归原作者/开源协议所有;欢迎通过原文链接访问源仓库。
发布者: 作者: 灏天文库 转发
评论区 (0)
U