开篇引导段:最近在 GitHub Trending 上刷到一个很有意思的项目:BetterWright。它把 Playwright 重新包装成了一个"为 AI Agent 而生"的浏览器——核心卖点不是"能自动化网页",而是"能在最少 token 消耗下自动化网页"。

一、项目概述

BetterWright 的定位是 the token-efficient browser for AI agents(面向 AI 智能体的 token 高效浏览器)。它解决的问题非常具体:当一个 LLM 驱动的 Agent 去操作浏览器时,观察(observe)这一步会无情地吃掉上下文窗口——原始 HTML、完整可访问性树、纯截图循环,每轮都要耗掉成千上万个 token,最后任务还没完成,上下文就先爆了。

它提供两种使用形态,但底层共享同一套持久会话、凭证保险库、网络策略与快照机制:

形态适用场景拿到什么
集成模式(Integrated)你的 Agent(Claude Code、Codex、Pi、任意 MCP 客户端、自己写的代码)把浏览当作大任务的一环一个 skill、一个 MCP server、或一个 JS API,让你自己的 Agent 逐步操控浏览器
独立 Agent 模式(Standalone)你把整个浏览器任务丢出去,只等一个答案betterwright exec "<task>"——BetterWright 自带的浏览器 Agent 循环完成驱动,你只拿回一个 JSON 结果

核心特性可以概括为:

  • 压缩快照:用 Playwright 的 mode: "ai" 可访问性树,并把 Agent 无法操作的内容剪掉,留下 [ref=eN] 标记直接给模型点击/填写。
  • Diff 模式:动作之后只返回"变了什么",而不是整页重发。
  • 单轮完成:只读任务一个模型回合就结束,代码直接返回 {finalAnswer},无需二次确认。
  • 持久会话:一个长生命周期浏览器,登录态跨回合、跨天、跨重启存活。
  • 策略沙箱:模型代码运行在沙箱里,拿不到文件、进程或网络路由 API。
  • 凭证保险库:AES-256-GCM 加密,表单自动填充但密钥绝不进入对话上下文。

二、技术原理

2.1 观察栈:把 token 成本压到最低

README 里有一张表清楚说明了它的观察机制,这也是整个项目的"技术心脏":

机制Token 效果
压缩 Agent 快照Playwright mode:"ai" 可访问性树,剪掉 Agent 不能操作的部分——/url 属性行、非可操作角色的 ref、裸 generic 包装、重复文本、超过 100 字符的名字——只留 [ref=eN] 标记
Diff 模式动作后只返回变化部分,而非整页
仅交互过滤丢弃静态文本节点,只保留可点击/可填/可读的内容
作用域截断给出"下一步去哪看"的提示,而非静默截断的一堵墙
单轮完成只读任务一个模型回合结束,循环直接退出
持久会话一次登录、一次导航,不重复支付 token 成本
子 Agent 委派betterwright exec 把整个浏览转录留在子 Agent 内部,主 Agent 只花一次工具调用

2.2 架构:一个长寿命 Node Worker 持有浏览器

根据项目说明,CLI(或你的 JS 宿主)拥有一个长寿命的 Node worker。worker 持有持久浏览器上下文,向模型代码暴露沙箱化的全局变量(page、snapshot、screenshot、human、credentials 等);它会回调宿主来授权请求、解析凭证,但绝不让密钥出现在返回结果里。CDP 句柄和原始浏览器句柄始终留在 worker 内部。

从 package.json 可以看到它的模块导出结构非常清晰:

{
  ".": { "import": "./dist/src/index.js" },
  "./agent": { "import": "./dist/src/agent.js" },
  "./auth": { "./dist/src/auth.js" },
  "./policy": { "./dist/src/policy.js" },
  "./vault": { "./dist/src/vault.js" },
  "./mcp-server": { "./dist/src/mcp-server.js" },
  "./skills": { "./dist/src/skills.js" }
}

依赖极简——核心只依赖 playwright-core 和 tldts,可选 patchright-core,说明它把注意力放在了"浏览器 + 策略层"本身,而不是堆砌依赖。

2.3 为什么不是直接 Playwright?

Playwright 是为测试而生的:可信脚本、已知选择器、跑完即销毁。而 Agent 恰恰相反——不可信的模型输出根据所见决定下一步,且浏览器下一回合还得在。BetterWright 在关键维度上做了差异化:

维度PlaywrightBetterWright
观察原始可访问性树或自拼 HTML压缩、可 diff、脱敏的快照,为上下文窗口计价
会话每脚本一个浏览器一个受管持久浏览器,登录态跨回合存活
信任完整 API 访问模型代码沙箱化,无文件/进程/网络路由 API
网络任意 URL每次请求策略检查(DNS 重绑定防护),云元数据端点始终阻断
密钥写在脚本里AES-256-GCM 保险库,表单检测后自动填充且密钥不入对话
证据断言screenshot({kind:'proof'}) 带标签的产物,Agent 可引用为完成证据
验证码不在范围本地 captcha.solve() 支持勾选框/Turnstile/滑块

三、安装与快速开始

环境要求:Node.js 22+(实际 engines 字段要求 >=22.18.0)。安装时不会作为 npm 生命周期副作用下载任何东西,因此安装行为可预测,可加 --ignore-scripts。

npm install -g betterwright
betterwright init      # 引导式:下载浏览器 + 接线 Agent + 真实加载一个页面

init 是安全的,可重复运行,并报告哪些步骤已经完成。更细粒度的命令也都可以单独跑:

