feat(memory-b): H 收尾 W1–W8 —— 信号转发、写侧校验、建中心入口
五条全部处理。第 5 条确认属实:saveSpaceCentroid 在 lib/ 里零调用点,
真实使用下中心表永久空着,票③ 的「dim unknown」会从边角变成常态。
W1/W5 信号转发(最硬的一条)
- execute(args, exec) → commit(input, {signal}) → embedText(text, signal)
- abort 时按「向量化失败」降级落盘:块写入、vector 留空
- 依据 dsh-tools/lib/types/index.d.ts:120-122
★ 一处我判错了,据实记录
W5 我原本断言「abort 时 isError === false」,实测是 true。查了实现:
dsh-tools/lib/index.js:3408 在工具成功后**再检查一次 signal**,
已 abort 就用 toolAbortedResult 覆盖成功结果。所以 isError 不归本插件控制,
断言它等于和 harness 对抗。改为断言**副作用**(块已写入、vector 为空),
并额外断言 error.info.code === 'ABORTED'。
W2 九个调用点补 await
scripts/checkpoints.js ×4、test/plugin.test.js ×5、test/loader.test.js ×2
(loader 两处保持并发语义用 Promise.all)。
原本绿是因为「无 embedder 时第一个 await 之前就写完 putBlock」——
这个隐式事实一旦被一行 await 打破,九处会静默变成「块没写」,
而且红会出现在离原因很远的地方(靠后面能否召回间接断言)。
W3 空指纹先判后嵌
原来顺序是 await embedText → putBlock 抛 → catch → 再 putBlock,
语义终点对但白付一次网络往返,且与 recall.js:430 的口径不一致。
改为 embed 之前判 space.fingerprint === ''。
W4 写侧维度校验
commit 现在检查 vector.length === space.dimension,不符按 embed failure 处理
(无向量落盘)。不落坏向量的理由:召回侧会按 skippedDim 静默跳过,
用户看到的是「记忆里有这条,但永远召不回来」。
W8 建中心入口(第 5 条)
新增 store.rebuildCentroid(fingerprint),移植 RebuildCentroid
(centroid_store.go:246-320):
· 只取该空间的带向量行
· 维度不符跳过不强制
· 按 L2 归一化后累加均值
· 均值范数 >= 0.6 才判 anomalous(centroid.go:87)
· 计入 stats:meanLength / avgPairCosine / skippedRows
并新增 MEAN_THRESHOLD 常量。
★ W8 第一版判据是错的:我用完全共线的向量(meanLength = 1.0000)
去测 0.6 阈值,把阈值改成 0.95 仍然通过。已改用 ±35° 锥
(meanLength ≈ 0.879,落在 0.6 与 0.95 之间),并加断言
「语料必须落在两阈值之间」,否则测试自身就失去区分力。
判据与变异(全部在 /tmp 干净副本上跑)
W4 维度不符 → 块在、vector=''、fingerprint='' | 删维度检查 → 红
W5 abort → 块在、vector=''、elapsed<2s | 不传 signal → 红(实测 5003ms)
W6 无 embedder 时 {id,created} 与 putBlock 同形 | —
W7 空指纹 → embedText 调用 0 次 | 改为事后判 → 红
W8 同空间重建、跨空间隔离、坏维度跳过、阈值判定 | 去空间过滤 → 3 条红
| 去维度过滤 → 1 条红
| 阈值 0.6→0.95 → 1 条红
未越界
- 四态计数(noVector/unknownSpace/spaceMismatch/embedFailure)留给 F/D
- recallBlocks 仍然同步、未接入 vectorScores(V 组的事)
- 票③「dim 来自中心元数据而非配置」继续有效;saveSpaceCentroid 仍在用
dim ?? space.dimension,已在注释里标记为 C 组要区分「没建过」与
「建过但维度取自配置」——声明不是证据
顺序按你的调整记下:H(完工) → V → C → F → D。
C 组准入条件已满足一半:机制(rebuildCentroid)有了,
「谁触发建中心」待 C 组定。
验证
- npm test 99/99(90 + W4/W5/W6/W7 六条 + W8 三条)
- npm run check 10/10
- 提交前连跑 3 次 test + 3 次 check,6 次全绿
This commit is contained in:
@ -220,7 +220,7 @@ await checkpoint('V3', 'systemPrompt.context: sees agent, cannot see this step\u
|
||||
await checkpoint('V3b', 'agent/pre-step: injected recall lands in the accepted message batch', async () => {
|
||||
const app = makeApp()
|
||||
const mem = applyMemory(app.ctx, { path: join(workspace, 'v3b.db'), topK: 3 })
|
||||
mem.commit({ text: '值班室分机号 4324,值班人 老周', source: 'seed' })
|
||||
await mem.commit({ text: '值班室分机号 4324,值班人 老周', source: 'seed' })
|
||||
|
||||
const input = createUserMessage({
|
||||
content: [{ type: 'text', text: '值班室分机号是多少' }],
|
||||
@ -247,7 +247,7 @@ await checkpoint('V3b', 'agent/pre-step: injected recall lands in the accepted m
|
||||
await checkpoint('V3b†', 'MUTATION: silencing recall must break V3b (the test can turn red)', async () => {
|
||||
const app = makeApp()
|
||||
const mem = applyMemory(app.ctx, { path: join(workspace, 'v3b-mut.db'), topK: 3 })
|
||||
mem.commit({ text: '值班室分机号 4324,值班人 老周', source: 'seed' })
|
||||
await mem.commit({ text: '值班室分机号 4324,值班人 老周', source: 'seed' })
|
||||
// Mutate: no recall, ever.
|
||||
mem.contextForStep = () => ''
|
||||
const input = createUserMessage({
|
||||
@ -271,7 +271,7 @@ await checkpoint('V3b†', 'MUTATION: silencing recall must break V3b (the test
|
||||
await checkpoint('V3c', 'realistic 3-part batch: recall uses the question, not the injected context', async () => {
|
||||
const app = makeApp()
|
||||
const mem = applyMemory(app.ctx, { path: join(workspace, 'v3c.db'), topK: 3 })
|
||||
mem.commit({ text: '值班室分机号 4324,值班人 老周', source: 'seed' })
|
||||
await mem.commit({ text: '值班室分机号 4324,值班人 老周', source: 'seed' })
|
||||
|
||||
const batch = [
|
||||
{ role: 'user', source: { kind: 'user' },
|
||||
@ -329,7 +329,7 @@ await checkpoint('V5', 'unload disposes the sqlite handle; a second close is a n
|
||||
const path = join(workspace, 'v5.db')
|
||||
const app = makeApp()
|
||||
const mem = applyMemory(app.ctx, { path })
|
||||
mem.commit({ text: 'V5 句柄释放块', source: 'v5' })
|
||||
await mem.commit({ text: 'V5 句柄释放块', source: 'v5' })
|
||||
assert.ok(mem.store.isOpen, 'store open while plugin loaded')
|
||||
|
||||
// Disposing the plugin fiber must run our ctx.effect teardown.
|
||||
|
||||
Reference in New Issue
Block a user