DeepSeek Harness Hub
← 返回列表

grelvan/dsh-ocr-local

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

给 DeepSeek HarnessWeb 端装一个本地 OCR 兜底:当会话路由到的模型不支持图片输入时,

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

DeepSeek Harness 本地 OCR 插件:粘贴图片,PP-OCRv5 + ONNX Runtime 识别文字,完全离线 | Local OCR plugin for DeepSeek Harness — paste an image, get its text via PP-OCRv5 + ONNX Runtime, fully offline.

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

README

dsh-ocr-local

English ·

license GitHub

给 DeepSeek Harness(Web 端)装一个本地 OCR 兜底:当会话路由到的模型不支持图片输入时,
把图片里的文字读出来给模型;模型能看图时,插件完全静默、不插手。

识别引擎是 PP-OCRv5 + ONNX Runtime,纯 CPU、完全离线,图片不会离开你的电脑。

插件什么时候生效,什么时候静默

这是理解本插件最重要的一张表。判定基于当前会话实际路由到的模型声明的输入能力
(inputModalities),而不是猜:

| 会话路由到的模型 | 插件行为 | 模型实际拿到什么 |
| --- | --- | --- |
| 明确声明不支持图片(如纯文本模型) | ✅ 生效:图片存到本地缓存,并注入路径提示 → 模型调 ocr_image 识别 | 文字。Harness 对这类模型只给一句 [image omitted because this model accepts text only; …],没有任何路径,所以没有本插件模型就完全读不到图 |
| 明确声明支持图片(多模态模型) | 🔇 静默:不存缓存、不注入提示 | 图片本身。Harness 还会在图片前附一条只读副本路径,模型想逐字核对时可以直接对那个路径调 ocr_image |
| 无法确定(provider 未注册 / 查询失败 / 没声明模态) | 🔇 静默 | 按 Harness 原样处理。宁可不打扰,也不在一个可能能看图的模型上多塞提示 |

"无法确定就静默"是刻意的。如果你确实需要无条件介入,把 autoOcr 设成 'always'(见下方配置)。

关于「未登记的 model id」:DeepSeek 适配器对没写进模型目录的 id 会显式返回
inputModalities: ["text"],所以这类纯文本路由会被上表第一行正确覆盖。
其它 provider 若对未声明模型返回「无模态信息」,则落到第三行(静默)。

⚠️ 在 Web 设置页配置的模型,能力声明可能是空的

插件的判定完全依赖配置里声明的 inputModalities,而不是模型的真实能力。
而 Web 设置页不提供这个字段——这是最容易踩空的一处,单独说清楚。

声明写在 ~/.dsh/settings.yaml 的 llm-deepseek.models[] 里:

llm-deepseek:
models:
- id: deepseek-v4-pro
name: DeepSeek-V4-Pro
contextWindow: 1000000
inputModalities:        # ← 决定插件介入还是静默的就是这一项
- text

Web 模型编辑器只暴露 4 个字段:id / name / contextWindow / maxTokens。
inputModalities 不在其中,所以在页面上新增模型时,写回的条目里没有这一项,
而配置 schema 会给它兜底成 ['text']。

| 在 Web 页面上做的事 | 对该模型能力声明的影响 |
| --- | --- |
| 新增一个模型 | 条目里没有 inputModalities → 按纯文本处理 |
| 编辑已有模型的 name / 窗口 / tokens | 原有声明原样保留(页面的 patch 是「展开合并」语义,不会删掉它不认识的字段) |

兜底成纯文本会怎样:

- 该模型确实不支持图片 → 正好是插件该介入的场景,不用管。
- 该模型其实支持图片(自建 vision 端点,或想把某个多模态模型接进来)→ 插件会多此一举地
介入,图片被 Harness 投影成 [image omitted …] 占位符,本来能看图却看不到。
这时必须手工补声明:

inputModalities:
- text
- image

改完不用重启:settings.yaml 是热加载的(文件监听 + 去抖),保存即生效。

声明要满足的约束(写错会在加载时直接报错,不会静默忽略):

