前言¶
DSH 自带的 session_search 可以在历史对话里翻原文,但它记的是「原文」;Myosotis 记的是「结论」:你是谁、你们约过什么、这个仓库有什么坑、上次那件事最后怎么收的场。
三层记忆解决了两个常见问题:一是长期偏好和约定容易随会话消失,二是不同项目之间的记忆混在一起。Myosotis 把记忆按「生命周期」拆成全局、项目、会话三层,每层独立存储,按需注入。
是什么¶
dsh-myosotis 是给 DeepSeek Harness(DSH)的内存插件,维护者是 KurohaneKaoruko,分类为「记忆」。它为 DSH 提供三层持久记忆:全局、项目、会话。每轮对话时,插件会根据当前话题从记忆库里挑几条背景资料注入上下文;模型通过三个工具维护这些记忆,用户可以直接读写 MEMORY.md。
零依赖、零模型调用:检索采用词法匹配加权打分,不依赖向量检索或外部 embedding 服务;注入在请求路径上是毫秒级的,不增加网络请求开销。
三层记忆¶
| 层 | 放什么 | 存在哪 | 生命周期 |
|---|---|---|---|
| global 全局 | 用户偏好、习惯、身份、你们之间的经历与相处方式 | <DSH_HOME>/memory/global.json |
永久,跟着人走 |
| project 项目 | 技术栈、开发规范、架构决策、踩过的坑 | <cwd>/.dsh/memory/project.json |
永久,跟着仓库走,可提交进 git |
| session 会话 | 这次对话的临时线索、自动捕获的「记住…」 | <DSH_HOME>/memory/sessions/<id>.json |
默认 30 天过期 |
三层的区别不是「重不重要」,而是该跟谁走:换个项目还成立的写 global,只有这个仓库成立的写 project,只在这次对话成立的写 session。
安装与启用¶
插件放在 ~/.dsh/local-plugins/,并在 profile 里登记。仓库自带安装脚本:
node tools/install.mjs # 安装到 desktop profile
node tools/install.mjs --dry-run # 只看会改什么
node tools/install.mjs --uninstall
装完重启 DSH 生效。手动安装等价于三步:
- 把本目录复制到
~/.dsh/local-plugins/dsh-myosotis ~/.dsh/profiles/desktop/package.json里加依赖
"dsh-myosotis": "link:C:/Users/<你>/.dsh/local-plugins/dsh-myosotis",
并在dsh.profile.bundles数组末尾加"dsh-myosotis"- 在
~/.dsh/profiles/desktop/node_modules/下建一个指向第 1 步目录的链接
三个工具¶
| 工具 | 作用 | 关键参数 |
|---|---|---|
memory_save |
新增 / 更新 / 跨层提升 | text、scope、kind、importance、pinned、affect、date、id |
memory_search |
检索;不确定时先查 | query、scopes、limit |
memory_forget |
删除;按关键词批量删要先确认 | id / query + confirm |
memory_save 给 id 是更新,给 id 且 scope 不同就是提升:会话里自动捕获的线索,模型判断值得长期保留后,用同一个 id 把它提到 project 或 global。
kind 共九种:profile(身份)、preference(偏好)、habit(习惯)、convention(约定)、decision(决策)、fact(事实)、gotcha(坑)、episode(经历)、bond(关系)。
配置¶
在 profile 的 cordis.patch.yml 里覆盖,重启生效:
- id: memory
name: dsh-myosotis
config:
budgetChars: 3000 # 每轮注入的字符预算
sessionTtlDays: 14 # 会话记忆过期天数,0 = 不过期
autoEpisode: true # 日记模式:每轮自动记一条流水
| 键 | 默认 | 说明 |
|---|---|---|
enabled |
true |
总开关;关掉后磁盘上的记忆原样保留 |
globalDir |
'' |
全局目录;空 = <DSH_HOME>/memory |
projectDir |
.dsh/memory |
项目目录,相对工作目录 |
inject |
true |
是否注入记忆 |
policy |
true |
是否注入「记忆使用规则」段落 |
budgetChars |
2400 |
每轮注入的字符预算 |
maxGlobal / maxProject / maxSession |
400 / 300 / 120 |
各层条数上限,超出按保留分数淘汰 |
sessionTtlDays |
30 |
会话记忆过期天数 |
autoCapture |
true |
自动捕获「记住 / 以后都 / 不要再」这类明确指令 |
autoEpisode |
false |
日记模式(噪声大,默认关) |
episodeMinTools |
2 |
日记模式的工具调用门槛 |
markdownMirror |
true |
生成 MEMORY.md 镜像 |
它是怎么工作的¶
注入:每轮重新排序,但不抖动¶
memory:recall 是一个动态段落——systemPrompt.section() 的 text 传函数,每次组装提示词时按「当前这轮用户说了什么」重新选条。
选条的打分:
score = 词法相关性 × 2.4 # IDF 加权,标签 3 倍、标题 2 倍、正文 1 倍
+ 重要度 / 5 × 0.7
+ 新近度 × 0.3 # 半衰期 45 天
+ 使用频次 × 0.2 # 对数饱和,最多 +0.2
+ 置顶 × 0.45
+ 情绪强度 × 0.15
然后按字符预算截断,置顶 / 身份 / 关系类永远先入选——它们回答的是「这个人是谁、我们是什么关系」,不该因为话题不相关就消失。
同一轮里结果会被缓存:只要记忆没变、用户没说话,连续几步(读文件、跑测试、改代码)注入的都是同一段文本,不会把前缀缓存打碎。
检索:为什么不是向量¶
记忆注入在每一轮请求的组装路径上。这条路要求:同步、毫秒级、不发网络请求、结果可预测。向量检索要 embedding 调用,这三条全破。
词法检索 + IDF 加权在「几十到几百条人工维护的记忆」这个量级上够用:记忆本来就是人写的短句,用词重叠度很高。代价是同义改写检索不到——所以 memory_search 工具留给模型自己换词重试。
捕获:规则负责发现,判断留给模型¶
自动捕获只做高置信度的事:用户说「记住 / 以后都 / 不要再 / 我习惯 / 我们约定」时,把那一句落到 session 层。
不直接写 global/project 的原因:记错一条偏好,比少记十条更伤。规则能判断「这句话是在下指令」,判断不了「这条该跟人走还是跟仓库走」——后者需要上下文,交给模型。
「你还记得吗」这类问句不会触发捕获(NOT_A_COMMAND 拦掉)。
存储:同步读、异步写、原子替换¶
- 读发生在提示词组装路径上,必须同步——文件很小(几百条),一次
readFileSync可以接受 - 写走 120ms 防抖 + 「临时文件 + rename」原子替换,Windows 上对
EPERM/EACCES/EBUSY做短暂重试 - 一个进程内一个文件只有一个实例;多开窗口时是「最后写入者胜」,但任何时刻文件都是完整 JSON
情感记忆¶
这一层是给「把 Agent 当人相处」的用法准备的,但不需要 Agent 扮演任何角色——它只是让记忆带上当时的分量。
kind: episode记共同经历:「2026-10 一起把记忆插件的检索从分作用域 IDF 改成全局 IDF,他发现排序问题时挺兴奋」kind: bond记相处方式:「他讨厌客套和总结陈词,直接说结论就行」affect: { label: '挫败', intensity: 4 }给记忆标情绪重量;情绪强度会进检索分数date: '03-14'记每年重复的日子;注入时会显示「每年 03-14」
bond 和 profile 一样属于「永远先入选」的那一类——它们不该因为「这轮在聊构建脚本」就被排到预算外面。
提示词里的规则段落会要求模型:阶段性任务收尾、或与用户有一次值得留存的交流后,写一条 episode。什么时候值得记,是判断,不是规则——这部分交给模型,插件只负责让它记得住、找得回、不过期。
维护¶
命令行(不开 DSH 也能用)¶
node tools/memory.mjs stats
node tools/memory.mjs list --scope project
node tools/memory.mjs search 提交信息
node tools/memory.mjs add "提交信息一律用中文" --scope project --kind convention --importance 5
node tools/memory.mjs pin m_xxx
node tools/memory.mjs rm m_xxx
node tools/memory.mjs export --scope global
node tools/memory.mjs path
和插件读写同一批文件,改完下一次组装提示词就生效。
直接读 Markdown¶
每次写入后会同步生成 MEMORY.md(全局一份、每个项目一份):
# 全局记忆
> 由 dsh-myosotis 自动生成,是 JSON 的只读投影;手改会在下一次写入时被覆盖。
> 要增删改,请让 Agent 用 `memory_save` / `memory_forget`,或运行 `node tools/memory.mjs`。
最后更新:2026-10-24 18:15 · 共 2 条
## 身份
- 用户叫 Kaoruko,喜欢被直呼名字 ★
## 关系
- 深夜写代码时容易焦虑,先给结论再解释细节
它是只读投影,手改会在下一次写入时被覆盖——文件头里也这么写着。行尾的 HTML 注释是 id,方便对着 CLI 用。要改记忆,走工具或 CLI。
文件位置¶
<DSH_HOME>/memory/
├── global.json # 全局记忆
├── MEMORY.md # 全局记忆的可读镜像
├── sessions/
│ └── <sessionId>.json # 每个会话一份,默认 30 天过期
└── projects/
└── <hash>.json # 工作目录不可写时的项目记忆回退
<cwd>/.dsh/memory/
├── project.json
└── MEMORY.md
适用场景与注意¶
适合需要在多会话、多项目之间保持一致偏好、约定和踩坑记录的场景,比如长期协作的开发者。插件以当前 dsh 进程权限运行,安装前建议检查源码与许可证。
短结尾¶
Myosotis 用三层记忆和确定性检索,让 DSH 在不同会话与项目间保持连续性,同时保持低开销。相关目录页与源码:SkillHub 目录页、GitHub 仓库。