Skip to content
dsh.fish
Bundle

dsh-rice-guard

Repeat-output guard for DSH: watches the live assistant stream (thinking and text), catches verbatim loops and counting loops that differ only by a number, raises a manual-dismiss alarm, and cancels the runaway turn.

Source
XiaoSHang05
License
MIT
Updated
Updated 7 days ago

Readme

# 米饭守卫 · dsh-rice-guard

[![ci](https://github.com/XiaoSHang05/dsh-rice-guard/actions/workflows/ci.yml/badge.svg)](https://github.com/XiaoSHang05/dsh-rice-guard/actions/workflows/ci.yml)
[![release](https://img.shields.io/github/v/release/XiaoSHang05/dsh-rice-guard)](https://github.com/XiaoSHang05/dsh-rice-guard/releases/latest)
[![license](https://img.shields.io/github/license/XiaoSHang05/dsh-rice-guard)](https://github.com/XiaoSHang05/dsh-rice-guard/blob/main/LICENSE)

### 🐋 还在担心你的大鲸鱼偷吃你的米饭吗?

> 装上它 —— 大鲸鱼刚把同一句话嚼到第二遍,**碗就被端走了**。

| 报警测试 | 监控界面 |
| :---: | :---: |
| ![报警测试](docs/images/alarm-test.png) | ![监控界面](docs/images/monitor.png) |

| 卖点 | 说明 |
| --- | --- |
| 🍚 **一粒米都不多花** | 插件自身零 token 开销 —— 它不产字,它只是给字装了个刹车 |
| ⚡ **当场拿下** | 思考流和输出流一起盯,重复一冒头就掐断当前回合 |
| 🔔 **还不闷声** | 报警卡片弹出来、还带响声,想装作没看见都不行 |
| 🧂 **还能自己加料** | 你最怕它复读哪句,自己填进去,它就替你盯着那句 |

> 「大鲸鱼:我还能再吃一口。」
> 「米饭守卫:不,你不能。」

---

DSH 的独立插件:监视模型的**思考流和正式输出流**,发现重复内容后报警,并可自动停止当前回合。无需群聊插件;功能坞可选,不构成硬依赖。

## 安装与入口

兼容当前验证的 DSH `0.2.0-rc.2`;包声明范围为 `>=0.2.0-0 <0.3.0`。宿主需要提供 `webServer` 和 agent 运行时,以及 `@deepseek-ai/schemastery ^3.18.1`。

在包含本文件与 package.json 的插件目录中安装:

```powershell
dsh plugin --profile desktop add "link:$($PWD.Path)"
```

从 GitHub 安装(无需先克隆):

```powershell
dsh plugin --profile desktop add "github:XiaoSHang05/dsh-rice-guard"
```

**安装或更新宿主代码后重启 DSH**,然后刷新原来的网页。对话区会出现「监控」标签页。报警挂在全局浮层,无需一直停留在监控页。已安装过旧目录时,请先在插件管理器里处理旧链接,再装交付目录,避免两个目录的同名插件混用。

该命令走 DSH 的包安装流程。若现有 profile 的其他依赖触发包龄等供应链限制,安装可能失败;不要为本插件放宽整个 profile 的安全策略。检查失败信息后再处理对应依赖。本机此前已登记开发目录链接,交付文件夹不会自动替换那个链接。

## 使用

- 默认监视全部会话,思考与输出分别检测。
- 「启用监控」控制全局开关;会话行可以暂停/恢复单独监控。
- 「重复次数」控制**连续周期重复**阈值,默认 5。
- 「重复行占比」控制**独立的重复行检测**,默认最近 8 行中 75% 是已出现的行。调高重复次数不会关闭这条规则,需要同时调整重复行占比。
- 「编号循环占比」控制**编号循环检测**(第三条规则),默认 0.6。它抓的是「同一句式、每行只换一个数字」这类复读,见下节。
- 命中后会显示会话、流、重复片段、次数与打断状态。报警不会自动消失,点击「知道了」才关闭;关闭请求失败时卡片保留。
- 提示音使用 WebAudio。浏览器可能要求先与页面交互才能播放声音;声音不可用不影响报警卡片或宿主打断。
- 配置 `cancel: false` 可切换为只报警;`alarm: false` 不显示卡片;`sound: false` 关闭声音。
- 功能坞的客户端桥接可用时尝试注册入口;桥接不可用时仍可使用监控页和独立报警。

## 检测与边界

1. **周期重复**:在尾部窗口寻找连续相同片段;展示时把英文周期旋转到可读词边界。
2. **自定义短语**(默认关闭):你自己填几句最怕它复读的话,一行一句、每句至少两个字,出现 `customRepeat`(默认 5)次就报警。默认不填、不工作;在监控页打开开关并输入即生效。不同模型卡住的句子不一样,这条规则留给「你已经知道自己怕哪句」的情况。**正常行文里提到几次不算**:命中要求这几遍落在一个很短的跨度里,因此长文里引用你的句子多少次都不会触发。
3. **编号循环**:把每行的数字抹成同一个占位符后比较句式,**连续 ≥12 行同一句式**且**这些行里字母占字母加数字的比例 ≥60%** 才算命中。抓的是 `第 1 步:读取配置文件完成。`/`第 2 步:…` 这类每行都不同的假进度复读——它在字符和整行两个尺度上都不重复,另外三条规则看不见它。实测它可以在监控面板进度条始终 0% 的情况下白刷 6692–9492 字。
4. **重复行密度**:只归一化行首尾和连续空白,再比较相同的行。数字不同仍是不同内容,不把正常进度列表、带变化计数的重试日志视为重复行。
5. **一次命中锁存**:同一流在同一请求中只报告一次;新请求或重新配置时清空检测缓存。

四条规则按 `周期重复 → 自定义短语 → 编号循环 → 重复行密度` 的顺序问,先命中的算数:确切的复读不会被报成更模糊的信号;而你亲手填的句子比我们推导出来的句式更具体——它不是最具体的那条(逐字复读更具体),但比另外两条推测更具体。

阈值来自实测,不是估计:把这台机器有史以来记录的 970 MB 输出(约 4.2 万条正常回答)全部喂进真实检测器,最重的「同句式连跑」是 12 行、字母占比 34%(一份文件清单);编号循环最轻的一条是 67%。60% 落在这两者之间。12 行的余量来自同一份语料:正常输出里 8 行以上的同句式连跑,字母占比上限是 50%。

**已知边界(不是意外,是取舍)**:一条很长的正常枚举也会命中,例如 `port 8001 is listening` 连写 20 行。它与编号循环在语法上是同一件东西——一句话加一个新数字,没有任何东西能说明它会不会停——字母占比也分不开(那条清单是 79%,比最轻的真循环还高)。只能在「两个都报」和「两个都不报」之间选:报错一条长清单的代价是一张可关闭的报警卡,漏掉循环的代价是整个回合。不认同这个取舍的可以用 `structLines: 0` 关掉这条规则,或把行数阈值调高。

宿主通过 `agent/assistant-stream` 全局订阅实时增量,命中后调用:

```js
agent.cancel(
  { kind: 'hook', reason: 'rice-guard: repeated output' },
  { keepInbox: true },
)
```

这是**回合级止损**,不是逐段撤回:已经输出的字无法收回。保留排队输入不等于撤销已执行的工具操作。正常文章故意重复很多次也可能被打断,可按场景暂停监控或改成只报警。

只由空白或单个不同字符组成的片段会被排除,避免分隔线误报;因此单字符刷屏不是本版的检测目标。超出窗口长度/周期长度的重复也可能漏检。未加载插件之前的输出不能回溯检测。

## 配置

监控页的修改先立即作用于本插件,再尽力通过宿主 Settings 持久化。若 Settings 缺席或写入失败,配置仅保存在内存中。会话级暂停/恢复名单和命中历史均为内存数据,重启后清空。历史最多 50 条,默认界面展示最新 20 条;命中历史标题右侧的「清除历史」清空全部记录——**只清记录,不影响任何配置,也不会恢复已经被打断的回合**。

| 配置键 | 默认值 | 含义 |
| --- | --- | --- |
| enabled | true | 全局监控开关 |
| minRepeat | 5 | 连续周期重复阈值 |
| minUnit / maxUnit | 6 / 1024 | 搜索周期长度范围 |
| windowChars | 8192 | 各流保留的尾部窗口 |
| densityLines / densityRatio | 8 / 0.75 | 重复行窗口与占比 |
| structLines / structShare | 12 / 0.6 | 编号循环的行数与字母占比;`structLines: 0` 关闭该规则 |
| customPhrases | `[]` | 你自己填的句子,一行一句;空数组即关闭该规则(默认关闭) |
| customRepeat | 5 | 上面那些句子出现几次算循环 |
| cancel / alarm / sound | true | 自动打断、报警卡片、提示音 |
| scopeDefault | all | 默认全部监控;selected 仅监控显式选中的会话 |

`checkEvery=32` 是核心内部默认扫描步长,不是宿主 HTTP 配置字段。`watchText`、`watchReasoning`、`excludedSessions` 不属于本版配置 API。

## 结构

- `src/detect.js`:纯检测核心。
- `src/index.js`:宿主半,包含流订阅、配置、命中记录、取消与 HTTP/SSE。
- `src/client.js`、`src/client.css`:监控页与报警。
- `lib/client.js`:已构建的浏览器模块,无需再构建即可安装。
- `cordis.patch.yml`:安装条目,id 为 `rice-guard`。
- `CONTRACT.md`:跨半数据契约。
- `VALIDATION.md`:交付验证记录与证据边界。
- `scripts/`:构建、检查、测试与独立交付目录打包。
- `_probe/`:测试台与语料探针(离线,不随插件运行)。

读写接口位于 `/dsh-rice-guard/api/`:`health`、`state`、`config`、`monitor`、`hits`、`events`。SSE 只通知重新读取,状态以 GET state 为准;连接异常时退回轮询。接口使用宿主 Web 的认证边界,不额外提供公网认证。

## 开发与测试(完整源码目录)

交付文件夹与 GitHub 仓库包含源码、构建脚本和测试脚本,但**不携带 node_modules**。测试用的 React、jsdom 与 Schemastery 已声明为开发依赖,clone 之后先装一次:

```bash
npm install          # 装 jsdom / react / react-dom / @deepseek-ai/schemastery
npm run build        # 由 src/client.js + src/client.css 重建 lib/client.js
npm test             # 静态校验 + 八组测试台
node _probe/mutation.mjs
```

`npm test` 会先跑 `scripts/check.mjs`(产物新鲜度、语法、越权能力、颜色、清单路径、机器相关路径),再跑八组测试台,共 232 个断言。

变异台会临时改写八处源码、在 `finally` 中恢复,所以**不要与其他编辑或构建并行运行**。它把已知的 bug 放回源码,看台子会不会变红——抓不住的台子等于没有。

回放语料用的探针(`_probe/struct-real-fp.mjs`)读取本机 DSH 会话日志,把全部 assistant 消息重放给真实检测器。它需要本机的会话日志,**换一台机器会得到不同数字**——编号循环的阈值就是从这批数据里定的,方法、语料规模和结果都写在 `VALIDATION.md`。

发布前还有一道**历史**检查,因为 `git grep` 只看当前工作区,看不见已经改掉的内容:

```bash
git log --all -p --no-color > history.txt
node _probe/history-scan.mjs history.txt
```

它找的是家目录、`session-` 开头的会话 id、令牌与私钥。

**如果你 fork 了这份代码**:请在第一次 push 之前跑一遍。历史一旦推上去就不好收回了,清理已推送的历史意味着让所有人重新 clone;而只改工作区里的文件,对已经 clone 走的人没有任何影响。

MIT,见 LICENSE。改动记录见 `CHANGELOG.md`。

Install

dsh plugin --profile web add github:XiaoSHang05/dsh-rice-guard

Profile: web

  • This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source