- 必须是非空数组,元素只能是 text / image,且不能重复。
- 声明了 image 才能写 imagePixelBudget(像素预算,可填 low 或正整数)与 imageMaxBytes
(单图字节上限)。纯文本模型写了这两个字段会直接报错:
text-only catalog model "xxx" cannot declare image request limits。

非 DeepSeek 的 provider 卡片有自己的字段集合,能力声明的字段名以该 adapter 的配置 schema 为准。

顺带说清一个相关语义:settings.yaml 里的 models 是整体覆盖内置目录的,
而模型选择器显示的正是这个数组(适配器的 listModels 直接映射它)。
所以在 Web 页面上动过模型列表之后,选择器里有哪些模型完全以这个数组为准——
把 deepseek-v4-flash-vision-exp 删掉了,就一个多模态模型都切不过去了
(本机默认是保留的)。

为什么插件要动模型的"能力声明"

Harness 在你按下发送的那一刻(prompt 准入阶段)就会读模型声明的图片能力:只要声明里
没有 image,它会在消息进入会话之前直接拒绝,并提示
「当前模型不支持图片,请切换支持图片的模型」。图片进不了会话,插件也就永远收不到
user/message 事件——本地 OCR 兜底在这条路径上就成了一行死代码。

所以插件会在这一个方法(llm.resolveModelInfo)上给纯文本模型补上 image 声明,
Grant admission. This will not actually send the image to a text-only model: the request is constructed by the adapter according to its own model catalog, and before the image is put on the wire it will still be projected by the Harness into the text [image omitted because this model accepts text only; attachment sha256:…]. In other words, the plugin extends "admission", not "capability".

If you don't want this behavior, set autoOcr to false—then the plugin won't even install this layer of wrapping.

Relationship with vision models

The two paths do not interfere with each other, because they operate at different layers:

- The plugin only handles local OCR: it saves the image locally and prompts the model to call ocr_image.
- Whether the image should be sent to the model is determined by model capability / client configuration; the plugin does not intervene.

So under a multimodal model you don't need to do anything for this plugin: it will step aside on its own. If you want to manually make it read the text in an image, you can at any time directly tell the agent "use ocr_image to read this image".

Quick start

Step 1: Install the plugin

DSH profiles are isolated from each other, so the plugin must be installed into the profile you want to use (on the Web side this is usually called web).

First confirm that you can invoke the dsh CLI—it has several equivalent entry points, choose one based on what you have on hand (the commands below all follow this):

| Your situation | How to invoke |
| --- | --- |
| dsh command is in PATH | dsh plugin … |
| In the harness source repository (pnpm dsh is a script bundled with the repository) | cd  && pnpm dsh plugin … |
| Same as above, but you want to use the build output and not start tsx | cd  && node apps/cli/lib/bin.js plugin … |
| Directly use the CLI package published on npm | npx -y @deepseek-ai/dsh@alpha plugin … |

⚠️ Two things that easily get you stuck:
1. npx -y @deepseek-ai/dsh without a version tag may fail to invoke—the latest of this package on npm is stuck at the prerelease 0.1.5-rc.1 (while the source repository is already at 0.1.6-alpha.1). Either explicitly write @alpha, or directly use the entry point of the source repository; the version should preferably match your profile.
2. dsh plugin internally forwards through pnpm, so pnpm must be in PATH (if missing, it will report pnpm not found on PATH).

Install the stable version from npm:

dsh plugin --profile web add dsh-ocr-local

You can also install directly from GitHub (if you want to follow the latest commit, or when npm is unreachable):

dsh plugin --profile web add github:grelvan/dsh-ocr-local

Or install after cloning locally:

git clone https://github.com/grelvan/dsh-ocr-local.git
dsh plugin --profile web add ./dsh-ocr-local

When modifying code for development, installing a symlink with link: is faster—dsh plugin --profile web add link:./dsh-ocr-local, after which changes to files in the repository take effect immediately without reinstalling.

After installation, restart dsh for the plugin to take effect.

Step 2: Prepare the recognition engine (only once)

Run this one command in your own terminal (replace web with your profile name; pointing directly to the cloned repository also works):

