@tr1v3r/dsh-ltm
Local SQLite long-term memory with CJK-aware search, BM25 and character n-gram cosine reranking, deduplication, and lifecycle management.
Install
dsh plugin --profile web add @tr1v3r/dsh-ltm
Sandbox test results
| Stage | Result |
|---|---|
| 1 install | ✗ |
| 2 list | — |
| 3 mount | — |
| 4 boot | — |
dsh: initialized profile web at /home/node/.dsh/profiles/webFour stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).
README
dsh-ltm(中文说明)
本文件是 [README.md](README.md) 的中文对照说明;内容以英文版为准,两者同步更新。
面向 DeepSeek Harness(dsh)的结构化长期记忆插件:
本地 SQLite 存储 + 中文感知分词检索(CJK 二元分词 + FTS5)、BM25 与字符 n-gram 余弦混合重排、
近似重复检测、过期复核、以及从 dsh-memory 的一次性迁移 —— 零强制联网依赖。
为什么需要
dsh-memory@0.1.0是纯文本 + 默认分词器的 FTS5:中文检索基本不可用(整句变成单个 token)。- 长期记忆需要追加/检索之外的生命周期管理:结构化 scope 与 tags、重复检测、复核时间戳、有界召回。
安装到 profile
DSH peer 兼容范围在原有 <0.2.0 范围之外,包含实测通过的桌面版 0.2.1-rc.1、0.2.1-rc.3 与 CLI 0.2.1-rc.2。其他 0.2.x 预发布版本仍需单独验证。
@tr1v3r/dsh-ltm 0.1.2 及以上可用一条命令安装并注册 bundle 配置:
dsh plugin --profile web add @tr1v3r/dsh-ltm
把 web 换成你的 profile 名(例如 dsh-tui),然后重启该 profile。bundle 会把数据库路径设为
$DSH_HOME/memory/ltm.db;无需 API key 或 embedding 服务。
替换 dsh-memory 时先禁用其旧条目:两个插件都注册 memory_write / memory_search /
memory_forget。若此前手工插入过 ltm 条目,启用 bundle 前先移除手工 insert,避免重复实例。
安装不会自动迁移旧库;见[从 dsh-memory 迁移](#从-dsh-memory-迁移)。
手工组合(含缺少 bundle manifest 的 0.1.0–0.1.1):安装 npm 依赖后,在 profile 的
cordis.patch.yml 里 insert:
- insert:
- id: ltm
name: '@tr1v3r/dsh-ltm'
config:
path: !!js dshHomePath('memory/ltm.db')
path 必填,代码侧无默认值。推荐的部署路径是 $DSH_HOME 下的 memory/ltm.db ——
独立新库,本插件绝不写旧 memory/memory.db。
配置
键 · 默认 · 含义
path · (必填) · SQLite 文件,或 :memory:
defaultScope · "" · 兜底 scope;关闭自动检测时作为固定 scope
autoProjectScope · true · 从每个 agent 会话的 cwd/Git 仓库推导当前项目
escapeSequences · [] · 可选:渲染进提示词前以零宽空格打断的输出序列
promptRecentCount · 10 · 召回分节中未置顶的最近记忆数
promptMaxChars · 2000 · 分节硬性 UTF-16 字符预算;置顶记录优先保留
promptMaxTokens · (未设) · 可选的正安全整数硬 token 上限(与字符预算并存)
promptTokenizerPath · (未设) · 本地受支持的 Hugging Face tokenizer.json;与 promptMaxTokens 成对必填
maxTextChars · 2000 · 单条记忆最大字符数
searchLimitDefault / searchLimitMax · 10 / 50 · 检索结果数量限制
promptOrder · 50 · 召回分节顺序
dedupeThreshold · 0.8 · Jaccard 相似度 ≥ 该值判定近似重复
dedupeCosineThreshold · 0.92 · 余弦相似度 ≥ 该值同样判定近似重复
staleAfterDays · 90 · 超过该天数未确认的记忆标记 stale
默认情况下,记忆文本在召回提示词中逐字保留。escapeSequences 是部署级显式开关,
面向把渲染后的提示词再交给其它基于定界符解析器的环境;DSH 本身不需要。
非法值(空路径、非整数边界、阈值超出 [0,1]、短于 2 字符或含零宽空格的转义序列)
在插件加载时即抛错 —— fail loud,而不是等第一次工具调用。
自动项目隔离
autoProjectScope: true 时,每个 agent 解析自己的 session.header.cwd;绝不使用共享的
DSH 进程 cwd。Git 检出以其 canonical common Git directory 识别,因此子目录与 linked
worktree 共享同一项目 scope;非 Git 工作区按 canonical 目录识别。Git scope 名只含 SHA-256
短摘要(各种 linked-worktree 布局保持一致);目录 scope 另带可读 basename。绝不存储绝对路径。
模型面默认刻意收窄:
- 写入与近似重复检查使用当前项目 scope;
- 检索与自动召回只看当前项目 + 全局记忆(
scope=""); - update / forget / confirm / merge 拒绝可见 scope 之外的记录,merge 绝不跨 scope;
memory_list与 CLI 保留显式跨项目聚合/管理面。
设 autoProjectScope: false 可只用 defaultScope 作为固定部署 scope("" 即仅全局)。
scope 是上下文隔离边界,不是操作系统权限边界:能直接访问 SQLite 文件或 CLI 的人仍可管理所有记录。
可选离线提示词 token 预算
两个选项默认都不启用:既有纯字符输出保持不变。必须成对设置。可选依赖
@huggingface/tokenizers@0.2.0 只在配置了该功能的插件启动时加载一次;渲染保持同步,
无网络请求、下载或文件读取。CLI doctor 可用显式 JSON 配置渲染诊断投影(绝不读取运行中的
profile)。支持受限且经过保真测试的 ByteLevel/BPE 子集;不支持的管线/选项在启动时失败,
而不是静默近似。上限统计完整的转义后召回分节(含头部、元数据、换行、截断省略号与
省略提示);置顶记录优先,recent 绝不为了省略提示挤掉置顶。离线计数对所选 tokenizer 定义是
精确的,不是服务端用量的承诺。详见 [docs/prompt-token-budget.md](docs/prompt-token-budget.md)。
模型工具
兼容 dsh-memory 习惯:
memory_write(text, tags?, pinned?, force?)—— 先做去重检查;近似重复返回候选而非写入,
除非 force: true
memory_search(query, limit?)—— 中文感知分词 + 混合重排memory_forget(id, expectedRevision?)
新增:
memory_update(id, text?, tags?, pinned?, expectedRevision?)—— 原位修订,保留 idmemory_confirm(id | "*", expectedRevision?)—— 刷新复核时间戳、清除 stalememory_list(scope?, tags?, stale?, limit?)—— 过滤浏览(tags AND 语义)memory_merge(targetId, sourceIds[], text?, tags?, expectedRevision?, expectedSourceRevisions?)—— 合并重复;tags 默认取并集
身份未知时先检索既有事实/主题再写入。同一事实的状态变化应走 memory_update,
而不是再写一条或 force 写入近似重复。相似度不能证明等价或矛盾;请审阅候选。这是指引,
不是强制的额外检索调用。
成功的置顶写入与相关更新附带可选 budget 反馈;去重拦截的写入不声明新的置顶预算。
乐观并发(revision CAS,#32 阶段一)
每条存储的记忆都带 revision(正整数,从 1 起,与时钟无关)。所有读取 —— 检索结果、
list、提示行((#id, rev N, …))、去重候选 —— 以及每次成功写入都会报告它。向
memory_update / memory_confirm / memory_forget / memory_merge 传入
expectedRevision(你最近读到的版本)即把该变更变成同一个 BEGIN IMMEDIATE 事务内的
比较交换:若记录在你读取之后发生了变化,操作以结构化 MEMORY_REVISION_CONFLICT
({code, operation, id, expectedRevision, currentRevision})失败且什么都不写 ——
不会自动重试你的旧内容。重新读取记录,用其当前版本重试。
- 省略版本字段保持旧的、不受保护的行为;不带版本的调用绝不宣称受 CAS 保护。
- 严格模式的
memory_merge要求目标的expectedRevision加上恰好覆盖唯一源 id 集合的
expectedSourceRevisions;部分/重复/多余/包含目标的声明在任何读取之前即被拒绝。
id: "*"的memory_confirm只刷新复核时间戳,不是逐条验证,且拒绝expectedRevision。- 冲突与未知/已删除/越权 id 都是纯元数据失败(错误码:
MEMORY_REVISION_CONFLICT、
MEMORY_NOT_FOUND、MEMORY_INVALID_ARGUMENT、MEMORY_SCOPE_MISMATCH、
MEMORY_REVISION_OVERFLOW);错误中绝不包含记忆正文。
- 成功的 update/confirm/merge 返回新 revision;
memory_forget返回deletedRevision
(被删除时的版本)。
- revision 计数有上限(
Number.MAX_SAFE_INTEGER);到达上限的记录不能再被更新、确认或
作为 merge 目标 —— 整次操作拒绝,不静默溢出。删除不受上限限制。
阶段一限制(分阶段计划见 issue #32):CAS 只保护你观察到的版本,不证明内容正确;
阶段二至四(provenance/evidence、分层复核状态、历史/回滚)尚未实现;没有跨恢复/导入的
incarnation/tombstone 保护 —— 此类操作后请静默写入者并重新读取。
规模与限制
近似重复检测在每次非 force 的 memory_write 时扫描同 scope 的全部记忆。该设计面向个人
长期事实库而非大型文档集合。写成本随该 scope 内记忆的数量与长度增长;暂无基准支撑的
容量上限。多会话可共享本地 WAL 库:初始化后的兼容打开不抢 schema 写锁;首次初始化、
schema 修复与 token 索引重建仍需写入。SQLite 以 5 秒 busy 超时串行化写入者;SQLITE_BUSY
提示稍后重试,无自动应用层重试。这不是高并发服务,也不是跨机数据库同步。
CLI
npx -p @tr1v3r/dsh-ltm dsh-ltm --db /path/to/ltm.db <command> [--json]
`list / search / show / edit / tag / pin / merge / confirm / forget / upgrade-schema /
export / import —— 每个子命令都支持 --json`。未知 flag、互斥 flag 与多余位置参数一律
拒绝。默认数据库:$DSH_HOME/memory/ltm.db。
edit/tag/pin/confirm <id>/forget 接受 --expected-revision N;merge 另有
--expected-source-revisions id:rev,id:rev(恰好覆盖唯一源集合)。这些 flag 在打开数据库
之前完成校验;confirm --all --expected-revision 拒绝。CAS 失败以退出码 1 结束,--json
输出与工具相同的结构化 error 详情,人类模式只输出错误码/id/版本与重读指引,绝
…(truncated)
Rendered from the README published on npm. Formatted only, not edited.
Versions
Published on npm::10,最后更新 2026-10-25