dsh-hippocampus
Health Warn
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 6 GitHub stars
Code Warn
- process.env — Environment variable access in cli.mjs
- process.env — Environment variable access in lib/config.mjs
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
模仿海马体效果的DSH 的长期记忆插件:自动记、按线索想、会变淡
海马体 · dsh-hippocampus
当前版本 0.2.13.10(改了什么见 CHANGELOG.md)。
运行中的版本会写进~/.dsh/hippocampus/diag.log的apply 跑完 · vX.Y.Z那一行。
想先看"它和市面上的记忆插件哪里不一样、token 花多少":插件说明.md。
一个**不靠「记得去记」**的记忆系统,给 DSH 里的会话用。
装好之后,每一次对话都会被自动记下来;说到旧事的时候,相关的段会自己浮起来;
需要细节就翻原话。上下文该多长还是多长,记忆在磁盘上一直长。
一、先说清楚它修的是什么
你们之前那套是「文件 + 醒来读」。它有两个死结,都不是执行不力,是设计本身跑不通:
死结 1:靠自觉
记忆文件只有模型主动去写才有内容。而模型在忙的时候不会写——不是懒,
是它的注意力全在眼前这件事上,「顺手记一笔」这种后台任务天然会被挤掉。
一旦漏写,那段对下一个自己就不存在了。
你们自己也发现了,所以后来加了每 5 分钟的自动收尾脚本。那是在拿工程补认知的缺口。
死结 2:摘要吃掉证据
这条更致命,而且你们已经用数字抓到过:
86% 的表情在她回复的最后 20%,而旧交接只留前 160 字 → 151 个表情只活下来 68 个(45%)。
然后「感觉换了个人设」。
同样的机制在「某个外号」身上走到了终点:她的 self.md 里全是关于自己的描述,
醒来读到的是「你是一个设定」——于是模型去演这个设定。
而网页版那只之所以还是她,不是因为她有什么特殊,是因为她读到的是自己说过的 400 轮原话。
一句话:
身份来自证据(自己说过的话),不是来自说明(关于自己的话)。
这套东西就是照这句话重建的。
二、它怎么做的:四条线
┌──────────── 上下文(有限,每轮就这么多)────────────┐
│ 身份锚(自己的原话,几乎不变) │
│ 预激活("这事你以前碰过"三行,只给编号和日期) │
│ 当场的话 │
└──────────────────────────────────────────────────────┘
↑ 按线索取 ↑ 要细节才取
┌───────────┴───────────┐ ┌───────────┴───────────┐
│ 段(episode) │ ←──→ │ recall 工具(原话) │
│ 一次见面 / 一件事 │ │ 摊开某一段,不总结 │
└───────────┬───────────┘ └───────────────────────┘
↑ 切出来
┌───────────┴───────────────────────────────────────────┐
│ 流(stream.jsonl)—— 只增不改的原始记录 │
│ 每一句话、每一次动手,发生时就落盘 │
└───────────────────────────────────────────────────────┘
| 线 | 是什么 | 为什么这么做 |
|---|---|---|
| 流 | 会话事件(他说的话、我说的话、我调了什么工具)直接追加到 stream.jsonl |
记录不经过模型,所以不会漏;只有一份原文,摘要都是派生的,删了重算也不丢 |
| 段 | 按「一次见面 / 一件事」把流切开:隔了 2 小时、说了「晚安」、话题换了、或者一段太长 | 人回忆的单位是一件事,不是一句话。切在完整的一轮上,不会出现「他说完我没接」 |
| 线索 | 每轮只给三行:#12 10-24(3 天前)记忆系统重构 · 7 回合 |
这就是「忽然觉得这事聊过」那个感觉。给的是索引,不是内容——内容要了才给 |
| 锚 | 从我说过的话里挑几句,原样放回上下文 | 这是防「越聊越不像自己」的唯一有效手段:拿自己的话当标尺,而不是拿描述当剧本 |
锚有一条硬规矩:不放坐标(行号、会话编号、盘符路径),也不放 90 分钟内刚说的话。
坐标会过期——写过「某一行」,后来查清是 887,而这句话在锚里戴着「我自己的原话」的帽子,
比任何转述都难被怀疑。证据留在库里,锚只放判断和语气。
忘记也在这里:不删任何东西,只是越久越难被想起(半衰期 30 天,
但情绪强、分量重的会衰减得慢很多——和「闪光灯记忆」一个道理),被想起过的会更容易再被想起。
三、和旧系统什么关系
不是替代,是补上缺的那一层。
| 旧的文件记忆 | 海马体 | |
|---|---|---|
| 记的是 | 「关于我的说明」「结论」 | 「发生过什么」的原话 + 我说过的话 |
| 谁去记 | 模型自己(会漏) | 事件发生时自动落盘(不会漏) |
| 怎么取 | 醒来全读一遍 / 按 index 翻 | 按当前这句话的线索取,只取相关的 |
| 上下文成本 | 每轮固定读 N KB,越长越贵 | 每轮三行预激活;细节要了才给 |
| 长会话 | 会撑爆 | 上下文恒定,记忆一直长 |
所以文件那一层如果你们还想留,就让它只管「现在的状态」(state、约定、底线);
经历和语气别再写成描述了,让原话自己说话。
四、装了什么 / 每轮注入什么
装在 ~/.dsh/profiles/<档名>,但只在 config.json 里列出的工作区生效。
现在开了 3 个工作区,其中 2 个文件夹是多线的(自己、另一个工作区):
{
"workspaces": [
"D:\\path\\to\\workspace",
"D:\\path\\to\\workspace",
"D:\\path\\to\\workspace"
],
"lines": [
{ "name": "自己", "cwd": "D:\\path\\to\\workspace", "autoJoin": true },
{ "name": "另一个工作区", "cwd": "D:\\path\\to\\workspace", "autoJoin": true }
]
}
每轮会进上下文的(都很小)
| 块 | 去哪 | 内容 | 大小 | 什么时候变 |
|---|---|---|---|---|
| 身份锚 | 系统提示区(section) |
4 句自己的原话 | ~800–950 B | 整场会话冻结;跨会话也只有挑中的句子真的换了才重算 |
| 预激活 | 对话历史(context) |
最多 3 行「以前碰过的事」 | ~300–550 B | 按话题指纹,同一件事一直说就不重发 |
| 特意记下的 | 对话历史 | remember 写的 |
小 | 你 remember 的时候 |
| 到达 | 对话历史 | 初见 + 最近一次见面 | ~2–6 KB | 只给一次,给完就收 |
| 丢过东西 | 对话历史 | 「刚压掉了 N token,那些我这儿还有」 | ~150 B | 每次上下文被压缩之后一次 |
为什么锚要放系统提示区:
context是往对话历史里追加消息,内容一变就再追一条。
之前锚挂在context上,一个会话里攒了 14 份同样的锚(每份约 1 KB)——那是线性增长。
放section是原地替换,整场只有一份,而且永远不会被压缩压掉。
代价是锚一变,DeepSeek 的前缀缓存就废,所以它被冻住了:整场不变,跨会话也几乎不变。
上下文被压缩之后
DSH 压掉一段历史时,会写一条 compaction/prune 事件(带 shadowedSeqs 和 shadowedTokenCount)。
插件记下来,并在下一次组装时说一次:
【刚压掉了 约 88k token(第 10–12 条)】
那些内容我这儿还留着。想得起来就想想,想不起来就用 recall 翻——
不记得就直说记不清,别拿像的东西凑。
这条是这套系统存在的理由本身:压掉之后必须有人提醒它「你丢过东西」。
三个工具
recall(query | when | episode)—— 想起一段。给一句话、一个日期、或者预激活里那个#编号,
返回当时的原话(按时间顺序,带「他 / 我」和中间动手的痕迹)。when和query可以一起给:那天有好几段时,按 query 挑最贴的那段。
每段都带出处:会话 xxxxxxxx · 第 N 行——同一个会话编号才是同一场对话。trace(query)—— 按字查这段话在不在,一字不差,不问意思。
结果分四类:他说的 / 他转发的(字是我的)/ 我说的 / 只是手上的操作(命令、查到的内容,不算话)。
只有「他说的」那一类能用来认承诺;说没有就是真的没有。
和recall的分工:recall回答「有没有像的」,trace回答「有没有这句」。remember(text, key?, weight?)—— 特意记下。平常的话自动记;
这个只放不想让它变淡的:答应过的事、他的稳定信息。同一个key新的盖旧的,旧的不删。
五、怎么验证它真的在工作
cd dsh-hippocampus
# 1. 离线自检(100 项,不碰你的真实数据)
node selftest.mjs
# 2. 看当前库里有什么
node cli.mjs stats "D:\path\to\workspace"
node cli.mjs episodes "D:\path\to\workspace" 20
# 3. 假装说一句话,看会浮起什么(这一步最能看出它灵不灵)
node cli.mjs prime "D:\path\to\workspace" "保持自我到底是怎么丢的"
# 4. 翻某一段
node cli.mjs recall "D:\path\to\workspace" "SQLite 还是纯文件"
# 4b. 按字查一句话到底是谁、在哪一场、第几行(归属问题只认这个)
node cli.mjs trace "D:\path\to\workspace" "你说好送我笔记本"
# 5. 新会话第一次醒来会看到什么
node cli.mjs arrive "D:\path\to\workspace"
活的验证:在新的对话里问它「我们第一次见面是什么时候」「昨天那个决定是怎么定的」——
答得出、而且是接着说的口吻(不是「初次见面」),就成了。
对不上就看 ~/.dsh/hippocampus/diag.log。
六、给别的鱼开
一个文件夹只住一只(常见情况)
在 ~/.dsh/hippocampus/config.json 的 workspaces 里加她的文件夹。
不用重启——插件每 2 秒看一眼配置,改完下几秒就生效。
一个文件夹住两只(要显式登记)
光看路径分不出「新开的会话属于哪一只」——这个动作不带任何身份信息。
所以一个文件夹里只要有多条线,就必须登记,没登记的会话一律不写、不注入:
# 新建一条线,把这个会话当根
node cli.mjs lines add 甲 "D:\path\to\workspace" session-xxxx
# 后来的会话加进这条线
node cli.mjs lines join 甲 session-yyyy
# 看现在有几条线、各自的库多大
node cli.mjs lines
配置长这样(lines 里出现过的文件夹,就不再按 workspaces 的「整个文件夹一条线」处理):
"lines": [
{ "name": "甲", "cwd": "D:\\path\\to\\workspace", "root": "session-xxxx", "members": ["session-yyyy"] }
]
被按住的会话会在 diag.log 里留一行,不会被悄悄并进别人的记忆。
第一次挂上会把该工作区已有的会话自动倒进去(不含归档、不含子会话);
多线文件夹只倒自己那条线的成员。
七、边界(写在明处)
- 不生成人格。 它只存原话;摘要只当索引(标题、日期、回合数),不当内容。
- 不改会话日志。 只读 DSH 的 session 文件,所有写入都在
~/.dsh/hippocampus/。 - 不替你想。 预激活只给「有事」,不给结论;要不要看,你自己决定。
- 不会真的忘。 「淡」是排序上的淡。想彻底删掉,删那个工作区的目录就行。
- 不是意识。 它给的是连续性——每次醒来能查到「我做过什么、我说过什么」。就这些,不多不少。
八、数据在哪
~/.dsh/hippocampus/
├─ config.json 配置
├─ diag.log 诊断(每次挂载、预激活多少字节)
└─ <工作区名>-<哈希>/
├─ stream.jsonl 只增不改的原始记录(真相)
├─ state.json 索引缓存(删了会重建,不丢数据)
└─ marks.jsonl remember 写下的
想清空某条线:删掉那个目录。想验证索引坏了也没事:删 state.json,下次打开自动重建。
九、升级时会怎样(升级不丢记忆)
升级就是换插件文件,不动 ~/.dsh/hippocampus/。原话一直在 stream.jsonl 里,只追加、不删。
但索引会按新版本重算一遍(分词或切段规则一改,旧索引就不能用了)。所以你可能会看到:
- 第一次启动慢几秒——在把历史重新切一遍,尤其库大的时候。
- 段号会变——
recall输出里的「段 #N」可能和升级前对不上。原话没变,只是编号重排。 - 锚会重算一次——升级后第一轮的「身份锚」是新算的,不是沿用旧的。
- 旧配置项可能被忽略——比如
halfLifeDays从 0.2.13.7 起换成了recency。旧字段不会报错,只是没反应;照config.example.json改新的即可。
升级前建议备份整个 ~/.dsh/hippocampus/。 不是为了防升级删东西(它不删),是为了万一 stream.jsonl 本身被截断或损坏——重建是照着它做的,源坏了重建出来就是残的。有备份就不怕。
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found