dsh-fast-mode 1.5.24 → 1.5.25

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.
package/BENCHMARK.md CHANGED
@@ -2277,26 +2277,27 @@ python3 bench/textshare.py --arm off --limit 0 --json # 全文的口径与 t
2277
2277
 
2278
2278
  读法上的边界:这是本机 168 对套件转录,`off` 与 `def15` 各自只有一遍,落在 ±20 % 的诚实带内(见「诚实带」一节),所以「0.75–0.84」应读作方向与量级,不是精确系数;但「并行率不变」在这种样本量下是稳的(两条独立口径都给 1.00)。
2279
2279
 
2280
- ## 纪律提醒到底占多少:关掉它慢 1.54×(11 对,单遍)
2280
+ ## 纪律提醒到底占多少:同一块复测一次,方向就翻了(LEN3 vs LEN_DBG)
2281
2281
 
2282
- `def15` 与 `nodisc` 解析后的配置**只差一项**:`discipline: 'full' → 'off'`(`bench/arms/nodisc.yml`,用 `bench/arm-config.mjs --json` 对过). 于是这一块直接量「输出纪律」这条提醒的独立份额:
2282
+ `def15` 与 `nodisc` 解析后的配置**只差一项**:`discipline: 'full' → 'off'`(`bench/arms/nodisc.yml`,用 `bench/arm-config.mjs --json` 对过)。这一块量的是「输出纪律」这条提醒的独立份额:
2283
2283
 
2284
2284
  ```
2285
2285
  python3 bench/block_score.py LEN_MIG def15 nodisc
2286
2286
  python3 bench/block_score.py LEN_DBG def15 nodisc
2287
+ python3 bench/block_score.py LEN3 def15 nodisc
2287
2288
  ```
2288
2289
 
2289
- | 块 | 可用对 | wall 几何比(nodisc/def15) | 中位比 | def15 更快 | 符号检验 |
2290
+ | 块 | 可用对 | wall 几何比(nodisc/def15) | 中位比 | nodisc 更快 | 符号检验 |
2290
2291
  |---|---|---|---|---|---|
2291
2292
  | LEN_MIG(migrate,5 对) | 4 | 1.189 | 1.275 | 2/4 | p = 1.000 |
2292
- | LEN_DBG(debugfix,7 对) | 7 | **1.790** | 1.978 | **6/7** | p = 0.125 |
2293
- | 池化 | 11 | **1.542** | 1.744 | 8/11 | — |
2293
+ | LEN_DBG(debugfix,7 对) | 7 | 1.790 | 1.978 | 1/7 | p = 0.125 |
2294
+ | **LEN3(debugfix,7 对,复测同一块)** | 7 | **0.648** | 0.717 | **5/7** | p = 0.453 |
2295
+ | 两遍合并(14 对) | 14 | 1.077 | — | 6/14 | — |
2294
2296
 
2295
- - 在 **debugfix** 上关掉输出纪律几乎翻倍:中位 1.98×,7 对里 6 对变慢,步数 1.32×、输出 token 1.51×。
2296
- - 在 **migrate** 上没有信号:4 对里 2 对更快、2 对更慢,几何比 1.19 但被一个 2.86× 的单元格撑起来。
2297
- - 池化 **1.54×**(中位 1.74×)⇒ 输出纪律是本机目前**最大的单项杠杆**,量级 1.3–1.8×,方向稳定、精度不足(每个单元格只有一遍,落在诚实带内)。
2298
-
2299
- 这条同时给「不要再加提醒时机」提供了支持:既然唯一的提醒已经承担了最大份额,继续加同类提醒的边际收益应当来自**同一句话的措辞**,而不是更多触发点——这也是下一轮该测的东西。
2297
+ - 第一遍(`LEN_DBG`)看出「关掉纪律几乎翻倍」,第二遍(`LEN3`)同题同配、只换了一遍采样,几何比变成 **0.648**——方向相反,量级相近(1.79 与 1.54),**这不是「提醒有效」的证据,是单块采样的噪声**。
2298
+ - 两遍的离群值分别落在不同手臂上:`LEN_DBG` 的 `nodisc p6` 196 s、`p4` 85 s;`LEN3` 的 `def15 p4` 165 s、`p7` 125 s。块级几何比被这一两个单元格决定,7 对远不够定方向。
2299
+ - 所以**纪律提醒的独立份额目前不可判定**,不能对外报 1.54× / 1.79× 这类数字。能报的只有池化主效应(138 对 0.823×),其中「明细律」与其它提醒是同一个机制包里一起生效的,无法在单块里分离。
2300
+ - 这条给「不要再加提醒时机、不要再加重复提醒」提供了同样甚至更强的支持:连唯一那条提醒的份额都测不出来,继续加同类触发点没有可验证的收益。
2300
2301
 
