cbg33695/dsh-screen-reader0

dsh-screen-reader

Give the agent eyes on the screen: capture the whole desktop, one window, or one region and hand the IMAGE ITSELF to the session model (no second model, no description layer), plus an exact local pixel diff that locates what actually changed between two images instead of asking a model to guess. Windows only.

包名
dsh-screen-reader
版本
0.3.0
许可证
MIT
最近更新
2026年9月12日

安装

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:cbg33695/dsh-screen-reader

dsh-screen-reader

给 agent 装上眼睛:它自己看屏幕,而不是等你截图。 外加一个精确的像素差分——"变了没有、变在哪"由本地算,模型只回答"变成了什么"。

仅支持 Windows。 依赖 PowerShell 5.1 的 System.DrawingPrintWindow


⚠️ 先读这一段:它的优势到底是什么

它不提升视觉能力。 图像送进模型走的就是模型自带的那条 pipeline,被同一套归一化压到同样的 token 栅格。插件看得和内置视觉一样清楚,不多一分。

它真正多出来的,是模型原理上拿不到的两样东西——而这两样恰好决定了一个 agent 能不能自己干活:

优势为什么模型自带视觉拿不到对自动化意味着什么
1. agent 自己长眼睛模型只能读已经存在的文件。要让它看屏幕,本来只能你按 Win+Shift+S、保存、再拖进对话——每看一次就要你动一次手agent 能在执行过程中自己看:改完设置看一眼对不对、跑完脚本看一眼结果、卡住了看一眼界面上到底有什么。整条链路不需要人在场
2. 精确的"变了没有"问视觉模型"这两张图哪里变了",它会编造看起来合理的差异——这是它明确不擅长的方向判定"变没变"这件事从概率变成确定:本地算,精确、免费、还给边界框。模型被约束在"解释已经证实变化了的区域"上,没有编造的空间

一句话:别的插件在替模型看图,这个插件在给 agent 装手和眼睛。

这也是它唯一值得存在的理由。如果你愿意每次自己截图再粘进来,那它对你价值不大——手动截图 往往还更好,因为裁切边界是你定的。

什么时候不应该用它

你的需求更合适的选择
我只是想精一张图问问题用内置的 read_image,或 @liustack/modlens(★3.9k、L5 通过、输出结构化 JSON)。它在这件事上比本插件成熟,本插件不打算在这一点上竞争
我要跨平台本插件是 Windows 专属,做不到
我要精确测量像素用真正的测量工具。本插件是描述器,不是量具,实测尺寸误差可达 ±40%
我要"帮我看界面哪里不对"这种低对比度细节这是它的危险区,见下方边界一节

目录


它解决什么问题

模型没有眼睛在屏幕上。于是会发生这种事:

  • 你说"我这个界面哪里不对",模型只能靠你的描述
  • 模型说"你点一下那个按钮",它其实从没见过那个按钮
  • 你改完一个设置,模型不知道改成了什么
  • 你让 agent 渲染一张图/改一版 UI,它无法验证自己刚做出来的东西对不对

前三条是"看不见",第四条是"看不见而且无法自我验证"——后者才是自动化真正的瓶颈。

这个插件给模型两样东西:

  1. 看得见:抓屏幕(整屏 / 某个窗口 / 某个区域)→ 把图本身交给当前模型,不经过第二个模型转述
  2. 验得了:两张图之间精确定位变化区域 → 模型只解释"变成了什么"

两个工具

看屏幕

工具作用
see_screen抓屏并把图直接交给当前模型看window 只抓某个窗口,region 做归一化裁切(这是唯一能买到细节的手段,见下一节)。默认不调用第二个模型;transcribe: true 是给纯文本会话模型的兼容路径

比两张图

工具作用
see_diff两张图的差异:本地精确像素差分定位变化区域 → 只把变化区域交给模型解释。模型从不需要判断"有没有变"

这里没有"看一张图片文件"的工具。 原来有一个 see_image,但它只是内置 read_image 的较差重复(多一层模型间转述)。模型自带视觉之后这件事交给内置工具,已删除。


图像是怎么送进模型的(这一段决定了用法)

看屏幕的工具不把图交给另一个模型转录,而是把图本身作为内容块返回,由当前会话的模型直接看。 这条路径已实测验证。

而图像在 provider 侧会被强制归一化,这一点决定了所有用法:

