dsh-agent-browser
面向 DeepSeek Harness 的浏览器自动化插件:把 agent-browser
(Rust + CDP)接成一组窄工具 + 一个设置页,可选再挂一个由 dsh-judgment
判断的目标循环。
语言: 中文(本文件) · English
为什么不是 MCP:
agent-browser mcp会把几十个原子动作全局塞进模型上下文,而它结构上做不到 插件内部的有界循环、每步新鲜度门控、动作审计与设置页。形态取舍的完整论证见 DESIGN.md §1.2。 参考的三个同类项目(jev-browser-use/jkudish/jev-browser/forvela/jev-agent-browser) 也都不走"把动作墙交给模型"这条路——它们的形态是一个 delegate 工具 + 代码拥有循环。
状态(诚实清单)
| 项 | 状态 |
|---|---|
| 设置页(安装自检 summary + 平铺行流 + 开关/输入 + 打开配置文件) | ✅ 已实现 |
| 安装自检:两项(CLI / Chrome)+ 帮忙装 CLI | ✅ 已实现(host @Remote probe/installCli + 客户端手写 contribution,pnpm verify:remote 交叉验证) |
内置 skill(skills/agent-browser/:核心命令 + 5 份 references) |
✅ 已实现(ctx.skills.register() 运行时注册,装插件即得) |
browser_snapshot / browser_act / browser_read / browser_session / browser_screenshot |
✅ 已实现 |
browser_raw 逃生舱(白名单) |
✅ 已实现,默认关 |
browser_run(决策循环,judge = ctx.judgment) |
✅ 已实现并完成 39 任务真机基准验证(支持页面内抽取、inputs 一次填全、subgoals 阶段编排,以及 Jev + 上游 LLM 低成本早停交回闭环) |
CLI 版本要求 >=0.38.2(不设上限) |
✅ 已实现(0.38.2 实测基准;0.39.1 复验通过) |
| 动作审计落盘 / 配额可视化 | ⏳ 见 DESIGN.md §8(M3) |
实测:pnpm probe 会 spawn 真浏览器跑一遍(版本下限 → snapshot refs → 按 ref 点击 → 陈旧 ref 被拒)。
注意探针只覆盖"元素被删除且没有同名幸存"这一类;同名顺位回退是 CLI 的已知行为
(DESIGN §1.1 表格 #9),探针不假装测它。
安装
dsh plugin --profile desktop add https://github.com/j62268781-alt/dsh-agent-browser.git
装完在 设置 → 浏览器 里配置。本插件零 MCP:只在 enabled 为真时注册工具;
关掉「浏览器功能」= 一个工具都不进模型上下文。
agent-browser 需要单独装:
brew install agent-browser # 或 npm i -g agent-browser
agent-browser --version # 需要 >= 0.38.2(更新版本也可用)
工具面(窄核心 + 逃生舱)
| 工具 | 作用 |
|---|---|
browser_snapshot |
无障碍树 + @eN refs,返回 origin / refs / digest。先快照再动作。 |
browser_act |
唯一变更入口,一次一个动作:click/fill/type/press/select/scroll/hover/check/uncheck/upload/dblclick/focus。可带 expectUrl / expectName 做执行前的新鲜度断言。 |
browser_read |
只读:read <url>(markdown / llms.txt 协商)或读元素的 text/html/value/attr/title/url/count。 |
browser_session |
open / close / tabs。固定使用命名会话,不劫持别的 agent 或人类开着的窗口。 |
browser_screenshot |
截图(可关)。 |
browser_raw |
逃生舱:白名单内的其它 CLI 子命令(get / find / network / storage …);禁 mcp/chat/install/plugin/connect/auth/--cdp/--profile。默认关。 |
browser_run |
决策开才注册:一句话 goal → 插件内部有界循环 → trace + handoff。 |
关于 ref
ref 大部分时候是元素身份绑定的(0.38.2 与 0.39.1 实测):页面变了、元素没了、换页了,旧 ref 会给
Unknown ref / Could not locate element with role=… name=…。
但有一个例外:目标元素消失、页面上还有同名元素时,解析会退回 role+name 的序位匹配,
静默点到隔壁那个同名元素(如列表里连排的 Delete)。所以本插件不设 snapshot_id 强制门控,
而是靠每步重取 snapshot 消除决策窗口;browser_act 的 expectUrl / expectName
是额外保险——同名列表/批量操作用得上,建议显式给。详见 DESIGN.md §1.1(含复现命令)。
内置 skill
插件自带一份与已装 CLI 版本对齐的核心 skill(skills/agent-browser/):
SKILL.md(130 行):核心循环、两个最容易做错的点(会话隔离、等待)、定位优先级、可见性/复用登录态、 安全护栏、排障表;references/:commands.md(34KB,需要时才读)、snapshot-refs.md、session-management.md、authentication.md、trust-boundaries.md。
插件启动时用 ctx.skills.register() 把它注册成运行时 skill(resourceBase 指向 references 目录,
模型可按需读取绝对路径),并在末尾追加一段插件补充(工具名、browser_run、references 路径)——
SKILL.md 原文逐字不改,避免两份真相漂移。
为什么不直接用官方全量 skills:agent-browser skills get core --full 与 9 个专门技能(electron / slack /
dogfood …)很全,但不适合作为常驻目录;需要时仍可用 agent-browser skills get <name> 现拉。
ctx.skills是可选服务:宿主没装dsh-skill时跳过注册,插件其余部分照常工作。
安装自检(设置页顶部 summary)
进「设置 → 浏览器」自动跑一次,只有两项(路径之类的细节不显示)。按钮只有两个状态: 环境可用 → 灰色「已安装」(不可点);环境不可用(或还没检查过)→ 「检查安装」(可点,修完能重新确认)。
| 项 | 通过 | 不通过 |
|---|---|---|
| agent-browser | 已安装 0.39.1(任何 ≥ 0.38.2 的版本) |
本机没安装 + 修法,并出现 [安装] 按钮(Homebrew 优先,否则 npm i -g;点了才跑) |
| Chrome | 已就绪(Google Chrome 155) |
没找到 + 下载入口(Chrome 是必须项:没有它页面根本驱动不了) |
两项都过 → 卡片是绿的(浏览器环境就绪);任一项不过 → 红,直接摊开原因与修法。
Chrome 为什么不帮忙装:brew install --cask google-chrome 在 macOS 上要管理员密码,
一个后台进程挂着等密码是最糟的交互。CLI 可以(纯用户态安装),Chrome 只给命令与链接。
Chrome 的判定优先用 CLI 自己的 agent-browser doctor --quick --json(约 0.5s,它认得环境变量、
engine 与自定义路径);CLI 不可用时退回文件系统 / PATH 探测,消息写「没找到」而不是「没有」。
性能(实测,2026-10 · agent-browser 0.38.2)
| 被测 | 均值 |
|---|---|
spawn /bin/echo(spawn 下限) |
1.2ms |
spawn bash -c …(bash 路线) |
1.4ms ← 比直接 spawn 更贵 |
agent-browser --version(PATH 上的 Node 包装脚本) |
57.3ms |
原生二进制 --version / get title / snapshot -i / click @e2 |
3.1 / 3.7 / 5.1 / 16.8ms |
所以:
- 插件比"用 bash 调 CLI"更省,不是更贵:bash 是 shell + CLI 两次 spawn,插件是直接一次。
- PATH 上的
agent-browser是 Node 包装脚本(Homebrew / npx 都如此),每次调用白付一份 Node 启动。 插件默认自动解析到同目录的原生二进制(agent-browser-<platform>-<arch>),砍掉 11–15 倍启动开销; 要指定别的就配browser.executablePath(带/时原样使用)。brew upgrade后旧路径失效会自动重解析重试一次。 - 决策循环里 CLI 不是瓶颈:一步 ≈ 22ms,而一次 Jev 判断是 500ms 量级;真正的收益是 N 步压进 1 个宿主轮次,而不是省那几毫秒。
pnpm probe 会打印实际使用的命令与每条命令耗时,数字可复跑。
决策(browser_run:Jev + 上游 LLM 低成本闭环)
「决策」开启后多一个 browser_run(goal, inputs?, subgoals?, allowIrreversible?):插件内部跑一个有界循环——
每步页面内抽取候选(extract.ts 一次 eval 拿齐可见元素/状态/附近上下文 + 正文/标题/URL;必要时回退 a11y 树)
→ 确定性完成门(URL 新增段命中 / 弹层关闭消失 / 加购变 Remove / 下拉或勾选态全部达成 / 填值生效)
→ ctx.judgment.ask(state, { action: choice, goal_done, done_change, stuck, irreversible, login, page_error })
→ 停止门在**动作之前**判定(goal_done + 独立 confirm / likely_done / page_error / needs_login / stuck)
→ 首选动作无效时:仅当次优候选概率 p ≥ 0.25 才允许换路,否则**立即早停交回上游 LLM**(绝不盲点跳走)
→ 执行**一个**动作(或按调用方 `inputs` 直接填/选/勾)→ 记 history/diff → 下一步
核心纪律:
- 确定性门控优先,判断只做排序:域名白名单、不可逆拦截(
needs_confirmation)、状态守卫(已勾选不再反点)、Enter 消费防重填等全部由代码确定性执行。 - Jev + 上游 LLM 低成本闭环:Jev 擅长的确定性步骤在单次工具调用内快速跑完;遇到复杂自定义组件或置信不足(
ambiguous/no_progress/blocked/page_error/input_required),立即早停并在返回值里附带前 5 个可操作candidates(含ref句柄)与pageText摘要——上游主模型无需再调一次browser_snapshot,可直接用browser_act一步接管。 - 文字/密钥不猜:调用方可由
inputs一次传入(敏感字段自动脱敏为●●●,绝不出境);缺值时以input_required交回宿主。 - 结束语是
needs_verification,不是"已完成"——最终校验留给宿主(39 任务真机基准false-done = 0)。 - 降级必须显式:没有
ctx.judgment、用途名未配置、密钥缺失、配额耗尽 → 类型化失败 + 明确文案,绝不静默退回主模型假装在决策。
判断走 dsh-judgment:在 设置 → 决策 里建一条连接,
把用途名 browser.action 指向它(默认值就是这个)。换厂商/模型/密钥只改那里,不动本插件。
开发
pnpm install
pnpm build # host(tsc) + 客户端(esbuild → lib/client.js)
pnpm test # 注册面 + 自检 + 页面渲染 + 纯逻辑 + 决策循环回归(177 项单测,离线可跑)
pnpm typecheck
pnpm verify:remote # Remote 通路交叉验证(17 项):host SRC fallback ↔ 手写 contribution
pnpm verify:served # 问运行中的应用:boot 数据里有没有本插件的客户端半(7 项)
pnpm probe # 真 CLI 探针(会开浏览器,结束前自动关掉自己的会话)
pnpm bench:tasks # 真机任务集基准(支持 --tier offline,base,hard,guard --connection TypeSafe --repeat 3)
目录结构:
src/
├── config.ts 配置(schemastery;开关决定工具注册)
├── cli.ts agent-browser CLI 薄封装:spawn · JSON 信封 · 版本下限 · 原生二进制解析
├── extract.ts 页面内 DOM 候选抽取(一次 eval 拿候选 + 状态 + 上下文 near + 正文/标题/URL)
├── env.ts 环境探测(Chrome / 安装计划 / 跑命令)——自检那两项的本机侧
├── skill.ts 内置 skill 的加载与注册
├── index.ts 入口:Service 类(工具注册 + @Remote probe 自检)
├── task.ts 向后兼容导出入口(re-export ./task/index.js)
└── task/ 按核心职责拆分的决策循环模块
├── types.ts 公共类型、阈值常量(含 ALTERNATE_MIN_PROBABILITY = 0.25)与交回提示契约
├── utils.ts 纯工具函数(哈希、语义签名、快照 diff、键名/词元解析、截断)
├── candidates.ts 候选生成、目标相关性排序与特殊动作面(悬停/拖拽/按键)前置注入
├── gates.ts 确定性门控与字段状态核对(多子句勾选守卫、Enter 消费追踪、多跳 URL 证据、次优筛选)
├── judge.ts Jev 协议问题构造、二阶段下拉选值、独立完成确认与分布缩容重采
├── actions.ts 浏览器动作执行(fill/select/click/hover/drag/press)与异步加载等待
└── loop.ts 有界决策主循环(runTask)、有序子目标编排(runSubgoals)与上游 LLM 交回封装
client/ 设置页(统一 JSX,esbuild 打成 window.__ModuleLoader__ 单文件)
skills/ 随包发布的核心 skill(SKILL.md + references/)
bench/ 39 任务真机基准套件(tasks.mjs · run.mjs · score.mjs · nav.mjs 状态隔离与导航诊断)
tests/ 注册面 · 自检 · 抽取与门控纯逻辑 · 决策循环与基准单测(共 177 项)
scripts/ probe.mjs(真 CLI)· verify-remote.mjs(Remote 通路)· e2e-run.mjs
⚠️ 新增/客户端改动的生效方式(实测):
dsh plugin add之后 app 会热组合新的 bundle (无需重启:boot 数据里立刻有本插件,skill 目录里也出现了agent-browser)。 但 host 半是 Node 模块,改完lib/index.js要重启 DSH 才会换上新代码;客户端半重新构建即生效。 配置项写的是 volatile 字段:不会重建 entry,工具注册由服务监听loader/volatile-update自己重做, 所以改完即时生效、也不需要重启。
⚠️ 兼容性:DSH 的 boot 会把插件
peerDependencies里@deepseek-ai/dsh-*的版本范围 拿去和「dsh 运行时版本」比对,不满足就整个跳过这个 bundle(日志一行dsh: skipping profile bundle)。 所以这里用的是跨版本区间(>=0.2.0-rc.2 <0.3.0),不要精确钉死某个 dsh 版本。 当前验证过的运行时:dsh 0.2.1-rc.1 / cordis 4.0.5 / schemastery 3.18.5 / dsh-tools 0.2.1-rc.1 / dsh-typert-protocol 0.2.1-rc.1。
决策循环(
browser_run)的设计参考了三家实现并做了实测校准: fan-out 五问里的停止门在动作之前;goal_done只认「页面可见正文」这一个字段; 重复且无效的动作从概率分布里换次优候选;两次无效即停;状态超 12k 截断并显式标注; 结束时只给needs_verification+next("怎么验"),从不宣布完成。 真机端到端:pnpm e2e(真 Chrome + 真 Jev,两步点击后被独立确认为 SUCCESS); 真机测试(真的dsh进程 + agent 自己调工具):pnpm live --run "<任务>"; 每步观测走 CLI 的batch(树+正文+标题一次进程,省 ~114ms); 耗时基准(决策 vs 原子工具,hao123 首页):pnpm bench; 真实网站手试:pnpm try --url <url> --goal "<目标>" [--expect <正则>](实测 jevusers.com → Browser → jev-browser-use,2 步完成,见docs/try-jevusers.png)。
⚠️ 「决策」依赖 dsh-judgment(依赖在
package.json里显式声明:peerDependencies.dsh-judgment
peerDependenciesMeta.dsh-judgment.optional = true,所以不装也能用插件,只是不能开决策)。 设置页的判定是四态的——ready(可开启)/checking(自检未返回)/unknown(自检失败或 host 半版本不匹配)/missing(宿主明确说没有ctx.judgment)。只有ready才允许开启: 其余三态开关一律置灰并说明原因;点开启时还会向宿主checkJudgment()现问一次(不信任缓存), 依赖不在就不写配置。宿主侧也有兜底:apply()发现tools.decision开着但依赖缺失只警告、不注册工具。
许可
MIT
No comments yet. Be the first to write one.