DeepSeek Harness Hub
← 返回列表

tristan-mcinnis/dsh-browser-vision

DeepSeek Harnessspec-screened在 GitHub 查看 ↗
未验证

dsh-browser-vision — 能“看见”页面的 DeepSeek 浏览器工具

尚未跑自动兼容性验证,可查看页面内的依赖与入口分析。 · 最近上游提交 2026/8/22 · 已提供中文文档

DeepSeek Harness 的浏览器工具,能够“看见”页面:通过 CDP 驱动 browser-use,由 deepseek-v4-flash-vision-exp 提供支持。读取 canvas 文本、图像内文字和渲染图表,返回符合 schema 校验的 JSON,并报告每次运行的成本。

综合分
27.3
GitHub 分
27.3
用户评分
★ Stars
0
周下载量
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add tristan-mcinnis/dsh-browser-vision
该插件未发布到 npm,走 GitHub 源安装(pnpm 若拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑)
数据截至 2026/9/18(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-tools@deepseek-ai/schemastery
用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动

README

English  |  简体中文

dsh-browser-vision — 能“看见”页面的 DeepSeek 浏览器工具

DSH plugin
License: MIT

一个自包含的 browser-use 工具,通过 DeepSeek 的 OpenAI 兼容接口驱动。它的定位是便宜、快,用来替代把 browser-use 挂在通用大模型上的做法,并且可以被任何 agent 或 harness 调用(Codex、Claude Code、OpenCode、自建评测、CI 等),入口是一个带机器可读 JSON 模式的普通命令行。

DeepSeek 发布 deepseek-v4-flash-vision-exp 之后,agent 可以直接看页面,而不只是读 DOM。该模型与文本版 Flash 同价,一张截图最多只算 384 个输入 token,所以视觉不再是为了省钱要关掉的高级功能。它默认开启,而且评测结果显示这也是最省钱的设置。

它为什么便宜又快(全部可用环境变量覆盖):

- DeepSeek Flash(deepseek-v4-flash)/ Flash Vision(deepseek-v4-flash-vision-exp):比 GPT 级模型便宜得多、快得多,而且两者同价。
- 默认开启视觉(DSBROWSER_VISION_MODE=on):每一步都带一张截图。因为看不见的 agent 会把整个步数预算烧在它根本读不到答案的页面上,而步数比图片贵得多。
- flash 模式、不用 judge、不用 planning:每个任务少几次额外的模型调用。
- 共用一个浏览器:一个进程内所有任务复用同一个 Chrome 实例,评测套件也是在同一个浏览器里跑完所有用例,而不是每个用例开一个 Chrome。

视觉

DSBROWSER_VISION_MODE(或 --vision)有三档:

| 模式 | 模型 | 行为 | 什么时候用 |
|---|---|---|---|
| on (默认) | deepseek-v4-flash-vision-exp | 每一步都附一张截图 | 日常使用,以及一切与视觉相关的任务 |
| auto | deepseek-v4-flash-vision-exp | 以 DOM 驱动为主,browser-use 会注册一个 screenshot 工具,agent 觉得需要时才调用 | 长文本任务,且绝大多数步骤确实不需要看图 |
| off | deepseek-v4-flash | 只读 DOM,永不截图 | 纯文本与表单任务,或者没有视觉额度时 |

无头和有头模式下视觉表现一致,截图走的是同一个 CDP 调用。dsbrowser "任务" --no-headless --vision on 是官方支持的“边看边跑”方式,评测套件同样接受 --no-headless。

视觉能解决而 DOM 完全无解的场景:

- 画在  上的文字(兑换码、编辑器、地图标注)
- 烧录在图片里的文字(扫描件发票、页面内截图、banner)
- 图表,以及任何答案存在于图形而非字符串里的内容
- “哪个是高亮的 / 置灰的 / 被遮挡的”这类版式问题

实测

在离线 fixture 套件上,每种模式跑 6 个用例、每个用例 3 次(dsbrowser-eval --vision all --repeat 3),按空闲时段价格计算:

| 模式 | 通过 | DOM 用例 | 视觉用例 | 延迟中位数 | 截图数 | 费用(USD) | 费用(CNY) |
|---|---|---|---|---|---|---|---|
| off | 7/18 | 7/9 | 0/9 | 33.7s | 0 | $0.1021 | ¥0.6998 |
| auto | 14/18 | 5/9 | 9/9 | 8.3s | 10 | $0.0310 | ¥0.2132 |
| on | 18/18 | 9/9 | 9/9 | 6.6s | 111 | $0.0318 | ¥0.2180 |

真正意外的是费用那一列。on 发了 111 张截图,花的钱还是只有一张都不发的 off 的三分之一。看不见的 agent 不会快速失败,它会原地打转:把整整 20 步预算耗在一个根本不含答案的 DOM 上,而 20 个文本步骤远比一张 384 token 的图片贵。在这里,视觉不是为准确率付的溢价,它恰恰是让整个流程短到便宜的原因。

auto 是最尴尬的中间态。它把视觉用例全做对了,但纯 DOM 用例反而比两个极端都差,而且相对 on 一分钱也没省下。

一台机器、一个下午、n=3。自己重跑一遍再决定信不信:dsbrowser-eval --vision all --repeat 5。

看一眼要花多少钱

DeepSeek 会把每张图缩放到像素总量约等于 800×800,因此单张图上限是 384 个输入 token。按空闲时段缓存未命中价 ¥1.5($0.22)/ 百万输入 token 计算,一张截图约 ¥0.00058 / $0.00008。哪怕 20 步每步都截图,整个任务也不到一分钱。

两种货币都取自 DeepSeek 自己的价目表,美元来自英文页,人民币来自中文页。它们不是互相换算出来的,所以不会和任何即期汇率对上。高峰时段翻倍,窗口为北京时间 09:00-12:00 与 14:00-18:00(即 UTC 01:00-04:00 与 06:00-10:00);报告出的费用会按运行落在哪个窗口来计算。

有两点值得记住:

- 绝不要用 detail: low。 它会先裁到 512×512,页面文字就此不可读。实测下来模型不再是读出确认码,而是直接编了一个。本工具发出的每张图都固定为 detail: high。
- 要压就压截图本身,别压 detail。 DSBROWSER_SCREENSHOT_SIZE=1024x768 只缩小上传体积,不改变 DeepSeek 实际计费的 token 量。

在 DeepSeek Harness 里使用

一行安装

把下面这段丢给你的 agent,它会把安装全部做完:

把 dsh-browser-vision 插件装进我的 DeepSeek Harness profile。先执行 dsh plugin --profile web add "github:tristan-mcinnis/dsh-browser-vision" 添加插件,再执行 uv tool install "git+https://github.com/tristan-mcinnis/dsh-browser-vision" 安装 Python 引擎(pipx 或 pip --user 同样可以)。检查我的 shell 环境里是否已设置 DEEPSEEK_API_KEY,没有就直接告诉我,不要让我把 key 粘贴到配置文件里。然后重启 profile,调用 browser_vision_status 工具并把结果给我看。如果它说引擎不可用,就照它给出的提示处理。

或者自己跑:

dsh plugin --profile web add "github:tristan-mcinnis/dsh-browser-vision" && uv tool install "git+https://github.com/tristan-mcinnis/dsh-browser-vision"

之后重启 profile(web profile 关闭了 HMR)。把 DEEPSEEK_API_KEY 放进环境变量即可,插件从不存储也不转发 key,它只是继承引擎运行时的环境。装好后用 browser_vision_status 确认引擎是否可达。

工具

| 工具 | 作用 |
|---|---|
| browser_vision_task | 用自然语言描述一个网页任务并拿到答案。会导航、点击、输入、填表,并读出那些只以像素形式存在的内容。 |
| browser_vision_extract | 按你传入的 schema 从页面抽取字段,返回校验过的 JSON,下游不需要再解析一次。 |
| browser_vision_status | 报告引擎是否已安装,用来把“装错了”和“任务失败了”区分开。 |

插件配置项(写在 cordis.patch.yml,或在 设置 → 插件 里改):

| 配置项 | 默认值 | 含义 |
|---|---|---|
| command | dsbrowser | 引擎可执行文件路径 |
| vision | on | 工具调用的默认视觉模式 |
| maxSteps | 20 | 默认步数上限 |
| headless | true | 是否无头运行 Chrome |
| timeoutMs | 300000 | 单个任务的硬超时 |

环境要求

- Python 3.11 或 3.12
- 系统里要有 Chrome/Chromium(browser-use ≥0.13 通过 CDP 驱动 Chrome,不使用 Playwright,所以 playwright install 既不需要也不可用)

安装

推荐用 uv:

git clone https://github.com/tristan-mcinnis/dsh-browser-vision
cd dsh-browser-vision
uv venv --python 3.12 .venv
VIRTUAL_ENV=.venv uv pip install -e '.[dev]'
cp .env.example .env   # 然后填入 DEEPSEEK_API_KEY

或者用 pip:

python3.12 -m venv .venv
source .venv/bin/activate
pip install -e '.[dev]'

这会安装 dsbrowser 包,以及两个命令行入口:dsbrowser(别名 deepseek-browser)和 dsbrowser-eval。

密钥:普通环境变量或 1Password CLI

DeepSeek 的 key 可以来自环境变量 / .env(DEEPSEEK_API_KEY),也可以通过 op CLI 从 1Password 取。把 DEEPSEEK_API_KEY_OP 指向一个 secret reference,工具会用 op read --no-newline 解析:

.env
DEEPSEEK_API_KEY_OP="op://Personal/DeepSeek/api key"

op:// 引用直接写在 DEEPSEEK_API_KEY 里也可以。需要安装并登录 op CLI(op signin,无人值守 / CI 场景可导出 OP_SERVICE_ACCOUNT_TOKEN)。工具本身完全不碰系统钥匙串,而且 Chromium 也被显式挡在 macOS 钥匙串之外(见下方说明)。

命令行

dsbrowser "打开 example.com 并返回页面标题"
dsbrowser "读出 example.com/ticket 上 canvas 里画的兑换码" --vision on
dsbrowser "任务" --vision off              # 只读 DOM,成本下限
dsbrowser "任务" --json                    # 机器可读输出
dsbrowser "任务" --max-steps 40            # 覆盖步数上限
dsbrowser "任务" --no-headless --vision on # 一边看它跑,一边让它看页面
dsbrowser "任务" --model deepseek-v4-pro
python -m dsbrowser "任务"                 # 模块形式

结构化输出

用 --schema 读一张扫描件发票,这一个调用就是整个工具的缩影:看见页面、抽取字段、返回校验过的 JSON。

{
"invoiceNumber": "5512-B",
"totalDue": "$128.40",
"lineItems": [
{"label": "Standing desk", "amount": "$96.00"},
{"label": "Cable tray", "amount": "$18.40"},
{"label": "Delivery", "amount": "$14.00"}
]
}

上面这些内容 DOM 里一个字都没有,发票就是一张 PNG。一次模型调用,10.9 秒,$0.0013(¥0.0089)。

传入一个 JSON Schema,答案就会以校验过的对象返回,而不是一段散文,调用方也就不需要再花一次模型调用去解析:

dsbrowser "打开 https://example.com/pricing 并列出所有套餐" --json --schema '{
"type": "object",
"properties": {
"plans": {"type": "array", "items": {
"type": "object",
"properties": {"name": {"type": "string"}, "price": {"type": "string"}},
"required": ["name", "price"]
}}
},
"required": ["plans"]
}'

