dsh-loop-guard:给失控的 Agent 踩一脚刹车
一个长任务卡住时,最危险的不是报错,而是它看起来还在工作:同一句话滚到响应上限,反复读同一个文件,或者在 A→B→A 之间打转。dsh-loop-guard 是给 DSH 写的插件,专门在这些“没有进展”的时刻把回合停下来。

三种“看起来还在工作”
| 失控方式 | 看起来像什么 |
|---|---|
| 重复输出 | 模型把同一句话重复到响应上限,上下文越长越容易出现 |
| 工具循环 | 同一个目标反复读、同一个命令换参数重试,或者 A→B→A 来回走 |
| 失控回合 | 步数、时间、输出持续增长,却没有收敛 |
DSH 自带一个只检测“完全相同”工具调用的提醒,并且只建议模型停下。dsh-loop-guard 补的是更宽的那一段:近似重复、循环链、以及一直没有进展的回合。

它怎么判断
- 周期检测:在流式文本里找任意长度的重复单元,专门抓“一句短话无限重复”。
- 滚动哈希 shingle:用一个 64 字符切片覆盖窗口的大部分,抓重复段落,包括没有一直重复到末尾的情况。
- 近似工具链:同一个工具在相近参数上反复执行时累计计数,到阈值先提醒模型,再继续就取消。
- 回合刹车:步数、分钟、每小时调用次数、单回合输出长度都可以单独设上限。
工具调用的参数不会被当成模型输出。写文件、跑长命令是真实工作,不是退化。
安装
从 git 地址安装,需要把 <profile> 换成你实际运行的 DSH profile 名:
dsh plugin --profile <profile> add github:LISparrowC1/dsh-loop-guard也可以手动把包放进 $DSH_HOME/profiles/<profile>/node_modules/dsh-loop-guard/,再在 cordis.patch.yml 里加一条 loader。两种方式选一种,重复挂载会让插件跑两遍。
profile patch 只在 DSH 启动时读取,装完要重启 DSH。
配置
所有字段都有保守的默认值,想先观察再加严可以先开 dryRun:
- insert:
- id: loop-guard
name: 'dsh-loop-guard'
config:
enabled: true
dryRun: false
locale: zh
repetition:
minRepeats: 8
minSpanChars: 400
toolLoop:
fuzzyAdvisoryAt: 5
hardStopAt: 12
brake:
maxStepsPerTurn: 60
maxTurnMinutes: 30dryRun: true 只记录、不真正拦截;logEvents: true 会把判断写进 $DSH_HOME/loop-guard/events.jsonl,方便回看是哪一种循环触发的。
边界
它使用的是 DSH 文档里的扩展缝,不改 agent loop;被 guard 结束的回合会以明确的错误码收尾,并且最多按配置重跑一次。它不是“催模型继续”的工具,也不负责判断任务本身难不难——它只看有没有进展。
链接
dsh-loop-guard:给失控的 Agent 踩一脚刹车
https://lisparrowc1.github.io/2026/10/18/dsh-loop-guard/