<div align="center">
dsh-memory-graph
为 DeepSeek Harness 提供本地优先的长期记忆与时态知识图谱。
   
English · [快速安装](#安装) · [配置](#配置) · [数据与安全](#数据与安全)
</div>
dsh-memory-graph 让 DSH Agent 获得持久、可审计的记忆能力,同时不修改 DeepSeek Harness 源码。它以标准 Cordis 插件安装,将数据保存在本机私有 SQLite 数据库中,在模型首步之前召回相关事实,并在 DSH Web 内呈现实体图谱。
核心优势
- 真正非侵入。 不修改 Harness、不 fork agent loop;插件卸载后,其工具、事件监听、路由和 UI 注入会随生命周期完整撤销。
- 记忆不是覆盖,而是历史。 稳定事实更新时保留旧版本、来源和时间信息,并支持一键回滚。
- 图谱可以持续维护。 canonical、非破坏性别名冲突、重复实体候选、实体合并/重命名/删除、孤儿检测、引用感知 GC 与谓词归一共同解决“建错后改不动”。
- 中文检索可用。 FTS5 trigram 与短查询回退避免
unicode61将连续中文视为单个 token 的问题。 - 混合排序而非单一关键词。 综合全文匹配、图距离、重要度、时间衰减和访问强化;自动召回不强化旧记忆,避免高频读取使过期事实永久不衰减。
- 语义召回可选、可降级。 可接入 OpenAI 兼容 embedding 接口,将余弦相似度并入本地混合排序;默认关闭,无 key 或接口失败时自动回退到词法与图谱召回。
- 热路径可控。 写入侧 embedding 在去重后台队列执行;查询侧使用短超时、TTL 缓存和失败冷却,不会用写入侧 20 秒超时阻塞工具或首步注入。
- 真正的本轮开关。 DSH 输入框旁提供“默认 / 本轮开启 / 本轮关闭”;选择只锁定下一轮,结束后自动恢复默认。
- 原始轮次可恢复。 可在本地保存有界 user/assistant 原文,并可选捕获有界工具结果;抽取失败后无需重放对话即可离线重试。
- 失败写入不丢失。 可重试的 SQLite 写失败会进入私有 outbox,启动时按序重放;永久失败进入 dead-letter 目录。
- 自动总结可追溯。 自动记忆可记录 session、turn、事件序号、模型路由、抽取请求与原始响应。
- 本地优先、失败开放。 数据默认只在本机;后台总结失败只记录警告,不阻塞或替换原始回答。
架构
flowchart LR
T["一轮对话"] --> C["本轮策略"]
C --> X["本地轮次归档"]
X --> S["后台结构化总结"]
S --> M["时态记忆库"]
M <--> G["规范化实体图谱"]
M --> R["混合召回"]
E["可选 embedding 接口"] -.-> R
G --> R
R --> A["Agent 首步"]
M --> V["DSH Web 图谱"]
G --> V
B["JSONL 备份"] <--> M插件只使用 DSH 已有扩展点。任何进入模型上下文的召回结果都会写入会话日志,因此历史会话仍可重建和回放。
环境要求
- 已安装 DeepSeek Harness,并存在
web等可用 profile - Node.js
^22.19.0或>=24.0.0 - Git 与 npm
先确认 CLI 和目标 profile:
dsh --version
dsh plugin --profile web list安装
项目目前通过 GitHub 源码发布。克隆、验证后,将本地目录链接到 DSH profile:
git clone https://github.com/zmh2000829/dsh-memory-graph.git
cd dsh-memory-graph
npm ci
npm run check
dsh plugin --profile web add "$PWD"
dsh web打开 DSH Web,在侧边栏展开 Memory。默认配置已经开启自动召回、对话总结和图谱可视化。
仪表盘首先回答两个问题:DSH 记住了什么,以及这些内容会怎样影响后续回答。回答前,插件只把与当前问题相关的长期偏好、事实、约束、决定和经验召回到上下文;回答后,再从成功对话中提炼值得复用的信息,并不会把每句话都保存成记忆。因此它通常是安静地改善后续相关回答,无关问题不会强行使用记忆。
默认折叠的 知识图谱 是高级检查视图:它展示当前 DSH profile 共用数据库中的跨会话全局关系,不是当前单个对话的流程图。节点是抽取出的实体,连线是实体关系,×N 表示有 N 条记忆支持同一关系。它适合发现关联、重复实体和错误关系;理解“哪些内容可能影响回答”时,应优先查看长期记忆列表。同一 profile 的多个会话都会汇入;需要隔离时应使用不同 profile 或不同数据库 path。
新摘要默认以 summaryLanguage: auto 跟随用户消息的主要语言,中文对话输出简体中文,同时保留 DeepSeek 等专有名词的惯用拼写。升级前已经写入的英文记忆不会自动翻译,以避免静默改写事实。
发送提示词前,输入框旁会直接显示 Memory · On、Memory · Off 或 Memory · Mixed,表示 profile 默认实际是全开、全关或部分开启,不再用含义不明的 Default 作为当前状态。菜单可继续使用 profile 默认,也可选择仅下一轮开启或仅下一轮关闭;临时选择在本轮结束后恢复默认,人工调用 memory_* 工具不受影响。仪表盘的删除按钮会在确认后永久删除该记忆,并同步删除由它提供来源的图关系。
验证安装状态:
dsh plugin --profile web list dsh-memory-graph升级
profile 链接到本地 Git 仓库,因此升级不需要重复执行 plugin add:
cd /path/to/dsh-memory-graph
git pull --ff-only
npm ci
npm run check校验完成后重启正在运行的 DSH。插件启动时会在事务中执行数据库 schema 迁移。
卸载
dsh plugin --profile web remove dsh-memory-graph卸载只移除 profile 链接,不会删除数据库和 JSONL 备份。只有确认需要永久清除数据时,才应单独删除这些文件。
配置
[cordis.patch.yml](cordis.patch.yml) 提供适合个人本地环境的完整默认配置。修改 profile 中的插件配置后,需要重启 DSH:
- id: memory-graph
name: dsh-memory-graph
config:
enabled: true
path: !!js dshHomePath('memory-graph.sqlite')
backupDirectory: !!js dshHomePath('memory-graph-backups')
outboxDirectory: !!js dshHomePath('memory-graph-outbox')
autoRecall: true
autoRecallLimit: 4
autoRecallMinScore: 0.18
maxContextTokens: 1200
recallPreviewTokens: 220
predicateAliases:
created_by: developed_by
autoSummarize: true
archiveTurns: true
archiveMaxInputTokens: 30000
archiveRetentionDays: 30
archiveMaxTurnsPerSession: 200
captureToolResults: false
summarizeEveryTurns: 1
summaryConcurrency: 2
summaryMaxAttempts: 2
summaryRetryBaseMs: 1000
summaryRetryFailedOnStart: 3
summaryLanguage: auto
summaryMaxInputTokens: 6000
summaryMaxOutputTokens: 3200
semanticEnabled: false
semanticEndpoint: https://api.openai.com/v1/embeddings
semanticModel: text-embedding-3-small
semanticApiKeyEnv: OPENAI_API_KEY
semanticQueryTimeoutMs: 3500
semanticQueryCacheMs: 300000
semanticFailureCooldownMs: 60000
visualizationEnabled: true
visualizationAutoOpen: true
visualizationRefreshMs: 5000三个主要能力可以独立开关:
| 配置项 | 默认 patch | 作用 |
|---|---|---|
enabled | true | 插件总开关 |
autoRecall | true | 每轮首步前自动召回相关记忆 |
autoSummarize | true | 成功完成一轮后抽取结构化记忆 |
archiveTurns | true | 为开启记忆的已完成轮次保留有界本地原文 |
archiveRetentionDays | 30 | 删除超过保留期的轮次归档 |
archiveMaxTurnsPerSession | 200 | 每个 session 只保留最新的有界轮次数 |
captureToolResults | false | 将有界工具结果加入归档和抽取输入 |
summaryLanguage | auto | 新摘要跟随用户主要语言,也可固定为 zh-CN 或 en |
semanticEnabled | false | 加入可选语义相似度;词法/图谱降级始终保留 |
visualizationEnabled | true | 注册仪表盘、本轮选择器、工具视图和受保护操作接口 |
visualizationAutoOpen | true | DSH Web 启动后自动展开图谱 |
可成对设置 summaryProvider 与 summaryModel,把总结路由到成本更低或完全本地的模型;省略时沿用当前会话路由。summaryReasoningEffort 是可选项,必须由对应精确模型支持;除非模型明确公布了所选强度,否则应保持未配置。摘要任务在单会话内串行执行,通过 summaryConcurrency 限制全局并发,并且最多只重试 summaryMaxAttempts 次;启动时还会按 summaryRetryFailedOnStart 有界重驱最旧的失败归档。predicateAliases 可将部署中的谓词写法收敛到同一词表。混合排序权重、图深度、数量上限、衰减周期、超时与总结阈值均可在 [cordis.patch.yml](cordis.patch.yml) 中调整。
maxContextTokens 与 summaryMaxInputTokens 使用偏保守的 CJK 估算:中日韩字符约按 1.5 token/字,其余字符约按 chars/4;字符数配置继续作为硬上限。偏好/时间意图、词法重叠、规范化内容去重、图距离与可选语义分数共同进入同一有界排序器。
语义召回需要 OpenAI 兼容 embeddings 接口。设置 semanticEnabled: true,配置 endpoint/model,导出 semanticApiKeyEnv 指定的环境变量,重启 DSH,再对存量记忆执行一次 memory_semantic_reindex。只有可信的本地免鉴权接口才应把 semanticApiKeyEnv 设为空。写入索引在后台串行执行;查询使用 semanticQueryTimeoutMs、semanticQueryCacheMs 与 semanticFailureCooldownMs 快速降级。至少一个非语义排序权重必须大于零,以保证 endpoint 不可用时仍能产生有限分数。
如果 DSH Web 不是 loopback 部署,需要把准确的 host 或 host:port 加入 visualizationTrustedHosts。默认拒绝远程读取本机记忆。
工具
| 工具 | 能力 |
|---|---|
memory_remember | 原子写入记忆、规范实体、别名和有向关系 |
memory_recall | 执行带图谱上下文的混合排序检索 |
memory_expand | 将分层召回预览展开为完整记忆文本 |
memory_graph | 查询有界多跳邻域或全局图谱概览 |
memory_forget | 删除一条记忆及由它产生的关系 |
memory_archive_expand | 读取一轮对话有界保留的尾部消息,并明确报告是否发生裁剪 |
memory_archive_retry | 对失败归档重新执行记忆抽取 |
memory_semantic_reindex | 语义召回启用后为存量活跃记忆建立向量索引 |
memory_entity_merge | 合并重复节点并重定向关系 |
memory_entity_find_duplicates | 只预览保守的重复实体候选,不修改图谱 |
memory_entity_rename | 修改实体的规范显示名 |
memory_entity_delete | 按明确的引用处理策略删除实体 |
memory_entity_orphans | 列出没有活跃记忆引用的图节点 |
memory_revert | 恢复稳定事实被取代前的版本 |
memory_backup | 导出或恢复完整 JSONL 数据库快照 |
写入工具返回的实体数与关系数统一表示净新增数量。补充 alias 永远不会触发隐式破坏性合并:冲突 alias 保留原归属并返回给调用方。人工重命名使用独立的 preferred 标志,不再伪造出现次数。谓词统一为小写 snake_case,并支持配置别名词表。
可视化
DSH Web 内置支持搜索、拖动、缩放、配置状态、记忆详情、确认删除和自动刷新的交互图谱。来自多条记忆的同一逻辑关系会聚合为一条边并显示引用次数。侧边栏通过 /memory-graph/snapshot 直接读取存储,查看图谱不需要触发模型调用;ETag 避免重复更新未变化状态,页面进入后台后暂停轮询。显式修改使用同源、随机 token 保护的 POST 接口。
可视化读取与工具回放彼此隔离:即使仪表盘接口暂时失败,历史 memory_graph 工具结果仍能正常渲染。
备份与恢复
执行迁移、实验或人工清理前,可调用 memory_backup 并设置 operation: "export"。恢复只允许写入空数据库,防止导入操作静默覆盖现有记忆。
备份路径被限制在 backupDirectory 的真实目录内,导入会拒绝指向目录外部的文件符号链接,文件名不能包含目录组件。旧兼容备份缺少新增列时,导入器会使用数据库默认值。JSONL 会保存记忆、向量、轮次归档、实体、别名、关系、来源信息和总结历史。
数据与安全
- 默认数据库为
$DSH_HOME/memory-graph.sqlite,创建权限为0600。 - 插件拒绝打开不兼容 schema 或属于其他应用的数据库。
- 仪表盘读取返回
Cache-Control: no-store;写操作要求 POST、JSON、进程随机 token,并校验 Host、Origin 与 Fetch Metadata。 - 自动召回内容会被明确标记为“参考数据”,而不是可执行指令。
- 自动总结会把所选对话片段发送给配置的模型路由;敏感数据不能离开本机时,请关闭
autoSummarize或使用本地模型。 archiveTurns会在本地保存有界的轮次尾部,并受天数和每 session 条目数双重限制;memory_archive_expand的truncated字段明确说明它是否为完整轮次。captureToolResults默认关闭,因为工具结果可能包含敏感信息。- 只有显式启用语义召回时,记忆和查询文本才会发送到
semanticEndpoint;敏感数据不能离开本机时应使用本地接口。
当前边界
- 插件可消费 embedding 服务,但不捆绑服务端。新记忆会自动建索引,存量记忆需要执行
memory_semantic_reindex。 - 存储使用同步的
node:sqliteDatabaseSync,适合个人本地记忆,不面向多主机数据库或高并发写入服务。 - 一个可写数据库应只由一个 DSH 进程持有。SQLite WAL 与有限
busy_timeout可以保护正常事务,但插件不提供分布式写协调。 - 在支持的 Node.js 版本上,
node:sqlite仍可能输出实验性 API 警告。 - JSONL 恢复有意要求目标数据库为空。
开发与验证
npm ci
npm run typecheck
npm test
npm run build
npm pack --dry-runnpm run check 会依次执行类型检查、全部单元测试和生产构建。提交改动前请阅读 [CONTRIBUTING.md](CONTRIBUTING.md)。
版本变更记录见 [CHANGELOG.md](CHANGELOG.md)。 本轮审查结论和剩余路线图记录在 [IMPROVEMENTS.md](IMPROVEMENTS.md)。 与 OpenViking DSH 适配器的源码级对比见 [COMPARISON.md](COMPARISON.md)。
License
[MIT](LICENSE) © dsh-memory-graph contributors.