python3 ~/.dsh/profiles/web/node_modules/dsh-ocr-local/ocr/setup.py

It will automatically complete three things. The recognition model is released together with the plugin package, so there is no need to download it—therefore in practice you only need to install the Python dependencies (onnxruntime / numpy / opencv, about 85MB; using a domestic mirror usually takes 1-3 minutes):

[setup] dsh-ocr-local —— local OCR environment preparation
A total of 3 steps, about 2-6 minutes the first time (the bulk is downloading); each step prints progress, so you don't have to wait idly.

[setup] 1/3 Preparing the Python virtual environment ...
[setup] 2/3 Installing dependencies (about 85MB)...
Collecting onnxruntime ...
[setup] 3/3 Downloading the recognition model (about 21MB, skipped if already downloaded)...
[1/3] PP-OCRv5_mobile_det.onnx
47% (2MB/4MB) [direct connection]

After installation it will print a self-check command. After that, every recognition completes locally in seconds and no longer needs the network.

What to do if the network is bad (the script will try to save itself; the rest depends on these switches):

| Situation | What to do |
| --- | --- |
| GitHub is reachable but very slow | It will switch sources based on speed: if it stays below 200KB/s it switches to the next one, and the log will state "source X is too slow (80KB/s), switching to the next one" |
| GitHub is completely unreachable | It will automatically fall back: direct connection → ghproxy → gh-proxy → ghfast. If you want to pin one, set DSH_OCR_MODELS_MIRROR=https://ghproxy.com/ |
| Everything is slow overall | Adding a proxy is fastest: https_proxy=http://127.0.0.1:7892 http_proxy=http://127.0.0.1:7892 python3 …/ocr/setup.py |
| PyPI is slow | Dependencies already use the Tsinghua mirror by default; to change the source set DSH_OCR_PIP_INDEX= (set to empty = official source) |
| Want to adjust the speed threshold | DSH_OCR_MIN_SPEED_KBPS=500 (default 200) |

If all sources are slow, the last round will stop switching sources and directly finish downloading it—it won't spin forever switching sources.
If interrupted midway, just rerun it; files that have already been downloaded and passed sha256 verification will be skipped.

⚠️ Why is it not recommended to tell the agent "install it with ocr_setup"?
When the plugin executes Bash it may be in a restricted sandbox (network and writable directories are both tightened), while installing the environment precisely requires downloading dependencies and writing the venv and model directories. In practice, in the sandbox it gets stuck on pitfalls like "the uv cache directory is read-only", and the model repeatedly tries and fails, and it may still not succeed after several minutes. Let it run in your own shell, and it will succeed in one go.
The ocr_setup tool is still available (it calls the same script), but its success rate is low in a restricted environment.

After installation, send any image to the agent and say "recognize this image".

Step 3: Start using it

Method A: Paste a screenshot (most common)
在 Web 输入框里按 Ctrl+V / Cmd+V。图片走浏览器原生的附件流程进入会话,
之后按上面的表自动分流:纯文本模型 → 本插件兜底;多模态模型 → 模型直接看图。

方式 B:告诉 agent 图片路径

把图片文件的绝对路径(或 ~/…)发给 agent,说「识别这张图片」。

方式 C:直接用附件 sha256(模型自己会做的事)

纯文本模型收到的图片会被 harness 换成一句占位符:

[image omitted because this model accepts text only; attachment sha256:2cd17c8d…]

这串 sha256 就是线索——ocr_image 的 ref 参数认它(8 位前缀就够,如 2cd17c8d),
插件据此从本地缓存取图。工具描述里已写明这一点,模型通常第一次就调对,
不需要去 find 或者翻 ~/.dsh/attachments/。

能识别什么 / 有什么限制

| ✅ 擅长 | ⚠️ 效果一般 |
| --- | --- |
| 截图、报错弹窗、聊天记录 | 极小的字(如 4px)可能有个别错字 |
| 中文 + 英文混排、长段落 | 复杂背景、艺术字、手写体 |
| 暗色主题截图(自动反色处理) | 模糊或严重压缩的图片 |

