dsh-experience 0.0.0-stage → 1.0.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/CHANGELOG.md +507 -0
- package/CONTRIBUTING.md +87 -0
- package/LICENSE +21 -0
- package/README.md +165 -2
- package/USER-GUIDE.md +92 -0
- package/config.example.json +13 -0
- package/cordis.patch.yml +4 -0
- package/index.js +1325 -0
- package/lib/capture.js +868 -0
- package/lib/decay.js +279 -0
- package/lib/evidence.js +206 -0
- package/lib/lookup.js +53 -0
- package/lib/recall.js +253 -0
- package/lib/store.js +105 -0
- package/package.json +60 -4
- package/store.example.json +10 -0
- package/tools/e2e.mjs +447 -0
- package/tools/selftest.mjs +1366 -0
package/README.md
CHANGED
|
@@ -1,3 +1,166 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dsh-experience
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **带生命周期治理与指纹防伪的 DSH 跨会话自学习插件** —— 同类任务第一次慢,第二次直接用上第一次的经验,但绝不把一次偶然的变通当成真理。
|
|
4
|
+
|
|
5
|
+
[](https://github.com/Aatzer0/dsh-experience/actions/workflows/test.yml)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://github.com/Aatzer0/dsh-experience)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 它解决什么问题
|
|
12
|
+
|
|
13
|
+
Agent 每次会话都从零开始:读同样的配置文件、踩同样的坑、走同样的探索路径。
|
|
14
|
+
|
|
15
|
+
`dsh-experience` 在插件层把「做过的事」变成「可复用的经验」,并用八道闸门(G1–G8)防止脏经验污染后续决策:
|
|
16
|
+
|
|
17
|
+
1. **自动捕获** —— 任务结束时从工具调用轨迹里提炼步骤与反例,不额外调用模型;
|
|
18
|
+
2. **指纹验证** —— 同一「结果指纹」在多个独立会话中复现才累计证据(步骤文本可以完全不同);
|
|
19
|
+
3. **生命周期治理** —— 按状态设定半衰期,排序分数自动衰减,容量超 80% 时给出归档建议,但**从不物理删除**。
|
|
20
|
+
|
|
21
|
+
## 核心特性
|
|
22
|
+
|
|
23
|
+
| 能力 | 说明 | 位置 |
|
|
24
|
+
| --- | --- | --- |
|
|
25
|
+
| **L1 自动捕获** | 监听 `tools/result`,在 `turn-stopping` 用纯字符串规则蒸馏;八道闸门过滤噪声(G7 步骤质量、G8 否决复现)(无写操作、无恢复、非全绿验证、自噬调用……) | `lib/capture.js` |
|
|
26
|
+
| **L2 指纹防伪** | `targets + green` 结果指纹;指纹命中即视为同一经验,绕开「步骤文本相似度」这一不可靠判据 | `lib/evidence.js` |
|
|
27
|
+
| **人工终审** | `candidate → auto-verified → verified`,最后一跳只能由人完成;Agent 没有任何写状态入口 | `/memory approve` |
|
|
28
|
+
| **M3 生命周期** | `rank = freshness × utility`,半衰期 verified 180 天 / auto-verified 60 天 / candidate 30 天;`dormant` 只降权不删除 | `lib/decay.js` |
|
|
29
|
+
| **零性能损耗** | 捕获是 O(≤40 次调用) 的纯字符串运算,实测 `avgCaptureMs: 1`;召回在本地做词覆盖打分,无网络、无向量库 | `experience_debug` |
|
|
30
|
+
| **可观测** | 六层诊断逐层 PASS/FAIL,直接回答「为什么这次没命中」 | `/memory debug` |
|
|
31
|
+
|
|
32
|
+
- **平台隔离** —— 每条经验记录它是在哪个 OS 上学到的;`win32` 的「PATH 里没有 node」不会被拿到 `darwin` 上当事实用,跨平台既不合并也不召回。
|
|
33
|
+
|
|
34
|
+
## 为什么经验必须由你批准
|
|
35
|
+
|
|
36
|
+
自动捕获的经验只会进入 `candidate` 状态,**永远不会被注入**。只有你执行 `/memory approve <id>` 之后,它才变成 `verified` 并进入召回池。Agent 侧没有任何写入状态的入口——`experience_record` 只能新建候选,`experience_review` 是只读的,「批准」这个动作只存在于 `/memory` 命令里。
|
|
37
|
+
|
|
38
|
+
这是有意的取舍:一条错误经验被注入的代价,远高于你多点一次批准。
|
|
39
|
+
|
|
40
|
+
`auto-verified` 是给未来留的中间档(同一指纹在两个以上独立会话复现才升级),默认**既不自动升级、也不参与召回**(`autoPromote=false`、`allowAutoVerified=false`),在人工批准之前不会污染召回池。
|
|
41
|
+
|
|
42
|
+
## 架构
|
|
43
|
+
|
|
44
|
+
```mermaid
|
|
45
|
+
flowchart LR
|
|
46
|
+
A[Turn 开始] --> B["agent/pre-step<br/>step 1"]
|
|
47
|
+
B --> C{召回<br/>score ≥ 0.15}
|
|
48
|
+
C -- 命中 --> D["PromptContext<br/>注入经验摘要"]
|
|
49
|
+
C -- 未命中 --> E[正常执行]
|
|
50
|
+
D --> E
|
|
51
|
+
E --> F["tools/result<br/>采集调用"]
|
|
52
|
+
F --> G["agent/turn-stopping<br/>蒸馏 + 八道闸门"]
|
|
53
|
+
G -- 指纹/同域合并 --> H[(已有经验)]
|
|
54
|
+
G -- 新建 --> I[candidate]
|
|
55
|
+
I --> J{独立会话<br/>指纹复现 ≥ 2}
|
|
56
|
+
J -- 是 --> K[auto-verified]
|
|
57
|
+
K --> L((人工批准))
|
|
58
|
+
L --> M[verified]
|
|
59
|
+
M --> N["rank 衰减<br/>dormant / 归档建议"]
|
|
60
|
+
H --> N
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
设计取舍写在明面上,而不是留给人猜:
|
|
64
|
+
|
|
65
|
+
- **注入走 `systemPrompt.context` 而非 `agent.inject()`** —— context 文本在 `pre-step` 之后会被重新读取,因此同一 step 就能生效,且不产生会话事件噪音;
|
|
66
|
+
- **不使用 `storageDomain`** —— 它会引入 `zod` / `@deepseek-ai/*` 依赖;本插件坚持零依赖,用 JSON sidecar 存 `$DSH_HOME/experience/store.json`;
|
|
67
|
+
- **召回只在每轮第 1 步** —— 后续 step 的上下文已被工作内容污染,此时注入等于干扰。
|
|
68
|
+
|
|
69
|
+
## 安装
|
|
70
|
+
|
|
71
|
+
### 方式一:从 npm 安装(推荐)
|
|
72
|
+
|
|
73
|
+
```text
|
|
74
|
+
plugin_manager install_bundle target=dsh-experience
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
安装后 `plugin_manager list_plugins` 应出现 `dsh-experience` 且 `fiberPhase: active`。
|
|
78
|
+
|
|
79
|
+
**升级**:Web 插件页对该 bundle 点 **Update**,或再执行一次 `install_bundle target=dsh-experience@<新版本>`。升级走 `listVersions` → `updateBundle`,失败会自动回滚到旧版本。
|
|
80
|
+
|
|
81
|
+
### 方式二:本地开发(Junction 链接)
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
plugin_manager install_bundle target=link:<PATH-TO-dsh-experience>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
链接安装改代码即时生效;npm 安装是复制,改代码不生效,要发新版本。
|
|
88
|
+
|
|
89
|
+
装完在任意会话执行 `/memory list`,看到 `库 0 条` 即安装成功。
|
|
90
|
+
|
|
91
|
+
## 命令速查
|
|
92
|
+
|
|
93
|
+
| 命令 | 作用 |
|
|
94
|
+
| --- | --- |
|
|
95
|
+
| `/memory review` | 查看候选经验,附「为什么还没升级」;被规则否决的记录单列 |
|
|
96
|
+
| `/memory approve <id>` | 人工批准为 `verified`(`--approve-all` 批量) |
|
|
97
|
+
| `/memory reject <id>` | 否决并排除出召回池 |
|
|
98
|
+
| `/memory debug` | 六层诊断 + 整库衰减报告 |
|
|
99
|
+
| `/memory decay [--apply]` | 只读水位报告;`--apply` 归档(可用 `restore` 恢复) |
|
|
100
|
+
| `/memory supersede <id> [原因]` | 标记与当前规则冲突:永不召回,同指纹的轨迹也不再收录 |
|
|
101
|
+
|
|
102
|
+
完整命令见 [USER-GUIDE.md](USER-GUIDE.md),完整教程见 [docs/USAGE.md](docs/USAGE.md)。
|
|
103
|
+
|
|
104
|
+
## 配置
|
|
105
|
+
|
|
106
|
+
运行时配置位于 `~/.dsh/experience/config.json`,可用 `/memory config set <key> <value>` 修改(值会按默认类型强制转换):
|
|
107
|
+
|
|
108
|
+
| key | 默认 | 含义 |
|
|
109
|
+
| --- | --- | --- |
|
|
110
|
+
| `dailyLimit` | `10` | 每日自动捕获上限 |
|
|
111
|
+
| `libraryLimit` | `200` | 库容量(达到 80% 触发归档建议) |
|
|
112
|
+
| `mergeThreshold` | `0.75` | 同任务合并的文本相似度阈值 |
|
|
113
|
+
| `scopeMustMatch` | `true` | 目录不同不合并(硬拦截) |
|
|
114
|
+
| `captureBudgetMs` | `20` | 单次捕获耗时熔断线 |
|
|
115
|
+
| `autoPromote` | `false` | 是否允许证据升级到 `auto-verified` |
|
|
116
|
+
| `allowAutoVerified` | `false` | `auto-verified` 是否参与召回 |
|
|
117
|
+
| `minDistinctSessions` | `2` | 升级所需独立会话数 |
|
|
118
|
+
| `stepsJaccard` | `0.7` | 步骤稳定性门槛 |
|
|
119
|
+
|
|
120
|
+
## 隐私与安全
|
|
121
|
+
|
|
122
|
+
- 只写 `$DSH_HOME/experience/`,**不发起任何网络请求**;
|
|
123
|
+
- 捕获时对 `sk-…`、`ghp_…`、`AKIA…`、`Bearer …`、`password=` 等模式脱敏,绝对路径替换为 `<path>/`;
|
|
124
|
+
- 注入前做长度截断(默认 2 条 / 1600 字符);
|
|
125
|
+
- 仓库不包含任何作者运行时数据,骨架见 [store.example.json](store.example.json)。
|
|
126
|
+
|
|
127
|
+
## 检索加权
|
|
128
|
+
|
|
129
|
+
当任务包含报错(`#error`)或明确排错意图时,含有避坑经验的历史条目将获得 3 倍以上的检索加权,确保模型优先复用排错捷径。
|
|
130
|
+
|
|
131
|
+
实现上:`pitfalls` 与 `reverse_examples` 一并入索引,特征桶分级加权(`#error` 3、`#node` 2.6、`#module` 2.2…),
|
|
132
|
+
`#error`/`#node`/`#module` 同时命中再加确定性加分。实测同一批失败查询上,正确条目的分数从 0.07 提升到 0.36–0.50,
|
|
133
|
+
并在清理流水账后稳定排第 1。
|
|
134
|
+
|
|
135
|
+
## 适用边界
|
|
136
|
+
|
|
137
|
+
这个插件是给**一个人一台机器**的长期协作场景设计的,不是团队共享服务:
|
|
138
|
+
|
|
139
|
+
- **单用户**:经验库在 `~/.dsh/experience/`,没有账号体系、没有并发写保护、没有服务端。
|
|
140
|
+
- **单机**:网络位置、绝对路径、工具链安装方式都随机器而变;跨机器共享需要你自己想办法(同步文件、或用 `/memory teach` 重建)。
|
|
141
|
+
- **按 OS 隔离**:v1.1 起每条记录带 `platform` 标签,跨平台不合并、不召回。
|
|
142
|
+
- **需要人批准**:自动捕获只到 `candidate`,注入池里永远只有你亲手批过的 `verified`。
|
|
143
|
+
- **不替代 AGENTS.md**:经验是"上次怎么做的",规则是"必须怎么做"。两者冲突时以规则为准。
|
|
144
|
+
|
|
145
|
+
## 已知限制
|
|
146
|
+
|
|
147
|
+
- 召回仅在每轮第 1 步生效;
|
|
148
|
+
- `auto-verified` 默认不参与召回 —— 错误经验的代价高于多一次点击;
|
|
149
|
+
- 相似度是词覆盖打分 + 特征桶,不做向量检索(零依赖的代价);**同义改写可能漏召回**——记录里写 `MODULE_NOT_FOUND`、提问只说「模块找不到」时不一定匹配,这需要向量检索才能解决,属能力边界;
|
|
150
|
+
- 淘汰只归档不删除,需要 `restore` 才能回到召回池;
|
|
151
|
+
- 经验只描述「怎么做」,不包含业务代码本身。
|
|
152
|
+
|
|
153
|
+
已知问题的完整清单(含症状、影响、绕法)见 [docs/KNOWN-ISSUES.md](docs/KNOWN-ISSUES.md)。
|
|
154
|
+
|
|
155
|
+
## 开发
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
node tools/selftest.mjs # 108 pass / 0 fail(纯函数 + 闸门 + 衰减 + 脏数据)
|
|
159
|
+
node tools/e2e.mjs # 25 pass / 0 fail(假 Cordis context 驱动真实 apply())
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
零依赖、零构建步骤:修改 `index.js` / `lib/*.js` 后重载插件即可。贡献流程见 [CONTRIBUTING.md](CONTRIBUTING.md)。
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT © 2025 [Aatzer0](https://github.com/Aatzer0)
|
package/USER-GUIDE.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# dsh-experience 使用指南(v0.3 / r8-decay)
|
|
2
|
+
|
|
3
|
+
> 一句话:任务做完了会自动攒经验;你只需要偶尔确认、偶尔清理。
|
|
4
|
+
> 所有命令都在对话框里输入,命令名 `/memory`。
|
|
5
|
+
|
|
6
|
+
## 1. 每天最常用的四件事
|
|
7
|
+
|
|
8
|
+
### ① 看看攒了什么
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
/memory list # 每条一行:id [状态] (auto/manual) 标题 · dormant 标记
|
|
12
|
+
/memory show exp-12 # 某条经验的全文:步骤、反例、证据、衰减
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### ② 确认新经验(人工闸门)
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
/memory review # 候选列表,逐条带“为什么没升级”
|
|
19
|
+
/memory review --approve-all # 一次全批准
|
|
20
|
+
/memory approve exp-12 # 单条批准
|
|
21
|
+
/memory reject exp-13 # 单条否决
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
状态含义:
|
|
25
|
+
|
|
26
|
+
| 状态 | 能否被召回 | 含义 |
|
|
27
|
+
| --- | --- | --- |
|
|
28
|
+
| `verified` | ✅ | 人工确认过,主力经验 |
|
|
29
|
+
| `auto-verified` | ❌(默认) | 多次独立运行+指纹自证,但仍需你点头才进召回池 |
|
|
30
|
+
| `candidate` | ❌ | 刚自动捕获,等 review |
|
|
31
|
+
| `rejected` | ❌ | 已否决 |
|
|
32
|
+
|
|
33
|
+
> 想让 `auto-verified` 也参与召回(不建议):`/memory config set allowAutoVerified true`。
|
|
34
|
+
|
|
35
|
+
### ③ 排查“为什么这次没用上经验”
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
/memory debug # 最近一轮的六层诊断 + 整库衰减报告
|
|
39
|
+
/memory why # 同上,只看六层
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
六层依次是 `[1 存储] → [2 可召回] → [2.5 证据] → [2.6 衰减] → [3 检索] → [4 注入] → [5 模型可见]`,第一处 FAIL 就是卡点,行尾会直接写原因。
|
|
43
|
+
|
|
44
|
+
### ④ 清理变老的记忆
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
/memory decay # 只读:容量水位 + rank 最低的 5 条,什么都不删
|
|
48
|
+
/memory decay --age 400 # 模拟“整库老化 400 天”看效果(演示用,不改数据)
|
|
49
|
+
/memory decay --apply # 真正归档(移入 archive,可恢复)
|
|
50
|
+
/memory restore exp-13 # 误归档就恢复
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**安全保证**:`--apply` 只是把记录移进 `store.archive` 并写 `store.evictions` 审计,**从不物理删除**;`verified` 与 `pinned` 永不被归档。
|
|
54
|
+
|
|
55
|
+
## 2. 冷启动 / 手把手教
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
/memory teach 为 Python 项目加 CLI 参数解析 || 读 pyproject; 用 argparse; 写 --dry-run; 跑一次
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
手教的经验默认 `pinned=true`,永不衰减、永不被淘汰。
|
|
62
|
+
|
|
63
|
+
## 3. 参数调节
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
/memory config # 看当前配置
|
|
67
|
+
/memory config set dailyLimit 10 # 每天最多自动捕获几条
|
|
68
|
+
/memory config set libraryLimit 200 # 库上限(80% 即 160 条时开始提示淘汰)
|
|
69
|
+
/memory config set mergeThreshold 0.75
|
|
70
|
+
/memory config set allowAutoVerified false
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## 4. 日常节奏建议
|
|
74
|
+
|
|
75
|
+
- **每次做完一类新任务**:`/memory review` 扫一眼,值得留的批准。
|
|
76
|
+
- **每周一次**:`/memory decay` 看水位,库还小就什么都不用做。
|
|
77
|
+
- **发现某条经验总被召回却不合适**:`/memory reject <id>`,或直接告诉我改进它。
|
|
78
|
+
- **不确定为什么没生效**:先 `/memory debug`,把那一屏发我。
|
|
79
|
+
|
|
80
|
+
## 5. 我需要知道的三个事实
|
|
81
|
+
|
|
82
|
+
1. 召回只在**每轮的第 1 步**触发 → 新任务请开新的一轮(新消息)再描述任务。
|
|
83
|
+
2. `auto-verified` 默认不进召回池,这是有意的:错误经验的代价 > 多点一次批准。
|
|
84
|
+
3. 存档位置 `~/.dsh/experience/`:`store.json`(经验/轨迹/审计)、`config.json`、`applied.json`(构建自证)。
|
|
85
|
+
## 经验跟当前规则冲突了怎么办
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
/memory supersede exp-5 沙箱禁止 node --test,必须直跑测试文件
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
这条经验不会删除(留作证据),但从此不会被召回;同样的产物再出现时,G8 闸门会拒绝重新收录,
|
|
92
|
+
省掉"又被学一遍、又得再拒一次"的循环。规则改了以后想恢复,可以直接 `/memory approve exp-5`。
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"dailyLimit": 10,
|
|
3
|
+
"libraryLimit": 200,
|
|
4
|
+
"mergeThreshold": 0.75,
|
|
5
|
+
"scopeMustMatch": true,
|
|
6
|
+
"captureBudgetMs": 20,
|
|
7
|
+
"maxRecordChars": 2000,
|
|
8
|
+
"maxCalls": 40,
|
|
9
|
+
"autoPromote": false,
|
|
10
|
+
"minDistinctSessions": 2,
|
|
11
|
+
"stepsJaccard": 0.7,
|
|
12
|
+
"allowAutoVerified": false
|
|
13
|
+
}
|
package/cordis.patch.yml
ADDED