dsh-spend-guard:为 DeepSeek Harness 补上滚动窗口花费熔断

前言

在 DeepSeek Harness(DSH)里跑长任务、开 workflow 或扇出子代理时,花费会在短时间内抬升。DSH 核心本身没有「花费上限」或「步数上限」一类硬开关;市场里常见的 cost-meter 类插件,预算多半是展示与变色预警,不会在超支时自动停掉当前回合。下面介绍社区插件 dsh-spend-guard(维护者 summmmerz),它在宿主侧用滚动时间窗口 + 人民币阈值做提醒与熔断,用来弥补「能算钱但不能止血」的空档。

这是什么

dsh-spend-guard 是 DSH 的宿主侧插件,分类为 admin-security。当前版本 0.1.1(MIT 许可证,需 Node.js ≥ 20)。它不做独立定价,而是轮询 dsh-cost-meter 的账本,统计「最近 N 分钟」内花了多少(默认 10 分钟),并在达到阈值时:

  • 最近 10 分钟内花费达到 ¥1 → 发送 Windows 通知提醒;
  • 达到 ¥2 → 结束当前这一轮(与 GUI「停止」按钮同一路径)。

插件在 SkillHub 插件目录 收录;源码与说明见 GitHub:summmmerz/dsh-spend-guard。SkillHub 是社区维护的插件索引站点,与 DeepSeek / 幻方无官方从属关系。

和现有做法差在哪里

README 里对生态现状做了对比,要点如下:

  • dsh 核心:没有任何花费/步数上限开关。
  • dsh-cost-meter 的「预算」:按今日/本月/累计/自定义区间展示;≥80% 预警、≥100% 超支时变色,不会暂停任务。
  • 其他预算类插件:例如 dsh-budget 是会话/日/月 + USD,peer 范围写死 <0.2.0;dsh-balance-tracker 以元为单位、动作为取消当前回合,但窗口是自然日,且依赖 dsh 0.2.0 已移除的 0.1.x 客户端包。

dsh-spend-guard 的组合是:滚动分钟窗口 + 金额(元)+ 停当前轮,面向「子代理/workflow 短时间烧穿」这类场景。

核心功能:怎么算「最近 10 分钟花了多少」

插件不自己定价,而是轮询差分 cost-meter 账本(默认路径 <DSH_HOME>/storages/cost-meter/ledger.json):

  1. 每 pollMs(默认 10s)读一次账本;
  2. 对当天聚合值 days[<日期>].cost 求相对上次轮询的增量;
  3. 增量按采样时刻写入环形窗口,窗口内求和即为「最近 N 分钟花费」;
  4. 金额口径跟随账本:pricingCurrency(默认 USD)× 账本 config.exchangeRate(默认 7.2)= 人民币元,与 dsh-cost-meter 界面数字同源。

field 可选 cost(含订阅折算)或 apiCost(纯 API 口径)。

为什么不用 per-session 明细

账本里 days[日期].sessions[] 是「每个会话当天的累计值」,在重负载日会截断。README 记录:2026-10-04 实测该数组只保留 200 条、仅覆盖当天 42% 的花费(33.95 / 81.17 元),缺口 47.2 元来自子代理——正是熔断最该拦住的消耗。当天聚合值在任意负载下是完整口径(含子代理),因此插件只信聚合差分。test/window.test.mjs 中有回归测试:sessions 为空或条目被替换/截断时,窗口仍须正确计数。

检测延迟:最多约 pollMs + cost-meter 落盘间隔(默认各 10s),对 10 分钟窗口熔断通常可接受。

降级:账本读不到(未装 cost-meter、格式变动、权限问题)时不熔断,仅每 5 分钟打一条警告日志——算不出钱时不应拖垮 harness。

核心功能:熔断时怎么停

停止当前回合调用:

agent.cancel({ kind: 'user' }, { keepInbox: true })

