DeepSeek Harness (DSH) 的理念是“一切皆插件”。在多模态会话中,客户端通常按字节或像素计算图片预算,而上游模型(如 vLLM)往往按张数限制输入(--limit-mm-per-prompt.image)。字节预算无法阻止因图片过多导致的 400 错误,且 DSH 的 compact 压缩会重发相同的历史记录,导致重复失败。dsh-image-guard 旨在解决这一问题。
核心功能¶
该插件运行在 DSH 的 fetch 链路首层,主要处理“请求发送前”和“错误重试”两个阶段。
- 保留最新 N 张,其余替换为标记:插件会保留最新的
keepRecent张图片,其余图片被替换为可识别的标记(包含文件名、路径或指纹)。被裁剪的图片不会无声丢失,模型或 agent 需要时可再次获取。 - 解析上游上限:当上游因图片数量返回 HTTP 400 时,插件会从错误消息
At most N image(s)中解析上限 N,并按 N−1 张重试;解析到上限后,同进程内后续请求将直接按 N−1 张发送,不再触发 400。 - 节省视觉 token:每张被裁剪的图片净省约 930 token(实测上限 972 − 标记开销约 40)。设置页实时显示累计节省量。
- 不修改会话历史:仅修改即将发出的请求体(使用
structuredClone的副本),前缀缓存保持有效,关闭插件即恢复原有行为。 - 始终保持在 fetch 链首:插件拦截
globalThis.fetch的赋值,配合 200ms 看门狗与链上探针兜底,确保它始终位于dsh-net-proxy之前,避免代理模式下的绕过问题。
安装与启用¶
本插件目前尚未发布到 npm,也未被任何插件市场目录收录,无法在 DSH Desktop 的 GUI 插件市场中搜索到。需手动安装。
命令行 profile¶
dsh plugin --profile <name> add github:mafeis/dsh-image-guard
注意:desktop profile 由 Electron 持有,启动器拒绝对其执行插件管理请求。
DSH Desktop 手动安装¶
DSH Desktop 加载名为 desktop 的 profile,位于 ~/.dsh/profiles/desktop/。需手动编辑 manifest 并安装依赖。
- 编辑
~/.dsh/profiles/desktop/package.json,在dependencies中加入:
"dsh-image-guard": "github:mafeis/dsh-image-guard"
- 在同一文件的
dsh.profile.bundles数组中追加"dsh-image-guard"(该字段不存在则在dsh字段下新建):
"dsh": {
"profile": {
"bundles": ["dsh-image-guard"]
}
}
- 安装依赖后重启 DSH Desktop:
cd ~/.dsh/profiles/desktop && pnpm install --prefer-offline --ignore-scripts
配置与调试¶
配置文件位于 ~/.dsh/image-guard.json,修改后支持热更新,也可在设置页或通过 /_dsh/image-guard 路由修改。
常用配置项¶
| 字段 | 默认值 | 说明 |
|---|---|---|
enabled |
true |
总开关,关闭后完全透传 |
keepRecent |
12 |
保留最新的图片张数,其余替换为标记 |
maxRetries |
3 |
400 后的降级重试次数 |
learnLimit |
true |
是否从 400 响应中解析上游上限 |
dryRun |
false |
仅观察,不修改任何请求 |
markerLang |
空 | 标记语言,留空跟随宿主界面语言,zh / en 为固定值 |
placeholder |
空 | 标记模板,留空则使用生效语言的默认模板 |
pathMode |
basename |
full / relative / basename / none,决定路径显示方式 |
tokensPerImage |
972 |
估算节省量所用的单张图片视觉 token 数 |
verbose |
true |
是否输出日志 |
调试步骤¶
- 先观察再启用:在设置页打开
dryRun,此时仅记录将被裁剪的图片,不修改任何请求。 - 检查链路接入:设置 → 图片守卫 → 诊断。应显示守卫已接入 fetch 链,且链上探针计数持续增加。
- 查看状态:
~/.dsh/image-guard-status.json中的chatPosts开始增长。
标记机制¶
被裁剪的图片会被替换为标记。根据图片来源不同,标记内容不同:
- 有路径(默认):
[图片已省略 #1 · 01-race-start.png · 需要时按文件名在工作区内搜索后读取] - 仅有附件元数据:
[图片已省略 #1 · 01-race-start.png · 内联图片 sha256:bf23bcd3 (image/png 1280x720 618 KiB) · 无原文件路径,已无法取回] - 远端 URL:
[图片已省略 #1 · race.png · https://…/race.png · 需要时可重新访问该地址]
已知限制¶
- 仅控制张数:不压缩、不缩放图片;字节预算不在处理范围。
- 首次 400:上限只能从 400 响应中获知,首次请求仍会出现一次 400。
- 仅针对历史图片:最新 N 张按原样发送(含字节)。
- 内联图片无法恢复:已内联且无路径的图片无法取回,标记会明确说明。
- 依赖当前形态:依赖 DSH 当前的请求形态,形态不匹配的请求会透传。
结语¶
dsh-image-guard 通过拦截请求链路,解决了 DSH 客户端按字节预算而上游按张数限制导致的多模态会话中断问题。通过解析错误并动态调整图片数量,实现了会话的连续性。
目录页:dsh-image-guard
GitHub:mafeis/dsh-image-guard