Mneme — The Memory That Dreams

``` $ cat ~/.dsh/memory/preferences.md # 你的偏好,写在本机磁盘上,随时能改 ```

npm version npm downloads CI tests license Awesome

中文 · English(语义增强)

--- # dsh-mneme > 给 DeepSeek Harness 的跨会话记忆插件:让 Agent 记住你、记住项目、自动整理记忆。**Mneme**(Μνήμη)——希腊记忆女神 Mnemosyne 之名,掌管记忆与梦境,正如 autoDream 在后台巩固记忆。 `dsh-mneme` 是一个 [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness) 插件,为 Agent 提供持久的跨会话记忆能力。它借鉴了 Claude 的 **Dream 机制** 与 cc-haha / Claude Code 的 **autoDream** 记忆巩固功能——不仅**存储**记忆,还会**自动巩固**(去重、合并、冲突裁决、摘要生成),让记忆库越用越精炼。完整溯源与致谢见文末「[🙏 致谢](#-致谢)」。 ## ⏱️ 30 秒理解 **一句话**:给 Agent 装上跨会话记忆——记住你、记住项目,并在后台自动整理,越用越懂你;**v0.8.0 起**记忆可按 agent 与工作区隔离(`scopeEnabled`),多 Agent / 多项目互不串台;**v0.9.0 起**「这件事怎么跑」也能落进项目仓库(M.O.,`actionPipelineEnabled`,默认关)。 | ① 写入 | ② 存储 | ③ 进化 | |--------|--------|--------| | 对话中模型主动记录(`memory_save`,可带敏感度与事件时间);会话结束自动提炼(`autoSummarize`) | SQLite 主库 + 人类可编辑 Markdown 镜像;实体 / 属性 / 时间轴三层结构化;记忆带 agent / workspace / 敏感度 / 发生时间标注 | 新会话自动注入相关记忆(按当前作用域加权,strictScope 开启后显式收窄的归属硬隔离);autoDream 后台去重 / 合并 / 归档,冲突冻结待人工裁决 | ```bash # 30 秒上手 dsh plugin --profile web add @modusensus/dsh-mneme dsh web ``` **它不是什么**(边界声明): - 不是向量数据库——语义搜索是可选增强,默认零额外依赖 - 不替代会话日志——它存的是「值得跨会话记住的」精炼知识 - 不改变模型本身——进化的是记忆库与每次注入的上下文 - 删对话 ≠ 删记忆——记忆是跨会话的全局知识(v0.7.11 起不再与会话绑定),删除会话不影响已存入的记忆 ## ✨ 功能 ### 记忆存储(SQLite + Markdown 镜像) - **SQLite 主存储**:`~/.dsh/memory/memory.db`,`node:sqlite` 内置,零原生依赖 - **Markdown 镜像**:按类型分文件(`preferences.md` / `projects.md` / `decisions.md` / `history.md` / `summary.md` / `patterns.md` / `pitfalls.md` / `constraints.md` / `rejected-solutions.md`,外加只读视图 `documents.md`),文件头是 YAML frontmatter(`type` / `generated.by` / `generated.at` / `covered` / `coverage` / `tags`)。人类可读,条目标题与正文可手工编辑(**人工修改优先**合并回库);`documents.md` 例外:它只有 document 指针行(id、标题、摘要首句、文件路径),不含正文也不参与人工回填。文件头与条目元数据行由机器维护 - **10 种记忆类型**:`preference` 偏好 / `project` 项目 / `decision` 决策 / `history` 历史 / `summary` 会话总览 / `pattern` 模式 + 编码记忆三型 `rejected_solution` 被否方案 / `pitfall` 踩坑 / `constraint` 约束(v0.7.13 起,`codingRetrospect` 默认关;v0.7.11 近重写已收窄删除 user/fact 两型)+ `document` 文档指针(#230,只存摘要与 `doc_path`,正文留在磁盘由 agent 自管) - **镜像同步状态机(v0.3.6+)**:mirror 与主库强一致,用 `generation`(期望轮次)/ `applied_generation`(已应用轮次)建模同步债务 - 业务写操作在**自身事务内原子递增** desired generation——崩溃在 COMMIT 后、渲染前,重启也能凭 durable 债务恢复,绝不静默跳过(v0.3.8) - `generation` 用 SQLite 原子语句递增,多进程并发零丢失;带 `CHECK` 上界,负数/溢出拒绝 - 逐 type 记录 `committed / failed / pending` 回执,健康端点区分 `ok / degraded / unknown` - 状态写失败不静默:同步失败落日志并留债务,重启自动收敛 ### 模型工具(默认注册 12 个;开 `organizeEnabled` 后 13 个) | 工具 | 功能 | |------|------| | `memory_save` | 记录一条记忆(自动按标题去重合并;v0.8.0 起去重键含 agent/workspace/scope 作用域,可选 `sensitivity` / `occurred_at` 参数) | | `memory_search` | 全文搜索(中文子串友好,可启用向量语义搜索;可选 `occurred_from` / `occurred_to` 按事件发生时间过滤) | | `memory_list` | 按类型分页列出(`include_archived=true` 可查看已归档;可选 `occurred_from` / `occurred_to` 时间过滤) | | `memory_get` | 读取单条记忆完整正文(v0.7.25,按 id;memory_search / memory_list 命中后读全文) | | `memory_update` | 修改已有记忆 | | `memory_delete` | 删除记忆(按记忆 ID 精确删除) | | `memory_forget` | 抑制注入(降权不删除,可恢复) | | `memory_archive` | 归档/恢复记忆(v0.2.5;归档后隐藏于列表/搜索/注入/整理,`archived=false` 可恢复) | | `memory_pin` | 钉住一条记忆:无论类型、无论它和其他记忆怎么竞争,每轮都注入在记忆块最前面(#249 第二批;受 `pinnedInjectBudget` 上限约束,钉的顺序是「先钉的占位」,预算不够时最早钉的优先)。**只在她明确说「这条必须一直在」时用**,不是标记重要性的手段——重要性本来就参与排序。钉着 ≠ 会注入:遗忘 / 归档 / 暂停作用域都优先于 pin,回执里的 `injectable` 如实报告这件事。`unpin: true` 松开 | | `set_scope_state` | 按下/松开某个作用域的**可逆闸门**(#471:政策轴。暂停期间该作用域的记忆照旧存着,只是不注入、不参与巩固,松开即原样回来;不改任何一条记忆的标注,与上面逐条操作的 `memory_forget` / `memory_archive` 是两根不同的轴)。两级开关 `scopeState.enabled` / `.enforce` 默认都关,没开时按下不产生拦截效果 | | `memory_register_document` | 登记 agent 产长文档的指针行(#230:摘要 + `doc_path` + evidence 落 `document` 类型;正文留在磁盘、由 agent 自管,注册口唯一,旁路写入被拒) | | `memory_organize` | 主动整理(#231,**要 `organizeEnabled` 打开才注册**):`mode=dryRun` 先出比对报告(每条候选 `exact` / `near` / `new` + 命中行,**不写一行库**、只留审计回执),agent 判断后 `mode=apply` 引用那份报告的 `run_id` 落地。两种「不要它」要分清——**`discard`** = 这条候选不写,什么都不动(只进回执);**`archive`** = 把报告标为被取代的那条**既有行**归档(可恢复,绝不物理删除)。没有比对过的候选写不进库,一份报告也只许落地一次 | | `memory_runtime` | 本地推理运行时的状态 / 供给 / 校验(v0.7.32,自管运行时三档取件) | 开启 `chorographyEnabled` 后可用仓库地志工具。多个仓库已登记时请传仓库 ID。若搜索只读到部分页面,结果会标明漏查的页。 ### 工作流(M.O.,v0.9 beta;v0.13.0 起从面板下架) > **状态**:设置面板不再渲染这一组,界面上没有入口。三个旗标(`actionPipelineEnabled` / `actionRunEnabled` / `actionNudgeEnabled`)与 `/mo-run` 命令、座位注册逻辑都还在,语义零改动;想恢复界面把该组加回 `FEATURE_GROUPS` 与 `FEATURE_FIRST_SCREEN` 即可。下面是设计说明。 记忆库答的是「这件事**为什么**这么办」,答不了「这件事**到底怎么跑**」。v0.9 把后者从记忆库里的一行文字变成项目里可提交、可 review、可被 CI 读的一份文件——一条工作流一个 JSON: ``` <项目>/.mneme/pipelines/.json ← 项目层(默认;跟着项目提交) <插件包>/pipelines/.json ← 全局层(兜底;今天包里没有这个目录,有意为空) ``` - **两层来源,顺序即优先级**:同 id 两处都有,项目层赢,被挡住的那份进 `shadowed` 报出来(不静默丢弃);项目层的坏文件进 `unreadable` 而不拖死全局层——一层写坏不该让人连兜底那份也看不见 - **校验很硬**:`id` / `name` / `project` / `version` / `status` / `done` 与每一步的 `phase` / `title` / `when` / `do` / `done` / `why` 缺一不可,`risk` 限 R0–R3、`needs` 必须指向存在的步骤、图里有环直接报错——任何一处不合规**整条读不出来**,不做「跳过坏文件继续」:半个工作流比没有更危险 - **护栏,不是轨道**:绕行要留出口。自报 R0/R1 **且**不跨闸门 **且**兜底清单(`IRREVERSIBLE_PATTERNS`,机械匹配、不看自报)没命中 → 记账先走;其余 → 停下问人 - **候选分层**:草稿住 `/candidates/<项目键>/`,只在本地。升格要同时满足「走过两次(至少一次成功)」**且**「写得出反事实」;够格之后系统只**提议**,`promoteCandidate()` 只返回内容不写盘,提交与推送是人握着的那道门 - **两个指标分开数**:跳过(这次不用这一步,≥3 次提议删**这一步**)与绕开(这条路整个不对,≥3 次提议重看**这条路径**)各自累加,混在一起两种提议都发不出来 - **只读路由** `GET /api/dsh-mneme/pipeline[?id=&project=]`(`project` 非绝对路径回 400 `project-not-absolute`,不回退 `process.cwd()`);答复契约 `mneme.pipeline/v1`,面板渲染与 agent 读的是同一份数据 - 默认关(`actionPipelineEnabled`),设计取舍见 [docs/WORKFLOWS.md](docs/WORKFLOWS.md) ### 自动注入 + 会话摘要 - **自动注入**:新会话开局注入记忆摘要(`summary` 优先 + 少量高重要性条目) - **会话摘要**:`turn/end` 时用 LLM 提炼本次会话的偏好/决策/教训,自动入库(过滤 plugin 注入上下文,避免污染) ### autoDream 自动记忆整理 🧠 > **溯源**:autoDream 的理念源自 Claude Code 的 Auto Dream 记忆巩固功能(Anthropic,Memory 2.0 的后台整理子代理),其「离线巩固」思路与 UC Berkeley & Letta 的 *Sleep-time Compute* 论文([arXiv:2504.13171](https://arxiv.org/abs/2504.13171))一脉相承。dsh-mneme 将其引入 DSH 插件生态,并发展为自己的实现——K-Means++ 聚类预分组、类型化决策清单与可回放的 sha256 摘要审计链,详见文末「[🙏 致谢](#-致谢)」。 - **触发**:记忆数 > 10 或总字符 > 5000 时,异步自动触发(不阻塞写入) - **决策清单式整理**:LLM 输出 `keep` / `merge` / `archive` / `conflict` / `update` 决策清单,服务端校验后逐条应用 - `merge`:合并主题相近的条目,保留信息最完整者 - `archive`:归档过时/冗余条目(可恢复,不物理删除) - `conflict`:裁决矛盾信息,胜者保留、败者归档并追加溯源注释;`conflictFreezeEnabled`(默认关)开启后矛盾对不自动裁决,改为冻结进**冲突队列**(状态页并排对比,人工选保留方,#166) - `update`(v0.2.1):直接修正单条记忆的过时/错误内容(单 id / 必须实际变化 / 非 summary / 24h 保护 / 每次 ≤2) - **失败追踪(v0.2.1)**:用户纠正记忆时写入 `failure_memories` 表(旧值/新值),为后续自进化积累数据 - **摘要生成**:整理后生成"记忆库总览"(单一实例),作为下次会话的优先注入 - **Fail-safe**:非法 LLM 输出(未知 id / 非法 action / 越界 importance / 跨类型合并等"单条非法"决策,Issue #26)默认跳过该条并应用合法子集(run 记为 `degraded`),绝不破坏记忆库 - **裁决审计**:每次运行写入 `dream_runs` 审计表(输入快照 sha256 digest + 完整输入快照 + 决策清单 + 逐 id 去向 + receipt),可离线回放;merge / conflict / update 幂等应用,重放/并发重复执行无累积副作用;update 记录 `_before` 快照 #### dreamMaxTokens 调优指南 默认 `131072`(即上限,#135)已为思考型模型预留推理+正文的双重预算(部分思考型模型仅推理就可能消耗 8k+ token),常规记忆库无需调整。若**记忆量大**(数万字符以上)且正文仍被截断,优先按下方「巩固模型分类声明」换非思考模型 / 配 `dreamReasoningEffort` 压低思考,再考虑调大输入侧上限(如 `distillMaxChars`)——`dreamMaxTokens` 本身已无上调空间。 > **不要为了省 token 把它调小**(#135 成本审计实测):这个键是**上限**而非计费口径,流式按实际用量计费,压上限只在真撞顶时生效;而撞顶的后果是**整轮失败**(截断 → 无完整 JSON → `no json array in llm output`),且调度器只有 `ok` 轮才刷新基线,下一次写入会用同一批输入重跑。按 133 个有真实用量的成功轮折算,cap=16384 撞顶 78.9%、净总量 **+54.1%**;cap=32768 +15.9%。真正的大头(`dream_consolidate` 输出 token 里约 88% 是推理与 JSON 骨架,只有约 11.8% 是决策正文)由 `dreamReasoningEffort` 与模型选择决定,与本键无关。相关的数量护栏见 `dreamMaxMergePerRun`。 > 若使用**思考型模型**(如 deepseek-v4-flash / DeepSeek-R1 类),模型可能把全部预算花在 reasoning 上导致正文为空(日志出现 `no json array in llm output`)。#135 修复后:未配置 `dreamReasoningEffort` 时会**自动取模型支持的最低档**发流(不再省略字段让模型自带默认档——v4-flash 系默认 high——顶上烧光预算),默认配置即生效;显式 `none` = 不发送字段用服务商默认,`off`/`low`/`medium`/`high` 原样传递,模型不支持的档位自动换用(拒绝原因记入 llm_audit)。正文仍为空时配置 `dreamProvider`/`dreamModel` 指向非思考模型(`dreamMaxTokens` 默认已是上限 131072,无上调空间)。sleep 侧对应 `sleepReasoningEffort`。 **巩固模型分类声明**(settings panel「巩固模型」= `dreamProvider`/`dreamModel`,睡眠侧对应 `sleepProvider`/`sleepModel`): | 模型类别 | 例子 | 说明 | |---------|------|------| | **非思考模型(推荐)** | glm-5-2 类等 | 无 reasoning 声明;即使配了 effort 被 harness 拒绝,fallback 去掉字段重试即成功。空体风险最低 | | **思考模型(需实测)** | deepseek-v4-flash-ga 等 v4-flash-ga 系 | 默认开推理,可能烧光 token 预算返回空体;部分型号(如 v4-flash-ga)在 harness 侧被声明为**不接受任何 reasoning effort**(去掉 effort 时 harness 的 `defaultEffort` 会顶上来再次拒绝)。v0.7.26+ 已根治:`resolveDreamEffort` 发流前探测模型支持的档位,不支持的配置档位自动换用模型默认/首个支持档位,声明无 reasoning 能力的型号则省略字段。选用时建议配 `dreamReasoningEffort` 实测(未配置时自动取最低档),不行就换非思考模型 | ### Sleep Mode 系统级睡眠 💤(v0.4.0,opt-in) 从 autoDream 的"被动阈值触发"升级为"主动定时维护 + 分层压缩"。系统空闲 `sleepIdleMinutes` 分钟自动执行深度维护,**默认关闭**(`sleepModeEnabled: false`),开启后行为: - **可中断**:AbortController 实现,用户恢复活动即中止当前周期(`noteWrite` 重置空闲计时 + 中断信号) - **串行安全**:睡眠周期走 `service.enqueue` 串行队列,与 autoDream 严格不重叠;`minRefTimeMs` 防止快照后被召回的记忆被误降级 - **四阶段深度维护**: 1. `conflict_resolution`:全库冲突消解,strictness 三级可配(gentle 0.92 / normal 0.85 / aggressive 0.75) 2. `archival_demotion`:按 `last_accessed_at` 分层——30 天未召回压成摘要(原文进 `_full_content`,可无损恢复)、90 天完全归档 3. `pattern_discovery`:LLM 扫描近期记忆提炼规律,产出 `type=pattern` 记忆,evidence 强校验防伪造 4. `relation_completion`:检测孤立实体并补全隐含关系(共现 `related_to` / 项目 `part_of` / 技术 `depends_on`) - **Fail-safe**:每阶段独立 try/catch,LLM 故障只跳过对应阶段;无 LLM 路由时纯规则降级(demotion/relations)照常执行 - **审计延续**:睡眠周期写入 `dream_runs`,`run_type='sleep'`,与 autoDream 共用审计表可追溯 > 配置详见 `docs/SLEEP.md`。 官方设置面板 → 「记忆库设置」→「记忆」标签:按类型浏览、全文搜索;启用向量搜索后可用「语义」切换做向量召回。 ### 用户设置(画像 / 规则)与自定义指令 ⚙️ 官方设置面板 → 「记忆库设置」标签: - **用户画像**:一段自由文本描述用户自己(角色、背景、偏好),**每轮注入**到系统提示,让 Agent 始终遵循 - **规则**:Agent 必须遵守的行为规则列表(如"回答先给结论"),同样每轮注入 - **自定义指令**:注册斜杠命令(`/名称 [补充说明]`),触发时把用户定义的指令内容作为一条用户消息提交给当前 Agent,命令后缀会作为补充说明一并提交;界面仅显示提交回执(宿主不支持 followup 提交时退回旧行为,指令原文会显示在界面,便于复制后手动发送)。命令持久化到 SQLite,启动时自动注册到 DSH 命令表,增删实时生效 > 画像与规则通过独立的 `[用户设置]` 注入区块(优先级高于记忆库),即使记忆为空也会注入。 ### 向量搜索(语义搜索)🔎 可选能力:接入 OpenAI 兼容的 embeddings API,让搜索能命中**字面不同但语义相近**的记忆。 **配置**:官方设置 → 「记忆库设置」→ 滚动到底部「向量搜索」区块: | 字段 | 说明 | |------|------| | `启用向量搜索` | 总开关;开启后记忆面板出现「语义」切换 | | `API 地址 (Base URL)` | OpenAI 兼容端点,如 `https://api.openai.com/v1`;也支持 SiliconFlow、智谱、本地 Ollama 等 | | `API Key` | 对应服务的密钥 | | `模型名` | embedding 模型,如 `text-embedding-3-small`、`text-embedding-v3`、`bge-m3` 等 | 保存配置后点「重建索引」,为已有记忆批量补建向量(新写入的记忆会自动嵌入)。之后在记忆面板输入查询并点「语义」,即可用向量召回语义相关结果;向量服务不可用时自动回退全文搜索。 > ⚠️ 密钥仅保存在本机 `~/.dsh/memory/memory.db` 的 `user_settings` 表,不会上传,也不会写入代码仓库。 > 需要 embedding 而非 rerank 模型:如阿里云 `text-embedding-v3` 可用,`qwen3-vl-rerank` 是 rerank 模型(不走 `/embeddings`)。 ### 语义增强(Semantic)🧠 v0.2 起新增**可以完全离线运行的语义记忆引擎**(本地模型 + 精排 + 聚类): - **本地 Embedding**:三后端可选——ONNX(`Xenova/bge-small-zh-v1.5`,离线)/ Ollama / OpenAI 兼容,失败自动逐级降级,最差回退关键词搜索 - **Rerank 精排**:`Xenova/bge-reranker-base` 对召回候选交叉编码精排,提升 Top-K 准确率 - **autoDream 语义增强**:对记忆向量聚类(`clusterMemories`),自动发现主题相近 / 疑似矛盾的记忆,巩固更精准 - **搜索流水线**:混合召回(关键词 + 向量)→ Rerank → Top-K - **自管运行时**:本地推理的依赖闭包(`@huggingface/transformers` + `onnxruntime-node` + `sharp`,从宿主收编时本机实测 49 个包 / 约 393MB)可收编到 `~/.dsh/mneme/runtime/`,与 profile 的依赖图解耦——由于 profile 是所有插件共用的依赖图,这份闭包留在此前的位置会让「安装任何插件」都替它重走一遍整条依赖链;收编优先硬链接,同盘时几乎不额外占盘。`@huggingface/transformers` 已从 `dependencies` 降为**可选 peer**,因此安装插件不再携带它;正在使用本地嵌入的用户请先按文档收编,否则升级后本地嵌入不可用(读写信道不受影响) - **取回运行时的三条来源**:① 收编本机已有(零网络、同盘硬链接)→ ② 本地 `.tgz` 目录(`runtimeTarballDir`,某个包网络下不到时用)→ ③ npm registry(`runtimeMirror` 可换镜像),按随包发布的 `runtime-manifest.json` 逐个取并**先校验 sha512 再落盘**(win32-x64 实测约 33 个包 / 2142 个文件 / 数百 MB;清单平台无关,一份覆盖 win32/darwin/linux × x64/arm64;剔除 `onnxruntime-web`——Node 构建从不 import 它)。面板「向量索引」卡片在不就绪时会给出这段代价说明并提供一个按钮;不想开面板也可以让 agent 用 `memory_runtime` 工具(`status` / `provision` / `verify`)代做 配置只需在 `cordis.patch.yml` 里设置 `embedProvider`(默认 `openai`,保持 v0.1 行为;改为 `local` 即离线)。升级无需迁移数据。 ```bash node scripts/mneme-runtime.mjs status # 运行时在哪、来源、结构是否完整(不加载模型) node scripts/mneme-runtime.mjs adopt --from ~/.dsh/profiles//node_modules node scripts/mneme-runtime.mjs verify # 真加载运行时并跑一次推理 ``` > 三个命令的退出码为 `0` 健康 / `1` 不健康 / `2` 用法错误,便于脚本 gate;`--json` 输出机器可读结果。运行时不可用时不会崩——检索降级为关键词/BM25,读写不受影响。详见 [本地模型部署指南 §2.5](docs/LOCAL_MODEL.md)。 ### 实体结构化记忆(Entity Gene)🧬 v0.3.0 起新增**记忆基因**层:从记忆里抽取**命名实体**、**带时间轴的属性**、**实体间关系**,让搜索从"字面关键词"升级为"按实体/属性精确召回"。 - **三表**:`entities` / `entity_attrs`(`valid_until` 快照式时间轴)/ `entity_relations`,旧库打开自动建表,幂等无迁移成本 - **自动抽取**:`entityExtractionEnabled=true` 后,新写入的记忆 fire-and-forget 触发 LLM 抽取(同名实体去重、属性存时间轴、关系追加;失败绝不阻塞写入) - **实体搜索**(`searchMemories` 前缀路由,`entitySearchEnabled` 默认开): - `entity:阿尔托` → 属性精确关联的记忆(`_score 1.0`)排在关键词提及(`_score 0.7`)之前 - `attr:国籍=芬兰` → 精确匹配该属性值的记忆 - `attr:国籍` → 该属性键的**全部**当前有效记忆(value 为空契约) - **autoDream 联动**:update 决策写 `supersedes` 自引用(属性版本被替代);merge 决策把 loser 的属性归属迁移到 keeper(keeper 已有同键当前值则失效) > 📖 详见 [实体结构化记忆设计](docs/ENTITIES.md) · [语义增强架构](docs/SEMANTIC.md) · [本地模型部署指南](docs/LOCAL_MODEL.md) ### 记忆质量过滤 🧼(v0.4.6,默认开) 写库前对每条记忆做**启发式质量打分**(纯函数,无 I/O、无共享状态):元记忆词汇(谈论记忆系统本身)、自指类型标签、内容过短、重复度高、与近期记忆近似重复都会扣分(0-100): - `score ≥ 60`:正常存储 - `30 ≤ score < 60`:`quality_score` 落库,注入排序改为按 `importance × quality/100` 降权(degraded) - `score < 30`:归档并标记 `low_quality`——仍可显式搜索召回,只是**永不自动注入**。**例外(#135)**:`importance ≥ memoryQualityFilter.exemptImportance`(默认 4)的记忆只降权不归档——评分与信号标签照常落库,归档决定不再静默越过用户标注的重要性 `memoryQualityFilter.enabled` 可整体关闭,`archiveThreshold` / `degradeThreshold` / `minContentLength` / `exemptImportance`(1=全部豁免,5=仅最重要豁免)可调。此外更新记忆(`memory_update` 等)不会抹掉系统信号标签(`low_quality` / `duplicate` / `meta` / `self_referential` / `repetitive` / `short_content`)——它们是「为什么被降权/归档」的审计线索,与用户自传标签并集保留(#135)。 ### LLM 消耗审计 📊(v0.4.6,默认开) 每次**后台 LLM 调用**都会写入 `llm_audit_logs` 表:`tokens` / `duration` / `status` / `source`(由哪个触发产生)。失败调用记为 `status=error`,绝不阻塞功能本体;`retentionDays`(默认 90)在启动时清理超期行。 巩固那一轮还有一层**事后判决**:调用成功不等于决策可用,而校验发生在调用之后。判决以 `metadata.decision_validation`(`verdict` / `error_count` / `skipped_count` / `survivor_count` / `invalid_actions`)浅合并回同一行——`status`、tokens、时间戳一律不改,它们是调用层的事实。`verdict` 四档:`unparsed`(没解析出 JSON 数组)/ `rejected`(整单拒绝)/ `degraded`(只应用了合法子集)/ `ok`。有了这一层,「调用成功率」与「整轮有效率」才对得上账。 覆盖的后台调用(`source` 即触发源,`operation_type` 区分同一源下的不同调用): | `source` | `operation_type` | 说明 | |---|---|---| | `autoDream` | `dream_consolidate` / `dream_summarize` | 巩固的整理裁决与总览摘要,各一行 | | `autoDream` | `dream_narrative` | 叙述条合成,需 `dreamNarrativeEnabled`(默认关)才跑 | | `autoSummarize` | `summarize_compress` | 空闲蒸馏的压缩 | | `sleep` | `sleep_conflict` / `sleep_pattern` | Sleep Mode 的冲突裁决与模式挖掘,两阶段各一行 | | `entityExtract` | `entity_extract` | 实体抽取,**每次写入记忆都会跑** | > 后两条链路见 #250——它们是结构上漏接的后台路径(sleep 的 `streamText` 副本没有 `onUsage`、实体抽取适配器拿不到 `service`),不是刻意收窄口径。 新增两个只读 API: - `GET /api/dsh-mneme/semantic/llm-audit?page=&pageSize=&source=` — 分页查询 + 按 source 过滤 - `GET /api/dsh-mneme/semantic/llm-audit/stats?days=` — 近 N 天按 source 汇总预算(tokens / 次数 / 失败数) `/llm-audit/stats` 的 `by_source` 是服务端 `GROUP BY trigger_source`,不做任何白名单过滤,因此新链路自动进入汇总。 面板侧现状(#250 之后,未改面板): - 「LLM 消耗」卡读 `/llm-audit/stats`,但只渲染 `total_calls` / `total_tokens` 两个总数。新链路的 token 与次数**已经计入这两个总数**,所以「mneme 总共花了多少」现在是对的; - 按链路的拆分(`by_source`)目前只能从 API 读,面板不展示——「哪条链路最费」在面板上还答不了; - 状态页活动流仍只显示 `autoDream` / `autoSummarize` 两个硬编码来源(`lib/client.js`),新链路不会出现在活动流里。 这三处要动面板(活动流还有一处来源白名单加一处二元标签兜底,且 `pageSize=12` 会被每次写入都跑的实体抽取占满),留到面板批次,不在 #250 内。 > 实体抽取是**每次写入记忆都会跑**的链路,计入审计后 `llm_audit_logs` 的行数会随写入量线性增长(`retentionDays` 只约束上界、不约束增速)。若实测增速过快,再考虑按 `operation_type` 采样或单独保留期。 > 只读端点,与 list/search/semantic 一样在设置 `apiToken` 后仍保持开放。 沉淀曲线(面板状态页「8 周沉淀」图的数据源): - `GET /api/dsh-mneme/semantic/consolidation-stats?days=` — 近 N 天(缺省 56,钳 1–365)按天返回三条序列:`added`(新建记忆,排除外来导入行)、`merged`(当天被巩固掉的**去重记录数**,取自 `receipt_chain` 里 `kind∈(merge,update)` 且 `verdict='live'`)、`archived`(当天进入归档面的条数),另附三个窗口合计。 `merged` 刻意按去重 `record_id` 数而不是行数:同一条记录被多轮巩固重复记账是常态(活库实测 2412 行只对应 1796 个记录),按行数会把「又记了一遍」算成「又沉淀了一条」。新增与合并背离才是这张图要看的东西——2026-10-25–10-31 那段新增照旧、合并归零,只看新增或只看总量都发现不了。 ### 记忆库主区视图与记忆图谱 🕸️(v0.5.0) 记忆功能从侧边栏抽屉收进**主内容区全宽 tab**(conversation.view 插槽,与「对话 / Trajectory」并列),侧边栏「记忆」入口点击后直接激活该 tab。页面顶部子 tab 行切换三个子视图: - **记忆(三栏浏览)**:左栏分类树(类型 + 计数)/ 中栏时间树(月 → 日两级倒序、可折叠)/ 右栏详情(**全文不截断** + 复制全文);语义搜索内嵌工具栏开关(防抖 250ms),`entity:` 前缀可「在图谱中查看」 - **图谱**:输入实体名,加载以该实体为中心的关联网络—— - 服务端只读 ego-graph API:`GET /api/dsh-mneme/semantic/graph/ego?entity=&depth=1|2`(BFS 层级遍历,`limit` 防大图失控,实体不存在 404;配套 `/semantic/graph/entity-attrs` 查实体属性) - 前端**零依赖手写 SVG 力导向布局**(插件运行时无法 require vis-network 等第三方库):斥力 + 弹簧 + 向心引力物理模拟,节点按类型着色、按提及次数定半径,支持拖拽、点节点看属性、点边跳回来源记忆 - **设置**:画像 / 规则 / 指令 / 向量配置,限宽居中 图谱 ↔ 记忆双向互跳:图谱详情侧的关联记忆可点击,记忆边的「来源记忆」按 memory_id 直跳三栏视图并自动定位。 - **分享包三步(面板)**:「⋯」菜单里的**分享记忆包…**(报告先于下载:进包条数、同键折叠、六个跳过原因、可展开的跳过清单,超水位回 413 同屏)与**导入分享包…**(选文件 → 干跑预览将新建几条 / 撞名几条 → 确认才写库),外来行带 `from-<别名>` 徽标、工具条「外来」chip 按 `source=imported` 服务端筛、抽屉「收起这个包」两段式整包退出(先清点后物删)。口径与判据一条都不在面板里复述——出包在 `src/share.js`、入包闸门在 `src/share-import.js`、协议层在 `src/share-http.js`,细则与实测见 [docs/SHARE.md](docs/SHARE.md) ### 四路召回融合与会话热记忆 🔎(v0.5.0,v0.8.4 扩四路) - **BM25 稀疏第三路召回**(`src/search/bm25.js`):与向量召回、FTS5/LIKE 关键词并列——ASCII 词元 + CJK bigram 分词、IDF 加权(归一化 [0,1]),专有名词 / ID / 代码片段等散词查询不再依赖子串命中。融合规则:未召回行按 `0.3×BM25分` 回填;仅向量召回行获得词法加分;LIKE 已命中行不叠分。`bm25SearchEnabled` 可关 - **实体图召回轴(第四路,v0.8.4)**(`entityRecallEnabled` 默认关):开启后检索融合池三源扩四源——store 新增 `findEntitiesMentionedIn` / `getLinkedMemoryIds` 反查原语 + `idx_relations_memory` 索引,命中实体所在记忆按边入池,fuseRecall 的 blend / rrf / minmax 全配方参与;实体轴与 BM25 同为确认/回填信号(we 并入 wb 共用 0.3 回填权重),永不主导语义排序,失败降级空数组;keyword 模式纯文本路径不吃实体轴 - **自适应阈值**(`src/search/adaptive.js`):取代固定 `0.65` 截断——`entity:`/`attr:` 前缀放宽 0.5,短查询(<5 字符)收紧 0.7,长查询(>50)放宽 0.6,Top1/Top5 分差 > 0.3 时放宽让尾部进 Rerank;显式传 `threshold` 或 `adaptiveThresholdEnabled=false` 走旧行为 - **会话级短期热记忆**(`src/hot-memory.js`):最近 N 轮对话(默认 5 轮,`hotMemoryRounds`)按 token 预算(默认 2000,`hotMemoryMaxTokens`)滚动截断,从会话事件日志无状态重建、不落库;注入顺序为「短期上下文 → 长期记忆召回 → 摘要」 - **选择性注入**:query 向量可用时注入候选按主题相似度重排(`selectiveInjectEnabled` 可关);**搜索时语义去重**为激进选项(`searchSemanticDedup=true` 显式开启,近重复行 Rerank 前丢弃) - **召回基准**(`scripts/benchmark-recall.js`):标准查询集驱动,计算 Recall@5 与 MRR,`legacy`(三特性全关)vs `fused`(默认配置)双跑对比 ## 📜 版本历史 🧬 基因 → 🛡️ 审计加固 → 💤 睡眠巩固 → 🕸️ 召回融合与图谱 → 🌡️ 自进化 → 🔐 作用域隔离 → 🌐 MCP 生态 → 🧭 M.O. → 💭 情绪归因 → 📔 日记本 当前版本的能力以本 README 正文的配置章节为准——**这张表只留一句话头条**,每一版具体怎么实现见 [CHANGELOG](CHANGELOG.md) 与 [Releases](https://github.com/slow-stack/mneme/releases)。 | 版本 | 头条 | |------|------| | **v0.13.0**(2026-11-10) | 情绪终于有地方可放:`affective_context` 带情绪词与归因,三个独立会话才升成稳定倾向,不足就降级不丢弃 | | **v0.14.0**(2026-11-12) | 图像描述有了溯源表(摘要能回指哪张图),单次场合的情绪补上注入出口;召回侧两处静默截断(词法语料 500 行、注入池 200 行)改成全量 + 增量;后台 LLM 调用统一补 `sessionId` | | **v0.12.0**(2026-11-07) | pin 从「按类型猜」变成你亲手钉的事实;向量缓存从恒 0% 命中改为跟工作集自适应(1.8–2.4s → 370–424ms) | | **v0.11.0**(2026-11-05) | 记忆读得懂仓库:文件树与 import 图编译成模块划分与地志页 | | **v0.10.1**(2026-11-03) | 打开记忆详情不再崩掉整个宿主 | | **v0.10.0**(2026-11-02) | 记忆开始按「哪一轮」算账:蒸馏有了运行身份,溯源补上反方向 | | **v0.9.5**(2026-10-30) | 记忆能安全地递出去,也能整包收回来 | | **v0.9.4**(2026-10-21) | 三条链各自补上「最后一环」:主动整理、画像提取草稿、人格寻址收口 | | **v0.9.3**(2026-10-20) | M.O. 从「挖得出来」跑到「会被叫去挖」 | | **v0.9.2**(2026-10-19) | 让「看不见的失败」显形 | | **v0.9.1**(2026-10-18) | M.O. 从「看得见」跑到「跑得出账」 | | **v0.9.0**(2026-10-17) | M.O.(工作流)进入 beta:工作流住进项目仓库、可提交可 diff | | **v0.8.x** | 作用域隔离(agent / workspace 双维)、冲突队列人工裁决、stdio MCP server、图召回轴、冷启动 | > 早期版本中的实验性功能(Wiki-Link、tag 系统等)已在 v0.7.11 移除,详见 CHANGELOG 对应条目。 ## 📦 安装 ### 前置条件 - [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(DSH)— 兼容 DSH 0.1.x 与 0.2.x(三段式 peer 范围见 `package.json`);开发与 CI 已对齐 0.2 线,`0.2.0-rc.1` / `0.2.0-rc.2` 实测全绿。旧版兼容垫片(`Session.events` → `snapshotEvents()`,v0.7.9 起确保进入发布产物 lib/)保留 - Node 24+(`node:sqlite`) ### 安装步骤 #### 方式一:npm 安装(推荐) dsh-mneme 是一个 **bundle**(声明了 `dsh.bundle` manifest),安装即自动激活,无需手动写配置: ```bash # 1. 安装插件(自动注册 bundle 层) dsh plugin --profile web add @modusensus/dsh-mneme # 2. 重启 dsh web ``` > 如需自定义配置(阈值、延迟等),可在 `~/.dsh/profiles/web/cordis.patch.yml` 中按 `id: dsh-mneme` 覆盖默认值(见下方配置表)。 #### 方式二:从源码安装(本地目录) ```bash git clone https://github.com/slow-stack/mneme.git cd mneme/dsh-mneme dsh plugin --profile web add "$(pwd)" dsh web ``` > **路径必须绝对**:宿主对 `dsh plugin add` 的本地路径有一条硬校验(`parseInstallSpec` 里的 `a local path must be absolute`),相对路径(含 `.`、`..`)一律拒掉。上面用 `"$(pwd)"` 就地展开,就是为了不给它相对路径。 #### 方式三:直接从 Git 仓库安装 本仓库是**单仓多包**:仓库根那份 `package.json` 是**仓库壳**(`private`、不带 `dsh` 字段),插件本体在 `dsh-mneme/`。所以 Git 地址必须带 `#path:` 指到子目录: ```bash dsh plugin --profile web add 'git+https://github.com/slow-stack/mneme.git#path:/dsh-mneme' dsh web ``` | 写法 | 结果 | |------|------| | `git+https://github.com/slow-stack/mneme.git#path:/dsh-mneme` | ✅ 装出 `@modusensus/dsh-mneme`(实测 0.9.0,38 个包) | | `https://github.com/slow-stack/mneme#path:/dsh-mneme` | ✅ 同上;lockfile 会归一成 `github:slow-stack/mneme#path:/dsh-mneme` | | `git+https://github.com/slow-stack/mneme.git` | ❌ 装到仓库根那份壳(`0.0.0`、无 `dsh` 字段),宿主不认它是插件 | `#path:` 是 pnpm 的 git 参数(`parseGitParams` 里按 `path:` 前缀解析),**不是**本仓库自定义的语法;pnpm 官方文档没写这一条,所以这里给出实测结果。 #### 方式四:tarball ```bash cd dsh-mneme npm pack # 产出 modusensus-dsh-mneme-<版本>.tgz dsh plugin --profile web add "C:/path/to/modusensus-dsh-mneme-0.9.0.tgz" ``` > 必须用 `npm pack`,别用 `pnpm pack`:`bin/cli.mjs` 在工作区里是 CRLF,pnpm 会以 `ERR_PNPM_BIN_CRLF` 直接失败。 #### 自定义配置(可选) 默认配置即可用。如需调整,在 `~/.dsh/profiles/web/cordis.patch.yml` 中覆盖: ```yaml - id: dsh-mneme name: '@modusensus/dsh-mneme' config: memoryDir: ~/.dsh/memory language: zh autoInject: true autoSummarize: true maxInjectedItems: 5 importanceThreshold: 3 autoDream: true dreamThresholdCount: 10 dreamThresholdChars: 5000 dreamDelayMs: 2000 ``` ## 怎么升级(设置不会丢) **升级只动代码,不动你的记忆和设置。** 设置与记忆是**同一个 SQLite 文件**里的表(`user_settings` 与 `memories` 等),文件位置由 `memoryDir` 决定,默认在 `~/.dsh/memory`——**不在插件安装目录下**。所以卸插件、装插件都不碰它。 升级就是覆盖安装,装完重启一次: ```bash # --profile 后面写你当初安装它时用的那个名字,默认是 web dsh plugin --profile web remove @modusensus/dsh-mneme dsh plugin --profile web add @modusensus/dsh-mneme dsh web ``` > 上面是 npm 来源的装法;当初从 Git、tarball 或本地目录装的,按下同一种来源重装即可。唯一要求是**先 remove 再 add**。 > 不用重装 DSH。重装 DSH 动的是 profile,是更大范围的改动,与插件版本无关。 > 也不要只 `add` 不 `remove`——宿主可能拿到缓存的旧副本,版本号没变就说明没换成。 **这件事是实测过的,不是推断**:用 v0.12.0 的代码建库、写入画像、规则、人格卡与四个功能开关,再换成本次发布的代码打开同一个库——四项全部原样读回。反向也测了:新代码写的库,v0.12.0 同样读得出来。 **但有两处不对称要提前知道**:人格卡的按会话分配(`bySession`)以及 `scenario` / `mesExample` 两个字段,v0.12.0 都不认识——用旧版打开新版写的库,这些键读不出来,旧版再写回时可能把它们覆盖掉。所以**回退前先备份整个记忆库目录**(复制 `memoryDir`,默认 `~/.dsh/memory`),需要回退时把库一起退。另外,撤销某个开关只影响之后的行为,不保证把已经写下的数据(例如已经铸出的情绪倾向)一并回滚——要真正清掉得手动删对应条目。 唯一会让你丢数据的是**把库指进 profile 作用域**(形如 `…/profiles/<名字>/…`),宿主清理 profile 时会连带带走——把 `memoryDir` 保持在 profile 之外即可(默认就是),要迁移库则:停止写入 → 复制整个目录 → 改配置指向新位置 → 重启。 ## ⚙️ 配置 | 键 | 默认值 | 说明 | |----|--------|------| | `memoryDir` | `~/.dsh/memory` | 记忆存储目录(SQLite + Markdown) | | `language` | `zh` | 记忆语言:生成的记忆条目、注入标题与后台 LLM 提示词所用语言(`zh` / `en`) | | `autoInject` | `true` | 会话启动自动注入记忆 | | `autoSummarize` | `true` | 会话结束自动提炼摘要 | | `summarizeProvider` / `summarizeModel` | 空 | 摘要的 LLM 路由覆盖(空=使用当前会话模型);推荐轻量模型节省主模型 token | | `summarizeMinIntervalMinutes` | `0` | autoSummarize 最小触发间隔(0-10080,0=不限):两次蒸馏之间最短间隔,失败/degraded run 也占用(#127) | | `summarizeMaxEntriesPerRun` | `0` | 单次蒸馏产出记忆条数上限(0-50,0=不限):节流防单会话大量重复条目(#127) | | `summarizeMinWindowChars` | `0` | 蒸馏前零 LLM 预判:窗口可蒸馏文本不足此字符数直接跳过调用(0-100000,0=关);skip 原因写入审计(#239) | | `summarizeMaxRunsPerSession` | `0` | 每会话最多发起多少次蒸馏 LLM 调用(0-1000,0=不限);只计真实调用,被预判拦下的不占额度(#239) | | `summarizeParseFailureMaxRetries` | `0` | 连续几次解析失败后放弃当前窗口(0-10,0=关):确定性失败(模型把转录当成该自己回话,`finish_reason=stop`)重试收益为 0,而窗口只会越攒越大;设 2-3 可止血,达到阈值强制消费游标跳过,失败行 metadata 带 `raw_head`/`raw_has_bracket`/`streak`(#493) | | `summarizePeakHours` | 空 | 高峰时段(本地时间,逗号分隔、可带星期前缀,支持跨零点):空=关;命中时蒸馏顺延到非高峰、窗口累积后一次蒸、skip 原因入审计。例:按高峰计费的供应商可写 `mon-fri 08:00-12:00,14:00-18:00`(如 DeepSeek,以其官方定价页为准)(#239) | | `summarizePeakMaxDeferMinutes` | `120` | 高峰顺延上限(0-1440 分钟,0=不设上限):到点仍处高峰就照常跑,避免长高峰把蒸馏饿死(#239) | | `summarizeDedupeMode` | `off` | 落库前去重档位:`off`(默认=现状)/ `title`(零成本,仅拦完全同名)/ `vector`(复用 embedding 列做同会话语义近邻,无 LLM 调用,#127) | | `summarizeDedupeMinSim` | `0.92` | vector 去重档的相似度阈值(0.5-0.99) | | `summarizeDedupeWindowHours` | `24` | vector 去重的同会话时间窗(小时,0-168) | | `distillMaxChars` | `24000` | 蒸馏输入的正文截断上限(1000-200000):防止单轮转录把 LLM 输入撑爆 | | `distillRateLimitIntervalMs` | `1000` | 蒸馏 LLM 调用全局串行队列的最小间隔(ms,0-60000,429 保护默认开) | | `distillRateLimitRetries` | `3` | 命中 429 限流时的指数退避重试次数(0-10) | | `distillRateLimitBaseDelayMs` | `1000` | 429 退避基准延迟(ms):1s→2s→4s… | | `maxInjectedItems` | `5` | 最多注入几条记忆 | | `injectUncertaintyAdaptive` | `false` | 注入条数的查询自适应:确定性强的话题把条数收缩到一半(下限 1),模糊话题(回指/时间线索,或极短查询)维持 `maxInjectedItems` 上限;**只做单向收缩**,判据只看查询本身、不做额外检索(#239 第 5 项) | | `injectRotationTurns` | `0` | 注入位跨轮轮换:同一条记忆在最近 N 个查询轮次注入过后本轮不再优先(新鲜优先、不足回填,槽位数不变;会话边界自动重置;`0` = 关闭保持现状) | | `injectContentMaxChars` | `300` | 注入单条正文截断上限(60-4000,原硬编码 300,#164①/#225):截断尾部带上限/原长/全文 `memory_get` 指引;块预算 `Math.max(1500, 上限+600)` 随上限放大 | | `importanceThreshold` | `3` | 注入的最低重要性(1-5) | | `autoDream` | `true` | 自动记忆整理开关 | | `dreamThresholdCount` | `10` | 触发整理的记忆条数阈值 | | `dreamThresholdChars` | `5000` | 触发整理的总字符阈值 | | `dreamDelayMs` | `2000` | 整理异步延迟(去抖) | | `dreamProvider` / `dreamModel` | 空 | dream 的 LLM 路由覆盖(显式配置优先于 agent 默认模型;留空则回退到 agent 默认模型) | | `dreamMaxTokens` | `131072` | dream LLM 调用最大 token 数(上限 131072;#135 起默认即上限——思考型模型的 reasoning 与正文共享该预算,正文仍为空时优先换非思考模型或调 `dreamProvider`/`dreamModel`,见下方调优指南) | | `dreamReasoningEffort` | 未配置=最低档 | dream LLM 推理强度透传:`off` / `low` / `medium` / `high` / `none`(未配置 = 自动取模型支持的最低档,避免思考模型用自带默认档烧光预算;`none`=不传该字段、用服务商自带默认;v0.7.26+ 模型不支持配置档位时自动换用其支持的默认/首个档位,无 reasoning 能力的型号省略字段) | | `dreamCandidateMode` | `window` | dream 候选集构造:`window`(只取最近 `dreamMaxSnapshotSize` 条)/ `hybrid`(在此基础上并入向量翻出的高相似组)。纯时间窗口下,实测 45 对「双方活跃且 sim≥0.85」里 0 对能同时进窗口——该合并的一对几乎永远碰不到面。**已知边界**:dream 侧动作集仍是五分支(keep / merge / archive / update / conflict),hybrid 翻出的互补型 / 演进型对在 dream 里没有 `differentiate` / `supersede` 出口;正确出口在 sleep 侧(`sleepActionSet: full`)| | `dreamCandidateMax` | `0` | hybrid 的候选总量上限;`0` = 复用 `dreamMaxSnapshotSize`。候选总量与库总量解耦,输入成本不随库增长 | | `dreamCandidateMinSim` | `0.85` | hybrid 判「高相似」的阈值(与 sleep 的 normal 档对齐,两个模块共用同一个「高相似」定义) | | `dreamMaxArchivePerRun` | `8` | 单轮 archive 决策上限(超限整单拒绝,防一次性大扫除;正常清理可调高) | | `dreamMaxMergePerRun` | `40` | 单轮 merge 决策上限(超限整单拒绝)。merge 是巩固里唯一回吐正文的动作(每条要模型重写合并后的 content),所以这是「输出里正文占比」唯一可控的杠杆;默认 40 = 实测每轮 merge 数分布上沿(全库 p99 28 / max 39,历史 0 命中),与 archive 闸默认 8 同为尾险护栏而非成本削减工具。**注意**:`dream_consolidate` 的输出 token 里只有约 11.8% 是决策正文,其余约 88% 是推理与 JSON 骨架——想省 token 请调 `dreamReasoningEffort` 或换非思考模型,调小本键收益有限 | | `dreamSkipInvalid` | `true` | 单条非法决策跳过 + 合法子集应用 + run 记 degraded(#89);`false` 恢复严格模式整单拒绝 | | `allowCrossTypeMerge` | `false` | 显式放宽跨类型合并(默认关,类型边界由用户承担);跨类型意图的正确出口是 `sleepActionSet: full` | | `dreamImplicitKeep` | `true` | 显式决策覆盖率不足时的隐式 keep(未提及条目保持原样);`false` + `dreamMinExplicitCoverage: 0` 恢复旧严格行为 | | `dreamMinExplicitCoverage` | `0.5` | 显式决策覆盖率下限(0-1):合法子集低于该值降级 degraded 而非整单拒绝(#104 方向 1,PR #200) | | `dreamMaxSnapshotSize` | `200` | autoDream 滑动窗口上限:每次只对最近 N 条做 consolidation,窗口外不进 snapshot(防 LLM 输入撑爆) | | `dreamSummaryProvider` / `dreamSummaryModel` | 空 | 总览(dream_summarize)专用模型路由(留空 = 沿用 `dreamProvider`/`dreamModel`)。consolidate 有窗口(`dreamMaxSnapshotSize`)而总览输入随库增长,ctx 需求差数倍——用小 ctx 模型跑巩固时把总览指到大 ctx 模型(issue #258) | | `dreamSummaryMaxInputs` | `0` | 总览输入条数硬上限(0 = 不设上限):超过时按 `updated_at` 倒序只保留最新 N 条进总览,防小 ctx 模型被全库输入撑爆;总览口径脚注的条数随实际输入变化 | | `dreamMinIntervalMinutes` | `0` | autoDream 最小触发间隔(0-10080,0=不限):失败/degraded run 也占用 | | `dreamPeakHours` | 空 | 巩固侧高峰时段(本地时间,与 `summarizePeakHours` **同一份语法**):空=关;命中时不调模型、baseline 不刷新(阈值继续累积,留到非高峰一次性巩固)、登记 `skipped`/`peak-hours` 审计并顺延到最近的高峰结束时刻。适合「白天要留算力给交互、巩固挪到夜里」的场景(#239 第 4 项镜像到巩固) | | `dreamPeakMaxDeferMinutes` | `120` | 巩固侧高峰顺延上限(0-1440 分钟,0=不设上限):到点仍处高峰就照常跑,避免长高峰把巩固饿死(#239) | | `dreamNarrativeEnabled` | `false` | 叙述条总开关(#164 对齐,v0.8.4):按共享 tag 主题簇合成叙述 + evidence 证据链,注入候选排除(按需检索,常驻位只留 dream 总览);也走 feature_flags 白名单,lightMode 强制关 | | `dreamNarrativeMinCluster` | `3` | 主题簇合成叙述的最小成员数(2-20,v0.8.4) | | `apiToken` | 空 | 可选 API 鉴权 token;设置后写操作与密钥接口要求 `Authorization: Bearer ` | | `externalApiEnabled` | `false` | 独立 HTTP API 服务开关(v0.7.12):开 standalone API(CLI 依赖它) | | `externalApiHost` | `127.0.0.1` | standalone API 监听地址 | | `externalApiPort` | `8790` | standalone API 监听端口 | | `lightMode` | `false` | 轻量模式(v0.7.12):关闭所有重型增强(语义/实体/叙述/heat 等,见 feature_flags 白名单),纯存储+注入 | | `policyEpoch` | `0` | 冲突裁决规则版本:bump 后旧 dream_runs 降级为历史证据(receipt 不再驱动实时裁决) | | `embedProvider` | `openai` | 语义后端:`openai`(默认,兼容 v0.1)/ `local`(ONNX 离线)/ `ollama` | | `localEmbedModel` | `Xenova/bge-small-zh-v1.5` | 本地 ONNX embedding 模型 | | `localEmbedDimension` | `512` | 本地 embedding 向量维度 | | `localEmbedDevice` | `cpu` | 本地推理设备:`cpu` / `gpu` | | `localEmbedBatchSize` | `8` | 本地 embedding 批大小(1-64) | | `ollamaBaseUrl` | `http://localhost:11434` | Ollama 服务地址 | | `ollamaModel` | `nomic-embed-text` | Ollama embedding 模型 | | `embedModelCacheDir` | 空 | 模型缓存目录(空 = 用户级 `~/.dsh/mneme/models`) | | `embedModelMirror` | `https://hf-mirror.com` | 模型下载镜像源 | | `resilientModelDownload` | `true` | 模型文件下载断点续传与重试(v0.8.3,#194/#207):Range/If-Range 续传、416/偏移失配重置、单写者锁+降级直通、空闲看门狗;`false` 恢复 env.fetch 原样 | | `runtimeDir` | 空 | 自管运行时目录(#131):空 = `~/.dsh/mneme/runtime`,放收编/下载的 transformers+onnxruntime 闭包(不留宿主 profile 依赖图) | | `runtimeTarballDir` | 空 | 运行时离线取件:本地 `.tgz` 目录(如 onnxruntime-node 网络下不到时 `npm pack` 丢进去),有则优先于联网 | | `runtimeMirror` | 空 | 运行时 registry 镜像前缀(如 `https://npmmirror.com/mirrors/npm/`);留空用 runtime-manifest.json 官方地址 | | `vectorSearchTopK` | `20` | 向量搜索返回 Top-K | | `vectorSearchThreshold` | `0.65` | 向量搜索相似度阈值 | | `hybridSearchVectorWeight` | `0.6` | 混合搜索向量权重 | | `hybridSearchKeywordWeight` | `0.4` | 混合搜索关键词权重 | | `recallFusion` | `blend` | 召回融合配方:`blend`(默认,加权求和)/ `rrf`(Reciprocal Rank Fusion)/ `minmax`(min-max 归一化加权)——后两者按 rank/尺度感知融合,修复原始分数单位不匹配 | | `signalTransparency` | `false` | 给每条检索结果附加 `signals` 对象({keyword, vector, bm25, final})便于调试;只装饰不改排序 | | `rerankEnabled` | `false` | 是否启用 Rerank 精排(显式开启才加载本地 onnxruntime 模型) | | `rerankProvider` | `none` | Rerank 后端:`local` / `none`(默认 `none`) | | `rerankModel` | `Xenova/bge-reranker-base` | Rerank 交叉编码模型 | | `rerankBatchSize` | `8` | Rerank 批大小 | | `rerankMaxCandidates` | `30` | Rerank 最大候选数 | | `rerankScoreThreshold` | `0.1` | Rerank 分数阈值(低于丢弃) | | `rerankDtype` | `q8` | Rerank 模型量化档(`q8` ≈ fp32 体积的 1/4,`fp32` 可关) | | `reflectionUpdateEnabled` | `true` | update 决策总开关 | | `reflectionFailureTracking` | `true` | 失败追踪总开关 | | `reflectionUpdateMaxPerRun` | `2` | 每次整理最多 update 数 | | `reflectionUpdateMinAgeHours` | `24` | 新建记忆保护期(小时) | | `reflectionFailureRetentionDays` | `90` | 失败表(`failure_memories`)启动期清理保留天数(1-3650;此前写死 90) | | `codingRetrospect` | `false` | 编码记忆蒸馏(v0.7.13,默认关):完整转录提炼编码原子记忆(rejected_solution/pitfall/constraint 三类型) | | `codingKeywords` | 内置词表 | 编码任务识别词表(读取侧门控):命中即视为编码类任务,编码记忆才注入 | | `codingBoostFactor` | `2` | 编码记忆注入排序的 boost 倍数(1-5) | | `entityExtractionEnabled` | `false` | 实体抽取总开关(v0.3.0;存储层恒可用) | | `entityExtractionModel` | 空 | 抽取专用模型(空 = 用 agent 默认模型) | | `entityExtractionProvider` | 空 | 抽取专用模型服务商(空 = 用 agent 默认 provider/model,v0.7.29) | | `entityExtractionReasoning` | 未配置=最低档 | 实体抽取推理档位(同 dreamReasoningEffort 语义,v0.7.29):被模型拒绝时自动去 effort 重试 | | `entityExtractionMaxEntities` | `10` | 每次抽取实体数上限 | | `entityRecallEnabled` | `false` | 图召回轴(v0.8.4,#219):实体挂联记忆并入检索融合池(三源扩四源,blend/rrf/minmax 全配方参与);依赖实体抽取产出,也走 feature_flags 白名单,lightMode 强制关 | | `entityExtractionMaxAttrs` | `20` | 每实体属性数上限 | | `entitySearchEnabled` | `true` | `entity:` / `attr:` 前缀搜索开关 | | `trustEpistemicWeighting` | `false` | 记忆可信度加权(v0.4.5,opt-in 默认关):记忆按来源分级 `observation`> `inferred` > `subjective`,开启后检索排序优先高可信记忆、注入对 observation 标注 `[verified]`、dream merge/conflict 偏向高可信一方;关闭时 `epistemic_status` 仅随保存落库、不参与行为 | | `evalPersistTestResults` | `false` | 检索评估落库(v0.4.5,opt-in 默认关):开启后 `evaluateRetrieval` 把 precision/recall/mrr 快照写入 `recall_evals`;默认关时仅返回调用方不落库。生产 `searchMemories` 审计始终走 `recall_runs`,无条件不触碰 `recall_evals` | | `autoReindexOnBoot` | `true` | 存量记忆缺 embedding 时,向量已配置则启动后延迟后台按批次限速自动回填重建(设为 `false` 仅手动重建)。只补**活跃**记忆(归档/遗忘行不参与召回,#128);模型指纹一致时照常补缺失行、不再短路(#128) | | `hybridInject` | `true` | 注入语义召回优先(v0.4.6,Bug4):`injectCandidates` 带非空 query 时先走向量索引语义召回候选,规则筛选补足/去重;空 query / 无向量回退旧逻辑 | | `bm25SearchEnabled` | `true` | BM25 稀疏第三路召回(v0.5.0):ASCII 词元 + CJK bigram,IDF 加权,散词/ID/代码片段查询不再依赖子串命中 | | `adaptiveThresholdEnabled` | `true` | 自适应相似度阈值(v0.5.0):按查询形态动态截断(前缀 0.5 / 短查询 0.7 / 长查询 0.6 / 头部分差大放宽 0.5),显式传 `threshold` 走旧行为 | | `hotMemoryEnabled` | `true` | 会话级短期热记忆总开关(v0.5.0):关闭后热记忆块不再注入(长期召回不受影响) | | `heatEnabled` | `false` | 热度模型总开关(v0.7.0 / v0.7.20 回归,**默认关**——v0.7.12 起用户已习惯无 heat 行为):开启后提供热度字段 / sleep 热联合降级保护 / 前端热度投影;注入排序乘热度已拆为独立开关 `injectHeatEnabled`(#218 / E5 考卷:heat 乘进注入排序在真实年龄混合下饿死老约束,默认关,显式开启恢复旧序)。关闭则跳过 heat 计算与热度触达,sleep 降级退回纯时间分层。也走 feature_flags 白名单(面板可启停=回滚开关),lightMode 预设强制关 | | `heatGlobalBeta` | `1.0` | 广义指数形状参数 β(`H=exp(-λ·Δt^β)`,issue #218 拍板):β=1 纯指数;以 Δt>1 小时为准,β<1 衰减更慢(亚线性长尾)、β>1 衰减更快(超线性);0<Δt<1 的首小时内方向相反(Δt^β 随 β 增大而变小) | | `heatTypeDecay` | 内置 TYPE_DECAY | per-type 衰减因子 λ;λ=0 的类型免疫(preference/pattern/summary 热度恒 1.0,sleep 永不降级) | | `sleepHeatThreshold` | `0.05` | sleep 降级联合判定热度下限:heat<该值 **且** importance<5 才允许降级 | | `sleepModeEnabled` | `false` | Sleep Mode 总开关(v0.4.0,opt-in):会话空闲 `sleepIdleMinutes` 后触发深维护(巩固/降级/模式发现),与 autoDream 串行不重叠 | | `sleepIdleMinutes` | `5` | 触发 sleep 的空闲窗口(分钟,1-60) | | `sleepMinIntervalHours` | `8` | 两次 sleep 最小间隔(小时,1-168):间隔内再次空闲不重复触发 | | `sleepConflictStrictness` | `normal` | 冲突裁决严格度:`gentle`(高置信,阈值 0.92)/ `normal`(标准,0.85)/ `aggressive`(低置信也裁决,0.75) | | `sleepActionSet` | `conflict` | sleep 冲突阶段动作集:`conflict`(默认=现状,只用 conflict/keep,后台流程不静默扩张)/ `full`(六分支 merge/update/supersede/differentiate/conflict/keep,#126) | | `sleepArchiveDays` | `30` | 降级分层一:N 天未访问 → 缩为摘要(正文进 `_full_content`) | | `sleepCompressDays` | `90` | 降级分层二:N 天未访问 → 直接归档(实体关系保留) | | `sleepPatternMinMemories` | `100` | 模式发现扫描窗口(最近 N 条记忆,10-1000) | | `sleepPatternLookbackDays` | `30` | 模式发现回看实体属性变更的天数(1-90) | | `sleepMaxPatternPerRun` | `3` | 每轮模式记忆产出上限(0=禁用,0-10) | | `sleepProvider` / `sleepModel` | 空 | sleep 深维护专用 LLM 路由覆盖(留空用巩固模型或当前模型;建议同巩固模型选非思考模型) | | `sleepReasoningEffort` | 未配置=最低档 | sleep 各阶段 LLM 推理档位(同 dreamReasoningEffort 语义,v0.7.26) | | `recallRecordDefault` | `true` | recall_runs 记录默认开(显式传 `recordRecall:false` 的调用方不受影响);注入同样落账(`mode='inject'`,随本开关,#217) | | `recallRetentionDays` | `90` | recall_runs 滚动清理保留天数 | | `hotMemoryRounds` | `5` | 会话级短期热记忆轮次(v0.5.0):最近 N 轮对话滚动注入,从会话事件日志无状态重建、不落库 | | `hotMemoryMaxTokens` | `2000` | 热记忆 token 预算(v0.5.0,200-32000),超出滚动截断 | | `selectiveInjectEnabled` | `true` | 选择性注入(v0.5.0):query 向量可用时注入候选按主题相似度重排,替代固定规则序 | | `searchSemanticDedup` | `false` | 搜索时语义去重(v0.5.0,激进选项默认关):embedding 余弦 ≥0.95 近重复行在 Rerank 前丢弃 | | `searchSemanticDedupThreshold` | `0.95` | 语义去重相似度阈值(v0.5.0,默认 0.95,范围 0.5-1.0):`searchSemanticDedup=true` 时生效,调整可防小模型误折叠 | | `memoryQualityFilter` | `{enabled:true, archiveThreshold:30, degradeThreshold:60, minContentLength:10, exemptImportance:4}` | 记忆质量过滤(v0.4.6,默认开):写库前启发式打分 0-100,元记忆词汇/自指/过短/重复/近似重复扣分;≥60 正常存储,30-60 降权(注入排序按 importance×quality/100),<30 归档标记 `low_quality`(显式搜索仍可召回,永不自动注入;`exemptImportance` 豁免:importance ≥ 该值只降权不归档,#135) | | `llmAudit` | `{enabled:true, retentionDays:90}` | LLM 消耗审计(v0.4.6,默认开):每次后台 LLM 调用(`autoDream` / `autoSummarize` / `sleep` / `entityExtract`,对应 `operation_type` 为 `dream_consolidate` / `dream_summarize` / `dream_narrative`、`summarize_compress`、`sleep_conflict` / `sleep_pattern`、`entity_extract`)写 `llm_audit_logs`(tokens/duration/status/source);失败记 error 不阻塞;只读 API `/api/dsh-mneme/semantic/llm-audit` + `/llm-audit/stats` | | `scopeEnabled` | `false` | 作用域隔离总开关(v0.8.0,issue #17):memory_save 按会话身份写入 agent / workspace 标注(agentPreset / 工作区路径,registry 反查取不到回退 header.cwd,再取不到 NULL);去重键扩展含作用域三元——跨作用域同标题不再物理合并;检索与注入排序按当前会话作用域加权(命中 ×1.25、他 scope ×0.5 保留可见,issue #339 补齐注入通道)。也走 feature_flags 白名单(面板可启停) | | `strictScope` | `false` | 作用域硬过滤(v0.8.0 引入,v0.8.1 起只认显式声明;依赖 `scopeEnabled`):检索 / 注入 / 列表 / 单取四路过滤,**显式声明**收窄到他者作用域的记忆完全不可见;载体自动标注只降权保留可见——真正的物理隔离请用 sensitivity。会话身份解析不到时 fail-closed 只挡显式行。关闭时全部为软隔离(降权保留可见)。也走 feature_flags 白名单 | | `writeAdmission` | `{enabled:false, enforce:false}` | 写入准入(issue #254,默认关):把「这条该不该进库」前移到 LLM 之前。第 1 级是零 LLM 的确定性判据——空白 / 纯噪声 + 密钥 / PII(判据来源是 #164 A2,经 `sensitiveScan` 注入,本批只定义接口)。`enabled` 跑判据并落审计行(`llm_audit_logs` 的 `metadata.deny`),`enforce` 才真的拒(`store.save` 之前返回,`memory_save` 返回 `action:"denied"` + `reason`);只开 `enabled` = 仅告警、写入不拦。去重键命中不进第 1 级(归 write-update 放行),G1/G2 阈值只计量。也走 feature_flags 白名单 | | `preInjectGate` | `{enabled:false, enforce:false}` | 注入前判定(issue #380,默认关):每帧注入候选出池后、进入 system prompt 前,一次池级 LLM 调用判出「会向本次请求注入意见/立场」的记忆(判定协议与 E12 实验同源;实测过滤把谄媚 26.9%→2.5%,且判定结果绝不进模型上下文——给模型看标记的线已被 E2/E3 关闭)。`enabled` 跑判定并落审计行(真实 token 用量 + 被标记记忆 id),`enforce` 才在缓存命中帧真滤除;冷缓存首帧降级为原样注入(宿主注入渲染是同步回调,判定走 prefetch+cache)。pin 池与检索线索行豁免;判定失败原样注入不阻塞;整池被标记时放弃本次滤除(fail-open);判定连续失败 3 次熔断一个冷却期;无默认路由时审计行 `model_id` 落 `unknown` 占位;`llmAudit` 关闭时本闸不运行;lightMode 强制关。也走 feature_flags 白名单 | | `distillRecords` | `{enabled:false, retentionDays:90}` | 蒸馏原文留存 + 记忆级溯源(issue #399,默认关):把蒸馏从「替换」降级为「叠加索引」——蒸过的窗口原文旁路落 `distill_records`(一个事件一行,主键 `(session_id, seq)`,`INSERT OR IGNORE` 幂等),每条新记忆落 `memory_provenance` 回指来源窗口(`(memory_id, session_id, seq_start, seq_end)` 唯一)。**渲染与落盘共用同一隐私谓词**:私有推理链、插件注入的机器内容(AGENTS.md / skill 正文 / 文件变更通知)两条路都不进;工具调用入参与结果、code 输出**只进渲染、不落盘**(渲染是 `codingRetrospect` 的输入,落盘只留对话)。留存文本**不受** `distillMaxChars` / 工具裁剪约束(原文就是原文)。到期裁剪只删 `distill_records`,溯源链接保留指向空区间(面板显示「来源已过期」),**绝不级联删记忆**;删记忆时显式清链接。无历史回填、从未蒸馏的窗口不留记录。进 `dsh-mneme reclaim` 报告第三项;lightMode 强制关。也走 feature_flags 白名单 | | `capacityWarning` | `{dbSizeWarnMb:256, archivedRowsWarn:5000, archivedRatioWarn:0.95}` | 容量预警(issue #275,空间维度):库体积 / 归档区规模过线时在 `GET /api/dsh-mneme/health` 的 `capacity` 块与面板「库体积」卡上报。三道阈值都是**观察口**,不是动作触发——没有周期性消费者,预警也永不自动归档 / 删除(真删由人显式确认),出口是维护卡的无损回收(`dsh-mneme reclaim`)。阈值判 `>=` 过线、**0 = 关掉该条检查**;判级 fail-safe(读失败显式 unknown,绝不伪装 ok)。默认值锚定 #275 议题的形态区间(活跃数百 / 归档数千;占比健康区就在 0.8~0.9,故 0.95 起才算过线)。答复只带数字、不带库文件路径;归档净增速率是 recall-stats `archive` 块的第五指标,不在这里重复。也走 feature_flags 白名单(改动启动快照生效,面板显示「待重启」) | | `conflictFreezeEnabled` | `false` | 冲突冻结(v0.4.4):dream 发现矛盾对不自动裁决,冻结进冲突队列;状态页「冲突队列」支持并排对比与人工确认(保留 A / 保留 B / 仅标记已处理,v0.8.0) | | `conflictFreezeMaxPending` | `100` | 冲突冻结队列最大挂起数(1-1000):超出后不再入队(防队列无限膨胀) | | `escapePromptVariables` | `true` | 注入文本花括号转义(v0.8.0 恢复,issue #162):注入边界把 `{{...}}` 转义,防止 hot memory / 记忆原文里的 Go template / Vue 语法触发宿主 interpolate 抛错卡死会话(v0.7.4 曾修复、v0.7.11 误删) | > 🔐 **API 安全**:DSH 无内置鉴权且默认仅监听 `127.0.0.1`。插件 API 默认开放(便于 Web 面板即装即用)——**未配 `apiToken` 时读和写都开放**,真正的围栏是回环 + 宿主监听器的 Host/Origin 校验(它防 DNS rebinding 与跨站浏览器请求,但不建立身份)。如需防护(如局域网暴露),在配置中设置 `apiToken`:此时**写操作**(新增/更新/删除/导入/收包(`/receive`、`/receive/undo`——写的是**本机自己的库**,归 `token-write`,所以免 token 的 GET 在那两条路径上只会拿到 405)/画像/规则/命令/开关/开关计费类)与**敏感读**(`/export`、`/external-api`、`/vector-config`、`/vector-reindex`、`/maintenance/*`、`/runtime/provision`、`/share`、`/memory/provenance`(蒸馏窗口的逐条会话原文,另受 `distillRecords.enabled` 门控))需携带 `Authorization: Bearer `(前端设置面板填同一个 token);只读的 `list` / `search` / `semantic` / `inject-preview` 等**仍然开放**,不要把它们当成被 token 保护的面。`/api/dsh-mneme/vector-config` 返回的 `apiKey` 已掩码(`sk-***…`),存储仍保留明文供调用;前端回传空或掩码值表示"不改 key"。 > > 安全模型全表(哪些路由免鉴权、为什么)见 [SECURITY.md](../SECURITY.md#5-security-model-what-actually-protects-what)。 > ⚠️ **已知边界:极简模式(minimal agent preset)下不注入**(宿主设计,非插件缺陷):minimal 预设的组合文件显式 `complete: true` + `includeRuntimeContext: false`——整条系统提示词被 persona 钉死、全部 runtime context 快照被压制,**记忆注入 / 用户画像 / hot memory 在极简模式会话中一律不送达模型**(宿主内置 context 同样消失)。判断与解法:会话头部显示「极简模式」即为该形态;需要记忆注入请改用标准模式(会话级切换,或 `~/.dsh/settings.yaml` 设 `agent-presets.default: standard`——注意 `--dump-config` 显示的是 bundle 默认值,会被用户层覆盖,不能反映实际生效值);极简模式下的过渡方案是把画像/规则写入 `AGENTS.md`(agent-instructions 走 section 路径,不受该压制影响)。 ## 外部 API 与 CLI 除 DSH 内部端口外,插件还可以开启一个**独立的 HTTP 外部 API**(默认 `http://127.0.0.1:8790`,Bearer token 鉴权),供其他插件、CLI 脚本或桌面工具读写记忆,不依赖 DSH 内部端口。 ### 启用与鉴权 - 在插件设置中开启外部 API(默认监听 `127.0.0.1:8790`,仅本机可访问); - 访问 token 在插件设置 / 面板「设置 → 外部访问」中查看; - 除 `GET /health`(免鉴权)外,所有路由需携带 `Authorization: Bearer `,无效 token 返回 `401 {"error":"unauthorized"}`。 主要路由: | 方法 | 路由 | 说明 | |------|------|------| | `GET` | `/health` | 健康检查(免鉴权),返回 `{ok:true}` | | `GET` | `/status` | 版本、记忆统计、实体数、运行时长 | | `GET` | `/profile` | 用户画像,返回 `{profile:"..."}`;未设置时为空字符串 | | `GET` | `/rules` | 行为规则列表,返回 `{rules:[...]}`;未设置时为空数组 | | `GET` | `/memories?limit&offset&type&minImportance&source&order=chrono&include_archived&occurred_from&occurred_to` | 分页列出记忆(`include_archived=true` 含归档;occurred 时间窗按 `occurred_at` 回退 `created_at`) | | `GET` | `/memory/provenance?id=` 或 `?sessionId=`(可选 `&limit=`,默认 20、上限 100) | 溯源读出口,两个方向共用一条路径(#421 拆出两条:方向 1 记忆→窗口由 #509 交付,方向 2 会话→窗口由 #524 补上;宿主内面板路由 `/api/dsh-mneme/memory/provenance` 同一口径)。**两个主键互斥**:都缺 `400 {"error":"missing-id"}`、都给 `400 {"error":"ambiguous-id"}`(刻意不静默择一——两个方向的结果形状不同,猜一个只会在出事之后才发现)。方向 1 是一条溯源链接一个窗口 `{session_id, seq_start, seq_end, created_at, distill_run_id, run_type, trigger_source, run_status, records[]}`(#512 补运行归因:run 是归因主体,窗口只是它的一批输入;读不到整轮归因时整组 `null`,**不从区间或时间戳反推**),答复级另带 `mints[]`(行级归因:这条记忆出生自哪一轮 dream/sleep,只填 create);方向 2 把同一个 `(session_id, seq_start, seq_end)` 合并成一项(`GROUP BY`),带 `link_count`=这个窗口派生出几条记忆,按 `seq_start` 排——逐链接返回会让「这个会话蒸了几次」被重复行淹没。两个方向都带 `truncated`(撞上 limit 时说「还有更多」;少了它,被截断的答复与「本来就这么少」逐字节相同)。`records` 是那段蒸馏窗口的**逐条会话原文**(按 `seq` 升序、不越出区间)。没有链接返回**空 `windows`**——「不是蒸馏来的」是有效答案,不是错误。状态码阶梯:`401` 无 token → `400` 缺参/多参/`invalid-limit` → `409 {"error":"distill-records-disabled"}`(`distillRecords.enabled` 关着;该开关是**启动快照**,面板改完要到下次启动生效,面板会显示「待重启」)→ `503 {"error":"provenance-unavailable"}`(该方向要用的方法没接线,**按方向判**——只缺 session 侧方法时方向 1 仍然 200)→ `200`。`409` 与 `503` 必须分开读数:前者是「用户关了」,后者是「装配坏了」。**关开关 ≠ 删掉已留存原文**——只是不再经 HTTP 提供,盘上的行要 `dsh-mneme reclaim`(或重新开启后的启动裁剪)才走,详见 [docs/DISTILL.md](docs/DISTILL.md) | | `GET` | `/memories/:id` | 单条记忆 | | `POST` | `/memories` | 新增记忆 `{type,title,content,importance?,tags?,source?,sensitivity?,occurred_at?,agent_scope?,workspace_scope?}`;响应含 `action: created\|merged` | | `PUT` | `/memories/:id` | 局部更新 `{title?,content?,type?,importance?,tags?,reason?,agent_scope?,workspace_scope?}`;content 改写按 human_override 入档 | | `DELETE` | `/memories/:id` | 删除记忆 | | `GET` | `/search?q&mode=keyword\|vector\|hybrid\|auto&topK&occurred_from&occurred_to&agent_scope&workspace_scope` | 搜索(关键词 / 向量 / hybrid=向量领位关键词补位 / 自动)。scope 两参声明调用方身份(不传 = 不解析,行为与旧版一致;任一给出则 strictScope 硬过滤与 A2 软加权随之生效) | | `GET` | `/context?q&topK&threshold&agent_scope&workspace_scope` | 注入候选一站式聚合(issue #370,#363 承诺):宿主注入管线 `injectCandidates` 全语义(优先级分层 / 编码门控 / heat / scope 软加权 / hybrid 语义路 / pin 前置)+ 画像 + 规则,返回 `{profile, rules, items, pinnedCount, tier}` 结构化条目,不含渲染文本。`threshold` 是 **importance 阈值**(默认 3,与 `/search` 的相似度阈值同名不同义);`topK` 默认 5(对齐宿主 `maxInjectedItems`);scope 语义与 `/search` 同口径;跨轮轮换 / 热记忆 / 时间前缀不含(调用方自管);recall_runs 注入分账与宿主注入同口径(#217 `mode='inject'`) | | `POST` | `/bootstrap` | 冷启动(v0.8.4):从仓库目录反向构建初始记忆,body `{dir}` 必填;幂等((type,title,scope) 去重 + `_overwrite` 原地刷新),零 LLM、必有产出 | | `POST` | `/maintenance/reclaim` | 无损回收(#275 / #411,与面板同口径):**只有 POST**(本路径不提供 GET 预估分支)。字段只从 JSON body 读;`dryRun` 默认 `true`,只有 `dryRun:false` 且 `confirm:true` 才真删;`vacuum` 只允许显式指定 | | `POST` | `/share` | 记忆分享包:把活跃记忆按口径打成可外发的 JSON 包,纯读不写库;取行与跳过口径、脱敏规则与 3 MiB 体积水位见 [docs/SHARE.md](docs/SHARE.md)。本路径的 GET 一律 405 | | `POST` | `/receive` | 吃进一份分享包(body `{pack, confirm?}`,包内容整个送上来、服务端不读路径):**默认干跑**,只有 `confirm: true` 才写库;整包一个事务、按 `share_external_id` 幂等(同一个包重跑 `created = 0`),外来行标 `source=imported`,不参与本机巩固/降级。包里一条不合格就**整包拒**(收侧不做逐条修复,也不跑敏感扫描)。入站上限 4 MiB;GET 一律 405。细则与实测见 [docs/SHARE.md](docs/SHARE.md) | | `POST` | `/receive/undo` | 整包退出(body `{packId, confirm?}`,同样**默认干跑**只清点):按 `share_pack_id` **物删**(不是归档——归档行会占住 UNIQUE 位,撤了反而导不回来),`deleted` 是全量、`sample` 只给前 20 条预览;什么都没导入时答 0,不抛。GET 一律 405 | #### `/context` 的 `tier` 档位信号(#383) `GET /context` 的响应多一个 `tier` 字段,回答「这次注入跑在哪一档」。嵌入降级在调用方视角原本完全静默——`items` 看着正常,只是召回质量悄悄变差,外部集成方无从感知自己长期跑在降级档上。 | `tier` | 含义 | |--------|------| | `full` | 语义档:查询嵌入成功,且与库里的向量维度可比 | | `degraded` | 规则档:语义路本次不可用(原因见日志) | | `unknown` | 语义路未启用(`q` 为空),或判不出来(维度探针没跑成 / 一条向量都读不出)——那不是降级,但也不宣称 `full` | **两套消费者的分工:** `tier` 是**响应读数**,给调用方——三态、fail-closed(判不出来报 `unknown`,不报 `full`),用于在集成侧告警或切换策略。降级的**具体原因**是**日志读数**,给运维——走 daemon 的 stderr(`dsh-mneme: /context degraded to the rule tier ()`),不进响应体。原因枚举六值,互不吞并(成例是「修法不同就分开」):`embed-unavailable`(无可用嵌入接口:embedder 对象在,上面没有 `embedSingle` / `embed`)/ `embed-empty`(返回了空或非数组向量)/ `embed-timeout`(接口挂起,被超时护栏按缺失处理)/ `embed-error`(接口抛错)/ `embed-dimension`(接口成功了,但库里没有一条维度相符的活跃向量)/ `no-embedder`(压根没装配 embedder——daemon `--embed off`、lightMode、宿主未装配;**这是预期态,不是故障**)。六值的唯一真源是 `src/api-standalone.js` 导出的 `CONTEXT_DEGRADE_REASONS`,契约测试从它派生断言集合。 `embed-dimension` 值得单说:它是「拿到了向量却不可比」——`searchVector` 对维度不符的行直接跳过,不抛错、只是命中为零,于是召回静默退回关键词/规则档,而嵌入侧一切正常。修法是重建索引,不是查端点,所以它必须和前四个分开。`no-embedder` 与前四个也分开:前者(`embed-unavailable`)是适配器坏了、要修代码,后者是语义路没开、要把嵌装扮上或开起来,主体与动作都不同;并成一个值会让按 `embed-unavailable` 告警的面板对每个轻量模式安装报警。用词则与宿主面 `/api/dsh-mneme/semantic` 的 `reason` 对齐——同一个物理条件在两个面叫同一个名字(那边是另一套枚举、另一个消费者,别顺手统一)。 **`unknown` 之下的判档故障另有一条 warn:** `unknown` 有三个生产者(空 `q` / 维度探针没跑成 / 有行却一条向量都读不出来),响应体只有 `tier` 一个键、分不出它们,而调用方对 `unknown` 的策略常常是「不告警」——所以判不出来的情形会另打一行 `dsh-mneme: /context tier left unknown: `(探针抛错 / 没有可读的向量可比)。**它不是上面那六值的一员**(`tier` 不是 `degraded`,没有降级原因可记),运维程序不要按原因枚举解析它。 **预算:** 判档不是免费的。`q` 非空且装配了 embedder 时,一次 `/context` 真的会起一次查询嵌入(一次网络往返 + `embedTimeoutMs` 的超时窗口)——档位要回答的正是「这次嵌入成不成」。这里没有复用 `/health` 的数据源:standalone 的 `/health` 只回 `{ok:true}`,而 `embeddingDimensionBreakdown` 是宿主面的一次全表扫(每行 JSON.parse 一个向量),按请求跑会把语义路成本翻倍。维度探针因此走 `service.vectorDimensionState()`——**按匹配短路的存在性判据**(有没有一条活跃向量的维度等于查询维度),不是「读一行的维度」:混合维度库里 `LIMIT 1` 会抽错维(明明有可比的向量却报 `degraded`),首条恰好解析失败时又会漏报成 `full`。成本是一次无 `LIMIT` 的全 id 扫描 + 按匹配短路的解析:全库同维度(常态)时循环第一行就返回,只解析 1 个向量;只有一条都不匹配时才走完整个循环,而那正是要报 `degraded` 的情形,值得。解析复用 `getParsedEmbedding` 的缓存,缓存热不热取决于 `injectCandidates` 是否真的走了 vectorIndex 分支。 ### curl 示例 ```bash # 服务状态 curl -s -H "Authorization: Bearer $DSH_MNEME_TOKEN" http://127.0.0.1:8790/status # 列出最近 5 条记忆 curl -s -H "Authorization: Bearer $DSH_MNEME_TOKEN" \ "http://127.0.0.1:8790/memories?limit=5" # 新增一条决策记忆 curl -s -X POST http://127.0.0.1:8790/memories \ -H "Authorization: Bearer $DSH_MNEME_TOKEN" \ -H "Content-Type: application/json" \ -d '{"type":"decision","title":"采用 SQLite","content":"存储层使用 node:sqlite","importance":4,"tags":["存储"]}' ``` ### CLI 安装 插件自带零依赖 CLI(随 npm 包一起发布): ```bash npm i -g @modusensus/dsh-mneme dsh-mneme --help ``` 首次使用先配置服务地址与 token(也可用环境变量 `DSH_MNEME_URL` / `DSH_MNEME_TOKEN`,或 `--url` / `--token` 参数临时覆盖): ```bash dsh-mneme config set http://127.0.0.1:8790 <你的token> ``` ### CLI 常用命令 ```bash dsh-mneme status # 服务状态 dsh-mneme list --type project --limit 10 # 列出记忆 dsh-mneme search "部署流程" --mode vector --topk 5 # 语义搜索 dsh-mneme add --type decision --title "采用 SQLite" \ --content "存储层使用 node:sqlite" --importance 4 --tags 存储,决策 dsh-mneme get 42 # 查看单条 dsh-mneme delete 42 # 删除 dsh-mneme config show # 查看当前配置(token 打码) dsh-mneme share --dry-run # 分享包:只看会进几条、跳过几条,不落盘 dsh-mneme share --out pack.json # 写一个可外发的脱敏包(3 MiB 水位在源头拒) dsh-mneme receive pack.json # 吃包:默认干跑,一个字都不写 dsh-mneme receive pack.json --apply # 确认后真的吃进自己的库(整包一个事务,可重跑) dsh-mneme undo-share pack.json --apply # 按 pack_id 整包收回(物删,不是归档) ``` > 所有读取/写入命令支持 `--json` 输出原始 JSON;`config path` 打印配置文件路径(`~/.dsh-mneme/cli.json`)。 > 人类可读档只转达服务端文案里的 `hint` / `bytes` / `limitBytes`;闸门的 `detail`(坏在哪一格)与 > 413 的 `limit` 要 `--json` 才看得见——排查收包失败时先加它。 ### 独立服务(daemon,`dsh-mneme-serve`) 不启动 DSH 也能让记忆库对外服务:`dsh-mneme-serve` 在宿主之外组装同一套 lib,把数据面跑成常驻进程(路由与鉴权与「独立外部 API」完全同源)。适用场景:第三方集成(如 [Mneme-Bridge](https://github.com/SternChiri/Mneme-Bridge) 这类网页端桥接,见下方[社区适配器](#社区适配器))需要长期挂载记忆库,而 DSH 不必一直开着。 ```bash dsh-mneme-serve # 默认 ~/.dsh/memory + 8790 dsh-mneme-serve --memory-dir "D:\my mem" --port 8790 --host 127.0.0.1 dsh-mneme-serve --embed off # 纯关键词 + BM25(不取件模型) ``` - **鉴权与端口**:Bearer token 与 DSH 面板 / CLI 共用同一份(kv `external_api`,首次启动自动生成并持久化到 `memory.db`);端口/主机解析链与「外部访问」一致(显式参数 > 持久值 > 默认 8790/127.0.0.1)。配置端口被占会**直接报错退出**(不做端口顺延)——第三方把 URL 写死,静默换端口等于坏。因此 **daemon 与 DSH 的「外部访问」二选一**,不要同端口同开。 - **安全**:daemon 使用明文 HTTP,不提供原生 TLS。指定非回环 `--host` 时,请勿直接把服务暴露给不可信网络;远程访问请走 TLS 终止代理或 SSH 隧道。 - **能力边界(第一期,无 LLM)**:存储 / 检索(关键词 + BM25 + 向量)/ 镜像同步与人改合并 / `POST /maintenance/reclaim` / `POST /receive`(吃包入库与整包退出:结构落库 + 随提交排空嵌入,**不建实体**——外来行本来就不做抽取,见 [docs/SHARE.md](docs/SHARE.md))/ `/bootstrap` 全可用;巩固(autoDream)与蒸馏不在 daemon 内——巩固只属于 DSH 宿主进程,这是与宿主「单写者」的机械保证。`--embed` 默认 `local`(自管 runtime 与嵌入模型缺失时自动取件,约 200MB;失败降级关键词并打日志),也可选 `ollama` / `openai`(读宿主面板的 vector-config)/ `off`。 - **检索回执**:daemon 的 `/search` 同样落 `recall_runs`,第三方检索的复用统计不缺数。 - **生命周期**:stdout 仅就绪时打一行 `dsh-mneme-serve listening on http://host:port (pid N)`(供脚本解析实际端口),日志走 stderr;SIGINT/SIGTERM 优雅收库,Windows 强杀由 WAL 回放兜底。 - **已知限制**:与 DSH 同时运行属设计内场景(WAL 多进程并发),但去重是先查后写、库层无 UNIQUE 约束,双进程并发写同一 `(type, title, scope)` 有极小概率产生重复;daemon 与插件请同版本升级。细节与坑清单见 [docs/DAEMON.md](docs/DAEMON.md)。 ### MCP Server(任意 MCP 客户端接入) > Claude Code / Codex / Hermes / OpenCode / OpenClaw 等各客户端的最小挂载配置速查表见[根 README](../README.md#用在其他-ai-工具里mcp);本节是完整配置与安全说明。独立分发包 [`mneme-memory`](https://www.npmjs.com/package/mneme-memory)(bin `mneme-mcp`)已发布,与插件内置的 `dsh-mneme-mcp` 并存、挂载命令互相兼容。 插件自带 stdio MCP server(`bin/dsh-mneme-mcp.mjs`,零依赖,随 npm 包发布,bin 名 `dsh-mneme-mcp`)。任何支持 Model Context Protocol 的客户端(Claude Code、Cursor 等)挂载后即可获得与 DSH 内一致的记忆工具六件套:`memory_save` / `memory_search` / `memory_list` / `memory_get` / `memory_update` / `memory_delete`——工具名、参数与去重合并、重要性等语义与 DSH 内工具对齐(测试锁漂移)。 数据面走上面的**独立外部 API**(8790,Bearer):写入并发由 DSH 单点负责;DSH 未运行(外部 API 未启动)时 MCP 侧调用会报连接失败。scope 语义注意:外部 API 无会话上下文,`memory_save` 不做自动标注,只认显式 `agent_scope` / `workspace_scope` 声明。 配置优先级沿用 CLI 约定:环境变量 `DSH_MNEME_URL` / `DSH_MNEME_TOKEN` > `~/.dsh-mneme/cli.json` > 默认 `http://127.0.0.1:8790`。安全注意:服务默认只绑 `127.0.0.1`;若把 `DSH_MNEME_URL` 指向非回环的明文 HTTP 地址,Bearer token 将明文过网(启动时会有 stderr 警告)——远程场景建议走 SSH 隧道。 Claude Code 挂载示例(项目根 `.mcp.json`;token 在面板「设置 → 外部访问 API」查看): ```json { "mcpServers": { "dsh-mneme": { "command": "dsh-mneme-mcp", "env": { "DSH_MNEME_TOKEN": "<你的token>" } } } } ``` 未全局安装 npm 包时,把 `command` 换成 `npx`、加 `args: ["-p", "@modusensus/dsh-mneme", "dsh-mneme-mcp"]` 即可。 ## 社区适配器 以下项目由社区维护,把 mneme 接入第三方平台或宿主。它们不是本仓库的一部分,兼容性由各自维护者跟进——升级 mneme 后如遇接口变更,请优先查看对应项目的 release note。 | 项目 | 平台 | 说明 | |------|------|------| | [Mneme-Bridge](https://github.com/SternChiri/Mneme-Bridge) | DeepSeek 网页端 | 浏览器扩展(MV3)+ 本地 Node 服务:网页端会话蒸馏进 mneme 记忆库,DSH 侧记忆按话题回注网页对话,两侧共享同一份记忆。embedded(直接挂载 mneme lib)与 remote(standalone API,推荐配合 [`dsh-mneme-serve`](docs/DAEMON.md))双模式 | > 想被收录?在 [Discussions](https://github.com/slow-stack/mneme/discussions) 开帖介绍你的项目,或直接提 PR 补充本表。 ## 🏗️ 架构 ``` ┌─────────────────────────────────────────────────┐ │ 存储层:SQLite (archived/forgotten 状态) │ │ + Markdown 镜像(人工可编辑,双向同步) │ ├─────────────────────────────────────────────────┤ │ 服务层:saveWithDedupe / injectCandidates │ │ / mergeHumanEdits / onWrite 钩子 │ ├─────────────────────────────────────────────────┤ │ 模型接口:12 个工具 + 自动注入 + 会话摘要 │ ├─────────────────────────────────────────────────┤ │ autoDream:阈值调度 → LLM 决策清单 │ │ → 校验(fail-safe)→ 应用 → 摘要 │ ├─────────────────────────────────────────────────┤ │ Web 面板:设置面板内嵌 + 浏览/搜索(含向量) │ └─────────────────────────────────────────────────┘ ``` **源码结构**: ``` src/ ├── store.js # SQLite 存储(CRUD、搜索、归档/遗忘、schema 迁移、scope 四列) ├── mirror.js # Markdown 镜像(渲染/解析,人工优先) ├── service.js # 领域逻辑(去重合并、注入筛选、写入钩子、scope 加权/硬过滤、冲突队列) ├── scope.js # 作用域解析(agentPreset + workspace registry 反查,issue #17) ├── config.js # schemastery 配置 schema ├── tools.js # 12 个模型工具(defineTool;memory_organize 要 organizeEnabled 打开才注册,开了是 13 个) ├── user-settings.js # 「用户设置」注入块(人格 → 画像 → 规则)的唯一渲染处,注入与面板预览共用 ├── profile-extract.js # 从活跃记忆提取「助手眼中的用户」画像草稿(只出草稿,不写 user_settings) ├── organize.js # agent 主动整理:dryRun 比对报告 → agent 判断 → apply 落库(筛除=归档不删,留 dream_runs 回执) ├── inject.js # systemPrompt.context 动态注入(含花括号转义) ├── summarize.js # 会话结束 LLM 摘要 ├── dream.js # autoDream 调度 + runDream(LLM 决策 + 摘要) ├── dream/decisions.js# 决策校验(fail-safe)+ 决策应用 ├── dream/clustering.js # 候选集聚类(k-means++ seeding) ├── dream/sleep.js # Sleep Mode 分层压缩 ├── entities/extractor.js # 实体抽取器(v0.3.0:LLM JSON 抽取 + 去重 + fail-safe) ├── search/bm25.js # BM25 稀疏召回(v0.5.0:分词 + IDF 索引) ├── search/adaptive.js# 自适应相似度阈值(v0.5.0) ├── hot-memory.js # 会话级短期热记忆(v0.5.0:滚动轮次 + token 预算;注入剥离 reasoning) ├── runtime/ # 自管本地推理运行时(收编 / 本地 .tgz / registry 三档取件 + 校验) ├── embedding.js # OpenAI 兼容 embeddings 客户端 + 向量检索 ├── api.js # HTTP 路由(Web 面板数据通道,含 /conflicts 冲突队列) └── index.js # 插件接线 lib/ # src 的同步分发产物(npm run sync,prepack 钩子在发布时自动跑;一致性由 scripts/check-sync.js 在 CI 与 release.yml 校验——它只查不同步;唯一手写例外 lib/client.js——Web 面板 bundle,sync 不覆盖) test/ # 3446 个 node:test 测试(审计与三轴线压测不变量;src↔lib 一致性由 scripts/check-sync.js 发布闸门校验) scripts/ # e2e-dsh.js 端到端演示 · stress-dsh.js 三轴线压测 · sync-lib.js 同步 · check-sync.js 发布闸门 · benchmark-recall.js / benchmark-embed.js / benchmark-rerank.js 基准 · sync-test-badge.mjs 测试徽章 · build-runtime-manifest.mjs 运行时清单 ``` ## 🧪 开发 ```bash cd dsh-mneme npm install # 安装 peer 依赖(以 devDependencies 形式,用于本地测试) npm test # 运行 3446 个测试 npm run stress # 三轴线压测:长会话检索 / 冲突仲裁 / 多 Agent 并发(离线 mock LLM) npm run sync # 把 src/ 同步到 lib/(发布时由 prepack 钩子自动执行) ``` > 压测(`npm run stress`)三条轴线:**长会话检索**(Recall@k、陈旧残留率)、**冲突裁决**(可重放仲裁集:审计快照 hash + receipt + 幂等回放)、**多 Agent 并发**(丢更新、重复合并、事务/崩溃恢复)。每次 autoDream 运行都会写入审计表 `dream_runs`(输入快照 digest + 决策清单 + 逐 id 去向 + receipt),让高通过率下也能定位静默错误。 > `lib/` 是 `src/` 的同步分发产物(`npm run sync`):除 **`lib/client.js`**(Web 面板 bundle)为 lib 独有的手写文件、sync 与 check-sync 均不触碰外,其余全部由 src 复制而来。改 src 必 `npm run sync` 并提交 lib,否则发布产物静默失效(issue #65 教训)。 ## 📄 设计文档 > 设计文档在 `dsh-mneme/docs/`(本目录内,直接相对链接);只有能力对比表在仓库根 `docs/`,用 `../docs/` 指过去。GitHub 上从本目录打开均可正常跳转。 - [配置说明(全键参考)](docs/CONFIGURATION.md) - [蒸馏原文留存与记忆级溯源](docs/DISTILL.md)(#399) - [实体结构化记忆设计](docs/ENTITIES.md) - [语义增强架构](docs/SEMANTIC.md) - [本地模型部署指南](docs/LOCAL_MODEL.md) - [工作流(M.O.):本体住项目,证据住记忆库](docs/WORKFLOWS.md) - [连续性抢救(#249 N3):触发器口径与事件契约](docs/CONTINUITY.md) - [Sleep Mode:空闲四阶段深度维护](docs/SLEEP.md) - [存储与回收:表结构、保留期、`dsh-mneme reclaim`](docs/STORAGE.md) - [选择性遗忘与事实更正:行粒度契约与失效关系缺口(#532)](docs/FORGET.md) - [记忆分享包:出包与收包的口径、信任边界与实测(`dsh-mneme share` / `receive` / `undo-share`)](docs/SHARE.md) - [独立服务 daemon(`dsh-mneme-serve`)](docs/DAEMON.md) - [与 Zep / Graphiti / Mem0 / Letta / Hindsight 的能力对比](../docs/COMPARISON.md) ## 🙏 致谢 autoDream 的理念溯源(理念借鉴、实现原创): - **[Claude Code 的 Auto Dream](https://code.claude.com/docs/en/memory)**(Anthropic,Memory 2.0):理念源头——会话间隙由后台子代理整理记忆文件(去重、修矛盾、清衰减)。 - **[Sleep-time Compute: Beyond Inference Scaling at Test-time](https://arxiv.org/abs/2504.13171)**(UC Berkeley & Letta,arXiv:2504.13171):Auto Dream「离线巩固」思想背后的学术脉络。 - **cc-haha**:早期实现思路的参照之一。 在上述工作之上,dsh-mneme 做了自己的工程发展:K-Means++ 聚类预分组(`src/dream/clustering.js`)、类型化决策清单(keep / merge / archive / conflict / update,及 sleep 侧 supersede / differentiate)与可回放的 sha256 摘要审计链(`dream_runs` / `receipt_chain`)。如有遗漏的灵感来源,欢迎提 issue 指出。 ## 📚 相关研究 dsh-mneme 的机制设计与下列公开研究相互印证(以下引用均逐一核对过 arXiv 收录页;机制对应关系为我们自己的映射,非原论文观点): - **在线写入 + 离线巩固**:[Dual-Layer Agentic Memory with Fast Write Routing and Slow Consolidation](https://arxiv.org/abs/2608.22215)(arXiv:2608.22215)——与 autoDream 的「快写慢巩固」分工同构。 - **热度衰减与访问强化**:[FadeMem](https://arxiv.org/abs/2601.18642)(arXiv:2601.18642)——heat 记忆热度模型(指数衰减 × 访问强化 × 重要性调制)的学术近亲。 - **冲突裁决与状态追踪**:[StateMemBench](https://arxiv.org/abs/2608.19652)(arXiv:2608.19652)——记忆系统追踪演化状态的考卷;C1 冲突裁决(supersede 后不再引用旧值)以其为验收靶子。 - **何时该忘 / scope 门控**:[PersistBench](https://arxiv.org/abs/2602.01146)(arXiv:2602.01146);呈现层缓解见 [Structured Memory](https://arxiv.org/abs/2608.08300)(arXiv:2608.08300)——scope 语义域门控的问题出处与零契约变更改法。 - **领域全景**:[Write–Manage–Read 综述](https://arxiv.org/abs/2603.07670)(arXiv:2603.07670)——「写入 / 管理 / 读取」三段式与插件整体形状互证。 - **数月尺度的可靠性工程**:[Memory as Infrastructure](https://arxiv.org/abs/2609.05510)(arXiv:2609.05510)——与 sha256 审计链、fail-safe 降级同属「记忆即基础设施」路线。 - **相邻系统(对照)**:[Zep](https://arxiv.org/abs/2501.13956)(时序知识图谱)、[Mem0](https://arxiv.org/abs/2504.19413)(生产级抽取式记忆)、[Hindsight](https://arxiv.org/abs/2512.12818)(retain / recall / reflect 三操作)走「写入时 LLM 抽取 + 向量/图检索」路线;dsh-mneme 的差异点在于:巩固由 LLM 参与但产物全程留痕可回放,冲突冻结待人工复核,记忆只落本机。 ## 📜 License MIT ## 🤝 参与之前 行为守则:[CODE_OF_CONDUCT.md](../CODE_OF_CONDUCT.md)。它管三件事——**署名归人**(模型不进作者行)、**责任落在签下作者行的人身上**、**发言权按证据分配,不按身份**。参与 issue / PR / 评审即受其约束;使用本插件不受约束,MIT 已经授权。