2301
2302
  ## 同一配置再跑一遍会差多少:块级差 4 %,任务级差 15–31 %(REP2,10 对)
2302
2303
 
@@ -2319,3 +2320,30 @@ python3 bench/block_score.py REP2 off def15
2319
2320
  - 因此文档里只对外报**池化**口径(138 对 0.823×、中位 0.799×、93/138 更快),并明确标注「单格/单块差异落在 ±20 % 内是噪声」。**任何以单块(含 n≈5–12 的块)为依据的结论都不作为决策依据**——这一条现在有了同配置重复的直接证据,不再是凭经验给的宽限。
2320
2321
 
2321
2322
  `REP2` 的默认手臂同时复核了一件事:`def15` 在这里是**发布版源码**(`node_modules/dsh-fast-mode` 指回仓库),所以 0.791 也是「当前代码在跑」的又一次独立确认,不是旧数据的复用。
2323
+
2324
+ ## 被截断的调用身份:146 506 次调用里 782 组会同键(1.5.24 修的就是这个)
2325
+
2326
+ 调用身份由 `stableKey(参数)` 生成,上限 400 字符,超出即截断。身份用于两件事:判断「这一步有没有产生新观察」(重复调用、没进展的判据),以及给通过过的检查记录一个键。旧实现在截断时只留前 400 字符,于是**只在尾部不同的两条长调用会得到同一个身份**——最常见的就是同一个 heredoc 脚本改了后半段、同一个文件改了后面的内容。
2327
+
2328
+ 本机全量普查(3 168 个会话、146 506 次工具调用,`~/.dsh/sessions` 下全部转录):
2329
+
2330
+ | 量 | 值 |
2331
+ | --- | --- |
2332
+ | 工具调用总数 | 146 506 |
2333
+ | 参数序列化 > 400 字符(被截断) | 48 293(33 %) |
2334
+ | 不同 390 字符前缀组 | 47 031 |
2335
+ | **同前缀对应 ≥2 种不同完整参数(旧身份下同键)** | **782 组** |
2336
+
2337
+ 1.5.24 的修法:截断时附上整段序列化文本的 32 位 FNV-1a(8 位十六进制),长度 399 字符,上限 401 不变;不截断的短调用字节级不变。782 组里触发错误催办还需要「两条调用的结果大小也相同」(网关不给内容哈希时摘要就是字符数),所以线上未必发生过,但这条路径现在不再可能。回归测试在 `test/effort.test.js`。
2338
+
2339
+ ## 输出里到底有多少是「说给人看的」:可见文字 1.3 %,推理字符 99.6 %
2340
+
2341
+ `bench/textshare.py` 现在把 `reasoning` 部分单独计数(原来只数可见文字与工具参数):
2342
+
2343
+ ```
2344
+ python3 bench/textshare.py --limit 5
2345
+ prose 751 chars tool args 58,084 chars total 58,835 chars
2346
+ reasoning 218,718 chars
2347
+ ```
2348
+
2349
+ 可见文字只占(可见文字 + 工具参数)的 1.28 %;把推理也算进来,**推理占模型全部发出字符的 99.6 %**。所以提速不来自「少说话」:省下来的是**步数**与**每步的推理量**,而提醒文案只是同时压这两者的手段。`bench/mechanism.py` 的报告里也多了 `prose / args / reasoning / emitted` 四行(168 对同题同配的结论:`reasoning` 几何比 0.75,`emitted` 0.82)。
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.5.25(收回一个测不出来的结论;两个分析脚本补上推理字符)
4
+
5
+ - **撤回「纪律提醒 1.54×」这个数字**:`LEN3` 把 `LEN_DBG` 那一块(debugfix、7 对、`def15` vs `nodisc`,两臂只差 `discipline:'full' → 'off'`)同题同配再跑一遍,几何比从 **1.790** 变成 **0.648**——方向相反,符号检验两遍都不显著(p = 0.125 / 0.453),块级比值由一两个离群单元格决定(一遍在 `nodisc`,一遍在 `def15`,单格 165 s / 196 s)。所以纪律提醒的**独立份额目前不可判定**,对外只能报池化主效应 0.823×(138 对)。
6
+ - **文档据此改口径**:`docs/REFERENCE.md` / `REFERENCE.en.md` 不再说「单独关掉慢 1.54×、是最大单项杠杆」,改成「份额测不出来 + 机制上它确实把推理压短(池化 `reasoning` 字符 0.75×)」。`BENCHMARK.md` 的纪律一节重写成两遍对照表,并写明单块 n=7 不足以定方向。
7
+ - **两个分析脚本补上推理字符**:`bench/textshare.py` 现在把转录里的 `reasoning` 部分单独计数(原来只数可见正文与工具参数),`bench/mechanism.py` 的报告多了 `prose / args / reasoning / emitted` 四行。本机样本上推理占模型全部发出字符的 **99.6 %**,可见正文只占(正文+工具参数)的 1.28 %——这解释了为什么提速来自「步数与每步推理量」,而不是「少说话」。
8
+ - **没有行为变更**:不含开关、默认值、提醒时机或提示词改动。
9
+
3
10
  ## 1.5.24(稳定性修复:被截断的调用身份不再互相冒充)