--schema 也接受 .json 文件路径。支持对象、数组、基础类型、枚举、联合类型和本地 $ref;顶层必须是对象,所以裸数组要包一层属性。可选属性会返回 null,而不是逼着 agent 编一个它其实没找到的值。

如果你要的是正文而不是字段,--markdown 会把页面正文按干净的 Markdown 返回,去掉导航、广告和 cookie 横幅。

JSON 契约(给 agent 和 harness 用)

加上 --json 后,stdout 上只有一个 JSON 对象,stderr 保持干净:

{
"tool": "dsbrowser",
"version": "0.2.0",
"success": true,
"result": "PLUM-4482",
"steps": 3,
"duration_s": 14.2,
"error": null,
"model": "deepseek-v4-flash-vision-exp",
"vision_mode": "on",
"usage": {
"llm_calls": 4,
"images_sent": 1,
"prompt_tokens": 18422,
"completion_tokens": 611,
"cached_tokens": 12800,
"cost_usd": 0.00119,
"cost_cny": 0.00812
}
}

usage.images_sent 是模型实际看过的截图数量,在 auto 模式下可以用它判断 agent 到底有没有真的需要看页面。cost_usd 与 cost_cny 各自使用 DeepSeek 在该币种下公布的价格,并计入高峰/空闲时段与缓存命中。它们是估算,不是账单。

