tluoluo
dsh-persist
Persistent memory for DeepSeek Harness: per-conversation notes, selective injection, project memory, semantic Vault search + automatic retrieval.
- Stars
- 1
- Language
- TypeScript
- Created
- Aug 15, 2026
- Updated
- Aug 16, 2026
Introduction
dsh-persist
English | 简体中文
给 DeepSeek Harness 的 agent 装上"不会失忆"的长期记忆。 Persistent memory for DeepSeek Harness agents.
它解决什么问题
默认情况下 DSH 的 agent 换对话就失忆——上下文一关,什么都不记得。dsh-persist 把记忆做成 文件系统上的分层结构,每个对话按需注入:
| 层 | 存储 | 对标 | 怎么被读取 |
|---|---|---|---|
| 用户画像 + 长期记忆 | USER.md / MEMORY.md | 长期记忆(LTM) | 每对话可选注入 |
| 关键记忆 | memory.json | 速查卡片 | 每对话可选注入;工具读写 |
| 对话记忆 | sessions/<id>/memory.md | 工作记忆 | 只注入本对话 |
| 项目记忆 | projects/<key>/memory.md | PARA 的 Project 层 | 按项目勾选注入 |
| Vault 语义记忆 | vault.md + vault.db | Zettelkasten 卡片盒 | 按需语义召回(工具或「自动检索」开关,不静态注入) |
特性
- 对话独有记忆——每个对话一本"不会丢的笔记本",其他对话看不到、不注入
- 选择性注入——勾选什么才注入什么;新对话默认零注入,不占上下文
- 项目记忆互通——同一工作目录共享项目经验;命名项目可把同目录的多项目分开
- Vault 语义检索——配一个免费 key 就能"按意思"找记忆;不配自动降级为关键词搜索
- 自动召回(按对话开关)——每轮按你的消息自动检索相关记忆注入;记忆 tab 里勾选「自动检索」才开,新对话默认关(与静态注入同一心智)
- 记忆 tab——对话顶部可视化编辑 + 注入配置 + 实时预览(所见即所得)
- 全部纯文本——
~/.dsh-memory/下每个文件人类可读可改,随时备份、迁移、导出
快速上手
dsh plugin --profile web add dsh-persist # 1. 安装
# 2. 重启 DSH(dsh --profile web)
# 3. 打开对话 → 顶部「记忆」tab → 勾选要注入的块
想让 agent 记住什么,直接对它说"记住这个",它会自动写入本对话记忆。
要开语义搜索:在 硅基流动免费申请 key,设置环境变量
SILICONFLOW_API_KEY=... 后重启即可(不配也能用,自动降级为关键词搜索)。
怎么装?
曾用名
dsh-memory(npm 名已被占用,故改名dsh-persist)。存储路径~/.dsh-memory/、路由/dsh-memory/不变,旧数据无需迁移。
环境要求:Node.js >= 22.6(推荐 24+;测试用 --experimental-strip-types 自 22.6 起可用)。Vault 层用内置 node:sqlite:23.4+ 默认可用,22.6–23.3 需 --experimental-sqlite 标志;更低版本插件照常加载,仅 Vault 工具自动降级禁用(记忆/注入/UI 不受影响)。宿主为 DeepSeek Harness(需提供 tools / systemPrompt / webServer / sessions / agents 服务与 conversation.view UI 槽位)。
dsh-persist 是一个标准 DSH 组合包(bundle):声明了 dsh.bundle,通过 dsh plugin 装进任意 profile。@deepseek-ai/* 是 optional peer 依赖,由 DSH 宿主在运行时提供,npm 不会、也不需要安装它们。
从 npm 安装(自带构建好的 lib/,无需构建):
# 装进你的 web profile(首选)
dsh plugin --profile web add dsh-persist
# 或装进别的 profile
dsh plugin --profile demo add dsh-persist
安装后重启(dsh --profile web),插件即生效。
从 GitHub 安装(会拉源码并跑 prepare 构建,见下文):
dsh plugin --profile web add github:tluoluo/dsh-persist
从 git 安装时 pnpm 在首次
add后可能需要你授权运行prepare构建脚本:把dsh打印的包键复制进 profile 的pnpm-workspace.yaml的allowBuilds,再重跑add(详见 DSH 官方文档 publish.md)。
可选环境变量(全部非必需,装完就能用):
SILICONFLOW_API_KEY=...→ 可选,开启"语义搜索"。bge-m3 会把记忆转成向量、按意思找(比纯关键词更懂你)。不配也能用:Vault 记忆自动降级成关键词搜索,功能不受影响。在硅基流动免费拿 key,然后设这个环境变量即可。它是本插件唯一可选的"外挂大脑",用来让搜索更聪明,但不是必需。DSH_MEMORY_INJECT=0→ 关闭自动注入(默认开启)DSH_MEMORY_ALLOW_REMOTE=1→ 允许非本机(非 loopback)访问记忆 API(默认一律 403;仅当 DSH web server 绑定 0.0.0.0 时需要,请自行评估隐私风险)DSH_MEMORY_AUTO_VAULT=0→ 强制关闭所有对话的自动语义检索;=1→ 强制开启(不设置则按每个对话记忆 tab 里的「自动检索」勾选,默认关)DSH_MEMORY_AUTO_VAULT_NAMESPACES=user,dsh-persist→ 限制自动检索只查这些 namespace(默认查全部 namespace,含项目归档)
隐私提示:开启语义搜索(
SILICONFLOW_API_KEY)后,每条用户消息和记忆内容都会发送给硅基流动(SiliconFlow)做向量化;自动检索同样如此。介意请勿配置 key,或设DSH_MEMORY_AUTO_VAULT=0关闭自动检索(手动vault search仍可用)。
一句话:装完 dsh plugin add 重启就有记忆功能;想要更聪明的语义搜索,再去硅基流动拿个免费 key 配上。
装完怎么用(新手三步)
- 重启 DSH(
dsh --profile web,别用还在跑的旧进程),插件即生效。 - 打开 Web 界面,进入任一对话,会话顶部(轨迹 tab 右边)会出现一个 「记忆」tab——这里就是你本对话的记忆和注入开关。
- 想让 agent 记住什么,就在对话里直接说,agent 会自动通过
memory工具写入本对话记忆;或你在记忆 tab 里手动编辑。默认不注入任何记忆(不占上下文),你在记忆 tab 勾选后才把对应记忆每轮放进上下文。
可选:想用语义搜索,先在硅基流动拿到免费 key,设置环境变量
SILICONFLOW_API_KEY=...再重启,Vault 层就从关键词搜索升级为语义搜索(README 不替你存 key,请放在 DSH 宿主能读到的环境里)。
安全提示:/dsh-memory/api/* 只允许 loopback 访问(非本机请求返回 403,除非显式设置 DSH_MEMORY_ALLOW_REMOTE=1)。记忆内容包含个人身份信息,请勿在共享网络中开放。
开发构建
使用者不需要构建——发布包自带 lib/,dsh plugin add 直接装。
外部开发者在自己环境 clone 后,npm install 会自动运行 prepare(tsdown 纯转译,不依赖 @deepseek-ai 类型即可产出 lib/),因此能自包含地构建出可用的产物:
npm install # 自动跑 prepare → lib/index.js + lib/client.js
npm run prepare # 显式重建自包含产物(host 用 tsdown.host.config.ts,client 用 tsdown.config.ts)
npm test # smoke 测试(node --experimental-strip-types src/smoke.ts)
prepare 只做转译(不 type-check):它把源码里对 @deepseek-ai/* 的 import type 全部擦除,产物运行时只保留对宿主提供的两个 import(@deepseek-ai/dsh-tools.defineTool 与 @deepseek-ai/dsh-llm.createUserMessage)——所以无宿主类型也能构建,产物由 DSH 宿主持有并加载。
维护者(在 DSH 宿主树内、junction 到宿主依赖以获得 @deepseek-ai 类型的场景)可跑全量构建,额外产出 .d.ts 并做完整类型检查:
npm run build # typecheck + typecheck:client + build:host + bundle + dts
语义检索的端到端测试在
src/smoke-semantic.ts(需要真实SILICONFLOW_API_KEY),不包含在npm test里——需要时手动运行:
- PowerShell:
$env:SILICONFLOW_API_KEY=...; node --experimental-strip-types src/smoke-semantic.ts- bash:
SILICONFLOW_API_KEY=... node --experimental-strip-types src/smoke-semantic.ts
注意:
lib/已 gitignore;运行中的 DSH 需重启才加载新 host 代码(client bundle 刷新页面即可)。
怎么用?
工具动作(model 调用)
memory 工具的 scope 参数决定写/读到哪:
scope=conversation(默认)→ 本对话记忆:add追加一条笔记,list/search读全文scope=project→ 当前工作目录的项目记忆(同目录对话互通)scope=global→ 全局 keyed 记忆:add/get/search/delete(按 key)
| 动作 | 参数 | 作用 |
|---|---|---|
add | scope, content(global 还需 key) | 存一条记忆 |
get / search / delete | scope, key | global 的 keyed 操作 |
list | scope | 读对话/项目记忆全文 |
profile | profileKind, profileOp, content | 读写全局用户画像(USER.md/MEMORY.md) |
vault | vaultOp, content/query, namespace | 语义存取:add 存、search 查、list 列、export 导出、import 导回(带 namespace 隔离) |
记忆文件(全部人类可读可改)
| 文件 | 内容 | 怎么改 |
|---|---|---|
~/.dsh-memory/sessions/<id>/memory.md | 本对话记忆 | 记忆 tab 里编辑,或 memory add |
~/.dsh-memory/sessions/<id>/inject.json | 本对话注入配置 | 记忆 tab 里勾选 |
~/.dsh-memory/projects/<key>/memory.md | 项目经验 | memory add(scope=project),或直接编辑 |
~/.dsh-memory/USER.md / MEMORY.md | 全局池 | /dsh-memory/ 页面或直接编辑 |
~/.dsh-memory/memory.json | 全局 keyed | 直接编辑 JSON |
~/.dsh-memory/vault.md | Vault 语义记忆 | 工具增删(memory(vaultOp="add"/"delete"))自动同步此文件;手改文件后 memory(vaultOp="import")(或管理页「Vault 同步」)写回数据库 |
例子
对话 A(工作目录 /work/projA):
- 记忆 tab 勾选:用户画像 + 本对话记忆 + 项目记忆(projA)
- agent 每轮自动注入这三块;对话 B 看不到对话 A 的记忆
agent: 用户说他喜欢用空格缩进
agent: memory(action="add", content="用户偏好空格缩进") # 写入对话 A 的记忆
agent: 用户问"你记得我喜欢怎么缩进吗"(同一对话)
agent: memory(action="list") → 读回对话 A 的记忆
agent: 记录项目经验
agent: memory(action="add", scope="project", content="构建脚本在 build.ps1")
# 写入 /work/projA 的项目记忆,同目录其他对话也能勾选注入
agent: 全局 keyed 记忆(跨对话共享)
agent: memory(action="add", scope="global", key="user-name", content="小明")
技术设计(对应 DSH 课程)
| 部分 | 实现 | 课程模块 |
|---|---|---|
| 存储层 | MemoryStore / ProfileStore / SessionMemoryStore,文件 + 原子写入 | 模块 ⑤ 方案 A |
| Vault 层 | VaultStore + embedder(SQLite + bge-m3) | 模块 ⑤ 方案 C |
| 工具层 | ctx.tools.register(defineTool({...})),scope 三态 | 模块 ③④ |
| 选择性注入 | ctx.systemPrompt.context() 按会话读 inject.json 渲染(子 agent 沿 parentSession 继承所属对话) | 模块 ⑥ |
| 多 agent 隔离 | namespace + 对话/项目两级隔离 | 模块 ⑦ |
| Client UI | conversation.view 槽位 id memory order 20(轨迹右边),tsdown 自包含 bundle | 模块 ⑧ |
| 混合检索 | 有向量走语义、无向量走关键词,合并排序 | 模块 ⑤ |
| 插件结构 | name / inject / apply 三件套 | 模块 ② |
注意:改代码后需要重启 DSH 才会加载新 host 代码(
lib/更新了,但运行中的进程持有旧模块;client bundle 刷新页面即可)。
故障排查
| 现象 | 处理 |
|---|---|
| 记忆文件损坏(memory.json / inject.json 无法解析) | 插件会自动把损坏文件备份为同目录下 *.corrupt-<时间戳> 并重置为空,控制台会打印备份路径——从备份找回内容即可 |
| 记忆 API 返回 403 | loopback 守卫生效(默认只允许本机)。确认 DSH 绑定 127.0.0.1;确实需要远程时设置 DSH_MEMORY_ALLOW_REMOTE=1(自行评估隐私风险) |
| 想完全关闭自动注入 | DSH_MEMORY_INJECT=0 后重启 DSH |
| 改 host 代码不生效 | DSH 进程持有旧模块,需要重启 DSH(client bundle 刷新页面即可) |
| 注入内容过长 | 静态记忆块(USER/MEMORY/对话/项目)有 100 行截断,超出的部分不会注入——请在 /dsh-memory/ 页面精简对应文件。Vault 无静态注入:只经「自动检索」topK=3 或工具按需召回,不受行数限制 |
| 对话/项目记忆文件越来越大 | 注入有 100 行截断,但磁盘上的 memory.md 会持续增长——建议定期(如每个里程碑)用 memory 工具或直接编辑精简,过时条目移入 Vault 归档 |
| 手改 vault.md 后内容丢失 | 工具增删(memory(vaultOp="add"/"delete"))会全量重写 vault.md(自动同步镜像)。手改请在无工具操作的间隙进行,改完立即 memory(vaultOp="import") 或管理页「Vault 同步」写回数据库 |
贡献
- 代码结构:
src/顶层 = 宿主逻辑 + 公共纯函数,src/client/= 浏览器侧;两套产物独立构建(host 用tsdown.host.config.ts,client 用tsdown.config.ts),类型声明(.d.ts)由 tsc 生成 - 开发流程:改代码 →
npm run build(typecheck + host + client + d.ts 全量)→npm test全绿;只改 client 时可单独npm run bundle快速迭代 - 测试永远用临时目录(
src/smoke.ts已如此),绝不直接读写~/.dsh-memory/真数据 - 提交前跑
npm pack --dry-run确认发布内容(prepack钩子会自动构建)
路线图
- v1 基础:
memory工具 + 文件存储 - Builtin 层:用户画像 + 自动注入(
context()) - Vault 层:向量语义检索(bge-m3,含关键词降级)
- 对话层:每对话独有记忆 + 注入配置 + 项目(cwd)记忆
- Client UI:记忆 tab(轨迹右边)+ 编辑/注入配置面板
- 真正的自动向量注入:
agent/pre-step按当前消息检索 Vault topK=3 注入(记忆 tab 勾选「自动检索」,默认关;DSH_MEMORY_AUTO_VAULT=0/1可全局强制)
License
MIT