14px patch 网格 · 每轴 3:1 下采样 · 单图上限 384 token
你截的范围请求侧实际带宽每屏幕像素 → 请求像素
全窗 1942×1030≈ 950×5040.49
同一张缩到 1295×687≈ 950×5040.49
裁切 675×387≈ 879×5041.30
裁切 346×346(实测)原样 346×3461.00

(后两行要区别看待:675×387 那行是按源码常数推算的,没实测;346×346 那行是实测的。)

归一化其实是两段,而第一段可以实测:harness 自己先把请求图限制在约 64 万像素——实测一张 1920×1080 的截图,请求侧是 1066×600(= 639,600 px),provider 再把它压到 token 网格。 裁切之所以有效,是因为一块小图在第一段就被原样放过,跳过了那次 0.555 的线性缩放。

三件事因此是必然的,不是巧合:

  1. 全窗图下源图分辨率不影响准确度——两张不同分辨率的图落到同一个网格
  2. 裁切是唯一能买到细节的手段——实测有效细节密度约 2 倍(1.00 vs 0.49)
  3. 提高抓屏分辨率对准确度无用——更大的源图只会被压缩得更狠

这条不是推算出来的,是测出来的。 同一个侧栏区域,同一提示词:

输入方式读到的会话标题
全屏 1920×1080尖塔模组制作 ✗
裁切 346×346尖塔模组制作 ✓

真值取自 ${DSH_HOME}/storages 里的会话索引。全屏下 5 个标题 4 个一字不差、错 1 个字; 裁切后 0 错。而全屏的失效模式恰好是形近字(戮/戳 右半边都是"戈")——这正是 0.49 采样率预测的结果。

所以推荐用法是:先用 window 锁定应用,再用 region 放大到你要看的地方。


实测效果

以下全部是在真实运行中测出来的,不是设计目标。

视觉理解

项目结果
读柱状图数值(合成图,4 根柱)4/4 精确命中(120 / 60 / 180 / 90),颜色、排序全对
读 Blender 大纲视图的物体清单4/4Camera / Cube / Light / 球体),与 headless Blender 取出的真值完全一致
读标题栏中文路径逐字命中,含中文文件名与路径
判断哪个物体被选中修复前 0/3,修复后 2/2
LOCATION 结构化位置输出2/2 按要求吐出,能被正则解析成 region
读侧栏会话标题(全屏 1920×1080)5 个标题 4 个逐字命中,错 1 字
读同一区域(裁切 346×346)5/5 逐字命中,全屏下多出来的空格也一并消失

本地像素差分(这是插件里最可靠的一环)

项目结果
真实照片(1225×1254 JPEG)程序化加 2 处已知改动恰好检测到 2 个区域,零误报
位置准确性边界框比真实改动大约 30–45 px(= padding 10 + 网格量化 + 膨胀 1 格)
同一张图自比变化像素 0,正确报"没有变化"
合成校准图(4 处已知改动)4 个区域全部对上;其中"底部黄条消失"与"红圆移到右下"因膨胀被合并为 1 个区域(已知取舍)

成本

图像本身很便宜:provider 把每张请求图归一到 ≤384 视觉 token,不管原图多大。早先说的 "每次看图约 1000-1500 token"是错的

贵的是让模型把图"读成文字"。走转录兼容路径时,那个模型每次会先产出大量推理 token (实测 788 ~ 6621 字)才输出正文。所以默认路径根本不调用第二个模型——图直接交给当前模型看。


边界:它做不到什么

这一节是本文档最重要的部分。

1. 它是描述器,不是量具

实测:一条 30 px 高的色带被读成"40–45 px",误差约 40%

  • ✅ 可靠:谁比谁高、哪个被置灰、大致在左上还是右下
  • ❌ 不可靠:这两个元素差 8 px 吗、这个间距是 16 还是 12

2. 低对比度差异是危险区

反向验证有效(灰色 vs 高饱和蓝的"禁用按钮"判对不难),但真正的困难——两个相近的灰色、 1 px 错位、微弱色差——正是它会漏掉、或者更糟:自信地编一个的地方。而"帮我看界面哪里 不对"恰恰最需要这些。

3. 形状语义是弱项

实测案例:一个被编辑成"尖顶小房子"的立方体,在 Blender 视口里。

  • 模型看到了"一个非标准形状"、也说了"说明该物体已被编辑过"
  • 但它始终称它为"立方体",从未把网格的编辑归因到物体上

诚实补充:该机位下尖顶本来就不明显,所以这一次漏掉部分可原谅。但"不会把网格编辑归因到 物体"这个模式,不止出现在这一个案例里。

