pi-think-anchor 1.0.11 → 1.0.13

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.
Files changed (3) hide show
  1. package/README.en.md +56 -25
  2. package/README.md +53 -22
  3. package/package.json +1 -1
package/README.en.md CHANGED
@@ -2,14 +2,16 @@
2
2
 
3
3
  > Requires pi >= 0.74 · Node >= 20 · MIT · no runtime dependencies
4
4
 
5
- Monitors a "focus fingerprint" in the thinking chain and pulls back in graded steps when the band drifts, avoiding long unproductive digressions.
5
+ ## What it solves
6
+
7
+ In long sessions thinking drifts: repeated probing, hesitation, self-restatement, fewer sentences that actually advance. This extension reads a word-frequency fingerprint from thinking, decides whether you are focused / mixed / reactive, and pulls back in graded steps — only when needed.
6
8
 
7
9
  ## Features
8
10
 
9
- - Computes a focus/distraction word-ratio per thinking block and classifies the band over a sliding window (focused / mixed / reactive).
10
- - L1 gentle nudge when drifting into reactive; L2 stronger intervention if it persists; nothing at all when healthy.
11
- - Interventions are injected as trailing messages (neutral wording), never written into the system prompt.
12
- - Command `/think-anchor` for status, `/think-anchor stats` for version / bands / actions / effectiveness / log size.
11
+ - Computes a focus/distraction ratio per thinking block and classifies a band over a sliding window.
12
+ - L1 gentle nudge when drifting into reactive; L2 stronger intervention if it persists; silence when healthy.
13
+ - Interventions are trailing messages (neutral wording), never written into the system prompt.
14
+ - Commands `/think-anchor` (status) and `/think-anchor stats` (version / bands / actions / effectiveness / log size).
13
15
  - Log at `~/.pi/think-anchor.jsonl` (tail-preserving truncation at 1 MiB).
14
16
 
15
17
  ## Install
@@ -21,33 +23,27 @@ pi install ./pi-think-anchor
21
23
 
22
24
  ## Configuration
23
25
 
24
- ### File `~/.pi/think-anchor.json`
26
+ ### 1 · Config file `~/.pi/think-anchor.json`
25
27
 
26
- Every key is optional; defaults below apply when omitted, and invalid JSON falls back to defaults.
28
+ Every key is optional; invalid JSON falls back to defaults.
27
29
 
28
30
  | Key | Type | Default | Description |
29
31
  | --- | --- | --- | --- |
30
32
  | `enabled` | boolean | `true` | Master switch |
31
- | `score.minHits` | number | `3` | Evidence gate: a turn with fewer hits is neither scored nor intervened |
32
- | `score.spec` | number | `1.2` | Focused threshold (alpha-smoothed ratio >= this means spec) |
33
- | `score.react` | number | `0.5` | Reactive threshold (ratio < this means react) |
33
+ | `score.minHits` | number | `3` | Evidence gate: fewer hits in a turn means no scoring and no intervention |
34
+ | `score.spec` | number | `1.2` | Focused threshold (alpha-smoothed ratio >= this) |
35
+ | `score.react` | number | `0.5` | Reactive threshold (ratio < this) |
34
36
  | `score.alpha` | number | `1` | Alpha smoothing (pulls low-hit ratios toward 1) |
35
37
  | `windowSize` | number | `5` | Sliding window size in messages |
36
38
  | `weights.beta` | number | `0.4` | Weight of neutral words in the numerator |
37
39
  | `weights.eps` | number | `0.5` | Denominator floor (avoids division by zero) |
38
40
  | `intervention.l1CooldownMin` | number | `10` | L1 cooldown in minutes |
39
41
  | `intervention.l2CooldownMin` | number | `15` | L2 cooldown in minutes |
40
- | `intervention.maxL2` | number | `2` | Max L2 interventions per session |
41
- | `lexicon.positive` / `negative` / `neutral` | string[] | built-in Chinese lexicon | Override word lists (28 focus / 18 drift by default) |
42
- | `bands` | object | — | Legacy compatibility: used when `score` is absent |
43
-
44
- ### Environment
45
-
46
- | Variable | Value | Description |
47
- | --- | --- | --- |
48
- | `THINK_ANCHOR` | `0` | Hard off; overrides the config file |
42
+ | `intervention.maxL2` | number | `2` | Max L2 per session |
43
+ | `lexicon.positive` / `negative` / `neutral` | string[] | built-in Chinese lexicon | Override word lists (28 focus / 18 drift) |
44
+ | `bands` | object | — | Legacy: used when `score` is absent |
49
45
 
