DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

18477514055 /

18477514055/dsh-ledger-memory

Verified

台账记忆(Ledger Memory)—— DSH 的工程化项目记忆。实际兑现两件事:① 同一项目共用一份工程进度记忆,换对话、换模型近乎零交接成本;② 单对话在超长输入输出下仍守住准确性、可回溯、延续性、主动回查四条底线。

★ 3 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@edca61f3

dsh-ledger-memory(台账记忆)

一个面向 DSH(DeepSeek Harness) 的宿主侧插件,为每个项目建立独立、可回溯、可追查的长期记忆载体。

插件管理的对象并非对话记录,而是项目自身的记忆载体 —— 台账:可追溯、只追加、按周归档。 围绕台账还提供活动日志、上下文压缩与跨会话交接能力。

不预设任何模板。 台账的文件划分、命名与字段结构,全部由模型在初始化时依据项目类型自行确定; 插件仅承担检测、注入、回写、归档与 Git 协作等流程性职责。


解决的问题

问题 处理方式
新会话无法接续前一会话的项目进度 会话开始时注入活跃台账全文;内容未变更则不重复注入,以避免破坏 prompt 缓存
单个回合的产出未能沉淀 回合结束时将「已完成 / 受阻 / 下一步」写回活跃台账
台账持续增长、难以完整阅读 超出阈值时将最旧的已完成条目移入 归档目录/YYYY-MM/weekN,只追加、不删除
细节在上下文压缩中被抹去 压缩之前将用户原话、报错原文、变更文件清单持久化为逐字胶囊,压缩后与台账一并注回
新会话缺少承接信息 生成交接单,并把指针推入新会话的首轮注入,无需使用者主动查询
需要核查台账变更来源与时间 台账末尾自动维护变更登记;完整历史保存在只追加的活动日志中
仅为一次性排查、不构成项目 ledger_note 生成问题记录(现象 → 根因 → 结论 → 处理过程),不建立台账

上下文压缩机制

插件默认启用全量压缩;增量(分段)压缩默认关闭,可按需手动开启。

阶段 触发条件 行为
① 保底 · 全量 水位达到 70%(内核默认 80%,插件在其之前触发) 执行全量压缩,同时保留指向原始内容的指针:用户原话、报错原文、变更文件清单与重读入口
② 收尾 · 交接 切换会话时 由交接单承载,使新对话无缝接续
③ 可选 · 增量 需手动开启(默认关闭) 仅压缩最旧一段,削减上涨区间的约一半

关于默认关闭的增量压缩:该分支曾为主要路径,现已默认关闭且不再按固定节奏调用。 依据是实测取舍 —— 压缩幅度较浅的摘要大概率会被再次压缩(≤5 万 token 的浅层压缩中 72.7% 会被二次压缩), 层级增加会导致清晰度衰减。因此当前以全量压缩为主路径,单次压缩更深。 deltaTriggerPoints(触发涨幅)与 deltaRemovePoints(单轮最大压缩点数)两项配置仅在启用增量压缩时生效。

压缩遵循一条固定约束:先记录台账,再执行压缩 —— 尚未落盘的进展将成为唯一副本。 该约束由周期判定("自上次压缩以来是否落盘")保证,而非"是否已发出要求" (后者会使模型被阻塞:历史上曾出现拦截近 4 小时、压缩零次的情况)。 收尾硬门(hardMaintenanceGate)默认开启:未落盘则不压缩;关闭后回退为 "拦 3 次或超 20 分钟即放行"的旧行为。

安装

需要 Node.js ≥ 22.19。

# 走 npm
npm install dsh-ledger-memory

# 或走 DSH 自己的插件命令
dsh plugin add dsh-ledger-memory

装完重启客户端才生效 —— 内核对已加载的插件不会因文件改动自动重载。

升级(★ 如果"界面显示有新版本,却怎么装都是旧的",看这里)

从 1.0.0 起,正常升级不需要做任何特殊操作 —— ^1.0.0 允许整段 1.x, 所以 1.1、1.2 都在范围内,能自己爬上去。

若从更早的 0.x 版本升级上来且恰好受阻,那是两个叠加的原因:

  1. 依赖被写成 ^0.1.0 这种旧范围。 0.x 的插入符语义是 ^0.1.0 = >=0.1.0 <0.2.0 —— 不允许跨次版本,所以天花板永远是 0.1.1。 (1.x 没有这个问题:^1.0.0 的天花板是 2.0.0。)
  2. pnpm-workspace.yaml 里可能留着一行过期的豁免(pnpm 装成功时会自己往这里追加):
    minimumReleaseAgeExclude:
      - dsh-ledger-memory@0.1.0     # ← 这行就是枷锁
    
    ★ 卸载重装治不好它 —— 卸载动不了这个文件。这也正是"反复卸载重装仍是旧版"的原因。

