dsh-warm:工具阻塞期间保持 Prompt Cache 温热

前言

在 DeepSeek Harness(DSH)里跑长工具时,agent 循环会卡在 bash、job_output、wait_agent 等调用上,这段时间里会话往往不再向模型发请求。若上游提供商的 prompt cache 在这段时间内过期,工具返回后下一轮对话会按 cache miss 计费,并重新预填动辄数万 token 的前缀,TTFT 也会拉长。

常见做法是空闲会话也周期性 ping 模型保活,但维护者在实测中发现:多数会话并不会被再次打开,这类 idle warming 反而净亏。dsh-warm 把 warming 收窄到「仅在工具阻塞期间」执行,并配合按提供商配置的 TTL 网格、日志与 UTC 日预算,在可核对的数据下做成本与延迟权衡。

这是什么

dsh-warm 是 DSH 的 host 插件(分类:模型推理),由 JanEickholt 维护,当前 npm 包版本为 0.2.0(MIT)。它在会话的 agent 循环 阻塞在工具执行内部 时,在提供商 cache 即将过期前,把该会话 最后一次对话请求 经宿主 llm/stream waterfall 以 maxTokens: 1 重放一次,丢弃 1 token 响应,使工具结果仍落在温热的前缀上,后续真实请求可走 cache hit。

空闲会话与用户输入类工具(如 ask_user_question)不会被 warming。

核心功能

仅在工具阻塞时 warming

TTL 因提供商而异(维护者在其环境中对 Mistral 等做过 live probe)。工具或队友等待结束后循环会立刻继续,因此只在阻塞窗口内 ping 在维护者 50 天账本、350 会话的样本里表现为正收益;对从未恢复的 idle 会话做 warming 在该样本中为净负。

重放保持字节一致

循环构建的请求在进入 llm/stream 时已深冻结。warmer 捕获该对象,稍后发送浅拷贝,仅改 maxTokens: 1 与 signal(与用户中断链式 AbortSignal.any)。同一 adapter 序列化 ping,输入前缀与真实调用一致,提供商可从 cache 命中。ping 走完整 waterfall,dsh-cost-meter 会像普通调用一样记账。

按提供商调度与预算

providers 表按提供商配置 intervalMs、ttlMs、marginMs 等;有 ttlMs 时按过期锚点调度(例如在测得 302 s TTL、60 s margin 下约每 242 s ping)。内置 mistral、openai、anthropic 默认项;deepseek 默认不列入(disk cache 时长不确定,盲目 ping 易浪费)。支持 dryRun、每工具块 maxPings、dailyPingBudget(UTC 日)、maxConcurrentPings、excludeTools、minPrefixChars 等。

统计与 HTTP 快照:stats.enabled 时写 stats.json,并提供 /api/dsh-warm/stats。

安装与启用

插件以当前 DSH 进程权限运行;安装前请自行查看 GitHub 源码 与许可证。需要 Node.js >= 20。

从 npm 或 GitHub 安装(命令来自官方 README):

dsh plugin --profile web add dsh-warm                      # from npm
dsh plugin --profile web add github:JanEickholt/dsh-warm  # from GitHub, no npm needed

本地 link 方式:在 profiles/<profile>/package.json 的 dependencies 中加入 "dsh-warm": "link:/absolute/path/to/dsh-warm",并在 dsh.profile.bundles 中加入 "dsh-warm",在 profile 目录执行 pnpm install,再从终端重启 harness(例如 systemctl --user restart <your-dsh-unit>)。不要在会话内触发重启。插件的 cordis.patch.yml 会自动插入 loader 行,一般无需手改 cordis.patch.yml。

配置

首次加载会写入默认配置;编辑后需重启。路径:

$DSH_HOME/storages/dsh-warm/config.json

示例(字段含义见 README 表格):

{
  "enabled": true,
  "dryRun": false,
  "maxPings": 5,
  "providers": {
    "mistral":   { "intervalMs": 240000, "ttlMs": 302000, "marginMs": 60000 },
    "openai":    { "intervalMs": 240000, "ttlMs": 300000, "marginMs": 60000 },
    "anthropic": { "intervalMs": 240000, "ttlMs": 300000, "marginMs": 60000 }
  },
  "excludeTools": ["ask_user_question"],
  "minPrefixChars": 12000,
  "maxConcurrentPings": 2,
  "dailyPingBudget": 300,
  "pingTimeoutMs": 90000,
  "stats": { "enabled": true }
}

首次部署可设 dryRun: true,通过 journal 观察 would-be ping,再改为正式派发。日志与预算数据位于 $DSH_HOME/storages/dsh-warm/。

典型用法

无需在会话里单独调用 API:启用插件并配置 providers 后,当 agent 在长工具阻塞中且前缀长度不低于 minPrefixChars、提供商在表中、工具不在 excludeTools 时,warmer 自动按调度发 ping。

若使用 README 未列出的提供商或代理,应先实测 TTL,再添加类似 "opencode-go": { "intervalMs": 120000 } 的条目;空 {} 仅对三个内置已知提供商采用默认规则,未知名称会 warn 并丢弃。

适用场景与注意

适合:经常跑长 bash、等待子 agent 或 job_output,且对话前缀较大、提供商对 prompt cache 有 TTL 与 hit 折扣的 DSH 部署。

注意:

  • 插件不是 DeepSeek 或幻方官方组件;社区目录 SkillHub 条目 为独立站点。
  • 成本取决于你的提供商定价与前缀规模;README 给出了维护者环境下的量级说明(例如某提供商 hit 相对 miss 的折扣、单次 ping 与前缀 token 的关系),你的账户需自行核对。
  • dailyPingBudget 与 maxPings 用于限制 tail 风险;pingTimeoutMs 可中止卡住的流。
  • 调试可开 debug,事件写入 storages/dsh-warm/debug.jsonl。

结尾

dsh-warm 把 prompt-cache warming 限定在工具阻塞窗口,用 1 token 重放保持前缀一致,并配合 per-provider TTL、journal 与 UTC 日预算,面向 DSH 长工具场景下的 cache miss 与 TTFT 问题。进一步了解可看目录页与仓库:

  • https://www.skillhub.cn/plugins/JanEickholt/dsh-warm
  • https://github.com/JanEickholt/dsh-warm
羽毛球分组比赛记分
小程序二维码

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

Xiaoye