双路召回记忆并留痕,软淘汰可查可恢复
31.5/100
综合分
2
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/11/3 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add kira905/dsh-memory-toolkit该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:仅索引
是什么
dsh 原生插件 · search
装得上吗
本站尚未做安装检查
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 2 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 19 处,展示前 19 条)
- 写入本地文件dsh-memory-local/lib/index.js:189
if (path !== ":memory:") mkdirSync(dirname(path), { recursive: true }); - 发起网络请求dsh-memory-local/lib/bridge.mjs:35
const res = await fetch(url, { ...init, signal: AbortSignal.timeout(ms) }); - 执行外部命令来自配置/变量dsh-memory-local/build.mjs:51
const r = spawnSync(process.execPath, ['--check', full], { encoding: 'utf8' }) - 执行外部命令来自配置/变量dsh-memory-local/deploy.mjs:273
const probe = spawnSync(process.execPath, [path.join(here, 'test', 'probe-entity.mjs'), target], { - 执行外部命令来自配置/变量dsh-memory-local/rollback-injector-fix.mjs:162
const r = spawnSync(process.execPath, [RESTART_CLI, '--reason', reason], { encoding: 'utf8', windowsHide: true }) - 发起网络请求dsh-memory-local/src/bridge.mjs:35
const res = await fetch(url, { ...init, signal: AbortSignal.timeout(ms) }); - 写入本地文件dsh-memory-local/src/index.js:189
if (path !== ":memory:") mkdirSync(dirname(path), { recursive: true }); - 执行外部命令来自配置/变量dsh-memory-local/verify-after-restart.mjs:111
const probe = spawnSync(process.execPath, [path.join(here, 'test', 'probe-entity.mjs'), target, ...(expect === 'upstream' ? ['--upstream'] : [])], - 发起网络请求dsh-memory-local/verify-after-restart.mjs:132
const res = await fetch(`http://localhost:${port}/`, { headers: { origin: `http://localhost:${port}` } }) - 读取本地文件lib/deploy-guard.mjs:289
entries = readdirSync(dir, { withFileTypes: true }); - 读取本地文件lib/deploy-guard.mjs:330
raw = readFileSync(file, 'utf8'); - 执行外部命令来自配置/变量lib/deploy-guard.mjs:404
return spawnSync(process.execPath, ['--input-type=module'], { - 读取本地文件lib/deploy-guard.mjs:525
if (!existsSync(entityRoot)) return { ...empty, error: `实体目录不存在:${entityRoot}` }; - 读取本地文件lib/deploy-guard.mjs:526
if (!existsSync(libDir)) return { ...empty, error: `实体下没有 lib 目录:${libDir}` }; - 执行外部命令来自配置/变量memory-db-tools/_test/test-memory-scope.mjs:91
spawnSync(process.execPath, [script, ...args], { encoding: "utf8", env: { ...process.env, ...env } }); - 发起网络请求memory-engine/cli/serve-start.mjs:58
const res = await fetch(`http://${host}:${port}/health`, { signal: AbortSignal.timeout(timeoutMs) }); - 执行外部命令来自配置/变量memory-engine/cli/serve-start.mjs:283
const child = spawn(process.execPath, [ - 执行外部命令固定命令memory-engine/lib/band-sentinel.mjs:182
const r = spawnSync('ssh', argv, { - 发起网络请求memory-engine/lib/embedder.mjs:98
const res = await fetch(url, init);
扫描于 2026/11/3(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
智能体记忆层:向量召回 + FTS5 双路、命中留痕、分层软淘汰、只读体检工具 · 给智能体用的记忆层:向量 + FTS5 召回、召回溯源、分层软淘汰、只读审计工具包。零依赖。
README
由 DeepSeek 最新模型翻译生成dsh-memory-toolkit
这是什么智能体的记忆层:向量召回 + FTS5 关键词双路、命中留痕、分层软淘汰,外加一整套只读记忆库体检器具。
它解决什么「它怎么又忘了」和「它怎么记错了」其实是同一件事的两面:召回不可解释。这里做两件事 —— ① 每次召回都留痕:哪条记忆、因为什么被捡回来,可审计;② 淘汰是软的:搬走不等于删,能查、能恢复。另附 persona ↔ 记忆重叠体检,防止同一件事被写进两处。
一条命令跑起来
npm i -g @kira905/dsh-memory-toolkit
dsh-memory-summary # 新会话开工简报(只读,dsh 运行中也安全) dsh-memory-audit # persona ↔ 记忆 体检
先看它长什么样(真实输出) list-memory-summary.mjs — 新会话开工简报(只读,dsh 运行中也可安全执行)用法:node list-memory-summary.mjs [选项] --home <dir> DSH home(缺省 $DSH_HOME 或 ~/.dsh) --session <id> 指定会话 id(缺省读 DSH_SESSION_ID) --query <text> 直接给召回查询文本(缺省取会话上下文)
三个 CLI 全部只读、不启服务。装法与自检见下方「装完自检」。
展开完整文档(README 剩余 1 节)
dsh-memory-toolkit
这是什么智能体的记忆层:向量召回 + FTS5 关键词双路、命中留痕、分层软淘汰,外加一整套只读记忆库体检器具。
它解决什么「它怎么又忘了」和「它怎么记错了」其实是同一件事的两面:召回不可解释。这里做两件事 —— ① 每次召回都留痕:哪条记忆、因为什么被捡回来,可审计;② 淘汰是软的:搬走不等于删,能查、能恢复。另附 persona ↔ 记忆重叠体检,防止同一件事被写进两处。
一条命令跑起来
npm i -g @kira905/dsh-memory-toolkit
dsh-memory-summary # 新会话开工简报(只读,dsh 运行中也安全) dsh-memory-audit # persona ↔ 记忆 体检
先看它长什么样(真实输出) list-memory-summary.mjs — 新会话开工简报(只读,dsh 运行中也可安全执行)用法:node list-memory-summary.mjs [选项] --home <dir> DSH home(缺省 $DSH_HOME 或 ~/.dsh) --session <id> 指定会话 id(缺省读 DSH_SESSION_ID) --query <text> 直接给召回查询文本(缺省取会话上下文)
三个 CLI 全部只读、不启服务。装法与自检见下方「装完自检」。
本仓装完即用(0.1.3 起):dsh plugin --profile <profile> add @kira905/dsh-memory-toolkit 一条命令,dsh.bundle 会让它自动挂进 profile 的组合列表,不需要再手工写 cordis.patch.yml。包内 3 个 CLI(dsh-memory-audit / dsh-memory-summary / dsh-memory-export)与挂载无关,npm i -g 或 npx 直接用。
装完自检(三条只读验法)
<profile> 换成你实际用的 profile(默认 web),<数据根> 换成你的 DSH 数据根(默认 ~/.dsh)。
① 组合树里有没有它(只读、约 0.6 秒、不启服务)
dsh --profile <profile> --dump-config | Select-String "dsh-memory-toolkit"
期望:bundle 层出现一行 "# == <登记名>"、insert 层出现 "- id: <entry>"(问的都是「它挂上没」);本仓包名 @kira905/dsh-memory-toolkit —— 带 scope 装的写带 scope 名、老工装 / 手写的可能是裸名,两种都搜一遍 ⚠️ 若该包会 insert 一行,还要数一遍"同一个 id 出现了几次",必须 == 1
② 组合列表里有没有它(只读) Get-Content <数据根>/profiles/<profile>/package.json | Select-String "@kira905/dsh-memory-toolkit" 期望:dependencies 与 dsh.profile.bundles 两处都有(CLI 类只需要 dependencies;bundles 里也可能写裸名 dsh-memory-toolkit)
③ 命令在不在 —— 【只查存在,不执行】 Test-Path <数据根>/profiles/<profile>/node_modules/.bin/dsh-memory-summary.CMD # 装进 profile 的 Test-Path "$env:APPDATA\npm\dsh-memory-summary.CMD" # npm i -g 装的期望:True。两条路径按你实际用的装法挑一条
探针只做存在性检查(只看命令文件在不在,不执行任何命令)。
要不要用 --help 验,见本仓当前说明与该命令的参数表。
DSH(DeepSeek Harness)生态的记忆面工具集 —— 一个独立向量记忆引擎、一套记忆库只读体检工具链、一个自持 fork 的本地记忆插件,以及一个 persona ↔ 记忆体检小工具。
四件东西原本是同一套自用工作流的不同切面,抽出来独立成仓,可以单独用,也可以拼起来用: 引擎负责"记什么、怎么找回来",工具链负责"看得见、改得动",插件负责"接进会话"。
一、里面有什么
| 目录 | 是什么 | 形态 |
| memory-engine/ | 记忆引擎(离线层):独立矢量库 · 增量同步 · 兜底 hash 轮转 · 混合召回(向量 + FTS5 + 词表 BM25 + RRF + 编码降级兜底 + 旁路留痕) · 文档索引 · 启动自检 · 本地编码桥 | 纯 Node ESM + node:sqlite |
| memory-db-tools/ | 记忆库工具集:只读导出 / 六项体检分析 / 结构探针 / 特征词探针 / 与 persona 的重合度 / 看板账本只读探针 / 零改动自证 / 新会话开工简报 | 一组独立 CLI |
| dsh-memory-local/ | 本地记忆插件:上游 dsh-memory 的自持 fork(跨会话持久记忆 + 召回提示段 + 指针注入链),带构建 / 幂等部署 / 一键回滚 / 部署后自检 | DSH 插件 |
| persona-memory-audit.mjs | persona ↔ 记忆体检:把「persona 正文体积」与「记忆可见率」两项指标固定下来,超阈值即报红 | 单文件 CLI |
| lib/ | 四件共用的无依赖小件(参数白名单 / HTTP 存活判据 / zstd 帧解析 / 会话文件名 / 坑库打分) | 纯函数模块 |
同源说明
本仓部分文件与一套内部生产实现同源(同一份工程长期演进下来的两处落点)。
- 同源程度:按行级同源度口径统计(同名可比件里,仓内文件的非空行有多少能在参照件中找到,多重集扣减),本仓「≥50% 行同占比」= 64%。
- 这意味着:本仓是那套实现的提炼导出 —— 既不是逐字副本,也不是完全重写的独立实现。拿单个文件当「独立参考实现」读之前,请先跑下面的判据。
- 怎么复跑:同源度扫描器见 dsh-quality-toolkit 的 dsh-sim-scan.mjs(--repo <本仓> --prod-root <参照根>);分档阈值与口径写在它的 config/sim-scan.json 里,不藏在代码里。
- 许可:本仓许可见仓根 LICENSE(有子树例外时以子树 LICENSE 为准)。
召回链:降级 / 词表第二路 / 命中留痕(0.2.0 新增)
召回有三类老毛病,这一版各给一条可一键关回的解法;出厂默认一条都没改。
| 毛病 | 现在的做法 | 开关 | 出厂 |
| 编码超时/失败 ⇒ 那一轮一条记忆都不注入 | 查询先截断到 256 字符;编码带预算(默认 1500 ms),超时 / 抛错 / 空向量一律降级成固定 Top-K(按最近改动取,条数默认 8),结果里带 degraded.mode = "fixed-topk" 与 encodeError.name | fallbackTopK / encodeTimeoutMs / --fallback-top-k / --encode-timeout-ms | 降级常开(只作用于失败路径,成功路径一字不改) |
| 纯向量对精确词不敏感;中文又走不了 FTS5(unicode61 不切中文) | 词表第二路:jieba 词典最长匹配切词 + BM25(tf 饱和 + 文档长度归一 + 语料内 idf × 通用词具体度),与向量、FTS 一起进 RRF(k=60) | memory-engine/dict/lexicon-config.json 的 enabled、MEMORY_ENGINE_LEXICON=on/off、--lexicon on | 关(关掉时结果与改动前逐字节一致,见下) |
| 召回了什么、命中了哪条、准不准,事后无从判断 | 旁路 JSONL:query / 命中 id / 分数 / 是否降级 / 时刻;按字节轮转 + 天数清理,不无限增长 | 给目录:--recall-log <目录> 或 DSH_MEMORY_RECALL_LOG_DIR;DSH_MEMORY_RECALL_LOG=off 一键关回 | 不写(库不该在纯查询里偷偷落盘) |
为什么这么设计(完整版见 memory-engine/README.md 的「设计依据」):
- 降级用「最近改动」而不是随便凑数:拿不到向量时,语义相关已无从计算;此时最可能的「有用」是刚写进来的东西。兜底三档依次是数据源直给的 recent() → 元数据按 updated_at 降序 → 向量表里 id 最大的几条,保证非空、顺序确定、仍排除 pinned。
- 词表默认关:它的收益是「精确词多捞回来一截」,代价是「两路融合会重排名次」。本仓对外的承诺是默认行为不改 ⇒ 出厂关、由使用方一行配置打开。打开后 stats.lexiconStatus 从 off 变 ok,命中里出现 sources: ["vector","lexicon"]。
- 留痕默认不写:query 可能是隐私内容,库不该在纯查询里落盘;要判「召回准不准」就显式给目录,且字节/份数/天数三重封口。实际记录里不存记忆正文,只存 id 与分数。
- 为什么不复用生产那份补丁:生产侧是「按行号锚点重放」的补丁(patch--lexicon.mjs),搬进独立仓必碎;本仓是按行为重写的模块,靠夹具与旧版对拍证明等价,而不是靠 sha 相同。
词典体积与许可:词典.txt(1,029,582 B)+ 词频.txt(219,541 B)来自 jieba 的 extra_dict/dict.txt.small(MIT),随包内嵌在 memory-engine/dict/,合计约 1.25 MB(npm pack --dry-run 实测:整包 tarball 1.1 MB / 解包 2.8 MB / 140 个文件)。
为什么要内嵌:不内嵌的话 npm i 完这一路直接不可用(要么首次运行去联网拉,要么用户自己找词典)。素材目录可用 MEMORY_ENGINE_DICT_DIR 指到别处(打包方嫌大可以把它们挪出包,代价是这一路默认不可用)。
许可按子树优先:memory-engine/dict/ 保持上游 MIT(全文与版权声明见 dict/LICENSE-词典.md),与仓级 AGPL-3.0 不冲突 —— MIT 与 AGPL 兼容,分发时把那份版权声明原样带上即可(与本仓 dsh-memory-local/ fork 的上游 MIT 同一口径)。
词典/词频按字节原样保留(.gitattributes 里 -text),可与上游 sha256 直接对拍。
怎么复现这些结论:
cd memory-engine
node tools/verify-recall-chain.mjs # 15 项:截断 / 降级 / 词表 / 留痕 / 多来源 / CLI
node tools/verify-recall-chain.mjs --baseline <改动前的 lib 目录> # 追加 2 项:开关关掉时与旧版逐字对拍
分层软淘汰 + 号段哨兵(0.2.0 新增)
两个老毛病:记忆只能删、不能降级;多台机共用一个库时,id 号段越界没人喊。这一版各给一件,出厂全是干跑 / 只读。
| 件 | 干什么 | 命令 | 出厂 |
| cli/layer-migrate.mjs | 给记忆表加一列 layer,存量行回填「缺省层」 | --db <库>(干跑)· --apply --backup <文件> · --restore <备份> [--apply] | 干跑:不带 --apply 一个字节都不写 |
| cli/layer-move.mjs | 把一整层搬到同库的边表 memories_evicted(不删),一条命令出去、一条命令回来 | --layer <层> --apply · --back --apply · --dump <f> / --verify <f> | 干跑 |
| cli/band-sentinel.mjs | 看「下一条新 id」会不会落到别人的号段里 | --line · --apply --out <文件> | 只读:缺省连报告文件都不写 |
层名与号段表都在配置里(memory-engine/config/layers.json、memory-engine/config/bands.json),代码里没有任何一处写死 core / recall / archival,也没有任何一台机器的名字。
单机 vs 多机:怎么退化
- 单机(缺省) = 配置里只有一条 band。此时哨兵只读本机库的 sqlite_sequence,判「下一条新 id」是否落在 base, base+width)。不 ssh、不猜对端、不假装知道别的机器。
这一步就能抓住最常见的事故形态:合并搬入把 seq 顶高 ⇒ 之后每条新记忆都写进别人的段。
- 多机(可选) = 配置里有多条 band,且非本机条目带 remote: { ssh, node, db? }。只有这时才逐台去读,远端命令 / 别名 / node 路径全部来自配置。
- 远端读不到 ⇒ 结论写「未探测 / 不可达」,退出码不是 4(这条是生产件的血泪假红:把"连不上"和"号段坏了" 混成同一个码,就会天天告警、把真事故淹掉)。4 只留给「本机库读不到」这种真失败。
要"有段没探测就算失败",显式加 --require-all(那时 rc=5)。
判据沿用生产件的好口径:seq 决定的下一条 id 越出本段 ⇒ 红;出现更高位段的行 ⇒ 红(更高位 id 不可能由这一台合法产生);出现更低位的别台段行 ⇒ 只披露不判红 (历史搬入 / 合并搬入共用段,删行=删记忆,而且不影响后续新 id)。
为什么默认不自动动库
三个命令出厂都是干跑或只读,理由就一条:它们都直接改记忆库,而这个仓的定位是"工具集",不能替使用者决定"什么时候动、动哪本"。具体收口:
- 迁移 --apply 必须同时给 --db 与 --backup:不给库路径就不许动(防手滑改到家目录那本),不给备份路径就拒写(动 schema 前先备份是硬闸,不是建议)。
- 搬迁默认干跑;真搬要先 --dump 留一份逐行指纹,搬回来再 --verify 对拍。
- 哨兵是只读件:不写库、不碰 sqlite_sequence、不 VACUUM;要接定时/自检就显式 --apply --out <文件>,而且机器可读内容只落文件,不上 stdout(日志/报告才可被采集,stdout 留给人读)。
搬走不等于删
layer-move 把整层搬进同一个库文件里的边表 memories_evicted,行原样躺着,另带两列: content_sha(与该行内容绑定的指纹)和 batch(哪一批搬的)。搬回时先核指纹再写回:边表里只要有一行被改过,整批拒动(不会出现"一半搬回、一半丢了")。
- 硬判据:搬出行数 + 剩下行数 == 搬前总行数(事务内断言,不成立整批回滚)。
- 代价(选边表而不是另一个库文件):搬走的行仍占这个库文件的体积 —— 软淘汰省的是上下文与召回面,不是磁盘;边表也没有 FTS 索引,搬走的行在引擎召回里看不见,只能用 SQL 或 --back 取回。
换来的好处是搬进搬出都在一个事务里,守恒能当场断言,也不会留下一个可能被误删的旁路库文件。
- 会搬空主表时默认拒跑(--force 才动):那通常是层名填错了。
怎么复现本节结论:
cd memory-engine
node test/run.mjs 12-layering # 21 项:干跑零写 / 加列回填 / 幂等 / 还原 / 守恒 / 指纹一致 / 号段红绿对照
node cli/layer-migrate.mjs --help
node cli/layer-move.mjs --help
node cli/band-sentinel.mjs --help
会话日志 → 记忆:提炼的边界(0.2.0 新增)
记忆的输入面原先只有一条:库里已经有的记忆(cli/distill.mjs 走的就是这条)。这一版补上第二条 —— 一份会话日志进来,产出 ≤N 条可追溯、可整批撤回的结论条。两个新件,旧入口一个字节都没动:
| 件 | 干什么 | 出厂 |
| lib/session-source.mjs | 会话 JSONL 适配层:只挑「能回指来源的结论句」,每条带场次 + 行号 + 字节偏移 + 原文锚 | 纯本地、零依赖 |
| cli/distill-sessions.mjs | 新入口:抽句 → 预算闸 → 一次模型调用 → 拍快照 → 写回(distilled 前缀 + 批次标签) | dry-run |
一、不做摘要式写入
抽的是原句,不是"这段讲了什么"的概括。所以下面这些一律不进库(每条都计数,跑完一次性告诉你挡了多少):
- 寒暄("你好""好的""收到")与疑问句(句末问号);
- 过程叙述("先…然后…接着…最后…"这种流水账,哪怕句尾偶然出现"决定"两个字);
- 推测("我觉得/可能/大概/应该是",除非同一句里有"一律/必须"这类规则词撑着);
- 纯工具输出(工具事件、命令回显)与图片 / 思考段;
- 超过长度上限(缺省 240 字)的整段转述 —— 宁可漏掉,也不把长文硬塞进记忆库。
二、只写能回指来源的结论条
每条结论都挂着它依据的那几句原文:sessionId + 行号 + 行首字节偏移 + 原文前 48 字。
场次 id 从输入日志 {"type":"session", …} 那一行取:id 或 sessionId 两种字段名都认;两者都没有就传 --session-id,再没有就一律标 unknown(回指退化成「文件名 + 行号」,命令行会显式告警)。
模型这一层只允许合成,且必须报出自己引用了哪几条候选(refs);越界引用、空结论、超 120 字、重复的条目会被直接剔掉(结果里的 rejected 写明理由)。回指不成立 ⇒ 这条宁可不写。
标签形态(与旧提炼条同前缀,撤回器认的就是这个前缀):
distilled src=<批次键> srcids=<行号,...> date=<YYYYMMDD> sess=<场次> run=session:<批次 id>
三、没配每日预算就不跑
不给 --max-tokens、台账里也没配 ⇒ 一次模型都不调,打印「未配置每日预算 ⇒ 本轮不跑(这是故意的)」,退出码 0(这不是错误)。它不继承内核那 150000 的缺省额度,也绝不"没配 = 无限跑"。
给了预算但当日已超、或者"这一次调用估算下来就会超",同样不跑,并把已用 / 预算写出来。
实测(deepseek-flash,自造三轮夹具会话,每轮一次调用):prompt 344–355 + completion 80–98 = 424–453 token/轮(三轮合计 1311;脚本估算按 1018 token/轮,约 2.3 倍保守)。
建议 --max-tokens 20000:按估算约 19 轮/天,按实测约 45 轮/天。
撤回:一批一个键
一轮 = 一个批次 = 一个来源键,所以撤回是一条命令的事(用既有的撤回器,没另写一个):
node memory-engine/cli/distill-rollback.mjs --db <memory.db> --key <批次键> --apply
撤回器真删前会先 VACUUM INTO 拍快照,并生成一键恢复脚本;删除语句恒带 AND tags LIKE 'distilled%',所以就算键算错也不会误伤真实记忆。写库前那一份快照则由 distill-sessions 自己拍(--snapshot-dir 可指定), 拍不成就不写库。
压缩日志(.jsonl.zstd)与"不静默失败"
DSH 的会话日志是 zstd 压缩的(而且一次追加一帧,是多帧文件)。session-source 会自己走一遍帧结构、 逐帧解压(node 22.15+ / 24 自带 zstd,零依赖);解不开就抛一条人话错误,告诉你「这是压缩日志,请先解压再喂」, 不会只解第一帧就假装读完了,也不会静默返回空结果。
怎么复现本节结论:
cd memory-engine
node test/run.mjs 13-session-distill # 40 项:抽句回指 / zstd 多帧 / 预算闸三态 / 快照 fail-closed / 整批撤回
node cli/distill-sessions.mjs --help
node cli/distill-sessions.mjs --source <会话.jsonl> # 无预算 ⇒ 打印"不跑",rc=0
node cli/distill-sessions.mjs --source <会话.jsonl> --max-tokens 30000 # dry-run ⇒ 列出将写什么
二、环境要求
- Node.js ≥ 22(用到内置 node:sqlite,以及 ESM 顶层能力)
- 平台:Windows / Linux / macOS 均可;路径默认值一律从环境变量或当前工作目录推导,不含任何写死的机器路径
- memory-engine/ 的本地向量编码额外需要 @huggingface/transformers(可选,见下)
三、安装
git clone https://github.com/kira905/dsh-memory-toolkit.git
cd dsh-memory-toolkit
npm install # 本仓根包零运行时依赖,此步只装元数据
要跑本地向量编码(memory-engine 的 ONNX 路径),再装它自己的依赖:
cd memory-engine
npm install # 会拉 @huggingface/transformers(较大,约 300+ MB)
不想装这个依赖也可以:引擎的召回支持HTTP 编码桥(见下),以及纯 FTS5 的降级路径。
四、快速开始(命令可直接照抄)
① 体检工具链的用法与参数(零副作用,先看再跑)
node memory-db-tools/audit-export.mjs --help
② persona ↔ 记忆体检(只读;读 $DSH_HOME 下的 persona 与记忆库)
node persona-memory-audit.mjs --help
③ 新会话开工简报(只读:设备行 + 记忆清单 + pinned 全文)
node memory-db-tools/list-memory-summary.mjs --help
④ 记忆引擎的启动自检(需要先在 memory-engine/ 内 npm install)
cd memory-engine && node cli/selfcheck.mjs --json
一次典型的季度记忆库体检(全部只读,产物落你指定的目录):
node memory-db-tools/audit-export.mjs --out ./_audit/memories.json
node memory-db-tools/audit-analyze.mjs --in ./_audit/memories.json --out ./_audit
node memory-db-tools/audit-struct-probe.mjs --in ./_audit/memories.json
node memory-db-tools/audit-verify-unchanged.mjs --baseline ./_audit/memories.json --expect-unchanged
五、与 DSH 的集成点
本仓不依赖 DSH 才能运行,但下面三处是刻意的接缝:
| 接缝 | 说明 |
| DSH_HOME(缺省 ~/.dsh) | 记忆库(memory/memory.db)、向量库(memory-vectors/)、preset(.agent-presets/)、profile 覆盖(profiles/<profile>/cordis.patch.yml)一律从它推导 |
| dsh-memory-local/ 的部署形态 | 插件占用上游包槽位 profiles/<profile>/node_modules/dsh-memory(不新增包名、不改 profile wiring),cordis.patch.yml 的 insert.name 必须保持 dsh-memory |
| 环境变量注入 | MEMORY_ENGINE_(库/向量/模型/批大小/召回参数)、DOC_INDEX_(文档索引扫描面)、DSH_ARCHIVE(备份落点)、DSH_PROD_ROOTS(生产树放行闸) |
六、数据与安全
- 对记忆库只读:引擎与全套 audit- 工具打开 memory.db 一律 readOnly: true;唯一的写口是独立向量库与你自己指定的输出目录。
- 不收集、不外发:没有任何遥测;唯一的外部网络调用是你主动启用的 LLM/编码端点。
- 凭据只从环境变量或本地配置文件读,代码里没有、也不会写任何密钥。
- 生产写闸:还原 / 回滚类脚本动到生产树前必须显式 --allow-production,否则拒跑(--dry-run 是只读档,不受限)。
七、许可
AGPL-3.0(GNU Affero General Public License v3.0),全文见 [LICENSE。
个人、学习、开源项目可自由使用与修改。若要在闭源或商业场景中使用(AGPL 的传染条款不适用),请在 Issues 开一条说明用途,获取商业授权。
子树例外:dsh-memory-local/ 是上游 dsh-memory(MIT)的 fork,该子树保持上游 MIT 许可(见其 package.json 的 license 字段与 FORK-BASE.json); memory-engine/dict/(词典与词频)来自 jieba(MIT),同样保持上游 MIT(见 dict/LICENSE-词典.md);其余目录(引擎代码、工具链、共用件)为本项目原创,按 AGPL-3.0 + 商业授权双许可发布。
子树许可优先:分发整包时,上述两个 MIT 子树按 MIT 原样附版权声明即可,不适用 AGPL 的传染条款。
八、相关仓
- dsh-butler-archive —— DSH 大管家(自主运维机制)归档仓
- dsh-session-toolkit —— 会话标题 / 会话治理
- dsh-ecosystem-panel —— 生态面板
- dsh-firstaid —— 急救台 / 回滚演练
- 同属 DSH 生态的伴生组件各自独立仓、独立版本、许可各自独立。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
kira905/dsh-memory-toolkit
双路召回记忆并留痕,软淘汰可查可恢复
相关插件 · 搜索 / 抓取
LLM 知识平台Tencent/WeKnora
概览 ·快速开始 ·新特性 ·功能 ·客户端 ·文档 ·开发
★ 36,379已汉化DSH
72
智能体记忆系统MemTensor/MemOS
MemOS 2.0 Stardust星尘
★ 12,228已汉化DSH
71.3
跨代理记忆库zilliztech/memsearch
跨平台语义记忆,专为 AI 编程智能体打造。
★ 2,799已汉化DSH
70.9
技能语料库EverMind-AI/SkillCorpus
SkillCorpus 能给你什么
★ 695已汉化DSH
69.8
动画花园资源检索yjl9903/AnimeGarden
動漫花園 镜像站 | 动画 BT 资源聚合站 | 动画 BT 资源开放接口
★ 1,391已汉化DSH
69.2
代码图谱记忆syncable-dev/memtrace-public
你的智能体值得拥有结构化记忆。
★ 510已汉化MCP
67.9
这是你的仓库?认领条目