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

记忆透镜 Afeng-ovo/dsh-memory-lens

DeepSeek Harness
Afeng-ovo2 天前有提交已提供中文文档扫描:未发现敏感能力

给 DeepSeek Harness 的长期记忆插件:跨会话记住事实、偏好、约定与事件,自带存储、工具与设置页。

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

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

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add Afeng-ovo/dsh-memory-lens
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

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

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

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🟢 扫描:未发现敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描未发现敏感能力:最多只发网络请求、读取自身元数据或只读文件,未发现执行命令、文件写入或敏感信息读取。

发起外部网络请求
查看扫描证据(共 1 处,展示前 1 条)
  • 发起网络请求
    lib/client.js:33
    const response = await fetch(apiUrl(`${API}/${path}`), {
扫描于 2026/11/2(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

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

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

README

由 DeepSeek 最新模型翻译生成
dsh-memory-lens · 记忆透镜 给 DeepSeek Harness 的长期记忆插件。让智能体跨会话记住你的事:稳定事实、偏好、答应过的约定、发生过的事件。重启之后她仍然记得。 自带存储、自带工具、自带设置页,不依赖任何其他插件或服务。 名字叫「透镜」是因为它的重点不在存,而在看清楚存了什么:哪些事她记得牢、哪些快淡忘了、哪些之间有牵连。记忆系统最怕的不是忘,是「你以为她记得,其实她早就不提了」。 它解决什么问题 LLM 的上下文是会话级的。你今天告诉它「我在准备考研,别在这期间给我排旅行」,明天开个新会话,这句话就不存在了。 常见的补法是「把历史对话全塞回去」或者「自动抽取每轮对话存成记忆」。前者会把上下文撑爆,后者会把「嗯」「好的」「我看看」这类废话也存进库 —— 噪音一多,检索质量断崖式下跌,用户最后干脆把这个功能关掉。 这个插件走第三条路: - 记忆由智能体自己判断要不要记。三个工具 remember / recall / forget,代价是它得多想一步,好处是库里每一条都有人为它负责。 - 注入有硬预算。每轮最多 2000 字、12 条(四层共用),超了按层少注入几条,绝不截断单条正文。上下文是稀缺资源,无节制的注入等于慢性自杀。 - 注入不是「按重要度取前 12 条」。
展开完整文档(README 剩余 1 节)
dsh-memory-lens · 记忆透镜

给 DeepSeek Harness 的长期记忆插件。让智能体跨会话记住你的事:稳定事实、偏好、答应过的约定、发生过的事件。重启之后她仍然记得。

自带存储、自带工具、自带设置页,不依赖任何其他插件或服务。

名字叫「透镜」是因为它的重点不在存,而在看清楚存了什么:哪些事她记得牢、哪些快淡忘了、哪些之间有牵连。记忆系统最怕的不是忘,是「你以为她记得,其实她早就不提了」。

它解决什么问题

LLM 的上下文是会话级的。你今天告诉它「我在准备考研,别在这期间给我排旅行」,明天开个新会话,这句话就不存在了。

常见的补法是「把历史对话全塞回去」或者「自动抽取每轮对话存成记忆」。前者会把上下文撑爆,后者会把「嗯」「好的」「我看看」这类废话也存进库 —— 噪音一多,检索质量断崖式下跌,用户最后干脆把这个功能关掉。

这个插件走第三条路:

- 记忆由智能体自己判断要不要记。三个工具 remember / recall / forget,代价是它得多想一步,好处是库里每一条都有人为它负责。
- 注入有硬预算。每轮最多 2000 字、12 条(四层共用),超了按层少注入几条,绝不截断单条正文。上下文是稀缺资源,无节制的注入等于慢性自杀。
- 注入不是「按重要度取前 12 条」。固定层(📌 每轮必在)、近期层(这几天的事)、话题层(按这次对话检索)、兜底层(权重+新近度+轮换)四层分预算 —— 否则排在 13 名以后的记忆永远轮不到,用户以为她记得,其实一次都没进过上下文。
- 注入和检索用同一个打分函数。否则「面板上显示的」和「她真读到的」会变成两套真相,而这恰恰是记忆系统最不能出的问题。
- 会淡忘,但淡忘不是删除。强度低于阈值自动归档,归档条目仍可被检索和复活。
半衰期按类型分开(事实 365 天 / 偏好 180 天 / 约定 30 天 / 事件 14 天),因为一件事和一条约定本来就该以不同速度淡去 —— 全用同一个数字的话,「周五给你讲睡前故事」会像「我在准备考研」一样挂两年。
整理在启动时和每次写入后各跑一次(同一分钟内不重复),一次最多沉 3 条 ——衰减本来就慢,某天突然沉掉几十条,多半是半衰期被调错了,等发现时已经「失忆」。
刻意不挂后台定时器:一个插件悄悄改你的记忆文件、你既看不见也没法阻止,这种事不该发生。

安装

不用手动改 package.json。 dsh plugin add 装完会自动把包名写进 dsh.profile.bundles,重启就生效。

图形界面(最省事)

设置 → 插件 → 添加插件 → 填 dsh-memory-lens → 安装 → 重启 DSH。

也可以填 GitHub 地址(https://github.com/Afeng-ovo/dsh-memory-lens)、tarball 地址,或本机绝对路径。

命令行

装到默认 profile
dsh plugin add dsh-memory-lens

装到指定 profile
dsh plugin --profile desktop add dsh-memory-lens

三种来源都认:

dsh plugin add dsh-memory-lens # npm 包
dsh plugin add github:Afeng-ovo/dsh-memory-lens # GitHub 仓库
dsh plugin add /绝对/路径/到/dsh-memory-lens # 本地目录(开发用)

命令行的前置条件:dsh plugin 把参数转发给 pnpm,所以 pnpm 必须在 PATH 里。
桌面版内置了 pnpm(resources/runtime/primary-runtime/dependencies/pnpm),但从终端直接跑命令行时它不一定在你的 PATH 中 —— 报 'pnpm' 不是内部或外部命令 就是这个原因。
遇到就装个 pnpm(npm i -g pnpm),或者改用上面的图形界面。

装完重启 DSH。设置页左侧会出现「记忆」分区。

卸载

dsh plugin remove dsh-memory-lens

记忆文件留在 $DSH_HOME/memory-lens/memory.json,不会被卸载带走 —— 重装回来还是那些记忆。
不想要了就手动删这个文件。

配置:装好就能用,全部有默认值。要改的话两种方式 ——

图形界面:设置 → 插件 → dsh-memory-lens 那一行的 配置,改完即时生效。

或写进 ~/.dsh/profiles/<profile>/cordis.patch.yml:

- id: memory-lens # 必须等于插件的 settings 命名空间,别改 name: dsh-memory-lens config: storeFile: '' # 空 = $DSH_HOME/memory-lens/memory.json injectEnabled: true injectLimit: 12 # 四层加起来的总上限 injectMaxChars: 2000 # 四层共用的字数预算 injectRecentDays: 3 # 「近期层」往回看几天 injectTopicEnabled: true # 「话题层」:按这次对话检索 injectRotateSlots: 2 # 每轮给「冷门记忆」留几个名额(0=不轮换) decayHalfLifeDays: # 半衰期(天)。也可以直接写一个数字,表示所有类型共用 fact: 365 pref: 180 promise: 30 event: 14 decayArchiveBelow: 0.6 # 强度低于它就归档(0~1) maxEntries: 1000

decayArchiveBelow 默认 0.6,与 core 计算「预计淡忘 N 天后」用的是同一个阈值 —— 面板上显示的那天,就是它真正沉下去的那天。按默认半衰期,一条刚写下、权重 3 的记忆大约在这样的时候归档:事件 33 天、约定 70 天、偏好 418 天、事实 848 天(权重每高一分寿命更长:权重 5 的事实约 1116 天)。约定比事实淡得快得多是故意的:答应了却两个月没动静的事,不该像「我在准备考研」那样挂两年。

注入分四层,而不是「按重要度取前 12 条」

按重要度取前 N 条有一个说不出口的毛病:排在第 13 名以后的那些永远轮不到。它们没被归档、没被删除,只是每次都在队尾 —— 用户以为「她记得」,其实一次都没进过上下文。所以注入改成按层挑,一个 2000 字的预算分给四层:

| 层 | 挑什么 | 为什么需要它 |
| 固定层 📌 | 被钉住的条目,按权重降序,每轮必进 | 人设、称呼、主人的事、明确约定 —— 这些东西「重要度」不一定最高(比如生日),但必须一直在。钉住是用户(或 agent)显式表达的意图,不该被打分函数覆盖 |
| 近期层 | 最近 injectRecentDays 天(默认 3)内新建、更新或被兑现的 | 用户的原话是「不能忘记最近你们之间在干嘛」。只看分数会让三天前那次深夜聊天沉下去,而那正是最该被提起的事 |
| 话题层 | 用这次对话里用户说过的话做 BM25 检索,命中才进 | 这是「去记忆库里找相关内容」那一半。查询词只取用户发言:把 agent 自己的回复算进来,BM25 会把「她上次提过的记忆」判成相关 —— 那是自我强化,不是在想事情 |
| 兜底层 | 剩下的按「权重 + 新近度」排,外加 injectRotateSlots 个轮换名额 | 保底。轮换只从「按分数根本挤不进来」的那批里抽,不会把高分条目顶掉 |

几条刻意的取舍:

- 固定层不设百分比上限。按比例砍它(只给 45%)会造成最难受的那种失败 —— 面板上明明有 📌,注入里却没有,用户完全不知道为什么。真装不下时宁可让别的层少装几条,并把「有几条钉住的没塞进去」计入 pinnedDropped。
- 拿不到会话内容时话题层自动空着,名额让给兜底层。全新会话的第一条消息、或者宿主版本变了拿不到 agent,都不该让整体变差 —— 只该让这一层缺席。
- 第一次运行会自动钉一批(按「权重 + 称呼/人设/约定类关键词 + 是不是承诺」打分,最多 5 条),只在第一次做,用 store.meta.pinnedSeededAt 记着。之后你取消哪条,它就再也不会被系统钉回来。生日、纪念日、周年这类一年只用一次的条目会被排除(NOT_PIN_HINTS):真实库里就踩到过一条「10月18日……也是阿风的生日……小鲸鱼答应记住这天」因为带「答应」二字被钉进了固定层,而用户举的反例恰好就是「我的生日」。
- 面板的「上次注入」显示的是最近一次真实注入,不是重算出来的预览。注入一旦依赖会话内容,HTTP 请求里没有 agent 上下文,重算必然和真注入分叉 —— 那面板就成了漂亮的谎话。

injectRotateSlots 默认 2:库里的条数超过注入上限时,排序靠后的条目本来永远进不了上下文(查询为空时分数只剩「权重 + 新近度」,排序长期稳定)—— 它们没被归档,只是每次都在队尾,等于不存在。留两个名额,每小时换一批,让每一条都有机会进来一次。只挤占排在后半段的,不会把本该在的高分条目顶出去;库比上限还小时名额自动失效。

三个工具

| 工具 | 用途 | 关键点 |
| remember | 记一件事 | 同一件事换个说法时传 supersedes 指向旧条目,旧条目标为「已取代」而不是删掉;用户明确说「这条一直记着」时传 pin: true 钉进固定层 |
| recall | 按关键词查 | 和注入共用打分函数,touch=true 会更新「想起次数」 |
| forget | 忘掉一条 | mode=resolve 结案(仍可被提起)· mode=delete 真删 |

它不会自己偷偷记。 如果你希望 agent 主动记住某事,在对话里说一句「这个记一下」就行。

设置页「记忆」

装好后:设置 → 左侧导航 记忆。一个页面七块:

- KPI 五格:生效中 / 固定层(每轮必在) / 已结案 / 已归档 / 上次注入(超预算时变红并显示超出多少字)
- 记忆列表:按状态筛选、搜正文或标签,每条可 📌 固定 / 取消固定 / 续命 / 结案 / 归档 / 编辑 / 删除。被钉住的条目顶部有「📌 固定层(每轮必进)」或「📌 自动钉住(每轮必进,可取消)」的标记。每条生效中的记忆都会标出还剩多少天淡忘(如「约 24 天后淡忘」,≤7 天标红、8~30 天标橙、更久是中性灰)
- 快过期:只列 30 天内会淡忘的生效记忆,按最急的排前面,每条都能直接续命 / 编辑。这一屏故意不接搜索框和状态筛选 —— 它的意义是一份固定口径的清单,一筛就不再是那个清单了
- 她会想起什么:直接显示最近一次真实注入的原文 —— 不是「大概会想起这些」,是真的那一整段。上方一排小标签告诉你这次每层各进了几条(固定 N / 近期 N / 话题 N / 兜底 N),以及这次用的话题查询词。还没发生过注入时显示「—」而不是编一个预览:注入依赖会话内容,HTTP 请求里没有 agent 上下文,重算出来必然和真注入不一样
- 记忆图:三维星图。每条记忆是一个光点,共享标签的记忆之间连边。红色=快淡忘了,绿色=记得很牢,灰色=标签节点。点一下看详情和关联,拖空白转视角,滚轮缩放(可以推到很近,约 8 倍),「暂停旋转」停住它,方便点中某个点。
- 体检:点一下,用内核算法把整个库过一遍 —— id 重复、同一句话存了两份、说被某条取代而那一条不存在、时间戳写成秒、衰减整理从来没跑过、到期没提醒的约定……每条问题都带「怎么办」,而且只报能确定的,不猜、不评分
- 对账:填另一份记忆文件的路径,看两份库差在哪 —— 一致多少条、只在这边多少条、只在那边多少条、两边都有但字段不同。按「正文 + 类型」配对而不是按 id(两个系统各自生成 id,按 id 比会把同一条全报成「只在一侧」),而且只读,绝不改那份文件

星图是 canvas 2D 手写透视投影,零外部依赖 —— 不引 three.js 是刻意的:多一个依赖就多一个「打不开」的理由。

剩余天数怎么算的:面板上那句「约 24 天后淡忘」不是前端自己推的,是内核 longMemoryFalloutDays() 算好的成品值(snapshot 里叫 falloutInDays),前端一个字都不重算。它按「权重 × 半衰期」衰减,跌到 decayArchiveBelow(默认 0.6)就算淡出 —— 和真正归档用的是同一个函数、同一个阈值,所以面板上显示的那一天,就是它真正沉下去的那一天。前端要是自己再算一遍,只要时间戳差几毫秒、或者以后有人调了阈值,两边就会分叉,用户看到的是「它明明说还有 5 天,怎么就没了」。

为什么单列一个「快过期」:因为「还剩几天」写在一张卡片里,只在你想起来翻它的时候才有用。而记忆淡出是一件有时间窗的事 —— 30 天内会淡忘的条目,现在按续命还来得及,过了就沉底了(归档不是删除,但也不会再自然出现)。所以这一屏按「最急的排前面」把该管的聚在一起。

这个视图大部分时间会是空的,那是对的:半衰期调过之后(事件 14 天 / 约定 30 天 / 偏好 180 天 / 事实 365 天,跌破 0.6 才淡出),真实库里 30 天内会淡忘的通常只有 0~1 条。所以空态不写「暂无数据」,而是把最近的一条还差多少天讲出来 —— 有具体数字,人才知道这个视图是活的,只是暂时没什么可提醒的。也不要为了显得有内容就把阈值放宽到 60 天:那等于把「体检正常」改成「人人都有病」,用户反而学不会什么时候该按续命。

钉住 ≠ 不会淡忘(这一条实测纠正过一个错误文案):pinLongMemory() 只写 pinned / pinnedAt / pinnedAuto 三个字段,完全没碰 weight 和 lastUsedAt,所以钉住前后 falloutInDays 一模一样;一条钉住但已衰减的条目照样会被 organizeLongMemories() 归成 archived。钉住真正买到的是「每轮必进注入」(固定层不看分数、不参与轮换),不是免死金牌。所以钉住的条目照样显示剩余天数(藏掉倒计时等于骗人),徽章上也只写「每轮必进」而不写「不会淡忘」。想让它留久一点,该按的是续命(weight +0.5 且 lastUsedAt 归零,实测一条已到淡忘线的记忆续命后回到 35.6 天)。

放大倍率:原来滚轮只能放大约 2.2 倍(dist 下限 0.45),滚到底还是看不清一条记忆周围有什么。现在 dist 下限放宽到 0.12(约 8 倍),上限从 2.6 放到 3.2(「拉远看全貌」也够用)。同时把透视投影里的 zc 地板从 140 降到 60 —— 这个地板卡的是离相机最近的那批点:相机推近后它们的焦距会被 140 托住,k 到 140 就封顶,所以「凑近看清单个光点」怎么也做不到。按 draw() 同一套公式算(332 个点、球壳分布),最近处的放大系数从 1.78 提到 10.67,中位倍率从 2.2 倍到 7.1 倍。

推得这么近会带来一个新问题:相机会进到云团内部,有些点跑到相机背后(zcRaw 变成负数),k = fov / zc 一爆,一个点就能以极大半径糊满整屏,连到它的边还会拉成横穿画面的长条 —— 不是变模糊,是直接糊掉。同一套公式下 332 个点里有 50 个(15%)会跑到背后,而且只有把 dist 放宽到 0.12 之后才会出现(旧下限下是 0 个)。所以 project() 会返回一个 behind 标记,绘制时节点和边都按它裁掉(边的两个端点都要判)。这些点本来就不该出现在这张投影里,与其画成糊块,不如不画。细节见 docs/DESIGN.md 第七节。

画面停下来就不画了:力学收敛、没在旋转、没选中节点时,渲染循环会自己停帧;自动旋转时降到 30fps;只有拖拽、转视角、相机推进、选中涟漪、尺寸变化这些「画面真的在变」的时刻才续帧。这一条是修出来的缺陷 —— 早期版本无条件每帧续帧,力学早就收敛了还 60 次/秒重画几百个点,真机实测静置吃掉 12%~36% 主线程,332 条记忆时甚至到 42%,用户感知就是「装上这个插件之后很卡」。修完静置降到 0.0%,旋转时 8.1%。

记忆图

「她会想起什么」里那排分层小标签(固定 / 近期 / 话题 / 兜底)长这样:

分层注入

数据

默认存在 $DSH_HOME/memory-lens/memory.json。$DSH_HOME 跟着 DSH 自己的数据根目录走,换机器、换盘都不会丢。

{
"version": 1,
"buckets": {
"shared": [
{
"id": "lm-mvjb07iln17s",
"kind": "fact", // fact | pref | promise | event
"text": "阿风在准备考研",
"tags": ["学业"],
"weight": 4, // 1~5
"status": "active", // active | resolved | archived | superseded
"dueAt": { "when": "2026-12-20", "ts": 1797000000000 },
"createdAt": 1791000000000,
"hits": 3, // 被想起过几次 "lastUsedAt": 1792000000000,json "pinned": true, // 钉进固定层:每轮必进注入 "pinnedAt": 1792000000000, "pinnedAuto": false // true = 首次运行按规则自动钉的(可取消) } ] },
"meta": {
"pinnedSeededAt": 1792000000000, // 首次自动钉只做一次,靠这个记着 "pinnedSeededCount": 3 } }

纯 JSON,可读可改可同步。 想放进坚果云/OneDrive 做多机同步,把 storeFile 指过去就行。文件被外部改动时插件会自动重新加载,不会拿着内存里的旧副本装作没事。

面板里能做的事

- 记一条 / 编辑 / 续命 / 结案 / 撤销结案 / 归档 / 复活 / 删除(删除有二次确认)
- 📌 固定 / 取消固定 —— 钉住的条目每轮必进注入,不参与打分竞争
- 按状态筛选、按正文或标签搜索
- 想看原始数据就直接开 $DSH_HOME/memory-lens/memory.json —— 想备份、想同步、想手改,就那一个文件

从别的记忆系统迁移(没有按钮,走接口): bash

三种形状都收:{ "source": {...} }(脚本用法)、{ "items": [...] }、或把库直接当 body 传

curl -X POST http://127.0.0.1:<端口>/dsh-memory-lens/api/import \
-H 'content-type: application/json' \
--data-binary @old-memory.json

- 条目列表可以放在 items 里、也可以放在 buckets.shared 里、或者就是一个裸数组。
- 每条至少要有非空的 text;其余字段缺了会自动补 —— status 补 active, createdAt/lastUsedAt/updatedAt 补成导入那一刻,weight 补 3,hits 补 0。
补 createdAt 不是为了好看:衰减要靠它算「这件事多久没被提起了」,缺了就只能当成刚写下的,一条三年前的旧事会一直不淡出。
- 按「正文 + 类型」去重,重复的跳过;返回 { ok, added, skipped, total }。

检查一致性 / 和另一份库对账(面板「体检」标签页里也能点,这里是对应的接口): bash

体检:只读。返回 { checked, clean, counts:{high,medium,low}, findings:[{code,severity,message,fix}] }

curl http://127.0.0.1:<端口>/dsh-memory-lens/api/audit

对账:只读那份文件。返回 { identical, onlyLeft, onlyRight, differing, counts }

curl -X POST http://127.0.0.1:<端口>/dsh-memory-lens/api/diff \
-H 'content-type: application/json' \
-d '{"file":"D:\\\\qq-bridge\\\\state\\\\long-memory.json"}'

两条设计取舍值得先说清楚:

- 对账按「正文 + 类型」配对,不按 id。 两个记忆系统各自生成 id(lm-xxx),按 id 比会把「同一条记忆」全报成「只在一侧」—— 报错了地方比不报更糟。
- 对账只读另一份文件,绝不写它。 那份数据属于另一个程序,插件没有资格替它做决定,哪怕发现它「不对」。自动修会在用户不知情时改动别人的数据,比不一致本身更糟。

开发 bash
node test/tools.test.mjs # 工具层:62 项
node test/host.test.mjs # 宿主半装配:13 组
node test/client.test.mjs # 客户端半装配:5 组(含渲染循环静态断言)
node test/audit.test.mjs # 体检与对账:26 项
node examples/self-host/self-test.mjs # 内核自检:41 项

三个测试都是跑真实代码(最小桩,不是复制一份逻辑),因为这类插件的失败模式大多是「装上了,但什么都没发生」—— 没有异常、没有日志,只是那个页面不出现。

关于 output.render:@deepseek-ai/dsh-tools 的 defineTool 里有一句 const userRender = options.output.render;,output 少了 render 它不报错,而是把 undefined 带进闭包,等你真去渲染时抛 tool "remember" returned invalid output: output.render failed: userRender is not a function。
0.1.0 就是这样发出去的:三个工具都只写了 schema。更麻烦的是 execute 已经执行完、记忆已经落盘,用户收到的却是错误 —— 他会以为没记住,再说一遍,库里就多一条重复。
所以 test/tools.test.mjs 现在会把每个工具真的 render 一遍,检查返回的是内容块数组。

另一条同类的坑:工具返回值必须是 lossless JSON。{ dueAt: undefined } 会被 JSON.stringify 丢掉那个键,宿主据此判定「值不是无损 JSON」,整次调用报 value is not lossless JSON。没有期限的记忆占绝大多数,所以这条几乎必踩。
修法是条件展开(dropUndefined),不是写个 undefined 占位。

结构

lib/
index.js 宿主半:systemPrompt 注入 + 衰减整理 + 4 条 HTTP 路由 + Config tools.js 三个工具 store.js 存储(原子写 + mtime 感知外部改动) core/long-memory.js 纯逻辑:打分 / 衰减 / 检索 / 去重(零 IO 依赖,可单测) client.js 客户端半:设置页分区(含三维星图) cordis.patch.yml loader entry

core/ 刻意不碰 ctx、不碰 fs,只有纯函数。这样打分和衰减逻辑可以脱离 DSH 单独验证 —— 这两块一旦算错,整个插件的行为就全错了,而它们最容易在「看起来正常」的表象下悄悄算错。

想自己搭机器人?

examples/self-host/ 是同一份内核的另一种接法:命令行(cli.mjs)、一周演示(demo.mjs)、提炼提示词、日记检索、定时备份,以及接进你自己 bot 的六个接入点。

它不是第二份实现 —— 导入的就是 lib/core/long-memory.js,和插件跑的是同一套打分与衰减代码。想先看看这东西怎么工作、又不想装 DSH,从那里开始最快:cd examples/self-host && node demo.mjs。

已知边界

- 不做自动抽取。这是设计选择,不是还没做。
- 单桶(shared)。core 支持多桶,但一个智能体一个记忆库就够了,多桶只会让「她到底记得什么」变模糊。
- 不加密。文件是明文 JSON。密钥、证件号这类东西在写入前会被 looksSensitive() 拦掉,但拦的是模式匹配,不是保险箱。真有敏感信息需求,请用 storeFile 指向一个你自己加密的盘。
- 中文优化。分词按字 + 二元组做,英文检索能用但不如中文准。

License

MIT
预览分享卡片长什么样 ↓Afeng-ovo/dsh-memory-lens
分享 · DeepSeek Harness Hub
Afeng-ovo/dsh-memory-lens

给 DeepSeek Harness 的长期记忆插件:跨会话记住事实、偏好、约定与事件,自带存储、工具与设置页。

回到顶部