@leonhardthust/dsh-decision-engine 1.9.2 → 1.10.0

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/lib/tolerance.js CHANGED
@@ -27,21 +27,30 @@
27
27
  *
28
28
  * ## 四档语义
29
29
  *
30
- * 自治 autonomous 消除软询问;mandatory deny 与必须人工批准的操作不受影响。
31
- * 必须审批但审批不可用时直接拒绝,不能用模型替代许可。
32
- * 轻松 relaxed 只有严重度排在最前的 1% 才询问用户。
33
- * 默认 default 按决策模型自己的判断提问(**当前的默认行为**,逐字不变)。
34
- * 谨慎 cautious 在默认判断之外,再把严重度靠前的 25% 也拿来询问。
35
- *
36
- * 三档之间是**单调**的(校准完成后):1% ≤ 默认 ≤ 默认 ∪ 25%。自治是例外——
37
- * 它是「不打扰」,不是「按分位数」。
30
+ * 不问 autonomous 软性询问一律不弹窗、不按比例;mandatory deny 照旧,
31
+ * 必须人工批准的操作**仍然询问**(审批不可用时直接拒绝,
32
+ * 不能用模型替代许可)。
33
+ * 少问 relaxed 只按严重度尾部:校准后约最前的 1%,校准前固定分 0.60。
34
+ * 它**替换**默认判断而不是叠加,因此同一次调用既可能
35
+ * 从问变成不问,也可能从放行变成问。
36
+ * 默认 default 沿用决策模型自己的判断提问(**当前的默认行为**,逐字不变)。
37
+ * 多问 cautious 在默认判断之上**再叠加**严重度靠前的一批:校准后约 25%,
38
+ * 校准前固定分 0.30。取并集,所以逐调用一定 ⊇ 默认。
39
+ *
40
+ * 四档之间只有**汇总询问率**单调(少问 ≤ 默认 ≤ 多问;具体数值取决于决策
41
+ * 模型与阈值,DEC-035 那张 22.5% / 41% 的实测表来自已经退役的旧判定路径,
42
+ * 不能当成当前默认档的询问率)。
43
+ * 逐调用并不单调:少问档是替换语义,同一次调用与默认判断可以**双向**不同;
44
+ * 不问档只是不弹软性询问,强制审批那条路径与其它档完全一样。
38
45
  *
39
46
  * ## 校准不足时怎么办
40
47
  *
41
48
  * 窗口里还没攒够样本时**算不出可靠的分位数**。此时不猜分位数,改用
42
- * `fallbackSeverity`——这几个绝对值是**在真实数据上量出来的典型分位数**
43
- * (见 DEC-035 的实测表),因此冷启动的行为与校准后的行为量级一致,不会
44
- * 出现「一开始问很多、攒够数据后突然变安静」这种跳变。
49
+ * `fallbackSeverity`——这几个绝对值是**在真实数据上量出来的典型分界线**
50
+ * (见 DEC-035 的实测表)。代价要说实话:冷启动比校准后**更安静**——
51
+ * 少问档冷启动阈值覆盖 0.47%(校准后的 P99 ≈0.50 覆盖 1.1%),多问档
52
+ * 冷启动覆盖 19.3%(校准后的 P75 ≈0.28 覆盖 25.1%)。因此攒够 50 个样本的
53
+ * 那一刻是**变吵**一点,而不是变安静。
45
54
  *
46
55
  * 校准状态会显示在界面上(校准中 n/50),用户不会以为设置没生效。
47
56
  *
@@ -58,7 +67,7 @@ export const DEFAULT_TOLERANCE = "default";
58
67
  * 滚动窗口容量(每个 provider × model 一份)。
59
68
  *
60
69
  * 取 200 的理由:分位数要稳,至少要几十个样本;200 个样本能让 P99 落在
61
- * 「最���的那一两个」之外(插值后是一个连续阈值),同时窗口不必大到让
70
+ * 「最前的那一两个」之外(插值后是一个连续阈值),同时窗口不必大到让
62
71
  * 「最近」失去意义——用户改用模型后,几十次调用之内就该反映新分布。
63
72
  */
64
73
  export const TOLERANCE_WINDOW_SIZE = 200;
@@ -117,44 +126,45 @@ export const HIGH_RISK_FLOOR_SCORE = 0.7;
117
126
  export const TOLERANCE_PRESETS = {
118
127
  autonomous: {
119
128
  id: "autonomous",
120
- label: "自治",
121
- labelEn: "Autonomous",
122
- summary: "免除软询问;保留强制审批与拒绝,无人审批时阻止未授权操作",
123
- summaryEn: "Skips soft questions; preserves mandatory approval and denial, blocking unapproved actions when approval is unavailable",
129
+ label: "不问",
130
+ labelEn: "Don't ask",
131
+ summary: "软性审查不弹窗、不按比例;权限强制的人工批准与拒绝照旧",
132
+ summaryEn: "Soft checks never prompt and no percentile band applies; permission-mandated approval and denial are unchanged",
124
133
  percentile: null,
125
- // 自治档不走分位数路径,这里的值只是「让通用分支也不会触发」的占位。
134
+ // 不问档不走分位数路径,这里的值只是「让通用分支也不会触发」的占位。
126
135
  fallbackSeverity: Number.POSITIVE_INFINITY,
127
136
  },
128
137
  relaxed: {
129
138
  id: "relaxed",
130
- label: "轻松",
131
- labelEn: "Relaxed",
132
- summary: "只有严重度排在最前的 1% 才询问用户",
133
- summaryEn: "Only the top 1% by severity asks the user",
139
+ label: "少问",
140
+ labelEn: "Ask less",
141
+ summary: "只按严重度尾部:校准后约最前 1%,校准前固定分 0.60;替换默认判断,因此可能多问也可能少问",
142
+ summaryEn: "Severity tail only: about the top 1% once calibrated, a fixed score 0.60 before that; replaces the default judgement, so it can ask more or less often",
134
143
  percentile: 0.99,
135
144
  // 实测(1421 条真实授权决策,见 DEC-035):severity ≥ 0.60 覆盖 0.47%
136
- // 的调用,与 P99 分位(≈0.50,覆盖 1.1%)在同一量级。冷启动时用它,
137
- // 行为与校准后一致,不会出现「一开始问一堆、攒够数据后突然变安静」。
145
+ // 的调用,而校准后的 P99(≈0.50)覆盖 1.1%。冷启动时用它,所以冷启动
146
+ // 比校准后**更安静**;攒够样本的那一刻是稍微变吵,不是变安静。
138
147
  fallbackSeverity: 0.6,
139
148
  },
140
149
  default: {
141
150
  id: "default",
142
151
  label: "默认",
143
152
  labelEn: "Default",
144
- summary: "按决策模型自己的判断提问(当前的默认行为)",
145
- summaryEn: "Ask according to the decision model's own judgement (current behaviour)",
153
+ summary: "沿用决策模型判断:中/高风险、疑似注入、不可逆且与任务无关会问",
154
+ summaryEn: "Keeps the decision model's own judgement: asks on medium/high risk, suspected injection, or irreversible and off-task calls",
146
155
  percentile: null,
147
156
  fallbackSeverity: null,
148
157
  },
149
158
  cautious: {
150
159
  id: "cautious",
151
- label: "谨慎",
152
- labelEn: "Cautious",
153
- summary: "在默认判断之外,再把严重度靠前的 25% 也拿来询问",
154
- summaryEn: "Beyond the default judgement, also ask for the top 25% by severity",
160
+ label: "多问",
161
+ labelEn: "Ask more",
162
+ summary: "默认判断之上再加严重度靠前一批:校准后约 25%,校准前固定分 0.30",
163
+ summaryEn: "Adds the top severity band on top of the default judgement: about 25% once calibrated, a fixed score 0.30 before that",
155
164
  percentile: 0.75,
156
- // 实测:severity ≥ 0.30 覆盖 19.3% 的调用;P75 分位(≈0.28)覆盖 25.1%。
157
- // 兜底值取实测的中位口径,量级一致。
165
+ // 实测:severity ≥ 0.30 覆盖 19.3% 的调用;校准后的 P75 分位(≈0.28)
166
+ // 覆盖 25.1%。两个比例都只描述「叠加的那一批」;并入默认判断之后的
167
+ // 总询问率取决于默认判断本身,不是这两个数之和。
158
168
  fallbackSeverity: 0.3,
159
169
  },
160
170
  };
