用 DeepSeek-Harness-Vision-Tools 给纯文本 dsh 接上眼睛

前言

DeepSeek Harness(dsh)的核心理念是「一切皆插件」:模型、工具、会话、沙箱和界面都可以换。官方仓库是 deepseek-ai/deepseek-harness。日常驱动它的,往往是 DeepSeek、稠密 Qwen、Llama、Mistral 这类纯文本模型

把截图贴进对话时,会撞上两堵墙。第一堵在发送前:dsh 按路由声明的模态检查附件,文本模型会被直接拒绝,并点出模型名。第二堵更麻烦:如果在文本路由上硬写 input: [text, image],附件能发出去,但上游会在回合中途返回 400 "not a multimodal model"——此时用户消息已经落盘,会话会反复重试一个永远成功不了的请求。

只加一个「看图工具」也解不开第一扇门:模型要先看见图片,才会决定去调工具,而那正是 dsh 拒绝的请求。社区维护者 tonyd2wild 因此做了 DeepSeek-Harness-Vision-Tools:文本模型继续当大脑,视觉模型只负责看,图片字节不到大脑上下文里。本文按插件目录页、GitHub README、examples/dsh.md 和仓库源码核对后整理。

这是什么

DeepSeek-Harness-Vision-Tools 是一款会话与消息类社区插件,由 tonyd2wild 维护,许可证 MIT。目录页与 GitHub 均标注 10 stars;目录收录日期为 2026-08-10,仓库最近推送时间为 2026-08-13。插件包名是 dsh-plugin-visionpackage.json 中的版本为 0.1.0

README 开头写明:这是非官方社区项目,与 DeepSeek AI 无隶属、无背书、非其维护。问题请开到本仓库,不要报到 DeepSeek。社区插件目录 deepseek-harness-plugin.com 是独立站点,也不是 DeepSeek / 幻方的官方应用商店。

它解决的问题可以缩成一句:任意文本模型搭配任意视觉模型,给 dsh 两条进图通道。

入口 机制 谁触发
聊天里附上的图片 视觉代理 shim/vision_shim.py 自动拦截,模型到达前改写
磁盘上的图片文件 analyze_image 工具 plugin/vision/ 智能体按需调用

两条通道互补,不是主备关系。工具接不住聊天附件:附件必须先到模型,模型才会去调工具。代理也捡不起智能体还没放进消息的磁盘文件。图片只发给视觉模型;大脑看到的是 [Image: ...] 这类文字。

核心功能

大脑和眼睛分开,两端都由你选

仓库刻意不写死模型。维护者自己的 DeepSeek 构建和硬件组合别人很难复用,所以配方只留两个槽位:

槽位 放什么 建议
大脑(文本) dsh 已经在跑的任意 OpenAI 兼容文本模型 沿用现有模型
眼睛(视觉) 代理和/或工具去调的本地 VLM fast / detailed 分档

同一套眼睛可以服务两扇门。工具按调用选择后端;代理通过 --vision-model 指定一个:

角色 适用 仓库举例
fast(默认) 颜色、版面、粗内容 约 0.8B 的小 VLM,如 Qwen3.5-0.8B
detailed 小字、细部、偏 OCR 的工作 更大的 VLM,如约 27B 的 Qwen2.5-VL / Qwen3-VL

MODELS.md 给出的 fast 默认是 Qwen3.5-0.8B:Apple Silicon 走 MLX(mlx-community/Qwen3.5-0.8B-MLX-8bit),Windows / Linux / NVIDIA 走 llama.cpp(GGUF + mmproj)。活体占用大约 Mac 统一内存 2–3 GB,或 PC 约 2 GB 显存。不一定两档都开,先跑 fast 即可。

Ollama 目前还不能加载 Qwen3.5 的独立 mmproj,默认 fast 模型不能走 Ollama。若坚持用 Ollama 一行命令,仓库建议改 Moondream 或 Qwen2.5-VL,再把后端指到 http://127.0.0.1:11434/v1/chat/completions

视觉代理:聊天附件在到达大脑前变成文字

代理是一个只依赖 Python 标准库的本地 HTTP 服务,对外说 OpenAI API,夹在 dsh 和文本上游之间:

  1. POST /v1/chat/completions:把每条消息里的 image_url 块换成 {"type":"text","text":"[Image: ...]"};若整条消息都变成文本,再收成普通字符串(有的服务器对 block 数组更挑剔)。
  2. GET /v1/models 以及其他 GET 原样转发,方便 dsh 做模型发现。
  3. 流式响应按字节转发。dsh 每个回合都在流式输出,缓冲会卡住界面。
  4. 视觉失败时降级,不抛死:块变成 [Image: (image could not be analyzed: ...)],回合仍能结束。
  5. 请求体上限 64 MB,因为图片是 base64 内联的。

dsh 的模型路由要指向代理,并声明 input: [text, image]。这句话对代理为真,对文本大脑为假。路由描述的是它正在对话的对象。

analyze_image:磁盘文件由智能体决定何时看

plugin/vision/index.js@deepseek-ai/dsh-toolsdefineTool() 注册模型可见工具。参数来自源码:

参数 是否必填 含义
path 要分析的图片路径
backend fastdetailed(以挂载时实际配置为准)
prompt 问视觉模型的问题,默认 "Describe this image in detail."

工具把文件读成 base64,POST 到对应后端的 /v1/chat/completions,把返回的纯文本写进工具结果。未知后端会抛错并列出合法名称,没有静默回退,避免把 detailed 打成 fast 却看不出来。

读文件优先走 ctx.fs(遵守工作区边界和审批策略)。若退化到 readFileSync,会绕过沙箱,模型理论上能通过这个工具读盘上任意文件。仓库要求:只在受信任、有人值守的机器上依赖这条回退,无人值守前先把门闩上。

安装与启用

插件目录页给出的安装命令如下,在 DeepSeek Harness 终端执行:

dsh plugin add github:tonyd2wild/DeepSeek-Harness-Vision-Tools

目录页同时写了可复现写法:把 commit 哈希接到仓库名后面。

dsh plugin add github:tonyd2wild/DeepSeek-Harness-Vision-Tools#commit

插件以当前 dsh 进程的权限运行,安装时可能执行代码。安装前应检查源代码仓库和许可证。

这个仓库不是「只丢一个 JS 插件」那么简单:代理是独立 Python 进程,工具在 plugin/vision/。要两扇门都工作,需要按 README 把眼睛服务、代理和工具分别拉起来。前置条件(README「What you need」):

  • 已有 dsh 在跑的文本模型(任意 OpenAI 兼容端点)
  • 一台本地 VLM;最小的 fast 档大约还要 2–3 GB 余量
  • Python 3.8+(代理只用标准库,零第三方依赖)
  • pnpmdsh plugin 会调它;只装工具时需要)
  • 视觉运行时:Mac 用 MLX(mlx-vlm),Windows / PC 用 llama.cpp(或 Ollama 跑回退模型)

快速起步:

git clone https://github.com/tonyd2wild/DeepSeek-Harness-Vision-Tools
cd DeepSeek-Harness-Vision-Tools
cp .env.example .env      # 把端点改成你自己的主机
./setup.sh                # 拉起本地视觉服务(RUN_PROXY=1 时连代理一起起)

门 1,代理(聊天附件):

python3 shim/vision_shim.py --port 8900 \
  --upstream http://127.0.0.1:8000 \
  --vision-url http://YOUR_FAST_VISION_HOST:8081/v1/chat/completions \
  --vision-model your-fast-vlm

环境变量与 CLI 一一对应,CLI 优先:SHIM_PORT / --portVISION_TARGET / --upstreamEYES_URL / --vision-urlEYES_MODEL / --vision-model。健康检查:

curl http://127.0.0.1:8900/health

门 2,工具(磁盘文件)。先把插件拷到稳定路径,并按仓库「陷阱 2」改 package.json@deepseek-ai/dsh-toolslink:,指向当前 Harness 自带的那一份,不要装 npm 上的旧包(文档写的是 npm 上的 0.0.1-rc.1 比 Harness 自带的 0.1.0-rc.6 更老,而且会去引一个从未发布的包):

cp -r plugin/vision ~/.dsh/plugins/vision
dsh plugin --profile <p> add link:~/.dsh/plugins/vision

<p> 换成实际 profile,例如 webheadless。插件从 profile 目录解析,不要丢进安装目录的 node_modules,否则所有 profile 启动都会 ERR_MODULE_NOT_FOUND。每个要用的 profile 都要加一次。

典型用法

把一条 dsh 路由指到代理

模型路由写在 $DSH_HOME/settings.yaml 的 pi-ai 提供方块里。baseURL 指向代理,并声明图文输入。下面是 examples/dsh.md 的占位配置,主机和模型 id 换成你的:

llm-pi-ai:
  providers:
    vision-proxy:
      displayName: Your Text Model (via vision proxy)
      apiKeyEnv: YOUR_PLACEHOLDER_KEY_ENV
      api: openai-completions
      baseURL: http://127.0.0.1:8900/v1
      models:
        - id: your-text-model-id
          contextWindow: 262144
          maxTokens: 32768
          input: [text, image]

无密钥的上游也要填 apiKeyEnv,指向任意非空环境变量。省略的话 pi-ai 会去做环境发现,找不到就报 PI_AI_ERROR: No API key

再留一条直连上游、不声明 input 的后备路由,代理挂了还能继续干活:

    text-only-direct:
      displayName: Your Text Model (direct, no vision)
      apiKeyEnv: YOUR_PLACEHOLDER_KEY_ENV
      api: openai-completions
      baseURL: http://127.0.0.1:8000/v1
      models:
        - id: your-text-model-id
          contextWindow: 262144
          maxTokens: 32768

模型路由热加载,不用重启。保存后在模型选择器里选代理那条,附上一张图即可。

analyze_image 配两个后端

在插件配置块或环境变量里写:

export VISION_FAST_URL=http://YOUR_FAST_VISION_HOST:8081/v1/chat/completions
export VISION_FAST_MODEL=your-fast-vlm
export VISION_DETAILED_URL=http://YOUR_DETAILED_VISION_HOST:8010/v1/chat/completions
export VISION_DETAILED_MODEL=your-detailed-vlm