4. 分辨率不是准确度的杠杆,但裁切是

同一张 Blender 全窗图,同一提示词、同一模型,只改分辨率:

输入尺寸选中物体是否说明立方体未被选中编辑痕迹
1942×1030(原生)未明说只说"黑色三角形面"
1295×687(缩小 44.5% 像素)✅ 明确说"未选中"✅ 说"非标准形状…已被编辑过"

缩小版反而更好。 结论:provider 对输入做归一化,全窗图下源图分辨率不影响准确度。 机制见上一节;同一个机制也解释了为什么裁切有效——实测有效细节密度约 2 倍。

5. 只有一帧,没有时间与因果

  • ✅ 它能答:"屏幕现在是什么"
  • ❌ 它答不了:"这个弹窗是不是因为我点了 X 才出现的"

滚动屏幕记忆(screen_memory / screen_watch)曾经是为了补这一块,已在 0.3 删除: 实测没有真实用例,而每录一帧都会往附件库写一个文件。所以现在它确实只看当下

6. 附件库会增长

每次视觉调用都会往 ${DSH_HOME}/attachments 写入一个内容寻址文件,插件无法阻止llm.stream 的图像块需要持久化附件引用)。

实测观察:它会持续增长——一次长时间会话后达到 112 个文件 / 19.58 MB。也观察到过它 自己清零(28 个文件 / 2.1 MB → 0),触发机制至今未查明

0.2.1 曾有一个 vision_storage 工具用来报告/清理这个目录,0.3 已删除(它是诊断件不是能力)。 需要时你可以自己去那个目录看。

7. 仅 Windows

scripts/capture.ps1scripts/imageops.ps1 依赖 PowerShell 5.1、System.DrawingPrintWindowDwmGetWindowAttribute。非 Windows 上会如实报错,不会假装成功。


安装