识别结果里,字太小或置信度低的行会标注 ⚠,方便你判断哪些字不能全信。

为什么“逐字保真”这件事仍然值得用本地 OCR

多模态模型看图和本地 OCR 读图,拿到的信息量不一样:

| | 多模态模型直接看图 | 本地 OCR |
| --- | --- | --- |
| 送进模型的分辨率 | 受限于 Harness 的每图像素预算(默认 640,000 px)。1920×1080 的截图会被压到约 1066×600 | 检测阶段最长边 736px,但识别阶段是从原图裁块,小字还会先放大(字高下限 20px,最多 6×) |
| 输出 | 模型的一段转述,不告诉你它哪里不确定 | 逐行文本 + 每行置信度 / 字高 / 坐标框,低置信行标 ⚠ |
| 适合 | 看懂截图里发生了什么 | 抄代码、对报错信息、核 hash、读表格数字 |

诚实的边界说明:检测阶段的最长边限制是 736px,比多模态链路的约 1066px 更保守,
所以 4K 全屏截图里极小的字,本插件也可能漏检。准确的说法是
“检得到的行,认得更准”,而不是“分辨率无上限”。

配置(可选,默认不用动)

配置文件:~/.dsh/profiles/web/cordis.patch.yml

- insert:
- id: ocr
name: 'dsh-ocr-local'
config:
autoOcr: true                                   # 见下表
pythonPath: ~/miniconda3/envs/ocr/bin/python   # 可选:指定 Python
modelDir: ~/.dsh-ocr/models                     # 可选:模型目录
cacheDir: ~/.dsh/ocr/cache                      # 可选:图片缓存目录
maxCacheFiles: 300                              # 可选:图片缓存最多文件数
maxCacheAgeDays: 30                             # 可选:图片缓存保留天数

autoOcr 三态:

| 值 | 行为 |
| --- | --- |
| true(默认) | 自动判定:只有模型明确不支持图片输入时才介入 |
| false | 完全关闭自动介入。仍可手动让模型调 ocr_image |
| 'always' | 无条件介入(即使模型能看图也注入提示)。旧版 0.3.x 的行为,排查问题时可用 |

常用环境变量:

| 变量 | 作用 |
| --- | --- |
| DSH_OCR_MODELS_MIRROR | 模型下载镜像前缀(国内下载慢时设,如 https://ghproxy.com/) |
| DSH_OCR_PYTHON | 指定 OCR 用哪个 Python(默认自动找) |
| DSH_OCR_MODELS | 模型存放目录(默认 ~/.dsh-ocr/models) |

常见问题

Q:粘贴了图片,但模型说看不到图?
先确认当前模型是不是纯文本模型(这是本插件唯一该生效的场景)。如果是,检查引擎是否就绪
(对 agent 说「用 ocr_setup 检查 OCR 环境」)。如果用的是多模态模型,模型直接看图即可,
本插件按设计就是静默的。

Q:发送后弹出「当前模型不支持图片,请切换支持图片的模型」?
那是 Harness 的 prompt 准入拦下的,发生在插件能看到消息之前。0.4.3 起插件会把这层准入
放行(见上文「为什么插件要动模型的能力声明」),如果你还在旧版本、或把 autoOcr 设成了
false,就会看到这条提示。装 0.4.3+ 并保持 autoOcr 开启即可。

Q:就想直接看图,不想走本地 OCR?
Harness 自带多模态模型(如 DeepSeek-V4-Flash-Vision-Exp),在模型选择器里切过去就行,
本插件会自动退场。

Q:我在 Web 页面上加的模型明明能看图,插件却来插一脚?
页面上新增的模型条目没有 inputModalities,schema 把它兜底成 ['text'],插件于是按
「纯文本模型」介入——图片反而被投影成占位符,看不到图了。按上文「在 Web 设置页配置的模型」
一节手工补上 inputModalities: [text, image] 即可;保存后热加载生效,不用重启。

Q:在页面上编辑了模型,之前手写的 inputModalities 会丢吗?
不会。页面的字段补丁是「展开合并」语义,它不认识的字段会原样保留。会被漏掉的只有新增的条目。

Q:提示「环境未就绪」/「缺少依赖」?
对 agent 说「用 ocr_setup 安装 OCR 环境」即可自动修复;或手动运行
python3 ~/.dsh/profiles/web/node_modules/dsh-ocr-local/ocr/setup.py。

Q:模型下载很慢或一直失败?
脚本会自己处理,而且「慢」也会换源(不只是失败才换):持续低于 200KB/s 就换下一个,
日志里会写「源 直连 太慢(80KB/s  --registry=https://registry.npmjs.org/
2. 查镜像同步到哪了(Win10+ 自带 curl):
curl -s https://registry.npmmirror.com/-/package/dsh-ocr-local/dist-tags
3. 等它同步完(通常几十分钟内),之后裸 add 即可。
4. 不想等:打开 https://npmmirror.com/package/dsh-ocr-local,点右上角 SYNC 按钮
手动触发同步,一般几秒到一两分钟生效(实测有效)。

Q:安装时看着没反应、像卡住了?
0.4.3 起每一步都有实时进度——依赖安装会显示 pip 的输出,模型下载会显示
47% (2MB/4MB) [直连] 这种进度条和当前用的源。如果确实长时间没有任何新行,
那是真在等网络,按上一条加代理;也可能是走到了一个不通的源,30 秒内会自动换下一个。

Q:系统提示 pip externally-managed-environment(PEP 668)?
不要加 --break-system-packages。直接用 ocr/setup.py——它会自动创建虚拟环境,
绕开系统 Python 的限制。

Q:装环境时报 No module named 'pip',或者 venv 根本建不出来?
系统缺 python3-venv(Deepin / Debian / 部分 Ubuntu 会把 ensurepip 剥掉)。这种情况下
python3 -m venv 会报错退出却留下一个没有 pip 的残缺目录——0.4.3 起 setup.py 会实测
import pip 把它认出来,删掉重建,并自动改用 uv venv --seed(uv 自带 pip,不依赖系统
ensurepip)。直接重跑一次 python3 ~/dsh/dsh-ocr-local/ocr/setup.py 即可。

Q:sudo apt install python3-venv 没权限怎么办?
不用 sudo,装个 uv 就行:

curl -LsSf https://astral.sh/uv/install.sh | sh
python3 ~/dsh/dsh-ocr-local/ocr/setup.py    # 重跑,会自动走 uv

uv 装在 ~/.local/bin,哪怕那个目录不在 PATH 里,setup.py 也会去找。

Q:识别结果有错字?
看输出里的 ⚠ 标注。字太小时模型确实会看走眼:把原图放大一点再试,
或让 agent 把对应行再确认一遍。

Q:为什么插件不拦截我的粘贴了?
0.4.0 起不再拦截。Web 输入框原生就把粘贴的图片收进附件流程,拦截只会让
本来能看图的模型反而看不到图。旧配置里的 pasteToPath 已失效,可以删掉。

工作原理(一句话)

Web 端粘贴的图片按原生流程进入会话成为附件 → 插件先把 prompt 准入放行(给纯文本模型补
image 声明,否则图片进不了会话)→ 监听 user/message 事件 → 查询当前路由模型的
inputModalities → 只有明确不支持图片时,把图片存到 ~/.dsh/ocr/cache 并向模型注入
路径提示 → 模型调 ocr_image → 本地 PP-OCRv5 模型(ONNX Runtime,纯 CPU)→ 文字。
模型第一次使用时下载到 ~/.dsh-ocr/models,之后完全离线。
更多细节见 docs/usage.md。

升级

dsh plugin --profile web update dsh-ocr-local

dsh 可以用第 1 步表格里的任一入口(源码仓库里就是 pnpm dsh plugin --profile web update dsh-ocr-local)。

从本地目录安装的话,git pull 后在插件目录重跑一次 add 即可;用 link: 装的连这步都省了——
改完文件重启 dsh 就是最新代码。

许可

MIT(代码)。识别模型 Apache-2.0(PaddleOCR),安装时自动下载。见 LICENSE。

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

💬 加入 DPharness 群聊

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

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