DSH Plugins Marketplace

DSH Plugins

Plugins

/

dsh-user-memory

d

dsh-user-memory

Manifest valid★ 1

User-authored tiered long-term memory for DeepSeek Harness: injected as a system-prompt section, with a Chinese panel to view and edit it. The AI never writes to memory on its own.

UI (client)hasBundlePatch

dsh-user-memory

自研的 DeepSeek Harness 插件:把你本人编写的分级长期记忆注入到我的系统提示词里,并提供一个中文图形面板让你直接看和改。

  • 不是 skill,是宿主插件(Host plugin)——只要 profile 加载它,每轮都在。
  • 运行时只读(除非你操作)。它永远不自己写你的记忆:只有两种写入——你在面板里点保存,或你让 AI 用文件工具改。没有任何自动采集、自动总结、后台回写、网络访问。
  • AI 只能"提议",不能"决定":我可以把一条修改建议放进「待确认」区(memory_suggest 工具),但只有你在面板里点「采纳」,它才会变成一条通知、让我下一轮去写。见下面「待确认区」。
  • 采纳前后都能反悔:卡片上有一个「补充说明」输入框,采纳时它和我的原文一起交给我(冲突时以你的补充为准);采纳后、我下一轮开跑之前,卡片上会多一个「撤销采纳」,点它就回到待确认,补充还能改。
  • 零依赖,只用 Node 内置模块。整个目录拷到哪都能跑。

图形面板(推荐用这个)

重启 DeepSeek Harness 之后,左侧栏会出现一个「长期记忆」入口(和「插件」「上下文洞察」「技能中心」同一列),点开就是面板:

  • 五个分页:必须遵守 / 行为设定 / 用户画像 / 可选遵守 / 变更日志——全部中文,不用看文件名。
  • 直接改,点「保存这一节」。保存后立即生效,不需要重启。
  • 保存后底部会告诉你结果,例如「已生效:这一节会注入(约 320 字)」或「目前这一节是空的(只剩标题或注释),不会注入」——你一眼就知道这次改动到底有没有用。
  • 有未保存改动的分页会显示一个小黄点;「放弃改动」可一次性还原。
  • 面板只认这五个文件名,其它路径一律拒绝;单个文件上限 256 KB。
  • 顶部还有一个「自动采集记忆:已开启 / 已关闭」开关,点一下立即生效、不用重启(详见下面 autoCapture 一节):打开后我会在对话里主动提议值得长期记住的偏好。它只影响我提不提议,提议照旧要你点「采纳」才算数。

待确认区(面板里的「待确认」分页)

这是给我和你之间的一个闸门:

  1. 我用 memory_suggest 提议一条:目标文件 + 完整措辞 + 理由。这个工具不写任何记忆文件,只在 <DSH_HOME>\memory-suggest.json 里放一条 pending。
  2. 你打开面板 →「待确认」→ 看到卡片。卡片下方有一个**「补充说明」输入框**(可选):想额外加的要求、想改的措辞,写在这里。
    • 打字不落盘:输入框里的字只在你点「采纳」的那一刻才随这条存下来(存进 userNote),所以犹豫期间磁盘上没有任何痕迹;5 秒一次的自动刷新也不会擦掉你正在打的草稿。
    • 采纳后这一条会带着你的补充一起交给我,和我的原文冲突时以你的补充为准。补充上限 1000 字符,会自动去掉首尾空白。
  3. 点「采纳」或「忽略」。只有采纳之后,你下一次发消息时我才会收到一条 <MEMORY_SUGGESTION_ACCEPTED> 通知(带着你采纳的原文和你的补充);我到那时才总结措辞、写进记忆文件,并调用 memory_suggest_done 记账。
  4. 反悔窗口:采纳之后、我下一轮还没开始之前,卡片状态是「已采纳 · 可撤销,等我下一轮写入」,并多出一个「撤销采纳」按钮。点它这条回到 pending,你的补充原样留着,可以改完重新采纳。
    • 一旦通知已经交到我手里(状态变成「通知已送达 · 处理中」),撤销按钮就没了,并说明原因——此时改主意直接在对话里跟我说。
    • 如果你点撤销的那一刻我刚好开跑,服务端会如实拒绝并告诉你没能撤销(不会出现"看着撤了其实没撤")。
  5. 两种例外我会先停下把 diff 摆给你看:我的措辞与你采纳的原文有实质差异;或这条与文件里已有内容重复、冲突。

页签上有数字气泡显示待确认条数;待确认分页每 5 秒自动刷新(可以一边聊天一边看)。沉默 = 放弃:提议完我就回去干手里的活,不催你。

