中文EN

dsh-guard-degen

toolsnpmMIT

在模型输出边界检测并拦截退化符号流,并提供修复被污染工件与会话的工具。

下载量 / Downloads
569

安装

dsh plugin --profile web add dsh-guard-degen

沙箱测试

这个插件还没测过。提交测试 →

README

dsh-guard-degen

![CI](https://github.com/chunqing286/dsh-guard-degen/actions/workflows/ci.yml)

![npm](https://www.npmjs.com/package/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

链接

GitHub · Homepage · npm

同类插件