修法:

cd <DSH_HOME>/profiles/<profile>
cp package.json package.json.bak           # 备份
pnpm add dsh-ledger-memory@1.0.0           # ★ 关键就这一步:按精确版本装
# 然后重启客户端

★ 为什么"按精确版本装"这一步就能破局(实测): pnpm 会把那一行自己改成 - dsh-ledger-memory@0.1.0 || 1.0.0(追加而非替换), 于是新版本进了候选集,解析就取其中最新的 ⇒ 一步到位。

★ 建议顺手把那行清掉(不是必需,但更干净):删掉 pnpm-workspace.yaml 里 - dsh-ledger-memory@… 那一行,以后就不会再有歧义。

自查(不需要任何额外文件):打开 <DSH_HOME>/profiles/<profile>/pnpm-workspace.yaml,看有没有 - dsh-ledger-memory@ 开头的那一行。有 ⇒ 就是它;没有 ⇒ 问题在依赖范围(上面第 1 条)。

本仓库里另有一个更省事的只读诊断:node tools/_probe-install-version.mjs。 ⚠️ 它只在本仓库里有、不在 npm 包里(发布包内只有 lib/、README.md、 LICENSE、cordis.patch.yml 与 package.json —— 整个 tools/ 目录都不发)—— 所以从 npm 装的用户请用上面那条手工自查。

使用方式

安装后无需额外配置。在一个项目目录中开始工作,插件将依次执行:

  1. 检测到尚未建立台账时,注入一条提示,要求模型向使用者确认是否初始化(插件不会自行创建);
  2. 使用者确认后,模型依据项目类型设计台账结构并完成登记;
  3. 此后每个会话自动加载台账;到达水位阈值(或使用者主动要求)时,插件提示模型回写并归档。

关于「自动」的边界:插件不会自行写入台账或执行归档 —— 它负责在适当时机向模型 注入指令,由模型调用 ledger_write / ledger_archive 完成。 这是有意设计:台账内容需要判断(哪些值得保留),而插件不具备该判断能力。 因此模型忽略指令时,该回合不会落盘 —— 插件会在后续步骤再次要求(闸门机制)。 回合末的四选一卡片默认关闭(turnEndOffer),可在设置中开启。

手动命令

在对话里敲 /ledger(不带参数会弹一张可点的卡片),或用子命令:

/ledger status     看状态(有没有台账、用的哪个文件、几行、Git、日志开关)
/ledger show       把当前活跃台账全文打出来
/ledger init       初始化台账(由 AI 为这个项目设计一套结构)
/ledger update     让 AI 把本次会话的进展写回台账
/ledger archive    把最旧的已完成条目移进归档目录
/ledger compact    立刻做一次全量压缩(保留指针)
/ledger light      轻量压缩 · 只压**上一个回复**那一段(★ 需显式使用,不是常规动作)
/ledger log        查活动日志
/ledger handoff    写一份交接单
/ledger principles 看根本原则;`pending` 列提案;`approve <id>` 批准(仅限使用者本人批准)
/ledger mode       看/切换压缩模式(steady / surgical)
/ledger lock       把本场对话锁到某个项目目录(总工作区里用)

/ledger principles 是唯一仅限使用者本人执行的子命令:模型可以提议,但批准必须由使用者完成。

会话建立于总工作区时:锁定到子项目

若会话建立在包含多个项目的总工作区上,插件无法判断本次工作针对哪个项目, 默认将全部操作落在当前目录。此时可通过锁定指定目标:

/ledger lock 20-DSH插件/04-其余插件/10.项目台账

锁定后,本场对话的全部工具调用均落在该目录,无需每次单独指定。 /ledger lock 不带参数可查看当前状态,/ledger lock off 解除锁定。 会话首轮弹出的卡片中也提供该选项。

锁定状态会保持:重启或重新打开后依然有效,每场对话独立记录,互不影响。 记录保存在 DSH 自身的数据目录(plugin-data/)中,不写入工作区: 不创建注册表项,也不在目标目录留下任何标记 —— 项目目录不会因此新增文件。

若当前目录自身已有台账,仍以当前目录为准 —— 锁定仅接管"没有台账"的目录。

若锁定的目标目录被删除或移动,该锁定自动失效(回退为按工作目录解析), 后续操作不会指向不存在的路径。

