← 返回列表
✓ 可直接安装
为 DeepSeek Harness 设计的极简长期记忆插件,融合了
自动检查通过:npm 包已发布且 engines 声明满足基线(声明 Node >=22.19.0);该结论来自程序自动检查,未经人工实机验证。 · 最近上游提交 2026/9/14 · 已提供中文文档
极简·省 token 的 DSH 长期记忆插件:零 LLM 自动捕获、符号索引渐进披露、ESR 证据闭环。Minimalist token-saving memory for DeepSeek Harness: zero-LLM auto-capture, symbolic index, ESR-lite evidence closure.
综合分
36.2
GitHub 分
36.2
用户评分
—
★ Stars
6
周下载量
—
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add dsh-engramnpm 包 dsh-engram 已校验归属本仓库,走 npm 安装最省事
数据截至 2026/9/19(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
安装兼容性检查✓ 自动检查通过
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✓npm 包dsh-engram @ 0.4.0
✓Node 引擎要求 >=22.19.0 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
验证方式:npm registry 存在性 + package.json 静态校验 · 最后验证 2026/9/19 20:59:05
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-storage-domain@deepseek-ai/dsh-settings@deepseek-ai/schemastery@deepseek-ai/dsh-compaction@deepseek-ai/dsh-compaction-basic@deepseek-ai/dsh-llm用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动
README
dsh-engram
· English · 已交付 UI 控件清单
CI
为 DeepSeek Harness 设计的极简长期记忆插件,融合了
symbolic-index 与 pi-esr 的思想——
目标只有一个:省 token。
- 零 LLM 摄入 — 纯模式匹配从工具结果自动捕获有意义的事件(带书面 -m 提交信息的 git 里程碑、
关键文件编辑、反复出现的错误),另有显式 engram_store。热路径上没有任何模型调用,纯粹的操作
——git push / git stash / 无提交信息的 commit——刻意从不记录(见下方「自动捕获策略」)。
每条写入先过一道确定性密钥脱敏器——API key、JWT、Bearer token、私钥、AWS/Stripe/Slack/GitHub
token 与 key=value 形密钥一律替换为 标记,且在去重哈希 / 字符上限 / 落盘之前完成,
敏感内容永远进不了磁盘。测试失败用纯模式识别(npm test / node --test / vitest/pytest/jest
+ 失败行)自动捕获为 tags:["error","test"]、信号上调。DSH goal 域打通:completed/blocked 的
goal(goal/change 会话事件)自动沉淀为 handoff/error 记忆(tag:goal),目标结局不随会话消散
(读取面见 GET /api/dsh-engram/goals)。
- 符号索引 + 渐进披露 — 一个紧凑的 [ENGRAM] 块(默认预算 700 字符 ≈ 175 token;每条记忆一行)在
组装提示词时注入,并按会话冻结,让请求前缀字节稳定以复用 KV 缓存。模型需要细节时用
engram_recall / engram_detail 下钻,而不是把命中的原文灌进上下文。召回对内存池做
进程内 BM25 排序(TF·IDF + 标签/短语加权 + 时间衰减因子,确定性、零依赖);
命中实体锚定的记忆时附带实体邻域关系简表(复用 esr_link:node --rel--> node · conf%);
与历史失败高度同源的新错误会唤醒旧 error 记忆(刷新 recency + 命中,向 promoteHits
爬升直至重回索引,失败不重复堆积);本地零命中时自动兜底到 DSH 自带的跨会话全文索引
(ctx.sessionQuery,按 cwd 过滤)——不另建 SQLite,完全复用宿主。当 FTS 兜底也返回空、
且查询含 CJK 中文(FTS5 中文分词整段只算一个 token)时,召回会对最近若干会话日志做
有界的子串扫描(zstd 解压 + LRU 缓存,限文件数与字节),追加命中会话的确定性
past sessions 行。
- 会话启动自动召回(autoRecallOnStart,默认开) — 纯拉取式设计有个被实证戳穿的盲区:
召回工具只交模型自觉,而真实会话里模型几乎从不主动调 engram_recall(实测 19 个会话数千次
工具调用只出现 ~5 次召回)。因此宿主在新会话首次组装时,用会话首条用户消息对工作区做
一次确定性 BM25 召回,把命中前 ≤3 条直接注入 [RECALL] 块(默认 700 字符预算,"少而准",
agentmemory 候选①)——相关记忆不依赖模型想起来就已在上下文里。纯规则、零 LLM、
纯读不 bump 命中;superseded 过期真相不占注入槽(仍可 engram_detail 重取);
与 [ENGRAM] 一起按会话冻结保持前缀稳定。
- 记忆间语义(supersede/contradict) — engram_store 可选收 supersedes / contradicts
记忆 id(同工作区校验)。被 supersede 的「过期真相」在召回里降级到尾部、并从 [ENGRAM]
块中剔除(新陈述占行);被 contradicts 的记忆保留排序但标注 · contradicted by 。
旧行永不删除——可重取,只是排得诚实。可选 autoSupersede 配置(默认关)对"实体锚定 +
替换式更新(改用/不再/no longer/switched…)"自动打 supersedes;显式 supersedes 永远优先。
- 失败→解法闭环(零 LLM) — error 记忆带 cmd: 签名标签;同命令随后跑通时自动沉淀
procedure 记忆(fixed: — N earlier failing runs now succeed),并把旧 error
行打上 resolved(不影响其排序),召回因此浮出解法而不是过期失败。
- ESR-lite 证据闭环 — esr_task / esr_close / esr_link 给任务一个 draft → active → stable
生命周期,其中 stable 必须要有真实证据(artifact / evaluation / memory_ref),把"缺什么"
摊在明面上,而不是让 agent 没有证据就宣布完成。可开 verifyArtifact(默认开):非 URL 的
artifact 按工作区(= 会话 cwd)解析并在磁盘上实存校验,路径不存在则任务保持 ACTIVE、给出原因;
force:true(或关掉该开关)可跳过磁盘校验——三种证据门照样必填。工具与网页表单共享同一个
证据门(store.evidenceGate),口径绝不会漂移。
任务不再是孤立节点:esr_task(entity=…) 一键挂点——自动建 ent_ 节点并挂
task --relates_to--> entity 边(幂等),挂着领域锚点的任务立刻带关系;
esr_dep 双写——依赖边同时进 tasks[].deps(blocker 逻辑)与 links 表,图谱里每条
任务依赖都是一等可见的边。
- ESR 触发机制(pi-esr 对齐,混合:静态协议 + 冻结快照 + 单调渐进 actionables + 拉取) —
前缀缓存稳定是核心。模型「何时用 ESR」靠静态方法论(engram:esr-method,每轮逐字节相同);
[ESR] 块 = 确定性冻结快照(会话开始定下、确定性排序、时间戳排除、明示 WILL NOT
auto-refresh)+ 本会话单调渐进的 actionables:promote:/root-cause:/close:/stale:/escalate:
在它们成熟的那一刻被一次追加进块并冻结(永不回撤)——前缀只在「真正出现新决策点信息」时变化、
绝不逐轮漂移,同时保留决策点提醒(漏斗最窄处 / 复发失败 / 收工 / stale / 平衡)。实时状态仍可
拉取:esr_status(全量视图 + since_revision 增量短响应 + 派生 actionables)。
recorder 由 tools/result 订阅喂入(actionables 的数据源);P4 转化度量照常(#suggest- 标记 +
10 分钟归因 + GET /api/dsh-engram/triggerstats)。纯规则、零 LLM。
- 会话结束 todo 自动沉淀(draft 兜底,autoSinkTodosOnEnd 默认开) — 会话内 todo 仍是
轻量、随会话而逝的"工作记忆";但当会话在其拆解边界(session/disposed)关闭时仍挂着 pending
todo,这些待办会自动转为该工作区的 ESR draft 任务(名称=todo 原文、描述「源自会话计划」、
按已存在任务名去重、受 maxTasksPerWorkspace 上限约束),计划从此不静默蒸发。落到 draft
而非 active:不占 [ESR] 的 active 行、不产生证据义务,仍需刻意的 esr_claim / esr_task
推进才变 active——「进行中默认不写」的取舍不变,只把「收尾丢数据」的缺口补上。可关(profile
patch 或设置卡)。
- 记忆 GC(pi-esr 约束) — 定时、机械、只归档的回收:TTL 过期记忆归档、超容量工作区淘汰低价值条目、
stable 任务超保留窗离开 [ESR] 表面、悬空链接边清理。工作集(active 任务引用 / 任务记忆 / 已入索引命中)
永不触碰;不硬删任何东西——归档条目保留 id、始终可重取。
- Context GC(自动 GC = 替代 DSH 自动 compact) — 不是记忆面板 GC:接管 DSH 自带的上下文压缩
(compaction 服务),把「有损 LLM 全量摘要」换成机械驱逐 + 重取指针——扫描被驱逐轮次里的
engram/ESR 锚(#记忆id、tsk_、ent_、file_path),只留一行「细节在 engram_detail /
engram_recall / [ESR] 里」;只有无锚轮次才走 scoped LLM 叙事兜底(gcNarrative,默认开、可关成纯机械)。
六条 GC 约束(working-set-protected / pointer-salience / no-provenance-no-evict…)全部落地;任何错误回退
默认压缩,永不破坏 compact。
- Web 查看器 — 一个带统计的记忆浏览器和配置卡片,完全构建在 DSH 原生设置槽位上(不碰任何第三方 UI 包)。
MIT · node >= 22.19 · host 半边 + 浏览器半边合在一包
为什么还要一个记忆插件?
对既有 DSH 插件生态的调研显示,记忆领域里"召回桥 / 审批门 / LLM 蒸馏 / 向量+图"这几个方向已经挤满了。
dsh-engram 补的是对 token 纪律真正重要的三个空白:
1. 写入路径没有模型 — 捕获是确定性的模式匹配。
2. 提示词里不灌全文 — 只注入有界的符号索引 + 会话启动时自动召回的最相关 ≤3 条(预算内),
更深的检索按需进行("检索到≠灌全文")。
3. 诚实的任务闭环 — 没有证据就不能宣布 STABLE。
DSH 已经提供跨会话 FTS(ctx.sessionQuery)、存储(ctx.storageDomain)、提示词注入钩子和设置槽位;
dsh-engram 只是这些能力之上的一层薄组合层,而非重新实现。
安全模型
DSH 插件生态还很年轻、缺乏背书:没有官方目录、没有签名校验,且 harness 的读写权限三档约束不了插件自身代码。因此记忆插件面对的信任何题比工具更高——它趴在你的提示词前缀上,还替你在磁盘上写东西。dsh-engram 的模型,直说如下:
- 永远不联网。 本插件没有出口 socket、没有遥测、没有更新探针。任何东西都不可能把会话带出本机。
- 无 shell、除一个存储文件外不碰文件系统。 不执行命令、不碰任意路径。全部数据只落在一个 storage-domain 单元 ~/.dsh/storages/dsh_engram.json(外加经 DSH 自身 ctx.sessionQuery 做的会话日志读取)。
- 密钥在落盘之前就被脱敏。 每条写入路径——engram_store、自动捕获,以及本节的导入/恢复——都先把文本过一遍确定性的 redactText 规则引擎(API Key、JWT、Bearer token、私钥、key=value 密钥形态),之后才做去重哈希、字符上限与落盘。敏感文本永不落盘,召回时自然也不会有。
- 要做什么坏事,必须借你 harness 自己的权限。 插件只通过你已信任的工具同款的 seam 向宿主"请求"做事;不扩沙箱策略、不拉高到 danger-full-access——你保持 profile 默认,engram 就只继承那些边界。
- Web API 只读为主、loopback 栅栏。 /api/dsh-engram 下的 GUI 路由拒绝任何非回环调用方,除非你用 trustedHosts 显式放行某个主机名;即便放行,同源栅栏依然生效。
- 可核验。 npm run dsh-engram -- doctor(scripts/dsh-engram.mjs doctor)报告插件被配置去碰什么;整个 store 可检视、不透明的东西为零。如果对这一段有怀疑,要查的代码就是 lib/redact.js、lib/store.js 和本插件的 cordis.patch.yml。
这不是说它是个气闸:如果你的 profile 给了 agent danger-full-access 或 shell,agent 可以用——engram 是记忆层而不是沙箱。上面这句话的意思是:dsh-engram 自己不新增任何攻击面。
数据契约:导出 / 导入
记忆不该当人质。四张表(memories / tasks / links / entities)可以用一个稳定、带版本号的机器可读格式整体倒出再恢复——重装、换机,或者(写一层薄适配器之后)搬到别的 harness / MCP 端点 / Claude Code skill,语料都跟得走。契约是带自描述头的纯 JSON:
GET /api/dsh-engram/export?workspace=/path/to/project # 单个工作区
GET /api/dsh-engram/export # 全部工作区
{
"meta": { "format": "dsh-engram/export", "version": 1, "exportedAt": 1724…, "workspace": "/path/to/project" },
"memories": [ / 全部行,含归档——备份不丢溯源 / ],
"tasks": [ / draft/active/stable + 归档 / ],
"links": [ / 类型化边 / ],
"entities": [ / 图节点 / ]
}
恢复默认幂等、破坏性操作有门禁:
POST /api/dsh-engram/import # body: { payload, mode, dryRun?, workspace?, confirm? }
- mode: "merge"(默认)— 只写 id 尚不存在的行,备份可以随便重复套用而不会重复;每行保留自己的 workspace。
- mode: "replace" — 先把目标 workspace 的四张表清空,再只恢复属于它的行。破坏性操作,因此 body 里还要求 confirm: "restore"。
- dryRun: true — 只算出完全相同的执行计划(会写什么 / 跳过什么),不写任何一行。
- 导入遵守与 storeMemory 相同的不可变约定:记忆文本落盘前先过密钥脱敏;会破坏契约的行(缺 id/workspace、空文本、超 maxMemoryChars、merge 时 id 已存在、超工作区记忆上限)会被跳过并写进响应报告,而不是中止整批。
单工作区的恢复往返:
curl -s "http://127.0.0.1:3080/api/dsh-engram/export?workspace=$PWD" -o engram-backup.json
curl -s -X POST http://127.0.0.1:3080/api/dsh-engram/import \
-H "content-type: application/json" \
-d ""
兼容性
- DSH 版本:>=0.1.2-alpha.2 一句话结论:所有已真正发布的版本(0.2.0 … 0.3.6)只支持旧版
DSH(0.1.0-rc.7 / 0.1.1 API 图,peers ^0.1.0-rc.7)。DSH
0.1.2-alpha.2 及更新版本与它们不兼容——settings/client API 族在该版本
断裂。新版 DSH 支持自 0.3.7 起提供——它是新 API 代际的第一个发布(见下文)。
| dsh-engram 发布版本 | 状态 | 支持的 DSH | 声明方式 |
| --- | --- | --- | --- |
| 0.2.0 … 0.3.6(npm 全量 + GitHub v0.3.3–v0.3.6) | 已发布 | 仅旧版 —— 0.1.0-rc.7 / 0.1.0-rc.8 / 0.1.1-rc.1 / 0.1.1-rc.2 | peers @deepseek-ai/dsh-@^0.1.0-rc.7 |
| 0.3.7(npm + GitHub v0.3.7) | 已发布 | 仅新版 —— >=0.1.2-alpha.2 =0.3.7。
当前发布版本 —— 0.4.0
当前发布版本声明适配 DSH >=0.1.2-alpha.2 =0.1.2-alpha.2 =22.19.0 |
0.1.2-alpha.2 是这组 API 的最低版本:宿主设置注册使用
settings.installSection,浏览器端设置卡通过插件自有 HTTP 路由
(/api/dsh-engram/settings,见上文 Route B)读写。
0.1.2-alpha.2 和 0.1.3-alpha.2 已完成宿主导入与设置注册 smoke 验证;
0.1.5-alpha.1 已完成完整回归(并在 dsh 0.1.5-rc.1 隔离 profile 上真机验证
设置 GET/PUT/冲突/复位/跨重启持久化全通)。旧的 0.1.0-rc.7 与 0.1.1 API 图不在
本发布版本的兼容范围内。
如需按验证版本安装 DSH:
npm install --global @deepseek-ai/dsh@0.1.5-alpha.1
dsh --version
安装
从 GitHub(本仓库)
dsh plugin --profile web add github:skepsun/dsh-engram
发布到 npm 之后
dsh plugin --profile web add dsh-engram
本地开发(符号链接——改动立即生效)
dsh plugin --profile web add link:/path/to/dsh-engram
然后重启 dsh web。数据保存在 ~/.dsh/storages/dsh_engram.json。
npm 与 GitHub 两种装法都不需要手动补依赖:pnpm 会自动安装 zod,
并把可选的 @deepseek-ai/ peers 嵌套装进插件自身的 node_modules,CLI 也会
自动把插件登记进 profile 的 dsh.profile.bundles。下面的 setup-links 只在
link: 开发工作流里需要——pnpm 故意不为符号链接目录安装依赖。
需要新建会话才能看到注入的 [ENGRAM]/[ESR] 块和全部工具——提示词与工具注册表都是按会话装配的。
link: 安装的依赖准备
符号链接安装的插件从自身 checkout 的 node_modules 解析 import,而这层依赖
不被 git 跟踪,换机器(尤其 Windows)会报
ERR_MODULE_NOT_FOUND: Cannot find package 'zod'(接着是 @deepseek-ai/ peers)。
一条命令重建依赖层:
cd /path/to/dsh-engram
node scripts/setup-links.mjs # 把 @deepseek-ai 工作区包软链进 node_modules,
并安装 zod(优先复用 harness pnpm store,
找不到则回退 npm install)
脚本会自动定位 harness:../deepseek-harness(仓库上一级平级),也支持「仓库父级平级」布局
(如 E:\deepseek-harness 与 E:\kototoro_demo\dsh-engram)——都找不到再用 DSH_HARNESS_DIR 指定。
node scripts/setup-links.mjs --check 只打印状态不写入。
在 Web 端能得到什么
重启后,全部落在 DSH 原生设置界面里:
- 侧边栏「ESR 看板」入口 + 全屏看板 — 侧栏(New Session 下方)新增一行 ESR 看板 入口,右侧带实时活动任务数徽标(30s 轮询 /overview 汇总各工作区 active 任务)。点击在中间列打开全屏看板:草稿 / 进行中(证据缺口) / 就绪(证据齐) / 已闭环 四列 + 工作区筛选 + 搜索 + 内联新建表单 + 每张卡片的「补齐证据 → 关闭」表单(与 esr_close 同一证据门:artifact + evaluation + memory_refs)。头部带「看板 / 图谱」切换:图谱视图复用完整的关系图谱(esr_node/esr_link 力导向图,实体圆节点 + 任务勾选徽标,支持拖拽/缩放/点选查看关系明细),跟随工作区筛选,20s 轮询保持实时。入口与看板按 task-board 的 DOM 级挂载惯例自愈(MutationObserver 重插/重挂),并与 task-board / ssh 面板做跨面板互斥(打开本面板会关掉对方,点侧栏会话/项目行自动回到对话)。对话子树始终挂载在下方、由 html[data-dsh-engram-board-active] 控制显隐,切换零状态丢失。
- 输入框上方的「任务」统一条 — 接管 DSH 内建 todo 工具的自己同款 dock 槽位
(同一个 conversation.input.dock 单元格 / id: todo、更低 priority,从而遮蔽内建
TodoPanel),把两套任务平面合并成一个现代化控件:会话当前计划(todo_write 的
todos 投影)+ 工作区持久 ESR 任务(证据缺口徽标 + 内联「补齐证据 → 关闭」表单)
+ 关系图(以 节点 → 关系 → 节点 芯片呈现,实体/任务名自动解析)。有内容才显示,
15s 轮询保持实时;若 loopback 围栏的 API 不可达,内建计划仍照常渲染(只是不显示 ESR 部分)。
条首有工作区切换 chip:默认跟随当前会话(标题注明),下拉可把 ESR 任务/关系的来源固定到任意
工作区(打 ✓ 标记),再点 × 或「跟随会话」即恢复;切换即时重取,内置 todo 仍属本会话——纯 UI 焦点
切换,不动模型会话上下文(注入块按会话冻结,前缀稳定)。
- 设置 → Engram 记忆 — 独立的一级设置页签(位于「插件」之后),不再是「插件」页里的子 tab;默认
「全部工作区」视图完整展示所有工作区的记忆/任务/关系(按工作区分组,工作区下拉 + 上一/下一工作区
翻页;记忆表格另行 10 条/页分页 + 跳页下拉,仅「类型 / 内容 / 操作」三列——正文列占满,时间、
标签、signal/hits/TTL 等全部折叠进内容行内(meta 行 + 标签行),正文限高三行省略、
行内「展开全文/收起」与 hover 均可看全文,归档/删除按钮竖向堆叠)。概览统计卡片(各工作区/类型的计数、自动捕获总量、各工作区 [ENGRAM] 索引
token 估算、GC 累计统计)、可搜索/可过滤的记忆表格(含归档与删除操作)、ESR 任务看板(「新建任务」
表单 + 点击「填写证据关闭…」补 artifact/evaluation/memory_ref 转 STABLE)、节点与关系清单
(节点 = 模型用 esr_node 登记的领域对象,如包/服务/仓库/概念;关系 = esr_link),
以及一个独立的 关系图谱 页签:手写 SVG 力导向图(无第三方图库,保持 bundle 纯净),
实体为圆形节点、任务为勾选徽标、关系按类型着色带方向箭头;支持拖拽节点/平移/滚轮缩放/重组,
悬停高亮邻域、点选节点在悬浮面板查看其全部关系与关联对象,悬空链接(端点缺失)单独计数提示。
以及一个 注入预览 页签:用与 systemPrompt 完全相同的纯函数实时渲染模型每个会话看到的
[ENGRAM] 索引块(order 40)与 [ESR] 任务/闭环块(order 41),终端风双栏展示(行级着色:
块头/任务行/drill 行/escalate 提醒高亮),附行数·字符·~tokens 成本与记忆/任务/关系/节点计数芯片,
每 20s 自动刷新、可一键复制注入块原文(新增 GET /api/dsh-engram/preview?workspace=…)。
以及记忆 GC 面板(dry-run 开关 + 运行按钮 + 指针报告)。
任务卡片(看板与 ESR 页)都带 证据进度环:一个三弧 SVG 圆环对应
artifact · evaluation · memory_ref 三道闭环门——全绿=证据齐可闭环、琥珀=有缺口、灰=尚无证据;
看板头部还有一个聚合环,显示全部进行中任务的证据完备度(%)与就绪数,一次看清整盘闭环进度。
纯 SVG 实现(无图表库,保持 bundle 纯净)。
以及一个 遥测仪表盘 页签:把 /stats 的真实调用累计(工作区 × 天滚动)画成纯 SVG 仪表盘——三枚大圆环
直读 ESR 主动性(与 escalate 阈值 0.34 比对,偏低标橙并提示)、召回命中率、detail 转化,
五张小指标卡(累计调用 / esr / 记忆 / 平均命中每查询 / 失败),近 14 天 mem-vs-esr 堆叠柱状图 +
工具调用 Top 8 横向条形图(mem 蓝 / esr 紫,与全文配色一致),20s 自动刷新,样本不足(] 来源标记) | 读 |
| engram_detail | 一条记忆 id 的完整记录(来源、标签、命中数) | 读 |
| esr_task | 创建任务实体(draft → active) | 写 |
| esr_close | 按证据协议关闭任务(artifact + evaluation + memory_ref) | 写 |
| esr_link | 在两个实体之间添加类型化关系(迷你图) | 写 |
| esr_dep | 在任务间添加依赖边(blocks / relates-to / parent-of) | 写 |
| esr_claim | 原子认领任务(assignee + claimedAt,draft → active) | 写 |
| esr_unclaim | 释放已认领任务的 assignee | 写 |
| esr_ready | 列出可认领任务(无 blocker、无人认领) | 读 |
| esr_status | 拉取实时 ESR 状态 + 派生提示(since_revision 增量短响应) | 读 |
| esr_node | 创建/更新实体节点(稳定符号) | 写 |
| esr_gc | 运行本工作区的记忆 GC(dry_run:true 预览不落库) | 写 |
| esr_model | 工作区预计算心智模型(brief/full,max_chars) | 读 |
[ESR] 块是每会话冻结快照,绝不中途刷新——要最新状态调 esr_status。
GC:两块——记忆面板回收 + Context GC(替代自动 compact)
记忆面板回收(存储维护)
定时回收(gcIntervalHours,默认 24h)+ esr_gc 手动触发 + GUI 按钮,按 pi-esr 方式把存储保持在
有界内——机械、工作集保护、只归档:
- TTL 过期记忆归档(软删;id 保留,可通过 GUI 的 archived 筛选检索);
- 超容量工作区淘汰最低价值的非保护记忆;
- stable 任务超 gcStableRetentionDays 归档、离开 [ESR];
- 两端点都已消失的链接被清理(悬空边)。
GC 永不触碰工作集:active 任务 memory_refs 引用的记忆、task 类记忆、已入索引的命中
(hits >= promoteHits)。用 esr_gc + dry_run: true 先预览。不硬删——报告的末尾为所有
归档项附上重取指针,归档可恢复、不是丢失。
Context GC(替换 DSH 自动 compact)
DSH 默认的上下文压缩是有损 LLM 全量摘要(compaction-basic):被驱逐的历史被压缩成散文,
不可查询、摘要本身还烧 token。dsh-engram 的自动 GC 替代它:ContextGcEngine extends
BasicCompactionEngine 只重写唯一的 summarize() 钩子,把摘要正文换成:
1. 扫描被驱逐消息的 provenance 锚——engram_store/engram_recall/engram_detail 回显的
#记忆id、esr_ 涉及的 tsk_/ent_、file_path 锚(esr_gc 等管理工具不算锚);
2. 指针摘要:每条被驱逐类别都带显式重取调用(engram_detail(id: "…") / engram_recall(query)
/ [ESR] 块 / esr_ready),active 工作集在摘要里复述不驱逐;
3. 兜底叙事:只有无锚轮次(纯对话/推理)才走 scoped LLM 摘要(gcNarrative,默认开;关掉后整条
路径零 LLM,无锚轮次截断原文保留)。
触发时机完全跟随 DSH 现有 compact(step pressure / context-overflow / /compact);锁、回放校验、
tool-call/result 配对、token 定价全部复用基本引擎。任何错误 → 回退默认压缩,永不破坏 compact。
装配即注册 compaction 服务,卸载/reload engram 自动还回默认引擎。
收缩闸门(shrink gate):harness 拒绝任何不比被驱逐片段更小的 checkpoint(summary is not
smaller than the shadowed content),否则压缩回滚、上下文永不缩小、最终触发模型侧溢出。Context GC
据此自预测闸门:用 host tokenMeter 复刻 harness 的 framing 估算(实测逐 token 一致),把指针
摘要/叙事体按被驱逐 span 的 token 预算动态裁剪尾部(指针头与工作集保留,细节仍在会话日志可
重取),保证 checkpoint 严格缩得更小、压缩真正提交——不出现"start 涨、summary 不涨"的假接管。
完整用法见 docs/CONTEXT-GC-GUIDE.zh.md(从 0 开始的使用教程)。
启用入口(这一节就是答案:该功能开在哪)
Context GC 的装配在两个平面——web 现在全部自动,零配置:
- host 平面(headless / TUI / base 型 profile)——开箱即用,无需任何配置:
主插件 lib/index.js 在 ctx.effect 里直接
mountCompactionEngine(ctx, resolved, { readWorkspace }) → new Engine() 注册 compaction 服务;
配合插件 patch 禁用基座 compaction-basic 行,engram 即为该平面唯一 provider。
- gcReplacesCompaction: true(默认)→ ContextGcEngine(机械驱逐 + 重取指针);
- gcReplacesCompaction: false → 挂裸 BasicCompactionEngine(退化为 DSH 默认 LLM 摘要),
compaction 服务永不缺席。
- 关闭/开启:profile patch 里给 engram 行加 config: { gcReplacesCompaction: false },或设置卡
「记忆 GC」区的 gcNarrative 关掉叙事。
- preset 平面(web profile)——全自动,零配置,覆盖全部预设:web 面把 compaction 放在 agent
preset 的 isolate realm(shipped 预设自挂 compaction-basic),host 条目进不去、profile patch 也
够不到预设文件(mountPreset 的 Include 不带 patches——已对源码核实)。所以插件在启动时
(agentPresets 服务就绪后)自动改写每个仍处于出厂 stock 布局的预设——默认预设 + 整张 roster
(shipped 根 + ~/.dsh/.agent-presets 用户根,standard/code/cordis 和你自己的预设都覆盖):
把 compaction 组里的 compaction-basic 行换成 dsh-engram/compaction。这一自动装配是:
- 默认开(autoWebCompaction: true,设置卡「记忆 GC」可关),幂等——已替换则跳过;
关掉的含义:只是在之后的启动时不再自动接管(headless/TUI/base 的 host 平面不受影响)——
它不会还原之前已接管的预设,要还原用 npm run web-compaction:revert;
- 逐预设判定,只碰 stock 布局——用户自定义过 compaction 组的预设,以及根本没有 compaction 组的
预设(如 shipped minimal),一律不写、只记日志;
- 带备份 + 严格校验:每个文件写前在旁边留 agent.cordis.yml.engram.bak(create-only,保留首次
原件),写后重读校验,任何一步可疑即回滚该文件;改写失败只 warn,该会话仍用 DSH 默认摘要,
永不影响宿主。
- 配置真实传导:写进预设行的引擎配置来自设置卡/配置里的 gcReplacesCompaction 与
gcNarrative——改了这两个开关,下次启动会自动把已接管的行刷新成新配置(幂等重写),
所以设置卡对 web 平面同样生效,不再硬编码 true/true。
- 手动的完全等价物(预览 / 审计 / 卸载前回归,随 npm 包发布的 CLI,默认扫 shipped + 用户两个根、
可 --file 指定单个):
npx dsh-engram status # stock → 未动;wired → 已接管;custom → 不碰
npx dsh-engram doctor # status + 按缺口排序的下一步建议
npx dsh-engram enable # 等价于启动时自动装配(通常 no-op)
npx dsh-engram revert # 还原全部预设的 stock 行(卸载 dsh-engram 前先跑这个)
(仓库内 npm run web-compaction: 是同一 CLI 的别名。)
替换后的 compaction 组形如:
- id: compaction
name: cordis:group
group: true
isolate:
compaction: true
toolResultPruner: true
config:
- id: engram-compaction # ← 替换原 compaction-basic
name: dsh-engram/compaction
config:
gcReplacesCompaction: true # 来自设置;false=该 session 用默认 LLM 摘要
gcNarrative: true # 来自设置;false=纯机械无 LLM
- id: command-compact
name: '@deepseek-ai/dsh-command-compact'
- id: tool-result-pruner
name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
原有配置保留
⚠️ 卸载前先 revert:预设引用 dsh-engram/compaction 后,卸载 dsh-engram 会让该行悬空、
web 会话挂 loading。所以卸载 engram 前先 npx dsh-engram revert(还原全部预设,
或逐个恢复各自的 .engram.bak)。harness 大版本升级会覆盖 shipped 预设,悬空引用随之自愈。
从 npm 安装后的第一印象(可感知 / 易引导 / 易配置)
- 自动:装进 profile 后启动一次即可——host 平面由 mountCompactionEngine 直接接管;web 平面
由 autoWebCompaction(默认开)在启动时自动改写全部 stock 预设。无需任何手动配置。
- 可感知:插件启动时把权威状态写到 $DSH_HOME/engram/context-gc.status.json
(host 平面模式 + web 平面每个预设的接管结果 + 生效配置)。随时
npx dsh-engram status 或 npx dsh-engram doctor 查看;web 记忆看板(ESR 任务看板)头部也有
一枚状态徽标("Context GC·主机 ·N 预设",来自 overview API)。启动日志也有两行关键确认:
engram context-gc: compaction = Context GC … 与 engram web-provision: wired Context GC into preset …。
- 易配置:一个设置卡(「记忆 GC」区)管全部:autoWebCompaction(web 自动接管开关)、
gcReplacesCompaction(用 Context GC 还是回退默认摘要)、gcNarrative(叙事兜底开关)——
改动后 dsh web 重启生效,web 预设行会被自动刷新成新配置。
验证是否生效:dsh web(或对应 profile)重启后——
- host 平面(headless/TUI/base):启动日志出现
engram context-gc: compaction = Context GC (mechanical eviction + re-fetch pointers);
- web 平面:日志出现
engram web-provision: wired Context GC into preset "standard" (…); restart dsh web so sessions pick it up
(每个被接管的预设一行);或 npx dsh-engram status 显示 wired;任意预设的会话内
command-compact/压力触发即走机械驱逐 + 重取指针。
- (若见 … keeping the existing compaction service 说明同域已有 provider、未替换成功;web-provision … skipped (custom)
说明某预设是自定义/无 compaction 布局、未被触碰;dsh-compaction-basic unavailable 说明环境缺依赖,自动回退默认压缩。)
BoundaryPrune(子任务边界剪枝,默认关)
Context GC 管「压缩的摘要内容」,BoundaryPrune 管「确定性剪枝的触发时机」——
在 todo 完成推进 / goal 终态这些子任务边界,主动调用 DSH 自带的
toolResultPruner(确定性 surface-replace,零 LLM),把早已超阈值的旧工具
结果剪掉头尾保留,省掉它们在后续每个请求里的整段重放。两个开关互相独立。
- 证据链(docs/ORACLE-ANALYSIS.zh.md +
docs/PROPOSAL-resultpack.zh.md):97 个真实
会话实测,工具结果重放暴露 2.77B–7.53B 字符(≈692M–1.88M token),96% 的
结果字节来自 bash/read/run_code;反事实仿真显示 todo 边界覆盖 79% 的暴露,
且偿还门在所有配置下提升净收益 12–32%。
- 偿还门:剪枝会重写前缀(KV 缓存击穿一次),只有当会话预计还要跑
boundaryPruneMinRemaining(默认 50)个以上请求时才 fire——预计长度用
workspace 历史先验 + 超过后 ×2 外推(有界乐观),长会话不会被挡死。
- 诚实激活面:goal=0 且无 todo 推进的会话天然不触发(无边界不剪,零副作用);
全量干跑 77 会话激活 38%。pruner 服务缺席即自动禁用并打一条日志。
- 默认关:boundaryPrune: false。开启后启动日志出现
engram boundary-prune (todo @N reqs): pruned …。
自动捕获策略
捕获是确定性、离线的——只看到工具结果,从不看对话本身。什么会被记录成一条记忆:
| 工具结果 | 行为 | 信号 |
|---|---|---|
| git commit … -m "提交信息" | 记录——书面提交信息就是这条记忆 | 0.55 |
| git merge / rebase / cherry-pick / tag / checkout -b | 记录(里程碑) | 0.5 |
| git push / git stash / 无 -m 的 commit | 跳过——操作回显,不是决策 | — |
| 写入/编辑关键配置与文档路径 | 记录 | 0.3 |
| 读取配置路径 | 记录 | 0.3 |
| 反复出现的工具错误 | 记录(按消息去重) | 0.25 |
显式 engram_store 的记录不受上述规则约束(按会话限流)。
谁能拿到 [ENGRAM] 索引行(这才是真正进提示词的部分):
signal >= minIndexSignal 或 hits >= promoteHits 或 kind === "task",
再由 indexMaxLines / indexMaxChars 封顶。另有一道额外的闸保持管道干净:
自动捕获的 git 命令回显——文本里嵌着 shell 命令链(git push: cd … && …)——
即使信号超阈值也不进索引,直到被召回命中晋升为止。其余条目安静地躺在存储里,
按需用 engram_recall / engram_detail 取用——「检索到 ≠ 注入」。
注入块
模型实际看到的内容(每个会话渲染一次,然后冻结):
ESR 操作协议(静态,每轮逐字节相同)
1. 动工前:esr_ready 看可认领工作;esr_status 拿实时状态。
2. 多步工作 → esr_task(draft 起步);动工 → esr_claim;收工 → esr_close(三证齐)。
state 是唯一真相:拿不准 state 就 call esr_status。
[ENGRAM] workspace: symbolic-index · 2 memories · 1 task(s) active · 0 links
[D] 06-18 Decided: use sqlite-vec for retrieval #a2331d87
[T] 06-18 Retrieval upgrade — ACTIVE · gap: artifact, evaluation, memory_ref #tsk_8b26
drill: use [RECALL] below · engram_detail (full record) · engram_recall (more) · esr_task/esr_node/esr_link (work)
[RECALL] recall · 1 hit(s) · first msg: retrieval
- [D] 06-18 Decided: use sqlite-vec for retrieval upgrade #a2331d87 ×2
context: use above · engram_detail · engram_recall
[ESR] tasks: 1 active / 1 stable
- tsk_0d: Retrieval upgrade — ACTIVE · gap: artifact, evaluation, memory_ref
- closed: tsk_9a (RAG eval) · +1
snapshot from session start — WILL NOT auto-refresh; call esr_status for live state
this-session actionables (frozen)
promote: 2 pending todo(s) vs 1 ESR task(s) — esr_task(name="…") #suggest-promote
前缀:[D] 决定 · [E] 错误 · [P] 流程 · [F] 事实 · [I] 洞察 · [H] 交接 · [T] 任务。
被反复使用(hits >= promoteHits)的流程记忆升格为「实证经验」——前缀变 [P✓] 且稳定排在
索引块最前(每组内部仍按新旧排序,块保持确定性);这是我们对 TencentDB Agent Memory「Skill=
经过验证的可执行经验」的零 LLM 对应物。
入选规则遵循「自动捕获策略」(信号阈值 / 命中晋升 / git 回显守卫),并按配置的行数与字符预算封顶。
id 通过 engram_detail 取完整记录。工作区没有任务时,[ESR] 仍会渲染一行点名 esr_task/esr_close,
让机制对模型保持可见,而不是整体消失。
[RECALL](会话启动自动召回,autoRecallOnStart)按会话首条消息对工作区做确定性 BM25 召回,
把命中前 autoRecallLimit 条注入,受 autoRecallMaxChars 字符预算约束;无命中或空工作区时不渲染。
它是一条提示而不是全文灌入——模型该把它当成"这条相关,可能需要用 engram_detail 看细节"。
纯读、不 bump 命中、superseded 过期真相不占槽位;随 [ENGRAM] 一起按会话冻结。
配置
默认值以 token 为优先;可通过 profile 补丁(~/.dsh/profiles/web/cordis.patch.yml)或 Web 配置卡片覆盖任意键:
- id: engram
config:
autoCapture: true # 零 LLM 工具结果捕获
sessionSearch: true # engram_recall 也可对历史会话 FTS
recallScope: workspace # 召回范围:workspace(严格工作区隔离,默认)| global(可选跨工作区召回,带 [W:] 来源标记)
autoRecallOnStart: true # 会话启动自动召回:按首条消息注入 [RECALL] 块(false=回到纯拉取)
autoRecallLimit: 3 # [RECALL] 最多注入条数(少而准)
autoRecallMaxChars: 700 # [RECALL] 字符预算
autoCapturePerSession: 40
indexMaxLines: 12 # [ENGRAM] 行数上限
indexMaxChars: 700 # [ENGRAM] 字符上限(token 预算)
minIndexSignal: 0.4 # 低于此信号的自动捕获不进索引
(git 命令回显即便超阈值也不进,直到命中晋升)
promoteHits: 3 # ……直到被召回这么多次才进索引
expireDays: 180 # 记忆 TTL(0 = 永不过期)
maxMemoriesPerWorkspace: 2000
gcEnabled: true # 定时记忆 GC
gcIntervalHours: 24 # 回收节奏
gcStableRetentionDays: 120 # 超过此天数的 stable 任务离开 [ESR]
gcReplacesCompaction: true # Context GC:接管 DSH 自动 compact(false=回退默认 LLM 压缩)
gcNarrative: true # 无锚轮次走 scoped LLM 叙事;false=纯机械(零 LLM)
boundaryPrune: false # BoundaryPrune:todo/goal 边界主动剪超阈值旧工具结果(默认关)
boundaryPruneMinRemaining: 50 # 偿还门:预计剩余请求数低于此值不剪
engramIndexOrder: 40 # systemPrompt section 顺序(位于 tools 段之前)
esrOrder: 41
开发
npm test # 152 个测试:核心 + Web API + GC + Context GC + ESR 触发(node:test)
npm run build:client
仓库结构:lib/(宿主半边:store / capture / index-block / tools / api / settings)、
client/(浏览器半边,TSX + build.mjs)、test/(node:test)。
故障排查
Web 界面一打开就停在 “Failed to load plugins”,loader 报错形如:
failed to apply loader entry … (@linxin666/dsh-client-ui-web-ui-settings):
keyed slot "settings.plugin.item" requires options.key
原因:DSH 自 0.1.0-rc.7 起把配置卡槽位 settings.plugin.item 声明为按
settings 命名空间键控(卡片用自身编辑的命名空间作 key 注册——dsh-engram
的配置卡正是用 key: "dsh-engram" 这样注册的)。@linxin666/dsh-web-ui-all
0.2.0 之前的 dsh-client-ui-web-ui-settings 向该槽位注册分组卡片时没有
提供 key;而 loader 只要有一个 entry 失败就会中止整个启动流程,于是 GUI
一直卡在失败页。
修复方式:
- 正确修复——升级全家桶:@linxin666/dsh-web-ui-all@^0.2.x。0.2
系列已把自身设置面从键控槽位迁出,改为一级 settings.section(上游正是
为这个报错做的修复)。
- 临时解阻:在已安装的
node_modules/@linxin666/dsh-client-ui-web-ui-settings/lib/client.js 中给那
个 settings.plugin.item 注册补上 key: "web-ui-plugins",然后重启
dsh web。(在按命名空间键控的派发下,分组卡片只是不显示,不影响页面其
他部分。)
相关项目
- symbolic-index — 原始跨会话记忆插件(5 信号 RRF 融合、sqlite-vec、Dream Engine)。
- pi-esr — 项目全周期的证据驱动任务状态;这里的闭环协议是它的简化形态。
许可
MIT扫码进群