dsh-llm-pi-ai-image-offload:为 pi-ai 路由恢复图片 token 计量

前言

在 DeepSeek Harness(DSH)里,工具返回带图片的结果时,dsh-spill-policy 会依据 LLM 适配器提供的 imageRequestPricing 估算图片占用的 token,再与 maxInlineTokens 比较,决定是否把过大的 base64 内联内容移出请求。默认情况下 LlmAdapter.imageRequestPricing() 返回 undefined,只有 dsh-llm-deepseek 实现了该方法;走 @deepseek-ai/dsh-llm-pi-ai 的路由没有实现,spill-policy 在取不到计算器时会抛错并被外层捕获,日志里出现「keeping the inline content」,整段大图仍 inline 发给模型,容易撑爆上下文。dsh-token-meter 也会退回到固定结构启发式,而不是按实际发送的字节计价。

下面介绍社区插件 dsh-llm-pi-ai-image-offload(维护者 ender-candle),它在适配器实例上补回 imageRequestPricing,让 spill-policy 能按字节估算视觉 token 并 offload 超大图片。

这是什么

dsh-llm-pi-ai-image-offload 是 DSH 的 Cordis 插件包(npm 名 dsh-llm-pi-ai-image-offload,版本 0.1.0,MIT)。定位是:为未声明图片计价的 pi-ai 适配器路由安装 imageRequestPricing,并在每次 llm/adapters-updated 时重新应用。这里的「pricing」指 token 核算,不是计费。插件零运行时依赖,分类上属于模型推理相关能力;在 SkillHub 插件目录 可查到条目(社区站点,与 DeepSeek / 幻方无官方从属关系)。

它修复的问题

当工具结果携带图片时,spill-policy 会调用:

const calculator = ctx.get("llm")?.imageRequestPricing(provider, model);
if (calculator === undefined) throw new Error("the current model has no image token calculator");

pi-ai 路由没有 override 该方法,上述逻辑失败后被插件外层 try 吃掉,工具结果保持完全 inline,多兆字节的 base64 会原样进入 provider 请求。装上本插件后,无计价的适配器实例会得到基于传输字节的估算,spill-policy 才能与 maxInlineTokens 对齐并 shed 放不下的图片。

核心行为

  1. 对返回「无图片计价」的适配器实例挂载 imageRequestPricing;已有真实实现的适配器不会被覆盖(上游修复后本补丁会自动失效)。
  2. 若某适配器自己的实现会 throw,则不会被替换。
  3. Offload 不会从会话或工作区删除文件;模型会收到 spill-policy 的说明和可再通过 read_image 读取的路径。

计价公式(路由未发布 vision-token 账时,按实际传输字节计):

transmitted = min(attachment.bytes, imageRequestTarget(...).maxBytes)
visualTokens = ceil(base64Length(transmitted) / charsPerToken)

其中 base64Length(b) = ceil(b / 3) * 4,与 harness 的字节换算一致。README 给出的失败案例:2048×2048 JPEG 约 1,266,320 字节、请求预算 1 MiB 的路由上,截断后 base64 约对应 349,526 tokens,高于默认 maxInlineTokens: 12500,因此应被 shed。

配置

字段均可选,通过 profile 里该 bundle 的 config 传入:

field default meaning
enabled true 关闭整个补丁
charsPerToken 4 每个 token 对应的 base64 字符密度
maxVisualTokens 400000 单次图片 occurrence 的上限
providers (all) 允许的 provider route id 白名单
debug false 向 harness logger 记录每条被 patch 的路由

示例(profile patch 片段):

- id: llm-pi-ai-image-offload
  name: dsh-llm-pi-ai-image-offload
  config:
    debug: true

安装与启用

先从 GitHub 安装到指定 profile(推荐):

dsh plugin --profile <profile> add github:ender-candle/dsh-llm-pi-ai-image-offload

需要可复现安装时可 pin commit:

dsh plugin --profile <profile> add github:ender-candle/dsh-llm-pi-ai-image-offload#<sha>

本地 checkout 路径安装:

dsh plugin --profile <profile> add /absolute/path/to/dsh-llm-pi-ai-image-offload

dsh plugin add 会更新 profile 的 package.json 与 pnpm-lock.yaml。接着在 profile 的 package.json 里把 bundle 名加入 dsh.profile.bundles:

{
  "dsh": {
    "profile": {
      "bundles": ["...", "dsh-llm-pi-ai-image-offload"]
    }
  }
}

包内 cordis.patch.yml 会自动插入 host 行,一般无需手改 patch。重启 harness 后 bundle 在启动时加载。

如何确认已生效

Harness logger 使用内存 ring buffer,事后未必能看到 warn。README 建议:debug: true,再生成一张大图触发 spill,在 session log 中查找 image/offload 事件;未打补丁前该信号不会出现。

本地可跑单元测试(18 项,无额外依赖):

node test/offload.test.mjs

适用场景与注意

  • 适合:使用 @deepseek-ai/dsh-llm-pi-ai 路由、工具结果常带大图 base64,且已启用 dsh-spill-policy 的 DSH profile。
  • 权限与安全:插件与当前 dsh 进程同权限运行;安装前请阅读 GitHub 仓库 源码与 MIT 许可证。
  • 风险(README 原文):LlmAdapter 实例字段不是文档化的扩展点;registry 形状变化时补丁可能静默失效(行为回到补丁前)。对上游按 vision 分词的路由,估算可能偏多,这是保守策略,只会更早触发 shed;provider 返回的 usage 仍是权威计量。

结尾

dsh-llm-pi-ai-image-offload 用很小的补丁把 pi-ai 路由缺失的 imageRequestPricing 补回来,让 spill-policy 和 token 预算逻辑重新能处理超大 inline 图片,避免上下文被 base64 占满。

  • 目录页:https://www.skillhub.cn/plugins/ender-candle/dsh-llm-pi-ai-image-offload
  • 源码与中文 README:https://github.com/ender-candle/dsh-llm-pi-ai-image-offload
羽毛球分组比赛记分
小程序二维码

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

小夜