50
- ### Example
46
+ Example:
51
47
 
52
48
  ```json
53
49
  {
@@ -58,19 +54,54 @@ Every key is optional; defaults below apply when omitted, and invalid JSON falls
58
54
  }
59
55
  ```
60
56
 
57
+ ### 2 · Environment
58
+
59
+ | Variable | Value | Description |
60
+ | --- | --- | --- |
61
+ | `THINK_ANCHOR` | `0` | Hard off; overrides the config file |
62
+
63
+ ### 3 · Commands
64
+
65
+ | Command | Purpose |
66
+ | --- | --- |
67
+ | `/think-anchor` | Current band, hit count, drift share, last intervention result |
68
+ | `/think-anchor stats` | Version / band distribution / actions / effectiveness / log size |
69
+
70
+ ## Where it acts (CoT path)
71
+
72
+ | Stage | Does |
73
+ | --- | --- |
74
+ | `turn_end` | Reads each assistant thinking block -> word fingerprint (focus / drift / neutral) -> sliding-window aggregation -> band |
75
+ | Evidence gate | When **this turn** has fewer than `minHits` hits it only advances the window: no score, no intervention (avoids mistaking "long and scattered" for repeated probing) |
76
+ | State machine | `healthy -> warning -> critical`; after L2 is exhausted it goes silent instead of escalating forever |
77
+ | `session_start` | Resets state machine and band history, keeping the last 3 samples and pending (cross-session settlement) |
78
+
79
+ It only reads thinking and **never rewrites it** — unlike compression extensions, it decides whether to nudge, not how the text should look.
80
+
81
+ ## Feedback path (how interventions get back to the model)
82
+
83
+ | Item | Path | Why |
84
+ | --- | --- | --- |
85
+ | L1 / L2 intervention text | Trailing message, `deliverAs: "nextTurn"`, `display: false` | Changing content must not enter the system prompt (prefix cache); neutral wording keeps it from reading as blame |
86
+ | Band and reason | Intervention text carries the current ratio and hit count | The model sees the evidence, not just an order |
87
+ | Effectiveness `eff` | Settled three turns after an intervention, written to the log (not injected) | Judge by the actual ratio change instead of assuming it worked |
88
+ | No escalation when ineffective | Previous `eff >= 0` -> L2 degrades to L1 | Measured: L2 gives no gain on already-drifting sessions |
89
+ | Adaptive silence | >= 8 samples and < 50% improvement -> record only (`acted=muted-*`) | An ineffective intervention should stop interrupting |
90
+
91
+ Actions blocked by cooldown/limits are logged as `blocked-L*`, distinguishable from "nothing needed" (`none`).
92
+
61
93
  ## How it works
62
94
 
63
- - `turn_end` evaluates each assistant thinking block, then a conditional trailing injection on the next turn.
64
- - v2.3 criteria: per-turn evidence gate plus an alpha-smoothed ratio; low-evidence turns are not scored.
65
- - No escalation when ineffective: if the previous intervention did not improve the ratio, L2 degrades to L1.
66
- - Effectiveness loop: `eff` is settled three turns after an intervention; the window survives 30 minutes across sessions.
67
- - Adaptive silence: with >= 8 samples and < 50% improvement it records without injecting.
95
+ - v2.3 criteria = per-turn evidence gate + alpha-smoothed ratio (alpha=1; react < 0.5 / mixed < 1.2 / spec >= 1.2).
96
+ - Calibrated by replay: react 18.3% -> 9.7% over scorable turns; alpha pulls low-hit ratios toward 1.
97
+ - The cross-session window survives 30 minutes so an intervention near the end of a session is not lost.
68
98
 
69
99
  ## Limitations
70
100
 
71
101
  - Turn-level evaluation, not in-stream real time.
72
102
  - The default lexicon is Chinese-oriented; English sessions depend on wording.
73
103
  - Interventions are hints, not enforcement.
104
+ - The gate is heuristic: very short thinking or pure tool turns are usually not scored.
74
105
 
75
106
  ## License
76
107
 
package/README.md CHANGED
@@ -2,14 +2,16 @@
2
2
 
3
3
  > 适用 pi ≥ 0.74 · Node ≥ 20 · MIT · 无运行时依赖
4
4
 
5
- 监测 thinking 的「专注指纹」,波段漂移时分级拉回,避免长时间发散。
5
+ ## 它解决什么问题
6
+
7
+ 长时间对话里 thinking 会慢慢「散」:反复试探、犹豫、自我复述,真正推进的句子变少。本扩展读 thinking 的词频指纹,判定当前是「专注 / 过渡 / 发散」,在发散时分级拉回——且只在需要时才出声。
6
8
 
7
9
  ## 功能
8
10
 
9
- - 每段 thinking 计算专注 / 发散词频比率,滑动窗口聚合后判定波段(高专注 / 过渡 / 发散)。
11
+ - 每段 thinking 计算专注 / 发散词频比率,滑动窗口聚合后判定波段。
10
12
  - 跌入发散时 L1 温和引导;持续发散时 L2 强干预;健康时零注入。
11
13
  - 干预走对话尾部注入(中性措辞),不写 system。
12
- - 命令 `/think-anchor` 查看状态,`/think-anchor stats` 查看版本 / 波段 / 动作 / 有效率 / 日志体积。
14
+ - 命令 `/think-anchor`(状态)、`/think-anchor stats`(版本 / 波段 / 动作 / 有效率 / 日志体积)。
13
15
  - 日志 `~/.pi/think-anchor.jsonl`(超 1 MiB 保留尾部整行)。
14
16
 
15
17
  ## 安装
@@ -21,33 +23,27 @@ pi install ./pi-think-anchor
21
23
 
22
24
  ## 配置
23
25
 
24
- ### 配置文件 `~/.pi/think-anchor.json`
26
+ ### 1 · 配置文件 `~/.pi/think-anchor.json`
25
27
 
26
- 所有键可省略,缺省用下表默认值;非法 JSON 自动回退默认。
28
+ 所有键可省略;非法 JSON 自动回退默认。
27
29
 
28
30
  | 键 | 类型 | 默认 | 说明 |
29
31
  | --- | --- | --- | --- |
30
32
  | `enabled` | boolean | `true` | 总开关 |
31
- | `score.minHits` | number | `3` | 证据门槛:单轮词频命中少于该值则不评分、不干预 |
33
+ | `score.minHits` | number | `3` | 证据门槛:单轮命中少于该值则不评分、不干预 |
32
34
  | `score.spec` | number | `1.2` | 高专注阈值(α 平滑比率 ≥ 该值为 spec) |
33
35
  | `score.react` | number | `0.5` | 发散阈值(比率 < 该值为 react) |
34
- | `score.alpha` | number | `1` | α 平滑(让低命中比率趋向 1,避免「长而散」误判) |
36
+ | `score.alpha` | number | `1` | α 平滑(把低命中比率拉向 1,避免「长而散」误判) |
35
37
  | `windowSize` | number | `5` | 滑动窗口消息数(长工具链中约 1 轮) |
36
38
  | `weights.beta` | number | `0.4` | 中性词计入分子的权重 |
37
39
  | `weights.eps` | number | `0.5` | 分母下限(防除零) |
38
40
  | `intervention.l1CooldownMin` | number | `10` | L1 冷却(分钟) |
39
41
  | `intervention.l2CooldownMin` | number | `15` | L2 冷却(分钟) |
40
- | `intervention.maxL2` | number | `2` | 每会话 L2 次数上限 |
41
- | `lexicon.positive` / `negative` / `neutral` | string[] | 内置中文词表 | 词表覆盖(专注 28 词 / 发散 18 词) |
42
+ | `intervention.maxL2` | number | `2` | 每会话 L2 上限 |
43
+ | `lexicon.positive` / `negative` / `neutral` | string[] | 内置中文词表 | 覆盖词表(专注 28 词 / 发散 18 词) |
42
44
  | `bands` | object | — | 旧版兼容:无 `score` 时由 `bands.spec/react` 映射 |
43
45
 
44
- ### 环境变量
45
-
46
- | 变量 | 取值 | 说明 |
47
- | --- | --- | --- |
48
- | `THINK_ANCHOR` | `0` | 硬关闭,优先级高于配置文件 |
49
-
50
- ### 示例
46
+ 示例:
51
47
 
52
48
  ```json
53
49
  {
@@ -58,19 +54,54 @@ pi install ./pi-think-anchor
58
54
  }
59
55
  ```
