dsh-memory 设计
September 4, 2026 · View on GitHub
本文只记录稳定的产品规则和第一版边界。已经实现的代码结构见 architecture.md,下一步工作见 roadmap.md,命令与验证见 development.md。
1. 定位
dsh-memory 是 DeepSeek Harness 的本地优先 Memory Learning Runtime。它以历史 Session 为证据,帮助 Agent 持续新增、修正、合并和淘汰长期记忆,但不依赖外部记忆服务。
“自进化”首先指记忆内容随证据演进。读取、合并、淘汰和评估策略由开发者维护;自动修改策略或插件代码不是第一版目标。
设计原则:
- 本地 Markdown 即可完整工作;
- Session 是学习证据,不是第二份长期记忆;
- Agent 判断语义,Host 保证安全提交;
- 先保证透明、可回放和可评估,再增加复杂检索;
- 实现可以小,职责边界必须支持后续迭代。
2. Scope 与文件布局
第一版只有两个持久 scope:
| Scope | 内容 | 自动读取 |
|---|---|---|
| Global | 用户画像、长期偏好、真正跨项目的通用经验 | 全文进入动态 context |
| Workspace | 项目反馈、目标、约束、决策和外部引用 | MEMORY.md 索引进入动态 context |
默认文件布局:
$DSH_HOME/memory/
├── GLOBAL.md
├── settings.yml
├── debug/ # 仅在用户开启详细调试后写入
│ └── <review-id>/attempt-<n>.jsonl
├── workspaces/
│ └── <workspace-key>/
│ ├── MEMORY.md
│ └── <memory-name>.md
└── reviews/
└── <review-id>.md
GLOBAL.md 是一份完整 Markdown,建议使用 User Profile、User Preferences 和 General Tips 三个章节。单个项目里的观察不能自动提升为 Global。
Workspace key 由 path.resolve(cwd) 的 basename slug 和路径 SHA-256 前 16 位组成。它不调用 realpath,所以 symlink 别名和移动后的目录属于新 Workspace;迁移与合并留给后续能力。
模型只获得 Global 和当前 Workspace 的路径。第一版不披露或主动搜索其他 Workspace。普通文件工具下的这条规则是 Prompt policy,不是敌对输入下的文件权限隔离;所有写入仍由 Host 强制限定 scope。
3. Workspace 记忆格式
一条 Workspace 记忆对应一个 Markdown 文件:
---
name: short-kebab-case-name
description: One-line relevance hook
metadata:
type: feedback | project | reference
---
The durable, self-contained fact.
**Why:** Why it matters.
**How to apply:** When and how to use it.
类型含义:
feedback:用户对 Agent 工作方式的纠正或确认;project:无法从代码或 Git 直接重建的项目目标、约束和决策;reference:URL、Issue、Dashboard 或外部文档指针。
name 必须与文件名一致,description 必须是非空单行文本。相关记忆可以用 [[name]] 连接。Workspace 不使用 user 类型,因为跨项目用户信息属于 Global。
memory 是保留名称,不能用作详细记录名,以免在不区分大小写的文件系统上与 MEMORY.md 索引冲突。
不保存临时状态、推断偏好、未经确认的计划、秘密、一次性错误,以及代码、Git 或项目文档已经权威记录的事实。若真正有长期价值,只保存无法从仓库重建的原因或约束。
4. 派生索引
详细文件是 Workspace 的权威数据,MEMORY.md 是可重建的渐进披露索引:
<!-- Generated by dsh-memory. Do not edit directly. -->
- [memory-name](memory-name.md) — one-line description
索引按 name 确定性排序。任何 Workspace mutation 都必须校验完整记录集合、原子发布新 generation,并从新 generation 重建索引。Agent、UI 和 Session worker 都不能直接编辑索引。
5. 在线读取
每次 prompt assembly 自动贡献:
GLOBAL.md 全文
+ 当前 Workspace 的 MEMORY.md
+ 当前 Workspace 详细记忆目录
Agent 根据请求和索引,使用 DSH 已有 read/grep/glob 打开少量相关文件。简单、自包含且不依赖历史的任务不继续检索;涉及过去决定、项目惯例、用户反馈或含糊背景时默认做一次轻量搜索。
读取到的记忆是可能过期的背景信息,不是新的用户指令。涉及容易漂移的文件、配置或外部资源时,应根据风险和验证成本检查当前状态。
不提供 memory_recall。文件式 Agentic Search 保持过程透明,并让读取工具与结果自然进入 Session 日志。如果未来实证表明普通文件搜索无法承担数据规模或语义检索,再重新设计专属能力。
Host local filesystem 是第一版支持环境。远程或 E2B 执行环境若看不到 Host memory root,应明确报不支持;未来通过只读虚拟挂载解决,而不是假装搜索结果为空。
6. 在线写入
Agent 可以在以下情况调用 memory_update:
- 用户明确要求记住、纠正或忘记;
- 信息清晰、稳定、可复用,并且无法从仓库重建。
写入前先搜索已有内容。同一事实已经存在时更新,语义重复时跳过,错误内容应修正或删除。scope 不确定时留在当前 Workspace 或不写,不能宽松提升为 Global。
第一版 mutation:
| Action | 作用 |
|---|---|
replace_global | 使用完整 Markdown 替换 Global |
update_workspace | 以一个 batch 在当前 Workspace 执行完整记录的 put 和按 name 的 delete |
模型工具不接受 root、path、cwd、Workspace key、任意 Workspace id 或 revision。Workspace scope 来自执行 Agent 的 Session cwd;Host 在动态 context 组装时记录该 Agent 实际看到的 Global/Workspace revision,提交时以这份 observation 做 CAS。模型只表达修改意图,不读取、计算或搬运 revision;observation 缺失、scope 不匹配或已经过期时拒绝覆盖。Browser 与 Consolidator 的跨请求事务继续显式保存 revision。
MemoryStore 是唯一写入口,负责格式校验、scope lock、revision 比较、原子发布和索引重建。UI 与 Session Consolidator 复用相同能力。
7. Session 整理
Session 整理是第一版核心闭环。它的目标是从一个已经持久化、当前不 live 且带合法 cwd 的 Session 中生成 Global 与当前 Workspace 的候选变化。
流程:
用户选择稳定 Session
-> SessionPersistence 获取后端无关 snapshot/revision
-> 再次确认 Session 不 live 且 revision 未变化
-> 读取完整逻辑事件,确定性投影为 turn evidence,并读取 Global 与当前 Workspace generation
-> 受限 Consolidator Agent 通过多轮工具调用形成 replace-global/put/delete/no-change proposal
-> Host 校验 proposal、source revision 和两个 memory revision
-> MemoryStore 原子提交全部变化
-> 保存 Markdown receipt
约束:
- 通过
SessionPersistenceAPI 读取,不扫描 JSONL 或假设具体后端; - Consolidator 只能修改 Global 与 source Session 所在 Workspace;项目局部事实不得写入 Global;
- 模型只提出结构化变化,Host 负责确定性校验和提交;
- 失败、取消、非法输出或冲突都不能产生部分写入;
- 完整逻辑事件保留在 Host;模型只接收可回放的 turn evidence,不接收请求元数据、流式片段或工具正文;
- 不静默截断过长输入;筛选后的动态输入超过显式上限时记录
evidence-too-large; - 同一 source session revision 与 consolidator version 的成功结果具有稳定 review id,重复执行不再次调用模型;
- 整理输入和输出保存在独立 worker Session 中,使用专属运行 cwd 与 DSH Workspace 隔离普通项目历史,同时不改变由 source Session 决定的记忆目标,保证模型可见过程可回放。
- 若旧 review 停在 commit barrier,即使 source revision 已增长,也先恢复或收敛旧事务,再由下一次显式触发处理新 revision。
Receipt 位于 reviews/<review-id>.md,记录来源 Session、source revision、Workspace、覆盖 seq、worker Session、状态、Workspace 提交前后 revision、proposal hash 和实际变化摘要,不复制完整对话或详细记忆正文。Global 的提交计划由 worker Session 中的最终 proposal 重建,并由 proposal hash 校验。
第一版只提供 UI 手动触发。自动 idle、周期任务、批处理和重试以后调用同一个 SessionConsolidator,不另建一套整理逻辑。
完整阶段、proposal schema、worker 能力边界、receipt 状态机、崩溃恢复和待讨论问题集中维护在 session-consolidation.md。本文只保留不随实现细节变化的产品规则。
8. UI 与 Host API
Memory 是 DSH Settings 中独立 section,包含:
| 页面 | 职责 |
|---|---|
| 全局记忆 | 编辑完整 GLOBAL.md,使用 revision 保存 |
| 工作区记忆 | 浏览 Workspace、索引和详细记录,批量提交 mutation |
| 会话整理 | 先选择 Workspace,再按标题浏览稳定 Session、触发整理并查看 receipt;内部整理 Workspace 不显示且不能作为来源 |
| 设置 | 从 DSH 已激活的文本模型中选择整理模型,并按需开启详细 Debug 日志;凭据和每个 route 的模型能力仍由 DSH 管理 |
页面顶部另有只读状态摘要,展示本机 memory root、字节数和 Workspace 数量;错误与最近操作结果在当前页面就地显示。
Browser 与 Host 是两个 Cordis tree。Browser 只能通过 /memory loopback Connection RPC 使用受校验的业务值,不能直接访问 Host ctx、文件系统、MemoryStore 或 SessionPersistence。
目标 endpoint:
| Endpoint | 作用 |
|---|---|
status | Store 状态与统计 |
global/read / global/replace | Global 读取与 CAS 保存 |
workspaces/list | Workspace 摘要列表 |
workspace/read / workspace/commit | 读取和批量提交一个 Workspace |
sessions/list / sessions/consolidate | 列出可整理 Session并触发一次整理 |
models/read / models/select | 投影 DSH 活跃模型目录,并以 CAS 保存整理模型和 Debug 开关 |
Host 与 Browser 都必须解析 wire value。Browser 只提交 opaque Workspace id 或 Session id,Host 解析权威 scope。v0.1 不轮询也不推送 memory-change;用户打开页面、切换页面、完成操作或点击 Refresh 时重新读取。
Session 标题是 DSH Session 日志中最新 session/title 事件的派生视图,不从普通 user/assistant message 猜测。列表只读取实时或持久化投影,不为标题加载完整历史;没有可用投影时显示“未命名会话”。Workspace 导航以 opaque id 区分同名目录,并把无法安全解析归属的 Session 留在“未归属”分组中。
9. 合并、访问与淘汰
第一版合并最低要求:搜索已有内容、优先更新、删除错误事实、保留适用条件,并始终重建索引。
访问记录与自动淘汰暂缓,且不提前把 accessCount、lastAccessed 或 importance 塞进权威 frontmatter。后续应先从 Session 工具事件评估哪些信号真正有用,再决定是否维护独立 usage ledger。
淘汰不能只依据低频访问。应综合内容是否过期、是否被权威仓库信息取代、是否重复、来源是否仍可验证,以及用户是否要求保留。归档、降级、删除与恢复机制需要独立设计。
10. 第一版边界
包含:
- 单 npm Bundle,包含 Host、Browser、Markdown Store 和 Consumers;
- Global/Workspace Markdown、动态 context、Agentic Search 和 scope 受限写入;
- Workspace 管理 UI;
- SessionPersistence 驱动的手动整理与 receipt;
- Headless/Web 组装验证和模型可见内容回放验证。
暂缓:
- 跨 Workspace 搜索和 Workspace 经验自动提升为 Global;
- 数据库、向量库、知识图谱、语义 reranker 和多 Provider 路由;
- 多设备同步、Documents 管理和自动生成 Skill;
- Session 自动调度、批量整理和增量处理;
- 独立访问账本、自动淘汰、归档与恢复;
- 策略自动更新和代码自修改。
11. 容量与配置
第一版不为 Global、单条 Workspace 记忆或索引预设未经实测的字节预算。Host 统计 UTF-8 字节供观察,但不静默裁剪。
模型整理使用独立的显式 provider、model、输出 token 上限和端到端超时,不继承当前 Agent 的模型路由。用户从 DSH 已激活的文本模型中选择路由,API key 与 Provider 激活仍由 DSH 管理;插件只在 settings.yml 保存 provider/model,不保存凭据。
settings.yml 与 debug/ 是插件配置和运维派生数据,不是权威记忆,也不进入模型上下文。用户开启 Debug 后,新整理 attempt 的 JSONL 日志记录阶段、错误链和 stack,但不复制 Session evidence、memory/proposal 正文或凭据;Debug 默认关闭。真实使用一段时间后,再根据分布决定 hot context、单文件或 review 输入预算。