方式一:作为 profile bundle(推荐,尚未在真实实例上装过

dsh profile add 
 dsh-screen-reader

装完重启 DSH,然后在任意会话里问它"你现在有哪些和屏幕、图片相关的工具?"—— 应当列出两个see_screensee_diff

这是 DSH 生态的标准做法,也是我把它列为推荐首选的原因:一条命令、不用切 preset、对试用者 门槛最低。但我本人没有在真实实例上装过一次(见状态表)。如果你的实例不接受它,请用方式 二,并把完整报错发到 issue。

⚠️ 0.2.0 的 bundle 是坏的,0.2.1 才修好。 我代码审查时发现:lib/screen.jslib/toolbox.js 原来用 new URL('capture.ps1', import.meta.url) 找 PowerShell 助手, 可是发布包里 JS 在 lib/、助手在 scripts/,于是它去找那个不存在的 lib/capture.ps1。 任何 bundle 安装都会在第一次抓屏时报 The argument '.../lib/capture.ps1' to the -File parameter does not exist. 现在改成运行期同时适配两种布局(同一份源码在 preset 与发布包里都对),并且把这个检查写进了 生成脚本的断言(tools/build-lib.mjs)。如果你装的是 0.2.0,请升级。 但要说清楚:我验证的是"路径能解析到真实文件并成功抓屏",仍然没有在真实实例上完整装过一次

方式二:作为 agent preset(已验证可用

lib/scripts/ 放进一个 preset 目录,配好 agent.cordis.yml 后,用该 preset 开一个会话。 preset 的写法与相对路径解析规则见 DSH 文档。

装完先自检

你现在有哪些和屏幕、图片相关的工具?

应当列出两个see_screensee_diff

  • 看得到 → 装好了。接着调用一次 see_screen 抓张图,确认真的能返回图像
  • 看不到 → 插件没有被加载。改用另一种安装方式,或把 DSH 版本、profile 名、完整报错发到 issue

从 0.2 升到 0.3:破坏性变更

0.3 是一次大幅精简。工具从 8 个减到 2 个。

0.2 里的工具0.3原因
see_screen保留给 agent 装眼睛。这是插件的核心
see_diff保留本地精确差分,模型做不到
see_image删除内置 read_image 的较差重复
screen_watch删除实测没有真实用例;每帧写一个附件文件
screen_memory删除同上(时间维度已放弃)
vision_routes删除诊断件
vision_selftest删除从未跑过;且只覆盖差分链,没覆盖 see_screen
vision_storage删除诊断件

升级时要做什么:

  1. 如果你有 prompt 或自动化脚本里写了被删的工具名,改掉。 调用已删工具会得到"未知工具", 不会静默降级
  2. see_screen 的插件声明变了inject['tools','timer'] 变成 ['tools']。用 preset 方式安装且自己写过 agent.cordis.yml 的话不需要改——inject 在插件源码里,不在配置里
  3. preset 行不用改:两行插件仍然叫 ./plugin/screen.js./plugin/toolbox.js, 只是各自少注册了几个工具
  4. 老版本不会自动升级;插件是本地/包安装,没有自动更新通道

没变的: 两个保留工具的参数、返回值、以及 cordis.patch.yml 的插入方式都没动。


隐私

这一节必须说清楚,因为屏幕内容是最敏感的东西。

  1. 不抓屏就什么都不会发生。 0.3 删掉了持续录制,所以没有任何后台抓屏。只有你(或 agent) 显式调用 see_screen 时才截一张
  2. 截图只交给当前会话的模型。 它走的是你平时发图片的同一条通道,受同一套数据策略约束。 插件不会把图发给任何第三方
  3. 磁盘上插件自己只留一份图:固定路径原地覆盖(${DSH_HOME}/vision/screen.png), 插件停止时删除
  4. 但附件库是另一回事:每次视觉调用都会往 ${DSH_HOME}/attachments 新增一个内容寻址 文件,且不会自己清理(实测一次会话积累到 112 个文件 / 19.58 MB)。见边界第 6 条
  5. see_diff 不抓屏:它只读你指定路径的两张图片文件
  6. 建议:在需要时调用、用完不必长期开着——反正它也不会自己抓屏了

设计说明

为什么插件模块零依赖

加载器的规则是相对 specifier 按 preset 目录解析,而裸包名按 harness 安装位置解析——本地 preset 够不到 @deepseek-ai/dsh-tools。所以插件刻意不依赖任何包:工具定义不用 defineTool(...), 而是直接构造它产出的那个运行期结构;schema 用运行期 JSON Schema 原样书写。只 import node:fs / node:url 两个内置模块。

代价:失去 defineTool 的自动参数校验,所以每个 execute 都自己做类型兜底。

为什么要删掉那一半工具

判据只有一条:这件事模型自己能不能做?

  • 能做的(看图、读图里的字)→ 删,交给内置能力
  • 做不到的(看屏幕、精确判定像素变化)→ 留

按这条判据,see_image 是重复,三个诊断件不是能力,持续录制的价值没有被任何真实用例证明 而代价是确定的(附件库无上限增长)。删除后插件从 8 个工具、约 78 KB 源码,缩到 2 个工具、 约 54 KB。

为什么差分在本地算

视觉模型做细粒度找茬很差,而且会编造"看起来合理"的差异。像素级差分是精确的、免费的、还能 给出变化区域的边界框。所以分工是:本地算差异,模型只解释差异。

为什么 see_screen 要返回图而不是文字

曾经它把图交给第二个模型转录成文字再交回来。那条路有三个问题:多一层转述误差、受那个模型的 推理预算折磨(实测常常把全部预算花在推理上导致正文为空)、还要多付一次调用。现在直接把图作为 内容块返回,由当前模型自己看。transcribe: true 保留为纯文本会话模型的兼容路径。

为什么提示词把"主体"放在最前

实测:如果提示词先要求逐字转录文字,模型会为了抄文字忽略画面内容。把"先答主体、文字放最后" 写成顺序要求之后,判断选中物体的准确率从 0/3 变成 2/2。


验证状态:哪些测过、哪些没测过

项目状态
抓屏 / 窗口 / 区域裁切 / DPI 原生分辨率✅ 独立验证(含失败路径与幂等性)
本地像素差分✅ 独立验证(合成图 + 真实照片)
视觉提示词✅ 实测(多轮)
工具把图像直接交给模型(image 内容块)✅ 已实测(探针验证真的送达)
see_screen 的转录兼容路径✅ 实测(多轮,见成本一节)
bundle 方式安装⚠️ 未在真实实例装过。但 0.2.1 修掉了一个必然让 bundle 失败的路径 bug(见安装一节),并已实测"包能导入 + 解析出的 ps1 真实存在 + 用它成功抓屏"
准确率的单一数字⚠️ 只有一次极小样本的字符准确率(见下),不构成基准
screen_watch / screen_memory 的实用价值从未被任何真实用例证明(这是 0.3 删除它们的直接理由)
vision_selftest 的自动打分❌ 从未跑过(已删除)

仍然没有"准确率 92%"这种基准数字。 上面所有"实测效果"都是具体案例,不是基准。

唯一一个勉强算"数字"的东西:一次 5 条会话标题、共 50 个汉字的对照测试里,全屏 1920×1080 读错 1 个字,裁切 346×346 后读错 0 个字。样本太小,不足以称为准确率——它能说明的 只有一件事:全屏下的失效模式是形近字,而裁切能修掉它。


已知问题与后续计划

0.3.0 改了什么:

  • see_diff 会删掉你的输入图(数据丢失,已修)imageops.ps1 的清理规则原来会删除输出目录里 最旧的 N 个文件,不管是谁写的。于是当你要比较的两张图恰好和裁切图在同一个目录时,清理在写完 裁切图之后运行、保护了裁切图、然后把你的 a.png / b.png 一起吃掉。这是静默的数据丢失,而且丢的 正是唯一无法重新生成的东西。现在清理只删本工具自己产出的文件diff*_A.png / diff*_B.png), 并且每个调用点额外显式保护它收到的输入。已验证:输入保住,裁切图仍然不增长
  • 工具从 8 个减到 2 个(判据见设计说明)。删除 see_imagescreen_watchscreen_memoryvision_routesvision_selftestvision_storage
  • 随之移除了变化指纹、限流闸门、滚动缓冲、timer 依赖、并发互斥——以及 see_screen 上一个 从来走不到的分支(它一直以 force=true 调用 observe,所以那些判断本就是死路)
  • see_screen 源码 50.3 KB → 35.1 KB;toolbox 27.5 KB → 19.1 KB
  • 破坏性变更清单见上一节

0.2.1 修了什么:

  • 发布包里的 ps1 路径是坏的(0.2.0 的 bundle 必然启动即失败):改用运行期双布局解析 (同一份源码在 preset 与发布包里都对),并把生成脚本 tools/build-lib.mjs 收进仓库、加上断言
  • capture.ps1 -Out 的裸文件名会污染当前目录:现在裸名字一律解析到 ${DSH_HOME}/vision/ 并自动补 .png;绝对路径行为不变
  • 文档更正:裁切的细节增益从推算的"2.7 倍"下调为实测约 2 倍

后续计划:

  1. 跑一个真正的准确率基准 —— 这是当前最大的空缺。原来打算用 vision_selftest 做,但它 只覆盖了差分链、没覆盖 see_screen 链,而后者才是最需要数字的地方。所以它被删了, 基准要重新设计
  2. @deepseek-ai/dsh-toolsdefineTool 重写工具定义,拿回参数校验
  3. Config schema 接上(现在可调参数是文件头常量)
  4. 修剪 scripts/imageops.ps1 里已无调用方的模式(calib-draw / calib-bad / storage / prune),它们原本服务于已删除的工具
  5. 形状语义:尝试"先定位主体 → 再局部放大"的两段式,它已被证明对选中判断有效

已知的设计妥协:

  • lib/screen.jslib/toolbox.js 之间有约 40 行重复的视觉路由与错误处理。原因见文件头 注释:加载器按 URL 缓存模块,用相对 import 会把两个插件的生命周期绑在一起
  • 差分边界框比真实变化大 30–45 px(可调 -Padding / -Dilate / -GridW
  • 时间维度(滚动屏幕记忆)已被放弃。如果将来有真实用例,可以从 git 历史里找回 (git show 98306ac5:lib/screen.js

如何反馈

这个插件是实验性的,而且它自己承认了不少做不到的事。我最想要的反馈是"它在你这里错在哪", 不是赞美。

发 issue 时请带上:

  1. 安装方式(bundle / preset)与 dsh --version
  2. 你调用的工具与完整参数
  3. 完整的返回或报错(不要只贴一句"不工作")
  4. 如果涉及看图:最好把那张图也附上。没有图我无法判断是插件的问题还是模型的问题

许可

MIT。见 LICENSE


与其它插件的关系

插件定位关系
内置 read_image读一张图片文件see_image 曾是它的重复实现,已在 0.3 删除
@liustack/modlens看图问答、输出结构化 JSON(★3.9k、L5、Gold)在"精一张图问问题"上比本插件成熟。本插件不打算在这一点上竞争
本插件给 agent 装眼睛 + 精确判定像素变化独特之处是"agent 自己看"和"本地精确差分",不是"看图问答"