Show HN:面向 AI Agent 的验证浏览器——13 毫秒创建窗口,单次调用完成检查
英文标题:🔶 Show HN: A verification browser for AI agents – 13ms windows, one-call checks
Hwatu 是一款面向 AI Agent 的 Linux/WebKitGTK 验证浏览器,主打以单次命令完成页面加载、执行、截图和验证,并将窗口创建时间降至约 13 毫秒。项目宣称,其 `hwatu check` 的完整验证中位耗时约为 35 毫秒,而以常驻服务方式运行的 Playwright 同类流程约为 341 毫秒。Hwatu 内置像素差异评分、动画量化、控制台与网络错误检查、CAPTCHA 检测及人工接管等能力,旨在让 Agent 基于可验证证据而非截图猜测来完成前端任务。对开发者而言,它提供 MCP、CLI 和 Unix Socket 协议接入,并支持在无头执行与人工可见会话之间实时切换。
122.6 KB
中文
给你的 Agent 装上真正的眼睛,让其执行循环立即提速
- 别再让 Agent 声称“像素级完美”。让它证明达到 97.49%。
- 别再为每次页面检查支付 5 次工具调用。
hwatu check仅需一次调用,约 35 毫秒,比常驻服务器上的 Playwright 快约 9 倍。 - 别再让浏览器窗口抢走你的注意力。默认无头运行,你可以继续输入。
- 别再交付 170 MB 的 Chromium。一个静态二进制文件,加上发行版自带的 webkitgtk。
- 愿景:持久化产品原则、原生平台策略、群体协作模型
- Agent 指南:协议、原语与验证循环
- 人工用户指南:平铺窗口管理器中的浏览器一侧
- 基准测试:每一项数据、测量方式与方法论
- 路线图:正式计划、优先级与非目标
- 持续改进:激活指标、反馈循环与每周节奏
- 发布工具包:可复用文案、渠道和衡量计划
安装 → 检测工作流 → 连接 Agent → 验证页面 → 交接给人工
curl -fsSL https://raw.githubusercontent.com/hongnoul/hwatu/main/scripts/install.sh | bash
hwatu setup
一个静态二进制文件,加上发行版提供的 webkitgtk-6.0(安装程序会进行检查)。在 Arch 上:yay -S hwatu。从源代码构建:cargo build --release。
安装程序仅安装二进制文件。hwatu setup 会检测受支持的编码 Agent,并输出可用连接方式,但不会修改它们的配置。准备就绪后,请显式选择客户端:
hwatu doctor
hwatu setup --client claude --scope project --dry-run
hwatu setup --client claude --scope project
hwatu demo
- **安装:**下载两个二进制文件并检查 WebKitGTK。
- **检测:**查找 Claude Code、Cursor、Jcode 或通用 MCP 工作流。
- **连接:**使用 Jcode 原生 Socket、MCP 或 CLI 回退方案。
- **验证:**使用
doctor或demo运行无头渲染冒烟测试。 - **交接:**仅在需要人工参与时,才将同一个实时会话实体化显示出来。
配置可预览、幂等,并且可通过相同的客户端和作用域加上 --undo 撤销。项目作用域会创建可共享配置;用户作用域则保持为个人配置。Claude Code 会在下次启动时要求每位用户批准项目作用域的 MCP 服务器。
手动 MCP 配置仍然只需一个可移植条目:
{ "mcpServers": { "hwatu": { "command": "hwatu", "args": ["mcp"] } } }
或者完全跳过 MCP:每条命令都可以是一次简短 CLI 调用,或通过 Unix Socket 发送一行以换行符分隔的 JSON。
hwatu localhost:3000 # 像打开终端一样打开一个窗口
试过 hwatu 吗?一次成功检查、一次安装失败,以及一个缺失的工作流,都是有价值的信号。欢迎提交一份两分钟的使用报告,或报告 Bug。
告诉你的 Agent 使用 Hwatu
连接 Hwatu 后,其工具即可供 Agent 使用。项目指令会告诉 Agent 何时使用这些工具,以及哪些证据可被视为成功。请将以下内容添加至 AGENTS.md、CLAUDE.md、Cursor 规则文件,或你的执行框架中的等效文件:
## 前端验证
在前端修改后使用 Hwatu。执行受影响的用户旅程,并使用 `expect`
验证其预期的可见结果、导航结果或持久化结果。一次成功的点击或干净的控制台
并不能证明成功。在验证结果后,请检查 `console`
是否存在额外的 JavaScript 错误和请求失败。
然后让任务的证明标准具体化:
在 /settings 实现显示名称编辑功能。使用 Hwatu 输入“Test User”,
保存后验证可见的成功状态,重新加载页面,确认数据已持久化,
并报告所有控制台错误。
同一条指令适用于 Hwatu 的 MCP 工具、简短 CLI 命令或 Jcode 的原生集成。明确指出用户旅程,以及能够证明其正常工作的可观察结果。参见完整的 Agent 指南,其中包括更完整的可复制粘贴策略和验证循环。
- 像素差异评分:匹配百分比、差异区域与热力图(
diff) - 将动画转化为数值:时长、缓动和速度(
motion) - 确定性动画帧:将所有动画固定在时间点 t(
seek) - 以 JSON 表示页面状态,使用 token 而不是像素(
snapshot) - 带有结构化错误信息的真实输入事件(
click/type/scroll/upload) - JavaScript 错误、控制台输出和失败请求(
console) - 通过 JSON 行或 MCP 通知推送事件订阅(
watch) - 带轮询的单次调用页面断言(
expect) - 无头 / 后台 / 聚焦是_每个窗口_的属性,可实时切换
- 人工交接:
hwatu focus <id>会将实时会话放入你的平铺式窗口管理器 - CAPTCHA / 反机器人检测,以及结构化等待/恢复(
challenge) - MCP 服务器、普通 CLI 和单行 JSON Socket 协议
- 面向人工用户的极简 WebKit 浏览器:原生广告拦截、Vim 风格栏、崩溃恢复
为什么不用 Playwright 或 chrome-devtools-mcp?
给 Agent 提供浏览器有三种方式,其中两种并不擅长这件事:
| 运行方式 | Agent 循环的成本 |
|---|---|
| 冷启动库(Playwright,每项任务启动一次) | 引擎随脚本启动 |
| 常驻浏览器(你的 Chrome + devtools-mcp) | 一个完整的人工浏览器持续驻留 |
| hwatu | “最冷的常驻守护进程”:引擎保持热启动,其他一切均不存在 |
hwatu 仅保留让检查瞬间完成所需的部分:引擎、GPU 上下文、编译后的广告拦截规则,以及预热的 WebView;不会保留任何服务于坐在屏幕前人工用户的东西。因此,它可以在没有标签栏的情况下保持热闲置;即使以相同方式驱动一个保持热运行的 Playwright 服务器,其每个客户端仍需 341 毫秒,而 hwatu 仅需 39 毫秒(基准测试)。
第二个区别在于返回结果。Playwright 和 chrome-devtools-mcp 从根本上说是自动化 API:它们让 Agent_驱动_浏览器,再返回原始截图和 DOM,由 Agent 自行判断。
hwatu 不同。它是一款_验证型_浏览器:内置了测量原语,而浏览器本身是一个常驻热启动守护进程,其中创建一个窗口仅需 13 毫秒,无头状态是窗口属性,而非启动模式。
这就是为什么其循环如下所示:真实命令,真实输出:
hwatu --headless localhost:3000 # 它自己的窗口;你永远看不到它
hwatu --headless staging.example.com # 参考页面
hwatu diff --id 2 --other 1 --heatmap /tmp/heat.png
# {"match_percent":85.13,"regions":[{"x":0,"y":160,"w":2048,...}]}
hwatu motion --id 1 # 参考页面的动画,以数值形式呈现
# easing cubic-bezier(0.25,1,0.5,1), 300ms, marquee 29.78px/s ...
# ...Agent 编辑代码...
hwatu diff --id 2 --other 1
# {"match_percent":97.49} # 持续提升优于猜测
我们曾针对 stripe.com 首页的一个克隆版本运行这一循环:Agent 将像素匹配度从 85.1% 提升至 98.8%。复现方法见:scripts/demo/。完整验证流程(打开、加载、执行、截图、关闭)只需一条命令、一次工具调用,中位耗时约 35 毫秒(基准测试):
hwatu check localhost:5173 --eval 'document.title' --shot=/tmp/after.png
# {"title":"My App","eval":"My App","shot":"/tmp/after.png",
# "console":[...],"load_ms":13,"total_ms":35}
手头已有生成的 HTML、但没有服务器?hwatu render 是同样的单次调用流程,可将标记直接作为输入:无需临时文件,也不需要 python3 -m http.server:
echo '<h1>generated</h1>' | hwatu render --stdin --shot=/tmp/gen.png
# {"rendered":true,"shot":"/tmp/gen.png","load_ms":5,"total_ms":28}
# 无需轮询,即可响应加载、控制台、下载和窗口事件。
hwatu watch --kinds load,console
# {"event":"load","seq":1,"window_id":7,"data":{"state":"started",...}}
MCP 客户端可调用 subscribe_events,通过 notifications/hwatu/event 获取同一事件流。每个连接都会获得从零开始、严格单调递增的序列号;关闭或阻塞连接会丢弃该连接的订阅,但不会阻塞守护进程。
通过 Playwright 的常驻进程内 CDP 连接运行相同流程——这是其最佳情况——需要 82 毫秒和五次 API 调用。按照 hwatu 的实际运行方式进行测试,即每次检查使用新客户端、但针对保持热运行的引擎时,Playwright 的流程为 341 毫秒,而 hwatu 为 39 毫秒:hwatu 从设计上就是常驻热启动守护进程,Playwright 则是需要你自行维持热状态的库。
当 Agent 遇到 CAPTCHA 或需要人工判断时,hwatu focus 会在你的平铺式窗口管理器中实体化显示它的实时会话,Cookie 和状态均保持不变。你操作十秒钟,然后 Agent 接管回来。**这是没有任何其他工具可以声称拥有的形容词:可中断。**在其他所有工具中,无头模式在启动时就已确定,人工无论如何都无法查看该会话。而在 hwatu 中,它是窗口属性,可实时双向切换。
速度同样来自这一设计决策:hwatu 是一个常驻热启动守护进程,而非一个每次启动的库。引擎、GPU 上下文、编译后的广告拦截规则集和预热 WebView 都会跨任务持续存在,因此检查从热管线而非冷进程启动。Playwright 是一个库,天然倾向于冷启动。让它保持热状态是需要_你_自行构建的能力(服务器进程、连接管理、上下文池);hwatu 则将热启动作为默认且唯一的模式交付。
图例:✅ 是 / 内置 · 🟡 部分支持 / 有限 · ❌ 否
| 能力 | Playwright | chrome-devtools-mcp | hwatu |
|---|---|---|---|
| 验证流程(加载 + 执行 + 截图),常驻进程内 | 82 ms | 不适用 | 35 ms |
| 作为常驻_服务_的验证流程(每次检查使用新客户端) | 341 ms | 不适用 | 39 ms |
| 每次验证流程所需工具调用数 | 5 | 5 | 1 |
| 像素差异评分、区域与热力图 | 🟡 1 | ❌ | ✅ |
| 数值化动画,可固定在动画中途 | ❌ 2 | 🟡 3 | ✅ |
| 在_实时_会话上于无头 ↔ 有界面间切换 | ❌ | ❌ | ✅ |
| 会话中途人工接管,状态保持完整 | ❌ | ❌ | ✅ |
| N 个并行 Agent 时不抢夺焦点 | 🟡 4 | 🟡 4 | ✅ |
| CAPTCHA 检测及结构化等待/恢复 | ❌ | ❌ | ✅ |
| 无需 Node、无需按版本下载浏览器 | ❌ | ❌ | ✅ |
1 toHaveScreenshot 会与保存的基准图进行比较:这适合测试套件中的通过/失败判断,而不是 Agent 可以持续提升的评分。
2 标准做法是禁用动画,或快进到最终状态,以避免测试不稳定。
3 原始 CDP 可以查询动画状态,但没有关于缓动、速度或关键帧的数值化摘要。
4 无头模式没有问题;每个有界面窗口都会弹出并夺取焦点。
对比反映各项目在撰写时的情况,欢迎提出修正。需要诚实说明的是:Playwright 在冷启动速度(190 毫秒对 435 毫秒,仅在每次启动时支付一次)和内存方面仍然胜出;hwatu 渲染的是 WebKit 而非 Chromium(对于特定浏览器引擎 Bug,仍应在 CI 中保留 Playwright 测试矩阵),并且目前仅支持 Linux。完整的对比数据和方法论参见:docs/benchmarks.md。
采用 AGPL-3.0 许可证。Linux。WebKitGTK 6。
English / 原文
Make your agent harness loop instantly faster by giving it real eyes
- STOP your agent claiming "pixel-perfect." Make it prove 97.49%.
- STOP paying 5 tool calls per page check.
hwatu checkis one call, ~35 ms (beats warm-server Playwright ~9x). - STOP browser windows stealing your focus. Headless by default, you keep typing.
- STOP shipping 170 MB of Chromium. One static binary + your distro's webkitgtk.
- Vision: durable product principles, native platform strategy, swarm model
- Agent guide: protocol, primitives, verification loops
- Human guide: the tiling-WM browser side
- Benchmarks: every number, measured, with methodology
- Roadmap: plan of record, priorities, non-goals
- Continuous improvement: activation metric, feedback loop, weekly cadence
- Launch kit: reusable copy, channels, and measurement plan
Install → Detect workflow → Connect agent → Verify page → Hand off to human
curl -fsSL https://raw.githubusercontent.com/hongnoul/hwatu/main/scripts/install.sh | bash
hwatu setup
One static binary plus your distro's webkitgtk-6.0 (the installer checks). On Arch: yay -S hwatu. From source: cargo build --release.
The installer installs binaries only. hwatu setup detects supported coding agents and prints the available connections without changing their config. Choose a client explicitly when you are ready:
hwatu doctor
hwatu setup --client claude --scope project --dry-run
hwatu setup --client claude --scope project
hwatu demo
- Install: download two binaries and check WebKitGTK.
- Detect: find Claude Code, Cursor, Jcode, or a generic MCP workflow.
- Connect: use Jcode's native socket, MCP, or the CLI fallback.
- Verify: run a headless rendered smoke test with
doctorordemo. - Hand off: materialize the same live session only when a human is needed.
Setup is previewable, idempotent, and reversible with the same client and scope plus --undo. Project scope creates shareable configuration; user scope keeps it personal. Claude Code asks each user to approve project-scoped MCP servers when it next starts.
Manual MCP configuration remains one portable entry:
{ "mcpServers": { "hwatu": { "command": "hwatu", "args": ["mcp"] } } }
Or skip MCP entirely: every command is a short CLI call or one newline-delimited JSON line over a Unix socket.
hwatu localhost:3000 # open a window like you open a terminal
Tried hwatu? A successful check, a failed install, and a missing workflow are all useful signals. Share a two-minute use report or report a bug.
Tell your agent to use Hwatu
Connecting Hwatu makes its tools available. A project instruction tells the agent when to use them and what evidence counts as success. Add this to AGENTS.md, CLAUDE.md, Cursor rules, or the equivalent for your harness:
## Frontend verification
Use Hwatu after frontend changes. Exercise the affected user journey and
verify its intended visible, navigational, or persisted result with `expect`.
A successful click or clean console is not proof of success. Check `console`
for additional JavaScript and request failures after verifying the outcome.
Then make the task's proof concrete:
Implement display-name editing on /settings. Use Hwatu to enter “Test User,”
save it, verify the visible success state, reload, confirm persistence, and
report any console errors.
The same instruction works with Hwatu's MCP tools, short CLI commands, or Jcode's native integration. Name the user journey and the observable result that proves it worked. See the full agent guide, including a larger copy-paste policy and verification loops.
- Pixel-diff scoring: match percent + diff regions + heatmap (
diff) - Animations as numbers: duration, easing, velocity (
motion) - Deterministic animation frames: pin all animations at time t (
seek) - Page state as JSON, tokens not pixels (
snapshot) - Real input events with structured errors (
click/type/scroll/upload) - JS errors, console output, failed requests (
console) - Push event subscriptions as JSON lines or MCP notifications (
watch) - One-call page assertions with polling (
expect) - Headless / background / focused as a per-window property, switchable live
- Human hand-off:
hwatu focus <id>drops the live session into your tiling WM - CAPTCHA / anti-bot detection with structured wait/resume (
challenge) - MCP server, plain CLI, and a 1-line JSON socket protocol
- A minimal WebKit browser for humans: native ad blocking, vim-style bar, crash restore
Why not Playwright or chrome-devtools-mcp?
There are three ways to give an agent a browser, and two of them are bad at it:
| How it runs | What it costs the agent loop
---|---|---
Cold library (Playwright, launched per task) | engine starts when the script does | fast to call , slow to run : every check pays engine startup; no state survives between tasks
Warm browser (your Chrome + devtools-mcp) | a full human browser stays resident | resources spent on tabs, extensions, sync, UI you never render, and its windows steal your focus while you work
hwatu | "the coldest warm daemon" : engine hot, everything else absent | 8 ms spawns, 35 ms verified checks, invisible until you ask to see it (focus), interruptible in both directions
hwatu keeps exactly what makes checks instant (engine, GPU context, compiled adblock, a prewarmed WebView) and nothing that serves a human sitting in front of it. That's why it idles warm without a tab bar, and why a kept-warm Playwright server driven the same way still costs 341 ms per client to hwatu's 39 (benchmarks).
The second difference is what comes back. Playwright and chrome-devtools-mcp are, at their core, automation APIs: they let an agent drive a browser, then hand back raw screenshots and DOM for the agent to eyeball.
hwatu is different. It is a verification browser: the measurement primitives are built in, and the browser itself is a warm daemon where a window costs 13 ms and headless is a window property, not a launch mode.
That is why the loop looks like this, real commands, real output:
hwatu --headless localhost:3000 # its window; you never see it
hwatu --headless staging.example.com # the reference
hwatu diff --id 2 --other 1 --heatmap /tmp/heat.png
# {"match_percent":85.13,"regions":[{"x":0,"y":160,"w":2048,...}]}
hwatu motion --id 1 # the reference's animations, as numbers
# easing cubic-bezier(0.25,1,0.5,1), 300ms, marquee 29.78px/s ...
# ...agent edits code...
hwatu diff --id 2 --other 1
# {"match_percent":97.49} # climbing beats guessing
We ran this loop against a clone of stripe.com's landing page: an agent took it from 85.1% to 98.8% pixel match. Reproduce it: scripts/demo/. A full verification pass (open, load, eval, screenshot, close) is one command, one tool call, ~35 ms median (benchmarks):
hwatu check localhost:5173 --eval 'document.title' --shot=/tmp/after.png
# {"title":"My App","eval":"My App","shot":"/tmp/after.png",
# "console":[...],"load_ms":13,"total_ms":35}
Generated HTML in hand and no server? hwatu render is the same one-call pass with the markup as input: no temp file, no python3 -m http.server:
echo '<h1>generated</h1>' | hwatu render --stdin --shot=/tmp/gen.png
# {"rendered":true,"shot":"/tmp/gen.png","load_ms":5,"total_ms":28}
# React to load, console, download, and window events without polling.
hwatu watch --kinds load,console
# {"event":"load","seq":1,"window_id":7,"data":{"state":"started",...}}
MCP clients can call subscribe_events for the same stream as notifications/hwatu/event. Each connection gets a strictly monotonic sequence starting at zero; closing or stalling the connection drops its subscription without blocking the daemon.
The same pass through Playwright's warm in-process CDP connection, its best case, is 82 ms and five API calls. Shaped like hwatu actually runs (a fresh client each check against a kept-warm engine), Playwright's pass is 341 ms vs hwatu's 39 : hwatu is a warm daemon by design, Playwright is a library you have to keep warm yourself.
And when the agent hits a CAPTCHA or a judgment call, hwatu focus materializes its live session, cookies and state intact, in your tiling WM. You act for ten seconds. It takes back over. This is the adjective no other tool gets to claim: interruptible. Everywhere else, headless is decided at launch and a human can never see the session at any price. In hwatu it's a window property, switchable live, in both directions.
The speed follows from the same design decision: hwatu is a warm daemon , not a library you launch. The engine, the GPU context, the compiled adblock ruleset, and a prewarmed WebView outlive every task, so a check starts from a hot pipeline instead of a cold process. Playwright is a library and cold by nature. Keeping it warm is something you build (a server process, connection management, context pooling); hwatu ships warm as the default and the only mode.
Legend: ✅ Yes / built-in · 🟡 Partial / limited · ❌ No
| Capability | Playwright | chrome-devtools-mcp | hwatu |
|---|---|---|---|
| Verify pass (load + eval + screenshot), warm in-process | 82 ms | n/a | 35 ms |
| Verify pass as a warm service (fresh client per check) | 341 ms | n/a | 39 ms |
| Tool calls per verify pass | 5 | 5 | 1 |
| Pixel-diff score + regions + heatmap | 🟡 1 | ❌ | ✅ |
| Animations as numbers, pinned mid-flight | ❌ 2 | 🟡 3 | ✅ |
| Headless ↔ headed on a live session | ❌ | ❌ | ✅ |
| Human hand-off mid-session, state intact | ❌ | ❌ | ✅ |
| No focus stealing at N parallel agents | 🟡 4 | 🟡 4 | ✅ |
| CAPTCHA detection + structured wait/resume | ❌ | ❌ | ✅ |
| No Node, no per-version browser download | ❌ | ❌ | ✅ |
1 toHaveScreenshot compares against stored goldens: pass/fail for test suites, not a score an agent can climb.
2 Standard practice is to disable animations or fast-forward to the end state to avoid flakes.
3 Raw CDP can query animation state, but there is no numeric summary of easing/velocity/keyframes.
4 Fine headless; every headed window pops and takes focus.
Comparison reflects each project at the time of writing; corrections are welcome. Honest caveats: Playwright still wins cold start (190 vs 435 ms, paid once per boot) and memory; hwatu renders WebKit not Chromium (keep a Playwright matrix in CI for engine-specific bugs), and it is Linux-only today. Full head-to-head data and methodology: docs/benchmarks.md.
AGPL-3.0 licensed. Linux. WebKitGTK 6.
