pi-think-anchor 1.0.13 → 1.0.14
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/README.en.md +30 -85
- package/README.md +29 -84
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -1,107 +1,52 @@
|
|
|
1
|
-
# pi-think-anchor
|
|
1
|
+
# pi-think-anchor
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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.
|
|
8
|
-
|
|
9
|
-
## Features
|
|
10
|
-
|
|
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).
|
|
15
|
-
- Log at `~/.pi/think-anchor.jsonl` (tail-preserving truncation at 1 MiB).
|
|
3
|
+
Chain-of-thought drift monitor: reads a word-frequency fingerprint from thinking, classifies focused / mixed / reactive, and nudges when drifting.
|
|
16
4
|
|
|
17
5
|
## Install
|
|
18
6
|
|
|
19
7
|
```bash
|
|
20
8
|
pi install npm:pi-think-anchor
|
|
21
|
-
pi install ./pi-think-anchor
|
|
22
9
|
```
|
|
23
10
|
|
|
24
11
|
## Configuration
|
|
25
12
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
Every key is optional; invalid JSON falls back to defaults.
|
|
29
|
-
|
|
30
|
-
| Key | Type | Default | Description |
|
|
31
|
-
| --- | --- | --- | --- |
|
|
32
|
-
| `enabled` | boolean | `true` | Master switch |
|
|
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) |
|
|
36
|
-
| `score.alpha` | number | `1` | Alpha smoothing (pulls low-hit ratios toward 1) |
|
|
37
|
-
| `windowSize` | number | `5` | Sliding window size in messages |
|
|
38
|
-
| `weights.beta` | number | `0.4` | Weight of neutral words in the numerator |
|
|
39
|
-
| `weights.eps` | number | `0.5` | Denominator floor (avoids division by zero) |
|
|
40
|
-
| `intervention.l1CooldownMin` | number | `10` | L1 cooldown in minutes |
|
|
41
|
-
| `intervention.l2CooldownMin` | number | `15` | L2 cooldown in minutes |
|
|
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 |
|
|
45
|
-
|
|
46
|
-
Example:
|
|
47
|
-
|
|
48
|
-
```json
|
|
49
|
-
{
|
|
50
|
-
"enabled": true,
|
|
51
|
-
"score": { "minHits": 3, "spec": 1.2, "react": 0.5, "alpha": 1 },
|
|
52
|
-
"windowSize": 5,
|
|
53
|
-
"intervention": { "l1CooldownMin": 10, "l2CooldownMin": 15, "maxL2": 2 }
|
|
54
|
-
}
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
### 2 · Environment
|
|
13
|
+
Config file `~/.pi/think-anchor.json` (optional; defaults apply, invalid JSON falls back):
|
|
58
14
|
|
|
59
|
-
|
|
|
15
|
+
| Key | Default | Description |
|
|
60
16
|
| --- | --- | --- |
|
|
61
|
-
| `
|
|
62
|
-
|
|
63
|
-
|
|
17
|
+
| `enabled` | `true` | Master switch |
|
|
18
|
+
| `score.minHits` | `3` | Fewer hits in a turn: no scoring, no intervention |
|
|
19
|
+
| `score.spec` | `1.2` | Focused threshold |
|
|
20
|
+
| `score.react` | `0.5` | Reactive threshold |
|
|
21
|
+
| `score.alpha` | `1` | Alpha smoothing |
|
|
22
|
+
| `windowSize` | `5` | Window size in messages |
|
|
23
|
+
| `weights.beta` | `0.4` | Neutral-word weight in the numerator |
|
|
24
|
+
| `weights.eps` | `0.5` | Denominator floor |
|
|
25
|
+
| `intervention.l1CooldownMin` | `10` | L1 cooldown (minutes) |
|
|
26
|
+
| `intervention.l2CooldownMin` | `15` | L2 cooldown (minutes) |
|
|
27
|
+
| `intervention.maxL2` | `2` | Max L2 per session |
|
|
28
|
+
| `lexicon.positive/negative/neutral` | built-in Chinese lexicon (28 focus / 18 drift) | Lexicon override |
|
|
29
|
+
| `bands.spec/react` | `1.2 / 0.5` | Legacy keys (used when `score` is absent) |
|
|
30
|
+
|
|
31
|
+
Also: `THINK_ANCHOR=0` hard off; log `~/.pi/think-anchor.jsonl` (1 MiB truncation, tail-preserving).
|
|
32
|
+
|
|
33
|
+
## Commands
|
|
64
34
|
|
|
65
35
|
| Command | Purpose |
|
|
66
36
|
| --- | --- |
|
|
67
|
-
| `/think-anchor` | Current band,
|
|
68
|
-
| `/think-anchor stats` | Version
|
|
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`).
|
|
37
|
+
| `/think-anchor` | Current band, hits, drift share, last intervention result |
|
|
38
|
+
| `/think-anchor stats` | Version, band distribution, actions, effectiveness, log size |
|
|
92
39
|
|
|
93
|
-
##
|
|
40
|
+
## What it does (source facts)
|
|
94
41
|
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
-
-
|
|
42
|
+
- `turn_end`: reads assistant thinking -> fingerprint (pos/neg/neu) -> sliding window -> band; when hits < `minHits` it only advances the window.
|
|
43
|
+
- State machine `healthy -> warning -> critical`; L1/L2 nudges are injected as `nextTurn` trailing messages.
|
|
44
|
+
- No escalation: when the previous `eff >= 0`, L2 degrades to L1.
|
|
45
|
+
- `session_start`: resets state, keeping only the last 3 samples and pending (cross-session settlement).
|
|
98
46
|
|
|
99
|
-
##
|
|
47
|
+
## Credits
|
|
100
48
|
|
|
101
|
-
|
|
102
|
-
- The default lexicon is Chinese-oriented; English sessions depend on wording.
|
|
103
|
-
- Interventions are hints, not enforcement.
|
|
104
|
-
- The gate is heuristic: very short thinking or pure tool turns are usually not scored.
|
|
49
|
+
Distilled from the DSH plugin [`dsh-anchored-monitor`](https://dsh-plugin.org/zh/plugins/aik358/dsh-anchored-monitor) 0.3.1: the mechanism is upstream, the code is not (turn-level, no separate process).
|
|
105
50
|
|
|
106
51
|
## License
|
|
107
52
|
|
package/README.md
CHANGED
|
@@ -1,107 +1,52 @@
|
|
|
1
|
-
# pi-think-anchor
|
|
1
|
+
# pi-think-anchor
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## 它解决什么问题
|
|
6
|
-
|
|
7
|
-
长时间对话里 thinking 会慢慢「散」:反复试探、犹豫、自我复述,真正推进的句子变少。本扩展读 thinking 的词频指纹,判定当前是「专注 / 过渡 / 发散」,在发散时分级拉回——且只在需要时才出声。
|
|
8
|
-
|
|
9
|
-
## 功能
|
|
10
|
-
|
|
11
|
-
- 每段 thinking 计算专注 / 发散词频比率,滑动窗口聚合后判定波段。
|
|
12
|
-
- 跌入发散时 L1 温和引导;持续发散时 L2 强干预;健康时零注入。
|
|
13
|
-
- 干预走对话尾部注入(中性措辞),不写 system。
|
|
14
|
-
- 命令 `/think-anchor`(状态)、`/think-anchor stats`(版本 / 波段 / 动作 / 有效率 / 日志体积)。
|
|
15
|
-
- 日志 `~/.pi/think-anchor.jsonl`(超 1 MiB 保留尾部整行)。
|
|
3
|
+
思维链锚定监测:读取 thinking 的词频指纹,判定专注 / 过渡 / 发散,发散时分级提示。
|
|
16
4
|
|
|
17
5
|
## 安装
|
|
18
6
|
|
|
19
7
|
```bash
|
|
20
8
|
pi install npm:pi-think-anchor
|
|
21
|
-
pi install ./pi-think-anchor
|
|
22
9
|
```
|
|
23
10
|
|
|
24
11
|
## 配置
|
|
25
12
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
所有键可省略;非法 JSON 自动回退默认。
|
|
29
|
-
|
|
30
|
-
| 键 | 类型 | 默认 | 说明 |
|
|
31
|
-
| --- | --- | --- | --- |
|
|
32
|
-
| `enabled` | boolean | `true` | 总开关 |
|
|
33
|
-
| `score.minHits` | number | `3` | 证据门槛:单轮命中少于该值则不评分、不干预 |
|
|
34
|
-
| `score.spec` | number | `1.2` | 高专注阈值(α 平滑比率 ≥ 该值为 spec) |
|
|
35
|
-
| `score.react` | number | `0.5` | 发散阈值(比率 < 该值为 react) |
|
|
36
|
-
| `score.alpha` | number | `1` | α 平滑(把低命中比率拉向 1,避免「长而散」误判) |
|
|
37
|
-
| `windowSize` | number | `5` | 滑动窗口消息数(长工具链中约 1 轮) |
|
|
38
|
-
| `weights.beta` | number | `0.4` | 中性词计入分子的权重 |
|
|
39
|
-
| `weights.eps` | number | `0.5` | 分母下限(防除零) |
|
|
40
|
-
| `intervention.l1CooldownMin` | number | `10` | L1 冷却(分钟) |
|
|
41
|
-
| `intervention.l2CooldownMin` | number | `15` | L2 冷却(分钟) |
|
|
42
|
-
| `intervention.maxL2` | number | `2` | 每会话 L2 上限 |
|
|
43
|
-
| `lexicon.positive` / `negative` / `neutral` | string[] | 内置中文词表 | 覆盖词表(专注 28 词 / 发散 18 词) |
|
|
44
|
-
| `bands` | object | — | 旧版兼容:无 `score` 时由 `bands.spec/react` 映射 |
|
|
45
|
-
|
|
46
|
-
示例:
|
|
47
|
-
|
|
48
|
-
```json
|
|
49
|
-
{
|
|
50
|
-
"enabled": true,
|
|
51
|
-
"score": { "minHits": 3, "spec": 1.2, "react": 0.5, "alpha": 1 },
|
|
52
|
-
"windowSize": 5,
|
|
53
|
-
"intervention": { "l1CooldownMin": 10, "l2CooldownMin": 15, "maxL2": 2 }
|
|
54
|
-
}
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
### 2 · 环境变量
|
|
13
|
+
配置文件 `~/.pi/think-anchor.json`(可省略,缺省用默认;非法 JSON 回退默认):
|
|
58
14
|
|
|
59
|
-
|
|
|
15
|
+
| 键 | 默认 | 说明 |
|
|
60
16
|
| --- | --- | --- |
|
|
61
|
-
| `
|
|
62
|
-
|
|
63
|
-
|
|
17
|
+
| `enabled` | `true` | 总开关 |
|
|
18
|
+
| `score.minHits` | `3` | 单轮命中少于该值:不评分、不干预 |
|
|
19
|
+
| `score.spec` | `1.2` | 高专注阈值 |
|
|
20
|
+
| `score.react` | `0.5` | 发散阈值 |
|
|
21
|
+
| `score.alpha` | `1` | α 平滑 |
|
|
22
|
+
| `windowSize` | `5` | 窗口消息数 |
|
|
23
|
+
| `weights.beta` | `0.4` | 中性词分子权重 |
|
|
24
|
+
| `weights.eps` | `0.5` | 分母下限 |
|
|
25
|
+
| `intervention.l1CooldownMin` | `10` | L1 冷却(分钟) |
|
|
26
|
+
| `intervention.l2CooldownMin` | `15` | L2 冷却(分钟) |
|
|
27
|
+
| `intervention.maxL2` | `2` | 每会话 L2 上限 |
|
|
28
|
+
| `lexicon.positive/negative/neutral` | 内置中文词表(专注 28 词 / 发散 18 词) | 词表覆盖 |
|
|
29
|
+
| `bands.spec/react` | `1.2 / 0.5` | 旧版兼容(无 `score` 时生效) |
|
|
30
|
+
|
|
31
|
+
其他:环境变量 `THINK_ANCHOR=0` 硬关闭;日志 `~/.pi/think-anchor.jsonl`(1 MiB 截断,保留尾部整行)。
|
|
32
|
+
|
|
33
|
+
## 命令
|
|
64
34
|
|
|
65
35
|
| 命令 | 作用 |
|
|
66
36
|
| --- | --- |
|
|
67
37
|
| `/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`)在日志里可区分。
|
|
38
|
+
| `/think-anchor stats` | 版本、波段分布、动作分布、有效率、日志体积 |
|
|
92
39
|
|
|
93
|
-
##
|
|
40
|
+
## 作用(源码事实)
|
|
94
41
|
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
-
-
|
|
42
|
+
- `turn_end`:取 assistant 消息 thinking → 指纹(pos/neg/neu)→ 滑窗聚合 → 判波段;本轮命中 < `minHits` 时只推进窗口。
|
|
43
|
+
- 状态机 `healthy → warning → critical`;L1/L2 干预以 `nextTurn` 尾部消息注入。
|
|
44
|
+
- 无效不升级:上次 `eff >= 0` 时 L2 降为 L1。
|
|
45
|
+
- `session_start`:重置状态机,仅保留最近 3 条与 pending(跨会话结算)。
|
|
98
46
|
|
|
99
|
-
##
|
|
47
|
+
## 借鉴
|
|
100
48
|
|
|
101
|
-
-
|
|
102
|
-
- 词表默认偏中文;英文会话效果取决于用词。
|
|
103
|
-
- 干预是提示不是强制,模型可能不遵守。
|
|
104
|
-
- 门槛是启发式:极短 thinking 或纯工具轮次通常不评分。
|
|
49
|
+
蒸馏自 DSH 插件 [`dsh-anchored-monitor`](https://dsh-plugin.org/zh/plugins/aik358/dsh-anchored-monitor) 0.3.1:机制思想来源,非代码移植(本实现为回合级,无独立进程)。
|
|
105
50
|
|
|
106
51
|
## License
|
|
107
52
|
|