4
11
 
5
12
  - **问题**:调用身份的 `argsKey` 有 400 字符上限,超出就截断。两条参数只在尾部不同的调用(最常见:同一个 heredoc 脚本改了后半段、同一个文件改了后面的内容)会得到**同一个身份**。身份被用来判断「这一步有没有进展」——若两条调用的结果长度还恰好相同(无内容哈希时按字符数当摘要),这一步就会被判成「重复调用、没进展」,模型因此收到不该有的催办;复用检查也会把结论记到同一个键上。
@@ -14,7 +21,7 @@
14
21
  ## 1.5.22(只加量测与文档:机制拆解、纪律份额、同配置重复性;默认行为不变)
15
22
 
16
23
  - **机制拆解落地成工具**:新增 `bench/mechanism.py`,把 168 对「同 tag、同任务、同 pair、只差 arm」的会话转录逐项数出来(步数、调用数、并行率、只叙述步、prose/参数/推理字符)。结论写进 BENCHMARK:并行的两条口径都是 1.00(即并行**不是**杠杆);步数 0.83 / 调用 0.79 / 参数 0.84 / prose 0.80 / 推理 0.75,全都落在 0.75–0.84——插件是「每样都少一点」,不是砍某一种输出。转录里推理字符占输出字符的 99.8 %(每会话中位 47.5 k vs 92),所以只盯可见文字优化、天花板是 0.2 %。
