dsh-mmroute — 多模态路由(全程交叉版)
English summary — Multimodal router for DeepSeek Harness (DSH). Text-only models (e.g. DeepSeek, GLM) can still handle visual tasks: every image in every step of the agent stream — user uploads, read_image results, MCP tool renders (Figma screenshots, …) — is transcribed into detailed text (verbatim OCR, chart data, visual detail) by a multimodal understander model before the request is dispatched, cached per attachment. Unmarked models are auto-classified by their adapter-declared modalities (overridable per model); image-related request failures self-recover by rerouting through the understander and retrying. Settings page: mark models multimodal/text-only, pick the understander, watch transcription/recovery stats. Works with PNG / JPEG / WebP / GIF.
为 DeepSeek Harness(DSH)里的每一条模型路由做图片模态调度,并且贯穿整个 agent 流程:
- 多模态模型 / 声明图片输入的模型 —— 图片原样直发;
- 纯文本模型(显式标记,或「自动纯文本路由」判定)—— 每次请求里的每张图片,先由指定的多模态理解模型转述为详细文字(含图中文字逐字转录、图表数据转录、视觉细节),再连同对话一起交给纯文本模型作答;
- 报错自愈 —— 纯文本模型(或网关实际拒图的"多模态"模型)一旦出现图片类失败,自动把该路由转入转述路径并让 agent loop 重试:两类模型在整条流程里交叉接手,而不是"第一次读完图就不再管"。
这样,DeepSeek、GLM 等纯文本模型也能处理看图问答、截图分析、Figma 渲染审查等视觉任务。
Multimodal router for DeepSeek Harness: text-only models get every image in every step transcribed to detailed text by a multimodal understander before the request is dispatched; image-related request failures auto-recover by rerouting through the understander and retrying. Works with image attachments (PNG / JPEG / WebP / GIF).
工作原理
agent loop 的每一次模型调用(每个 step:用户首图、read_image 返回、
MCP 工具(如 Figma get_screenshot)中途产生的新渲染图……)
│
├─ ① 准入放宽:resolveModelInfo 遮蔽让「会被转述」的模型通过
│ harness 的图片准入检查(用户上传 / read_image / MCP 图片 alike)
│
├─ ② llm/stream 拦截:按 attachmentId 收集本次请求的全部图片
│ (含 tool-result 嵌套形态),逐张交给理解模型做**全量结构化
│ 转述**(≤12000 字符:图型判定 / 全部文字逐字转录 / 图表数据
│ / 布局 / 颜色 / 异常 / 不确定区域;内容寻址缓存,跨步骤 / 跨
│ 重启有效;当前问题作为侧重参考注入,但完整性优先、绝不省略)
│
└─ ③ 改写后的纯文本请求交给原模型作答 —— 对话完全无感
转述末尾附 [提示] 行:作答模型可调用 vision_relook 工具对
任意已转述图片发起聚焦复看(「指挥与执行」协作)
任何一步漏网(未标记、网关拒图……)导致适配器报图片类错误时:
└─ ④ agent/request-error 自愈:拉理解模型转述 → retry
(每条路由每进程最多自愈 2 次,杜绝重试风暴)
- 自动纯文本路由(默认开启):未标记的模型按适配器原生声明判定 —— 声明图片 → 直发;声明纯文本或未声明 → 自动按纯文本处理(先转述再作答)。关闭后未标记模型完全保持 harness 原生行为。
- 显式标记优先于自动判定:「多模态」标记的模型请求(含图片)原样直发,适合适配器未声明图片能力、但网关实际支持的模型;「纯文本」标记的模型总是先转述。
- 单一理解模型,用户手选:理解模型即你 dsh 配置里的多模态模型(自动发现或手动指定),不内置免费端点、不借用任何外部登录态;并发转述同一张图自动合并为一次调用。
- vision_relook 定点复看:作答的文本模型可对任意已转述图片发起聚焦核查(
attachmentId + 聚焦问题 + 可选区域),由理解模型逐字精确作答、看不清就明说 —— 两个用户手选的模型形成「指挥与执行」协作。 - 准确性 + 完整性铁律(设计原则):给纯文本模型的数据必须准确且完整 —— 逐字转录不翻译、全量覆盖不因侧重省略、宁声明「不确定区域」绝不猜测;单条转述上限 12000 字符(足以容纳密集截图的全量逐字转录)。
- 历史图摘要重放(默认开启):同一张图在会话历史中再次出现时以 ≤1200 字符摘要重放,首次出现仍为全量转述 —— 长会话不因旧图全量重放而膨胀;可在设置页关闭。
- 理解模型不可用 / 调用失败 / 返回空描述时,以说明性占位文字降级,不会中断对话轮次。
安装
dsh plugin --profile web add dsh-mmroute
本地开发安装(<path> 为本仓库的检出路径):
dsh plugin --profile web add <path>/dsh-mmroute
dsh plugin 是 pnpm 转发器:会把依赖写入 profile 的 package.json,并把声明了 dsh.bundle 的包自动加入 dsh.profile.bundles。安装后重启 dsh web 生效。
或手动加入 profile 的 package.json(路径相对 profile 目录):
{
"dependencies": { "dsh-mmroute": "file:../dsh-mmroute" },
"dsh": { "profile": { "bundles": ["dsh-mmroute"] } }
}
使用
- 打开 设置 → 多模态路由(侧边栏底部设置面板内)。
- 「自动路由与报错自愈」卡片:
- 自动纯文本开关(默认开启)—— 未标记模型的自动判定与报错自愈总开关;
- 历史图摘要开关(默认开启)—— 旧图重放用截断摘要,节省上下文;
- 运行统计(转述 / 自愈 / 复看次数)与已自动转入转述路径的路由列表。
- 在「多模态理解模型」下拉中选择:
- 自动 —— 使用发现的第一个原生多模态模型;
- 或指定任一候选(含你手动标记为多模态的网关模型)。
- (可选)在「模型模态标记」里为个别模型显式选择 默认 / 多模态 / 纯文本,覆盖自动判定。
- 直接在对话里粘贴 / 上传图片,或让 agent 调 Figma 等 MCP 工具产生渲染图即可 —— 每一步的新图都会被处理。
配置持久化在本机 $DSH_HOME/mmroute.json(默认 ~/.dsh/mmroute.json),重启后仍然生效;可在设置页一键清除图片转述缓存。
免费与本地理解模型
理解模型可以是任何声明图片输入的 provider —— 包括免费云模型与本地模型。以下片段合并进 $DSH_HOME/settings.yaml 的 llm-pi-ai.providers 段(注意:Web「添加自定义提供方」表单不会写入图片能力元数据,视觉模型请手写 input: [text, image]):
# 智谱 bigmodel.cn —— glm-4.6v-flash 永久免费(大陆直连)
llm-pi-ai:
providers:
zhipu:
api: openai-completions
baseURL: https://open.bigmodel.cn/api/paas/v4
apiKeyEnv: ZAI_API_KEY
models:
- id: glm-4.6v-flash
name: "智谱: GLM-4.6V-Flash (永久免费)"
contextWindow: 131072
maxTokens: 8192
input: [text, image]
Key 写入 ~/.dsh/.credentials.yaml(ZAI_API_KEY: sk-...)或导出同名环境变量,重启 dsh web 后该模型即可在「多模态理解模型」下拉中使用。其他免费渠道:阿里云百炼(新用户每系列 100 万 token/90 天,qwen-vl-plus 等)、硅基流动(Qwen2.5-VL 系列)。本地 Ollama 同样适用:把本地视觉模型配为 pi-ai provider(OpenAI 兼容端点 http://127.0.0.1:11434/v1,声明 input: [text, image])即可完全离线转述。理解模型全量转述对小模型要求不高,免费额度通常足够。
边界行为(发布者自查清单)
| 场景 | 行为 |
|---|---|
| agent 流程中途出现新图片(工具返回 / MCP 渲染) | 该步请求在发送前被拦截转述,含 tool-result 嵌套形态 |
| 未标记模型 + 自动纯文本路由开启 | 按适配器声明判定:声明图片直发,否则转述 |
| 未标记模型 + 自动纯文本路由关闭 | 完全保持 harness 原生行为(含原生拒绝) |
| 图片类请求失败(UNSUPPORTED_CONTENT / 网关拒图文案) | 自动转入转述路径并 retry;每路由每进程 ≤2 次 |
| 自愈后再次请求 | 命中内存 override,直接转述(重启后失效,可固定为标记) |
| 理解模型指向纯文本标记的模型自身 | 理解调用失败 → 占位文字降级,无递归(WeakSet 放行自有请求) |
| 无任何多模态模型可用 | 占位文字说明如何配置,对话继续;自愈不触发 |
| 理解调用失败 / 空描述 / 中止 / 限流 | 该图降级为占位文字,对话不中断,其余图片不受影响 |
| 文本模型调用 vision_relook | 对已转述图片聚焦核查:逐字精确作答,看不清/未找到明确说明 |
| 并发请求转述同一张图 | 合并为一次理解调用(in-flight 去重) |
| 同一张图在会话历史中再次出现 | 摘要重放(≤1200 字符,可关闭);首次出现仍为全量;同一请求内全量始终在场,摘要不损失信息 |
| 超长描述 | 截断至 12000 字符并注明(完整性优先:足以容纳密集截图的全量逐字转录) |
| 缓存 / 标记数量 | 转述缓存上限 300 条(FIFO 淘汰);标记上限 2000 条 |
| 状态文件损坏 / 字段异常 | 按默认值重新开始,不阻断宿主启动 |
| 会话已有图片时切换到会被转述的模型 | 准入放行(这正是放宽的目的) |
| 会话已有图片时切换到原生拒图模型(自动路由关闭) | harness 原生拒绝(行为不变) |
provider/model id 含 /、引号、Unicode |
精确字符串键 + JSON 编码,无解析歧义 |
| 悬空标记(provider 已移除) | 不显示、不计数、不生效,但保留在状态文件中 |
| 两个浏览器标签页同时写配置 | 每个方法只触碰自己的键,落盘同步无交错 |
| 跨站 / DNS-rebinding 攻击 API | 信任围栏:仅回环或 trustedHosts + 同源标记 + JSON Content-Type + 64KB 上限 |
| 无头配置(无 webServer) | 拦截层照常工作,仅设置页不可用 |
安全与隐私说明
- 标记与自动判定是对端点能力的声明,不是检测:把实际不支持图片的模型标成「多模态」,请求会由供应商报错(与 pi-ai 官方
input: [text, image]声明语义一致)—— 此类报错会被报错自愈捕获并自动降级为转述。 - 理解模型调用会把图片发送给你指定的多模态模型 —— 请自行确认该模型的隐私条款。
- 设置 API 仅接受本机同源请求;插件不上报任何数据。
resolveModelInfo的遮蔽只影响图片准入与模型目录展示,不参与请求路由校验;插件停用后自动恢复原方法。
已知限制
- 当前 DSH 附件系统 v1 仅支持图片(PNG / JPEG / WebP / GIF);视频不在支持范围内。
- 理解模型的 token 消耗独立计费,不出现在主对话的用量统计中。
- 报错自愈依赖失败文案 / 错误码的图片特征启发式(
UNSUPPORTED_CONTENT+ image 字样,或 message 含 image / multimodal / vision / 视觉 / 图片);无法识别的文案不会触发自愈,但显式「纯文本」标记仍会全程转述。
许可
MIT
No comments yet. Be the first to write one.