# dsh-tool-vision **GitHub**: [bcdahb0-jpg/dsh-tool-vision](https://github.com/bcdahb0-jpg/dsh-tool-vision) · [English](README.md) 给 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 外接**视觉模型**的插件。 DeepSeek 自家模型是纯文本的,而且 harness 的每次模型请求都**严格从会话日志推导**(`llm/stream` 请求必须与持久化推导一致,否则 agent-loop invariant 会报 `log-reconstruction desync`)。本插件用两条路径补上缺口: 1. **`inspect_image` 工具** —— 把图片(本地文件或 http(s) URL)发给任意支持 `image_url` 内容块的 OpenAI 兼容 `/chat/completions` 端点,把视觉模型的文字回答带回对话。 2. **图片桥(v0.2.1)** —— 粘贴的图片在**进入持久化日志之前**就被转换成 `inspect_image` 指引文本,拦截点是 `agent/pre-step` waterfall(这是 harness 唯一允许插件替换"进入某一步的消息"的缝;替换后的消息会**成为**持久化的 `user/message` 日志,所以请求重建 invariant 天然满足)。旧版本已经写进日志的图片消息,会在该会话下一次 pre-step 时用 surface `replace` 惰性修复。只有 `multimodalModels` 白名单内的模型直收图片块;**不参考模型的 `inputModalities` 声明**——因为很多配置为了通过 prompt 准入检查,会给纯文本模型声明 `input: [text, image]`(那只是声明,不代表上游真的能吃 `image_url`)。 **聊天界面显示图片(v0.3.8)**:当会话模型走桥接路由(默认 provider `tool-vision`)时,持久化日志**保留原始图片块**,聊天框直接渲染粘贴的图片(不再显示 `[User sent an image ... exported to: ]` 这类路径文本);桥接逻辑原样保留在背后——`createBridgeAdapter` 在请求流式层把图片块改写为 `inspect_image` 指引文本,文本模型收到的提示与之前完全一致。 - 除 dsh SDK 外零依赖 —— 兼容任意端点:OpenAI GPT-4o、Qwen-VL(DashScope)、GLM-4V(智谱)、Moonshot、Gemini 兼容端点、本地 Ollama 等。 - 注册在**全局工具层**:进程内所有 Agent 都能调用 `inspect_image`。 - **Web UI 设置栏(v0.3.0)**:设置 → 视觉模型 编辑 `tool-vision` 命名空间(API 地址、只写密钥、模型、桥接选项),写入 `settings.yaml`,**改动即时生效无需重启**。API 密钥存放在 `settings.yaml` 而非 profile patch;插件按包名挂载(`name: 'dsh-tool-vision'`)以便 web 端发现客户端 bundle。 ## 安装 GitHub 安装(推荐): ```sh dsh plugin --profile add github:bcdahb0-jpg/dsh-tool-vision ``` 然后在 profile patch(`$DSH_HOME/profiles//cordis.patch.yml`)里挂载: ```yaml - insert: - id: tool-vision name: 'dsh-tool-vision' config: baseURL: 'https://api.openai.com/v1' apiKeyEnv: 'VISION_API_KEY' model: 'gpt-4o-mini' ``` 不装包、直接加载本地路径: ```yaml - id: tool-vision name: './plugins/dsh-tool-vision/index.js' ``` ## 配置 | 字段 | 默认值 | 含义 | |---|---|---| | `baseURL` | `https://api.openai.com/v1` | OpenAI 兼容 API 基地址 | | `apiKey` | `''` | API 密钥(优先于环境变量) | | `apiKeyEnv` | `VISION_API_KEY` | 存放密钥的环境变量名 | | `model` | `gpt-4o-mini` | 视觉模型 id | | `maxTokens` | `1024` | 视觉调用最大输出 token | | `timeoutMs` | `60000` | 单次请求超时 | | `maxImageBytes` | `10MB` | 本地图片大小上限 | | `description` | 默认描述 | 工具描述(模型可见) | | `bridgeTextOnly` | `true` | 把粘贴图片转成文本指引(发给看不懂图片的模型时) | | `bridgeExportDir` | 临时目录 | 桥接图片导出目录(`os.tmpdir()/dsh-vision-bridge`) | | `multimodalModels` | `[]` | 直发图片块的模型 id(如 `mimo-v2.5`) | | `bridgeModel` | `true` | 注册"桥接模型条目":模型选择器出现 provider `tool-vision`、模型名带"(tool-vision 桥接)",选中后贴图即可通过 harness 准入检查,**无需在 settings.yaml 里手动声明 `input: [text, image]`** | | `bridgeRoute` | `tool-vision` | 桥接模型条目的 provider 路由 id(显示在选择器里) | | `bridgeProvider` | `deepseek-official` | 桥接委托的文本模型 provider:文本轮次原样转发给它,图片块在请求层改写为 `inspect_image` 指引 | | `bridgeModelIds` | `deepseek-v4-flash, deepseek-v4-pro` | 镜像到桥接路由的模型 id 列表(空 = 全部镜像) | ## 桥接模型条目(自包含,v0.4.0) 启用后插件会在模型选择器注册一个声明图片输入的桥接路由(默认 provider `tool-vision`, 模型名如 `DeepSeek V4 Flash(tool-vision 桥接)`)。选择它之后: 1. 粘贴/拖拽图片能通过 harness 的 prompt 准入检查(该检查发生在任何插件钩子之前, 且 `dsh-llm-deepseek` 对 DeepSeek 模型硬编码纯文本输入——过去必须靠 settings.yaml 里 `llm-pi-ai` 声明 `input: [text, image]` 才能放行,现在完全由本插件接管); 2. 图片桥(`agent/pre-step`)把图片转成 `inspect_image` 指引文本,或经 `multimodalModels` 白名单保留原图块后在请求层改写; 3. 文本轮次原样委托给 `bridgeProvider` 的真实适配器(默认 DeepSeek 官方)。 **解耦承诺**:桥接模型条目、图片桥、`inspect_image` 工具、设置命名空间全部由本插件 注册/卸载。删除本插件 = 选择器条目、桥接、工具、设置一起消失,settings.yaml 无需 任何残留声明;更换视觉方案时直接替换插件即可。 ## 图片桥配置 1. 在模型设置里给要贴图的模型声明图片输入(pi-ai 风格),让 harness 放行图片消息: ```yaml llm-pi-ai: providers: your-provider: models: - id: deepseek-v4-flash input: [text, image] ``` 2. 在插件配置里列出真正多模态的模型,让它们直收图片块: ```yaml - id: tool-vision name: 'dsh-tool-vision' config: multimodalModels: ['mimo-v2.5', 'grok-4.5'] ``` 之后在文本模型下贴图: - **走桥接路由(默认推荐)**:聊天框直接渲染图片;发给文本模型的是 `inspect_image` 指引(`[User sent an image, exported to: . Inspect it with the inspect_image tool...]`),Agent 会调用视觉端点查看并把结果带回对话。 - **其他纯文本模型**:转录里留下那条指引文本(不再以像素图形式渲染),Agent 同样会调用视觉端点。 > 为什么不用 `llm/stream`?harness 会冻结每个请求,且 agent-loop invariant 会拒绝任何与会话日志推导不一致的请求;这个 cordis 版本的 waterfall `next()` 也无法替换请求参数。`agent/pre-step` 才是受支持的缝:它的决策消息**会成为**持久化日志,invariant 天然成立。 密钥解析顺序:`config.apiKey` → `process.env[apiKeyEnv]` → `process.env.OPENAI_API_KEY`。 ## 工具:`inspect_image` | 参数 | 必填 | 含义 | |---|---|---| | `path` | ✅ | 图片路径(绝对路径,或相对当前工作区)或 http(s) URL | | `question` | – | 可选的具体问题 | | `detail` | – | `auto` / `low` / `high` 分辨率提示 | 示例端点(`baseURL`): - **OpenAI**:`https://api.openai.com/v1` —— `gpt-4o`、`gpt-4o-mini` - **阿里云 DashScope(Qwen-VL)**:`https://dashscope.aliyuncs.com/compatible-mode/v1` —— `qwen-vl-plus`、`qwen-vl-max` - **智谱(GLM-4V)**:`https://open.bigmodel.cn/api/paas/v4` —— `glm-4v-flash`(免费档)、`glm-4v-plus` - **Moonshot(Kimi)**:`https://api.moonshot.cn/v1` —— `moonshot-v1-8k-vision-preview` - **Ollama 本地**:`http://localhost:11434/v1` —— `llama3.2-vision`(无需密钥) ## 限制 - 走桥接路由时,图片块保留在会话日志中(聊天界面显示图片),但发给文本模型的仍是文本指引——文本模型无法做像素级上下文推理;视觉模型的描述通过 `inspect_image` 回传。非桥接的纯文本模型则直接在日志里写指引文本。 - 图片以 base64 传输;注意隐私与大小限制。 - 独立于 dsh-llm 的路由/重试体系;失败会向 Agent 返回明确错误。 ## License MIT