17
- - **纪律提醒的独立份额实测**:`bench/arms/nodisc.yml`(与 `def15` 只差 `discipline: 'full' → 'off'`)在 `LEN_MIG` + `LEN_DBG` 上给出 pool **1.54×**(11 对,中位 1.74×;debugfix 单看 1.79×、6/7 对变慢)⇒ 输出纪律是目前最大的单项杠杆。据此把 `docs/REFERENCE.md` / `REFERENCE.en.md` 里「这条不是提速主要来源」的旧说法改成实测口径。
24
+ - **纪律提醒的独立份额实测**:`bench/arms/nodisc.yml`(与 `def15` 只差 `discipline: 'full' → 'off'`)在 `LEN_MIG` + `LEN_DBG` 上给出 pool **1.54×**(11 对,中位 1.74×;debugfix 单看 1.79×、6/7 对变慢)⇒ 当时把它报成最大的单项杠杆(**这条已被 1.5.25 的 LEN3 复测推翻,见上**)。据此把 `docs/REFERENCE.md` / `REFERENCE.en.md` 里「这条不是提速主要来源」的旧说法改成实测口径。
18
25
  - **同配置重复性**:`REP2` 把 `migrate`+`debugfix` 用同一套配置再跑一遍。块级 0.791 vs 首遍 0.823(差 4 %),任务级差 15–31 %,单格跨度 0.28×–1.79× ⇒ 「单块/单格差异在 ±20 % 内算噪声」现在有同配置重复的直接证据,不再只是经验宽限。
19
26
  - **文档口径刷新**:可见正文占「正文+工具参数」字符的比例按最新全量(385 会话 / 3 659 消息 / 4 007 调用)从 1.18 % 更新为 1.19 %。
20
27
  - **没有行为变更**:默认值(`outputDiscipline:'late'`、`stepBudget:0`、`workspaceMap:false`、`disciplineRepeatAtSteps:0`、`compactRereadHint:false`、`enabled:true`、`gear:auto`)全部未动;这一版只加量测工具与文档。
@@ -53,11 +53,14 @@ def collect_all():
53
53
 
54
54
  def stats_for(run):
55
55
  text = textshare.read_transcript(run[4])
56
- prose, args, msgs, calls, multi, textonly = textshare.tally(text)
56
+ c = textshare.counts(text)
57
+ prose, args, msgs, calls, multi, textonly = (c['prose'], c['args'], c['messages'],
58
+ c['calls'], c['multi'], c['textonly'])
57
59
  if not msgs:
58
60
  return None
