@dafantong01/dsh-memory-palace
Local long-term memory for DSH sessions: archives older turns under context pressure, with four tiered hot-memory boards, project documents, memory families with editable family names and root transfer that preserves project hot memory, summary keyword locator with segment-level candidates and pagin
Install
dsh plugin --profile web add @dafantong01/dsh-memory-palace
Sandbox test results
| Stage | Result |
|---|---|
| 1 install | ✓ |
| 2 list | ✓ |
| 3 mount | ✓ |
| 4 boot | — |
Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).
README
记忆回廊(dsh-memory-palace)
把会话原文按「一轮对话」为单位、分类归档到当前工作区的 memory-palace/ 目录。
目的:在 DSH 自动压缩之前,先把较早的完整原文落到磁盘,压缩后还能查回原话。
0.3.7 发布版:本地长期记忆。归档较早的整轮原文,提供四板块分级注入、项目档案与项目热记忆、记忆家族与根转移、摘要关键词定位与候选翻页、手动注入、20K 分段、tag 关联检索、归档触发快路径与压缩恢复;数据都在当前工作区。
0.1.x 的归档基础说明仍适用。
功能总览
- 热记忆四板块:
identity(角色自我)、user 板块(用户画像)、rules(规则铁律)、memories(关键记忆)。 - 分级注入:
mind-seal每轮;important每 5 轮 / 10k 输出 token;normal每 12 轮 / 30k;minor压缩后一次。共享计数器,受总预算约束。 - 项目档案:会话级 Markdown 文档,支持新建/编辑/启用/停用/归档/取消归档/软删除;启用档案进入小型注入清单,停用档案可按需读取,归档档案模型只得到错误;正文按 20K 分段加载。
- 项目热记忆:按
projectLabel绑定项目档案,可挂任意板块;项目启用才注入,停用/归档不注入、不占预算;每个会话独立存储,同名项目可并存,支持显式跨读。 - 记忆家族:母归属/子会话/脱离/让位/解散/根转移;母归属可单独修改家族显示名,不改文件夹与成员;子会话的全局热记忆只读,但可以拥有自己的项目热记忆;长记忆互读;文件锁、journal、选举、根转移与旧根只读。
- 自动捕获:严格命令式只负责识别,由模型按语义写入对应板块;非命令式由模型自行判断;规则只进候选。
- 手动注入:页签条目可「立即注入」;下一轮注入带【手动注入】段。
- 检索关联:项目档案检索命中后,按同项目或共享 tag 自动补最多 3 条相关文档。
- 归档快路径:压力超阈值时只用 tokenMeter 触发,检测不再全量解码日志;真正归档时才解码校验。
- 摘要定位:
palace_summary(keyword=...)先扫月度摘要,返回年月/类别/轮次;本会话未命中时可按配置回退家族、全库搜、询问用户,或仅停在候选摘要。候选按段取:每月最多 1–6 段(默认 3),用本地词法打分排序,整段摘要返回;每页最多 3 个月,月数上限 = 保底月数 + 增量(0–18),用返回的candidateCursor翻页。 - 彻底压缩时的归档:勾选「归档后彻底压缩」并手动归档时,除最后
archiveTailTurns(默认 2)轮外全部归档;不勾选仍按原保留窗口。 - 缓存自检:只统计缓存实际追加/重建,每 20 次写入做一次全量解码校准(可调 10–100);重建后自动清理 manifest 未引用的旧 chunk。
- 检查/安装更新:页签检查更新;安装更新需用户确认,失败可回退。
- 本地优先:归档、热记忆、项目档案、家族状态都在当前工作区
memory-palace/;不依赖外部记忆服务。
安装
dsh plugin --profile <profile> add @dafantong01/dsh-memory-palace
安装后重启 DSH,在会话页签打开「记忆宫殿」,给会话命名后才会归档。
已知限制
- 不做 embedding;检索是关键词/时间召回,口语化或跨义描述可能搜不到。
- 不自动注入项目档案全文;按需用
memory_doc_search/memory_doc_read。 - 归档、项目文档、热记忆的写锁以单 DSH 实例为前提;多实例不做强一致保证。
- 可与其他记忆工具并存;迁移前先备份并人工抽查。
- UI 的提示位置、原位编辑、保存后排序属于已知低优先级体验项。
工作方式
1. 触发检查:每 checkEveryTurns 轮(默认 5),在回合收尾处检查一次。要同时满足两道门:
- 压力门:上下文占用 ≥
triggerPressure(默认 60%)× 窗口。
占用取 DSH 自己的计量 ctx.tokenMeter.measure(session).totalTokens——压缩后它会回落;
拿不到计量服务时才退回「日志轮次累加」,并在提示里标注「日志估算」。
- 批量门:可归档轮数 ≥
minBatchTurns(默认 10)或 可归档文本量 ≥ 无需归档量 ×batchByteFraction(默认 50%)
2. 提醒:两道门都过,插件注入一条极短提示,请模型调用归档工具(enableSteer:false 可关掉,关掉就没有自动归档)。
3. 分类(逐轮判断):模型逐轮决定每轮属于哪个类别,用 segments 提交:
[{category, fromTurn, toTurn}, …]。同一类别可以拆成多段,最后合并成一个文件;
每个 turn 只能归一类,区间不许重叠。
4. 保留:无需归档的近期文本量 = 窗口 × retainFraction(默认 12%),比这更新的轮次不动。
取整轮粒度、严格不超过该上限;只有单轮本身就超上限时,至少保留最后 1 轮。
12% 的用意:压得比 DSH 压缩尾部(约 16%)更小,压缩时不会碰到你还没归档的近期内容。
5. 写入:插件从会话日志逐字取出每个类别的轮次,按时间正序写进该类别一个文件(超 maxFileBytes 才分卷)。模型不复述内容,不会截断或改写。
6. 覆盖去重:已归档轮次记在 .state.json 的 archivedRanges;新请求先把已覆盖的轮次剔掉,只写剩下的。
7. 归档后压缩:compactAfterArchive 默认关。compactNow() 不接受范围参数,压多少由 DSH 自己决定,
可能压掉尚未归档的近期轮次(原文仍在会话日志里,可再归档)。默认不动它。
目录与文件名(会话级隔离)
<当前工作区>/
memory-palace/
.namespaces.json # 会话ID → 文件夹名 的映射(插件维护)
config.json # 新会话的默认值(旧版遗留,仅作默认)
<文件夹>/ # 记忆名:英文(大小写均可)+数字/空格/下划线/连字符
config.json # 本会话自己的开关/参数/记忆名
.state.json # 本会话水位、已归档区间
index.jsonl # 本会话的归档索引(路径相对本文件夹)
<YYYY-MM>/ # 月份容器:归档时按归档年月找/建
_summary.md # 该月摘要:只追加,每段对应一次归档里的一个类别
<category>/
<前缀>-<category>-<YYYYMMDD-HHmmss>.md
- 文件夹名 = 页签里填的「记忆命名」:英文(大小写均可)、数字、空格、下划线、连字符,≤32 位;首尾不能有空格,中文与路径分隔符会被拒绝。
- 没命名时用会话ID,且不产生任何记忆文件(只建 config/state)。
- 在页签里改名并保存 → 文件夹就地改名,索引里的路径是相对的,不需要跟着改。
- 文件夹即归属:
memory-palace/<你的文件夹>/里的文件都属于那个会话;文件名前缀只是历史署名,不代表归属。 - 摘要每月一份:
<YYYY-MM>/_summary.md,只追加;同一次归档里每个类别写一段,段头是「归档时间 · 对话时间范围 · [类别] 轮次范围」。 - 月份按该批对话首轮的时间归属:10-31 深夜、11-01 才归档的对话仍落进
2026-10/。 - 文件头注明
turn = 日志事件序号(含系统指令、插件注入、工具调用),不是“第几条消息”。 - 一次归档调用里,同一类别的所有轮次进同目录下的一个文件(超
maxFileBytes才按轮分卷-p1/-p2)。 - 不同类别 = 不同文件,即使来自同一次调用。
- 同秒冲突自动加
-2。分类目录强制英文小写。
从旧结构迁移
早期版本用过不同的目录结构。迁移脚本只用于本项目早期数据,不包含在发布包中;新用户从 0.1.0 开始使用无需迁移。
当前采用的结构是 <会话>/<YYYY-MM>/<category>/。
工具
工具 · 作用
palace_archive · 归档轮次。未命名会话会拒绝。用 segments 逐段给类别:[{category, fromTurn, toTurn}, …];并用 summaries=[{category, text}, …] 给每个类别写一段只概述该类别本次轮次的话(逐段追加到当月 _summary.md)。单类别时可用 summary 代替。可传 selfName 作文件名前缀。超出保留边界或已归档的轮次会直接报错(防止模型估错轮次号)
palace_turns · 列轮次清单:turn 号 + 发言者 + 开头 70 字,用于归档前确认每轮内容。turn 是日志事件序号(含系统指令/插件 steer/纯工具轮),不要用“第几条消息”换算
palace_summary · 读月度摘要:不指定 month 时列出最近几个月(摘要末尾预览 + 归档文件数);month=YYYY-MM 返回该月摘要全文;category 只看该月某类段落;append + fromTurn/toTurn 可给还没归档的近况补写一段摘要
palace_search · 检索归档。默认只查本会话;namespace 查别的会话、all:true 扫全部命名空间(结果标来源)、category 只查某类。keyword 支持空格分隔的多关键词(AND,引号包短语)。默认按轮聚合(每轮一条代表命中 + 该轮命中数),limitTurns 控制轮数(默认取 searchTurnLimit=12);groupByTurn=false 回原始命中行(旧 limit 语义)。片段约 220 字符,总输出上限 8K,文件读失败会跳过并注明。只搜归档正文,不搜 _summary.md(摘要里的概述性说法要用 palace_summary 读);但原文 0 命中时会自动回查摘要并标注「摘要不是轮次」,可用 includeSummary:true 强制追加
palace_read · 读归档。fromTurn/toTurn 返完整原文;path 可省略(按轮次自动找文件),也接受 search 的「文件夹/…」路径;可传 namespace。末尾附一行回链(提取 turn 引用/文件名/工具名,自动提取仅供参考)
palace_config · 查看/修改本会话配置(get 会同时返回 effective / 工作区默认 / 本会话配置)
检索四步法(为省 token 设计)
0) 不知道在哪个月:palace_summary(limit=6) → 每月摘要末尾预览;找到大致年月 0b) palace_summary(month="2026-10") → 该月全部摘要段落(哪次归档、讲了什么) 1) palace_search(keyword="事故教训", limit=3) → 约 1 千字符:命中位置 + 轮次 + 行号 + 文件路径 2) palace_read(fromTurn=48, toTurn=48) → 该轮完整原文(思考、工具调用、返回都在)
- 单次读取默认上限 60,000 字符(约一本书的三分之一),硬上限也是 6 万。
- 单轮超过 6 万字符时,返回前 6 万并提示
用 offset=N 继续,可分段读完整个大轮(实测 12.7 万字符分 3 段读完)。 - 不给轮次、也不给 path 时只返回文件开头 6 万字符,并提示按轮次精确取。
开关(会话级,fail-closed)
真正生效要同时满足(现在收成一个统一门 gateState(),页签会显示“为什么没生效”):
1. 全局总闸 enabled(默认开,只是允许插件工作)
2. 本会话开关:memory-palace/<文件夹>/config.json 里 enabled: true,新会话缺省为关
3. 已命名:记忆名为空时,即使开了开关也不归档(只建文件夹和配置)
4. 自动提示:injectNotice / enableSteer 任一关掉 → 工具仍可用,
…(truncated)
Rendered from the README published on npm. Formatted only, not edited.
Versions
Published on npm::25,最后更新 2026-11-05