智能体调用形态是 analyze_image(path, backend, prompt)。例如对截图或票据覆盖提示词:prompt: "List every object and any visible text."

Web 表面上,面向模型的工具行在宿主层是关掉的,要靠 agent preset 才能看见。不要去改随安装分发的 standard preset,升级会被覆盖。建一个新 id 的用户 preset(例如 standard-vision),组合 webanalyze_image。用户 preset 不能复用已分发的 id,否则会被静默遮住。然后在 settings.yaml 里设默认(热加载):

agent-presets:
  default: standard-vision

Preset 第一次开会话才挂载。看干净启动日志不能证明工具在;要开一场真实会话。

告诉智能体:它并没有变成多模态模型

dsh 会把 $DSH_HOME/AGENTS.md 读进每场会话。代理生效后,模型常会推断自己被切到了视觉路由。仓库提供了一段可粘贴说明,核心是:你仍是原来的文本模型;图已经被代理或工具写成了 [Image: ...];请说「描述表明……」,不要说「我能看见……」。

仓库给出的端到端核对方法

文本模型拿到图片块时,会编一段看起来合理的描述,读起来像成功。仓库用模型猜不到的纯色图做过观察:

  • 非流式:纯绿 → “The image is a solid, bright green.”
  • 流式:纯蓝 → “a solid, uniform field of deep, saturated blue”(5 个 chunk,干净的 [DONE]
  • 同一套文本模型,直接塞图片会返回 400 "is not a multimodal model",用来证明字来自代理的视觉腿,不是大脑

复现方式:生成几张纯色 PNG,聊天里附一张问颜色(代理路径),或让 analyze_image 指向文件(工具路径),核对描述是否对得上。

适用场景与注意事项

适合:希望保住现在这颗文本大脑,又要让 dsh 对截图、照片、摄像头帧有情境感知的人。仓库原话是:它附加能力,不接管部署;代理是独立进程,随时可杀;工具只是多一个智能体可以调用的能力。

不适合当成原生多模态。仓库「Honest limits」写得很直:

  • 描述是有损的。细部、精确空间关系和小物体可能丢。版面推理请换更大的 detailed 模型。
  • 小 VLM 读图中文字弱。fast 用体积换精度。OCR 重的工作把代理或工具指到更大的 VLM,并接受额外内存和延迟。
  • 大脑推理的是字,不是像素。多数智能体工作(屏幕上有什么、照片里是什么、读一条报错)够用;关键 OCR 或细粒度视觉推理不够。
  • input: [text, image] 是声明,不是检查。只对真正收图的端点声明:代理,或真正的 VLM。写在纯文本模型上,会在消息落盘后中途 400。

另外几条来自 examples/dsh.md 的坑:

  1. llama.cpp 路径必须带 --mmproj(视觉投影器)。backends/pc_llamacpp.sh 会自动传。漏了的话 llama.cpp 会静默按纯文本跑,描述全是空话。这是最常见的「进程在跑但看不见」。
  2. Windows 绝对路径在 preset 里可用,在 profile patch 里会把 C: 当成 URL scheme,报 ERR_UNSUPPORTED_ESM_URL_SCHEME。两边都用裸包名更稳。
  3. settings.yaml(模型路由、默认 preset)不用重启;改 preset 的 agent.cordis.yml 也不用,下场会话按文件时间戳换代;改插件 index.js 要重启(ESM 按进程缓存);代理本身只重启代理进程,dsh 不受影响。

若文本大脑跑在 DGX Spark 上,仓库认为 fast 视觉模型大约 2–3 GB,通常可以和大脑放在同一台机器,把 --vision-url 指到 127.0.0.1。Spark 上报的「空闲」内存会高估 CUDA 实际可用量(GPU 与 CPU 共用一块池);小 fast 多半放得下,大 detailed VLM 未必。先确认服务起来再依赖。

持久化方面:代理可用 autostart/ 里的 Windows 登录脚本(.vbs)或 Linux systemd user unit;工具靠上面的用户 preset。两者独立。

小结

DeepSeek-Harness-Vision-Tools 做的不是把 dsh 换成多模态模型,而是在聊天附件和磁盘文件两条入口上,把「看」交给本地视觉模型,把「想」留给原来的文本模型。目录页安装命令是 dsh plugin add github:tonyd2wild/DeepSeek-Harness-Vision-Tools;真要两扇门都通,还要按 README 起视觉服务、代理,并按 profile 用 link: 挂上 plugin/vision

安装前请阅读源码和 MIT 许可证。插件以当前 dsh 进程权限运行。问题请开到本仓库,不要报到 DeepSeek。

  • 插件目录:https://deepseek-harness-plugin.com/zh-CN/plugins/deepseek-harness-vision-tools/
  • GitHub:https://github.com/tonyd2wild/DeepSeek-Harness-Vision-Tools
  • 集成说明:仓库内 examples/dsh.md
  • DeepSeek Harness 官方仓库:https://github.com/deepseek-ai/deepseek-harness
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