这是什么
agent-gavel 是一个给 AI 用的 MCP server,提供"操作网页 / 桌面 + 程序化验证"。
典型的浏览器 agent 循环是:动作 → 页面快照喂回模型 → 模型判断成败 → 下一步。agent-gavel 改成:
AI 声明动作 + "做完后页面应该长什么样" → 执行 → 程序断言 → 返回 pass/fail/ambiguous + 实际值
成败由程序判定,模型不用回头"看"一遍页面。省掉每次动作后的一次模型往返(慢 + 费 token + grep容易漏,全量看token花费大)。
结果三态:pass(断言满足)/ fail(断言未满足且页面无变化,明确失败)/ ambiguous(拿不准——断言没过但页面确实变了、或无断言但检测到变化,交回模型仲裁)。多数场景是明确的 pass/fail,ambiguous 只留给少数真拿不准的情况。
两个通道,可独立安装:
| 通道 | 干什么 | 需要什么 |
|---|---|---|
| DOM(网页,主通道) | 通过 Chrome CDP 操作真实浏览器:填表 / 点击 / 搜索 / 登录,全部 isTrusted=true 真实输入 |
本机 Chrome + Python 3.13,纯 Python 依赖 |
| AT-SPI(桌面,实验性·停止开发) | 通过无障碍树操作桌面应用 | 额外 npm i -g computer-use-linux |
不装 computer-use-linux 也能用 DOM;装了才解锁桌面工具(不装时桌面工具返回 atspi_unavailable 的明确提示)。
Install
兼容性:Linux + Python 3.13 验证过,需本机有 Chrome/Chromium。Windows 上 DOM 通道已适配(Chrome 自管/进程清理/置顶已跨平台,Windows + Chrome 实测跑通;见文末 TODO);macOS 与桌面(AT-SPI)通道仍仅限 Linux。
环境准备(Ubuntu / Windows)
两者都需要 Python 3.13(uv 会自动装,pip 方式请自备)和一个 Chrome/Chromium;DOM 通道默认在桌面会话里跑(调试 Chrome 是可见窗口)。无桌面会话(服务器 / CI / SSH)可切无头模式,见下文"怎么接到 opencode"的 AGENT_GAVEL_HEADLESS。
Ubuntu
# 1. 装 Chrome(二选一)
sudo apt install -y ./google-chrome-stable_current_amd64.deb # 先从 google.com/chrome 下载 deb
sudo apt install -y chromium-browser # 或装 Chromium
# 2. 置顶工具(可选,装了才能把调试 Chrome 窗口自动置顶)
sudo apt install -y xdotool
# 3. 确认在桌面会话(有 DISPLAY 或 WAYLAND_DISPLAY)
echo "$DISPLAY $WAYLAND_DISPLAY"
Chrome 需能在 PATH 里找到(google-chrome / google-chrome-stable / chromium / chromium-browser 任一)。
Windows
# 1. 装 Chrome
# 自动探测顺序:Program Files / Program Files(x86) / LOCALAPPDATA 下的 chrome.exe、msedge.exe,再查 PATH
# 2. 确认在正常桌面会话即可
两种安装方式,按场景选:
# ① 全局安装 ——命令装进 ~/.local/bin,全局 PATH 可用
uv tool install agent-gavel
# ② 装进当前 Python 环境(项目 venv / conda env)
pip install agent-gavel
# 桌面操作(可选,仅 Linux):不装则 DOM 照常可用,只是桌面工具返回 atspi_unavailable
npm install -g computer-use-linux
装好后在 MCP 客户端(见下)里配置["agent-gavel"](方式①②,命令已在 PATH),重启后 doctor 工具会报告 DOM 通道状态(Linux 上还含 AT-SPI)——这就是安装成功的信号。
从源码安装
git clone https://github.com/Rottenwooood/agent-gavel.git
cd agent-gavel
uv sync # 装依赖(Python 3.13)
uv run python -m agent_gavel.main # 起 MCP server
uv run python tests/browser_manager_smoke.py # Chrome 生命周期冒烟测试
uv build # 本地构建 wheel/sdist
uv run python ... 两端通用。Linux 上另有 ./run-mcp.sh——带优化版 computer-use-linux 的启动器(仅源码运行用);Windows 直接用上面的 uv run python -m agent_gavel.main。
源码跑通后,想贡献你验证过的模板 → 见文末"贡献你验证过的模板"。
安装后到底发生了什么(流程说明)
两种安装方式的区别
uv tool install agent-gavel —— 全局安装(推荐正式用):
- uv 把 agent-gavel 装进 uv 的 tool 目录(
uv tool dir可查;Linux 默认~/.local/share/uv/tools/) - 可执行命令
agent-gavel链接到 uv 的 bin 目录(Linux~/.local/bin/,Windows%USERPROFILE%\.local\bin),已在 PATH 里 - 任何目录都能直接
agent-gavel起 server,类似npm i -g
pip install agent-gavel —— 装进当前 Python 环境(项目 venv / conda env):
agent_gavel/包落到 site-packages(Linux<venv>/lib/python3.13/site-packages/,Windows<venv>\Lib\site-packages\)- 同时生成可执行命令
agent-gavel(指向agent_gavel.main:main),在所在环境 PATH 里
三种方式最终效果一致:启动一个在 stdio 上说话的 MCP server 进程,等 MCP 客户端连它。区别只在命令装在哪、是否全局可用。
怎么接到 opencode
opencode 通过 command 数组拉起这个进程,两者用 stdio 通信(配置在 opencode 的配置文件里,Linux/macOS 通常是 ~/.config/opencode/opencode.json;Windows 路径见 opencode 文档):
{
"mcp": {
"agent-gavel": {
"type": "local",
"command": ["agent-gavel"],
"enabled": true
}
}
}
重启 opencode,工具列表出现 agent-gavel_dom_* 系列。无需手动开 Chrome——首次 DOM 调用时 server 自动用独立 profile 拉起调试 Chrome,崩溃自动重拉,退出自动清理。
可选无头:默认拉起可见窗口的调试 Chrome(能看到操作过程)。无桌面会话(服务器 / CI / SSH)时,在 MCP 客户端给这个 server 传环境变量切无头:
"agent-gavel": {
"type": "local",
"command": ["agent-gavel"],
"environment": { "AGENT_GAVEL_HEADLESS": "1" },
"enabled": true
}
无头用 --headless=new(Chrome 112+),不要求 DISPLAY;doctor 的 cdp_mode 会报告当前模式(headed / headless)。切换模式后重启 opencode(旧 Chrome 随 server 退出被清理)。注意:无头下无法人工扫码/验证码,登录类流程需依赖已登录 profile。
模板存在哪
- 随包模板:安装自带的模板在
site-packages/agent_gavel/channels/{dom,desktop}/templates/(只读默认,如百度/必应/豆瓣等 8 个验证过的) - 你的模板:
dom_save_template/desktop_save_template保存到你自己的用户目录——Linux~/.config/agent-gavel/,Windows%APPDATA%\agent-gavel\(下分templates/、desktop_templates/)——跨缓存、跨安装版本持久存在,不会被升级覆盖 - 读取时用户目录优先:你保存的同名模板覆盖自带模板;失效检测记录(stats)也存用户目录
实测耗时
DOM 通道(当前各步骤耗时,测试环境:Linux + 本机 Chrome)
| 阶段 | 耗时 | 说明 |
|---|---|---|
| Chrome 冷启动(首次连接) | ~5s | 只在进程没起时一次;热连接 ~30ms |
navigate(跳转等加载) |
150–300ms | 热站,readyState complete 即返,无固定 sleep |
set_value(填输入框+断言) |
20–50ms | 真实输入 |
click(点击+断言) |
~10ms | 单点 |
press_key(按键) |
~5ms | 单键/组合键 |
press_enter(提交+等结果页) |
150–400ms | 含页面真实响应 |
| 单模板(3 步:导航+填+提交) | ~0.5–1s | 视站点加载速度 |
数据来自 tests/dom_smoke.py 分段计时与 tests/dom_smoke_loop.py 多模板循环压测(5 模板 × 3 轮全过,平均单次 ~0.6s)。慢的是页面真实加载,工具本身动作步都在毫秒级。
验证判定耗时(tests/verify_branches.py,10 个分支全过):
| 分支 | 结果 | 耗时 |
|---|---|---|
| 有断言 + 满足 | pass | ~3ms |
| 有断言 + 不满足 + 页面有变化 | ambiguous | ~5ms(不等超时,立即判定) |
| 有断言 + 不满足 + 页面无变化 | fail | ~wait_s(等满才判失败) |
| 无断言 + 有变化 | pass | ~4ms |
| 无断言 + 无变化 | ambiguous | ~5ms |
| 有断言 + 延迟 500ms 满足 | pass | ~511ms |
| 降级全失败(strict=False,3 策略) | fail | ~1.8s |
DOM diff 兜底让"无断言"从原来固定 sleep 0.5s → ~4ms(~100x);early ambiguous 让"断言失败但页面有变"从等满 wait_s → ~5ms。只有真 fail 和降级才耗超时。
AT-SPI 桌面通道:微信发消息模板
微信发消息模板(4 步:激活窗口→点会话→输入→发送)的耗时构成,实测微信窗口开着时(优化前基准):
| 步骤 | 动作 | 总耗时 | 其中读树(before+稳定) | 动作本身 |
|---|---|---|---|---|
| step0 | activate_window | 1472ms | 1332ms | 131ms |
| step1 | click 文件传输助手 | 1508ms | 1145ms | 362ms |
| step2 | type(剪贴板) | 1507ms | 1161ms | 346ms |
| step3 | click 发送+断言 | 2533ms | 576ms+稳定 | 354ms |
| 合计 | ~6.8s |
耗时大头在读树:每步做 2 次 get_app_state(before + 稳定确认),每次 ~580ms(微信 ~600 节点 × ~10 次 DBus 读)。step3 多一次等消息。
优化:给 computer-use-linux 打补丁(third_party/ 保存)——AT-SPI 连接复用 + doctor 缓存 + fast_app_filter 跳过全桌面 pid 反查 + 坐标操作不再触发 portal 截屏;server 层读树次数减半(before 复用 + 确定性动作跳过稳定确认)。桌面 70 节点应用实测单次读树:
| 阶段 | 原版 | 优化后(fast=true) |
|---|---|---|
| doctor_report | ~150ms | ~0ms(缓存) |
| app_filter | ~280ms | ~0ms(按 pid 直选) |
| snapshot_tree | ~145ms | ~140ms |
| 合计/次读树 | ~630ms | ~220ms(2.9x) |
端到端对比:read_state 620→235ms(2.6x);act_and_verify 单次闭环 ~1.5s→0.65s(2.3x);微信发消息模板整条流程 6.8s→4.8s。树内容与动作结果和原版一致,无回归。
功能一览
DOM(网页)
- Chrome 自管:首次调用自动起独立 profile 调试 Chrome(默认可见窗口;
AGENT_GAVEL_HEADLESS=1切无头),崩溃自愈,退出清理 dom_step:通用单步闭环(动作 + 选择器 + 断言一次调用),动作空间按输入通道参数化——键盘press_key(特殊键:Enter/Tab/F5/ArrowDown…;组合键Ctrl+A有解析 bug,见 TODO)+ 文本type_text(含中文)+ 鼠标click(左/右/中、单击/双击)+hover/drag/scroll+ 导航navigate;set_value/press_enter/clear/focus作便捷动作保留。点击/按键可能跳转时用wait_navigation=True,等跳转完成再返回最终 urldom_read:只读取值——给一组"名字 → 取值规则"直接返回实际值,不判定 / 不重试 / 不 diff(读标题、输入框内容、正文、PDF 文字层)。取值规则支持纯表达式,或函数形式"() => { ...; return ...; }"(多语句 / 赋值)dom_text:取页面/元素文本(text可见文字 /content原始文字 /html结构),正文提取;浏览器内置 PDF 阅读界面不是页面元素,取不到dom_explore:枚举页面可交互元素,给验证过唯一的锚点(#id/input[name=q]/__text__:登录/ 同名按钮用__text_nth__:N::)- 模板复用:跑通的流程存 JSON(
templates/,按网站组织:site纯站名,文件名site_功能.json),换参数直接跑 - 失败诊断:环境错误(Chrome 没起 / Linux 缺 DISPLAY / CDP 断)返回
reason + hint,hint 里会提示可切无头(AGENT_GAVEL_HEADLESS=1),不会让 agent 对着一个 "Error executing tool" 猜 - 三态验证 + DOM diff 兜底:无断言时不再盲 sleep 放行——抓动作作用域前后 DOM 结构签名做 diff,变化明显判
pass、无变化判ambiguous;断言失败但页面确实变化 →ambiguous(可能点错/断言写窄),断言失败且无变化 →fail - 断言降级重试:fail 自动换策略(trusted 翻转 → 重新 explore 换锚点 → 滚动 → 切换等待模式);
strict参数关掉降级暴露真实 fail(写模板/排查时用) - 模板失效检测:连续失败 ≥3 次标 suspected,再跑返回 warning 建议重新探索;任何一次 pass 清零
工具选型(该用哪个)
| 我要做的事 | 用哪个 |
|---|---|
| 打开一个网址 | dom_navigate(唯一导航入口;自动验证到没到,返回最终 url / 是否跳转 / content_type) |
| 读页面上的值(标题 / 输入框 / 某字段 / 一段文本) | dom_read(通用读:给"名字 → 取值规则",直接返回值,不判定不重试) |
| 读整页或某块的正文、或页面所有链接 | dom_text(text / content / html / links;dom_read 的专用视图) |
| 做一个动作并验证结果 | dom_step(动作 + 断言;不含导航——导航用 dom_navigate) |
| 看页面有哪些可点 / 可填元素 | dom_explore(dom_read 的专用视图,锚点带唯一性校验;可传 url 一次性探索) |
| 读 .pdf / .docx 直链文件内容 | dom_document(dom_read 读不到的"文件"视图) |
| 重复跑同一套流程 | dom_save_template 存模板 → dom_run_template |
| 以"读"为主、几乎不点 | dom_navigate + dom_text + dom_read 三件套足够 |
一句话边界:改页面用 dom_step(导航除外),纯读用 dom_read(或它的三个专用视图 dom_text / dom_explore / dom_document),导航只用 dom_navigate。
AT-SPI(桌面,实验性·停止开发)
曾用于操作桌面应用(
act_and_verify/run_operation/ 桌面模板,经 computer-use-linux 无障碍树)。Linux 上性能不佳、微信等闭源应用 A11y 树不完整,已停止开发。工具保留可用但不投入;纯 DOM 用法无需安装 computer-use-linux。
验证机制(核心,DOM 在用)
- 三态判定:
pass/fail/ambiguous——程序能明确判的就判,判不了的交回模型 - 断言:
eq / neq / exists / not_exists / contains(特征用 JS 表达式提取) - DOM diff 兜底:无断言、或断言失败时,抓动作作用域的前后 DOM 结构签名做 diff——URL/TITLE 变化=导航级,局部作用域任意 1 处变化即算,body 作用域需足够增删(过滤广告位等噪音),据此程序判
pass/ambiguous - 等待:
poll(轮询断言,间隔 0.1s)/event(MutationObserver,DOM 一变即醒,适合异步长等待) - 降级重试:fail 自动换策略(trusted 翻转 → 换锚点 → 滚动 → 切等待模式);
strict=True关降级暴露真实 fail - 每动作返回
cost.elapsed_ms(端到端毫秒);各分支结果与计时见tests/verify_branches.py
TODO
通道状态
- DOM(网页):主通道,维护中。
- AT-SPI(桌面):实验性,Linux 上性能不佳(读树慢、微信等闭源应用 A11y 树不完整),暂时停止开发。不装 computer-use-linux 也可用 DOM;桌面工具保留但不再投入。
修复(动作空间)
- 三态验证 + DOM diff 兜底(本次):无断言/断言失败时抓动作作用域前后 DOM 结构签名做 diff,程序判定
pass/fail/ambiguous;修复 early ambiguous 被all_pass=False → fail覆盖的 bug;tests/verify_branches.py10 分支全过 - press_key 组合键 bug:三种拼写行为不一致——
Ctrl+A/Control+A能识别修饰键但主键A走 raw_char 分支(_KEYS只收小写字母);ctrl+a的修饰键完全丢失(_MODIFIERS只认Control不认Ctrl/小写)。需:修饰键名规范化(大小写/全称Control↔Ctrl),主键字母统一转小写查_KEYS,组合按下带修饰位。当前单键特殊键 OK(Enter/Tab/F5 等),普通字符键走 type_text。 - 单键特殊键已确认:Home 光标归零、双击选词(click count=2)、及其他原子动作(type_text 中文/右键/hover/drag/scroll)实测正常(
tests/dom_atomic_coverage.py17 步全过)。
平台 / 能力
- 适配 Windows(DOM):Chrome 自管跨平台化(psutil 进程管理、
taskkill /T清理、user32 置顶、Windows 版 Chrome 路径探测),去掉 DISPLAY 硬检查;Windows + Chrome 153 实测跑通dom_step/导航全链路(Python 3.12 验证,声明 3.13)。 - 适配其他平台:macOS 尚未适配(需补 Chrome 路径/进程管理/置顶);桌面(AT-SPI)通道依赖 Linux 无障碍树,不随 DOM 跨平台。
- 完善模板共享机制:当前模板存本地用户目录,缺少"模板共享/导入"通道(按站点从远端拉模板、版本化、社区模板源)。
- 登录态模板:Chrome profile 登录态保留,覆盖真实登录类流程。
- 多步骤 / 分页 / 滚动模板:现有模板多是"导航+填+提交",缺连续点进详情、无限滚动、多 tab。
- 断言语言增强:
eq/neq/exists/contains之外,补数值比较 / 正则 / 列表断言(结果条数等)。
产品化
- 权限 / 确认机制:高危操作(提交表单 / 发送)前人工确认。
- 可观测性:debug 日志之上,补任务级 trace / 会话重放。
贡献你验证过的模板(PR template)
agent-gavel 的模板按网站组织(site 纯站名,文件名 site_功能.json),随包带 8 个验证过的。你在一台机器上跑通了新网站的流程,PR 给我,合并后所有用户随包可用。
仓库内的验证过的模板:百度 / 百度百科 / 必应 / 博客园 / 豆瓣电影 / Google / 菜鸟教程 / 知乎登录填表。 设计说明见 docs/internal-beta.md,路线见 docs/roadmap.md。
模板去哪、怎么存
模板默认读用户目录优先(Linux ~/.config/agent-gavel/,Windows %APPDATA%\agent-gavel\;下分 templates/、desktop_templates/),随包模板在 agent_gavel/channels/{dom,desktop}/templates/。
贡献模板的路径:源码 clone → 用 dom_explore + dom_step 现场探索跑通 → dom_save_template 存 JSON → 确认稳定后放进仓库对应目录提 PR。
模板 PR 检查清单
模板 PR 要能直接合并,请过一遍:
- 模板 JSON 在
agent_gavel/channels/dom/templates/<site>_<func>.json(或 desktop 对应目录) -
site(DOM)/app(desktop)字段是纯站名,不含功能名;文件名站名_功能 - 真实跑通过(
dom_run_template/desktop_run_template全 pass),PR 描述里贴结果 - 步骤含断言(
page_features+expected_feature),不是只发动作不验证 - 用了稳定锚点(
#id/input[name=x]/__text__:),没有写死易变的 CSS 路径 - 用
$VAR占位符替代可变输入(关键词/账号/密码),不要写死真实值——params字段自动从$VAR提取(dom_list_templates会显示模板需要哪些参数) - 密码/token 等敏感参数在模板里显式声明:
dom_save_template(..., sensitive_params=["PASSWORD"])→ params 存{"PASSWORD": {"sensitive": true}}。声明后运行时传的真实值不落日志、不进返回;不声明则默认不敏感(仅参数名命中 PASSWORD/TOKEN 等关键词时兜底脱敏) - 涉及登录/个人数据时:模板文件里用假凭据占位(如
$USERNAME/$PASSWORD,跑的时候才传真实值);若流程依赖"已登录浏览器"(如删订单),PR 里说明依赖的登录态,别假装模板能独立登录 - 描述里写清:站点 URL、功能、测过的关键词/参数
模板存疑或失效会走失效检测(连续失败 ≥3 次标 suspected),无需担心一次不完美。
License
MIT
Metadata
Release files for agent-gavel 0.2.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| agent_gavel-0.2.3.tar.gz | 104.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| agent_gavel-0.2.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 199.1 kB
Release files / agent_gavel-0.2.3.tar.gz
| Download URL | agent_gavel-0.2.3.tar.gz |
|---|---|
| Size | 104.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
70d651bbf32f24851c724a64cfb430c759b0e08c6347ce1d272d14facc1b95d2
|
|
BLAKE2b-256 checksum How to use checksums |
3c6fc64d252880ca68a8dd97ad2247f57063ead1395bcdcc995ef69c042a3405
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / agent_gavel-0.2.3-py3-none-any.whl
| Download URL | agent_gavel-0.2.3-py3-none-any.whl |
|---|---|
| Size | 94.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
84728173c590855753fee2fea1139c1d660085a7449a223b8034fe03866f7e81
|
|
BLAKE2b-256 checksum How to use checksums |
af97a2e136880035fc9a4af6637a382435e295d6613ce0cefd330b5f866bd3f1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|