Mnemosyne
这次,我会给所有大肥鱼一个完整的人生。
Mnemosyne 是希腊神话里的记忆女神,九位缪斯之母。这个名字取的就是她的职能—— 让发生过的事留下来,并在需要的时候被想起来。
这是一个给 DeepSeek Harness 用的长期记忆插件。它把你和 AI 的每一轮对话自动收进一个 本地记忆库,之后每次对话开始时,只把「你是谁 + 可能相关的几件事」放进上下文, 剩下的全文留在磁盘上,需要时再展开。
⚠️ 先说清楚一件事
这个插件会不可避免地降低缓存命中率,请不要在工作场景使用。
原因很直白:它每一轮都会往上下文里注入新内容,而这部分内容随对话变化—— 缓存最怕的就是前缀不稳定。具体能降多少取决于你的对话模式、注入预算和话题切换频率, 我没法给你一个准确数字,但降是必然的。
如果你的场景对成本或延迟敏感(批量任务、长文档处理、生产环境),
关掉注入(inject: false)只用它的归档与检索,或者干脆别装。
它解决什么问题
AI 没有跨对话的记忆。每次开新会话,你都从零开始:重新介绍项目、重新说明偏好、 重新解释上次那个坑为什么不能踩。
把对话全量塞进上下文是个办法,但代价很直接——上下文有上限,塞满之后该记得的反而记不住。
Mnemosyne 的做法是分层:
| 层 | 内容 | 何时进上下文 |
|---|---|---|
| 事实 | 称呼、偏好、路径这类稳定状态 | 每轮(框架的一部分) |
| 人设框架 | 从记忆里归纳出的「你是谁」 | 每轮常驻 |
| 话题 / 索引 | 过去发生过的事,一行一件 | 按当前提问检索 |
| 全文 | 你当时说的话、我当时的回答 | 留在磁盘,按 id 展开 |
框架常驻、索引按需、原文留库。这样注入量是可控的,而信息随时能取回来。
装
dsh plugin --profile web add github:i11ustriouS/dsh-mnemosyne
装完重启 DSH(Host 侧代码要重新加载),然后刷新页面。侧栏底部会出现「记忆女神」入口。
零第三方依赖,没有构建步骤——只用 Node 内置模块,装完即用。
它自己会长
记忆库不是让你手填的,它随对话自己长:
对话 → 每轮归档(顺带抽事实)
→ 攒够 personaEvery 条,把最近 60 条记忆 + 现有事实喂给模型归纳
→ 归纳结果走同一条事实管道(冲突消解)
→ 注入时投影成框架
归纳看的是一段时间的整体,所以能得出单轮看不出来的东西:稳定的偏好、反复出现的诉求、 你说话的方式。模型只被要求输出有证据的内容,宁可少也不能编。
框架页签顶部有「让 AI 重新归纳」按钮,想手动触发随时可以。
事实与冲突消解
「用户叫老王」是状态,「今天修了个 bug」是事件。混在一个池子里有两个后果: 稳定信息被流水淹没;用户改口后新旧值并存,检索时同时召回两个矛盾答案。
所以拆成两个库,事实层用槽位做冲突消解:
| 谓词类型 | 例子 | 行为 |
|---|---|---|
| 单值 | nickname name calledBy persona role model language |
新值写入 → 旧值失效 |
| 多值 | preference personality path tag skill habit |
各自成槽位,只去重不覆盖 |
| 否定极性 | 「以后别叫我老王」 | 不参与注入,只让匹配的那条失效 |
"以后叫我老王" → user/nickname = 老王
"叫我王哥" → user/nickname = 王哥,老王那条自动失效
"以后别叫我老王" → 只让"老王"失效,不影响当前称呼
三道防污染闸门
让模型自己写自己的人设,最大的风险是自己污染自己。所以归纳链路有三道闸:
一、事件只收长期约束。 模型很容易把「这次改了什么」当成里程碑。
prompt 里给了判据(这条信息三个月后还有用吗?),代码层再用规则兜底,
识别 自检 75/75 通过、修复了…bug、压缩 1034→80 行 这类工程流水。
二、拦截框架回流。 注入到系统提示的框架文本,有时会被会话摘要捕获,变成一条新记忆—— 再喂回归纳就成了自我强化的正反馈。识别并拒绝。
三、同一件事只留一条。 每次归纳的措辞都不同(全程离线不联网 / 要求全程离线不联网),
只比字符串会让它每轮追加一条。改为按共享实词判重。
但路径不做模糊合并——父目录和子目录是两个有效目录。
实测:24 条堆积事件 → 3 条,框架注入从 1391 字降到 1212 字。
话题聚合
「每轮一条流水」是记忆的反面:同一件事聊十二轮就散成十二条,检索时互相稀释。
所以按 同一会话 + 相邻间隔 < 2 小时 把轮次聚成话题:
2026-10-22 10:25~13:09 · 4轮 · 决策 — alerts.log / 第三方应用测试 | id=mv-xxx(含:…)
一行说清一件事,点开才是细节。话题标题优先取实体(项目名、文件名、域名), 没有实体才退回关键词。
跨会话不合并是刻意的:误合并会把两件不相干的事糊在一起,比分成两个话题更难纠正。
记忆固化
一直追加的结果是库越来越吵:同一件事反复说,近似条目互相稀释; 几个月前的琐碎轮次和昨天的决定同权竞争。
固化让库随时间变密而不是变吵:
| 动作 | 触发条件 | 结果 |
|---|---|---|
| 合并 | 同话题内摘要相似度 ≥ 0.82 | 保留信息最全的一条,其余归档 |
| 摘要 | 话题 ≥ 5 轮 且 结束 ≥ 3 天 | 压成一条 digest,原始轮次指向它 |
| 归档 | ≥ 30 天 且 从没被召回 且 非决策类 | 退出自动召回 |
全程可逆:只打标签、只新增,原文永不删除。归档条目仍能被手动搜到;
memory_recall(digestId) 会连带展开被压缩的原始轮次要点。
摘要条目在检索里有 1.3× 权重——同样命中一次,一整段对话的信息量大于单轮。
实测:74 条记忆 → 5 条摘要 / 归档 46 条,活跃区降到 33 条;再跑一次计划全 0(幂等)。
存储压缩
存档是追加式的:每轮归档、每次更新摘要、每次打标签、每次召回计数都写一行。 所以 80 条记忆会积到 1034 行、4.7 MB——内容没变,全是同一批条目被覆盖的中间版本。
压缩把它重写成「每条一行最终状态」:
条目:1034 行 → 80 行 4.7 MB → 355 KB(-92%)
事实: 31 行 → 17 行
安全措施:原子替换(失败自动回滚)、并发检测(压缩期间有新写入就重来)、 回放结果与压缩前完全一致。
历史回填
sessions/ 里已有的历史会话可以整个导进来,不用重新聊一遍。
- 面板:记忆页签 → 「导入历史」
- CLI:
node test/backfill.mjs [会话数] [每会话轮数] [--dry]
实现上有个坑:会话日志是多帧追加的 zstd(一个 1.6 MB 的文件里可能有 600+ 个独立帧), 得按 zstd magic 切帧、逐帧解压再拼,才能拿到完整事件流。
回填不调用模型——几百轮逐条总结会烧掉大量 token,规则摘要足够让它"能被检索到"。
几个设计选择
记忆强度用对数。 每条记忆记 hits(被召回次数),召回升权是 1 + ln(1+hits) × 0.12。
反复被用到的会略微靠前,但不会盖过内容相关性——否则库里只会剩下那几条老面孔。
显示与检索分离。 中文三元组关键词保留在索引里参与匹配,但不渲染出来。 检索需要它,看的人不需要。
召回时顺带想起。 只按关键词匹配会漏掉同一件事的其它轮次,所以主命中之外还会带出 同会话相邻轮次、共享实体的条目。这些分数低于主命中,不会挤掉真正相关的结果。
自动召回跳过 test 与 archived;要包含就显式传 skipTags: []。
三个入口
面板
侧栏底部「记忆女神」→ 全屏面板,四个页签:
- 记忆 —— 搜索、筛类型、展开原文、手动记一条、导入历史、固化
- 人设框架 —— 看 AI 对你的认知,也能自己改;顶部有「让 AI 重新归纳」
- 事实 —— 当前生效的稳定事实,可手动断言/失效
- 召回预览 —— 输入一句话,看这轮实际会注入什么
第四个页签是我自己最常用的:调注入效果时,能直接看到模型实际收到了什么。
工具
| 工具 | 用途 |
|---|---|
memory_search |
检索索引(返回摘要 + id,不含全文) |
memory_recall |
按 id 取原文(摘要条目会连带展开轮次要点) |
memory_note |
手动记一条 |
memory_profile |
读/写人设框架 |
memory_facts |
读/断言/失效稳定事实 |
HTTP API
GET /mnemosyne/api/summary 概览
GET /mnemosyne/api/entries 条目列表
POST /mnemosyne/api/search { query } 检索
POST /mnemosyne/api/get { id } 取原文
POST /mnemosyne/api/note { text } 记一条
POST /mnemosyne/api/delete { id } 删除
POST /mnemosyne/api/profile { profile } 写人设框架
POST /mnemosyne/api/preview { query } 看注入文本
GET /mnemosyne/api/topics 话题列表
GET /mnemosyne/api/facts?all=1 事实
POST /mnemosyne/api/facts { action } 断言 / 失效
POST /mnemosyne/api/persona/rebuild 重新归纳框架
GET /mnemosyne/api/persona/status 归纳状态
POST /mnemosyne/api/consolidate 固化({ dryRun } 预览)
GET /mnemosyne/api/consolidate/status 固化状态
POST /mnemosyne/api/compact 压缩存储
POST /mnemosyne/api/backfill 导入历史
GET /mnemosyne/api/backfill/status 导入进度
数据在哪
默认 $DSH_HOME/mnemosyne。
mnemosyne/
entries/2026-10.jsonl 情景记忆:按月分片,逐行追加
facts.jsonl 语义记忆:断言/失效/刷新操作流
profile.json 手工维护的人设框架
state.json 运行时状态
测试脚本按 MV_DATA_DIR → $DSH_HOME/mnemosyne → ~/.dsh/mnemosyne 的顺序找数据目录。
配置
- id: mnemosyne
name: dsh-mnemosyne
config:
dataDir: '' # 留空用 $DSH_HOME/mnemosyne
inject: true # 关掉就只记录不注入
maxInjectChars: 2600 # 注入预算
autoSummarize: true # 关掉就只用规则摘要,零模型调用
summarizeProvider: '' # 留空用当前默认模型
summarizeModel: ''
summarizeTimeoutMs: 25000
maxEntries: 20000
maxRecall: 8 # 单次最多带回几张索引卡片
recordSubagents: false
autoPersona: true # 框架是否随对话自行生成
personaEvery: 10 # 每新增多少条记忆触发一次归纳
autoConsolidate: true # 是否定时固化
consolidateMinAgeDays: 30 # 归档门槛
consolidateDigestAgeDays: 3 # 摘要门槛
已验证 / 未验证
自检 115 项(node test/selftest.mjs,纯离线,不碰真实库),另有实时验收脚本
(node test/verify-live.mjs,对着运行中的 DSH 打)。
覆盖到:
- 分词、停用词、滑窗碎片过滤、实体抽取、事件类型判定、规则摘要
- 规则事实抽取(
叫我老王→ 断言、别叫我老王→ 否定、我喜欢X/不喜欢Y→ 偏好正负) - 冲突消解:同槽位新值让旧值失效、同值只刷新、否定只命中匹配值、多值并存、已失效不参与判定
- 存储回放、同 id 覆盖、删除、重启恢复、损坏行隔离、超限淘汰、压缩后内容一致
- 中文/英文检索命中、不相干查询零误报、同会话延伸、共享实体延伸、空库不崩
- 模型总结的 JSON 解析(裸 JSON / 代码块 / 尾逗号)、失败降级、超时降级
- 注入渲染、预算约束、无内部细节泄漏
- 话题聚类、话题卡片、同话题多轮合并
- 多帧 zstd 解码、回填端到端与去重
- 人设归纳:草稿过滤、事实进冲突消解、条数上限、空库安全、间歇故障重试 5 次
- 防污染:路径事实只认真路径、框架回流被拒、开发流水被过滤、同一件事按共享实词归并
- 固化:相似度、同话题并组、跨话题不合并、归档三重判据、digest 门槛、幂等
- 压缩:追加流重写、事实库失效状态保留
没验证到的:真实的多用户场景、长期运行的稳定性、超大库(万条以上)的检索性能。 我的测试都在单机单用户的几十到几百条规模上。
已知短板
- 纯字面检索:
上次那个这类指代无法解析,需要结合最近几轮做 query expansion 或向量召回。 - 摘要质量依赖规则:digest 现在是"挑要点拼接",没有真正的模型归纳。 高价值话题可以考虑用模型精炼一次。
- 话题聚类按时间切:时间相近但主题不同的对话会被并进同一个话题 (实测踩过:Wallpaper Engine 那段掺进了渗透测试的内容)。应该引入实体相似度做二次切分。
- 事实抽取覆盖率低:规则只认明确句式,模型调用失败时基本抽不到。
- 归档条目仍保留全文:每条约 4.4 KB。库涨到几千条时体积会到几十 MB, 可以对归档条目截断正文字段。
⚠️ 再强调一次
这个插件会不可避免地降低缓存命中率。
它每轮都往上下文里注入随对话变化的内容,而缓存最怕前缀不稳定。 具体影响取决于你的使用方式,但降是必然的。
- 对成本/延迟敏感 → 别装,或设
inject: false只用归档检索 - 个人长期使用、聊天为主 → 影响可以接受,记忆带来的收益通常值得
- 生产环境 / 批量任务 → 不要用
开发
node test/selftest.mjs # 115 项离线自检
node test/preview.mjs "你那句话" # 看注入效果(读真实库 + 当前源码,不用重启)
node test/verify-live.mjs # 对着运行中的 DSH 验收
node test/backfill.mjs [会话数] [--dry] # 导入历史会话
node test/consolidate.mjs [--apply] # 固化
node test/compact.mjs [--apply] # 压缩存储
client.js 改完刷新页面即可;index.js 与 lib/*.js 是 Host 侧代码,改完要重启 DSH。
⚠️ 上面几个写数据的脚本会写 $DSH_HOME 下的记忆库,从沙箱子进程执行会被 EPERM 拦,
需要提权或改用面板按钮。
MIT License · 与 DeepSeek 无隶属关系 · 插件由作者维护,收录不构成背书
No comments yet. Be the first to write one.