设置面板

设置 → 项目台账(台账 / 压缩 / 日志),共 43 项配置,分为四页:常规、注入、扩展记忆、高级。主要可调项包括:配置优先级、压缩水位、 收尾硬门、回合末卡片、活动日志各档开关、活跃台账行数上限、变更登记保留条数、 水位提醒档位、逐字胶囊的采集范围与 ledger_recall 单次回读上限、文件锚定的权重阈值等。

关于「谁说了算」:该项决定设置面板与项目清单(.dsh-ledger.json)冲突时以哪一方为准。 默认值为「以设置面板为准」,即面板的修改对所有项目立即生效(含已初始化的既有项目)。 受该项管辖的是 6 项:mode、blockInjection、fileAnchor、maxActiveLines、 changeRegisterMax、logLevels;其余配置项始终以面板为准。 若同一代码仓库内不同子项目需要不同上限(例如大型项目 800 行、小型项目 150 行), 可将该项切换为「以项目清单为准」。

面板提供「推送到所有对话」操作:执行后所有对话在下一回合重新读取当前设置。

压缩流程:单一方式(分流后压缩)

此前的两种方式(按位置压缩 / 按内容分流压缩)已合并。分流现为每次压缩的标准步骤, 不再是可选项 —— 设置中的 mode 仅为兼容既有项目清单而保留,取值不影响行为。

合并原因:分流(ledger_triage)决定「哪些内容可被压缩、各自如何处理」, 而标记为「留在上下文」的部分会在压缩后逐字注回 —— 这是"指针得以完整保留"的前提。 按位置压缩无法识别哪些内容确实不可丢失。

分流将当前区间划为三个桶:

  • 留(keep):下一轮若缺失即会导致错误的内容(当前状态、未验证假设、既定约束)。压缩后逐字注回。
  • 落(stash):后续可能需要查阅、但不应占用上下文的内容(判断与依据、已排除的路径、关键文件位置)。 写入记忆目录的 _stash/ 目录(V3 下位于 <项目名>-ledger-memory/ 内),永不注入,按需检索。
  • 丢(drop):已被取代的内容,仅记录到日志。

keep 设有条数上限(单次与累积各一项)——保留过多等同于未压缩; 超出时按模型给出的顺序截断,并如实报告截断条数。

分流为每回合义务:每条回复结束前需完成落盘(ledger_write)与分流(ledger_triage)。 收尾硬门默认开启 —— 未完成时该回合的压缩不会发生(宁可让水位上涨至 78% 触发强制压缩, 也不保留"未落盘即压缩"的窗口)。注入指令的措辞按硬门状态分为两套,二者不自相矛盾。

二十二个工具

以下工具由模型在对话中调用,也可要求模型调用:

工具 作用
ledger_status 报告这个项目的台账状态(只读)
ledger_read 读台账 / 决策记录 / 归档 / 卡片
ledger_write 写活跃台账、决策记录、流水或原则
ledger_init 登记台账结构(四条硬约束:一个活跃台账 / 一份独立决策记录 / 一个只追加归档目录 / 行数上限)
ledger_archive 把旧条目移进只追加的归档
ledger_conform 检查既有项目的台账是否符合预期,能修的只做加法
ledger_migrate 反向的那一半:检测旧架构并给出拆分/搬家步骤(op:"split" 拆卡片 · op:"v3" 迁入记忆子目录,两者默认干跑)
ledger_note 写一份问题记录(非项目场景,有终点的那种)
ledger_log 查活动日志(五档,从不注入上下文)
ledger_log_write 追加一条回合小结(L1)
ledger_recall 读回被压缩掉的历史逐字原文。★ 两段式:mode:"index" 先给逐事件索引(seq / 类型 / 字数 / 摘要),再用 mode:"events" + seqs 只取点名的那几条;不带 mode 时按 seq 区间整段取(旧行为不变)
ledger_handoff 写交接单,供下一个会话接手
ledger_triage 将阶段性产出划为「留 / 落 / 丢」三个桶
ledger_lock 将本场对话锁定到指定项目目录(跨重启保持;记录不写入工作区)
ledger_goals 维护目标树(卡片联成树;目标完成时补记一条教训)
ledger_lessons 维护经验与教训(经验需注明出处,教训需注明根因)
ledger_index 为卡片目录生成多维索引表(融合模型判断、时间、稳定性与星级)
ledger_extension 维护自定义扩展记忆(6 种模式,按需挂载项目专属记忆单元)
ledger_propose_principle 提议一条根本原则(模型仅可提议)
ledger_principles_access 请求使用者开启授权窗口,之后可修改根本原则
ledger_compact 请求执行一次高保真压缩
ledger_checkpoint 将当前选择交由使用者决定(记录台账 / 记录并压缩 / 记录并交接 / 跳过)

