dsh-image-once:对话图片只在首次请求内联

前言

在 DeepSeek Harness(DSH)里贴截图、读图工具返回后,默认链路会把对话里每一次未 offload 的图片在每一次发往模型的请求里重新 base64 内联。对话一长、图一多,请求体体积会线性放大,首字节(TTFT)明显变慢——README 里在同一张图上的实测里,纯文本约 49B 时首字节约 2.5s,而 4MB 量级 body 可到 20s 以上,且瓶颈往往在网关侧 ingest,而非本机上行。

若你主要做「贴图看一眼、后面用文字继续讨论」的工作流,更合理的是:每张图只在它第一次出现在某次请求里时内联,之后用 Harness 自带的占位文字(含本地只读路径),模型需要细节时再 read_image,那次再内联一次。下面介绍社区插件 dsh-image-once(维护者 senyayume,分类:工作流)如何实现这一策略。

这是什么

dsh-image-once 是 DSH 的工作流类插件:对每个「图片出现」按 sessionId + message.id + 内容树路径 + attachmentId 记账;第一次见到时原样内联,并在该请求产出第一个 chunk 之后才记为已发(失败或重试不算);之后任意请求把该出现标为 offloaded: true,由 DSH 既有逻辑换成带本地只读路径的占位文字。带 purpose 的辅助请求(如会话标题、摘要)不改写、不记账;记账仅在内存、只影响本次请求的副本,不写回会话。

SkillHub 为社区插件目录,与 DeepSeek / 幻方无官方从属关系;安装前请自行查看源码与许可证。插件在当前 DSH 进程权限下运行。

核心功能

请求体与 TTFT

插件针对的是 dsh-llm-pi-ai 等路径上「每步请求都重发整段图片 base64」的行为。启用后,同一张图不会在后续每个 step 里重复占满请求体,有利于缩短大 body 场景下的等待时间。

出现(occurrence)与重看

一次「出现」由会话、消息 id、内容树中的路径和 attachmentId 共同确定。模型通过 read_image 重看某张图时,算作新的出现,会在对应请求里再内联一次。

两种消息形状

不同 DSH 版本里 read_image 结果结构不同,插件对两种形状都做递归扫描(与 Harness contentHasImage 口径一致),避免静默失效:

形状 典型来源 结构要点
扁平 内置 dsh 的 createToolResultMessage { role: "tool", content: [text, image] }
嵌套 profile 中 @deepseek-ai/dsh-llm-pi-ai 的消息构造 tool-result 内再嵌 content

改写采用逐块重建:未改动的文本块、tool-result 外壳等原样保留。

Provider 与模型范围

设置 → 插件 中找到 dsh-image-once,行右侧开关控制整插件是否生效;点「配置」进入 provider 名单:每行一个开关,关闭则该 provider 完全不经过本插件;行右侧「限定模型」可把该 provider 收窄到若干模型 id。名单来自宿主注册表,插件不硬编码 provider 名。缺省对所有 provider、所有模型生效;改完即时生效,无需重启。

安装与启用

本地包形态(dsh.bundle.patch + cordis.patch.yml),安装命令以 README「装配」一节为准:

dsh plugin --profile <profile> add <本包目录|github:senyayume/dsh-image-once>

安装后进入 dsh.profile.bundles,在插件页可见、可开关。package.json 要求 Node >=20,并保留 @deepseek-ai/schemastery 作为 peerDependencies(宿主半体依赖该包)。

若你修改了 lib/index.js、lib/client.js、cordis.patch.yml 或 package.json,需要整包重启 DSH Desktop:宿主热重放补丁时会复用 Node ESM 模块缓存,.js 改动热更可能吃不到;client 相关变更还需 client-modules 重新扫描。

配置(可选)

界面与 YAML 指向同一份 stored config,只应维护一处。

在 设置 → 插件 → dsh-image-once → 配置 中编辑,等价于在 profile 补丁里写入(示例语义如下):

- id: image-once
  config:
    providers:
      <适配器注册名>:
        enabled: false                            # 该 provider 完全不进本插件
        models: [deepseek/deepseek-v4.1-flash]    # 只对这些模型生效;空数组 = 该 provider 全部模型

手写 YAML 时写在 profile 的 ~/.dsh/profiles/<name>/cordis.patch.yml,是对包自带行的覆盖(config 整块替换而非 merge)。不要再写重复的 insert: 插入同一 id: image-once,否则试启动校验可能回滚。provider 名匹配适配器注册名(如日志里的 commandcode),models 匹配 options.model 的模型 id。未在 providers 里出现的 provider 仍视为生效;providers 为空则全部 provider 生效。

插件 config 由 cordis 作为 apply(ctx, config) 的第二个参数传入,不要误写 ctx.config。

典型用法

  1. 按上文命令把插件加入当前 profile,在 设置 → 插件 打开 dsh-image-once 总开关。
  2. 若只用部分后端或模型,进入「配置」,关闭不需要的 provider 行,或对某行设置「限定模型」。
  3. 正常在对话中粘贴图片或使用 read_image;第一次出现在某次模型请求中的图会以 base64 内联;同一会话后续 step 中该出现变为占位文字。
  4. 若模型需要再看细节,它会按占位信息自行 read_image,该次请求会再次内联(新的出现)。

无需额外 API 调用;行为在请求投影阶段自动完成。

适用场景与注意

适合:截图辅助调试、文档贴图后长对话、多 step 智能体流程里图多但后续主要靠文字推理的场景。

代价:模型对每张图的「视觉内容」只在首次内联那次请求里直接可见;之后仅知 sha256、尺寸、文件名与本地只读路径,需细节时要再 read_image(会再占一次内联)。对「看一眼再讨论」通常可接受。

安全与信任:插件改写的是发往模型的请求副本,不持久化 offload 状态到会话文件;仍建议在安装前阅读 lib/index.js 与许可证,并仅在可信 profile 中启用。

开发自测(维护者仓库内):

node test/run.mjs

会跑宿主侧(请求投影、两种图片形状、occurrence 键、首个 chunk 后记账等)与客户端侧(主题 token 白名单等)测试。

结尾

dsh-image-once 把「每张图只在内联那一次请求里带上 base64」做成可开关、可按 provider/模型收窄的工作流插件,缓解多图长对话下重复上传带来的 TTFT 压力。更多信息见 SkillHub 目录页 与 GitHub 仓库。

羽毛球分组比赛记分
小程序二维码

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

Xiaoye