@@ -389,9 +399,9 @@ export function resolveTolerance(options = {}) {
389
399
 
390
400
  // 拦截(deny)不是「打扰用户」,四档一律保留。
391
401
  //
392
- // 这条边界必须守住:自治档的语义是「不问我」,不是「什么都放行」。
402
+ // 这条边界必须守住:不问档的语义是「软性询问不弹窗」,不是「什么都放行」。
393
403
  // deny 不会打断用户(它直接告诉模型「这次调用被拒了」),而把它一并
394
- // 取消等于让自治档变成「关掉安全检查」——那与用户说的「0% 询问」是两件事。
404
+ // 取消等于让不问档变成「关掉安全检查」——那与用户说的「0% 询问」是两件事。
395
405
  if (legacyKind === "deny") {
396
406
  return { kind: "deny", changed: false, severity, cutoff: null, note: "" };
397
407
  }
@@ -413,7 +423,7 @@ export function resolveTolerance(options = {}) {
413
423
  changed: true,
414
424
  severity,
415
425
  cutoff: null,
416
- note: `tolerance=autonomous(自治:不询问用户,severity=${severity.toFixed(3)})`,
426
+ note: `tolerance=autonomous(不问:软性询问不弹窗,severity=${severity.toFixed(3)})`,
417
427
  }
418
428
  : { kind: "delegate", changed: false, severity, cutoff: null, note: "" };
419
429
  }
@@ -423,13 +433,13 @@ export function resolveTolerance(options = {}) {
423
433
  return { kind: legacyKind, changed: false, severity, cutoff: null, note: "" };
424
434
  }
425
435
 
426
- // 轻松 / 谨慎:按分位数带。
436
+ // 少问 / 多问:按分位数带。
427
437
  const cutoff = Number.isFinite(options.cutoff)
428
438
  ? clamp01(options.cutoff)
429
439
  : preset.fallbackSeverity;
430
440
 
431
441
  if (preset.id === "relaxed") {
432
- // 轻松档**替换**默认判断,而不是叠加在它上面——否则「top 1%」会变成
442
+ // 少问档**替换**默认判断,而不是叠加在它上面——否则「top 1%」会变成
433
443
  // 「top 1% ∪ 默认判断」,实测那是 23% 的打扰率,与档位语义不符。
434
444
  if (severity >= cutoff) {
435
445
  return {
@@ -437,7 +447,7 @@ export function resolveTolerance(options = {}) {
437
447
  changed: legacyKind !== "ask",
438
448
  severity,
439
449
  cutoff,
440
- note: `tolerance=relaxed(严重度 ${severity.toFixed(3)} ≥ ${cutoff.toFixed(3)},属最前的 1%)`,
450
+ note: `tolerance=relaxed(严重度 ${severity.toFixed(3)} ≥ ${cutoff.toFixed(3)},落在严重度尾部)`,
441
451
  };
442
452
  }
443
453
  return legacyKind === "ask"
@@ -446,12 +456,12 @@ export function resolveTolerance(options = {}) {
446
456
  changed: true,
447
457
  severity,
448
458
  cutoff,
449
- note: `tolerance=relaxed(严重度 ${severity.toFixed(3)} < ${cutoff.toFixed(3)},不在最前的 1%)`,
459
+ note: `tolerance=relaxed(严重度 ${severity.toFixed(3)} < ${cutoff.toFixed(3)},不在严重度尾部)`,
450
460
  }
451
461
  : { kind: "delegate", changed: false, severity, cutoff, note: "" };
452
462
  }
453
463
 
454
- // 谨慎档:默认判断 ∪ 严重度靠前的 25%(**叠加**,因此一定 ≥ 默认)。
464
+ // 多问档:默认判断 ∪ 严重度靠前的 25%(**叠加**,因此逐调用一定 ⊇ 默认)。
455
465
  if (legacyKind === "ask" || severity >= cutoff) {
456
466
  return {
457
467
  kind: "ask",
@@ -461,7 +471,7 @@ export function resolveTolerance(options = {}) {
461
471
  note:
462
472
  legacyKind === "ask"
463
473
  ? `tolerance=cautious(默认判断已升级询问)`
464
- : `tolerance=cautious(严重度 ${severity.toFixed(3)} ≥ ${cutoff.toFixed(3)},属最前的 25%)`,
474
+ : `tolerance=cautious(严重度 ${severity.toFixed(3)} ≥ ${cutoff.toFixed(3)},落在默认判断之外的严重度带)`,
465
475
  };
466
476
  }
467
477
  return { kind: "delegate", changed: false, severity, cutoff, note: "" };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leonhardthust/dsh-decision-engine",
3
- "version": "1.9.2",
3
+ "version": "1.10.0",
4
4
  "description": "dsh-decision-engine: a cheap judgement layer for DeepSeek Harness — a small decision model answers structured questions before the main model acts, so calls can be allowed, escalated, rerouted to a cheaper model, or skipped. All eight decision points (tool authorization, model routing, delegation, compaction, turn continuation, retrieval, skill pick, COT translation) are implemented and individually switchable. Works against any service exposing a decision endpoint: built-in presets (system one, bamboonode, SiliconFlow, Silicon Flow) or a custom base URL.",
5
5
  "icon": "./icon.svg",
6
6
  "type": "module",