根本原则(PRINCIPLES.md)仅限使用者本人修改:模型只能提议,或请求开启限时授权窗口 (默认 10 分钟,每次修改消耗一次授权)。该限制为有意设计 —— 根本原则属于使用者的约束,而非模型配置。

记忆的分类与存放位置

判定依据:语义不同的内容分别存放。 在 V3 架构下(清单声明 memoryRoot),所有记忆文件与目录统一存放在 <项目名>-ledger-memory/ 专用子目录中,与项目源码严格物理隔离;V2 项目则保持在项目根目录(向后兼容)。将"当前状态"与"历史流水"置于同一文件会相互干扰 (实测:重构前活跃台账 645 行中 412 行(64%)为日期流水,导致注入内容被历史挤占、行数上限被触发)。

★ 新建项目默认就是 V3。 ledger_init 在全新项目上不传 memoryRoot 时会自动建成第 3 代: 记忆目录取 <项目名>-ledger-memory/,并一并建出卡片三目录(decisions/ lessons/ goals/)与 _ledger-log/ ⇒ 项目生而在最新代,不需要事后迁移。两条相关行为:

  • 既有项目不会被偷偷升级:判断依据是清单是否为空。重跑 ledger_init 补建东西时,原来怎么布局还怎么布局。
  • 想用老布局(V2):显式传 layout: "flat" —— 记忆文件直接躺在项目根。
文件 语义 可变性 是否注入 维护方式
LEDGER.md 当前状态 可覆盖 / 精简 / 归档 每回合注入 ledger_write({target:"active"})
JOURNAL.md 回合流水(按天、按会话) 只追加、不修改 不注入(按需查阅) ledger_write({target:"journal"})
decisions/ 长期决策(一卡一文件) 只追加,不参与归档 摘要见台账 ledger_write({target:"decisions"})
extensions/ 自定义扩展记忆(6 种模式,注册表 registry.json) 按模式定义(只追加或可覆盖) 摘要可选注入 ledger_extension
_ledger-log/ 活动日志 L0–L4(含使用者原话) 只追加 永不注入 自动采集;ledger_log 查询
_ledger-archive/ 归档 只追加 否 ledger_archive
_stash/ 分流中"后续可能需要查阅"的部分 只追加 否 ledger_triage 的 stash 桶
_handoff/ 交接单 只追加 否(仅提供指针) ledger_handoff
goals/ 目标树(卡片联成树) 卡片可更新 会注入 ledger_goals
lessons/ 经验与教训(一卡一文件) 只追加 按需查阅 ledger_lessons
PRINCIPLES.md 根本原则 仅使用者本人可修改 按需读取 /ledger principles(模型仅可提议)

卡片化:核心记忆(decisions/、goals/、lessons/)以及卡片型扩展记忆均采用一卡一文件(文件名如 D195.md / g1.md / L3.md), 使单一大型文件无需整体载入 —— 模型可依编号仅检索所需卡片。 卡片数量增长后,可用 ledger_index 生成多维索引表(模型判断 3.0 / 新近度 1.5 / 稳定性 0.8 / 星级 1.0 融合排序)。 权威数据始终为卡片文件本身,索引表仅为缓存;需要最新排序时重新计算。

「卡片化」与「区块注入」是两条独立的轴

两者均涉及"切分",但切分对象不同 —— 一条决定内容在磁盘上的存储形态,另一条决定每回合推入上下文的内容:

轴 A:卡片化(存储) 轴 B:区块注入(推送)
切分对象 文件 —— 单个大文件拆分为多个小文件 单次注入的正文 —— 按 Markdown 标题切分
粒度 decisions/D195.md(一卡一文件) 「一/二、总纲」段落(标题至下一标题)
决定方式 项目清单中的 decisionsDir / lessonsDir / goalsDir 设置中的 blockInjection
适用范围 仅那三类长期记忆(决策 / 目标树 / 经验教训) 活跃台账(LEDGER.md)
目的 使模型按需仅取一张卡片,无需整体读取 使每回合的重注无需整份重推
关闭后 回退为"单文件承载全部" 回退为"整份台账每回合全量注入"

因此,"台账文件仍为整体"与"区块注入"并不矛盾: LEDGER.md 与 JOURNAL.md 有意保持单文件 —— 前者为"当前状态快照"(本应一次读全), 后者为"只追加的流水"(天然线性)。需要卡片化的三类记忆已采用文件夹形式(一卡一文件)。

