前言¶
在 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 中:
dsh.profile.bundles数组加入"dsh-loop-guard"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 许可证,确认闸门默认值是否符合你的环境。
被误锁或需要停手时(均不依赖模型):
- 首选:设置 → 插件市场 → 已安装 → 关闭本插件「启用中」,然后重启 DSH(对本插件,市场开关不是热生效)。
- 只解锁预算:删除或编辑
~/.dsh/loop-guard-state.<profile>.json中对应会话记录,或调高 / 将DSH_LOOP_GUARD_MAX_TOKENS、DSH_LOOP_GUARD_SESSION_MAX_TOKENS设为0后重启。 - 卸载:按仓库提供的
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