全部插件

DSH / BUNDLE / BUNDLES

@marquez807/dsh-experience-memory

v0.5.0Marquez807 / dsh-experience-memory8e78d86b53

可安装组合包组合包与其他模块社区 · Topic 自动分析

概览

@marquez807/dsh-experience-memory

源码级技术说明跨会话长期经验记忆 for DeepSeek Harness — 证据定级 fail-closed:只有用户原话、工具实测或工作区文件引证过的经验才配自动注入,无从核实的只留候选、永不注入;工作区/领域双作用域,需两个工作区各自独立印证才升到领域级;每轮按话题门控注入相关经验,被检索到或被成功复用都加分,所以常用的留下、没人碰的自然退役;退役可逆,只有 purge 才真删;动手前提示要求记录自带 recall_for 锚点(path: / tool: / command:),没声明就不打断工具调用;维护在每轮结束时跑,不占检索热路径,还会标记出处文件已不存在的记录。纯 SQLite + FTS5,零构建依赖。· Cross-session experience memory: graded evidence, query-gated injection, just-in-time delivery keyed on declared anchors, reversible retirement.展开完整技术说明收起技术说明
跨会话长期经验记忆 for DeepSeek Harness — 证据定级 fail-closed:只有用户原话、工具实测或工作区文件引证过的经验才配自动注入,无从核实的只留候选、永不注入;工作区/领域双作用域,需两个工作区各自独立印证才升到领域级;每轮按话题门控注入相关经验,被检索到或被成功复用都加分,所以常用的留下、没人碰的自然退役;退役可逆,只有 purge 才真删;动手前提示要求记录自带 recall_for 锚点(path: / tool: / command:),没声明就不打断工具调用;维护在每轮结束时跑,不占检索热路径,还会标记出处文件已不存在的记录。纯 SQLite + FTS5,零构建依赖。· Cross-session experience memory: graded evidence, query-gated injection, just-in-time delivery keyed on declared anchors, reversible retirement.

README / ZH

插件文档

目录摘要

跨会话长期经验记忆 for DeepSeek Harness — 证据定级 fail-closed:只有用户原话、工具实测或工作区文件引证过的经验才配自动注入,无从核实的只留候选、永不注入;工作区/领域双作用域,需两个工作区各自独立印证才升到领域级;每轮按话题门控注入相关经验,被检索到或被成功复用都加分,所以常用的留下、没人碰的自然退役;退役可逆,只有 purge 才真删;动手前提示要求记录自带 recall_for 锚点(path: / tool: / command:),没声明就不打断工具调用;维护在每轮结束时跑,不占检索热路径,还会标记出处文件已不存在的记录。纯 SQLite + FTS5,零构建依赖。· Cross-session experience memory: graded evidence, query-gated injection, just-in-time delivery keyed on declared anchors, reversible retirement.

dsh.pub 核对固定版本的组合包契约、运行时事实与分发语义;完整 README 请查看源仓库。

在 GitHub 阅读完整 README

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 融合时不需要迁移。 - **注入层是查询门控的,因此对话题漂移敏感**。查询取自最近两条用户消息,所以用户回一句「继续」时, 查询层会清空。核心层(跨工作区印证过的领域级经验)正是为这个缺口存在的,但它只覆盖被印证过的内容, 工