像人一样遗忘,也像人一样回忆。
32.9/100
综合分
4
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/11/3 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add Toctsu/dsh-full-life-memory该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · chat
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 1 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
读写/删除本地文件发起外部网络请求
查看扫描证据(共 3 处,展示前 3 条)
- 写入本地文件index.js:285
mkdirSync(dirname(dbPath), { recursive: true }); - 写入本地文件index.ts:433
mkdirSync(dirname(dbPath), { recursive: true }) - 发起网络请求client.js:515
const response = await fetch(REGISTRY_URL, { cache: 'no-store', headers: { accept: 'application/json' } })
扫描于 2026/11/3(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/cosmokit@deepseek-ai/schemastery@deepseek-ai/cordis@deepseek-ai/dsh-agent@deepseek-ai/dsh-tools给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
给 DeepSeek Harness 做仿生记忆的插件,把上下文折叠成 token 线索再重建记忆,对话再长也不会溢出,让所有模型拥有完整的一生。
README
由 DeepSeek 最新模型翻译生成Full-Life Memory
不存原文,只存线索。
像人一样遗忘,也像人一样回忆。
@tuhengyu/dsh-full-life-memory
一个给 DeepSeek Harness 用的「仿生终身记忆」插件:上下文快满时,它把旧对话蒸馏成几个关键词,从历史里物理删除;当你再提起那件事,它顺着线索把记忆重新长出来。
上下文不是硬盘。
人不会记住三个月前的每一句话,只留下几个名字、半句原话、一种「我们好像聊过」的感觉。
需要的时候,再顺着这些线索,把当时的场景重新拼出来。
✨ 三个核心卖点
| 🧠 仿生记忆不存原文,只存 Token 线索 | 落盘的只有 keywords、seed_tokens、一句摘要——没有 message 正文,没有向量,没有 embedding。回忆时用线索组装一段"想起"的提示交给模型,而不是把数据摊开给它看。 |
| ⚡ 零 Token 成本正则蒸馏,不调模型 | 遗忘不经过任何 LLM:引号里的原话、文件路径、标识符、中文短语,用规则抽出来就完事。一次折叠 0 次模型调用、0 token 计费、0 延迟。 |
| ✂️ 真正释放上下文物理切除,不是"请忽略" | 走 DSH 的 surface 节点替换,被切掉的 6 个节点直接从下一轮请求里消失。
展开完整文档(README 剩余 1 节)
Full-Life Memory
不存原文,只存线索。
像人一样遗忘,也像人一样回忆。
@tuhengyu/dsh-full-life-memory
一个给 DeepSeek Harness 用的「仿生终身记忆」插件:上下文快满时,它把旧对话蒸馏成几个关键词,从历史里物理删除;当你再提起那件事,它顺着线索把记忆重新长出来。
上下文不是硬盘。
人不会记住三个月前的每一句话,只留下几个名字、半句原话、一种「我们好像聊过」的感觉。
需要的时候,再顺着这些线索,把当时的场景重新拼出来。
✨ 三个核心卖点
| 🧠 仿生记忆不存原文,只存 Token 线索 | 落盘的只有 keywords、seed_tokens、一句摘要——没有 message 正文,没有向量,没有 embedding。回忆时用线索组装一段"想起"的提示交给模型,而不是把数据摊开给它看。 |
| ⚡ 零 Token 成本正则蒸馏,不调模型 | 遗忘不经过任何 LLM:引号里的原话、文件路径、标识符、中文短语,用规则抽出来就完事。一次折叠 0 次模型调用、0 token 计费、0 延迟。 |
| ✂️ 真正释放上下文物理切除,不是"请忽略" | 走 DSH 的 surface 节点替换,被切掉的 6 个节点直接从下一轮请求里消失。不是 system prompt 里那句"请忽略更早的内容",是真的不发了——KV cache / 显存跟着一起降。 |
🔁 它是怎么运转的
agent/pre-step
│
▼
tokenMeter.measure(session)
│
< 8000 ───┴─── ≥ 8000
│ │
keep going ▼
keep the last 4 turns, fold the oldest 2
│
▼
regex distill → keywords + seed_tokens (0 LLM calls)
│
┌─────────┴─────────┐
▼ ▼
SQLite memory_clues surface replace
(only file on disk) 6 nodes → 1 checkpoint
│ │
│ ▼
│ 【🧠 记忆已折叠】…
▼ │
recall_from_clues ◄───────┘
rebuilds the scene
🆚 和别的做法比
| 方案 | 每轮额外成本 | 上下文占用 | 旧事怎么找回来 | 真的移除了吗 |
| 全量保留 | 0 | 线性增长,迟早爆窗 | 原文还在 | ❌ |
| LLM 摘要压缩 | 每次压缩 1 次模型调用 | 压缩后仍占位 | 读那段摘要 | ✅(摘要节点) |
| 向量 RAG | 嵌入调用 + 每次检索 | 检索片段临时塞入 | 召回原文分块 | ❌ 原文还在库里 |
| Full-Life Memory | 0 | 只留一行提示 | 线索 → 模型自己重建 | ✅(surface 替换) |
一句话区别:向量库是「查」,线索是「想」。 检索把原文递给你,回忆让模型自己长出来——后者更像人,也更省。
🚀 安装
plugin_manager { action: "install_bundle", target: "<本目录的绝对路径>" }
装完重启一次 DSH。之后每个新会话都会带上:
- recall_from_clues 工具(Phase 1)
- 自动遗忘钩子(Phase 2,默认开启)
兼容性:@deepseek-ai/dsh-agent / dsh-tools 声明为 >=0.2.1-rc.2 <0.3.0。
DSH 会把这两个 peer 依赖当作兼容版本要求;钉死小版本会在 DSH 升级后报「不兼容」,所以这里用区间,0.2.x 的补丁与 RC 升级都能直接通过。
本插件是 link 安装:改完代码不用重装,但要重启 Host 才会重新加载模块。
⚙️ 配置
- insert:
- id: full-life-memory
name: '@tuhengyu/dsh-full-life-memory'
config:
dbPath: '~/.dsh/full-life-memory/memory.db' # 线索库 limit: 5 # 一次回忆取回几条线索 maxPromptChars: 1800 # 回忆提示的字符上限
autoForget: true # Phase 2 总开关 forgetThresholdTokens: 8000 # 超过多少 token 开始遗忘 forgetTurns: 2 # 每次切掉几轮已完成对话 keepCompletedTurns: 4 # 最近这么多轮永远保留
sceneOverrides: {} # 按场景覆盖,见下一节
| 字段 | 默认 | 说明 |
| dbPath | ~/.dsh/full-life-memory/memory.db | 目录自动创建,WAL 模式 |
| limit | 5 | 单次回忆最多带几条线索(上限 20) |
| maxPromptChars | 1800 | 超出截断并标注「更早的线索已淡忘」 |
| autoForget | true | 关掉后只剩手动回忆,历史不再被切 |
| forgetThresholdTokens | 8000 | 用 ctx.tokenMeter 的口径;没有该服务时按字符粗估 |
| forgetTurns | 2 | 一次折叠几轮 |
| keepCompletedTurns | 4 | 保护最近对话:已完成轮数 ≥ forgetTurns + keepCompletedTurns 才动手 |
| sceneOverrides | {} | 按场景覆盖,键是会话 header.agentPreset |
🌐 多场景覆盖
桌面端、QQ 群、QQ 私聊、微信……同一个模型在不同入口的「记忆压力」完全不同,所以除了全局默认,还可以按场景覆盖:
autoForget: true
forgetThresholdTokens: 8000 # 全局默认(桌面端) sceneOverrides: qqbot: enabled: true forgetThresholdTokens: 4000 # 群聊碎片多,早点折叠 keepCompletedTurns: 1 # 只保最近 1 轮 wechat: enabled: false # 微信交给别的插件,本插件直接让路
场景标识 = 会话的 header.agentPreset(小写)。DSH 的 SessionHeader 没有单独的「渠道」字段,QQ/微信这类接入插件通常用自己的 agent 预设建会话,所以拿它当场景键最稳;没有预设时退回 header.origin,再退回 default。桌面端的默认预设通常是 standard,想给桌面单独一套规则就写 standard:。
生效规则:
- 场景里没写的字段继承全局
- enabled: false 直接跳过折叠(连日志都不写)
- 场景可以反向打开全局关闭的折叠——全局 autoForget: false 只影响没有场景覆盖的会话
设置页的「场景」标签页可以增删改这些覆盖,不用手写 YAML。
👥 群聊记忆隔离(Phase 3)
DSH 把一个 QQ 群当成一个 Session,默认是「全群共用一份记忆」——群友 A 说的话,B 一问就可能被召回,这就是串台。
打开 isolateByUser 后:
| 阶段 | 行为 |
| 识别 | 从最新一条用户消息的正文前缀抽发送者:[User: 12345]、[用户: 12345]、小明(123456):、qq: 10001;正则可用 senderPatterns 覆盖 |
| 折叠 | 按发送者分组:自己的对话存成私有线索(user_id = 10001),别人的存成全群共享(user_id IS NULL);othersAsGlobal: false 则直接丢弃别人的对话 |
| 召回 | 默认 scope: auto = 自己的 + 全群共享;scope: user 只看自己;scope: global 只看共享 |
| 降级 | 认不出发送者 → 自动回到全局模式,不报错、不丢数据 |
isolateByUser: false # 全局默认关 othersAsGlobal: true # 别人的对话进共享记忆 sceneOverrides: qqbot: isolateByUser: true # 群聊场景打开
旧库不用手动处理:启动时检测 memory_clues 缺 user_id 就自动 ALTER TABLE 补上,历史线索保留为共享记忆(user_id 为 NULL),新折叠的才按人归档。
🔌 NapCat / AstrBot 这类桥接的兼容
桥接插件不会给 DSH 传结构化发送者,只能从正文里读。为了少踩坑,做了四件事:
| 场景 | 处理 |
| 昵称: 内容(AstrBot 常见) | 新增昵称前缀正则;注意:、12:30 这类不会误判 |
| 一条 prompt 合并多个发言人 | 逐行识别,按行归属,不再整条算给第一个人 |
| [CQ:at,qq=…]、[User: 123] | 蒸馏前剥掉,不会变成线索词 |
| 格式对不上、识别失败 | 退化成全群共享,不报错;日志里 owners: global 能看出来 |
排查格式时打开 debugSender: true,折叠时会打印:
full-life-memory: sender probe preset="qqbot" sender="小明" groups=2 sample="我的猫叫豆豆"
还有两个边界要记住:
- 纯昵称做键不稳定(改名、同名);能让桥接带上 QQ 号就带 [User: 12345],比昵称可靠。
- 桥接必须按群复用 DSH Session;如果每次请求都新建会话,永远凑不出「已完成轮次」,折叠不会触发。
🎯 场景兜底:桥接不设 agentPreset 怎么办
场景原本只认 header.agentPreset。如果桥接不设预设,用 sceneMap 按正则匹配会话的 preset / origin / 会话 id / cwd:
sceneMap:
'^qq-': qqbot # 会话 id 前缀 'napcat|astrbot': qqbot sceneOverrides: qqbot: isolateByUser: true
匹配到场景后,线索会带上 scene 字段,排错时能直接看出这条记忆来自哪个场景。
🚧 隔离粒度:跨群还是按群
isolationScope 决定同一个人的记忆能不能跨群看到:
isolationScope: user # 默认:同一个人跨群共享(人设一致) isolationScope: user+scene # 更严格:同一个群才共享(避免 A 群的事在 B 群被抖出来)
user+scene 下查询会额外带上 scene = 当前场景 的条件;全群共享记忆(user_id IS NULL)两种粒度都可见。
🤖 AstrBot / NapCat:对话不经过 DSH 时
如果整条链路是 NapCat → AstrBot → 模型,DSH 根本不在场,插件的钩子不会执行。
仓库里的 astrbot/ 是一个独立的 AstrBot 插件,默认用纯 Python 读取同一个 SQLite,不需要 Node:
QQ 消息 ──► AstrBot ──► astrbot/ 插件 ──► memory.py (sqlite3) ──► 线索
安装(AstrBot 插件页 → 安装插件):
- 从链接安装:填插件仓库地址(仓库根目录就是 astrbot/ 的内容)
- 从文件安装:下载 astrbot_plugin_full_life_memory.zip(Release 附件)
- 或者直接把 astrbot/ 拷进 data/plugins/astrbot_plugin_full_life_memory/
配置里把 db_path 指到 DSH 用的同一个库即可(留空默认 ~/.dsh/full-life-memory/memory.db)。
命中触发词(记得 / 上次 / 之前…)时回忆一次并注入 system_prompt,同一个群友 120 秒内只查一次。
也可以用 flm CLI 自己接别的平台:
npm i -g @tuhengyu/dsh-full-life-memory # 安装后得到 flm 命令
flm recall --text "你还记得上次那个数据库吗" --user 10001 --scene qqbot flm remember --keywords "数据库,sqlite" --seed "用 node:sqlite 存线索" --user 10001 flm stats
输出是 JSON,方便任何语言调用。安装与配置细节见 astrbot/README.md,发布到插件的步骤见 astrbot/MARKETPLACE.md。
核心逻辑只有一份(同一个 SQLite、同一套 SQL),DSH 插件、AstrBot 插件、CLI 都只是消费者。
🛠 设置界面
两个入口,同一张表单:
- Settings → 终身记忆(独立设置页)
- Plugins → 已安装插件 → Full-Life Memory(插件详情页的配置区,DSH 的插件配置约定位置)
选项用大白话,不再出现「阈值 / 折叠轮数」这类实现词:
| 控件 | 含义 |
| 遗忘触发条件 | 对话超过多少 token 时开始遗忘(调大记得更久) |
| 遗忘会话轮数 | 一次遗忘几轮已经聊完的对话 |
| 最近记忆保留轮数 | 最近这么多轮永远保留,方便接着聊 |
| 自动遗忘 | 总开关 |
分为两个标签页:
- 全局默认:上面那四项
- 场景:每个场景一张卡片(启用开关 + 三个数值 + 删除),底部输入场景标识即可新增
下方实时算出「什么时候会触发」:例如 至少要有 6 轮聊完的对话(遗忘 2 轮 + 保留 4 轮)才会开始,改数字时同步刷新。
🌍 多语言
表单右上角有语言下拉框,选项来自 DSH 的语言服务(不是插件自己写死的列表),切换时调用 ctx.locale.setLocale(id)——也就是跟随全局偏好,插件不维护自己的语言状态。
只带 zh / en 两套文案,加语言只要两步:
// client.js
const DICTS = { zh, en, ja } // ① 补一份字典
// locale/ja.json(插件管理器里显示的名字) { "meta": { "title": "フルライフ記憶", "description": "…" } }
不需要改 UI:下拉框会自己出现 ja(前提是宿主语言列表里有它,可用 ctx.locale.addLanguage({ id, label, fallback }) 注册)。缺键会走宿主的 fallback 链,不会显示 undefined——client-smoke 里有一条断言专门检查所有字典键集合一致。
🔄 检查更新 / 一键更新
表单上方有一行版本信息:v0.3.5 [检查更新],最右侧是语言下拉框。
| 状态 | 表现 |
| 点「检查更新」 | 请求 registry.npmjs.org/<包名>/latest,与运行中的版本比对 |
| 有新版本 | 显示 → v0.3.2 和「立即更新」 |
| 已是最新 | 顶部轻提示「当前已是最新版本」,2.6 秒后自动消失 |
| 点「立即更新」 | 调用 DSH 的 pluginManager.installBundle,由 Host 执行 pnpm 下载并校验 |
| 更新完成 | 弹出模态框:「更新已下载,请重启 DSH 以应用新版本」 |
为什么不自己 child_process 跑 pnpm:pluginManager 已经处理了 profile 写入锁、依赖脚本审批、peer 兼容性检查、失败回滚、registry 回退和「是否需要重启」的判定。自己 spawn 会绕过这些,还可能把 profile 的 package.json 写坏。
版本探测优先走浏览器直连 registry;如果部署的 CSP/CORS 拦住了跨域请求,会退回 Host 的 pluginManager.inspect 再查一次。
这些字段标了 .volatile():保存后下一轮就生效,不用重启 Host;已经折叠掉的历史不会被恢复。
👀 你会看到什么
对话流里多一行系统提示:
【🧠 记忆已折叠】为了腾出空间,我已淡忘更早的 2 轮对话,留下的线索:E:/Fishwork/app.ts、recall_from_clues、node:sqlite。
需要时我会用 recall_from_clues 自己回忆。
日志里多一行账:
full-life-memory: folded 2 turn(s) (~9123 tokens) from surface seq 2..9
as compaction 6f1c…; clues: E:/Fishwork/app.ts, recall_from_clues, node:sqlite
然后你随口问一句「我们之前那个 sqlite 的事呢?」——模型会自己调 recall_from_clues,把线索接成回忆。
🧩 工作原理(30 秒版)
1. 测压:agent/pre-step(waterfall)里用 ctx.tokenMeter.measure(session) 读当前请求压力。
2. 选段:只挑已完成轮次(turn/end 之后),天然避开半个 tool-call;surface 第 0 个 system 节点永不参与。
3. 蒸馏:正则抽 keywords 与 seed_tokens,连同 session_id 写进 memory_clues 表。
4. 切除:session.append('user/message', checkpoint, { surfaceOp: { op:'replace', startSeq, endSeq } })——和 DSH 自己的 dsh-compaction-basic 逐字同一条路径。
5. 留痕:外面套 compaction/start → summary → replace → end,Chat 因此把它渲染成原生的「记忆折叠」行,而不是一条用户消息。
安全边界都是显式校验过的:区间必须工具配对平衡、没有未闭合的 compaction/start、同一轮最多切一次、最近轮次受保护。任何一步失败都只是"这次不遗忘",绝不留半截事务。
❓ 常见问题
忘掉的东西还能找回来吗? 能找回"上下文",找不回"原文"——这正是设计目标。线索足够模型重建当时在聊什么、做到哪一步;逐字复述不重要。
真的不花 token 吗? 遗忘过程 0 token(纯正则)。只有你主动回忆时才会花几百 token——这笔钱花在真正需要的那一刻,而不是每一轮都替全部历史付费。
中文能匹配吗? 能。线索库用 LIKE 模糊匹配而不用 FTS5:中文不分词,线索本来就短,LIKE 反而更稳;% 和 _ 已转义。
会不会和 DSH 自带的自动压缩打架? 不会。检测到未闭合的 compaction/start 就跳过;自己开事务时严格成对闭合,不会把锁留给别人。
会不会把系统提示或工具配对切坏? 不会。system 头永不切;只切 turn/end 之后的范围;区间前后都做 tool-call/result 平衡校验。
数据在哪?会不会上传? 只有一个本地 SQLite 文件,默认 ~/.dsh/full-life-memory/memory.db。没有网络请求,没有遥测。
能只用手动回忆吗? autoForget: false。历史不再被切,recall_from_clues 照常可用。
🗂 项目结构
dsh-full-life-memory/
├── package.json # bundle 清单 + dsh.bundle.patch + dsh.client ├── cordis.patch.yml # 插入 Host 插件行 + 默认 config ├── index.ts # Host 源码(类型完整,改这里) ├── index.js # Host 运行时 = index.ts 编译产物 ├── client.js # 浏览器半边:Plugins 页里的设置界面 ├── locale/{en,zh}.json # 插件管理器里的标题与描述 ├── icon.svg └── test/ ├── smoke.mjs # Phase 1:命中 / 未命中 / 截断 / 转义 / 连接回收 ├── auto-forget.mjs # Phase 2:阈值 / 切除区间 / 配对校验 / 落库 / 每轮一次 └── client-smoke.mjs # 设置页:用假 slots/configForms 跑一遍渲染
🧪 测试
node test/smoke.mjs
node test/auto-forget.mjs
node test/client-smoke.mjs
auto-forget.mjs 用一个仿 Session 契约的假会话跑完整链路,并额外做一次 index.ts 与 index.js 的行为对拍:同一个场景分别执行,逐字段比较追加事件、被切的 surface 区间、落库的线索行,确保"编译产物"和源码不漂移。
覆盖:阈值以下不动、到阈值切最旧两轮、replace 区间精确、system 头保留、后续轮次完好、线索落库、同轮不重复切、最近轮次受保护、工具配对不平衡拒绝、未闭合 compaction 拒绝、落库失败不留半截事务。
🧭 设计取舍
规则蒸馏不如语义摘要细腻——它抓的是「名字、路径、术语、原话」,不是「结论」。这是刻意的:遗忘不该再花钱,也不该再等一次模型响应。真想要语义摘要,只需替换 extractClue() 一个函数,返回结构不变,其余逻辑(选段、切除、落库)完全复用。
🛣 Roadmap
- [ ] extractClue 的可选 LLM 后端(默认仍是正则)
- [ ] 遗忘曲线权重:越久远的线索越淡,命中时排在后面
- [ ] 按项目 / workspace 分区的线索库
- [ ] /forget 手动命令与「回忆命中率」统计
- [ ] 线索之间的弱关联图,支持「顺着一条线索想起另一条」
📄 License
MIT © tuhengyu · 完整文本见 LICENSE
记忆不是存储,是重建。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
Toctsu/dsh-full-life-memory
像人一样遗忘,也像人一样回忆。
相关插件 · 对话 / 记忆
上下文中枢oceanbase/powercontext
上下文跟着工作走 —— 跨会话、跨智能体、跨人。
★ 1,489已汉化DSH
73.1
智能体记忆库plastic-labs/honcho
Honcho 是记忆基础设施,用来构建有状态的智能体。这些智能体能理解不断变化的人、智能体、群组、项目和想法。
★ 7,918已汉化DSH
71.7
长程智能体控制面loopx-project/loopx
给 Agent 一个目标,让工作持续向前。
★ 6,358已汉化DSH
71.3
智能体上下文数据库volcengine/OpenViking
给 AI 智能体用的上下文数据库
★ 41,453已汉化DSH
71.3
长效机器人团队Fei2411/DS-Bot
Download DS Bot for Windows 10/11 64-bit / 下载 Windows…
★ 5,293已汉化DSH
71.2
可携带记忆层EverMind-AI/EverOS
为什么选择 EverOS
★ 13,797已汉化DSH
71.1
这是你的仓库?认领条目