← Back to home@pomelotea-yuzu

dsh-memory-layered

Layered (global + per-project) bounded file-backed memory for DeepSeek Harness — model-curated with user intervention, versioned writes, TTL expiry, session search, and an in-chat memory panel

Stars
0
Language
TypeScript
Created
Aug 15, 2026
Updated
Aug 24, 2026
GitHub repo

Introduction

@deepseek-ai/dsh-memory-layered

DeepSeek Harness 的双层(全局 + 项目)有界文件记忆插件:模型自主维护 + 用户干预, 带版本化回滚、TTL 过期、会话搜索、对话页记忆面板。

设计文档见 DESIGN.md。

能力

  • 双层记忆:global(跨项目的事实/约定)+ project:<workspace-uuid>(当前工作区,映射 DSH workspaceRegistry)
  • 有界注入:每层字符预算(默认 2000+2000),超限拒写 + 模型当回合整合(hermes 式)
  • 溢出归档:整合不动推入 ARCHIVE.jsonl(可检索、不注入、永不真删)
  • 版本化:每次写前快照,/memory undo 回滚
  • TTL:临时事实自动过期进归档
  • 会话搜索:memory search 搜注入层 + 归档 + DSH 会话历史(ctx.sessionQuery)
  • 用户干预:/memory 斜杠命令 + 手改 MEMORY.md(漂移检测兜底)
  • 记忆面板:对话页输入行 🧠 按钮,弹层显示全局/项目两层 + 使用率
  • 会话交接摘要:长对话切换前在 🧠 弹层手动"生成"→ LLM 把最近 N 轮蒸馏成结构化交接(独立 handoffs.jsonl,不占记忆预算,按全局/项目分层)→ 新对话自动携带(项目层优先、全局兜底),或手动"带入"预填输入框

工作原理

存储

$DSH_HOME/memories/
├── global/
│   ├── MEMORY.md          # 注入层:§ 分隔条目,人类可读可手改
│   ├── memory.meta.json   # 元数据:条目 id → {addedAt, ttl?}
│   ├── ARCHIVE.jsonl      # 归档层:溢出/过期条目,append-only
│   ├── handoffs.jsonl     # 交接摘要:独立存储,保留最近 N 条
│   └── versions/          # 每次写前的快照(undo 用)
└── projects/<workspace-uuid>/
    └── (同结构)

会话循环

  1. 会话开始:注入一条冻结快照(<system-reminder> 块,含两层条目 + 使用率)。中途写入只改文件、不改快照(前缀稳定,KV cache 整会话有效)。
  2. 写入:模型调 memory 工具(或你发"记住…"触发它)→ 预算检查 → 原子写盘。
  3. 超限:拒写并返回当前条目,模型当回合合并/删减(整合);整合不动的推 ARCHIVE。
  4. 跨会话:下一会话重新注入当前文件内容。

会话交接摘要

长对话切换新对话前,在 🧠 弹层的"交接摘要"分区点生成:

  1. host 端读当前会话日志最近 handoffTurns 轮(turn/start 定界,跳过插件注入/工具消息),经 ctx.llm.stream 用会话自身路由(session.requestContext())发起一次模型调用。
  2. 模型产出结构化摘要(首行 ≤40 字标题 + 目标/结论/关键事实/未完成/偏好/下一步 小节),总长 ≤ handoffCharLimit(默认 1500 字符),存入作用域 handoffs.jsonl(与 MEMORY.md 预算隔离,保留 handoffMaxKeep 条淘汰最旧)。
  3. 自动携带:新会话首个 admitted step,若日志无 handoff 消息且存在"非本会话"的最新交接,注入一条 source {kind:'memory', form:'handoff'} 消息(与快照同语义:resume 复用、compaction 后重注入)。层级:项目层优先(同项目延续更具体的上下文),项目层无交接时回退全局层(跨项目切换兜底)。
  4. 手动带入:弹层列表每条有"带入"按钮 → 优先 inputActions.setDraft 预填输入框(可编辑后发送),不可用时退化剪贴板。
  5. 多选管理:每条交接前有复选框,勾选后出现批量工具栏——「带入」(多条用 --- 分隔合并预填输入框)、「删除」、「取消」;切换层级时勾选自动清空。
  6. 层级切换:交接按作用域分层存储(全局 handoffs.jsonl + 每个项目自己的 handoffs.jsonl),弹层交接分区有「全局 / 项目」tab——生成、删除、带入都作用于当前 tab 的层;会话无项目层时只显示「全局」。全局层交接适合跨项目通用的项目背景/团队约定。
  7. 会话关闭也能生成:生成时优先用存活会话(agents/sessions 服务)的日志;找不到时回退 ctx.sessionPersistence.loadStored() 从磁盘日志恢复(不要求会话仍在内存),模型路由从日志的 request/context 事件提取。
  8. 时序注意:快照/交接在会话开始时冻结,所以要在旧对话生成、之后新建的对话才自动携带;已切新对话可用"带入"补救。