工具干什么会不会写记忆
memory_suggest提议一条(文件限那五个,正文上限 4000 字符,单条可带一句理由)不会
memory_suggest_done处理完一条后记账:写进了哪个文件,或为什么不写不会

记忆库在哪(也可以直接改文件)

C:\Users\Adler\.dsh\memory\
├── rules-must.md      必须遵守(硬性)   ← 注入
├── agent.md           我的行为设定        ← 注入
├── profile.md         用户画像            ← 注入
├── rules-should.md    可选遵守(酌情)    ← 注入
└── log.md             变更日志            ← 不注入,只给你自己记账

直接记事本改也行,效果与面板完全一样——文件是唯一真相。

注入规则(决定它花多少 token)

行为说明
注入位置系统提示词的一个段落,order = -500:Harness 身份之后、人格设定之前
顺序必须遵守 → 行为设定 → 用户画像 → 可选遵守
<!-- --> 注释永远不注入。所以模板里的说明文字不占上下文、不干扰我
只有标题没有正文整节不注入。空文件 = 零开销
文件都没内容整块不注入,一个字符都不占
token 成本记忆不变时系统提示词逐字节不变 → 吃满 prompt cache;改了才会有一次新快照
预算默认 6000 字符,超了按节截断并注明「已截断」
autoCapture默认关。关着时一个字都不多注入;打开后末尾多一段固定提醒,且不算进 6000 预算。面板顶部的开关就是它,且优先级高于这里

让 AI 帮你写(两条流程)

  1. 「记住…」 → AI 把内容整理成条目,先把 diff 摆给你看,你确认后才落盘。
  2. 「整理记忆」 → AI 把这段时间的沉淀批量整理出来,同样先给你审。

配置

在 profile 的 bundle patch 里(cordis.patch.yml):

- insert:
    - id: user-memory
      name: 'dsh-user-memory'
      config:
        enabled: true     # false = 停用注入,不卸载
        dir: ''           # 空 = <DSH_HOME>/memory
        maxChars: 6000    # 注入总预算(含前言与截断提示)
        autoCapture: false  # true = 打开「记忆采集提醒」,见下节

改完需要重启才对 composition 生效;只改记忆文件内容则不需要。

实测注意:profile 里的 config: 是「整块替换」,不是逐键合并。 在 C:\Users\Adler\.dsh\profiles\<profile>\cordis.patch.yml 里写 - id: user-memory + config: 时,你列出的键会顶掉 bundle 里的整套 config。比如只写 autoCapture: true,dsh <profile> --dump-config 读回来就是 config: { autoCapture: true } —— dir 和 maxChars 都不见了(退回内置默认值)。 所以覆盖时把你关心的键一次写全:enabled / dir / maxChars / autoCapture。

autoCapture:要不要让我主动提建议(默认关)

默认 false。关着的时候,注入的提示词与没有这个开关时逐字节相同——一个字符都不多。

开关在面板里,点了立刻生效

面板顶部有一个「自动采集记忆:已开启 / 已关闭」按钮(不用切分页,打开面板就在),点一下就切换。不需要重启 App,也不需要改任何配置文件——它写的是 <DSH_HOME>\.autocapture 这一个文件(内容就是一个 on 或 off),宿主每轮读到它就把注入段落同步过去,你在下一次发消息时就能看到效果。

状态从哪来,按钮下面的小字会告诉你:

小字意思
「当前状态来自插件配置」还没人点过面板,状态由 config.autoCapture 决定
(另一句:讲开关作用)面板点过了,面板优先于配置,而且跨重启保留

优先级顺序:.autocapture 文件 > config.autoCapture > 内置默认 false。文件里写了 on/off 以外的内容(例如你手改坏了),等于「没有意见」,自动退回看配置——不会因为一个字写错就静默关掉整个功能。

这个开关只动注入文本:它不写你的记忆文件、不碰待确认区、不改任何记忆内容。「AI 从不自行写入」照旧。

也可以走配置文件(老办法)

把 config: 里的 autoCapture 写成 true,然后重启 App。适合一开始就想默认打开、或者要写进 dotfiles 的场景。

注意:一旦你在面板上点过开关,.autocapture 文件就存在了,之后它会一直盖住配置文件——想交还给配置,删掉 <DSH_HOME>\.autocapture 即可。

打开后,注入段落的末尾会多出一小节固定文本:

## 记忆采集提醒(开关已打开)

- 本次对话中若发现值得长期记住的用户偏好或规则(明说的要求、反复出现的偏好、明确的禁止项),调用 `memory_suggest` 把它放进待确认区。
- 不要直接改记忆文件:用户点了「采纳」才会真正写入。
- 只提你确实观察到的,不要为了凑数而提。

它做的是「提醒模型自己去调 memory_suggest」——它只影响我会不会主动提议,不改变闸门本身:提议仍然只是往待确认区放一条 pending,仍然要你在面板里点「采纳」才会真正写进记忆文件。「AI 从不自行写入」这条不变量没有任何松动。

几个要点:

零开销关着时拼接的是空字符串,提示词与不开这个开关时逐字节相同
不占预算这段提醒不算进 maxChars(6000 是给你那四层正文的),所以打开它不会挤掉你的记忆内容
文本是常量内容写死在插件里,绝不随对话变化——否则系统提示词每轮都变,prompt cache 每轮失效,比多这几十个字贵得多
不保证每次生效这只是「提醒模型去调工具」,不保证它每轮都调。真觉得它总忘,再考虑叠加别的兜底(当前没有)
非强制同上:它不会让我「必须」提议,也不会让我「越权」直接写文件

安装位置

D:\AI\dsh-plugins\dsh-user-memory\      ← 插件本体(本目录)
C:\Users\Adler\.dsh\profiles\desktop\node_modules\dsh-user-memory   ← Junction 指向上面的目录

自测

node test/selftest.mjs        # 52 项:注入与服务端(含面板开关)
node test/suggest-test.mjs    # 48 项:待确认闸门 + 补充框 + 撤销
node test/panel-test.mjs      # 32 项:面板 UI(需要 react + jsdom,见下)

selftest.mjs:注入位置与顺序、注释剔除、空节不注入、预算截断、停用行为、autoCapture(关闭时逐字节等于「没有这个字段」,打开时只在末尾追加常量提醒且不占 6000 预算;面板开关能即时开关注入、跨重启保留、优先于配置、手改坏的状态文件退回看配置、拒绝非布尔值,且不点开关时绝不自己创建状态文件),以及面板接口(读取、保存、拒绝非法文件名、拒绝非文本、畸形请求体不崩、注入反馈文案)。

suggest-test.mjs 盯的是闸门最要紧的那条不变量:提议绝不碰记忆文件;只有 resolveSuggestion 能改状态;非法决定与未知编号被拒;内部步骤(压缩、起标题等)吃不掉一条采纳,只有带真实用户消息的那一步才送达;注入的是一条 ours 的、冻结的 user 消息;重启后待确认区与编号延续;补充(采纳时写入、去除首尾空白、1000 字截断、空补充不写、编辑后以最新一条为准、随通知原文一起交给我)与撤销(仅 accepted 可撤销 → 回到 pending、保留补充、丢掉 decidedAt;pending 与 delivered 一律 409;未知编号 404)。

panel-test.mjs 在 jsdom + React 里渲染真实的面板组件,像浏览器一样点分页、打字、点「采纳」「撤销采纳」、点顶部的采集开关、手动触发 5 秒轮询,断言:待确认卡片有补充框且初始为空、打字不发任何请求、采纳把补充一起发出去、采纳后变成只读展示 + 出现撤销按钮、撤销后回到输入框并保留草稿、轮询不擦草稿、已送达的卡片没有再撤销的入口;采集开关不切分页就能看到、显示的是宿主报来的真实状态、点击发 on:true/false、按钮与说明文字随之翻转、配置来源与面板来源的文案不同。

它需要的 react / react-dom / jsdom 故意不是本插件的依赖(本插件零依赖、无构建步骤)。想让面板测试真的跑起来,装到一个临时目录再指给它(没设 UMEM_UI_DEPS 时它会打印 SKIP 并以 0 退出,CI 因此保持无依赖):

npm install --prefix P:\_temp\umem-deps react@19 react-dom@19 jsdom@26
$env:UMEM_UI_DEPS = 'P:\_temp\umem-deps\node_modules'
node test/panel-test.mjs

改完代码先跑这三个(改了 index.js 或 client.js 还要重启 App 才生效)。

完全卸载 / 回退

plugin_manager  remove_bundle  dsh-user-memory

插件消失,记忆库 C:\Users\Adler\.dsh\memory\ 不会被删(数据与代码分离)。想彻底清干净就再手删那个目录。

已知边界(必须说清)

  1. 「必须遵守」不是技术强制。插件只能把规则放进系统提示词的强位置,不能让模型永不失误。写得具体、可判定、够短,比写得多更有效。
  2. 改代码需要重启。加载器用裸 import(),Node 的 ESM 模块缓存按路径记账且不失效——改 index.js 或 client.js 后必须重启 App。改记忆文件不需要。
  3. 两个 profile 互相独立。desktop(桌面版)与 web(网页版)各有自己的 node_modules;这个插件目前只装在 desktop。
  4. 不改用 locale 服务。界面文案是硬编码中文。这是有意取舍:单语言自用插件,省掉一层 locale 注册。

Comments

Loading…