dsh-loop-guard:在工具调用前拦截烧钱循环

前言

在 DSH 里跑 agent,成本往往不在「多聊几句」,而在工具层:同一条命令反复执行、两个工具来回切换、同一个错误撞了两次还在重试,或者一轮问答里子代理扇出失控——这些都会把 token 账单悄悄拉高。靠模型自觉停手并不稳;单靠用户级技能提醒,也挡不住真的把调用发出去的那一步。

dsh-loop-guard 是维护者 514koishi-495 发布的本地工作流类插件(社区目录分类:工作流)。它在每次工具调用真正发生之前,用纯算术规则判断「这一步是不是又在白烧钱」,需要时直接拦下;不看内容、不调模型。README 写明它与用户级技能 ~/.dsh/skills/no-waste-loop-guard/ 配套:技能负责自觉,插件负责赖不掉。设计原则之一是绝不把 agent 永久锁死——各处拦截都带逃生门。

这是什么

dsh-loop-guard(npm 包名 dsh-loop-guard,当前版本 1.1.7,MIT 许可证,要求 Node.js >= 22)通过 Cordis patch 挂在 DSH 工具调用链上。账本按「计费输入」统计:inputTokens + cacheReadTokens + cacheWriteTokens,而不是只看未命中缓存的 inputTokens。

插件状态与会话计数会落盘到 ~/.dsh/loop-guard-state.<profile>.json(认不出 profile 时退回 ~/.dsh/loop-guard-state.json)。任何开关或配置改动,生效时间都是下次启动 DSH——装完、改完都要完全退出进程再打开(关窗口不算)。

核心功能:闸门一览

下面按 README 中的表格归纳主要闸门(拦截时返回以 LOOP_GUARD_ 开头的错误码)。

类型 默认阈值(可调) 拦截码示例
本轮预算 6000 万计费输入(含该轮所有子代理) LOOP_GUARD_BUDGET_EXHAUSTED
会话总预算 10 亿(只算本会话,不含子代理) LOOP_GUARD_SESSION_BUDGET
家族预算 默认不设限 LOOP_GUARD_FAMILY_BUDGET
同签名重复 4 次(read 为 5 次) LOOP_GUARD_REPEAT
同骨架重复 10 次(命令行归一化) LOOP_GUARD_SKELETON
乒乓循环 3 轮 A-B-A-B LOOP_GUARD_CYCLE
同一个错误 2 次(只拦报错的那个工具) LOOP_GUARD_SAME_ERROR
子代理请求 / 工具数 150 / 250 LOOP_GUARD_SUBAGENT_REQUESTS 等
扇出与子代理异常 并发子代理 8、workflow 内 agent 24 等 LOOP_GUARD_FANOUT 等

本轮预算按轮清零:一轮 = 一条用户消息及其之后的全部工作;子代理消耗落在它花钱时所在的那一轮。会话总预算跨轮累计且只算宿主会话自己。家族预算统计自己加所有后代的跨轮累计;若希望「整场含子代理不超过 10 亿」,可将 DSH_LOOP_GUARD_FAMILY_MAX_TOKENS 设为 1000000000。

「同一个错误」门只认「工具名 + 归一化错误正文」;插件自己的拒绝、常见瞬时网络错误会跳过,避免误锁。todo_write、ask_user_question 等收尾或与用户交互的工具在名单内永不拦截。

逃生门分两种语义(README 强调):重复类闸门与三道预算门是「同一处连拦 3 次(默认 maxDenials)放行一次」;配额类闸门是「首超 warn 并放行一次,此后一直拦」。记账失败不会让会话出错——守卫变瞎,但不挡正常对话。

安装与启用

运行时目录与源码目录在 Windows 示例如下(Linux 下将 %USERPROFILE% 换为 $HOME 即可对照):

%USERPROFILE%\.dsh\profiles\desktop\node_modules\dsh-loop-guard\    ← 运行时读的
%USERPROFILE%\.dsh\local-plugins\dsh-loop-guard\                    ← 源码与测试

