🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

本地记忆库 eighteentang/dsh-plugin-memory-lite

DeepSeek Harness
eighteentang18 天前有提交已提供中文文档扫描:含敏感能力

给 DSH 的本地优先跨会话记忆:每轮自动沉淀 + 空闲自动整理 + 按需注入。数据全在本地…

30.8/100
综合分
1
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/13 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

自动兼容检查有告警:未发布到 npm registry,仅可从源码安装。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add eighteentang/dsh-plugin-memory-lite
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · chat
装得上吗
本站已在 dsh 0.2.1-rc.5 下真实安装成功
非静态推断
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 18 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查⚠ 装前注意

未发布到 npm registry,仅可从源码安装

展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✗npm 包dsh-plugin-memory-lite(未发布到 npm,仅可源码安装)
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/12 22:43:21

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

读写/删除本地文件发起外部网络请求
查看扫描证据(共 70 处,展示前 44 条)
  • 写入本地文件
    index.js:51
    appendFileSync(
  • 读取本地文件
    index.js:129
    const raw = readFileSync(REGISTRY_FILE, 'utf8');
  • 写入本地文件
    index.js:145
    mkdirSync(dirname(REGISTRY_FILE), { recursive: true });
  • 写入本地文件
    index.js:146
    writeFileSync(REGISTRY_FILE, JSON.stringify({ version: 1, stores: list.slice(-50) }, null, 1), 'utf8');
  • 读取本地文件
    index.js:190
    } else if (!existsSync(dbPath)) {
  • 写入本地文件
    index.js:225
    mkdirSync(dir, { recursive: true, mode: 0o700 });
  • 读取本地文件
    index.js:298
    return typeof cwd === 'string' && cwd.trim() && existsSync(cwd) ? cwd : null;
  • 读取本地文件
    index.js:316
    if (!existsSync(join(dir, 'memory.db'))) return;
  • 读取本地文件
    index.js:332
    if (config.globalMemory && existsSync(join(gdir, 'memory.db'))) {
  • 读取本地文件
    index.js:349
    if (existsSync(root)) {
  • 发起网络请求
    client/client.js:344
    const res = await fetch(`${API}${path}`, init);
  • 写入本地文件
    lib/storage.js:201
    mkdirSync(dir, { recursive: true, mode: 0o700 });
  • 写入本地文件
    lib/storage.js:205
    chmodSync(dbPath, 0o600);
  • 写入本地文件
    lib/storage.js:597
    mkdirSync(archiveDir, { recursive: true });
  • 写入本地文件
    lib/storage.js:599
    appendFileSync(file, rows.map((r) => JSON.stringify(r)).join('\n') + '\n', 'utf8');
  • 读取本地文件
    lib/storage.js:916
    const base = workspaceRoot && existsSync(workspaceRoot) ? workspaceRoot : null;
  • 读取本地文件
    lib/persona.js:41
    if (!existsSync(file)) continue;
  • 读取本地文件
    lib/persona.js:43
    out[slot] = readFileSync(file, 'utf8').replace(/^ FEFF/, '');
  • 读取本地文件
    lib/persona.js:44
    out.mtimes[slot] = statSync(file).mtimeMs;
  • 写入本地文件
    lib/persona.js:62
    mkdirSync(dir, { recursive: true });
  • 读取本地文件
    lib/persona.js:70
    if (existsSync(file)) {
  • 写入本地文件
    lib/persona.js:73
    mkdirSync(bdir, { recursive: true });
  • 写入本地文件
    lib/persona.js:75
    copyFileSync(file, backup);
  • 写入本地文件
    lib/persona.js:83
    writeFileSync(tmp, body, 'utf8');
  • 写入本地文件
    lib/persona.js:84
    renameSync(tmp, file);
  • 读取本地文件
    lib/persona.js:88
    return { ok: true, file, backup, bytes: Buffer.byteLength(body, 'utf8'), mtime: statSync(file).mtimeMs };
  • 写入本地文件
    lib/backup.js:53
    mkdirSync(join(logFile, '..'), { recursive: true });
  • 写入本地文件
    lib/backup.js:54
    appendFileSync(logFile, `${new Date().toISOString()} [${level}] ${msg}\n`, 'utf8');
  • 读取本地文件
    lib/backup.js:67
    if (!existsSync(file)) return { ok: false, reason: '文件不存在' };
  • 读取本地文件
    lib/backup.js:68
    const size = statSync(file).size;
  • 读取本地文件
    lib/backup.js:94
    if (!existsSync(dir)) return [];
  • 读取本地文件
    lib/backup.js:95
    return readdirSync(dir)
  • 读取本地文件
    lib/backup.js:97
    .map((f) => ({ name: f, path: join(dir, f), mtime: statSync(join(dir, f)).mtimeMs, size: statSync(join(dir, f)).size }))
  • 写入本地文件
    lib/backup.js:122
    mkdirSync(dir, { recursive: true, mode: 0o700 });
  • 读取本地文件
    lib/backup.js:133
    if (existsSync(tmpFile)) rmSync(tmpFile, { force: true });
  • 删除本地文件
    lib/backup.js:133
    if (existsSync(tmpFile)) rmSync(tmpFile, { force: true });
  • 删除本地文件
    lib/backup.js:138
    try { if (existsSync(tmpFile)) rmSync(tmpFile, { force: true }); } catch { }
  • 删除本地文件
    lib/backup.js:161
    try { if (existsSync(`${tmpFile}${side}`)) rmSync(`${tmpFile}${side}`, { force: true }); } catch { }
  • 删除本地文件
    lib/backup.js:168
    try { if (existsSync(tmpFile)) rmSync(tmpFile, { force: true }); } catch { }
  • 写入本地文件
    lib/backup.js:175
    renameSync(tmpFile, file);
  • 写入本地文件
    lib/backup.js:176
    try { chmodSync(file, 0o600); } catch { }
  • 删除本地文件
    lib/backup.js:180
    try { if (existsSync(tmpFile)) rmSync(tmpFile, { force: true }); } catch { }
  • 删除本地文件
    lib/backup.js:204
    unlinkSync(v.path);
  • 写入本地文件
    lib/backup.js:228
    try { mkdirSync(dir, { recursive: true, mode: 0o700 }); } catch { return { ok: false, reason: '备份目录不可用' }; }
扫描于 2026/11/1(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/dsh-tools
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 跨会话记忆结论
  • 本地SQLite存储
  • 按需注入记忆

README

由 DeepSeek 最新模型翻译生成
dsh-plugin-memory-lite 给 DSH 的本地优先跨会话记忆:每轮自动沉淀,空闲时自动整理,需要时按需注入。 数据全部存在本地 SQLite 文件里 —— 不联网、不上传、不依赖任何外部记忆服务。 它解决什么 DSH 的会话本身是有记忆边界的:换一个会话、或者一次压缩(compact)之后,之前聊过的结论就找不回来了。你得反复重新交代「我们用两层架构」「那个坑别再踩」。 这个插件在后台做三件事: | 层 | 做什么 | 什么时候 | 成本 | | 日志层 | 每轮结束把对话原样追加到本地 | 自动,每轮 | 零模型调用 | | 长期条目层 | 把日志里的结论蒸馏成「记忆条目」 | 你点「立即整理」时(也可开启空闲自动) | 每次一次模型调用 | | 注入层 | 把你这句话相关的记忆作为独立消息追加到对话尾部 | 自动,每轮 | 零成本 | 日志层永远不进提示词 —— 只有蒸馏后的条目才会被注入。 否则上下文预算和前缀缓存都会被日志淹没。 日志层存的不是「逐字原样」:落盘前会剔掉宿主注入的系统样板(<system-reminder> 里的用户全局 AGENTS.md 等),也会去掉与前文重复的部分(实测 10 轮对话曾因此写出 804 KB,相邻两条有 6/9 对是「后一条完整包含前一条」)。没有内容的轮次干脆不记。
展开完整文档(README 剩余 1 节)
dsh-plugin-memory-lite

给 DSH 的本地优先跨会话记忆:每轮自动沉淀,空闲时自动整理,需要时按需注入。

数据全部存在本地 SQLite 文件里 —— 不联网、不上传、不依赖任何外部记忆服务。

它解决什么

DSH 的会话本身是有记忆边界的:换一个会话、或者一次压缩(compact)之后,之前聊过的结论就找不回来了。你得反复重新交代「我们用两层架构」「那个坑别再踩」。

这个插件在后台做三件事:

| 层 | 做什么 | 什么时候 | 成本 |
| 日志层 | 每轮结束把对话原样追加到本地 | 自动,每轮 | 零模型调用 |
| 长期条目层 | 把日志里的结论蒸馏成「记忆条目」 | 你点「立即整理」时(也可开启空闲自动) | 每次一次模型调用 |
| 注入层 | 把你这句话相关的记忆作为独立消息追加到对话尾部 | 自动,每轮 | 零成本 |

日志层永远不进提示词 —— 只有蒸馏后的条目才会被注入。
否则上下文预算和前缀缓存都会被日志淹没。

日志层存的不是「逐字原样」:落盘前会剔掉宿主注入的系统样板(<system-reminder> 里的用户全局 AGENTS.md 等),也会去掉与前文重复的部分(实测 10 轮对话曾因此写出 804 KB,相邻两条有 6/9 对是「后一条完整包含前一条」)。没有内容的轮次干脆不记。

安装

命令行:

dsh plugin --profile <你的profile名> add github:eighteentang/dsh-plugin-memory-lite#v1.3.0

或者在界面里装(不用命令行):侧边栏 「插件」→「添加插件」,把地址贴进去。
那个输入框接受包名、Git 地址、压缩包、本地绝对路径四类写法:

| 贴什么 | 结果 |
| https://github.com/eighteentang/dsh-plugin-memory-lite#v1.3.0 | ✅ 装指定版本(推荐) |
| https://github.com/eighteentang/dsh-plugin-memory-lite | ✅ 跟随主分支 |
| github:eighteentang/dsh-plugin-memory-lite#v1.3.0 | ✅ 同上两种写法都行 |

⚠ 别贴 Releases 页的地址。 …/releases/tag/v1.3.0 会被直接拒绝(提示 a URL must point at a git repository or a tarball)—— 它只认仓库根地址或压缩包链接;版本要写成 #v1.3.0 拼在根地址后面。同理 …/tree/main 这类网页地址也不行。

这几条不是猜的:宿主侧的解析函数 parseInstallSpec 曾被抽出来逐例跑过(那是开发者本机的诊断脚本,不随仓库发布 —— 见下方「关于本仓库不含开发工具」)。

装完后重启客户端,设置页会出现 「记忆控制」。

#v1.3.0 是版本钉子 —— 钉住版本避免「装到一半上游改了」。
去掉 #… 就是跟随主分支(自动拿到新改动,也自动拿到新 bug)。
全部版本见 CHANGELOG.md;升级就是先卸载再装新版(DSH 的插件目前不支持自动更新)。

开发期用 link: 装(link:D:/path/to/memory-lite)也是支持的,但必须保留 peerDependencies,否则插件的包解析会失败。

怎么用

平时不用管

- 每轮对话结束,它自动把这一轮记下来(不花模型额度)
- 每句用户消息到达时,它按关键词找相关记忆,注入到对话尾部
- 整理成条目默认要你手动点(避免「没打招呼就花额度」)。
想让它自己长出来,把 autoDistill 设成 true —— 那样空闲 30 分钟后会自动整理(每天最多 4 次,见下面配置)。

想手动做点什么

设置页 →「记忆控制」分成上下两块:

- 上面两个文本框:人设设定 / 工作规范与行为准则 想写什么就写什么 —— 没有任何格式要求(不用标题、不用 markdown)。
两块内容存在插件自己的两个文件里:

$DSH_HOME/memory-lite/persona/persona.md ← 人设设定 $DSH_HOME/memory-lite/persona/rules.md ← 工作规范与行为准则

它们被注入成 system prompt 的一段(位置紧跟框架自己的人设段)—— 所以是 每一轮都生效,不像记忆库那样靠召回(规范类的东西「这轮没命中」就等于静默失效)。
保存后下一轮立刻生效,不用重启;写前自动备份到同目录的 backup/。
代价:它们随插件走 —— 插件停用/卸载,人设与准则就不生效了。
- 下面是长期记忆列表:搜索、按状态筛选、改状态(在用 / 已完成 / 已停用)、点「立即整理」。
列表是一行一条、行行等高(层级 · 项目 · 标题 · 敏感 · 用过N次 · 状态), 不展示正文 —— 标题/项目太长会省略号截断,鼠标悬停看全文。
「用过几次」显示成 3×(悬停会说明「被自动注入过 3 次」,– 表示还没用过)。

想手动记一条记忆:直接在对话里说(memory_note),比在界面上敲更快、还能带上上下文。

- 对 DSH 说(这些是模型可见的工具):
- 「记住:这个项目用两层记忆架构」→ memory_note
- 「搜一下我之前说过关于注入的东西」→ memory_search
- 「把那条标记成已完成」→ memory_update
- 「忘了那条吧」→ memory_forget(删除需要你点头,走审批)

用户级记忆(跨工作区)

有些事跟哪个项目无关:你是谁、做什么工作、说什么语言、跨项目的工作约定。
这类东西写进某个工作区,就只有那个项目能召回 —— 所以要显式写进用户级库:

$DSH_HOME/memory-lite/global/memory.db ← 用户级(跨工作区) <工作区>/.dsh-memory/memory.db ← 该工作区自己的(默认)
- 怎么写:对话里说「记住(跨工作区):我在深圳,做产品运营」→ 我用 memory_note 带 scope="user" 写进用户级库。默认是当前工作区,我不会靠 project 字段猜。
- 怎么起作用:每轮召回时「当前工作区 + 用户级」一起搜、合并排序,同分时工作区优先;同一条记忆只出现一次(按内容判重,不是按 id)。
- 不会拖累提示词:合并后仍受 injectMaxItems 那一个名额上限管,用户级记忆写多少条都不会让每轮注入变长。
- 不进常驻索引段:索引段只有 4096 B,容量留给工作区条目(所以用户级条目是靠「按需召回」生效,不是像人设那样每轮都在)。
- 面板:设置页 →「记忆控制」的库下拉里会多一项「用户级(跨工作区)」,可以像别的库一样看、搜、改状态。

还要更"永远在场"的?那是人设/工作规范两个文本框的活(它们每轮都注入)。
分工:每轮都必须遵守的规则 → 那两个框;换个项目还需要的事实 → 用户级库;跟这个项目有关的事 → 工作区库。

三种状态

| 状态 | 含义 | 之后会怎样 |
| 在用 | 内容有效,可能被召回 | 正常参与注入与检索 |
| 已完成 | 这件事做完了(todo 完成、话题达成目标) | 内容仍然有效,仍可被搜到与召回 |
| 已停用 | 这条不再正确或不再需要(过时、被取代、写错了) | 不再被注入,但仍可搜到、可随时恢复 |

数据在哪

<工作区>/.dsh-memory/memory.db ← 记忆库(每个工作区一个) <工作区>/.dsh-memory/archive/ ← 日志溢出后的归档(jsonl) $DSH_HOME/memory-lite/global/ ← 用户级库(跨工作区,见上一节) $DSH_HOME/memory-lite/persona/ ← 人设 / 工作规范两个文本框的内容 $DSH_HOME/memory-lite/workspaces.json ← 库 → 工作区 的注册表

删除记忆:三种方式,按彻底程度

1. 设置页把条目改成「已停用」—— 不注入了,但还在库里(推荐,可恢复)
2. 用 memory_forget 工具删单条 —— 需要审批
3. 直接删 .dsh-memory/ 目录 —— 全部记忆消失,不可恢复

配置

在 profile 的 cordis.patch.yml 里给插件加 config:

- id: memory-lite
name: dsh-plugin-memory-lite
config:
autoCapture: true # 每轮自动落日志(不花额度) autoRecall: true # 每轮按关键词注入(不花额度) autoDistill: false # 空闲自动整理 —— 默认关(会花模型额度) distillIdleMinutes: 30 # 开启 autoDistill 时:多久没说话才算"空闲" distillMinIntervalMinutes: 30 # 开启 autoDistill 时:两次整理之间的最小间隔 distillMaxPerDay: 4 # 开启 autoDistill 时:每天最多整理几次(成本硬上限) injectMaxItems: 3 # 单次最多注入几条 injectMaxChars: 2000 # 单次注入的字符上限 crossWorkspaceSearch: true # 允许跨工作区检索 distillProvider: '' # 留空 = 用会话默认模型 distillModel: ''

关于花额度:默认 autoDistill: false —— 日志照记,但只有你点「立即整理」时才会调用模型。
想让它自己长出来就把 autoDistill 设成 true,那时上面三个 distill 参数才生效(空闲多久算闲 / 冷却 / 每天上限)。

设计上的几个刻意选择

这些都是踩过坑之后定下来的,改动前请先读 AGENTS.md:

- 注入只走长期层。日志层永不进提示词,否则前缀缓存失效、上下文预算被淹。
- 注入是"只追加":作为独立消息追加在本轮已领取消息之后,不改写已有历史。
- 不重复注入:同一会话里已注入过的条目不再注入;压缩后重置这个记录并补回常驻记忆。
- 敏感内容 fail-closed:sensitive 在写入时算一次并落列,注入时只读列。
注入路径上再判断,等于把 fail-closed 变成 fail-open(判断代码出错就注入了)。
- 删除走审批:memory_forget 声明为破坏性工具,转人工确认。
- 超预算的日志留到下次:不"发都没发出去却记成处理过了"——那会永久丢内容。
- 蒸馏输入先剔系统样板,再取头尾:日志一条 = 一整轮,里面夹着宿主注入的 <system-reminder>(用户全局 AGENTS.md 等)。实测 10 条真实日志里有 7 条的 "前 1500 字"逐字相同 —— 不剔样板的话,模型看到的不是"这一轮发生了什么",而是同一段样板。剔完再按 头 40% + 尾 60% 取样(结论在尾部)。
- 常驻索引段每条只列 20 字:这段每轮都在 system prompt 里,中文 1 字 = 3 B。
实测 33 条时它已占到 4096 B 预算的 83.8%(每行 123 B)→ 只装得下约 33 条,再多就会静默掉出提示词。截到 20 字后约能装 49 条。别指望"回填标题"来救容量 —— 回填的标题同样会被截断,额外容量是 0。

已知限制

- 检索是关键词级的,不是语义向量检索。中文查询按连续串与子串匹配,所以换个说法可能搜不到(用 memory_search 时尽量带上你当时用的词)。
- 每个工作区一个库,默认只搜当前工作区;跨工作区要显式 scope="all"。
- 日志层有容量上限(每会话约 200 条),超出会先写归档文件再删库内行(不静默丢)。
- 空闲自动整理依赖宿主进程活着 —— 客户端关着的时候不会整理。

关于本仓库不含开发工具

本仓库只有插件本身(index.js / lib/ / client/ / locale/ / 图标 / 补丁 / README / LICENSE)。

校验器(verify-.mjs)、诊断脚本(diag-*.mjs)、迁移脚本与发布助手不在仓库里,它们只在开发者本机(原因:那些脚本含开发机的绝对路径与个人数据位置,不该跟着插件发给别人;这也是本项目的一贯纪律 —— 开发产物不进用户包)。

如果你想自己验证这个插件的行为,用它的运行时表现验:装完之后在设置页看「记忆控制」,或者用 memory_stats / memory_search 这些工具直接问它。
真机行为才是判据。

许可

MIT
预览分享卡片长什么样 ↓eighteentang/dsh-plugin-memory-lite
分享 · DeepSeek Harness Hub
eighteentang/dsh-plugin-memory-lite

给 DSH 的本地优先跨会话记忆:每轮自动沉淀 + 空闲自动整理 + 按需注入。数据全在本地…

回到顶部