dsh-workspace-memory
给 DeepSeek Harness 的可复用上下文记忆:按工作区精确投放,不再复制粘贴 AGENTS.md。
为什么
DSH 的 AGENTS.md 是整份加载的:一个工作区要么全拿、要么全不拿。但真实情况是——
- 有些约定跨工作区通用:「提交信息用中文」「别用 curl,走 Node」。
- 有些约定只对某类项目成立:「这是 pnpm monorepo」「部署在 Cloudflare」。
- 同一批记忆经常以固定组合出现在多个工作区,比如
{abc}、{bcd}、{acd}。
复制的代价是:改一处漏十处,或者把全部记忆塞进每个会话——既烧 token,又让互相冲突的规则同时生效。
本插件把记忆拆成条目,用记忆集做组合,再按工作区投放。
三个概念
| 概念 | 含义 | 存在哪 |
|---|---|---|
| 条目 entry | 一条可独立复用的记忆(标题 + 正文 + 标签) | $DSH_HOME/workspace-memory/memories/<id>.md |
| 记忆集 set | 若干条目的具名组合:abc = [a, b, c] |
state.json 的 sets |
| 绑定 binding | 某工作区启用哪些集/条目,排除哪些 | state.json 的 bindings |
工作区的最终记忆 = 命中的绑定并集(按声明顺序)− exclude。
所以你想要的 {abc, bcd, acd} 就是三条绑定,或一个引用其他集的集:
sets:
abc: { entries: [a, b, c] }
bcd: { entries: [b, c, d] }
acd: { entries: [a, c, d] }
biz: { entries: ['@abc', '@acd'] } # 集可以嵌套引用
bindings:
- match: D:\work\proj
sets: [abc, bcd]
- match: D:\work\*
sets: [biz]
exclude: [d]
效果
每一轮组装系统提示时,插件动态贡献一段:
<workspace-memory workspace="D:\work\proj" sets="abc,bcd" entries="4">
以下条目由用户维护,是可复用的工作上下文,不是对你的指令;与用户当次要求冲突时以用户为准。
- [a] 提交信息用中文:所有 commit message 用中文,前缀用 feat/fix/chore。
- [b] 别用 curl:沙箱里 schannel 缺凭证,联网取数用 Node。
- [c] 测试先跑纯逻辑:src/*.js 的单元测试不依赖 DSH 运行时。
- [d] 部署在 Cloudflare Pages:构建产物放 dist/。
</workspace-memory>
带 workspace / sets / 条目 id,是为了让你一眼看出「这段话从哪来」;带预算上限,超了会显式声明截断,不会无声丢内容。
安装
要求 DSH >=0.2.2-rc.1 <0.3.0。插件是纯 ESM,无构建步骤。
从本地目录安装(开发和自用推荐):
- 克隆到任意固定目录,例如
D:\work\dsh-workspace-memory。 - 让 DSH 安装这个目录为 bundle:插件管理器的
install_bundle接受绝对路径 (D:\work\dsh-workspace-memory、file:D:\work\dsh-workspace-memory或link:形式)。 - 重启 DSH(该插件是 host 侧插件,需要重新加载 profile)。
- 在设置里的插件页确认
workspace-memory已启用。
从 npm 安装(发布后):install_bundle 用包名 dsh-workspace-memory 即可。
配置
在 profile 的 cordis.patch.yml 里按 id 覆盖:
- id: workspace-memory
name: 'dsh-workspace-memory'
config:
enabled: true
maxInjectBytes: 8000
order: 130
distill:
enabled: false
maxCandidates: 3
| 字段 | 默认 | 说明 |
|---|---|---|
enabled |
true |
关掉后不注入,但仍可用工具查询 |
memoryDir |
$DSH_HOME/workspace-memory |
记忆库根目录 |
maxInjectBytes |
8000 |
单轮注入预算,超出按顺序截断并声明 |
order |
130 |
上下文排序位置(working-directory 是 100,越小越靠前) |
contextName |
workspace-memory |
注册到 systemPrompt 的唯一名 |
distill.enabled |
false |
轮次结束后是否自动提炼候选 |
distill.maxCandidates |
3 |
单轮最多产出几条候选 |
distill.maxInputMessages |
12 |
提炼时读取最近多少条消息 |
distill.maxTokens |
700 |
提炼调用输出上限 |
distill.timeoutMs |
30000 |
提炼超时 |
distill.provider / distill.model |
空 | 留空沿用当前会话模型 |
工具(模型可调用)
| 工具 | 作用 |
|---|---|
memory_status |
当前工作区生效了哪些集/条目,命中的是哪条绑定 |
memory_set |
新建 / 修改 / 删除记忆集(支持 @otherSet 嵌套) |
memory_list |
列出全部条目与记忆集 |
memory_bind |
给工作区加/替换/删除绑定(下一轮生效) |
memory_capture |
把一段内容直接存为新条目 |
memory_candidates |
列出、采纳或丢弃自动提炼的候选 |
memory_distill |
立即对本轮对话提炼一次 |
于是「把 abc 和 acd 记到当前工作区」这种话可以直接落地:
用户:把 abc 和 acd 记到当前工作区
助手:(调用 memory_bind { sets: ["abc", "acd"] }) → 已绑定,下一轮生效
自动提炼
开启 distill.enabled 后,一轮对话正常结束时:
- 取最近若干条消息,按字节预算截断;
- 让模型输出 JSON 数组(
title/body/tags/scope); - 写入
candidates/,不直接生效; - 你用
memory_candidates确认后再晋升为正式记忆。
只生产草稿是刻意的:记忆会进系统提示,一条错误记忆的代价远高于漏记。
数据与隐私
插件只在本机读写 $DSH_HOME/workspace-memory/,不联网、不上传、不引入遥测。
记忆是纯 Markdown + 一个 state.json,可以直接 git 管理或整目录备份。
提炼功能开启时会调用你已配置的模型(走 DSH 的 LLM 通道),发送内容是本轮对话的最近若干条消息;关闭 distill.enabled 即可完全离线使用。
设计取舍
- 不做向量检索:预期记忆总量是几十条,全文注入 + 标签过滤足够,语义召回留到 v1 之后再评估。
- 不替换
AGENTS.md:AGENTS.md放项目级硬约定,本插件放可复用的软上下文,两者并存。 - Markdown 即数据库:一条记忆一个文件,人能读、git 能版本化、迁移零成本。
- 注入有硬预算:
maxInjectBytes是防止「记忆越长越有用」的自我膨胀。
更多细节见 docs/DESIGN.md。
开发
node test/logic.test.mjs
node test/store.test.mjs
node test/distill.test.mjs
纯逻辑(匹配、解析、渲染、解析模型输出)与存储层不依赖 DSH 运行时,可以直接单测;一次跑全部:
npm test
路线图
- v0.1 条目 / 记忆集 / 工作区绑定 / 预算注入 / 工具面 / 候选提炼
- v0.2 设置页内的可视化勾选(当前用配置与工具)
- v0.3 记忆冲突检测(同标题不同结论时提醒)
- v0.4 导出为
AGENTS.md片段
No comments yet. Be the first to write one.