60
56
 
57
+ ### 2 · 环境变量
58
+
59
+ | 变量 | 取值 | 说明 |
60
+ | --- | --- | --- |
61
+ | `THINK_ANCHOR` | `0` | 硬关闭,优先级高于配置文件 |
62
+
63
+ ### 3 · 命令
64
+
65
+ | 命令 | 作用 |
66
+ | --- | --- |
67
+ | `/think-anchor` | 当前波段、命中量、发散占比、上次干预结果 |
68
+ | `/think-anchor stats` | 版本 / 波段分布 / 动作分布 / 有效率 / 日志体积 |
69
+
70
+ ## 作用点(CoT / 思维链通路)
71
+
72
+ | 阶段 | 做什么 |
73
+ | --- | --- |
74
+ | `turn_end` | 取每条 assistant 消息的 thinking 文本 → 词频指纹(专注 / 发散 / 中性)→ 滑窗聚合 → 判波段 |
75
+ | 证据门槛 | **本轮**命中 < `minHits` 时只推进窗口,不评分、不干预(避免把「长而散」当成反复试探) |
76
+ | 状态机 | `healthy → warning → critical`;L2 用尽后静默,不无限升级 |
77
+ | `session_start` | 重置状态机与波段历史,只保留最近 3 条与 pending(跨会话效果结算) |
78
+
79
+ 只读 thinking,**不改写任何内容**——这一点与压缩类扩展不同:它影响的是「要不要提醒」,不是「写得对不对」。
80
+
81
+ ## 回灌(反馈回到模型的通路)
82
+
83
+ | 项 | 通路 | 设计理由 |
84
+ | --- | --- | --- |
85
+ | L1 / L2 干预文本 | 对话尾部消息,`deliverAs: "nextTurn"`,`display: false` | 变化内容不能进 system(毁前缀缓存);中性措辞避免模型把提醒当指责 |
86
+ | 波段与理由 | 干预文本里带当前比率与命中量 | 让模型知道「依据是什么」,而不是只收到一句指令 |
87
+ | 效果结算 `eff` | 干预后第 3 轮结算,写入日志(不直接注入) | 用实际比率变化判断干预是否有效,而不是假定有效 |
88
+ | 无效不升级 | 上次 `eff >= 0`(未改善)→ 本次 L2 自动降为 L1 | 实测 L2 对已发散会话无收益,避免重复无效打断 |
89
+ | 自适应静默 | 样本 ≥ 8 且下降率 < 50% → 只记录不注入(`acted=muted-*`) | 长期看没效果的干预不该继续打扰 |
90
+
91
+ 冷却 / 上限挡下的动作记 `blocked-L*`,与「无需动作」(`none`)在日志里可区分。
92
+
61
93
  ## 机制
62
94
 
63
- - `turn_end` 逐条评估 assistant 消息的 thinking → 条件触发 → 下一轮注入。
64
- - v2.3 判据:单轮证据门槛 + α 平滑比率;命中不足的轮次不评分。
65
- - 无效不升级:上次干预实测无改善则本次 L2 自动降为 L1。
66
- - 效果闭环:干预后第 3 轮结算 `eff`;跨会话窗口保留 30 分钟。
67
- - 自适应静默:样本 ≥ 8 且下降率 < 50% 时只记录不注入。
95
+ - v2.3 判据 = 单轮证据门槛 + α 平滑比率(α=1;react < 0.5 / mixed < 1.2 / spec ≥ 1.2)。
96
+ - 判据经回放校准:react 18.3% → 9.7%(命中 ≥3 的可评分回合);α 让低命中比率趋向 1。
97
+ - 跨会话窗口保留 30 分钟,避免干预落在会话末尾时丢样本。
68
98
 
69
99
  ## 限制
70
100
 
71
101
  - 按回合评估,非流内实时。
72
102
  - 词表默认偏中文;英文会话效果取决于用词。
73
- - 干预是提示,不是强制;模型可能不遵守。
103
+ - 干预是提示不是强制,模型可能不遵守。
104
+ - 门槛是启发式:极短 thinking 或纯工具轮次通常不评分。
74
105
 
75
106
  ## License
76
107
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-think-anchor",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "Monitors thinking focus drift and nudges the model back on track.",
5
5
  "type": "module",
6
6
  "license": "MIT",