betterwright setup     # 为当前主机安装受管浏览器
betterwright update    # 刷新受管浏览器
betterwright doctor    # 列出已装/缺失以及如何修复

验证安装是否可用,最快的一行:

betterwright run -c "await page.goto('https://example.com'); return page.title()"
# {"ok": true, "result": "Example Domain", ...}

四、使用方法与实战

4.1 集成模式:让你的 Agent 操控浏览器

任何能跑 shell 命令的 Agent 都能驱动它。init 会自动探测你机器上的 Agent 宿主并接好:

betterwright init
# 或者手动逐个宿主安装:
betterwright skill --install       # ~/.claude/skills + ~/.agents/skills
betterwright skill --install --all # 还装上 ~/.cursor/skills
betterwright skill --status        # 检查是否到位、是否最新

或者以 MCP 方式接入(stdio server,提供 browser / browser_login / browser_download / browser_handoff / browser_doctor 工具):

npm install -g betterwright @modelcontextprotocol/sdk
claude mcp add betterwright -- npx betterwright mcp
betterwright mcp --check

从你自己代码驱动也很直接:

import { BetterWright } from "betterwright";

const bw = new BetterWright();
await bw.run("await page.goto('http://localhost:5173')", { session: "dev" });
const title = await bw.run("return page.title()", { session: "dev" });
console.log(title.result);
await bw.close();

run() 接收一个包含沙箱化全局变量的 Playwright 异步 JS 字符串,返回一个结果信封。

4.2 独立 Agent 模式:把任务整体丢出去

BetterWright 自带一个浏览器调优的 Agent 循环。接好模型,用自然语言给任务:

betterwright auth --login codex     # OAuth 登录,无需粘贴 API key
betterwright exec "find the top Hacker News story and give me its title and points" --model gpt-5.6-sol

循环会观察压缩快照、执行、验证、截图存证,最后打印一个 JSON 对象——答案、步骤、token 用量、证据路径。

把 exec 当作子 Agent 用是它最巧妙的设计:因为它是"一条 shell 命令进、一个 JSON 对象出",所以一个编码 Agent 可以把整段浏览器任务委派给它。一个 30 回合的结账流程,主 Agent 只花一次工具调用,而不是 30 页上下文。

4.3 模型选择:用真实 id,而非昵称

模型通过真实 id 选择,而不是适配器昵称。直接传你想要的模型 id(gpt-5.6-sol、claude-opus-4-8、qwen3:8b …)。BetterWright 会探测本机运行的 Ollama/vLLM、有 key 时的 OpenRouter,以及原生 Claude/Codex/Grok 路由;若恰好只有一个来源暴露该 id 就用它:

betterwright models                      # 查看可用模型
betterwright exec "check example.com" --model ollama/qwen3:8b
OPENROUTER_API_KEY=… betterwright exec "check example.com" --model anthropic/claude-sonnet-5

4.4 人介入:看、教、接手

每次运行都可以挂一个自托管的 live view:实时展示浏览器,带聊天在回合间引导 Agent,以及 handoff 流程处理不该全自动完成的时刻——MFA、顽固验证码、重大点击。Agent 暂停,你接手,点 Done,它带着你的备注继续。

五、常见问题与解决方案

Q:init 卡在下载浏览器? A:安装不会作为 npm 副作用下载浏览器,需要 betterwright setup 或 init 显式拉取受管 BetterChromium。GPU 缺失的 Linux 会用 SwiftShader 软件渲染器运行。

Q:Agent 客户端看不到 MCP 工具? A:先跑 betterwright mcp --check 诊断;确认已用 claude mcp add betterwright -- npx betterwright mcp 注册,且 @modelcontextprotocol/sdk 已安装。

Q:网络策略阻断了合法请求? A:网络策略对每次导航、子资源、WebSocket、原始 TCP 都做检查,且云元数据端点始终阻断(防 SSRF/DNS 重绑定)。若是误杀,需要检查策略配置(见 docs/network-policy.md),而非绕过。

Q:Agent 生成的密码把自己锁在外面了? A:保险库只把登录填充给模型代码、绝不给明文密钥,所以提供了单独的人类专用门:betterwright vault copy <id>(复制到剪贴板)、vault show <id> --reveal(只打到终端,重定向会失败关闭)。这些命令在 owner-only API 上,浏览器 worker 和模型代码片段都够不到。

Q:想换身份 / 并行多账号? A:--session <name> 是同一浏览器内的并行通道(共享 cookie jar,同一身份);--profile <name> 是独立身份(自己的 cookie jar、自己的会话守护进程、自己的 exec 历史),两者可同时运行且都保持登录。

Q:token 还是很高? A:打开快照的 Diff / 仅交互过滤;只读任务确保走单轮完成;把整段任务用 betterwright exec 委派成子 Agent,把浏览转录挡在主 Agent 上下文之外。

六、总结

BetterWright 真正有意思的地方,不是"又一个 Playwright 封装",而是它把 token 经济性当成了浏览器自动化的第一性原则来设计:压缩快照、Diff、单轮完成、持久会话、子 Agent 委派,每一环都在帮 Agent 省上下文。再加上策略沙箱、AES-256-GCM 凭证保险库、网络策略、验证码助手和 live-view 接手机制,它把"让不可信的模型安全地开一个浏览器"这件原本很危险的事,框进了一套可审计的边界里。

如果你正在给自己的 Agent 接浏览器能力,又苦于上下文被 HTML 灌爆、登录态反复丢失、密钥满天飞,BetterWright 值得一试。它基于 MIT 协议开源,欢迎 fork、修改、集成与商用。

项目地址:github.com/BetterWright/betterwright