三个入口

入口谁动作
memory 工具模型自主add / replace / remove / batch / archive / search
/memory 命令你list / undo / drop / set-ttl / search(直执行,不进模型)
手改 MEMORY.md你任意编辑;round-trip 则生效,否则备份 + 拒绝(漂移检测)

安装

dsh plugin --profile web add "link:D:/dsh_plugin/dsh-memory-layered"

或 npm:

dsh plugin --profile web add @deepseek-ai/dsh-memory-layered

装完重启 dsh web,刷新页面;并检查 profile 的 node_modules/@deepseek-ai 是否被建成真实目录(见 hyls9527 仓库 README 的注意项)。

配置

按 id 覆盖(profile 的 cordis.patch.yml):

- id: memory-layered
  config:
    globalCharLimit: 4000
    nudgeInterval: 0
字段默认含义
memoryEnabledtrue开关
globalCharLimit2000global 层字符预算
projectCharLimit2000project 层字符预算
nudgeInterval10无写入多少轮后提醒;0 关闭
versionsKeep100版本快照保留数
recallLimit10search 默认返回条数
recallSessionHistorytruesearch 是否含会话历史
dir$DSH_HOME/memories记忆根目录
handoffEnabledtrue交接摘要开关
handoffCharLimit1500交接摘要字符上限(标题 + 正文)
handoffMaxKeep10每作用域保留的交接条数,超限淘汰最旧
handoffTurns20生成时摘取最近多少轮对话
handoffAutoCarrytrue新会话是否自动携带最近一条交接

会话历史搜索(默认关)

DSH 的 session-query-sqlite 默认 openAt: never,memory search 的会话历史部分会静默降级。要开启,在 profile 补丁层加:

- id: session-query-sqlite
  config:
    path: ':memory:'
    openAt: first-search

命令

/memory list [global|project]
/memory undo [n]
/memory drop <id|子串>
/memory set-ttl <id|子串> <1d|7d|ISO日期>
/memory search <query>

安全不变量

  • 存在但读不了的文件 ≠ 空文件(fatal UTF-8 解码,写入拒绝)
  • 外部漂移(不 round-trip / 单条目超限)→ 快照 .bak.<ts> + 拒绝
  • 每次提交:跨进程锁 + 同目录原子 rename(0o600/0o700)
  • 整批 all-or-nothing;超预算拒绝 + 当回合整合;每回合失败上限 3 次
  • 注入转义 </system-reminder>;replace/remove 子串匹配 + 歧义保护
  • 版本化:写前必快照,任何整合可回滚;TTL 只归档不真删

错误码

code说明
ERR_UNREADABLE文件存在但读不了,拒绝写
ERR_DRIFT外部编辑不 round-trip,已存 .bak
ERR_OVER_BUDGET超字符预算,需整合
ERR_CONSOLIDATION_LIMIT本回合整合失败超 3 次,强制停止
ERR_NO_MATCH / ERR_AMBIGUOUS_MATCH子串未命中 / 命中多条
ERR_NO_VERSIONundo 目标版本不存在
ERR_TTL_FORMATttl 格式非法
ERR_NO_WORKSPACE显式 target:project 但无工作区

开发

corepack pnpm install
corepack pnpm typecheck     # tsc --noEmit
corepack pnpm test          # vitest
corepack pnpm build         # tsc + 复制 client.js
  • 覆盖率门禁:行/语句/函数 100%,分支 ≥95%(见 vitest.config.ts 说明)
  • 测试 198 例 / 11 个 spec
  • Node ^22.19 || >=24

已知限制

  • 单目录单进程集:per-agent 记忆需挂多个插件行、每个不同 dir
  • 无 prompt 注入扫描(信任文件,同 DSH 信任 AGENTS.md)
  • nudge 按轮次,非墙钟
  • 快照是持久 user message 而非系统提示词段(仓库外插件无法注册新事件词表)

License

MIT(大脑图标路径取自 Lucide,ISC,见 THIRD_PARTY_NOTICES.md)