59
61
  return {'prose': prose, 'args': args, 'messages': msgs, 'calls': calls,
60
- 'multi': multi, 'textonly': textonly,
62
+ 'multi': multi, 'textonly': textonly, 'reasoning': c['reasoning'],
63
+ 'emitted': prose + args + c['reasoning'],
61
64
  'callsPerMsg': calls / msgs,
62
65
  'multiShare': multi / msgs,
63
66
  'textOnlyShare': textonly / msgs,
@@ -107,7 +110,8 @@ def main():
107
110
  print('no matched groups with transcripts; nothing to compute', file=sys.stderr)
108
111
  return 1
109
112
 
110
- keys = ['messages', 'calls', 'callsPerMsg', 'multiShare', 'textOnlyShare', 'proseShare']
113
+ keys = ['messages', 'calls', 'callsPerMsg', 'multiShare', 'textOnlyShare', 'proseShare',
114
+ 'prose', 'args', 'reasoning', 'emitted']
111
115
  med = {x: {k: st.median([c[x][k] for c in cells]) for k in keys} for x in arms}
112
116
  summary = {'arms': arms, 'tag': a.tag or '*', 'groups': len(cells)}
113
117
  for k in keys:
@@ -118,10 +122,15 @@ def main():
118
122
  print(json.dumps(summary, indent=2))
119
123
  return 0
120
124
  print(f"matched groups: {len(cells)} (arm A = {arms[0]}, arm B = {arms[1]})")
121
- print(f"{'metric':14} {arms[0]:>10} {arms[1]:>10} {'B/A':>7}")
125
+ print(f"{'metric':16} {arms[0]:>12} {arms[1]:>12} {'B/A':>7}")
126
+ def fmt(v):
127
+ """Shares are small floats, counts are characters; keep both readable."""
128
+ return f'{v:.3f}' if isinstance(v, float) and abs(v) < 100 else f'{v:,.0f}'
129
+
122
130
  for k in keys:
123
131
  b, r = med[arms[1]][k], summary[k]['medianRatio']
124
- print(f"{k:14} {med[arms[0]][k]:10.3f} {b:10.3f} {(r if r is not None else float('nan')):7.3f}")
132
+ print(f"{k:16} {fmt(med[arms[0]][k]):>12} {fmt(b):>12} "
133
+ f"{(r if r is not None else float('nan')):7.3f}")
125
134
  return 0
126
135
 
127
136
 
@@ -62,8 +62,23 @@ def read_transcript(d):
62
62
 
63
63
 
64
64
  def tally(text):
65
- """(prose chars, tool-arg chars, messages, tool calls, multi-call messages)."""
66
- prose = args = msgs = calls = multi = textonly = 0
65
+ """(prose chars, tool-arg chars, messages, tool calls, multi-call messages).
66
+
67
+ The docstring on `counts` lists what each number is; this wrapper keeps the
68
+ historical tuple shape for callers that only need those six.
69
+ """
70
+ c = counts(text)
71
+ return c['prose'], c['args'], c['messages'], c['calls'], c['multi'], c['textonly']
72
+
73
+
74
+ def counts(text):
75
+ """Every per-run count, including the reasoning text the model emitted.
76
+
77
+ `reasoning` is the part type the transcript uses for a route's thinking
78
+ tokens. It is not billed as output prose, but it is decoded the same way and
79
+ dominates emitted characters, which is why the mechanism report compares it.
80
+ """
81
+ prose = args = msgs = calls = multi = textonly = reasoning = 0
67
82
  for line in text.splitlines():
68
83
  try:
69
84
  o = json.loads(line)
@@ -78,6 +93,8 @@ def tally(text):
78
93
  continue
79
94
  if part.get('type') == 'text':
80
95
  prose += len(part.get('text') or '')
96
+ elif part.get('type') == 'reasoning':
97
+ reasoning += len(part.get('text') or '')
81
98
  elif part.get('type') == 'tool-call':
82
99
  calls += 1
83
100
  this += 1
@@ -86,7 +103,8 @@ def tally(text):
86
103
  multi += 1
87
104
  elif this == 0:
88
105
  textonly += 1
89
- return prose, args, msgs, calls, multi, textonly
106
+ return {'prose': prose, 'args': args, 'messages': msgs, 'calls': calls, 'multi': multi,
107
+ 'textonly': textonly, 'reasoning': reasoning}
90
108
 
91
109
 
92
110
  def main():
@@ -100,11 +118,13 @@ def main():
100
118
  rows = collect(a.arm, a.tag, a.limit)
101
119
  per = []
102
120
  for d, name in rows:
103
- prose, args, msgs, calls, multi, textonly = tally(read_transcript(d))
104
- if msgs:
105
- per.append({'run': name, 'prose': prose, 'args': args, 'messages': msgs,
106
- 'calls': calls, 'multi': multi, 'textonly': textonly,
107
- 'share': prose / (prose + args) if prose + args else 0})
121
+ c = counts(read_transcript(d))
122
+ if c['messages']:
123
+ per.append({'run': name, 'prose': c['prose'], 'args': c['args'],
124
+ 'reasoning': c['reasoning'], 'messages': c['messages'],
125
+ 'calls': c['calls'], 'multi': c['multi'], 'textonly': c['textonly'],
126
+ 'share': c['prose'] / (c['prose'] + c['args'])
127
+ if c['prose'] + c['args'] else 0})
108
128
  if not per:
109
129
  print(f'no transcripts for arm={a.arm} tag={a.tag or "*"}; nothing to compute')
110
130
  return 1
@@ -123,6 +143,7 @@ def main():
123
143
  'textOnlyShare': (sum(r['textonly'] for r in per) /
124
144
  max(sum(r['messages'] for r in per), 1)),
125
145
  'proseChars': prose, 'toolArgChars': args, 'totalChars': total,
146
+ 'reasoningChars': sum(r['reasoning'] for r in per),
126
147
  'share': prose / total if total else None,
127
148
  'medianShare': st.median(shares), 'p10': shares[int(0.1 * (len(shares) - 1))],
128
149
  'p90': shares[int(0.9 * (len(shares) - 1))],
@@ -139,6 +160,8 @@ def main():
139
160
  print(f" sessions {summary['sessions']} messages {summary['messages']} "
140
161
  f"tool calls {summary['calls']}")
141
162
  print(f" prose {prose:,} chars tool args {args:,} chars total {total:,} chars")
163
+ if summary['reasoningChars']:
164
+ print(f" reasoning {summary['reasoningChars']:,} chars")
142
165
  print(f" prose share {100 * summary['share']:.2f} % "
143
166
  f"(median {100 * summary['medianShare']:.2f} %, "
144
167
  f"p10 {100 * summary['p10']:.2f} %, p90 {100 * summary['p90']:.2f} %)")
@@ -90,7 +90,7 @@ Details worth knowing:
90
90
  - **The fan-out number is the width the scheduler computed.** Under `auto` the section and the stall guard share one `autoSchedule` result, so the number the model reads and the number being enforced cannot drift apart.
91
91
  - **Prefetch appears only at width ≥ 4.** Below that, an extra call just competes with the work the step already has.
92
92
  - **The bounded-calls block appears only when `stallGuard !== false`**, because it describes the same policy the guard enforces.
93
- - **Output discipline has two strengths**: `light` for the legacy `fast` gear, `full` for `auto`/`turbo`/`max`/`ludicrous`. Both ask the model to skip call previews, restatements and progress reports; the last step still writes the answer in full. `full` adds one sentence: send bare calls when a step needs no prose. **It never shortens the answer and never tells the model to skip a check.** Scope matters: visible prose is only 1.19 % of the characters in (prose + tool arguments) (def15, 385 sessions; excludes reasoning and tool results; recompute with `python3 bench/textshare.py --limit 0`), so its value is not the words it deletes: switching that one item off (`bench/arms/nodisc.yml`) is measured at **1.54× slower** (11 matched pairs, median 1.74×), the largest single lever on this machine, and the mechanism is that it also shortens per-step reasoning (reasoning characters 0.753× inside the transcripts) and cuts steps to 0.83×; it still does not license the claim that "the work itself is unaffected". `python3 bench/block_score.py LEN_DBG def15 nodisc` recomputes it.
93
+ - **Output discipline has two strengths**: `light` for the legacy `fast` gear, `full` for `auto`/`turbo`/`max`/`ludicrous`. Both ask the model to skip call previews, restatements and progress reports; the last step still writes the answer in full. `full` adds one sentence: send bare calls when a step needs no prose. **It never shortens the answer and never tells the model to skip a check.** Scope matters: visible prose is only 1.19 % of the characters in (prose + tool arguments) (def15, 385 sessions; excludes reasoning and tool results; recompute with `python3 bench/textshare.py --limit 0`), so its value is not the words it deletes: it also shortens per-step reasoning (pooled 168 pairs: reasoning characters 0.75×, emitted 0.82×). Switching that one item off (`bench/arms/nodisc.yml`) cannot be sized at block level today: the same block (debugfix, 7 pairs) run twice gives `nodisc/def15` of 1.79× and then 0.648×, opposite directions, neither sign test significant, with the block ratio decided by one or two outlier cells (see the LEN3 section of `BENCHMARK.md`). Only the pooled main effect, 0.823×, is reportable, and inside it the discipline line acts together with the rest of the mechanism, so a single-block split is not available; it still does not license the claim that "the work itself is unaffected".
94
94
  - **`parallel: 1` is the documented "on but no fan-out" setting**: the switch still applies to the stall guard, and the guidance section disappears entirely.
95
95
 
96
96
  The cost is explicit: about 2,700 characters under `auto`, paid as input tokens on every request. That is the intended trade — input tokens for fewer round trips.
package/docs/REFERENCE.md CHANGED
@@ -94,7 +94,7 @@
94
94
  - **扇出目标里的数字就是调度器算出的宽度。** `auto` 下提示段和看门狗共用同一个 `autoSchedule` 结果,所以模型读到的数字和被执行的数字不会漂移。
95
95
  - **预取只在宽度 ≥ 4 时出现。** 宽度不够时多取一个调用只会挤占别的工作。
96
96
  - **有界调用那一段只在 `stallGuard !== false` 时出现**,因为它描述的规则和看门狗是一套东西。
97
- - **输出纪律有两档**:`light` 给旧档位 `fast` 用,`full` 给 `auto`/`turbo`/`max`/`ludicrous` 用。两档要求模型不预告调用、不复述结果、不写进度报告;最后一步的答案仍然要完整写出。`full` 只多一句"不需要文字时只发调用"。**它不会让答案变短,更不会让模型跳过检查。** 口径要说清:可见正文只占「正文+工具参数」字符的 1.19 %(def15 全量 385 会话;不含 reasoning 与工具返回;`python3 bench/textshare.py --limit 0` 可复算),所以它的价值不在"删掉的字"本身:把这一项单独关掉(`bench/arms/nodisc.yml`)实测慢 **1.54×**(11 对同题同配,中位 1.74×),是本机目前最大的单项杠杆,作用机制是连带把每步的推理压短(转录内 reasoning 字符 0.753×)并把步数压到 0.83×;同样不能由此宣称"工作内容不受影响"。`python3 bench/block_score.py LEN_DBG def15 nodisc` 可复算。
97
+ - **输出纪律有两档**:`light` 给旧档位 `fast` 用,`full` 给 `auto`/`turbo`/`max`/`ludicrous` 用。两档要求模型不预告调用、不复述结果、不写进度报告;最后一步的答案仍然要完整写出。`full` 只多一句"不需要文字时只发调用"。**它不会让答案变短,更不会让模型跳过检查。** 口径要说清:可见正文只占「正文+工具参数」字符的 1.19 %(def15 全量 385 会话;不含 reasoning 与工具返回;`python3 bench/textshare.py --limit 0` 可复算),所以它的价值不在"删掉的字"本身,而在它连带把每步的推理压短(池化 168 对里 `reasoning` 字符 0.75×、`emitted` 0.82×)。把这一项单独关掉(`bench/arms/nodisc.yml`)的份额目前**测不出来**:同一块(debugfix 7 对)跑两遍,`nodisc/def15` 一次 1.79×、一次 0.648×,方向相反、符号检验都不显著,块级几何比由一两个离群单元格决定(见 `BENCHMARK.md` 的 LEN3 一节)。能报的只有池化主效应 0.823×,其中明细律与其它提醒在一个机制包里一起生效,无法在单块里分离;同样不能由此宣称"工作内容不受影响"。
98
98
  - **`parallel: 1` 是"开着但不要求扇出"的正式写法**:开关仍然生效于看门狗,提示段整体消失。
99
99
 
100
100
  成本是明码标价的:`auto` 下这一段约 2.7 千字符,每个请求都要付这部分输入 token。这是有意的交换——用输入 token 换更少的往返。
package/index.js CHANGED
@@ -97,7 +97,7 @@ const TAG = '[fast-mode]'
97
97
  * Shipped identity of this build. Traced into the debug log so a stale install is
98
98
  * visible at a glance, and pinned by a test against `package.json`.
99
99
  */
100
- export const VERSION = '1.5.24'
100
+ export const VERSION = '1.5.25'
101
101
  const GLOBAL_AGENT = '__global__'
102
102
 
103
103
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-fast-mode",
3
- "version": "1.5.24",
3
+ "version": "1.5.25",
4
4
  "description": "DeepSeek Harness fast mode: same thinking effort, fewer round trips. An adaptive parallel tool fan-out, output discipline that never trims the answer, and a cancellable stall guard. Never changes reasoning effort, the model, or the request body. Provider-agnostic, one dependency (@deepseek-ai/schemastery).",
5
5
  "repository": {
6
6
  "type": "git",