dsh-plugin-vision
by Tianbaidi
DeepSeek Harness(dsh)的辅助视觉:经外部 OpenAI 兼容视觉端点分析图片并返回文本答案,适配任意主模型
Auxiliary vision for DeepSeek Harness (dsh): analyze images through an external OpenAI-compatible vision endpoint and get a text answer back. Works with any main model —…
安装
dsh plugin --profile web add github:Tianbaidi/dsh-plugin-visionGitHub 源码安装:首次需按提示配置 allowBuilds 构建授权后重试
安装与环境配置指引、插件开发教程见 DSH 中文社区文档 ↗
安装即在你的机器上以你的权限运行第三方代码——它可读写文件、使用凭据、访问网络,DSH 的工具审批不会为插件代码加沙箱。「检测到 manifest」仅代表发现 dsh.bundle / dsh.plugin 清单,不构成兼容性或安全审查;安装前请审阅源码,不熟悉的插件先在不含密钥的环境试用。
README
目录
- 为什么需要它
- 工作原理
- 四个能力
- 1. vision_analyze 工具
- 2. 图片附件转述(Web 界面贴图)
- 3. deepseek-vision provider 路由(Web 界面贴图,根治)
- 4. opencode-vision provider 路由(OpenCode Go + 图片转述)
- 免第二端口:把插件装进你现有的 GUI
- 4. 图片归档(贴图自动存盘 + index.json)
- 试运行
- 配置
- 多转述端点(注册表)
- 开发
- 更新日志
- v0.3.0 —— 转述端点注册表
- v0.2.0(2026-08-16)—— opencode-vision provider 路由
- v0.1.1(2026-08-16)—— 大修:TTFT 与缓存命中率
- v0.1.0
- 已知限制
- 发布
- License
DeepSeek Harness (dsh) 的辅助视觉插件: 把图片发给外部的 OpenAI 兼容视觉端点,返回文字分析结果。任何主模型都能用——包括纯文本的 DeepSeek。
为什么需要它
dsh 内置的 read_image 工具把图片注入主模型上下文,
要求主模型声明支持图像输入;而 DeepSeek 适配器是 inputModalities: ['text'],所以 read_image 在
DeepSeek 下直接拒绝执行。本插件走互补的辅助视觉路线:把图片发给独立的视觉模型,把回答以文字形式
返回给 agent,不依赖主模型的视觉能力。
read_image(内置) |
vision_analyze(本插件) |
|
|---|---|---|
| 图片去向 | 主模型上下文(原生) | 外部视觉端点 |
| 主模型需支持视觉 | 需要 | 不需要 |
| 结果 | 模型直接看到图片 | 纯文字回答 |
工作原理
- 读取图片——本地文件路径或
http(s)链接(有大小上限、魔数 MIME 嗅探)。 - 转成 base64 data URL。
- 向端点发 OpenAI 兼容的
chat/completions请求,content 为[{type:text},{type:image_url}]。 - 返回视觉模型的回答(思考型模型无正文时回退到
reasoning_content)。 - 转述结果缓存(v0.1.1 新增):同一图片(按内容 sha256 键控,跨粘贴/
read_image/恢复历史等 入口统一去重)+ 同一问题,只调用一次视觉端点;后续请求直接复用缓存文本,并把视觉端点采样 温度固定为0(temperature)保证输出确定。效果:- 图片进入会话历史后,后续每一轮不再重复调用视觉端点 → 首字 token 时间(TTFT)保持平稳;
- 转述文本永不变化 → 主模型(DeepSeek)的前缀缓存不会因转写波动而断裂,缓存命中率不衰减。
四个能力
1. vision_analyze 工具
把图片(本地路径或 URL)发给配置的视觉端点,返回文字回答——任何主模型都能用,包括纯文本的 DeepSeek。
2. 图片附件转述(Web 界面贴图)
Web 界面支持粘贴/拖拽图片,但 DeepSeek 适配器会拒绝图片内容(UNSUPPORTED_CONTENT)。
本插件挂钩 agent/pre-step(官方文档里"替换进入 step 的消息"的接缝):当用户消息携带图片块时,
调用视觉端点描述图片,在消息到达模型之前把每个图片块换成 [User-attached image description]
文字块。DeepSeek 永远只看到文字,贴图就能用了。当主模型路由声明支持图像输入时,自动跳过转述,
走原生视觉。用 attachImages 开关(默认 true)。转述失败会降级为明确提示,不会卡住对话。
attachMode 控制问视觉模型的方式:
auto(默认):用户随图写了文字时,把用户的原文原样作为问题传给视觉模型—— "这是谁?""翻译图里的文字""这个页面哪里有问题?"都会被直接作答,另附一行图片概要便于追问; 没有文字时退化为通用描述。describe:永远用通用描述提示词,忽略用户文字。
3. deepseek-vision provider 路由(Web 界面贴图,根治)
Web 界面的上传预检在所选模型未声明图像输入时会拒绝图片——所以在普通 DeepSeek 路由下,粘贴的图片
根本到不了 agent。本插件注册一个 deepseek-vision provider:一个 DeepSeekAdapter 子类,声明
支持图像输入(预检放行),在请求时把附件图片转述为文字,再委托给真正的 DeepSeek chat-completions
端点。主模型仍然是 DeepSeek——同一端点、同一 key、同一批模型。在模型选择器里选 "DeepSeek (vision via
plugin)",然后照常粘贴/拖拽图片即可。用 deepseekVision.enabled 开关(默认 true),provider id
为 deepseekVision.providerId(默认 deepseek-vision)。转述走上面的内容哈希缓存,图片在历史中
每轮都只会命中缓存文本,不再重复调用视觉端点。
4. opencode-vision provider 路由(OpenCode Go + 图片转述)
同样的包装思路,套在 OpenCode Go(opencode.ai/zen/go)上——它是 dsh
内置 pi-ai 库的原生 provider。本插件注册一个 opencode-vision provider:一个
PiAiAdapter 子类,提供 opencode-go 全部模型(deepseek-v4-flash/pro、qwen3.7-max/plus、
kimi-k3、glm-5.1/5.2、minimax-m3、grok-4.5 等),声明支持图像输入(Web 上传预检放行),并在
请求时把附件图片转述为文字(针对纯文本模型)。目录里标记为原生支持图像的模型(如 qwen3.7-plus、
minimax-m3)则原样透传图片,不做转述。在模型选择器里选 "OpenCode Go (vision via
plugin)",然后照常粘贴/拖拽图片即可。
用 opencodeVision.enabled 开关(默认 true),provider id 为 opencodeVision.providerId
(默认 opencode-vision)。凭据与基础 opencode-go 路由共用 OPENCODE_API_KEY,在 Models
页存一次,两条路由都能用。
安装版 pi-ai 目录还没收录的模型(账户可能支持更新的)可以用 opencodeVision.extraModels
直接声明——必须指定线上协议,端点与容量默认走 OpenCode Go 网关:
config:
opencodeVision:
extraModels:
- id: glm-5.3
api: openai-completions
contextWindow: 1000000
maxTokens: 131072
- id: qwen3.8-max
api: anthropic-messages
如果还想启用不带视觉的原始 opencode-go 路由(含全部模型),在 $DSH_HOME/settings.yaml 加
一段 llm-pi-ai 配置(热加载,无需重启):
# $DSH_HOME/settings.yaml
llm-pi-ai:
providers:
opencode-go:
apiKeyEnv: OPENCODE_API_KEY
displayName: OpenCode Go
# 去掉 `models` 则直接提供安装版 pi-ai 目录里的全部模型
models:
- id: deepseek-v4-flash
- id: deepseek-v4-pro
- id: qwen3.7-plus
- id: grok-4.5
# ……其余你想用的 opencode-go 模型
免第二端口:把插件装进你现有的 GUI
视觉 API key 优先从 harness 凭据服务解析(Web 界面已存的 key),回退到环境变量——GUI 里不需要
额外导出。把插件写进 home 级用户层 patch(对每个 profile 生效,包括你正在用的 web GUI,
无需第二个服务/端口):
# $DSH_HOME/cordis.patch.yml
- insert:
- id: vision
name: 'file:///<path-to-plugin>/lib/index.js'
把视觉 API key 加进已存凭据(或导出 VISION_API_KEY),重启一次 GUI 即可。注意用编译后的
lib/index.js——发布版 CLI 能加载 .ts 入口,但解析不了 src/ 里 .js 后缀的兄弟模块。
4. 图片归档(贴图自动存盘 + index.json)
每张粘贴的图片自动保存到 ~/.dsh/image-archive/,按日期编号命名
(2026-08-14_120331_001.png),写入 index.json 清单(路径、sha256、大小、来源、可选备注),
并把位置标注给模型([图片已存档: …])。配套两个工具:
image_archive—— agent 把重要图片(用户喜好、票据、关键数据)归档到指定文件夹并可加备注: 保存为<archiveDir>/<folder>/<名称或日期编号>.png并更新index.json。image_archive_find—— 按名称/文件夹/备注检索清单。
用 archive.enabled(默认 true)和 archive.dir(默认 ~/.dsh/image-archive)配置;
按附件 id 去重。
试运行
把 bundle 装进任意 profile(prepare 脚本会在安装时构建):
dsh plugin --profile web add github:Tianbaidi/dsh-plugin-vision
把视觉 API key 存进凭据或环境变量(VISION_API_KEY),重启 GUI。粘贴/拖拽图片提问即可,
或直接用 vision_analyze 工具。要用 opencode-vision 路由,再存一份 OPENCODE_API_KEY
(Web Models 页两种 key 都能存)。想用开发 overlay?指向你的本地检出:
- insert:
- id: vision
name: 'file:///<path-to-plugin>/lib/index.js'
Windows 注意:overlay 里插件路径必须是
file://URL(file:///D:/...%20...),不能用裸的D:/...路径——ESM loader 会把后者当成d:协议拒绝。
配置
| 键 | 默认值 | 含义 |
|---|---|---|
baseUrl |
(空,必须配置) | OpenAI 兼容 chat-completions 端点基地址。 |
apiKeyEnv |
VISION_API_KEY |
存放 API key 的环境变量名(或已存凭据)。 |
model |
(空,必须配置) | 端点上的视觉模型名。 |
visionEndpoint |
(空) | 活跃转述端点 id(对应 visionEndpoints 里的某条)。为空或 id 不在注册表时,回退到顶层 baseUrl / apiKeyEnv / model 单端点。 |
visionEndpoints |
[] |
命名转述端点注册表。每条需 id,可覆盖 baseUrl / apiKeyEnv / model / timeoutMs / maxImageBytes / temperature / seed / attachMode 中的任意子集;未设置的字段继承顶层值。id 同时充当转述缓存命名空间——切换端点后不会复用另一个模型的转述文字。 |
timeoutMs |
120000 |
单次调用超时(思考型视觉模型需要余量)。 |
maxImageBytes |
8388608(8 MB) |
图片大小硬上限。 |
temperature |
0 |
视觉端点采样温度。0(默认)保证同一图+同一问题输出确定,维持主模型前缀缓存稳定。 |
seed |
(未设置) | 可选固定随机种子(取决于端点是否支持)。 |
attachImages |
true |
把粘贴的图片转述为文字(供纯文本主模型使用)。 |
attachMode |
auto |
auto:把用户自己的提示词传给视觉模型;describe:始终用通用描述。 |
transcriptionCache.enabled |
true |
转述结果缓存:同一图片(按内容 sha256)+ 同一问题只调用一次视觉端点,后续请求直接复用缓存文本。修复 TTFT 膨胀与缓存前缀断裂。 |
transcriptionCache.file |
~/.dsh/vision-transcription-cache.json |
缓存落盘文件(原子写入,重启不丢)。 |
transcriptionCache.maxEntries |
1000 |
缓存条目上限,超出后淘汰最旧条目。 |
deepseekVision.enabled |
true |
注册 deepseek-vision provider(DeepSeek + 图片转述)。 |
deepseekVision.providerId |
deepseek-vision |
模型选择器里显示的 provider 路由 id。 |
opencodeVision.enabled |
true |
注册 opencode-vision provider(OpenCode Go + 图片转述)。 |
opencodeVision.providerId |
opencode-vision |
模型选择器里显示的 provider 路由 id。 |
opencodeVision.apiKeyEnv |
OPENCODE_API_KEY |
存放 OpenCode Go API key 的环境变量名(或已存凭据)。 |
opencodeVision.displayName |
OpenCode Go (vision via plugin) |
选择器/配置界面显示的 provider 名。 |
opencodeVision.extraModels |
[] |
合并进路由的未收录模型(比安装版 pi-ai 目录更新的账户模型);每条需 id + api(anthropic-messages / openai-completions / openai-responses),可选 baseURL / contextWindow / maxTokens / input / reasoning。 |
任意 OpenAI 兼容视觉端点都可用。默认值故意留空,不假定任何厂商:
| 厂商 | baseUrl |
model |
说明 |
|---|---|---|---|
| 智谱 GLM(免费档) | https://open.bigmodel.cn/api/paas/v4 |
glm-4.6v-flash |
免费注册即用,零成本开箱 |
| 阿里百炼(含 token plan) | https://dashscope.aliyuncs.com/compatible-mode/v1(或你的套餐端点) |
qwen3.7-plus / qwen-vl-max |
有套餐就用套餐端点 |
| Ollama(本地离线) | http://localhost:11434/v1 |
qwen3-vl:4b |
无需 API key |
| 任意 OpenAI 兼容网关 | 网关的 /v1 |
网关的视觉模型 | — |
按部署配置(profile 的 cordis.patch.yml 或插件行的 config):
- id: vision
name: dsh-plugin-vision # 或 file:// 指向 src/index.ts
config:
model: qwen-vl-max
timeoutMs: 120000
多转述端点(注册表)
一个端点不够用(比如 token plan 快到期、想用 OpenCode Go 视觉模型兜底)时,可以注册多个命名端点,用 visionEndpoint 选当前生效的那个。每条预设未设置的字段继承顶层值,所以通常只需写差异项:
- id: vision
name: dsh-plugin-vision
config:
# 当前生效的转述端点(plan 到期时改这里即可)
visionEndpoint: aliyun
visionEndpoints:
- id: aliyun
baseUrl: https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1
apiKeyEnv: ALIBABA_CODING_PLAN_API_KEY
model: qwen3.7-plus
- id: opencode-kimi
baseUrl: https://opencode.ai/zen/go/v1
apiKeyEnv: OPENCODE_API_KEY
model: kimi-k3
temperature: 1 # 必须:kimi 模型拒绝默认的 temperature 0
- id: opencode-qwen
baseUrl: https://opencode.ai/zen/go/v1
apiKeyEnv: OPENCODE_API_KEY
model: qwen3.7-plus
OpenCode Go 候选模型已用真实 key 对着线上端点实测(2026-08-16,带图 payload):目录里声明支持图像的 go 模型,目前只有 kimi-k3、kimi-k2.7-code 能真正当转述端点,且两者都必须 temperature: 1——插件默认的 temperature: 0 会被拒("invalid temperature: only 1 is allowed for this model")。mimo-v2.5 能答但偶尔返回空文本;minimax-m3 能答但把推理内联进正文(<think>…)。kimi-k2.6、qwen3.6-plus、qwen3.7-plus 目前上游 503 "Endpoint is unavailable"(纯文本和带图都 503,直连/代理都试过)——不是客户端问题,等网关恢复再试。切换端点不会吃到另一个模型的旧缓存——每条预设的 id 就是转述缓存的命名空间。
开发
pnpm install # 安装已发布的 @deepseek-ai peer 依赖
pnpm typecheck
pnpm test # 58 个 vitest 用例:MIME 嗅探、payload、解析、图片加载、execute、转述缓存、provider 路由、端点注册表
更新日志
v0.3.0 —— 转述端点注册表
新增 visionEndpoints / visionEndpoint 配置:注册任意数量的命名转述端点,用 visionEndpoint 切换当前生效的那个。每条预设未设置的字段继承顶层配置,所以通常只需写 baseUrl / apiKeyEnv / model。预设 id 同时是转述缓存命名空间——切换端点不会复用另一个模型的缓存文字。activeVisionEndpoint(config) 解析当前端点并已导出。旧式单端点配置(baseUrl / apiKeyEnv / model)原样兼容(无 id → 默认缓存桶,磁盘上已有条目继续有效)。
v0.2.0(2026-08-16)—— opencode-vision provider 路由
新增 opencode-vision provider 路由:在模型选择器里一条入口提供 pi-ai 内置的 OpenCode Go
全部模型目录,声明图像输入(Web 上传预检放行),并对纯文本模型在请求时把附件图片转述为文字
(原生支持图像的模型如 qwen3.7-plus / minimax-m3 原样透传图片)。实现为 PiAiAdapter
子类、直接复用安装版 pi-ai 目录,三种线上协议(openai-completions / anthropic-messages /
openai-responses)与 provider 的推理特化行为全部保留。与基础 opencode-go 路由共用
OPENCODE_API_KEY。opencodeVision.extraModels 可以把目录未收录的账户模型(如
glm-5.3、qwen3.8-max)按显式协议合并进路由;新增 7 个测试(目录列出、extra 合并、图像
声明、转述、原生视觉透传、回放对齐、注册)。
v0.2.0 修复:委托层在线上把 model.provider 改回目录名,导致 pi-ai 在会话回放状态里写的是
opencode-go,而 harness 的 source 记的是 opencode-vision——恢复会话时报 "invalid pi-ai
replay state: provider does not match assistant source"。适配器现在把每条 finish 块的
replayState.provider 对齐到路由 id。修复前写入的会话,可把导出 JSONL 里每条消息的
replayState.provider 改成与 source.provider 一致来修复。
v0.1.1(2026-08-16)—— 大修:TTFT 与缓存命中率
修复 deepseek-vision provider 的两个性能问题(由真实会话数据定位):
- TTFT 膨胀:此前图片一旦进入会话历史,每一轮请求都会同步重调视觉端点(实测首字时间从
~1s 恶化到 22–69s)。新增
transcriptionCache(内容 sha256 + 问题 sha256 键控,落盘~/.dsh/vision-transcription-cache.json),同一 (图片, 问题) 只转写一次,后续直接复用。 - 缓存命中率衰减:此前转写文本非确定性,导致主模型前缀缓存从图片位置起永久断裂(实测命中率
从 99.8% 衰减到 91.1% 且持续下降)。新增
temperature(默认0)与可选seed,保证同一图 输出确定,前缀缓存保持稳定。 - 新增 5 个测试覆盖缓存命中、内容哈希键控(粘贴 vs
read_image归档副本)、持久化、淘汰、确定性载荷。
v0.1.0
初始发布:vision_analyze / vision_reask / image_archive 工具、图片附件转述、deepseek-vision
provider 路由、图片归档。
已知限制
- 图片来源为本地文件路径(相对 harness 工作目录解析)或纯
http(s)链接。远程链接直连抓取,未做 SSRF 加固——如果开放 URL 输入,请限制在可信网络内使用。 - 图片按原样发给端点,超限直接拒绝(不依赖 Pillow 缩放)。超大截图请先压缩。
- 视觉调用产生的 token 费用计入所配置端点的套餐。
- 转述缓存以「图片内容 sha256 + 问题文本 sha256」为键:同一图片换一个问题会重新调用一次视觉端点
(结果仍会缓存)。想强制刷新某张图的转述,可以删掉
~/.dsh/vision-transcription-cache.json中对应条目或整个文件。 - 一旦图片进入会话历史(用户粘贴、
read_image工具结果、恢复的会话),后续每一轮请求都会带着它; 转述缓存确保只有首次需要调用视觉端点,但历史中已存在的图片仍会占用转述后的文本 token。
发布
本项目是 bundle 格式(dsh.bundle.patch),可用 dsh plugin add 安装、在 GitHub 上加
dsh-plugin 话题分享、或 npm publish。完整清单见
配套脚手架仓库里的 PUBLISH.md。
License
MIT
原始 README: https://github.com/Tianbaidi/dsh-plugin-vision/blob/main/README.zh.md ↗
同类插件
查看全部 →
dsh-anchored-standard
两阶段 DeepSeek Harness 预设:先 Minimal 对齐的 bootstrap,再切完整 Standard 工具(Project2 98/99)

PicGo-Core
极致的图片上传引擎,CLI 与 API 双支持

awesome-deepseek-harness
DeepSeek Harness(DSH)及其优秀社区插件的精选指南。

awesome-deepseek-harness
DeepSeek Harness (DSH)生态系统:来自dsh-external/hub和公共dsh-plugin主题的精选插件、工具和基础设施。

AI-Novel-Writer
本地优先 AI 小说创作工作台,提供 Windows/macOS 桌面版与 DeepSeek Harness 插件开发预览,支持角色、大纲、章节蓝图、审稿修稿和本地模型。

mcp-for-stata
MCP-for-Stata:把 Stata 集成进你的 agent