从 GitHub 给他人安装时,README 给出的步骤是克隆到本地插件目录,并在 profile 的 package.json 登记两处:

git clone https://github.com/514koishi-495/dsh-loop-guard "$HOME\.dsh\local-plugins\dsh-loop-guard"

目录名须保持 dsh-loop-guard。在 profile 的 package.json 中:

  1. dsh.profile.bundles 数组加入 "dsh-loop-guard"
  2. dependencies 加入 "dsh-loop-guard": "file:../../local-plugins/dsh-loop-guard"(路径相对 profile 目录)

使用 file: 依赖是故意的:避免从 registry 拉包或 lockfile 缺项导致 bundle 回滚。改源码后需把 lib、package.json、cordis.patch.yml 同步到 node_modules 下对应目录,再完全退出 DSH 后重启。

配套技能 no-waste-loop-guard 不在本仓库,需单独放到 %USERPROFILE%\.dsh\skills\no-waste-loop-guard\SKILL.md。

典型用法:阈值与环境变量

多数阈值不用改代码,在启动 DSH 前设置环境变量即可。常用项如下:

变量 默认 说明
DSH_LOOP_GUARD_MAX_TOKENS 60000000 本轮上限;0 或负数表示不设限
DSH_LOOP_GUARD_SESSION_MAX_TOKENS 1000000000 会话累计;0 或负数表示不设限
DSH_LOOP_GUARD_FAMILY_MAX_TOKENS 不设限 家族累计
DSH_LOOP_GUARD_REPEAT_LIMIT 4 同签名重复
DSH_LOOP_GUARD_CYCLE_LIMIT 3 乒乓循环
DSH_LOOP_GUARD_SAME_ERROR_LIMIT 2 同一错误
DSH_LOOP_GUARD_MAX_DENIALS 3 逃生门连拦次数

预警比例默认 DSH_LOOP_GUARD_WARN_RATIO=0.7。完整列表见仓库 README。

本地开发可运行包内测试:

node test/smoke.mjs && node test/guards.mjs && node test/persist.mjs write && node test/persist.mjs read && node test/persist.mjs migrate

(在插件源码目录下执行,与 package.json 的 test 脚本一致。)

适用场景与注意

适合谁:长期在 DSH 里跑多工具、多子代理工作流的开发者;希望用硬规则兜住重复调用、循环和单轮 token 上限,又不想靠模型自我约束的场景。

权限与安全:插件以当前 DSH 进程权限运行,会读写 ~/.dsh/ 下状态文件并拦截工具调用。安装前应阅读源码与 MIT 许可证,确认闸门默认值是否符合你的环境。

被误锁或需要停手时(均不依赖模型):

  1. 首选:设置 → 插件市场 → 已安装 → 关闭本插件「启用中」,然后重启 DSH(对本插件,市场开关不是热生效)。
  2. 只解锁预算:删除或编辑 ~/.dsh/loop-guard-state.<profile>.json 中对应会话记录,或调高 / 将 DSH_LOOP_GUARD_MAX_TOKENS、DSH_LOOP_GUARD_SESSION_MAX_TOKENS 设为 0 后重启。
  3. 卸载:按仓库提供的 uninstall.ps1 流程操作后完全退出再打开。

预算到线是粘性的:到线后会持续拦截,累计值从存档恢复;收尾仍可通过免拦工具与逃生门放行。社区插件目录由 SkillHub 等站点维护,与 DeepSeek / 幻方无官方从属关系;安装来源以 GitHub 仓库与目录页说明为准。

结尾

dsh-loop-guard 把「防烧钱」做成工具调用前的代码闸门:重复、循环、子代理扇出与会话级预算都有默认判据,且为误拦留了重启与市场开关等人工出口。需要进一步了解参数与变更记录,可直接查阅仓库 README 与 CHANGELOG.md。

  • 社区目录页:https://www.skillhub.cn/plugins/514koishi-495/dsh-loop-guard
  • GitHub:https://github.com/514koishi-495/dsh-loop-guard
羽毛球分组比赛记分
小程序二维码

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

Xiaoye