Skip to content

fix: NoisePrototypeBank degeneracy guard — fix flaky smart-extractor-branches test#164

Open
AliceLJY wants to merge 1 commit intomasterfrom
fix/noise-bank-degenerate-embedding-guard
Open

fix: NoisePrototypeBank degeneracy guard — fix flaky smart-extractor-branches test#164
AliceLJY wants to merge 1 commit intomasterfrom
fix/noise-bank-degenerate-embedding-guard

Conversation

@AliceLJY
Copy link
Collaborator

@AliceLJY AliceLJY commented Mar 11, 2026

Summary

修复 master 上 smart-extractor-branches.mjs 的 flaky test。当 embedding 模型对所有文本返回相同向量时(如测试用的 deterministic mock),NoisePrototypeBank 会把所有内容误判为噪声,导致智能提取被跳过。

一、NoisePrototypeBank 退化检测

问题

  • NoisePrototypeBank.init() 嵌入 14 条噪声原型,用的是同一个 embedder
  • 测试的 mock embedding 对所有文本返回相同向量(void text
  • 所有原型向量 = 所有内容向量 → isNoise() 对一切输入返回 true(cos = 1.0 > 0.82)
  • 智能提取被跳过 → regex fallback 也无匹配 → merge/skip/multiRound 断言失败

改动

  • src/noise-prototypes.tsinit() 末尾加退化自检——比较前两个原型向量的余弦相似度,> 0.98 则禁用自身

回归测试

  • 无新增测试文件(修复的就是已有测试 test/smart-extractor-branches.mjs,9 个场景全部通过)
  • npm test 全量 10 个测试文件通过

与现有架构的协作关系

架构图

agent_end hook (index.ts,不变)
  ↓
filterNoiseByEmbedding (smart-extractor.ts,不变)
  ↓
NoisePrototypeBank.isNoise()  ← 🔧 本PR:init时加退化检测
  ↓                               如果退化 → _initialized=false → isNoise()直接返回false
extractCandidates → deduplicate → persist (不变)

不涉及 / 不修改的模块

  • index.ts — auto-capture 流程不变
  • smart-extractor.tsfilterNoiseByEmbedding 调用不变,仅 noiseBank 内部行为变化
  • extraction-prompts.ts — prompt 模板不变
  • ❌ 所有测试文件 — 零改动

兼容性评估

✅ 完全兼容

项目 说明
正常 embedding 模型 不同文本的余弦相似度远低于 0.98,退化检测不会触发
已有 noise bank 行为 isNoise() / learn() 接口和语义完全不变
配置 schema 无任何配置变更

❌ 不存在的不兼容

  • 不修改数据库 schema
  • 不修改 API 接口
  • 不修改配置格式
  • 不修改任何测试文件

设计决策

  • 为什么不改测试的 embedding 函数? 测试了 4 种文本相关 embedding 方案(bigram / char-seed / TF-weighted / 语言分离),数学上无法同时满足:(1) 中文内容 vs 中文噪声原型 cos < 0.82,(2) 中文种子 vs 中文候选 cos > 0.7。因为噪声原型包含中文句子("你还记得我喜欢什么吗"等),与测试内容共享大量汉字
  • 为什么不加配置开关? 退化自检是防御性修复,对正常 embedding 无感知,不需要用户介入
  • 为什么只比较前两个向量? 14 个原型包含英文和中文,正常模型下前两个("Do you remember what I told you?" vs "Can you recall my preferences?")的余弦不可能 > 0.98。如果这两个都一样,其余必然也一样
  • 阈值为什么是 0.98? 正常模型跨语言跨语义的 cos 远低于 0.9;退化 mock 的 cos = 1.0。0.98 有足够安全余量

Out of Scope

  • 改进 createDeterministicEmbedding 使其产生文本相关向量(会需要重写所有测试断言)
  • 新增 noise bank 配置开关(本修复使其不必要)
  • 扩展退化检测到多对向量采样(当前单对已足够,见设计决策)

统计

1 file changed, 17 insertions(+)

…ive noise filtering with non-discriminative embeddings

When the embedding model produces identical vectors for all inputs (e.g.
deterministic mock embeddings in tests), every text matches every noise
prototype with cosine similarity 1.0, causing the noise filter to reject
all content. Smart extraction is skipped entirely, falling back to regex
which also captures nothing.

Add a self-diagnostic after init(): if the first two prototype vectors
have cosine similarity > 0.98, the bank recognizes the embedding model
is degenerate and disables itself.

This fixes the flaky smart-extractor-branches.mjs test on master
(all 9 scenarios now pass reliably).
@github-actions github-actions bot requested a review from rwmjhb March 11, 2026 06:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants