DeepSeek Harness 插件

LittleBlackTong/dsh-plugin-memory

Star 数 ★ 4 下载量(近 30 天) 3,008 分类 记忆 收录于 2026-08-19 npm dsh-plugin-memory

带 LLM Wiki 结构与 SOUL 人格文件的长期 markdown 记忆库:会话启动注入 boot 块(默认提问后才注入、且只注入当前激活会话),含 remember/recall/consolidate/forget 工作流、内嵌 memory 技能、pack/unpack 迁移工具,以及空闲时以第一人称主动追忆(recall nudge)的拟人化能力。同时补上记忆库原先缺的“可观测 + 可维护”:按文件分配 boot 预算(索引路由表不再被截断)、自动戳记 last_access(salience 衰减真正生效)、只读记忆健康看板(体量/新鲜度/与 lint 同源的体检)、声明式索引编译器(页面声明 summary:,一条命令重写索引行),可拖动、可滚轮缩放的记忆图谱(页间链接 + 共享 tag)、结构化检索(tag/type/salience/hot/stale 过滤)、针对「尚无内容关系」页面的补链建议,以及以「最该做的 N 件事」收尾的 `checkup` 体检报告。同时把操作细节移到按需加载的技能,boot 注入体量减少约 23%。

安装

# npm 包(预构建)

dsh plugin --profile web add dsh-plugin-memory

# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)

dsh plugin --profile web add github:LittleBlackTong/dsh-plugin-memory

装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本——pnpm 默认拦截,所以安装可能停在 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED 或 ERR_PNPM_IGNORED_BUILDS;dsh 会打印出需要添加的确切键名,把它加进该 profile 的 pnpm-workspace.yaml 的 allowBuilds 下,重跑一次即可装上。放行构建本身就是一次信任判断:请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。

README

DeepSeek Harness 长期记忆插件:跨会话、可迁移、带「灵魂」的 markdown 记忆库。

English TL;DR — A Cordis plugin for DeepSeek Harness that gives agents a persistent, cross-session, migratable long-term memory: a markdown + git store (inspired by Karpathy's LLM Wiki pattern) with a SOUL.md persona file, auto-injected at every session start via the system-prompt runtime context, plus remember / recall / consolidate / forget workflows and portable CLI tooling.

特性

  • 开机强制注入(带路由表保护):插件通过 ctx.systemPrompt.context() 把记忆 boot 块(SOUL.md 人格 + MEMORY.md 协议 + index.md 目录 + 最近动态)注入会话上下文。宿主按投影去重:记忆不变就不重复注入,变化时新快照自动取代旧的——这是"新会话必先加载记忆"的硬保障,不需要模型碰运气调技能。总预算是 bootMaxChars,按文件分配:每个文件先拿一份均分(受实际大小封顶),剩余额度优先给 index.md、再给尚有内容未注入的文件——所以记忆库长大时,目录的尾部不会先被截掉(详见「boot 预算分配」)。默认还带两道礼貌闸门:deferUntilUserSpeaks(用户开口后才注入)与 activeSessionOnly(只注入当前激活会话),见「配置」。
  • last_access 自动戳记:MEMORY.md 的 salience 衰减规则依赖 last_access,而它以前纯粹靠自觉——模板里有字段、规则里提到它,但没有任何代码去写它,于是每个页面都原地变老、衰减表形同虚设。现在会话收到第一条真实用户消息时,插件会把 boot 块实际注入过的页面(SOUL.md / MEMORY.md / index.md + 目录里被引用到的页)戳成当天,每会话一次、按天幂等,只改 last_access 一行;dsh-memory touch <pages...> 可手动补记,trackPageAccess: false 可关闭。
  • SOUL.md 铸魂:安装后首要任务是和用户对话定义灵魂(名字、性格、价值观、语气、边界)、确认身份与关系(BOOTSTRAP.md 清单驱动,complete 前优先于常规任务)。
  • 铸魂自动引导:记忆库还没有灵魂(BOOTSTRAP.md 非 complete,或 SOUL.md 仍是占位模板)时,boot 块会自动前置一段第一人称引导词——「我的首要任务是确认我是谁,还有你是谁:我叫什么名字、怎么称呼你、你我是什么关系、我该是什么样的性格」——像 OpenClaw 初始化那样,由 agent 在对话里主动发起铸魂,逐项问、逐项写回,而不是等用户来喂。铸魂完成后引导词自动消失,零开销。
  • 复利记忆:遵循 Karpathy 的 LLM Wiki 约定——记忆是"一次编译、持续保鲜"的持久产物,不是每次查询重新 RAG。remember / recall / consolidate / forget 四操作 + salience 三级衰减。
  • 可迁移:记忆本体是纯 markdown + git + 自描述 schema,任何能读 markdown 的 agent 都能接手。dsh-memory pack/unpack 打包迁移。
  • 内嵌技能:通过 ctx.skills.register() 注册 memory 技能(操作协议随插件分发);项目级 .dsh/skills/memory 文件技能仍可覆盖它。
  • 防懒 digest 唤醒:每轮结束后,若 agent 空闲且记忆库超过 digestNudgeAfterMinutes 未写入,插件注入一条 digest 提醒(合成消息,走 agent.followup),把"会话收尾沉淀"从靠自觉变成有机制兜底;带冷却与每会话限次,不骚扰。独立于 dsh-plugin-heartbeat,两插件各自可装、互不依赖。
  • 主动追忆(拟人化):对话空下来时,插件会以第一人称主动提起一件真实记得的、关于用户或你们之间的事(偏好、往事、未了的决定、最近的进展),把记忆从"只写回"变成"也用起来"——像老友自然想起那样,而非报状态。间隔在最短/最长之间随机取值(不固定节奏),配合每会话限次,不骚扰、不编造、不硬聊;纯对话行为,不写记忆库。同样独立于 heartbeat。
  • git 自动提交:记忆库变更静默 autoCommitQuietSeconds 后自动 git add -A && git commit(无 .git 则跳过)——历史可回滚不再依赖 agent 记得 commit。
  • 设置面板:在 DSH 设置页提供「记忆 Memory」区块——总开关、记忆目录、开机注入、技能注册、主动追忆(开关 + 随机间隔范围 + 每会话次数)均可热改,立即生效,无需重启。
  • index 自动整理(声明式编译):索引行不再手写。页面在 frontmatter 里声明 summary:,dsh-memory index --write 把行重写为它的投影(标签 ≤32 / 摘要 ≤48 / 整行 ≤132 字符),只改行内容,分节与顺序逐字节保留,且幂等。index --check 给机器判定(有漂移退出码非 0),index --sync-frontmatter 把已写在 index 里的摘要回填进页面(老库一次性迁移)。digest 提醒会在索引漂移时附一句提示,agent 顺手就能修。
  • 互链成为默认动作:index.md 是目录(谁存在),页间互链才是关系(谁和谁有关)——实测一个真实记忆库里 29 页只有 1 条真互链,而图谱与跨页综合全都建立在这层关系上。现在三处一起推:skills/memory.md 的 remember 流程、MEMORY.md 模板的工作流、以及 digest 提醒的收尾指令,都明确要求"给这次碰过的页面各补 1–3 条相关页链接"。
  • 补链建议:dsh-memory graph --suggest 列出「只有索引入口、没有任何内容关系」的页面该引用谁——按共享主题 tag、标题词、同类型打分,并说明理由(共享 #dsh / 标题词 dsh / 同类型)。只报告不写入:是否连、怎么连由 agent 决定。GET /api/memory/graph?suggest=1 同源。
  • 记忆图谱:设置页里一张只读关系图 —— 不只是 index.md 那种"索引连着所有页"的星形,而是把页面已经编码但没人画出来的关系画出来:索引路由(index.md 指向每一页——这是全库最大的边集,43 条;先前版本跳过了元文件,导致 index 在图上孤立无援)、页间显式 markdown 链接(含相对路径解析)、共享 frontmatter tag(通用容器 tag 如 project/skill 会被忽略,稠密 tag 走锚点链而非全连接)。坐标由服务端 lib/graph-layout.js 一次性算好(确定性、无随机、无依赖),客户端只负责画 SVG。可交互:滚轮缩放(以光标为中心,缩放范围 0.4×–2.5×,非 passive 监听所以不会连带滚动设置页)、拖空白处平移、重置视图 回到全图;按住节点即可拖走,直接邻居按距离轻微跟随,松手后带缓动滑回原布局——拖动期间暂停过渡做到 1:1 跟手,用 O(邻居数) 的局部松弛而不是逐帧全量力导向(后者在几百页时会卡)。悬停高亮邻里,首次渲染从中心绽开(尊重 prefers-reduced-motion)。图谱每 25 秒自动刷新(面板开着时记忆变更会自己出现,副标题显示「更新于 HH:MM:SS」);取数失败保留上一张图而不是清空。视图数学(zoomAt/panBy/toGraphPoint)在 lib/graph-view.js 里是纯函数并有测试——缩放中心不漂移这件事必须被钉住。GET /api/memory/graph(只读、实时,?types=1 追加同类型弱边)。三类边在图上可区分:页间互链=实线蓝、索引路由=灰色虚线、共享 tag=细线。
  • dsh-memory checkup:把四个读者(lint 的完整性、status 的计数、看板的体量与新鲜度、graph 的关系)合成一份带优先级的报告 —— 体量、新鲜度条形图、连接度、一致性检查,最后给出"最该做的 N 件事"。四个数字是诊断,"做这三件事"才是产品。有活干时退出码非 0,可被脚本消费。--boot=N 用于按你的 boot 预算计算索引占比。
  • 记忆健康看板:同一区块下方是一张只读看板——记忆页数 / index 路由数 / 记忆字数、访问新鲜度条形图(今天 / ≤7 / ≤30 / ≤90 / >90 天)、四项体检结论(index 链接、孤儿页、frontmatter、新鲜度)与健康分、陈旧页候选、最近 5 条动态。数据来自新增的 GET /api/memory/insights(每次请求实时统计,不缓存),体检口径与 dsh-memory lint 同源,所以看板与 CLI 不会互相打脸。
  • 零构建:纯 ESM JavaScript,无编译步骤,pnpm add 即用。

架构

插件只拥有工作流,不拥有数据格式:

dsh-plugin-memory(本插件)
├── lib/index.js        # Cordis 入口:boot 注入 + 运行时技能注册 + settings 热改
├── lib/boot.js         # boot 块渲染(SOUL/MEMORY/index + 最近 log,按文件分配预算)
├── lib/pages.js        # 页面访问记账:extractReferencedPages / touchPages(last_access)
├── lib/insights.js     # 记忆健康看板的数据层(页数/分布/新鲜度/体检,只读)
├── lib/activity-tracker.js # 两道礼貌闸门:用户是否开口 + 当前激活会话
├── lib/digest-guard.js # 防懒 digest 唤醒(空闲 + 记忆库久未写 → followup 提醒)
├── lib/recall-nudge.js # 主动追忆(空闲 → 第一人称提起一件真实往事,纯对话不写库)
├── lib/scaffold.js     # 记忆库脚手架(模板只建不覆盖)
├── lib/client.js       # 客户端半:设置面板「记忆 Memory」区块 + 记忆健康看板
├── skills/memory.md    # 内嵌技能的操作协议正文
└── scripts/memory.mjs  # CLI:init/search/touch/lint/status/pack/unpack

记忆库(用户数据,默认 ~/.memory)
├── SOUL.md       # 人格与灵魂(用户主导)
├── BOOTSTRAP.md  # 铸魂清单(complete 前优先)
├── MEMORY.md     # schema 与维护协议(自描述)
├── index.md      # 页面目录    log.md # 时间线(append-only)
├── identity/ user/ skills/ decisions/ projects/{active,archive}/ concepts/
└── raw/          # 不可变源材料

安装

dsh plugin --profile <profile> add dsh-plugin-memory

(包内置 dsh.bundle manifest,dsh plugin add 会把它自动挂进 profile 的 bundles 层;dsh-market 里的一键安装同此通道。)

重启 profile(DSH Desktop 重启应用)后生效。

📌 版本要求:DSH session format v4(官方 DeepSeek Harness 0.1.7+)

注入消息的 source 使用 producer-owned kind plugin:memory。v4 的原生准入明确拒绝 v3 时代的 { kind: 'plugin', plugin: 'memory' } wrapper,报 format v4 message requires a producer-owned source kind(该错误被包成 code: "UNKNOWN", 所以在界面上会显示成 ... source kind UNKNOWN——UNKNOWN 是错误码,不是 kind 值)。

v4 与 v3 的白名单互斥(v3 只认 plugin),因此无法同时兼容两版。 0.8.0 及更早版本在 v4 上会触发该错误;旧版 DSH 请停留在 0.8.0。

⚠️ 不要再往 profile 的 cordis.patch.yml 里手写 - insert: {id: dsh-memory, ...}: 那会与 bundle manifest 的自动挂载产生两条同名 entry,整个 profile 会以 duplicate loader entry id "dsh-memory" 启动失败(2026-08-18 实机事故)。 运行期配置(enabled / memoryDir / autoInject / registerSkill / recallEnabled)改走 <dshHome>/memory.json(设置面板热改);composition 配置见下表。 如需覆盖某个 composition 键,用不带 insert 的 id 覆盖条目(见配置一节)。

配置

键 默认值 含义
enabled true 总开关:关闭后不注入 boot 块、不注册 memory 技能
memoryDir ~/.memory 记忆库绝对路径(~ 自动展开)
bootFiles [SOUL.md, MEMORY.md, index.md] 开机注入的文件
bootMaxChars 12000 boot 块总字符预算(防止占用过多上下文);按文件分配见下文
bootFileBudgets — 逐文件字符上限,例如 { index.md: 3500 };未列出的文件分剩余额度(composition 层,改完需重启)
trackPageAccess true 会话首条用户消息时,把 boot 块实际注入的页面戳 last_access(关闭后衰减表需手动维护)
autoInject true 会话开始时注入 boot 块
deferUntilUserSpeaks true 用户发出第一条真实消息后才注入(boot 块 / 追忆 / digest 提醒都遵守);面板可热改
activeSessionOnly true 只对「当前激活会话」(最近收到用户消息的会话)注入,后台会话不打扰;面板可热改
registerSkill true 注册内嵌 memory 技能
scaffold true 记忆库缺失时自动创建模板(只建不覆盖)
configFile <dshHome>/memory.json 用户可改配置的 JSON 文件路径(设置面板读写它)
digestNudgeEnabled true 防懒 digest 提醒总开关(composition)
digestNudgeAfterMinutes 120 记忆库超过多久未写入就提醒
digestNudgeCooldownMinutes 180 两次提醒的最小间隔
digestNudgeMaxPerSession 2 每个会话最多提醒次数
recallEnabled true 主动追忆总开关(面板可热改)
recallIntervalMinMinutes 30 随机间隔下限(分钟,面板可热改)
recallIntervalMaxMinutes 240 随机间隔上限(分钟,面板可热改)
recallMaxPerSession 3 每个会话最多追忆次数(面板可热改)
autoCommit true 记忆库 git 自动提交开关(composition)
autoCommitQuietSeconds 60 变更静默多久后提交(防抖)
autoCommitIntervalSeconds 60 变更轮询间隔

设置面板(热改)

enabled / memoryDir / autoInject / deferUntilUserSpeaks / activeSessionOnly / registerSkill / recallEnabled / recallIntervalMinMinutes / recallIntervalMaxMinutes / recallMaxPerSession 十项在 DSH 设置页的「记忆 Memory」区块中可改,即时生效:boot 注入、两道礼貌闸门、技能注册、主动追忆(含随机间隔与次数)随修改立即生效;记忆目录切换时自动为新目录初始化脚手架(scaffold: true 时)。其余键(bootFiles / bootMaxChars / bootFileBudgets / trackPageAccess / scaffold / configFile / digestNudge* / autoCommit*)只在 composition 配置层生效,改完需重启。

boot 预算分配(为什么 index 不会先被截掉)

bootMaxChars 是总预算,按文件分配而不是简单平摊:

  1. 有 bootFileBudgets 条目的文件先拿自己的额度;
  2. 其余文件各拿一份均分额度,以文件实际大小封顶——短文件把用不完的额度退回池子;
  3. 池子再补给「还有内容没注入」的文件(每个最多补到均分额度的两倍):index.md 优先,其余按体积降序——所以截断落在哪个文件上由体积决定,而不是由 bootFiles 的书写顺序决定。

当每个文件都小于均分额度时,结果与旧的平摊规则逐字节相同;只有记忆库长大后才会不同。这也意味着:index.md 是路由表,别把它写成摘要表——它会被完整注入,膨胀的代价是挤掉人格与其他记忆。

「当前激活会话」怎么判? DSH 宿主侧没有「浏览器当前聚焦的会话」信号(激活会话是前端概念)。插件用最近一次收到真实用户消息的 live root agent作为激活会话的代理:你在哪个会话里说话,哪个会话就激活;切到别处但不发消息时,宿主感知不到「切换」这个动作(这是代理的已知边界)。若日后需要精确到「展开/聚焦」级别,需补一小段客户端 focus 上报。

记忆健康看板

设置页「记忆 Memory」区块下方是一张只读看板,数据来自 GET /api/memory/insights:

展示 含义
记忆页 / index 路由 / 记忆字数 记忆库规模(字数按字符计,中文不会按字节虚高 3 倍)
健康分(0–100) 由孤儿页、缺 salience/type、陈旧页、失效 index 链接加权得出
访问新鲜度条形图 今天 / ≤7 / ≤30 / ≤90 / >90 天 / 从未戳记(驱动自 last_access 自动戳记)
四项体检 index 链接完整、无孤儿页、frontmatter 完整、访问新鲜度——口径与 dsh-memory lint 同源
陈旧页候选 超过 90 天或从未戳记的页面,按陈旧度排序(前 8)
最近动态 log.md 最新 5 条标题

看板是实时的:每次打开设置页(或改完任一配置项后)重新拉取,不落盘、不缓存、不写记忆库。头less 部署没有 webServer 时该路由与看板都不存在,其余功能不受影响。

覆盖 composition 键(例如把 boot 块预算调大),在 profile 的 cordis.patch.yml 里写不带 insert 的 id 覆盖条目:

- id: dsh-memory
  config:
    bootMaxChars: 12000

实现说明:DSH 的 settings wire 只服务硬编码的命名空间白名单,插件命名空间写不进去,因此本插件走自建通道——配置存 <dshHome>/memory.json(schema 校验 + 原子落盘),由插件自注册的 GET/POST /api/memory/config 路由服务,客户端区块 fetch 直连。

首次使用:铸魂

插件安装后,第一次会话里 agent 的首要任务不是干活,而是与你对话定义它的灵魂:名字、性格、价值观、语气、边界,以及你的身份与你们的关系。逐项确认并写回 SOUL.md / user/profile.md,直到 BOOTSTRAP.md 的 status 变为 complete。你可以随时跳过或暂缓。

四个操作

  • remember(记):把值得持久化的内容蒸馏成页面,同步更新 index.md、追加 log.md。维护 index 时守路由表纪律:一行一页、一句话摘要(≤ 80 字)+ salience。
  • recall(忆):会话开始读 boot 块;查询时先查 index.md 再钻页;必要时 dsh-memory search <关键词>。
  • consolidate(整理):dsh-memory lint 查矛盾、孤儿页、该归档的冷页;dsh-memory status 看陈旧页分布。
  • forget(忘):显式遗忘立即执行;自动衰减按 salience + last_access(冷页优先归档)。last_access 由插件自动维护,无需手改。

CLI

dsh-memory init [dir]                 # 创建记忆库脚手架
dsh-memory search <query> [--touch]   # 全文检索(--touch 给命中页戳 last_access)
dsh-memory touch [pages...]           # 手动戳 last_access(不带参数 = 全部页面)
dsh-memory graph --suggest            # 关系报告 + 「这页该连谁」建议(只报告)
dsh-memory query --type decision --tag dsh --hot --stale [关键词]
                                      # 结构化检索:先按 frontmatter 过滤,再匹配正文
dsh-memory checkup [--boot=N]         # 一份报告:体量/新鲜度/连接度/一致性 + 优先级行动清单
dsh-memory lint                       # 完整性体检
dsh-memory status                     # 健康概览(含陈旧页统计)
dsh-memory pack [out.tar.gz]          # 打包导出(含 manifest)
dsh-memory unpack <archive> [--force] # 从归档恢复

存储定位顺序:$MEMORY_DIR → ./.memory(存在时)→ ~/.memory。

迁移

记忆库是纯 markdown + git:拷贝即迁移。跨机器 / 跨 agent / 能力降级档位见 docs/MIGRATION.md。

常见问题

Q:和手写的 .dsh/skills/memory 文件技能(skill 版)什么关系? skill 版是"软保障"(技能目录只注入简介,正文靠模型主动加载);本插件是"硬保障"(boot 块随系统提示词运行时上下文自动注入)。两者可共存:文件技能(rank 100)会覆盖插件内嵌技能(rank 250)的协议。如果你之前为了软保障改过系统提示词 persona(如 profile 补丁里的开机指令),装上本插件后建议移除那段 persona,避免双份注入。

Q:boot 块会不会每次请求都重复注入、烧 token? 不会。运行时上下文按投影去重:内容不变只注入一次;记忆更新后新快照取代旧快照。

Q:记忆库放在哪里最合适? 默认 ~/.memory(全局、跨项目)。需要按项目隔离时,把 memoryDir 配到项目内,或让 agent 在项目里维护 .memory/。

Q:可以加密吗? 记忆含敏感内容时,可把 memoryDir 放进加密卷 / 私有仓库。格式不变,插件无感知。

开发

git clone https://github.com/LittleBlackTong/dsh-plugin-memory.git
cd dsh-plugin-memory
node scripts/memory.mjs --self-test   # 冒烟测试(无需安装依赖)

零构建:lib/ 直接是运行时代码,lib/types/index.d.ts 供 TS 消费方使用。boot.js / scaffold.js 只依赖 node:* 内置模块,可独立复用。

路线图

  • TypeScript 重写(带完整类型与构建步骤)
  • embedding/BM25 检索(规模超过几百页后替代 index 先行)
  • MCP server(让非 DSH 的 agent 也能用同一套记忆库)
  • GitHub Actions CI(跑 --self-test 与 lint)
  • 记忆加密存储选项

欢迎在 Issues 里提需求、报 bug、交 PR。

License

MIT

内容来自项目 README(GitHub)↗

链接

同类插件

查看整个分类 →

社区评论

评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。