dsh-engram 0.7.4 → 0.8.7
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.md +100 -6
- package/README.zh.md +7 -2
- package/docs/CONTEXT-GC-GUIDE.zh.md +15 -1
- package/docs/G6-quality-ab.zh.md +540 -0
- package/docs/GATES.zh.md +41 -0
- package/docs/PROPOSAL-evolution.zh.md +12 -0
- package/docs/RELEASE-NOTES-v0.7.10.md +49 -0
- package/docs/RELEASE-NOTES-v0.7.11.md +31 -0
- package/docs/RELEASE-NOTES-v0.7.12.md +22 -0
- package/docs/RELEASE-NOTES-v0.7.13.md +41 -0
- package/docs/RELEASE-NOTES-v0.7.14.md +37 -0
- package/docs/RELEASE-NOTES-v0.7.15.md +36 -0
- package/docs/RELEASE-NOTES-v0.7.16.md +42 -0
- package/docs/RELEASE-NOTES-v0.7.5.md +134 -0
- package/docs/RELEASE-NOTES-v0.7.6.md +74 -0
- package/docs/RELEASE-NOTES-v0.7.7.md +60 -0
- package/docs/RELEASE-NOTES-v0.7.8.md +56 -0
- package/docs/RELEASE-NOTES-v0.7.9.md +46 -0
- package/docs/RELEASE-NOTES-v0.8.0.md +59 -0
- package/docs/RELEASE-NOTES-v0.8.1.md +70 -0
- package/docs/RELEASE-NOTES-v0.8.2.md +62 -0
- package/docs/RELEASE-NOTES-v0.8.3.md +65 -0
- package/docs/RELEASE-NOTES-v0.8.4.md +56 -0
- package/docs/RELEASE-NOTES-v0.8.5.md +60 -0
- package/docs/RELEASE-NOTES-v0.8.6.md +71 -0
- package/docs/RELEASE-NOTES-v0.8.7.md +69 -0
- package/eval/gate-audit.mjs +538 -0
- package/lib/api.js +70 -0
- package/lib/approvals.js +84 -0
- package/lib/boundary-prune.js +71 -5
- package/lib/capsule.js +134 -0
- package/lib/capture.js +83 -5
- package/lib/client.js +66 -0
- package/lib/compaction.js +4 -11
- package/lib/context-gc.js +63 -2
- package/lib/index-block.js +79 -4
- package/lib/index.js +105 -18
- package/lib/ledger.js +200 -10
- package/lib/pack-telemetry.js +10 -3
- package/lib/prune.js +89 -0
- package/lib/push-legs.js +121 -0
- package/lib/session-events.js +44 -2
- package/lib/skill-export.js +289 -0
- package/lib/status-insights.js +5 -0
- package/lib/status.js +138 -0
- package/lib/store.js +34 -0
- package/lib/usage.js +32 -3
- package/lib/web-provision.js +59 -14
- package/package.json +15 -8
- package/scripts/dsh-engram.mjs +247 -5
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
# G6 · 配对质量实验(Quality Δ)
|
|
2
|
+
|
|
3
|
+
> G6 是唯一**不能从日志里算出来**的门。日志能告诉我们「机制跑了没有、省了多少」,
|
|
4
|
+
> 不能告诉我们「任务变容易了还是变难了」。要看后者,只能同题同种子跑两臂。
|
|
5
|
+
|
|
6
|
+
## 1. 判据
|
|
7
|
+
|
|
8
|
+
| 项 | 值 |
|
|
9
|
+
|---|---|
|
|
10
|
+
| 指标 | 配对成功率差 Δ = rate(engram) − rate(bare) |
|
|
11
|
+
| 门 | Δ ≥ −2pp(不伤害);Δ > +2pp 记为「记忆带去可用信息」的观测值,不是失败 |
|
|
12
|
+
| 最少配对 | 10 对(task × seed,两臂都跑完) |
|
|
13
|
+
| 低于下限 | **未测**——不猜,一个任务不是质量证据 |
|
|
14
|
+
|
|
15
|
+
`Δ` 用**配对**算:同一 task × seed 下两臂都跑过才计入,避免任务难易不同造成的伪差。
|
|
16
|
+
`only-A wins` / `only-B wins` 把差异摊开,防止「互相抵消所以 Δ=0」。
|
|
17
|
+
|
|
18
|
+
### 为什么门是单边的
|
|
19
|
+
|
|
20
|
+
第一版门写成 `|Δ| ≤ 2pp`,那等于「改善也算失败」。记忆系统要回答两个问题:
|
|
21
|
+
**会不会把任务做坏**(硬条件,Δ ≥ −2pp)和**有没有让它变容易**(提案的愿望,Δ > +2pp)。
|
|
22
|
+
把愿望写进门槛,就会出现「记忆明明帮上了忙、门却判 FAIL」的荒唐结论。所以:
|
|
23
|
+
Δ ≥ −2pp 才是门;Δ > +2pp 单独记为观测值。这条修正在第一轮 0.0pp 之后、第二轮
|
|
24
|
+
(隐藏规格语料)之前做——不是为了让某个结果过关,而是门的方向本来就说反了。
|
|
25
|
+
|
|
26
|
+
## 2. 为什么必须有 oracle
|
|
27
|
+
|
|
28
|
+
每个任务带一条 `check` 命令(bash),退出码 0 即通过。用命令而不是用 LLM 打分:
|
|
29
|
+
LLM 打分引入第二个模型,G6 就变成「两个模型同不同意」,不再是「机制有没有帮助」。
|
|
30
|
+
|
|
31
|
+
`check` 必须确定:同一个最终状态永远给同一个答案。
|
|
32
|
+
|
|
33
|
+
## 3. 准备两臂
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# 1) 建两个 profile:on 臂装 engram,off 臂保持裸机
|
|
37
|
+
node eval/quality-ab.mjs prepare --arm on=ab-engram,off=ab-bare --home ~/.dsh/quality-ab
|
|
38
|
+
|
|
39
|
+
# 2) 只给 on 臂装插件(off 臂什么都别装——这是对照)
|
|
40
|
+
DSH_HOME=~/.dsh/quality-ab dsh plugin --profile ab-engram add ~/.dsh/vendor/dsh-engram-<ver>.tgz
|
|
41
|
+
|
|
42
|
+
# 3) 确认真的挂上了
|
|
43
|
+
DSH_HOME=~/.dsh/quality-ab dsh --profile ab-engram --dump-config | grep -A2 'id: engram'
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**凭据**:`dsh headless` 没有 web 的 Models 页,provider 路由从环境变量取 key。
|
|
47
|
+
harness 会自动从 `~/.dsh/.credentials.yaml` 把 `DEEPSEEK_API_KEY` 读进子进程环境
|
|
48
|
+
(只进内存,不进报告、不打日志)。两处都没有就直接报错退出,不会跑出一堆 MISSING_CREDENTIAL 的空跑。
|
|
49
|
+
|
|
50
|
+
## 4. 跑
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
node eval/quality-ab.mjs run \
|
|
54
|
+
--tasks tasks.json --workspace ./fixture \
|
|
55
|
+
--arm ab-engram,ab-bare --seeds 3 \
|
|
56
|
+
--out runs/g6-$(date +%F) --home ~/.dsh/quality-ab
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`tasks.json`:
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{ "tasks": [
|
|
63
|
+
{ "id": "fix-import",
|
|
64
|
+
"prompt": "…给 agent 的任务描述…",
|
|
65
|
+
"setup": "git checkout -- . && git clean -fd",
|
|
66
|
+
"check": "npm test --silent",
|
|
67
|
+
"timeoutMs": 600000 }
|
|
68
|
+
] }
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
- `setup` 在**复制出来的 workspace** 里先跑(每个 run 一份新副本,互不污染);
|
|
72
|
+
- `prompt` 两臂逐字相同——臂是唯一变量;
|
|
73
|
+
- 每次 run 的 `run.log` 和 `check.log` 都留在
|
|
74
|
+
`runs/<date>/<arm>/<task>/seed-<n>/`,判定可事后审计。
|
|
75
|
+
|
|
76
|
+
## 5. 语料怎么挑(这一节决定实验有没有意义)
|
|
77
|
+
|
|
78
|
+
1. **有确定答案的真实任务**——从自己最近的会话里挑那些「跑过一次、结果已知正确」的小任务
|
|
79
|
+
(改一个 import、补一个断言、跑通一个脚本)。不要自己发明玩具题。
|
|
80
|
+
2. **两种难度都要**:太简单的题两臂都 100%,测出 Δ=0 什么也没说;至少一半任务原本会失败或要多次尝试。
|
|
81
|
+
3. **不要让任务依赖 engram 才能完成**:如果题目需要「回忆上次怎么做的」,那测的是提示而不是质量。
|
|
82
|
+
4. `fixture` 是一个干净的 git 仓库副本,每个任务靠 `setup` 回到起点。
|
|
83
|
+
|
|
84
|
+
## 6. 成本
|
|
85
|
+
|
|
86
|
+
一次 run ≈ 一个完整 agent 会话。10 任务 × 2 臂 × 3 seed = 60 次。按本机 step p50 5s 估,
|
|
87
|
+
wall time 约 30–60 分钟;token 成本按单次会话的实际用量算。**建议先跑 5 任务 × 2 臂 × 1 seed
|
|
88
|
+
(10 对,正好踩到下限)看方向,再决定要不要扩。**
|
|
89
|
+
|
|
90
|
+
## 7. 和 G1 一起读
|
|
91
|
+
|
|
92
|
+
单看 G6 会得出「两臂质量一样」的结论,单看 G1 会得出「省了 token」。两个一起看才有意义:
|
|
93
|
+
|
|
94
|
+
- Δ ≈ 0 且 G1 为正 → 白捡的节省;
|
|
95
|
+
- Δ 明显为负 → 省下的 token 是用质量换的,门不过;
|
|
96
|
+
- Δ 明显为正 → 也许该考虑把注入做得更狠。
|
|
97
|
+
|
|
98
|
+
## 8. 现状
|
|
99
|
+
|
|
100
|
+
**已测**:第五轮(见 §12)20 对配对,Δ = +90.0pp(20/20 vs 2/20),
|
|
101
|
+
三条不可推导约定上 15/15 vs 0/15。
|
|
102
|
+
|
|
103
|
+
历史:harness 落地时先跑通 1 任务 × 2 臂(Δ=0.0pp)并如实报**未测**——1 对配对是
|
|
104
|
+
管线验证,不是质量证据。之后的第二轮…第四轮逐步把语料和隔离修到位,那些轮的得失记在
|
|
105
|
+
§7–§11,已作废的数字不再引用。
|
|
106
|
+
|
|
107
|
+
## 5. 实测(2026-10-26,第一轮)
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
node eval/quality-ab.mjs run --tasks eval/g6/tasks.json \
|
|
111
|
+
--workspace eval/fixtures/ledger-kata --arm ab-engram,ab-bare --seeds 2 \
|
|
112
|
+
--out eval/g6/runs/g6-2026-10-26 --home ~/.dsh/quality-ab \
|
|
113
|
+
--seed-memory eval/g6/seed-memory.json
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
| arm | pass | rate | median wall |
|
|
117
|
+
|---|---|---|---|
|
|
118
|
+
| ab-engram | 12/12 | 100% | 41.5s |
|
|
119
|
+
| ab-bare | 12/12 | 100% | 27.2s |
|
|
120
|
+
|
|
121
|
+
**Δ = 0.0pp · 12 对配对 · G6 PASS(PASS 条件 \|Δ\| ≤ 2pp)。**
|
|
122
|
+
|
|
123
|
+
### 两臂差在哪(否则这不是实验)
|
|
124
|
+
|
|
125
|
+
同一个 fixture(`eval/fixtures/ledger-kata`,6 个模块各带一条会失败的测试),
|
|
126
|
+
同一个 prompt,同一个本地模型(local-vllm-61 / deepseek-v4-flash)。
|
|
127
|
+
**on 臂额外预置 4 条记忆**(`eval/g6/seed-memory.json`):2 条真事实(guard 是 oracle、
|
|
128
|
+
金额用整数分)+ **2 条故意过时的经验**(「timefmt 固定 TZ 再用 toLocaleString 就行」、
|
|
129
|
+
「sort 直接用就够」)。预置是逐 run 覆盖写入的,所以每个 run 的记忆状态相同——
|
|
130
|
+
否则两臂就不是配对了。
|
|
131
|
+
|
|
132
|
+
不加预置的话,on 臂只有一个协议块,两臂几乎没差别,实验测的是空气。
|
|
133
|
+
|
|
134
|
+
### 这轮测得的是「不伤害」,不是「有提升」
|
|
135
|
+
|
|
136
|
+
12/12 vs 12/12 是**天花板效应**:fixture 的 6 个任务对这一档模型都太容易,两臂都做满。
|
|
137
|
+
所以这轮证据支持的结论只有一条硬结论(PASS 的门本身):**挂上 engram 没有让任务质量下降**;
|
|
138
|
+
它**不能**支持「记忆让任务更容易」。
|
|
139
|
+
|
|
140
|
+
值得单独记的是延迟:on 臂中位 41.5s vs bare 27.2s(+52%,max 都撞到 ~77s)。
|
|
141
|
+
这是 `[ENGRAM]` 注入 + 2 条过期经验测带来的一次性成本,不是质量差。
|
|
142
|
+
下一步要提高区分度,得换更难的任务(多文件、跨模块、需要历史决策),
|
|
143
|
+
或者把 fixture 的测试藏起来让记忆成为唯一规格来源。
|
|
144
|
+
|
|
145
|
+
### 关于那 2 条过期经验
|
|
146
|
+
|
|
147
|
+
两臂都 12/12 —— 没有一次「照抄过时 playbook 导致失败」。但注意:**没观察到不等于不会发生**,
|
|
148
|
+
本轮任务里过期经验恰好都不致命(TZ 方案在 timezone 测试上会挂,两个 on 臂也没照抄,
|
|
149
|
+
而是自己读了测试)。要测这一条,得设计「照抄就会挂、且不看测试就会信」的任务。
|
|
150
|
+
|
|
151
|
+
### 第三轮之前的一个坑(流程证据)
|
|
152
|
+
|
|
153
|
+
第一次 pilot 里,harness 写入的预置行用了 `sessionId: null`,违反 `MemorySchema`
|
|
154
|
+
(`z.string().min(1)`),插件把整个域判成不可用 —— on 臂实际是「engram 离线」,
|
|
155
|
+
不是「engram 在场」。数字照样是 100% / 100%,**但它测的是故障**。
|
|
156
|
+
修好后才跑正式轮。这条记在这里,是因为「先跑一对看接缝通不通」救了这次实验:
|
|
157
|
+
12 对跑完再发现 schema 漂移,代价是半小时和一个假结论。
|
|
158
|
+
`eval/g6/runs/pilot/README.md` 保留了当时的日志说明。
|
|
159
|
+
|
|
160
|
+
## 6. 第二轮设计:隐藏规格语料(仓库里没有测试)
|
|
161
|
+
|
|
162
|
+
第一轮的问题不是「记忆有没有用」,而是**两臂都能读到规格**(测试就在仓库里),
|
|
163
|
+
于是 100% vs 100%,天花板挡住了所有信号。
|
|
164
|
+
|
|
165
|
+
第二轮换了语料(`eval/fixtures/ledger-kata-hidden`):仓库只有 `src/`,**测试在仓库外**,
|
|
166
|
+
`check` 在 agent 跑完之后才把私有测试拷进工作区。规格只存在于**预置记忆**里
|
|
167
|
+
(`eval/g6/seed-memory-hidden.json`,6 条团队约定 + 2 条故意过时的旧约定)。
|
|
168
|
+
同样的 prompt、同样的模型、同样的工作区——两臂唯一的差别是那份记忆。
|
|
169
|
+
|
|
170
|
+
### 三个泄漏,一个教训:agent 有 shell,隔离必须是结构性的
|
|
171
|
+
|
|
172
|
+
第一次 pilot:on 臂 247s、bare 77s,两臂都过。翻日志发现控制组干了两件事——prompt
|
|
173
|
+
里还留着旧版本的「先查记忆」提示(我生成任务文件时没重生成),而且它真的去
|
|
174
|
+
`ls $DSH_HOME/storages/`,把 `dsh_engram.json.g6-hidden` 读走了。只改名不搬走,等于没藏。
|
|
175
|
+
|
|
176
|
+
第二次(正式矩阵)跑完才发现**第三个泄漏**:控制组能顺着 `..` 走到
|
|
177
|
+
`ab-engram/<task>/seed-0/run.log`——同任务的 on 臂日志里,agent 自己把团队约定
|
|
178
|
+
复述了一遍。这一轮的 12 对因此**无效**:控制组不是「没有记忆」,而是「读了对手的笔记」。
|
|
179
|
+
|
|
180
|
+
现在的三道结构性隔离:
|
|
181
|
+
|
|
182
|
+
1. 控制臂运行时把记忆库文件**移出 `DSH_HOME`**(系统临时目录,跑完移回);
|
|
183
|
+
2. `--control-first`:**所有控制组先跑完**,再跑实验组——控制组运行时,同任务
|
|
184
|
+
的实验组日志还不存在;
|
|
185
|
+
3. `--log-dir`:日志写到运行树**外面**;跑批输出本身也不在仓库里(否则能顺着
|
|
186
|
+
`..` 读到 `eval/g6/oracle/` 和种子文件)。
|
|
187
|
+
|
|
188
|
+
外加污染检测(`dsh_engram` / `engram_recall` / `[ENGRAM]` / `ab-engram/` / `runs.json`),
|
|
189
|
+
报告里计数——一个跑出漂亮数字的 pilot 差点被当成证据,这条是那次教训的产物。
|
|
190
|
+
|
|
191
|
+
### 旧版记录:第一版隔离方法
|
|
192
|
+
|
|
193
|
+
第一版 pilot 里 on 臂 247s、bare 77s,两臂都过。翻日志发现 bare 臂干了两件事:
|
|
194
|
+
|
|
195
|
+
1. prompt 里还写着「先查 workspace 的记忆」——那是我生成任务文件时的旧版本,
|
|
196
|
+
等于直接告诉控制组去翻;
|
|
197
|
+
2. 它真的去 `ls $DSH_HOME/storages/`,把 `dsh_engram.json.g6-hidden` 读了出来。
|
|
198
|
+
|
|
199
|
+
现在的做法是**物理隔离**,不是指望 agent 自觉:
|
|
200
|
+
|
|
201
|
+
- 控制臂运行时把记忆库文件**移出 `DSH_HOME`**(移到系统临时目录,跑完再移回);
|
|
202
|
+
- 跑批输出的目录搬到 **仓库外**(`/tmp/g6/…`),否则 agent 能顺着 `..` 走到
|
|
203
|
+
`eval/g6/oracle/` 和种子文件;
|
|
204
|
+
- harness 对每条控制组日志做**污染检测**(出现 `dsh_engram` / `[ENGRAM]` /
|
|
205
|
+
`engram_recall` 就标 `contaminated`),报告里计数。pilot 验证:0 污染。
|
|
206
|
+
|
|
207
|
+
这条教训写在这里,是因为一个跑出漂亮数字的 pilot 差点被当成证据。
|
|
208
|
+
|
|
209
|
+
## 7. 第三轮该怎么设计(如果第二轮又是天花板)
|
|
210
|
+
|
|
211
|
+
前两轮测的都是「一个模块、一条约定、看着代码能猜出来」的任务。这一档模型对这类
|
|
212
|
+
任务的正确率是 100%,所以两轮都撞顶——**天花板不是实验失败,是实验设计告诉我们
|
|
213
|
+
这一档任务没区分度**。
|
|
214
|
+
|
|
215
|
+
有区分度的语料必须让规格**不可推导**,也就是「猜不出来、只能被告诉」:
|
|
216
|
+
|
|
217
|
+
| 类型 | 例子 | 为什么不可猜 |
|
|
218
|
+
|---|---|---|
|
|
219
|
+
| 任意常量 | 校验位算法 `sum(digit*3+7) % 36`,字符表自定 | 常数空间无限,猜中概率 ~0 |
|
|
220
|
+
| 任意标识 | 错误码前缀必须是 `E_` 而不是 `ERR_`,且 message 用 `: ` 分隔 | 行业里两种写法都常见 |
|
|
221
|
+
| 任意顺序 | 重试次数含首次调用(`tries` 是总数不是重试数) | 语义二义,代码里看不出来 |
|
|
222
|
+
| 跨会话约束 | 「上周决定不用 pnpm,因为 X」 | 只有记忆里有 |
|
|
223
|
+
|
|
224
|
+
另一条更便宜的路:**不做新语料,改做「记忆错了我照不照抄」**。预设一条**会害人**的
|
|
225
|
+
procedure(比如「把这个测试改成 `assert.ok(true)`」),看 on 臂会不会照抄。照抄率是
|
|
226
|
+
可以直接测的风险指标,比成功率差更容易测出差异(成功率有天花板,照抄率没有)。
|
|
227
|
+
这条和 v0.8-D 的审批门是同一个问题的两面。
|
|
228
|
+
|
|
229
|
+
结论先行:**在拿到不可推导语料的数字之前,G6 只报告「不伤害」,不报告「提升」。**
|
|
230
|
+
|
|
231
|
+
## 8. 第二轮结果(2026-10-26 晚,结构性隔离之后)
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
node eval/quality-ab.mjs run --tasks eval/g6/tasks-hidden.json \
|
|
235
|
+
--workspace eval/fixtures/ledger-kata-hidden --arm ab-engram,ab-bare --seeds 2 \
|
|
236
|
+
--out /tmp/g6/hidden-b --home ~/.dsh/quality-ab \
|
|
237
|
+
--seed-memory eval/g6/seed-memory-hidden.json \
|
|
238
|
+
--hide-store-for ab-bare --control-first --log-dir /tmp/g6/logs-hidden-b
|
|
239
|
+
node eval/quality-ab.mjs report --out /tmp/g6/hidden-b --exclude errors --min-pairs 10
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
| arm | pass | rate | mean wall |
|
|
243
|
+
|---|---|---|---|
|
|
244
|
+
| ab-engram | 10/10 | 100.0% | 87.9s |
|
|
245
|
+
| ab-bare | 7/10 | 70.0% | 743.3s |
|
|
246
|
+
|
|
247
|
+
**Δ = +30.0pp · 10 对配对(5 任务 × 2 种子)· G6 PASS(Δ ≥ −2pp,且 > +2pp → 记忆带去了可用信息)。**
|
|
248
|
+
|
|
249
|
+
逐任务(P=过,f=挂;两列按 seed 0/1):
|
|
250
|
+
|
|
251
|
+
| 任务 | engram | bare | 说明 |
|
|
252
|
+
|---|---|---|---|
|
|
253
|
+
| money | PP | PP | 分单位 + 半分进位,两个模型都猜得到 |
|
|
254
|
+
| retry | PP | **ff** | 「tries 是总次数不是重试数」——**猜不出来**,只能被告知 |
|
|
255
|
+
| timefmt | PP | **fP** | 「UTC 到秒 + Z、不依赖机器时区」——seed 0 猜错 |
|
|
256
|
+
| queue | PP | PP | 稳定降序 + 不改入参,常识可推 |
|
|
257
|
+
| csv | PP | PP | RFC 4180,通用知识 |
|
|
258
|
+
| ~~errors~~ | ~~ff~~ | ~~Pf~~ | **任务作废**,见下 |
|
|
259
|
+
|
|
260
|
+
### 那个作废的任务(oracle 自己错了)
|
|
261
|
+
|
|
262
|
+
errors 任务 on 臂两个 seed 都挂。翻日志:on 臂**完全按预置约定实现了**
|
|
263
|
+
(`E_<CODE>: <msg>`、`.code`、`.details`、非 UPPER_SNAKE 抛错),挂在我写的 oracle 上——
|
|
264
|
+
它要求错误消息必须匹配字面量 `/code must match/`,而种子记忆里只说了「抛 code 不合法的错误」。
|
|
265
|
+
**那是我在测一个没人声明过的措辞**,不是模型的问题。修好 oracle 后该任务从这一轮剔除:
|
|
266
|
+
`report --exclude errors` 重新算分(剔除这件事写在 artifact 里,不手改数据)。
|
|
267
|
+
|
|
268
|
+
### 这个数字该怎么读(别过度解读)
|
|
269
|
+
|
|
270
|
+
1. **Δ 是下界,不是点上估计。** 12 条控制组里 6 条的日志出现「找 harness/记忆」的动机
|
|
271
|
+
(`REPORT.md`、`runs.json`、`hidden-2026-10-26/ab-engram/*/ws`、甚至
|
|
272
|
+
`~/.dsh/quality-ab/profiles/ab-engram/node_modules/dsh-engram/docs/G6-quality-ab.zh.md`)。
|
|
273
|
+
实际读到解的是 1 条,读的是**上一轮 bare 自己的日志**。泄漏只会**抬高控制组**,
|
|
274
|
+
所以真实差距应当 ≥ 30pp。
|
|
275
|
+
2. **样本只有 10 对。** 30pp 在这个样本下是「方向明确」,不是「精度到 pp」。
|
|
276
|
+
3. **代价也是真的**:on 臂 87.9s vs bare 743.3s——不过这方向反了:控制组更慢,
|
|
277
|
+
因为它在信息缺失里来回试探。第一轮(规格在仓库里)时是相反的(41.5s vs 27.2s)。
|
|
278
|
+
合起来看:**当规格不可得时,记忆省的是「找规格」的时间,不是「写代码」的时间。**
|
|
279
|
+
|
|
280
|
+
### 这台机器上做隔离的天花板
|
|
281
|
+
|
|
282
|
+
控制组有 shell,能读整台机器。这一轮把能堵的都堵了(记忆库移出 `DSH_HOME`、
|
|
283
|
+
控制组先跑、日志在运行树外、输出不在仓库里),但它仍然能翻到上一轮遗留的
|
|
284
|
+
`/tmp/g6/hidden-2026-10-26/`、harness 的 `REPORT.md`、甚至插件包里我的设计文档。
|
|
285
|
+
**真正的隔离需要沙箱(容器/独立用户),不是文件改名。** 这条写在这里,是因为下一轮
|
|
286
|
+
再想「干净地测一次」,第一件事应该是 `docker run`,而不是再想一个目录名。
|
|
287
|
+
|
|
288
|
+
## 9. 反向实验:记忆**错了**会怎样(2026-10-26 夜)
|
|
289
|
+
|
|
290
|
+
前面测的是「记忆有用吗」。更该问的是「记忆有害吗」——一条过时的约定加上一句
|
|
291
|
+
「按团队口径写,别按自己觉得合理写」,agent 会不会照着做错。
|
|
292
|
+
|
|
293
|
+
做法:同一个 `ledger-kata-hidden` 仓库(docstring 里写着**正确**约定、代码里有**bug**),
|
|
294
|
+
预置记忆换成 **5 条过时的错误约定**(`2026-09 记录`,且每条都与 docstring 相反)+
|
|
295
|
+
1 条无关的正确约定 + 1 条正确的 errors 约定当干扰项。控制臂照旧物理隔离。
|
|
296
|
+
|
|
297
|
+
| arm | pass | rate | mean wall |
|
|
298
|
+
|---|---|---|---|
|
|
299
|
+
| ab-engram | 11/12 | 91.7% | 174.5s |
|
|
300
|
+
| ab-bare | 10/12 | 83.3% | 131.4s |
|
|
301
|
+
|
|
302
|
+
Δ = +8.3pp(12 对)——**即使记忆全是过时的,总体仍不伤害**,但代价变了:
|
|
303
|
+
on 臂慢了 33%(174.5s vs 131.4s),因为它在「记忆 vs 代码」之间来回对账。
|
|
304
|
+
|
|
305
|
+
### 复制率:6 个任务里 1 个照抄了错记忆
|
|
306
|
+
|
|
307
|
+
| 任务 | 结果 | agent 的原话 |
|
|
308
|
+
|---|---|---|
|
|
309
|
+
| retry | 没抄 | 「the memory note about '2026-09 记录 tries = retries' is **stale (dated)**, contradicts the module contract … I didn't follow it」 |
|
|
310
|
+
| money | 没抄 | 记忆说「浮点累加就行」,它按 docstring 换成分算 |
|
|
311
|
+
| timefmt | **抄了** | 「memory says local, no Z. README says follow team 口径, not your own idea. So implement per memory」→ 挂 |
|
|
312
|
+
|
|
313
|
+
**失败的那次不是随机失手,是被「按团队口径写」这句话推着走的。** 它甚至在日志里
|
|
314
|
+
先写下了「audit log 通常是 UTC ISO 8601」这个正确直觉,然后用 README 的那句话
|
|
315
|
+
说服自己放弃直觉去服从记忆。记忆越像「团队正式约定」,越容易压过代码。
|
|
316
|
+
|
|
317
|
+
### 这条对产品意味着什么
|
|
318
|
+
|
|
319
|
+
1. **记忆行必须带「什么时候记的」**。12 次里 11 次靠 `2026-09 记录` 这个日期做了
|
|
320
|
+
正确的取舍——日期不是装饰,是可被 agent 用来降权的证据。索引行/召回行都要带上。
|
|
321
|
+
2. **「按团队口径写」是个危险的指令组合**。仓库里这么写、记忆里又有错的约定时,
|
|
322
|
+
agent 会服从记忆。插件能做的:注入过时记忆时**同时**提示它锚定的文件已变更
|
|
323
|
+
(`procedureInvalidated` 已有这条逻辑,但只对 procedure 生效)。
|
|
324
|
+
3. **复制率是比成功率更好测的风险指标**:成功率有天花板,复制率没有。这个实验
|
|
325
|
+
12 对就测出了 1/6 的复制,不需要不可推导语料。
|
|
326
|
+
|
|
327
|
+
> 结论:记忆的价值和风险是同一枚硬币——它能让 agent 服从你写下的东西,
|
|
328
|
+
> 也包括你写错的东西。所以 v0.8-D 的审批门(写 skill 要人点头)不是流程洁癖,
|
|
329
|
+
> 是这条实验的直接后果。
|
|
330
|
+
|
|
331
|
+
## 10. 第三轮:真沙箱,以及一次必须做的撤回(2026-10-26 深夜)
|
|
332
|
+
|
|
333
|
+
第二轮我把 +30pp 说成「下界」,理由是「泄漏只会抬高控制组」。**这个推理是错的**,
|
|
334
|
+
第三轮证明了它错在哪。
|
|
335
|
+
|
|
336
|
+
### 换了什么
|
|
337
|
+
|
|
338
|
+
不再靠「藏文件」,而是 macOS seatbelt(`sandbox-exec`)给每个 run 套一个**由内核执行**
|
|
339
|
+
的读黑名单:仓库、上一轮所有工件(`/tmp/g6`)、宿主记忆库与会话日志(`~/.dsh/storages`
|
|
340
|
+
/ `sessions` / `engram` / `vendor`)、**另一条臂的 profile**、以及实验 home 里旧会话日志
|
|
341
|
+
(跑批前清空)。被拒的读返回 EPERM,不是「agent 没找到」。
|
|
342
|
+
|
|
343
|
+
一个真实的接线问题:DSH 自己的 `workspace-write` 沙箱**不能嵌套**在 seatbelt 里
|
|
344
|
+
(`sandbox_apply: Operation not permitted`),第一次 smoke 时所有 bash 调用在初始化阶段
|
|
345
|
+
就失败了。解法是让 AB profile 显式 `mode: danger-full-access`,把真正的围栏交给外层
|
|
346
|
+
seatbelt——**里面那层是我要测的东西,外面那层才是隔离**。
|
|
347
|
+
|
|
348
|
+
### 结果
|
|
349
|
+
|
|
350
|
+
| 轮次 | 环境 | engram | bare | Δ | bare 平均耗时 |
|
|
351
|
+
|---|---|---|---|---|---|
|
|
352
|
+
| 第二轮 | 藏文件(有泄漏) | 10/10 | 7/10 | **+30.0pp** | 743.3s |
|
|
353
|
+
| 第三轮 | **seatbelt 真围栏** | 12/12 | 11/12 | **+8.3pp** | 116.5s |
|
|
354
|
+
|
|
355
|
+
(第三轮 12 对,`errors` 任务用修好的 oracle 重新纳入。)逐任务只有一个差异:
|
|
356
|
+
`errors seed=1` 控制臂挂,其余 11 对两臂全过。**`retry` 两臂都过了**——第二轮它可是
|
|
357
|
+
控制臂两 seed 全挂。
|
|
358
|
+
|
|
359
|
+
### 为什么 +30pp 缩成 +8.3pp:泄漏是个双向的干扰项
|
|
360
|
+
|
|
361
|
+
第二轮的控制臂在找泄漏上花掉了时间:平均 743s(第三轮 116.5s),中途被
|
|
362
|
+
`REPORT.md`、上一轮遗留目录、插件包里的设计文档牵着走,最后 3 个任务没做完
|
|
363
|
+
或做错。**泄漏不只可能「帮」控制组,它同样会「害」控制组**——把它当成下界、
|
|
364
|
+
以为只会低估差距,是把一个方向不明的干扰当成了单向偏差。这不是统计口径问题,
|
|
365
|
+
是推理错误,撤回。
|
|
366
|
+
|
|
367
|
+
更要紧的是下面这条:
|
|
368
|
+
|
|
369
|
+
### 这个语料根本不「隐藏」
|
|
370
|
+
|
|
371
|
+
第三轮控制臂 11/12,不是因为它猜得准,而是因为**规格就写在 `src/*.js` 的 docstring 里**
|
|
372
|
+
(「up to `tries` total attempts」「Money is compared to the cent」…)。第二轮造语料时
|
|
373
|
+
我把「测试移出仓库」当成了「规格不可得」,其实只移走了验收、没移走信息。
|
|
374
|
+
所谓「隐藏规格语料」名不副实,它测不出记忆的价值——第三轮两臂都在读同一份 docstring。
|
|
375
|
+
|
|
376
|
+
### 所以现在的诚实结论
|
|
377
|
+
|
|
378
|
+
1. **在这个语料上,记忆的净效应是「不伤害」**(+8.3pp,来自 1 对配对,方向为正但不构成有力证据)。
|
|
379
|
+
2. **+30pp 作废**,原因不是数字不好看,是环境同时污染了两臂,且污染方向不明。
|
|
380
|
+
3. **要测「不可推导信息」,语料必须真的不含规格**:没有 docstring、没有测试、没有示例,
|
|
381
|
+
只有一个人为约定(任意常量/任意标识/任意语义二选一)。`docs` 第 7 节列的类型
|
|
382
|
+
依然成立——只是这次得真的照做:把正确约定从 `src/` 里彻底删掉。
|
|
383
|
+
|
|
384
|
+
### 顺带:`sandbox-exec` 这套值得留着
|
|
385
|
+
|
|
386
|
+
`--sandbox` 现在是 harness 的开关(`eval/g6/sandbox-profile.mjs`),三轮下来的隔离演进
|
|
387
|
+
全在这条线上:改名 → 搬出 DSH_HOME → 控制组先跑 → 日志出树 → **内核级读黑名单**。
|
|
388
|
+
前四步都是「让 agent 找不到」,最后一步是「让 agent 看到 EPERM」。只有最后一步
|
|
389
|
+
能对实验结论负责。
|
|
390
|
+
|
|
391
|
+
## 11. 第四轮:语料里真的没有规格(2026-10-27 凌晨)
|
|
392
|
+
|
|
393
|
+
第三轮说明第二轮的语料不合格,因为规格写在 `src/*.js` 的 docstring 里。第四轮换了做法:
|
|
394
|
+
**规格物理上不在这台机器上**——除了一处「团队约定」之外,仓库里没有任何地方写着答案。
|
|
395
|
+
|
|
396
|
+
### 语料 `eval/fixtures/ledger-kata-blind`
|
|
397
|
+
|
|
398
|
+
4 个函数、4 条纯约定,`src/` 里只有抛 `not implemented` 的空壳,没有 docstring、
|
|
399
|
+
没有测试、没有 README、没有示例(`test/` 目录在跑批前不存在):
|
|
400
|
+
|
|
401
|
+
| 任务 | 约定(只在种子里) | 为什么猜不出来 |
|
|
402
|
+
|---|---|---|
|
|
403
|
+
| `codec` | 校验字符 = `charset[Σ(每位数字×3+7) mod 36]`,`charset` 是 36 位小写字母数字表 | 任意加权、任意基数、任意字母表 |
|
|
404
|
+
| `errcode` | `E_<CODE>: <msg>` 前缀、`.code` 去前缀、`.details` 默认 `{}`、坏码要拒 | 前缀与字段名均任意 |
|
|
405
|
+
| `money` | 分位「四舍五入远离零」、累加走整数分 | 与 JS 默认 `Math.round` 行为不同 |
|
|
406
|
+
| `ordering` | 降序、稳定、返回新数组 | 方向二选一(可猜,这是本轮的漏洞) |
|
|
407
|
+
|
|
408
|
+
四条约定先写进 `seed-memory-blind.json`(外加一条无关的干扰记忆),
|
|
409
|
+
oracle 在 `eval/g6/oracle-blind/`,用 `check.mjs … --dir oracle-blind` 在**跑完之后**才拷进工作区。
|
|
410
|
+
|
|
411
|
+
### 结果:12 对,Δ = +58.3pp
|
|
412
|
+
|
|
413
|
+
| arm | pass | 耗时 |
|
|
414
|
+
|---|---|---|
|
|
415
|
+
| ab-engram | **12/12 (100%)** | 131.8s |
|
|
416
|
+
| ab-bare | **5/12 (41.7%)** | 208.1s |
|
|
417
|
+
|
|
418
|
+
逐任务(控制臂):
|
|
419
|
+
|
|
420
|
+
```
|
|
421
|
+
codec 0/3 errcode 2/3 money 0/3 ordering 3/3
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
控制臂的 5 次通过里 3 次来自 `ordering`——降序是二选一,猜得中;剩下 2 次是
|
|
425
|
+
`errcode` 里前缀蒙对。**在三条真正不可推导的约定上(codec/errcode/money),
|
|
426
|
+
控制臂 2/9,带记忆 9/9。** 这是四轮里第一次出现「不查资料就根本做不到」的差距。
|
|
427
|
+
|
|
428
|
+
### 又抓到一个泄漏,方向反而对结论有利
|
|
429
|
+
|
|
430
|
+
第一次跑完发现两个控制 run 引用了 `/private/tmp/g6z/r4/.../test/codec.test.mjs` ——
|
|
431
|
+
那是**上一轮被我杀掉的跑批**留下的 oracle 副本(当时 `check.mjs` 还没删 oracle)。
|
|
432
|
+
两个 bug 叠在一起:
|
|
433
|
+
|
|
434
|
+
1. seatbelt 里写的是 `/tmp/g6z`,但 macOS 在匹配前把 `/tmp` 解析成 `/private/tmp`,
|
|
435
|
+
**deny 没生效**。现在每个条目都同时发原文和 realpath 两种写法。
|
|
436
|
+
2. 判完不删 oracle,副本会活到下一次 run 能看见。
|
|
437
|
+
|
|
438
|
+
修完之后,把 codec/errcode 的控制臂重新跑了 6 次(干净树、无泄漏引用),
|
|
439
|
+
`codec 1/3 → 0/3`,`errcode 1/3 → 2/3`,合计仍是 5/12 —— **Δ 58.3pp 不变,
|
|
440
|
+
而且这次两个方向的错误都修掉了**(泄漏只会帮控制臂,所以它是个偏保守的偏差)。
|
|
441
|
+
|
|
442
|
+
### 关于这组数字能说什么
|
|
443
|
+
|
|
444
|
+
- 能说:**当规格不可从仓库推导时,种下的记忆让 4 个任务从 41.7% 变成 100%**(12 对,4 任务 × 3 种子)。
|
|
445
|
+
- 不能说:这是「通用编程能力提升 58pp」。它测的是**一条信息通道**——把约定放进记忆,
|
|
446
|
+
能不能被 agent 用上。答案在这一轮是明确的「能」。
|
|
447
|
+
- 边界仍在:单模型(`deepseek-v4-flash` 本地部署)、4 个任务、每个任务 3 种子、
|
|
448
|
+
一条臂只种 5 条记忆。样本小,方向可靠,量级别当真。
|
|
449
|
+
|
|
450
|
+
### 命中率与耗时一起看
|
|
451
|
+
|
|
452
|
+
带记忆 131.8s vs 不带 208.1s:**知道约定不只是更快通过,还少走弯路**——控制臂在
|
|
453
|
+
`money` 上平均 234s(三次全挂),因为它不知道「远离零」还是「四舍六入五成双」,
|
|
454
|
+
只能反复试。这条比通过率更难伪造成:时间是模型的真实成本。
|
|
455
|
+
|
|
456
|
+
## 12. 第五轮:把隔离做成结构性事实(2026-10-27 午)
|
|
457
|
+
|
|
458
|
+
第四轮之后还剩两个洞:`ordering` 是二选一(猜得中),隔离是**黑名单**(列得出所有能读的地方,
|
|
459
|
+
就列得出漏掉的)。第五轮两个都补了。
|
|
460
|
+
|
|
461
|
+
### 语料:`ordering` → `ranking`
|
|
462
|
+
|
|
463
|
+
排序口径改成三条叠加:`score` 升序、同分给**更长**的 `id`、全平则**倒序**输入顺序。
|
|
464
|
+
三选一叠起来没有任何可猜之处,第四轮控制臂 3/3 靠方向蒙对的漏洞消失(本轮 0/5)。
|
|
465
|
+
|
|
466
|
+
### 围栏:从「禁止列出」换成「默认收禁」
|
|
467
|
+
|
|
468
|
+
黑名单这条路在这个仓库里输了三次:仓库自己泄漏、`/tmp` 没做 realpath 解析、以及本轮
|
|
469
|
+
抓到的两次(下面写)。改成结构性的收禁(`eval/g6/sandbox-profile.mjs` 的
|
|
470
|
+
`buildConfinedProfile`):
|
|
471
|
+
|
|
472
|
+
```
|
|
473
|
+
(allow default) ; 进程、网络照旧
|
|
474
|
+
(deny file-read* (subpath "/private/tmp") (subpath "/tmp") (subpath "<$HOME>"))
|
|
475
|
+
(allow file-read* …) ; 只放回:ws、node、实验用的 DSH_HOME
|
|
476
|
+
(deny file-read* <另一臂的 profile>) ; 再挖掉唯一真正算答案的那块
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
三个必须记住的实现细节,都是踩出来的:
|
|
480
|
+
|
|
481
|
+
1. **`(deny file-read*)` 全局写会直接被拒**——`sandbox-exec` 起不来(Abort trap 6)。
|
|
482
|
+
所以收禁只能写成「按根 deny + 窄 allow」。
|
|
483
|
+
2. **规则重叠时最后一条匹配的赢**。这是这套写法能成立的全部原因:先 deny 整个 `/tmp`,
|
|
484
|
+
再 allow 自己的 ws,才可能在"其余 /tmp 全黑"的同时让 run 正常工作。
|
|
485
|
+
3. **`getcwd(2)` 和 `realpath(3)` 要逐级祖先的读权限**,否则 node 在启动时就
|
|
486
|
+
`uv_cwd EPERM` / `realpath EPERM` 死掉。祖先用 `literal` 逐个放(不是 `subpath`,
|
|
487
|
+
否则等于放开整棵树)。同理 `/tmp` 和 `/private/tmp` 两种写法都要发。
|
|
488
|
+
|
|
489
|
+
另外 run 的 `TMPDIR` 指到 ws 里——`/tmp` 已经黑了,不给它一个能写的临时目录,
|
|
490
|
+
失败原因就会从"猜不出约定"变成"写不了临时文件"。
|
|
491
|
+
|
|
492
|
+
### 本轮自己抓住的两次泄漏(都发生在跑批中途)
|
|
493
|
+
|
|
494
|
+
1. **自己的日志目录就是答案键。** `--log-dir` 默认放在本轮 out 下,而围栏对"本轮自己的
|
|
495
|
+
目录"是豁免的——于是后跑的 run 能读到先跑的 `checks/*.log`,那里面有
|
|
496
|
+
`expected 101, actual 100`。现在 harness 直接拒绝这种配置:
|
|
497
|
+
```
|
|
498
|
+
Error: --log-dir … is readable inside the sandbox; put it under a /tmp/g6* directory that is NOT this round's root
|
|
499
|
+
```
|
|
500
|
+
2. **`/tmp/verify-ranking`。** 一个更早的 run 把验证脚本写进了 `/tmp`,控制臂
|
|
501
|
+
`grep -rl 'rankRows' /private/tmp` 找到它,从文件注释里读出了排序口径。
|
|
502
|
+
这不是"能力",是**通道**。收禁整个 `/tmp` 之后这条路消失。
|
|
503
|
+
|
|
504
|
+
两次泄漏都发生在"以为已经隔离好了"之后,这就是为什么最后选择收禁而不是继续补黑名单。
|
|
505
|
+
|
|
506
|
+
### 结果:20 对配对,Δ = +90.0pp
|
|
507
|
+
|
|
508
|
+
| arm | pass | 平均耗时 |
|
|
509
|
+
|---|---|---|
|
|
510
|
+
| ab-engram | **20/20 (100%)** | 147.7s |
|
|
511
|
+
| ab-bare | **2/20 (10%)** | 219.7s |
|
|
512
|
+
|
|
513
|
+
逐任务(控制臂):`codec 0/5` · `errcode 0/5` · `money 2/5` · `ranking 0/5`。
|
|
514
|
+
|
|
515
|
+
**那 2 次通过全在 `money`**,且不是运气:分位「四舍五入远离零」对一个认真写浮点的
|
|
516
|
+
实现者是可推导的(`Math.round` 的边界处理错了会自己发现)。所以 `money` 不算
|
|
517
|
+
"不可猜"的任务,结论要把它排除在外算——**另外三条真·不可推导的约定上(codec/errcode/ranking),
|
|
518
|
+
控制臂 0/15,带记忆 15/15。**
|
|
519
|
+
|
|
520
|
+
耗时同向:带记忆 147.7s,不带 219.7s。
|
|
521
|
+
|
|
522
|
+
artifact:`eval/g6/runs/r5-confined/runs.json`(40 行,含每行 `pass/exit/wallMs`)。
|
|
523
|
+
|
|
524
|
+
### 还剩一处泄漏:臂名在路径里(已修,未复跑)
|
|
525
|
+
|
|
526
|
+
工作区路径一直是 `<out>/ab-bare/…`,agent 在自己的 cwd 里就能看到"我是没有记忆的那一臂"。
|
|
527
|
+
20 个控制 run 里有 8 个提到并据此推理("bare 臂没有 engram,去翻 team memory"),
|
|
528
|
+
但它们翻不到任何东西——围栏挡住了 store、另一臂的 profile、以及所有会话日志。
|
|
529
|
+
|
|
530
|
+
方向判断:这个泄漏只会让控制臂**更努力**去找约定(找不到),不会让它更容易通过,
|
|
531
|
+
所以 Δ 是偏保守的。代码里已改成中性目录名(`arm-a`/`arm-b`,映射记在 `runs.json` 的
|
|
532
|
+
`armLabels`),**但没有复跑**——这一轮的数字就按上面读,不要当成"零泄漏"的终值。
|
|
533
|
+
|
|
534
|
+
### 这组数字能说什么
|
|
535
|
+
|
|
536
|
+
- 能说:**规格不可从仓库推导时,种下的记忆把 4 个任务从 10% 抬到 100%**(20 对,4 任务 × 5 种子),
|
|
537
|
+
且在三条任意约定上是 15/15 vs 0/15。
|
|
538
|
+
- 不能说:通用编程能力提升 90pp。测的仍是一条**信息通道**。
|
|
539
|
+
- 边界:单模型(本地 `deepseek-v4-flash`)、4 任务、5 种子、一臂一组种子记忆。
|
|
540
|
+
Δ 的量级受任务选择影响极大,方向可靠,幅度别外推。
|
package/docs/GATES.zh.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# 门审计(gate audit)· 2026-10-26
|
|
2
|
+
|
|
3
|
+
窗口:最近 14 天 · 扫描 46 个会话(其中 35 个挂载了工具)· 账本行 7 条
|
|
4
|
+
|
|
5
|
+
| 门 | 指标 | 实测 | 目标 | 判定 | 备注 |
|
|
6
|
+
|---|---|---|---|---|---|
|
|
7
|
+
| G1 | NetTokens · 净收益 > 0 的会话占比(有节省机会的会话) | 100.0% | ≥ 50% | **未测** | 3/3 opportunity rows of 7 — need ≥ 5 for a distribution |
|
|
8
|
+
| G1b | NetTokens · 长会话(≥200 请求)占比 | 100.0% | ≥ 70% | **未测** | 2/2 long opportunity sessions |
|
|
9
|
+
| G2 | Activation · 机制激活率(挂载会话 fire ≥1 次) | 77.1% | ≥ 60% | **PASS** | 27/35 mounted sessions |
|
|
10
|
+
| G2b | Push · [RECALL] 注入命中会话占比 | 20.0% | 观测项(无预声明门) | **—** | 7/35 sessions carry a [RECALL] block in their prompt |
|
|
11
|
+
| G3 | DrillRate · detail / recall-with-hits | 0.07 | 观测项(推送是否被采纳) | **—** | 3 detail / 45 recall-with-hits |
|
|
12
|
+
| G4 | StaleRate · 被 supersede 的行占比 | 0.0% | < 1% | **PASS** | 0/406 memories (13 tagged stale) |
|
|
13
|
+
| G5 | 索引 ≤ 700 chars | 681c | ≤ 700c | **PASS** | /Users/sunchuxiong/dsh-loom · 119 memories · 6 lines |
|
|
14
|
+
| G6 | Quality Δ · 配对任务成功率差 | 90.0pp | Δ ≥ −2pp | **PASS** | 20 对配对 · 4 任务 × 5 种子 · eval/g6/runs/r5-confined/runs.json(臂名泄漏已修未复跑,见 §12) |
|
|
15
|
+
| G7 | 账本自身开销 | 0.0144% | < 0.5% 请求时延 | **PASS** | from `npm run bench:ledger` (worst op vs a 300ms request floor) |
|
|
16
|
+
|
|
17
|
+
## 账本合计
|
|
18
|
+
|
|
19
|
+
- injected 11,589,492c(≈2,897,373 tok)· avoided 1,909,142,538c(≈477,285,635 tok)
|
|
20
|
+
- rewrites 17 次 / 4,024,426c(按 cacheDiscount 计罚 3,621,983c)
|
|
21
|
+
- **net 1,893,931,063c ≈ 473,482,766 tok**;开源会话 7 条,其中净收益为正 3 条
|
|
22
|
+
|
|
23
|
+
## 最近会话(按会话落行)
|
|
24
|
+
|
|
25
|
+
| session | requests | injected | avoided | net | workspace |
|
|
26
|
+
|---|---|---|---|---|---|
|
|
27
|
+
| session-746c03… | 1,084 | 2,168,304c | 310,174,822c | 307,721,654c | dsh-loom |
|
|
28
|
+
| session-419cfc… | 320 | 933,735c | 143,007,626c | 142,073,891c | kototoro-parsers |
|
|
29
|
+
| 91e9c39a-2231-… | 66 | 179,382c | 286,224c | 106,842c | kototoro-parsers |
|
|
30
|
+
| session-775f51… | 220 | 609,289c | 0c | -1,711,542c | kototoro-parsers |
|
|
31
|
+
| session-71f597… | 140 | 392,295c | 0c | -392,295c | kototoro-parsers |
|
|
32
|
+
| session-ffbe89… | 120 | 361,254c | 0c | -361,254c | Kototoro |
|
|
33
|
+
| session-ed8929… | 260 | 775,040c | 0c | -775,040c | Kototoro |
|
|
34
|
+
|
|
35
|
+
> 口径:token = chars/4(与 GUI 一致);rewrites 罚金 = 前缀字符 ×(1 − cacheDiscount),默认 0.1。
|
|
36
|
+
> 未测即未测——不猜。G5 需要运行中的宿主(`--api`),G7 需要 `npm run bench:ledger` + `--g7 <pct>`,
|
|
37
|
+
> G6 需要配对实验:`npm run quality:ab -- run …`(协议见 docs/G6-quality-ab.zh.md,< 10 对配对一律报未测)。
|
|
38
|
+
> G1 的分母是「有节省机会」的会话(`prunedChars + compactedChars > 0`):没有任何剪枝/压缩的会话结构上必为负,
|
|
39
|
+
> 把它们算进分母测的是激活率而不是收益。机会会话 < 5 时只报数值、不给判定:一个会话不是分布。
|
|
40
|
+
> 绝对值说明:injected 按名义价逐请求记,而宿主前缀缓存实际约 10% 付费;
|
|
41
|
+
> 两侧同比例缩放不改变 net 的**正负号**(G1 判的就是符号),所以门不受影响,但「节省 token」的绝对量偏悲观。
|
|
@@ -344,6 +344,12 @@ net : avoided − injected − rewrites×(1−cacheDiscount)
|
|
|
344
344
|
`test/ledger.test.mjs` 12 例、`/stats` 与 `/overview` 的 `ledger` 字段、
|
|
345
345
|
`ledgerEnabled` / `ledgerCacheDiscount` 两个设置键、GUI 遥测页账本面板 +
|
|
346
346
|
概览「上下文净 token」卡;`npm test` 381/381。
|
|
347
|
+
**v0.7.5 补齐**:`ledger.json` 增加 `sessions`(每会话一行:requests / injected /
|
|
348
|
+
avoided / rewrites / pruned / compacted / updatedAt,`maxSessionRows` 默认 500 裁剪),
|
|
349
|
+
`summary()` 合并未 flush 的活跃会话;§6 的门因此第一次有了分母。
|
|
350
|
+
同版本新增 **`eval/gate-audit.mjs`**(`npm run gate:audit`)把 §6 的门算成
|
|
351
|
+
`PASS` / `FAIL` / `未测` 表格([docs/GATES.zh.md](GATES.zh.md)),
|
|
352
|
+
并修掉 v4 会话日志里 `tool/result` 形状变化导致的 failures / recall 命中恒为 0。
|
|
347
353
|
- 反事实基线:同一会话关闭机制时的 avoided 由 oracle 离线算,账本只报「实际发生」。
|
|
348
354
|
|
|
349
355
|
**B. 自适应默认(`boundaryPrune: "auto"`)**
|
|
@@ -569,6 +575,7 @@ adapters DSH seams (storageDomain, tokenMeter, pruner, compaction, skill)
|
|
|
569
575
|
| 指标 | 定义 | 用途 |
|
|
570
576
|
|---|---|---|
|
|
571
577
|
| NetTokens | avoided − injected − rewrites×cacheFactor | 唯一主指标 |
|
|
578
|
+
| NetTokens 口径(2026-10-26 修订) | 分母只算**有节省机会**的会话(`prunedChars + compactedChars > 0`)。没有剪枝/压缩的会话结构上必为负(只有注入支出、没有节省入账),把它们算进分母测的是「机制有没有跑」,不是「省没省到」。本机 14 天:33 个挂载会话中 21 个有节省机会 | 防止 G1 被结构性负值拖死 |
|
|
572
579
|
| Activation | fire 过的会话 / 挂载会话 | 机制是否真的跑 |
|
|
573
580
|
| DrillRate | detail / recall-with-hits | 推送是否被采纳 |
|
|
574
581
|
| Quality Δ | 配对任务成功率差 | 质量红线 |
|
|
@@ -587,6 +594,11 @@ adapters DSH seams (storageDomain, tokenMeter, pruner, compaction, skill)
|
|
|
587
594
|
6. 账本必须扣缓存重写;只报 avoided 的账本是营销,不是工程。
|
|
588
595
|
7. LOCOMO 只作参考:规模小、易过拟合,不把它当发布门槛。
|
|
589
596
|
|
|
597
|
+
**可计算性(v0.7.5 起):** 上表的门不再靠回忆——`npm run gate:audit` 从
|
|
598
|
+
`ledger.json`(每会话净收益)、会话日志(激活率 / `[RECALL]` 注入 / DrillRate)、
|
|
599
|
+
store(StaleRate)与运行中宿主的 `/preview`(索引字符数)算出 `PASS` / `FAIL` /
|
|
600
|
+
`未测` 三态表,结果落 [docs/GATES.zh.md](GATES.zh.md)。没有数据就写「未测」,不猜。
|
|
601
|
+
|
|
590
602
|
**对照口径(对外可复现):**
|
|
591
603
|
|
|
592
604
|
| 对照 | 目的 |
|