网页自动化最脆弱的部分,常常不是“会不会点击”,而是:页面变化以后,模型还在不在操作它刚才看到的那个元素。
固定 selector 可以应付稳定页面;但动态 id、延迟渲染、列表重排和 iframe 会让“第几个按钮”迅速失效。agent-browser 的核心答案是 accessibility snapshot 与短元素引用:先让 Agent 看到语义树,再用引用操作;页面变化后重新快照,不继续相信旧引用。
这次我没有用公开网站做演示,而是建了一个隔离 fixture,故意让 id 和 DOM 顺序变化,再用 agent-browser 0.33.1 与 @playwright/cli 0.1.17 跑完全相同的任务。
| 项目 | 说明 |
|---|---|
| 内容类型 | 原理拆解、本地运行时审计与同任务对照 |
| 适合读者 | 需要让 Codex 操作动态网页,或正在选择浏览器 CLI 的开发者 |
| 阅读 / 跟做时间 | 约 10 分钟 / 20–30 分钟 |
| 前置条件 | Node.js、本机 Chrome,以及可安装项目级 CLI 依赖的隔离目录 |
| agent-browser runtime | npm 0.33.1,lockfile 固定 integrity |
| 上游源码审计 | vercel-labs/agent-browser@da1237e,审计时仓库版本为 0.33.2 |
| Playwright CLI | 0.1.17 |
| 浏览器 | 本机 Chrome,无远程 Provider |
| fixture | 动态 id 表单、延迟重排文章列表、响应式页面 |
| 视口 | 1440×900、390×844 |
| 核对日期 | 2026-08-01 |
| 结果边界 | 单机隔离页面,不代表公开网站、登录态或长任务性能 |
版本说明
源码审计和实际 runtime 相差一个 patch:文章谈结构时引用固定 Git commit,谈命令与结果时只引用
0.33.1实际输出。CLI 自带的agent-browser skills get core才是本轮运行命令的直接说明。
一分钟概览
agent-browser的 Skill 是 51 行发现入口,完整工作流由已安装 CLI 按版本返回。- accessibility snapshot 用角色、名称和状态描述页面;引用是本次快照中的短句柄,不是永久 selector。
- 页面导航、重排或重渲染后必须重新快照,旧引用应视为失效。
- 本 fixture 中两套 CLI 都以 19 条命令、0 重试、0 定位失败完成。
agent-browser输出约 4.2 KB,Playwright CLI 约 8.4 KB;本机耗时不能直接解释成工具性能。
1. Skill、CLI 和浏览器是三层
本轮开始时,本机已经有 agent-browser Skill 目录,但 PATH 找不到 agent-browser。这意味着 Codex 能发现说明,却不能执行说明里的命令。
当前上游故意把 SKILL.md 做成 discovery stub:
SKILL.md
→ agent-browser skills get core
→ 与 CLI 版本匹配的 workflow/reference
→ agent-browser native CLI
→ Chrome / Chromium via CDP
这种设计解决了“复制一份很快过时的命令大全”,但也把 CLI 变成了必要运行时。我的项目固定安装:
{
"dependencies": {
"agent-browser": "0.33.1",
"@playwright/cli": "0.1.17"
}
}
安装以后,我先跑两类检查:
agent-browser --version
agent-browser doctor --offline --quick
agent-browser skills get core
只有 version、Chrome 探测和 core Skill 都成功,才进入网页任务。
2. accessibility snapshot 到底改变了什么
浏览器 DOM 里可能是:
<input id="name-77b8..." name="name">
下一次请求 id 会完全不同。但它的 label 仍是“姓名”,accessibility tree 仍会把它暴露为名为“姓名”的 textbox。
语义快照更接近:
textbox "姓名" [ref=e7]
textbox "标签" [ref=e8]
button "保存登记" [ref=e6]
Agent 不需要记住随机 id,只需完成:
snapshot
→ 找到 role=textbox, name=姓名
→ fill ref
→ 找到 button, name=保存登记
→ click ref
→ 页面变化,重新 snapshot
元素引用不是 selector 的永久替代品。agent-browser 的 core Skill 明确说明 refs 在页面变化后会 stale。可靠性来自“语义定位 + 重新观察”,不是来自某个 e7 永远正确。
本轮实际输出还提醒了一个实现细节:agent-browser 0.33.1 快照显示 [ref=e7],命令使用 @e7;Playwright CLI 导航后会出现 f1e7 这类带 frame 前缀的 ref。实验脚本不能假设两种工具的 ref 语法完全一致。
3. 三个 fixture 为什么这样设计
动态 ID 表单
每次 HTTP 请求用 crypto.randomUUID() 生成新的 input id,但保留 <label for>。任务要求填写“林檎”和“人工智能”,提交后验证状态文本。
它用来排除硬编码 id 带来的假稳定。
DOM 顺序变化后的文章导航
文章页在 350ms 后重新排序。Agent 必须等待、重新快照、搜索“稳定”,再打开标题为“让 Agent 稳定完成网页任务”的文章。
它用来检查工具是否依赖“第二条结果”或旧引用。
响应式截图和控制台
同一页面在 1440×900 与 390×844 下截图,并查询 console error。截图是交付证据,console 负责捕捉页面表面正常但运行时出错的情况。
agent-browser 在 1440×900 下生成的本地 fixture 截图,三层验收卡片完整呈现
图 1:桌面视口。页面本身很简单,重点是截图命令、视口和控制台都进入同一验收记录。
agent-browser 在 390×844 下生成的本地 fixture 截图,卡片切换为单列
图 2:移动视口。fixture 不依赖公开网络,两个 CLI 面对的是同一份 HTML、CSS 和脚本。
4. 对照结果
固定 Prompt 要求两套 CLI 都在页面变化后重新快照,不使用随机 id,失败最多重试一次。
| 指标 | agent-browser 0.33.1 | Playwright CLI 0.1.17 |
|---|---|---|
| 命令数 | 19 | 19 |
| 重试 | 0 | 0 |
| 定位失败 | 0 | 0 |
| 命令输出体积 | 4,190 bytes | 8,363 bytes |
| 总耗时 | 31,575 ms | 6,051 ms |
| 三项验收 | 通过 | 通过 |
这里最容易误读的是耗时。
agent-browser 的总计包含本机 Windows daemon/close 路径,连续多轮实验都出现约 30 秒级尾部等待;Playwright CLI 在本机关闭更快。页面操作本身没有独立 trace,因此不能把 31.6 秒写成“agent-browser 慢 5 倍”。更可靠的结论只有:
- 两者命令数相同;
- 两者没有重试和定位失败;
agent-browser的可见命令输出约为对照的一半;- 本机 lifecycle 差异需要单独 profile。
这也是为什么文章只报告案例范围内的数值,不做普遍排名。
5. 持久会话解决什么,不解决什么
agent-browser 支持:
--session:隔离不同浏览器实例;--restore/--session-name:保存 cookies 与 localStorage;--profile:复用自定义或现有 Chrome profile;- state file 与 auth vault;
- CDP 连接和云 Provider。
它们能减少重复登录,却也扩大安全边界。仓库 README 明确提醒 state 文件可能含明文 session token;CDP 端口意味着本机进程可以控制浏览器。
我的默认策略是:
公开 fixture → 临时 session,不持久化
测试账号 → 项目专用 profile,加入 .gitignore
真实登录态 → 最小域名、最小权限、短生命周期
现有 Chrome → 只有任务明确依赖登录态时才连接
“会保存会话”不是可靠性本身。可靠性还需要知道会话何时过期、状态放在哪里,以及出错后是否应该重登而不是反复点击。
6. 本地 browser-controller 为什么需要更新
本机 browser-controller Skill 给出的路由思路仍有价值:Playwright 做默认测试,agent-browser 做语义交互,Browser Use 处理登录态,Chrome DevTools 处理深度调试。
但源码审计发现三类漂移:
- 路径仍指向
~/.claude/skills,实际 Skill 安装在.codex/skills。 - 它把 Playwright 写成“总是可用”,而本轮开始时
playwright-cli并不在 PATH。 - 当前
agent-browser已支持 network、profile、CDP、console、a11y、React introspection 等更多能力,旧矩阵把它描述得过窄。
这说明路由 Skill 也需要版本和回归检查。一个“负责选择工具”的 Skill 如果能力表过时,会在任务真正开始前就选错路。
7. 四种工具怎样分工
| 场景 | 优先工具 | 原因 |
|---|---|---|
| 动态页面、紧凑语义交互 | agent-browser | snapshot/ref 流程直接,CLI 输出紧凑 |
| 通用 E2E、现有 Playwright 工程 | Playwright CLI | 测试生态、调试与代码接入自然 |
| 已登录网页、需要用户现有状态 | Browser Use / Chrome 控制 | 重点是状态复用,不只是 selector |
| 性能 trace、network packet、深层调试 | Chrome DevTools | 更适合诊断底层浏览器行为 |
这不是固定优先级。真实项目还要看:宿主已有哪些工具、是否允许连接用户浏览器、是否需要生成可维护的测试代码,以及凭据边界。
8. 一套更稳的网页操作习惯
打开目标 URL
→ 获取交互元素语义快照
→ 按 role + accessible name 选择引用
→ 执行动作
→ 等待具体文本、URL 或 load 状态
→ 页面变化后重新快照
→ 读取真实结果
→ 截图 + console + URL 三种证据收口
失败时先问:页面变了吗、旧 ref 是否失效、等待条件是否具体、元素是否被覆盖。不要第一时间换成更脆弱的 CSS,也不要盲目增加固定 sleep。
上一篇是 Superpowers:工程纪律与一次失败实验。下一篇用同样的“机制 + fixture + 边界”方法对照前端 Skill 三件套。本系列默认读者已经了解 Skill 的基本加载方式;需要补背景时,可从 Codex Skills 入门和进阶篇开始。