判定依据:询问"它存储在磁盘何处" ⇒ 卡片轴;询问"本回合向模型推送多少" ⇒ 区块轴。

扩展记忆(6 种模式)

为满足项目特有的记忆需求(游戏机制、专有术语、规范清单等),同时避免污染核心记忆体系,插件支持用户自定义的扩展记忆:

  • 存储形态:统一存放在记忆目录的 extensions/ 下,以 registry.json 为注册表真源。 · 卡片型(decisions / goals / lessons):extensions/<id>/cards/<key>.md,一卡一文件。 · 单文件型:文件名随模式而定 —— active → LEDGER.md、journal → JOURNAL.md、 plain → content.md。
  • 六种模式:
    1. decisions(决策型):卡片型,编号即地址(如 D1.md)。
    2. active(活跃台账型):单文件,可覆盖与精简,适合维护当前业务状态。
    3. journal(流水记录型):单文件,只追加、不改旧条目。
    4. goals(目标树型):卡片型,一卡一节点,记录目标演进。
    5. lessons(经验教训型):卡片型,沉淀经验与踩坑教训。
    6. plain(普通独立记忆型):单文件,普通读写,无特殊约束。
  • 隔离与防冲突:扩展记忆不得占用核心保留名(ledger / decisions / journal / goals / lessons / principles),也不得改变基础模板;可通过工具 ledger_extension 或设置面板的 「扩展记忆」页管理。

为什么没有「原则型」:原则只该有一个出处 —— 核心的 PRINCIPLES.md。 再让使用者"额外扩展一份原则"没有语义,而且它会是唯一需要授权的扩展模式, 那条授权路径本身也得另造一套。故不提供该模式;根本原则仍走 ledger_propose_principle(模型提议)→ ledger_principles_access(使用者开授权窗口)→ /ledger principles approve(使用者批准)。

能力边界说明:goals/、lessons/ 以及 extensions/ 均属于可选能力,并非所有项目自动具备。 插件不会擅自创建这些文件 —— 如需使用,应在该项目清单中登记(或由模型征得使用者同意后创建)。 项目未启用时,插件不会自动创建,也不会因此报错。

活动日志(五档)

写入项目登记的日志目录(V3 下位于 <项目名>-ledger-memory/ 内),只追加、永不改写,且从不注入上下文(按需查询):

档 内容
L0 台账变更里程碑
L1 回合小结(带会话编号)
L2 文件流向
L3 工具调用流水(默认关闭)
L4 逐字存档使用者的每一句话(带会话编号与时间)

采集类档位仅识别会话的工作目录,不会推断子项目。 在包含多个项目的目录中开会话时,采集类档位无法写出内容 —— 此行为为有意设计。

依赖与许可

  • 零运行时依赖(dependencies 为空)
  • 唯一 peer 依赖:@deepseek-ai/cordis
  • 许可:MIT

自检(仅存在于源码仓库,不随 npm 包发布)

安装包内不包含自检。 发布包的 files 白名单仅含 lib/、README.md、LICENSE、 cordis.patch.yml、package.json —— 整个 tools/ 目录不发布。 原因:tools/ 内含一批会改写源码的探针(变异测试),将其随包发布存在风险; 且自检脚本本身约 1.14 MB。此发布边界为有意设计,并非打包遗漏。 因此,从 npm 安装的副本中执行 npm test 会指向不存在的文件 —— 此为预期行为。

在源码仓库中(git clone 之后)执行,无需网络、无需安装 DSH:

npm test          # 全套离线自检
npm run probes    # 只读探针 + 变异测试

已知边界

  • 注入通过 agent/pre-step 实现(可替换本轮消息),不使用 systemPrompt.context。
  • 修改代码后需重启客户端;内核对已加载插件不进行热重载。
  • 采集类日志档位在"容器工作区"(包含多个项目的目录)中无法写出内容。
  • 压缩服务仅可通过 agentPresets.serviceFor(agent, "compaction") 取得,ctx.get("compaction") 恒为 undefined。
  • V3 记忆目录的 Git 隔离由三层共同负责:.gitignore 规则 · 插件内部的结构性拒绝暂存(V3 项目一律不自动 git add)· 目录与源码物理分离。记忆目录内的 SECURITY.md 只是给模型的安全准则,不构成技术隔离,也不应被当作唯一防线。
—/ 5

No ratings yet

Verified DSH bundle

Commit edca61f3b974

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout