Overview
@marquez807/dsh-experience-memory
README / EN
Package documentation
Registry summary
dsh.pub verifies the pinned bundle contract, runtime facts, and distribution semantics. The complete README remains in the source repository.
Read the full README on GitHubLIMITATIONS
Known limitations
> 这一节用英文标题是为了让锚点稳定(测试按标题逐字定位其中的数字)。 - **"反复犯的错"只被统计,不会被自动写成经验。** 这是量过之后的选择,不是省略:七天里本机 63 个会话 产生 358 次工具失败,最常见的一类(改文件前没读,143 次 / 5 个会话)**错误信息里就写着怎么做** ("read the file, then retry"),前两类合计占 178 次——记忆在那类失败上加不进任何信息,重复是手滑 而不是不知道,而且 harness 的编辑工具本身就是那个守卫。`failure-recovered` 这条判据本仓库**标定过 一次并判为噪音**(71 命中 → 5 条算数),这次的数据是**支持**那次判断,不是推翻它。所以这一版只做 两件不冒险的事:把失败按形状记下来(不注入、不写记录),以及**把我们自己的报错写成能照做的** (`domain` 那条错误进过 Top-10,10 次 / 3 个会话)。判据与数字见 CHANGELOG。 - **`/memory-gaps` 的"相关"是关键词重合度,不是语义覆盖。** 错误原文是英文、记录多半是中文,中文记录 可能一条都对不上,所以那个分数**只会偏低**,报告里也这么写。它的用途是让人看见"这件事一直在发生", 不是给出"该记一条"的结论。 - **`/memory-gaps` 会指出"哪条记录写了却没挡住"。** 判定要同时满足三条:关键词**全中**(且至少两个词, 一个词的重合是巧合)、记录比这些重复**早**(一小时宽限,不然新写的记录会被下一次手滑冤枉)、写完 之后**又犯了至少 3 次**。测得的例子:那条"本机抓不了网页"的经验写于 21:05,之前 `web_fetch` 三种 失败每小时 0.54/0.34/0.14 次,之后 0.00/0.12/0.00——**这是"经验挡住了错误"目前唯一的硬证据**。 要复算随时可以跑 `audit/verify-prevention-before-after.mjs`。 - **`/memory-gaps` 里有些行不是错误。** 用户打断计划评审、工具被中止、用户取消等待,都会被记成"失败" 形状——它们是**用户的动作**,不是 agent 的判断失误。这一版刻意不过滤:过滤要靠一张"这不算错"的字面 清单,而本仓库在这类清单上翻过车(一个词之差就绕过去)。代价是报告前几行可能混着这类行;缓解方式是 **每一行都带原始报错**,读者一眼能认出来。实测数据支持这个取舍:重启后 19 次失败里有 3 次是这一类。 - **计数只在"回合结束"时读最近一个回合**,实测边界(重启后 19 次 vs 逐回合重数 19 次,完全一致): 重启前就已经在跑的回合不会被记(那一版还没这个功能),从头到尾没停过的会话也不会被记。 一致性检查脚本是 `audit/diagnose-counter-gap.mjs`,随时可以照原样重跑复核。 - **动手前提醒:已经做了,但用的是"记录自己声明适用哪次调用",不是"照着 `trigger` 字段猜"。** 早先推迟这条是因为那条路实测不可靠:拿"即将调用的工具名出现在某条记录的 `trigger` 里"当触发条件, 13,198 次调用里会触发 949 次(7.2%),最大触发源是 `grep`(623 次触发只对应 3 次失败——"grep 断言" 这种句子被误当成触发器),而真正该触发的 `web_fetch` 反而被淹没。**原因不是调参**:分辨"这条讲的就是 用这个工具"和"顺带提到这个工具"需要语义判断,本插件不做模型调用;"两个词撞上"推不出"这条经验适用 于这次调用"。所以改成写记忆时用 `recall_for` **声明**(`path:` / `tool:` / `command:`),没声明就 不在动手前出现。预注册的四条判据是这么结的: | 判据 | 结果 | |---|---| | 触发率 ≤2% 的调用 | **1.18%**(同一份 15,383 次调用回放) | | 单条记录误触发 <300 | **83** 次(工作区里有三个 `tools.js`;按文件名锚会撞 508 次,改成相对路径后 83) | | 不增加每轮固定开销 | **没变**,204 字节照旧 | | 覆盖 ≥15% 的失败 | **撤掉**,理由见下面那条 | **代价与边界**:动手前这一层只对"用户说过、文件里查不到"的知识实证有效(见下文「它到底有没有用」); 库里**声明了锚点的永远是少数,而且这个数随库变化**(2026-09-25 在本工作区实测:可投递 234 条,自己声明锚点的 70 条,动手前静默的 107 条;在库所属工作区的根目录跑 `node dsh-experience-memory/tools/anchors.mjs` 可重测),其余动手前静默——多数讲的是"讨论某项目时"这类没有 文件可锚的事,得人工补 `tool:` / `command:` 锚点,**这一步没有自动化**——`tools/backfill-anchors.mjs` 只从记录的出处推断 `path:` 锚点,`tool:` / `command:` 仍然要人写。 - **"覆盖 ≥15% 的失败"这条判据已撤,换成它本来想表达的那句话**:"这条经验写下之后,同类事件还犯不犯?" 撤的理由是实测出来的,不是嫌麻烦:442 次工具失败里 **83% 是工具自己拒绝、并在报错里写着下一步怎么做** (`file has not been read` 一类占 49%),没有任何记忆能预防它;而分子需要一个语义判断 ("这条记录本该拦住这次失败吗"),本框架刻意不做模型调用——连词共现代理都会把"数据源独立性纪律" 算成"工具调用被中止"的相关记录,同一个病在上一层复发。继续追的唯一达标办法是把"改文件前先读"挂到 `edit` 上(占 28.3% 的调用,超触发预算 14 倍),那是作弊而不是覆盖。换成的新问题**是能答的**: `failure_shape` 按形状计数并保留发生时间、`delivery` 记下送过什么、`tools/prevention-ledger.mjs` 出四分类账。实测与撤除依据见 [`docs/DELIVERY-GAPS.md`](docs/DELIVERY-GAPS.md) 第五节 (含 2026-09-23 13:00 的撤除条)、第十五、二十节。 - **类型注解从不被检查。** 构建只做剥离,toolchain 里没有 `tsc`(零构建依赖是刻意的),所以类型不一致不会被任何一步 发现——错注解被原样删掉,运行期行为不受影响,连测试都不会惊动。类型在这里是给人读的文档,不是被验证的契约。 要加门禁就得引入 TypeScript 依赖,与"构建期零依赖"冲突;这是明知的取舍,现在明确写在这里。 - **相关性闸会让"只共享功能词"的相关匹配落空。** 常驻层要求命中标识符或共享一个实词,所以一句只含「这个/可以」这类词的 回话不会带出任何记录——即使某条记录确实相关。缓解手段是按需检索:`memory_recall` 不受这道闸约束。 - **标题比较折叠标点,所以同标题的不同主张可能被一起退役。** 这是刻意的弱把手换来的:动作是**退役而非删除**, `supersededBy` 与纠错日志都留痕,判断错了可以恢复。 - **那一行经验提示是每轮无条件付费的**:204 字节,即使这个工作区永远不记任何东西也照付。这是有意的取舍—— 把它做成"有记忆时才出现"会让它在库空时消失,而库空正是它要解决的问题。`RECORD_HINT` 的长度由测试钉了 256 字节上限;要彻底关掉它,删掉 `src/index.ts` 里的那次 `ctx.systemPrompt.context` 注册即可(它只贡献文本, 没有别的副作用)。 - **动手前把经验递到眼前,要求记录自己声明"适用哪次调用"**(`path:` 文件名 / `tool:` 工具名 / `command:` 命令里的词, 写记忆时填 `recall_for`)。**没声明的记录不会在动手前出现**,只进每轮摘要、只被 `memory_recall` 搜到。 这是一次实测后的取舍:旧做法靠"调用与记录撞上同一个词"来猜,在 15,383 次真实调用上对 57% 的调用都发了提示, 抽 47 条人工看只有 5 条真的相关(10.6%);把门槛调紧到能去掉噪声,召回又掉到个位数。判据为什么改、实测数字、 人工抽查与失败路径,见 [`docs/DELIVERY-GAPS.md`](docs/DELIVERY-GAPS.md);`node tools/anchors.mjs` 能看当前库的覆盖率。 - **没有语义/向量检索**。v1 只有 FTS5 + 标识符精确匹配 + 证据排序;`record.embedding` 列已预留,加入 RRF 融合时不需要迁移。 - **注入层是查询门控的,因此对话题漂移敏感**。查询取自最近两条用户消息,所以用户回一句「继续」时, 查询层会清空。核心层(跨工作区印证过的领域级经验)正是为这个缺口存在的,但它只覆盖被印证过的内容, 工