与 GUI「停止」一致(README 指向 dsh-api-session-controller 的 stop 实现):只结束当前回合,不销毁会话,已排队的输入保留。

默认 cancelScope: all 会结束所有正在运行的 agent(含子代理)——熔断目的是立刻止血。若只希望结束主会话,可设 cancelScope: root。

配置

默认值可通过 profile 的 cordis.patch.yml 覆盖(插件包内自带 cordis.patch.yml,见 package.json 的 dsh.bundle.patch):

- id: dsh-spend-guard
  config:
    windowMs: 600000        # 滚动窗口(ms),默认 10 分钟
    warnYuan: 1             # 窗口内达到该金额(元) → 提醒
    stopYuan: 2             # 窗口内达到该金额(元) → 结束当前回合
    pollMs: 10000           # 账本轮询间隔(ms)
    field: cost             # cost(含订阅折算) | apiCost(纯 API 口径)
    exchangeRate: null      # 汇率覆盖;null = 用账本的 config.exchangeRate
    warnCooldownMs: 60000   # 两条提醒之间的最小间隔(ms)
    stopCooldownMs: 60000   # 两次熔断之间的最小间隔(ms);0 = 超限就一直熔断
    cancelScope: all        # all=结束所有在跑的 agent(含子代理) | root=只结束主会话
    toast: true             # 是否发 Windows 通知
    debug: false            # 每次轮询打印窗口金额
    ledgerPath: ""          # 账本路径;留空=自动定位

整插件禁用:

- id: dsh-spend-guard
  disabled: true

启用后,日志里会出现类似一行(路径以本机为准):

[dsh-spend-guard] 已启用: 滚动 10 分钟, 提醒 ¥1.00 / 熔断 ¥2.00, 账本 <path>

提醒与熔断分别带 提醒: / 熔断: 前缀,便于 grep。

安装与启用

README 未给出单独的 dsh plugin add … 安装命令。可按社区习惯从 SkillHub 目录页 或 GitHub 仓库 获取插件包,并按 DSH 的 Cordis 插件/bundle 方式装入 profile。

前提:本机需已安装并运行 dsh-cost-meter,且账本持续落盘;否则插件只记警告、不熔断。

安装前建议自行阅读源码与 MIT 许可证:插件以当前 DSH 进程权限运行,会读取账本并在超阈值时调用 agent.cancel。

典型用法与自测

日常无需额外操作:装好插件、保证 cost-meter 在跑,即按默认 10 分钟 / ¥1 提醒 / ¥2 熔断 工作。若要改阈值或只熔断主会话,改上一节的 cordis.patch.yml 即可。

仓库自带窗口逻辑测试:

node test/window.test.mjs

或用 npm script:

npm test

测试覆盖:首次轮询建基线、按当天聚合增量计数、1 元提醒 / 2 元熔断、sessions[] 为空或截断时的回归、跨日不误熔断、聚合回退不作负增量、cancelScope 语义,以及熔断须走 cancel({ kind: 'user' }, { keepInbox: true })。

适用场景与已知边界

适合:本地长期开 DSH、容易触发 workflow/子代理扇出、希望在「最近几分钟」维度上硬停当前回合的开发者。

注意:

  • 依赖 dsh-cost-meter;未启用时静默不生效(仅日志)。
  • 窗口精度受轮询间隔限制(默认约 10–20s)。
  • 只统计 DSH 账本内的花费;其他终端或网页产生的消费不在窗口内。
  • 默认熔断会打断所有在跑回合(含子代理);多会话并行且不想被连带中断时,用 cancelScope: root。

结尾

dsh-spend-guard 用滚动窗口把 cost-meter 已有账本变成「能提醒、能停轮」的守卫,默认 ¥1 通知 / ¥2 熔断,并刻意用当天聚合差分避免子代理在 sessions[] 截断时被漏计。目录页:https://www.skillhub.cn/plugins/summmmerz/dsh-spend-guard;源码:https://github.com/summmmerz/dsh-spend-guard。

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

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

小夜