mimo-stable 1.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mimo_stable-1.1.0/LICENSE +21 -0
- mimo_stable-1.1.0/PKG-INFO +299 -0
- mimo_stable-1.1.0/README.md +267 -0
- mimo_stable-1.1.0/mimo_stable.egg-info/PKG-INFO +299 -0
- mimo_stable-1.1.0/mimo_stable.egg-info/SOURCES.txt +14 -0
- mimo_stable-1.1.0/mimo_stable.egg-info/dependency_links.txt +1 -0
- mimo_stable-1.1.0/mimo_stable.egg-info/entry_points.txt +2 -0
- mimo_stable-1.1.0/mimo_stable.egg-info/top_level.txt +1 -0
- mimo_stable-1.1.0/pyproject.toml +22 -0
- mimo_stable-1.1.0/scripts/__init__.py +1 -0
- mimo_stable-1.1.0/scripts/benchmark_fixtures.py +51 -0
- mimo_stable-1.1.0/scripts/check_version.py +35 -0
- mimo_stable-1.1.0/scripts/detect_loop.py +465 -0
- mimo_stable-1.1.0/scripts/recovery_policy.py +78 -0
- mimo_stable-1.1.0/setup.cfg +4 -0
- mimo_stable-1.1.0/tests/test_detector.py +62 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 xli498
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mimo-stable
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: Record, detect, and apply engineering guardrails for degenerate loops in LLM output and tool calls
|
|
5
|
+
Author: xli498
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 xli498
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Requires-Python: >=3.10
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# LLM Degenerate Loop Guardrails
|
|
34
|
+
|
|
35
|
+
[English README](README.en.md)
|
|
36
|
+
|
|
37
|
+
[](https://github.com/xli498/mimo-stable/actions/workflows/quality.yml)
|
|
38
|
+
[](https://github.com/xli498/mimo-stable/releases)
|
|
39
|
+
[](LICENSE)
|
|
40
|
+
[](pyproject.toml)
|
|
41
|
+
|
|
42
|
+
面向国产及其他 LLM 的输出流和工具调用退化循环经验记录与检测工具。
|
|
43
|
+
项目最初来自 MiMo `reasoning=True` 场景的重复输出观察,后来发现类似现象
|
|
44
|
+
也可能出现在 GLM 等其他模型中。这里分享的是识别、止损和复盘方法,不是
|
|
45
|
+
针对任何模型的根治方案。
|
|
46
|
+
|
|
47
|
+
> [!IMPORTANT]
|
|
48
|
+
> 当前项目的核心能力是**检测与止损**,不是自动修复模型。检测到循环后,
|
|
49
|
+
> 由上层运行器决定停止、切换模型、重试或人工复核。
|
|
50
|
+
|
|
51
|
+
## 目录
|
|
52
|
+
|
|
53
|
+
- [30 秒开始](#30-秒开始)
|
|
54
|
+
- [问题描述](#问题描述)
|
|
55
|
+
- [三层防御体系](#三层防御体系)
|
|
56
|
+
- [检测策略](#检测策略)
|
|
57
|
+
- [可复现证据](#可复现证据)
|
|
58
|
+
- [如何记录和分享新案例](#如何记录和分享新案例)
|
|
59
|
+
- [工程侧缓解措施](#工程侧缓解措施不是模型修复)
|
|
60
|
+
- [示例](#示例)
|
|
61
|
+
- [文件结构](#文件结构)
|
|
62
|
+
- [测试](#测试)
|
|
63
|
+
- [安装与集成](#安装与集成)
|
|
64
|
+
- [许可](#许可)
|
|
65
|
+
- [行为契约](#行为契约)
|
|
66
|
+
|
|
67
|
+
## 30 秒开始
|
|
68
|
+
|
|
69
|
+

|
|
70
|
+
|
|
71
|
+
> **核心流程:** 模型输出 → 检测信号 → 保守决策摘要 → 上层运行器处理。
|
|
72
|
+
> 检测器和恢复层都不执行重试、模型切换或工具调用。
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
python3 scripts/detect_loop.py --log fixtures/loop_detected.log
|
|
76
|
+
python3 tests/test_detector.py
|
|
77
|
+
python3 scripts/benchmark_fixtures.py
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
机器集成使用单份 JSON 摘要和退出码:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
python3 scripts/detect_loop.py --json --timeout 60 --log fixtures/loop_detected.log
|
|
84
|
+
# 退出码 0 = 未检测到;1 = 检测到;2 = 参数/输入错误
|
|
85
|
+
|
|
86
|
+
# 将检测摘要转成保守的恢复决策(只输出决策,不执行重试)
|
|
87
|
+
python3 scripts/detect_loop.py --json --timeout 60 --log fixtures/loop_detected.log | \
|
|
88
|
+
python3 scripts/recovery_policy.py --retryable
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
典型检测摘要:
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"loop_detected": true,
|
|
96
|
+
"details": {
|
|
97
|
+
"type": "consecutive_identical_output"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
| 退出码 | 含义 |
|
|
103
|
+
| :---: | :--- |
|
|
104
|
+
| `0` | 未检测到循环 |
|
|
105
|
+
| `1` | 检测到循环 |
|
|
106
|
+
| `2` | 参数或输入错误 |
|
|
107
|
+
|
|
108
|
+
## 问题描述
|
|
109
|
+
|
|
110
|
+
在特定模型、参数、任务和上游服务条件下,LLM 可能进入退化循环状态:
|
|
111
|
+
|
|
112
|
+
- **症状**:同一段输出重复,持续时间异常增长
|
|
113
|
+
- **语言切换**:特定中文任务中切换为英文,且无视语言约束
|
|
114
|
+
- **功能停滞**:不执行有进展的工具调用,仅重复输出文本
|
|
115
|
+
- **根因尚未确定**:`reasoning=True`、上下文长度、工具链状态、服务端实现等
|
|
116
|
+
都可能是相关变量;本项目不把相关性写成因果结论。
|
|
117
|
+
|
|
118
|
+
类似表现并不等于同一个 bug。MiMo、GLM 或其他国产模型的案例必须分别记录
|
|
119
|
+
模型版本、端点、参数、任务和时间,不能把一个模型的触发概率或规避经验直接
|
|
120
|
+
外推到另一个模型。
|
|
121
|
+
|
|
122
|
+
## 三层防御体系
|
|
123
|
+
|
|
124
|
+
### 第一层:工程侧限制(止损,不是修复)
|
|
125
|
+
|
|
126
|
+
在运行器中设置硬性超时和 Token 限制(具体字段以所用框架官方文档为准):
|
|
127
|
+
|
|
128
|
+
```yaml
|
|
129
|
+
# 示例结构;字段名和数值必须按实际框架、模型与任务校准。
|
|
130
|
+
provider:
|
|
131
|
+
model: <your-model>
|
|
132
|
+
timeout: <task-specific-limit>
|
|
133
|
+
max_tokens: <task-specific-limit>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
> [!WARNING]
|
|
137
|
+
> **历史案例,不是通用推荐:** 最初 MiMo 案例曾使用
|
|
138
|
+
> `timeoutSeconds=180` 和 `maxTokens=8000` 限制单次资源占用。这些值不是
|
|
139
|
+
> 跨模型配置建议,也不表示能够改变模型进入循环的概率。
|
|
140
|
+
|
|
141
|
+
作用是限制单次故障的最长资源占用,不改变模型本身的循环概率。
|
|
142
|
+
|
|
143
|
+
### 第二层:行为检测
|
|
144
|
+
|
|
145
|
+
运行 `scripts/detect_loop.py` 实时监控模型输出,检测连续重复。
|
|
146
|
+
|
|
147
|
+
管道输入以空行切分输出块;`--timeout` 的持续时间只在流式输入期间有实际意义,
|
|
148
|
+
离线日志使用日志中的块时间戳。
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
python3 scripts/detect_loop.py --log logs/sample_degenerate_loop.log
|
|
152
|
+
model_output 2>&1 | python3 scripts/detect_loop.py
|
|
153
|
+
python3 scripts/detect_loop.py --json --log logs/sample_degenerate_loop.log
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
检测规则:
|
|
157
|
+
|
|
158
|
+
- 连续 3+ 次输出块完全相同或高度相似
|
|
159
|
+
- 连续 3+ 次工具调用参数完全相同
|
|
160
|
+
- 已知副作用工具的重复调用
|
|
161
|
+
- 显式声明中文任务后的英文漂移
|
|
162
|
+
- 可选的持续时间门控
|
|
163
|
+
|
|
164
|
+
### 第三层:行为规则(AGENTS.md)
|
|
165
|
+
|
|
166
|
+
在 AGENTS.md 或对应运行器规则中加入检测和处置约束。详见
|
|
167
|
+
[SKILL.md](SKILL.md)。该层是提示和流程规则,不替代运行时检测。
|
|
168
|
+
|
|
169
|
+
## 检测策略
|
|
170
|
+
|
|
171
|
+
默认文本重复采用**持续时间门控**:需要达到 `--threshold` 次相似输出,且
|
|
172
|
+
重复窗口达到 `--timeout` 秒。这适合日志后处理,减少短暂重复的误报。
|
|
173
|
+
|
|
174
|
+
如果上层需要在达到重复次数后立即得到信号,可显式使用:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
python3 scripts/detect_loop.py --text-mode instant --log fixtures/repeated_but_short.log
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
工具调用重复和已知副作用工具的重复调用不受文本持续时间门控影响;生产集成仍应
|
|
181
|
+
结合幂等键、调用结果和重试原因做二次判断。
|
|
182
|
+
|
|
183
|
+
## 可复现证据
|
|
184
|
+
|
|
185
|
+
> [!NOTE]
|
|
186
|
+
> Fixture 用于稳定回归,历史日志用于记录具体观察;两者的证据性质不同。
|
|
187
|
+
|
|
188
|
+
`logs/sample_degenerate_loop.log` 是历史观察日志;`logs/fixed_normal_run.log`
|
|
189
|
+
是一次正常运行日志。它们只说明具体案例,不构成模型故障率统计,也不证明
|
|
190
|
+
任何参数能“修复”模型。
|
|
191
|
+
|
|
192
|
+
当前仓库没有足够实验次数估计任何模型的通用触发概率,因此不提供“某模型
|
|
193
|
+
有 X% 概率出问题”之类的结论。
|
|
194
|
+
|
|
195
|
+
## 如何记录和分享新案例
|
|
196
|
+
|
|
197
|
+
建议至少记录以下信息,并在发布前脱敏:
|
|
198
|
+
|
|
199
|
+
- 模型与精确版本、供应商/端点、调用时间段
|
|
200
|
+
- `reasoning`、temperature、max tokens、上下文规模等实际参数
|
|
201
|
+
- 任务类型、是否多轮、是否涉及工具调用
|
|
202
|
+
- 重复发生前后的输出块摘要或哈希,不公开密钥、隐私和完整敏感工具参数
|
|
203
|
+
- 是否真正发生资源浪费/副作用,检测器是否报警,检测延迟
|
|
204
|
+
- 重试、切换模型或改变参数后的结果
|
|
205
|
+
|
|
206
|
+
案例记录用于复盘和横向比较,不应写成因果证明。没有原始证据时,使用
|
|
207
|
+
“观察到”“可能相关”“尚未复现”,不要使用“必然”“已证明”“彻底解决”。
|
|
208
|
+
|
|
209
|
+
## 工程侧缓解措施(不是模型修复)
|
|
210
|
+
|
|
211
|
+
| 措施 | 作用 | 边界 |
|
|
212
|
+
| :--- | :--- | :--- |
|
|
213
|
+
| `timeoutSeconds=180` | 限制单次资源损失 | 不改变模型行为 |
|
|
214
|
+
| `maxTokens=8000` | 限制输出上限 | 不等于不再循环 |
|
|
215
|
+
| 行为层检测 | 提供停止/切换信号 | 需要上层执行恢复动作 |
|
|
216
|
+
|
|
217
|
+
## 示例
|
|
218
|
+
|
|
219
|
+
- [基础文本检测](examples/basic-text-detection.md)
|
|
220
|
+
- [工具调用检测](examples/tool-call-detection.md)
|
|
221
|
+
- [恢复决策接入](examples/recovery-policy-integration.md)
|
|
222
|
+
|
|
223
|
+
每个示例都只展示输入、检测信号或决策输出;实际停止、重试、切换模型和人工复核
|
|
224
|
+
仍由上层运行器负责。
|
|
225
|
+
|
|
226
|
+
## 文件结构
|
|
227
|
+
|
|
228
|
+
```
|
|
229
|
+
mimo-stable/
|
|
230
|
+
├── README.md
|
|
231
|
+
├── SKILL.md
|
|
232
|
+
├── pyproject.toml
|
|
233
|
+
├── CHANGELOG.md
|
|
234
|
+
├── scripts/
|
|
235
|
+
│ ├── detect_loop.py # 循环检测脚本
|
|
236
|
+
│ ├── benchmark_fixtures.py # 可复现 fixture 基准测试
|
|
237
|
+
│ └── recovery_policy.py # 保守恢复决策层(不执行副作用)
|
|
238
|
+
├── tests/test_detector.py # 行为契约测试
|
|
239
|
+
├── examples/ # 最小集成示例
|
|
240
|
+
├── fixtures/ # 规范化回归样例
|
|
241
|
+
├── logs/ # 历史观察日志
|
|
242
|
+
└── references/parameters.md
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## 测试
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
python3 -m py_compile scripts/*.py
|
|
249
|
+
bash -n scripts/*.sh
|
|
250
|
+
python3 tests/test_detector.py
|
|
251
|
+
python3 scripts/benchmark_fixtures.py
|
|
252
|
+
# 或使用仓库提供的入口:
|
|
253
|
+
bash scripts/test_short.sh
|
|
254
|
+
bash scripts/test_long.sh
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
当前 benchmark 覆盖 9 个规范化案例:循环、正常输出、短时重复、近似文本、
|
|
258
|
+
变化参数重试、非连续工具调用、工具 key 顺序、重复副作用工具和中文任务语言漂移。
|
|
259
|
+
|
|
260
|
+
历史日志在默认 180 秒阈值下可能不报警;复核时使用 `--timeout 60`。生产阈值
|
|
261
|
+
应按业务容忍度评估,不能把测试阈值直接当作生产配置。
|
|
262
|
+
|
|
263
|
+
## 安装与集成
|
|
264
|
+
|
|
265
|
+
项目保持零运行时依赖。当前支持源码直接运行和本地 CLI 安装,尚未发布到 PyPI。
|
|
266
|
+
|
|
267
|
+
### 方式一:直接运行源码
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
git clone https://github.com/xli498/mimo-stable.git
|
|
271
|
+
cd mimo-stable
|
|
272
|
+
python3 scripts/detect_loop.py --log fixtures/loop_detected.log
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### 方式二:安装本地 CLI
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
python3 -m pip install --no-deps .
|
|
279
|
+
mimo-loop-detect --json --timeout 60 --log fixtures/loop_detected.log
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
> [!NOTE]
|
|
283
|
+
> 当前尚未发布公共 PyPI 包,因此不要使用 `pip install mimo-stable` 获取发行包。
|
|
284
|
+
|
|
285
|
+
上层运行器不应在检测到循环后盲目重试:
|
|
286
|
+
|
|
287
|
+
1. 保存脱敏后的事件摘要;
|
|
288
|
+
2. 停止当前生成或工具链;
|
|
289
|
+
3. 根据任务是否幂等决定重试;
|
|
290
|
+
4. 必要时切换模型或请求人工复核。
|
|
291
|
+
|
|
292
|
+
## 许可
|
|
293
|
+
|
|
294
|
+
MIT
|
|
295
|
+
|
|
296
|
+
## 行为契约
|
|
297
|
+
|
|
298
|
+
`fixtures/` 中的规范化样例用于稳定回归,历史日志用于说明观察事实;二者不互相
|
|
299
|
+
替代。执行 `python3 tests/test_detector.py` 可验证检测器行为。
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# LLM Degenerate Loop Guardrails
|
|
2
|
+
|
|
3
|
+
[English README](README.en.md)
|
|
4
|
+
|
|
5
|
+
[](https://github.com/xli498/mimo-stable/actions/workflows/quality.yml)
|
|
6
|
+
[](https://github.com/xli498/mimo-stable/releases)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](pyproject.toml)
|
|
9
|
+
|
|
10
|
+
面向国产及其他 LLM 的输出流和工具调用退化循环经验记录与检测工具。
|
|
11
|
+
项目最初来自 MiMo `reasoning=True` 场景的重复输出观察,后来发现类似现象
|
|
12
|
+
也可能出现在 GLM 等其他模型中。这里分享的是识别、止损和复盘方法,不是
|
|
13
|
+
针对任何模型的根治方案。
|
|
14
|
+
|
|
15
|
+
> [!IMPORTANT]
|
|
16
|
+
> 当前项目的核心能力是**检测与止损**,不是自动修复模型。检测到循环后,
|
|
17
|
+
> 由上层运行器决定停止、切换模型、重试或人工复核。
|
|
18
|
+
|
|
19
|
+
## 目录
|
|
20
|
+
|
|
21
|
+
- [30 秒开始](#30-秒开始)
|
|
22
|
+
- [问题描述](#问题描述)
|
|
23
|
+
- [三层防御体系](#三层防御体系)
|
|
24
|
+
- [检测策略](#检测策略)
|
|
25
|
+
- [可复现证据](#可复现证据)
|
|
26
|
+
- [如何记录和分享新案例](#如何记录和分享新案例)
|
|
27
|
+
- [工程侧缓解措施](#工程侧缓解措施不是模型修复)
|
|
28
|
+
- [示例](#示例)
|
|
29
|
+
- [文件结构](#文件结构)
|
|
30
|
+
- [测试](#测试)
|
|
31
|
+
- [安装与集成](#安装与集成)
|
|
32
|
+
- [许可](#许可)
|
|
33
|
+
- [行为契约](#行为契约)
|
|
34
|
+
|
|
35
|
+
## 30 秒开始
|
|
36
|
+
|
|
37
|
+

|
|
38
|
+
|
|
39
|
+
> **核心流程:** 模型输出 → 检测信号 → 保守决策摘要 → 上层运行器处理。
|
|
40
|
+
> 检测器和恢复层都不执行重试、模型切换或工具调用。
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
python3 scripts/detect_loop.py --log fixtures/loop_detected.log
|
|
44
|
+
python3 tests/test_detector.py
|
|
45
|
+
python3 scripts/benchmark_fixtures.py
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
机器集成使用单份 JSON 摘要和退出码:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
python3 scripts/detect_loop.py --json --timeout 60 --log fixtures/loop_detected.log
|
|
52
|
+
# 退出码 0 = 未检测到;1 = 检测到;2 = 参数/输入错误
|
|
53
|
+
|
|
54
|
+
# 将检测摘要转成保守的恢复决策(只输出决策,不执行重试)
|
|
55
|
+
python3 scripts/detect_loop.py --json --timeout 60 --log fixtures/loop_detected.log | \
|
|
56
|
+
python3 scripts/recovery_policy.py --retryable
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
典型检测摘要:
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"loop_detected": true,
|
|
64
|
+
"details": {
|
|
65
|
+
"type": "consecutive_identical_output"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
| 退出码 | 含义 |
|
|
71
|
+
| :---: | :--- |
|
|
72
|
+
| `0` | 未检测到循环 |
|
|
73
|
+
| `1` | 检测到循环 |
|
|
74
|
+
| `2` | 参数或输入错误 |
|
|
75
|
+
|
|
76
|
+
## 问题描述
|
|
77
|
+
|
|
78
|
+
在特定模型、参数、任务和上游服务条件下,LLM 可能进入退化循环状态:
|
|
79
|
+
|
|
80
|
+
- **症状**:同一段输出重复,持续时间异常增长
|
|
81
|
+
- **语言切换**:特定中文任务中切换为英文,且无视语言约束
|
|
82
|
+
- **功能停滞**:不执行有进展的工具调用,仅重复输出文本
|
|
83
|
+
- **根因尚未确定**:`reasoning=True`、上下文长度、工具链状态、服务端实现等
|
|
84
|
+
都可能是相关变量;本项目不把相关性写成因果结论。
|
|
85
|
+
|
|
86
|
+
类似表现并不等于同一个 bug。MiMo、GLM 或其他国产模型的案例必须分别记录
|
|
87
|
+
模型版本、端点、参数、任务和时间,不能把一个模型的触发概率或规避经验直接
|
|
88
|
+
外推到另一个模型。
|
|
89
|
+
|
|
90
|
+
## 三层防御体系
|
|
91
|
+
|
|
92
|
+
### 第一层:工程侧限制(止损,不是修复)
|
|
93
|
+
|
|
94
|
+
在运行器中设置硬性超时和 Token 限制(具体字段以所用框架官方文档为准):
|
|
95
|
+
|
|
96
|
+
```yaml
|
|
97
|
+
# 示例结构;字段名和数值必须按实际框架、模型与任务校准。
|
|
98
|
+
provider:
|
|
99
|
+
model: <your-model>
|
|
100
|
+
timeout: <task-specific-limit>
|
|
101
|
+
max_tokens: <task-specific-limit>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
> [!WARNING]
|
|
105
|
+
> **历史案例,不是通用推荐:** 最初 MiMo 案例曾使用
|
|
106
|
+
> `timeoutSeconds=180` 和 `maxTokens=8000` 限制单次资源占用。这些值不是
|
|
107
|
+
> 跨模型配置建议,也不表示能够改变模型进入循环的概率。
|
|
108
|
+
|
|
109
|
+
作用是限制单次故障的最长资源占用,不改变模型本身的循环概率。
|
|
110
|
+
|
|
111
|
+
### 第二层:行为检测
|
|
112
|
+
|
|
113
|
+
运行 `scripts/detect_loop.py` 实时监控模型输出,检测连续重复。
|
|
114
|
+
|
|
115
|
+
管道输入以空行切分输出块;`--timeout` 的持续时间只在流式输入期间有实际意义,
|
|
116
|
+
离线日志使用日志中的块时间戳。
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
python3 scripts/detect_loop.py --log logs/sample_degenerate_loop.log
|
|
120
|
+
model_output 2>&1 | python3 scripts/detect_loop.py
|
|
121
|
+
python3 scripts/detect_loop.py --json --log logs/sample_degenerate_loop.log
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
检测规则:
|
|
125
|
+
|
|
126
|
+
- 连续 3+ 次输出块完全相同或高度相似
|
|
127
|
+
- 连续 3+ 次工具调用参数完全相同
|
|
128
|
+
- 已知副作用工具的重复调用
|
|
129
|
+
- 显式声明中文任务后的英文漂移
|
|
130
|
+
- 可选的持续时间门控
|
|
131
|
+
|
|
132
|
+
### 第三层:行为规则(AGENTS.md)
|
|
133
|
+
|
|
134
|
+
在 AGENTS.md 或对应运行器规则中加入检测和处置约束。详见
|
|
135
|
+
[SKILL.md](SKILL.md)。该层是提示和流程规则,不替代运行时检测。
|
|
136
|
+
|
|
137
|
+
## 检测策略
|
|
138
|
+
|
|
139
|
+
默认文本重复采用**持续时间门控**:需要达到 `--threshold` 次相似输出,且
|
|
140
|
+
重复窗口达到 `--timeout` 秒。这适合日志后处理,减少短暂重复的误报。
|
|
141
|
+
|
|
142
|
+
如果上层需要在达到重复次数后立即得到信号,可显式使用:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
python3 scripts/detect_loop.py --text-mode instant --log fixtures/repeated_but_short.log
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
工具调用重复和已知副作用工具的重复调用不受文本持续时间门控影响;生产集成仍应
|
|
149
|
+
结合幂等键、调用结果和重试原因做二次判断。
|
|
150
|
+
|
|
151
|
+
## 可复现证据
|
|
152
|
+
|
|
153
|
+
> [!NOTE]
|
|
154
|
+
> Fixture 用于稳定回归,历史日志用于记录具体观察;两者的证据性质不同。
|
|
155
|
+
|
|
156
|
+
`logs/sample_degenerate_loop.log` 是历史观察日志;`logs/fixed_normal_run.log`
|
|
157
|
+
是一次正常运行日志。它们只说明具体案例,不构成模型故障率统计,也不证明
|
|
158
|
+
任何参数能“修复”模型。
|
|
159
|
+
|
|
160
|
+
当前仓库没有足够实验次数估计任何模型的通用触发概率,因此不提供“某模型
|
|
161
|
+
有 X% 概率出问题”之类的结论。
|
|
162
|
+
|
|
163
|
+
## 如何记录和分享新案例
|
|
164
|
+
|
|
165
|
+
建议至少记录以下信息,并在发布前脱敏:
|
|
166
|
+
|
|
167
|
+
- 模型与精确版本、供应商/端点、调用时间段
|
|
168
|
+
- `reasoning`、temperature、max tokens、上下文规模等实际参数
|
|
169
|
+
- 任务类型、是否多轮、是否涉及工具调用
|
|
170
|
+
- 重复发生前后的输出块摘要或哈希,不公开密钥、隐私和完整敏感工具参数
|
|
171
|
+
- 是否真正发生资源浪费/副作用,检测器是否报警,检测延迟
|
|
172
|
+
- 重试、切换模型或改变参数后的结果
|
|
173
|
+
|
|
174
|
+
案例记录用于复盘和横向比较,不应写成因果证明。没有原始证据时,使用
|
|
175
|
+
“观察到”“可能相关”“尚未复现”,不要使用“必然”“已证明”“彻底解决”。
|
|
176
|
+
|
|
177
|
+
## 工程侧缓解措施(不是模型修复)
|
|
178
|
+
|
|
179
|
+
| 措施 | 作用 | 边界 |
|
|
180
|
+
| :--- | :--- | :--- |
|
|
181
|
+
| `timeoutSeconds=180` | 限制单次资源损失 | 不改变模型行为 |
|
|
182
|
+
| `maxTokens=8000` | 限制输出上限 | 不等于不再循环 |
|
|
183
|
+
| 行为层检测 | 提供停止/切换信号 | 需要上层执行恢复动作 |
|
|
184
|
+
|
|
185
|
+
## 示例
|
|
186
|
+
|
|
187
|
+
- [基础文本检测](examples/basic-text-detection.md)
|
|
188
|
+
- [工具调用检测](examples/tool-call-detection.md)
|
|
189
|
+
- [恢复决策接入](examples/recovery-policy-integration.md)
|
|
190
|
+
|
|
191
|
+
每个示例都只展示输入、检测信号或决策输出;实际停止、重试、切换模型和人工复核
|
|
192
|
+
仍由上层运行器负责。
|
|
193
|
+
|
|
194
|
+
## 文件结构
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
mimo-stable/
|
|
198
|
+
├── README.md
|
|
199
|
+
├── SKILL.md
|
|
200
|
+
├── pyproject.toml
|
|
201
|
+
├── CHANGELOG.md
|
|
202
|
+
├── scripts/
|
|
203
|
+
│ ├── detect_loop.py # 循环检测脚本
|
|
204
|
+
│ ├── benchmark_fixtures.py # 可复现 fixture 基准测试
|
|
205
|
+
│ └── recovery_policy.py # 保守恢复决策层(不执行副作用)
|
|
206
|
+
├── tests/test_detector.py # 行为契约测试
|
|
207
|
+
├── examples/ # 最小集成示例
|
|
208
|
+
├── fixtures/ # 规范化回归样例
|
|
209
|
+
├── logs/ # 历史观察日志
|
|
210
|
+
└── references/parameters.md
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## 测试
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
python3 -m py_compile scripts/*.py
|
|
217
|
+
bash -n scripts/*.sh
|
|
218
|
+
python3 tests/test_detector.py
|
|
219
|
+
python3 scripts/benchmark_fixtures.py
|
|
220
|
+
# 或使用仓库提供的入口:
|
|
221
|
+
bash scripts/test_short.sh
|
|
222
|
+
bash scripts/test_long.sh
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
当前 benchmark 覆盖 9 个规范化案例:循环、正常输出、短时重复、近似文本、
|
|
226
|
+
变化参数重试、非连续工具调用、工具 key 顺序、重复副作用工具和中文任务语言漂移。
|
|
227
|
+
|
|
228
|
+
历史日志在默认 180 秒阈值下可能不报警;复核时使用 `--timeout 60`。生产阈值
|
|
229
|
+
应按业务容忍度评估,不能把测试阈值直接当作生产配置。
|
|
230
|
+
|
|
231
|
+
## 安装与集成
|
|
232
|
+
|
|
233
|
+
项目保持零运行时依赖。当前支持源码直接运行和本地 CLI 安装,尚未发布到 PyPI。
|
|
234
|
+
|
|
235
|
+
### 方式一:直接运行源码
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
git clone https://github.com/xli498/mimo-stable.git
|
|
239
|
+
cd mimo-stable
|
|
240
|
+
python3 scripts/detect_loop.py --log fixtures/loop_detected.log
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### 方式二:安装本地 CLI
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
python3 -m pip install --no-deps .
|
|
247
|
+
mimo-loop-detect --json --timeout 60 --log fixtures/loop_detected.log
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
> [!NOTE]
|
|
251
|
+
> 当前尚未发布公共 PyPI 包,因此不要使用 `pip install mimo-stable` 获取发行包。
|
|
252
|
+
|
|
253
|
+
上层运行器不应在检测到循环后盲目重试:
|
|
254
|
+
|
|
255
|
+
1. 保存脱敏后的事件摘要;
|
|
256
|
+
2. 停止当前生成或工具链;
|
|
257
|
+
3. 根据任务是否幂等决定重试;
|
|
258
|
+
4. 必要时切换模型或请求人工复核。
|
|
259
|
+
|
|
260
|
+
## 许可
|
|
261
|
+
|
|
262
|
+
MIT
|
|
263
|
+
|
|
264
|
+
## 行为契约
|
|
265
|
+
|
|
266
|
+
`fixtures/` 中的规范化样例用于稳定回归,历史日志用于说明观察事实;二者不互相
|
|
267
|
+
替代。执行 `python3 tests/test_detector.py` 可验证检测器行为。
|