dsh-guard-degen
在模型输出边界检测并拦截退化符号流,并提供修复被污染工件与会话的工具。
安装
dsh plugin --profile web add dsh-guard-degen
沙箱测试
这个插件还没测过。提交测试 →
README
dsh-guard-degen


退化守卫:给 DeepSeek Harness 装一道「模型符号流退化」的护栏,并附赠工件/会话的清理工具。
为什么需要它
某些模型(实测集中在 MiniMax 系列)在思考阶段会退化成单字符长跑,例如:
⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ ⇒ …
退化不一定是同一个符号的重复,也可能是多字符单元整段整段地重复:
⇒⇒§ ⇒⇒§ § ⇒⇒§ ⇒⇒§ § …(整段整段地重复下去)
这种输出会一路写进会话文件、思考内容,甚至被模型自己写进仓库里的文档和源码。
一旦会话被污染,「继续」只会得到更多同样的符号流——会话本身已经不可用,
但文件里的污染会留给下一个会话,形成越积越多的脏工件。
本插件做四件事:
1. 流式拦截——在模型输出到达界面前检测符号游程,按策略截断/消毒/删除,避免污染进入会话与磁盘。
2. 写后校验——写文件类工具执行后回读文件,发现退化内容就阻断并把反馈回灌给模型。
3. 重试与换模——退化导致请求失败时自动重试(同模型 N 次),必要时切换到指定模型。
4. 工件与会话清理——扫描仓库与历史会话,产出报告,由你选择哪些要修(默认只预览,不写盘)。
流式守卫有四种处置方式,思考流与正文流各设各的:
方式 · 退化段怎么处理
off · 不检测也不改:退化段原样流过去。
truncate(默认) · 切断该通道,本轮以「退化」错误收尾。
sanitize · 把退化段折叠成一个标记(〔退化符号流×N〕),本轮继续。
remove · 退化段整段删掉,一个标记都不留,本轮继续。
remove 是最安静的一种:模型输出读起来就像退化段从来没写过。
被删掉的原文仍然记进判定流水作为证据,但与 sanitize 不同,它不可回退——
流里已经没有东西可以写回去了。
工件治理覆盖两种退化形态:单个符号的游程,以及多字符单元整段重复
(例如 ⇒⇒§ ⇒⇒§ § 反复出现)。勾选条目后点「预览修复」,界面直接给出
红/绿对照(红=修复前的原文,绿=修复后的样子),确认无误再落盘。
「多字符单元整段重复」这一形态在四个地方都受治理,各有各的开关与份数阈值:
位置 · 开关 · 份数阈值 · 默认
工件修复 · artifacts.unitRun · artifacts.unitRepeats · 开 / 4
流式守卫 · stream.unitRun · stream.unitRepeats · 开 / 4
工具守卫 · toolGuard.unitRun · toolGuard.unitRepeats · 开 / 4
会话遮蔽 · sessions.unitRun · sessions.unitRepeats · 开 / 4
关掉某个开关,只是那一个地方退回「只管单符号游程」;其它地方照旧,
四道守卫互不牵连。份数阈值同理——四处各调各的,调大更宽松,调小更敏感。
安装
一条命令(包已发布到 npm):
dsh plugin --profile <你的 profile> add dsh-guard-degen
<你的 profile> 换成宿主实际使用的 profile 名(dsh web 通常是 web,
桌面端应用用的是它自己的 profile,名字未必相同)。
也可以直接从仓库装:
dsh plugin --profile <你的 profile> add github:chunqing286/dsh-guard-degen
也可以在 DSH 界面里点一下:插件 → 发现,添加索引源
https://cdn.jsdelivr.net/npm/dsh-plugin-catalog/index.json,
搜「退化守卫 / guard degen」,点安装。
装好之后,插件自带的 cordis.patch.yml 会作为 bundle 层被应用,宿主半边与浏览器半边一起装上,
运行时会话和设置页里立刻能看见「退化守卫」分区。
手动安装
不想用包管理器的话,在
$DSH_HOME/profiles/<你的 profile>/cordis.patch.yml 里追加一条 insert:
- insert:
- id: guard-degen
name: <指向插件目录的相对路径或包名>
name 里的相对路径以该 patch 文件所在目录为锚点(这是宿主 dsh-app-boot 的既定语义),
所以指向任何本地目录都可以;改成包名则需要该包能被解析(profile 的 node_modules 或安装自带包)。
走相对路径安装时,插件目录要能解析到自己的运行时依赖
(@deepseek-ai/dsh-typert-protocol、@deepseek-ai/schemastery,以及它们依赖的
@deepseek-ai/cordis、@deepseek-ai/cosmokit);走 npm 安装则由 npm 自动装齐。
也可以走 profile 的 bundle 机制:在 profile 的 package.json 里写
"dsh": { "profile": { "bundles": ["dsh-guard-degen"] } } 并把包列进 dependencies,
插件自带的 cordis.patch.yml 会作为 bundle 层被应用。
宿主侧插件的 Config 是原生 Schemastery schema,因此设置页会自动渲染出配置表单;
插件自己在 设置 里注册了一个独立分区 退化守卫(与「插件」并列),
分区下面有三个页签,每个页签各占一页(互斥显示,不会叠在一起):
- 配置:全部配置的图形入口。
- 日志:判定流水——每一次退化判定都记着命中了哪条规则、发生在哪个会话、
守卫做了什么处置。列表带会话ID与会话名称两列,可以按会话筛选
(下拉选会话,或按会话 ID / 名称做文本筛选);点一条记录能看检出的退化特征
(哪个单元重复了几次),以及红/绿对照(红=治理前的退化原文,
绿=治理后留下的东西:净化时是折叠标记,删除时是「什么也没留下」的说明,
截断时是「后面没有了」的说明;
没有自动治理时绿行写明「只记录判定,未改写」)。误报反馈入口也在这里。
- 工件:工件扫描报告、被污染会话列表,逐项勾选后再修复。
会话列表同时给出会话标题和会话 id;选中会话点「预览遮蔽」后,
界面直接给出红/绿对照(红=遮蔽前原文,绿=遮蔽后的标记),
不用再去翻生成的磁盘文件。
会话遮蔽有两种内容方式,工件页与会话视图里都能切换:
折叠成标记:把整段退化折叠成〔退化内容已遮蔽×N〕。整段删除:整段删掉,不留痕迹(默认)——磁盘上的内容里一个字标记都不写,
对照视图的绿行显示的就是删完剩下的文字(符号是真的没了,
不是「被换成了某一句话」)。整条内容都被删光时,绿行会写明「整条内容被删空了」。
切换之后预览会立刻按新方式重算,所以不用先落盘再回头看文件。
遮蔽还分两种落地方式(工件页与会话视图里都能切换):
另存为副本:把遮蔽后的内容写成新文件,原档不动。直接改写会话存档:两个页面默认都是这一种——遮蔽的就是眼前这个会话本身
(原档先备份),不用再去别处找一份副本。
会话视图里的「退化守卫」
会话页的视图行里除了「对话 / 轨迹 / 上下文 / 编队」,还有一个 退化守卫 视图
(与它们并列),针对的就是当前会话:
- 工作区工件:扫描范围是当前会话所在的工作目录(由宿主从会话存档头里解析,
浏览器只传会话 id,不能自选目录),修的也是这个目录里的工件。
- 当前会话:直接遮蔽当前会话存档里的退化内容,方式与工件页一致。
「执行遮蔽」默认就地改写当前会话:会话存档被原子替换(先写临时文件再改名),
原文件备份到 ~/.dsh/guard-degen/backups/,写完回读复核。
三件事要清楚:
- 界面上的历史消息不会当场变——对话视图是宿主内存里的内容,
重新打开会话才能看到改写后的文本。
- 进行中的会话会继续追加:改写之后宿主新写入的消息仍接在改写后的内容后面。
- 落盘前会以最新内容重算:预览之后若又写入了新消息,遮蔽按最新内容重算一遍,
不会把新消息抹掉。
命令
命令 · 作用
/degen-status · 看当前守卫状态、规则表、最近判定
/degen-scan [路径…] · 只读扫描工件(默认扫当前工作区),产出报告
/degen-fix <文件路径…> · 修复指定文件里的退化内容(先备份再改写)
/degen-clean · 遮蔽当前会话里的退化内容(默认就地改写存档,原档先备份)
/degen-ack · 把最近一次判定登记为误报,并调高对应规则阈值一档
配置要点
键 · 默认 · 说明
enabled · true · 总开关
modelIncludes · [] · 只守卫这些模型(留空=全部模型)
stream.reasoning · truncate · 思考流策略:off/truncate/sanitize/remove
stream.text · truncate · 正文流策略:off/truncate/sanitize/remove
stream.holdbackChars · 256 · 保留窗口:判定前不立即放行的字符数
stream.maxInterventionsPerCall · 1 · 单次调用最多干预次数(防止反复改写)
stream.unitRun · true · 流式守卫是否治理「多字符单元整段重复」
stream.unitRepeats · 4 · 流式守卫里单元重复多少份才算退化
toolGuard.mode · warn · 工具参数里的退化:off/warn/refuse
toolGuard.scope · all · all/write-only/oversized-only
toolGuard.unitRun · true · 工具守卫是否治理「多字符单元整段重复」
toolGuard.unitRepeats · 4 · 工具守卫里单元重复多少份才算退化
postExecute.enabled · false · 写后校验开关(回读文件,有 IO 成本)
postExecute.roots · [] · 限定校验范围;留空=工作区内
retry.policy · same-model · same-model/steer/no-retry
retry.maxSameModelRetries · 1 · 同模型重试次数
retry.switchModel.enabled · false · 是否允许自动换模型(需显式给 provider+model)
artifacts.mode · remove · 修复方式:collapse(折叠成标记)/remove(删除)
artifacts.apply · false · 默认只预览,不写盘
artifacts.unitRun · true · 是否治理「多字符单元整段重复」这类退化
artifacts.unitRepeats · 4 · 单元重复多少份才算退化
sessions.mode · replace · 会话遮蔽方式:copy(另存新档)/replace(覆盖原档)
sessions.maskMode · remove · 会话遮蔽内容方式:collapse(折叠成标记)/remove(整段删除)
sessions.unitRun · true · 会话遮蔽是否治理「多字符单元整段重复」
sessions.unitRepeats · 4 · 单元重复多少份才算退化
logging.enabled · true · 判定流水落盘
…(truncated)
以上内容来自 npm 上的 README,我们只做格式化,不改内容。
版本
npm 上共发布:3,最后更新 2026-10-31