退出码:0 任务完成 · 1 任务失败(agent / 网络 / 模型错误,error 有值)· 2 配置错误(例如缺少 DEEPSEEK_API_KEY,或在不支持图像输入的模型上开了视觉)。

从其他 agent 调用

任何能执行 shell 命令的 harness 都能用:

result=$(dsbrowser "总结 https://example.com/pricing 的定价页" --json)
然后用 jq 解析 $result:.result、.steps、.usage.cost_usd、.usage.cost_cny、.error

Python harness
import json, subprocess
out = subprocess.run(["dsbrowser", task, "--json"], capture_output=True, text=True, check=False)
payload = json.loads(out.stdout)
print(payload["result"], payload["usage"]["cost_cny"])

也可以当库用:

import asyncio
from dataclasses import replace
from dsbrowser import Config, DeepSeekBrowserAgent

async def main():
config = replace(Config.from_env(), vision_mode="on", headless=False)
async with DeepSeekBrowserAgent(config=config) as agent:
outcome = await agent.run_task("读出 example.com/ticket 上 canvas 里的兑换码")
print(outcome.result, outcome.images_sent, outcome.cost_usd, outcome.cost_cny)

asyncio.run(main())

配置

| 变量 | 默认值 | 含义 |
|---|---|---|
| DEEPSEEK_API_KEY | — | 必填(或用 DEEPSEEK_API_KEY_OP) |
| DEEPSEEK_API_KEY_OP | — | 1Password CLI secret reference(op://Vault/Item/field),经 op read 解析 |
| DEEPSEEK_BASE_URL | https://api.deepseek.com | OpenAI 兼容端点 |
| DEEPSEEK_MODEL | deepseek-v4-flash | 视觉为 off 时使用的模型 |
| DEEPSEEK_VISION_MODEL | deepseek-v4-flash-vision-exp | 视觉为 auto 或 on 时使用的模型 |
| DSBROWSER_VISION_MODE | on | off · auto · on(旧的 DSBROWSER_USE_VISION=true/false 仍可解析) |
| DSBROWSER_SCREENSHOT_SIZE | — | 宽x高,上传前先缩放截图 |
| HEADLESS | true | 无头浏览器(两种模式下视觉都可用) |
| MAX_STEPS | 20 | 单个任务的最大步数 |
| DSBROWSER_CHROME_ARGS | --password-store=basic,--use-mock-keychain | 额外的 Chrome 参数;默认值让 Chromium 远离 macOS 钥匙串 |
| DSBROWSER_FLASH_MODE | true | browser-use 的 flash 模式提示词优化 |
| DSBROWSER_USE_THINKING | false | agent 逐步思考(更慢,有时更准) |
| DSBROWSER_USE_JUDGE | false | 额外的 judge 校验(更慢,更贵) |
| DSBROWSER_ENABLE_PLANNING | false | 长任务的 planner 子 agent(更慢) |
| DSBROWSER_DISABLE_THINKING | true | 关闭 DeepSeek 原生推理(thinking: disabled);token 约少 10 倍,延迟明显下降 |

评测

离线套件会起一个本地确定性站点。三个用例只靠 DOM 就能答;另外三个必须看见页面才能答,正是后者让这套评测能把不同视觉模式区分开:在 --vision off 下,这三个用例本来就应该失败。

| 用例 | 能力 | 需要视觉 | 判定信号 |
|---|---|---|---|
| search_and_extract | 输入、点击、抽取 | 否 | 名称、价格、路径完全一致 |
| navigate_and_submit_form | 导航、填写、下拉、勾选、提交 | 否 | 确定性的确认码 |
| negative_search | 处理空状态 | 否 | 报告结果数为零 |
| canvas_code | 读  上绘制的文字 | 是 | 兑换码 PLUM-4482 |
| image_invoice | 读烧录在图片里的文字 | 是 | 发票号与应付总额 |
| chart_reading | 读懂以图片渲染的图表 | 是 | 最高的月份及其标注数值 |

离线契约测试:不需要 API key,不需要浏览器
pytest -q

实跑评测:会消耗 DeepSeek token
dsbrowser-eval                      # 使用当前的 DSBROWSER_VISION_MODE
dsbrowser-eval --vision all         # 在完全相同的任务上横扫 off/auto/on
dsbrowser-eval --vision-only        # 只跑那三个必须看页面的用例
dsbrowser-eval --vision on --no-headless   # 同一套件,有头浏览器
dsbrowser-eval --repeat 5 --json    # 每个用例跑五次
dsbrowser-eval --case canvas_code
python -m dsbrowser.evals

每次运行都会把原始输出、错误、耗时、每个用例的 token 用量与费用,以及按模式汇总的结果写入 eval-results/run-.json。汇总行会把通过率拆成 DOM 用例和视觉用例,并给出延迟中位数、截图数量和总费用,从而在完全相同的任务上比较各模式。在比较不同配置之前,至少跑五轮。本地 fixture 衡量的是 agent 可靠性,不受公网站点漂移影响;把公网任务(比如 Hacker News 那类)单独作为真实性测试,因为网络状态和页面变化让它们不适合当回归门禁。

说明

- browser-use 会为 DeepSeek 关掉视觉,本工具把它改回来。 agent/service.py 里至今写着 if 'deepseek' in self.llm.model.lower(): use_vision = False,那是在 DeepSeek 视觉模型出现之前写的。DeepSeekBrowserAgent 会在构造出的 Agent 上恢复该设置;没有这一步,无论你怎么配置,agent 都会静默地一张截图都不截。
- 图片只允许出现在 user 消息里。 否则 DeepSeek 返回 400 "Image in system message is unsupported",而且一条这样的消息就会毁掉整次运行。每个请求发出前都会被清洗:出现在 system 或 assistant 消息里的图片会被替换成一段文字占位。
- 视觉需要视觉模型。 向 deepseek-v4-flash 发图片会返回 400 "This model does not support image",所以这种配置会在启动时就被拒绝并以退出码 2 结束,而不是跑到一半才炸。
- 用 response_format: json_object 做结构化输出。 DeepSeek 的 function calling 后端会把单键嵌套对象拆平,于是 browser-use 的 AgentOutput action 联合类型({action_name: params})回来时变成了裸的 params 字典,pydantic 校验直接失败。把 schema 走 JSON object 模式(并把 schema 嵌进 system prompt)能让 DeepSeek 返回完全正确的结构,而且已验证在消息同时携带截图时依然成立。
- 用量统计是我们自己做的。 上游 ChatDeepSeek 在所有分支上都返回 usage=None,所以本项目的 DeepSeekBrowserLLM 自行读取 DeepSeek 的 prompt_cache_hit_tokens / prompt_cache_miss_tokens 并按次累加。cost_usd、cost_cny 以及评测里的费用列都建立在这之上。
- 用 keep_alive=True 复用浏览器。 除非 profile 显式选择退出,Agent.run() 会在每个任务结束后杀掉浏览器会话;没有这一条,共用的浏览器在第一个任务之后就死了("CDP client not initialized")。
- deepseek-v4-flash 默认会推理,因此 DSBROWSER_DISABLE_THINKING=true 会通过一个 httpx 事件钩子在每个请求上带 thinking: {"type": "disabled"}。在一次极简调用上实测 token 少了约 10 倍(98 → 10)。
- browser-use ≥0.13 不再使用 Playwright,而是通过 CDP 驱动系统里的 Chrome/Chromium。请确保已安装 Chrome。
- Chromium 默认带 --password-store=basic 与 --use-mock-keychain 启动,因此无头运行永远不会去读(往往已经过期的)macOS 钥匙串,也不会弹窗。如果你确实需要在 profile 里做真实的密码存储,用 DSBROWSER_CHROME_ARGS 覆盖。

上游仓库有新提交时邮件通知你(每天最多一封,无更新不打扰),随时一键退订。

💬 加入 DPharness 群聊

插件用法、部署报错、新插件第一时间同步——群里问,比一个人翻文档快。

点击加入 QQ 群
DPharness 群聊二维码,手机 QQ 扫码进群
扫码进群