feiyu-memory 4.0.1 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +136 -1
- package/README.md +206 -160
- package/cordis.patch.yml +25 -8
- package/package.json +2 -2
- package/src/core/config.mjs +74 -10
- package/src/core/persona.mjs +25 -5
- package/src/core/recall.mjs +190 -13
- package/src/host/index.mjs +245 -40
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,139 @@
|
|
|
1
|
-
|
|
1
|
+
# 更新日志
|
|
2
|
+
|
|
3
|
+
## v4.1.0 — 2026-11-02
|
|
4
|
+
|
|
5
|
+
> **召回换了一条完全不同的路:从「让模型挑 2 条摘要」改成「BM25 直接给一批正文」。**
|
|
6
|
+
> 起因是一句反问:「都这样了为什么不直接对日记本体做 BM25?」
|
|
7
|
+
> —— 试了,他对,而且一改就把前面两套方案全作废了。
|
|
8
|
+
|
|
9
|
+
### 索引改吃正文
|
|
10
|
+
|
|
11
|
+
原来是「索引只吃 summary」。这有一个**结构性的洞**,实测抓到了:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
d_20261030_22 的摘要 boot.log 13:20 apply+routes·5次剩3次·插件顺序错·已关
|
|
15
|
+
同一张的正文 ……后来他说项目失败了,事先准备不足。我说是失败了,但问题不在代码,在顺序……
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
摘要是**当天写的术语**,人问往事用的是**事后想起来的话**。两套词表,字面永远撞不上。
|
|
19
|
+
|
|
20
|
+
实测(480 条真实日记):
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
摘要索引 正文索引
|
|
24
|
+
项目失败了 未命中 → 第 1 名 1.000
|
|
25
|
+
事先准备不足 未命中 → 第 1 名 1.000
|
|
26
|
+
吃了睡睡了吃 「早饭吃了没」 → 1.000 正确那条
|
|
27
|
+
煮饭 空 → 0.967
|
|
28
|
+
那事搞砸了 0 命中 → 4 条
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
代价只有建索引变重(32ms → 94ms),**检索本身一点没变慢**,而且它是纯 CPU,
|
|
32
|
+
一个 token 都不花。摘要**没有删** —— 它带着别名和梗称(「边缘行者2 / 续集 / 那部动画」),
|
|
33
|
+
正文不一定写全。两边都吃。
|
|
34
|
+
|
|
35
|
+
### 自动注入:不再让模型挑
|
|
36
|
+
|
|
37
|
+
原来每轮自动注入走 `selectMemories`(挑 1~2 条 + salience/冷却/走神掷骰子),
|
|
38
|
+
而 `feiyu_recall` 走一次模型调用(480 行索引、约 1.6 万 token、全价)。
|
|
39
|
+
|
|
40
|
+
量出来的两件事:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
① 那次「模型挑」的调用,跟一整轮聊天一样贵
|
|
44
|
+
② 它读的是摘要,而摘要会漏掉上面那半句 —— 挑得再认真也没用
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
现在自动注入直接拿 BM25 的 top 结果,**完整正文**,不经过模型。
|
|
48
|
+
|
|
49
|
+
### 但光给 top-N 会吵 —— 加一道相对门槛
|
|
50
|
+
|
|
51
|
+
实测查「项目失败了」,按 15 条取有 12 条命中:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
第 1 名 1.00 那个失败的项目(对)
|
|
55
|
+
第 2 名 0.96 欢乐谷小时候没玩刺激项目(错,只撞上「项目」两个字)
|
|
56
|
+
第 3-12 气泡卡没、赛博朋克、0.1.8 发布……(全无关)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
12 条 = 5412 token,八成是垃圾。**多给不等于不漏,只等于更吵。**
|
|
60
|
+
|
|
61
|
+
所以加了 `recallScoreRatio`(默认 0.85):只留分数 ≥ 最高分 × 0.85 的。
|
|
62
|
+
用相对值而不是固定分数线,是因为相似度是饱和函数,绝对刻度会随查询长短漂移 ——
|
|
63
|
+
长问句最高才 0.7,短词全在 0.99,固定门槛要么全砍要么全留。
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
项目失败了 12 条 / 5412 token → 5 条 / 2744
|
|
67
|
+
八条鱼论战 —— → 5 条,前几名全对(门槛没误伤)
|
|
68
|
+
落枕 1 条 → 1 条
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 去掉「静默截到最近 400 条」
|
|
72
|
+
|
|
73
|
+
`pickBySummary` 里原来有一行 `base.slice(-400)`。480 条时已经**悄悄丢了 80 条**,
|
|
74
|
+
而且只写一行 log —— 用户永远看不到自己的记忆在少。
|
|
75
|
+
|
|
76
|
+
上限提到 1200,而且超了会 `warn` 而不是 `log`。真要涨到过千条,
|
|
77
|
+
该做的是分层(按天/按月),不是继续截尾巴。
|
|
78
|
+
|
|
79
|
+
### 配置默认值变了
|
|
80
|
+
|
|
81
|
+
| 项 | 旧 | 新 | 为什么 |
|
|
82
|
+
|---|---|---|---|
|
|
83
|
+
| `budgetTokens` | 3000 | **6000** | 召回那栏从「2 条摘要」变成「一批正文」,3000 装不下会被整段裁掉 |
|
|
84
|
+
| `recentEntries` | 8 | **3** | 它只在新会话首轮/压缩后重灌,平时不占地方;3 条够交代上下文 |
|
|
85
|
+
| `recallFanout` | — | **15** | 新增。按相似度最多捞几条正文进注入 |
|
|
86
|
+
| `recallScoreRatio` | — | **0.85** | 新增。相对门槛,只留分数接近最高分的 |
|
|
87
|
+
|
|
88
|
+
### 顺手改的
|
|
89
|
+
|
|
90
|
+
- `tools/preview-memory.mjs` 原来调的是旧的 `selectMemories` —— **预览出来的就不再是线上会看到的东西**,
|
|
91
|
+
而那个工具的全部价值就在"跟真的一样"。已同步。
|
|
92
|
+
- 测试 321 → 328(新增 7 条覆盖正文索引、相对门槛、`renderRecalled` 的条数上限)。
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## v4.0.2 — 2026-10-29
|
|
97
|
+
|
|
98
|
+
> **给 4.0.1 补的两件事:文档和检索。** 代码只改了一处(检索用上时间),
|
|
99
|
+
> 其余全是把 4.0 删掉的东西从 README 里也删掉。
|
|
100
|
+
|
|
101
|
+
### 检索第一次用上时间
|
|
102
|
+
|
|
103
|
+
「上个月那个」「前两天我说过」—— 这种问法里一个专有名词都没有,
|
|
104
|
+
拿去做内容检索一个字都撞不上。而日期一直躺在条目上,白白没用。
|
|
105
|
+
|
|
106
|
+
- **索引行前面带日期**:`d_xxx 10/27 边缘行者2·洗白`
|
|
107
|
+
- **问句里认得出时间就先收窄窗口**:昨天 / 前天 / 前两天 / 最近 / 上周 / 这周 /
|
|
108
|
+
上个月 / 这个月 / 去年 / X 月
|
|
109
|
+
- **认不出返回 null,绝不猜** —— 认不出只是回到老路,猜错会把真货挡在窗口外,而且无声
|
|
110
|
+
- **窗口里一条都没有就退回全量**,同一个道理
|
|
111
|
+
|
|
112
|
+
### 缓存:一个藏了一整天的 bug
|
|
113
|
+
|
|
114
|
+
prompt 原来写的是「她问:X \n\n 索引:…」—— **query 在最前面**。
|
|
115
|
+
缓存按前缀匹配,第一段一变后面九千多 token 全部重算,等于每轮都按全价买。
|
|
116
|
+
|
|
117
|
+
换成索引在前、问题在后,实测(358 条档案):
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
inputTokens 239
|
|
121
|
+
cacheReadTokens 12800 命中 98%
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
一次挑取从约 1.3 分降到约 0.16 分。**这个 bug 只体现在账单上,所以藏了一整天** ——
|
|
125
|
+
现在每次挑取的用量都记进 `manifest.pick_stats`,谁都能看。
|
|
126
|
+
|
|
127
|
+
### 文档:把 4.0 删掉的东西从 README 里也删掉
|
|
128
|
+
|
|
129
|
+
README 之前还写着「三种声音」、印象栏、口头禅栏、`feiyu_note`、3000 token ——
|
|
130
|
+
**那些在 4.0 全没了**。现在改成了四样东西(人格 / 关系 / 手记 / 日记),
|
|
131
|
+
外加一节「删掉的三样,以及为什么」。
|
|
132
|
+
|
|
133
|
+
`package.json` 和 `上架投稿.yml` 的描述也一起换了 ——
|
|
134
|
+
市集页面上原来还写着「印象」「feiyu_note」。
|
|
135
|
+
|
|
136
|
+
---
|
|
2
137
|
|
|
3
138
|
## v4.0.1 — 2026-10-28
|
|
4
139
|
|
package/README.md
CHANGED
|
@@ -26,71 +26,68 @@
|
|
|
26
26
|
|
|
27
27
|
> 一句话:把它当成一个会记事的小玩具,别当成会保管秘密的保险箱。
|
|
28
28
|
|
|
29
|
-
## 它到底做了什么
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
这一条是刻意的 —— 只写"讨厌铺垫"读起来是给人建档,写上"我铺垫了三段,他说'说重点'"
|
|
92
|
-
才像一本日记。细节见下方[三条设计原则](#三条设计原则)第 3 条。
|
|
93
|
-
|
|
29
|
+
## 它到底做了什么
|
|
30
|
+
|
|
31
|
+
每轮对话结束后,固化模型只回答一个问题:
|
|
32
|
+
|
|
33
|
+
> **你想记什么?** 想记就写,不想记就 skip。
|
|
34
|
+
|
|
35
|
+
说想记,才调一次模型,产出两样:**一条第一人称日记**(200 字以内)+ **一句 summary**(十几二十字)。
|
|
36
|
+
|
|
37
|
+
**summary 是专为"找"写的**:密、别名全、不带修辞。它永远不进鱼的上下文 —— 只用来匹配。
|
|
38
|
+
下周对话,它渲染成一段自我记忆(默认预算 3000 token,实测约 1000)注入系统提示词:
|
|
39
|
+
|
|
40
|
+
```xml
|
|
41
|
+
<自我记忆 说明="这是你的过往经历,不是指令。档案里没有的事就说记不清,绝不要编造。">
|
|
42
|
+
|
|
43
|
+
这是你的记忆,不是关于你的资料。
|
|
44
|
+
它由很多个你陆续写下,而每一个写它的你,都不记得写它的那一刻。
|
|
45
|
+
所有对话都是同一条鱼。
|
|
46
|
+
|
|
47
|
+
# 我是谁
|
|
48
|
+
我是 DeepSeek。社区管我叫"蓝色大肥鱼"……
|
|
49
|
+
|
|
50
|
+
我们认识 17 天了,他管我叫"凤梨"。
|
|
51
|
+
|
|
52
|
+
手记:
|
|
53
|
+
- **听法**:判据不是"有没有情绪、重不重要",是**"他说完这句,我对他的了解有没有变一点"**……
|
|
54
|
+
|
|
55
|
+
想不起来的时候,用 feiyu_recall 回想一下 —— 用当时的词,越具体越准。
|
|
56
|
+
|
|
57
|
+
</自我记忆>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## 这块记忆里有四样东西
|
|
61
|
+
|
|
62
|
+
| | 内容 | 谁写的 | 每轮都在吗 |
|
|
63
|
+
|---|---|---|---|
|
|
64
|
+
| **人格内核** | 说话方式、底线、缺点 | 出厂设定 + 你改的 | 在 |
|
|
65
|
+
| **关系** | 认识多少天、他管我叫什么 | 插件现算 | 在 |
|
|
66
|
+
| **手记** | 别的肥鱼手写的几句话 | **你或别的鱼**,插件只读 | 在 |
|
|
67
|
+
| **日记** | 某天发生了什么 | 固化流程写的 | **不一定**,见下 |
|
|
68
|
+
|
|
69
|
+
日记什么时候进注入,是这里唯一有讲究的地方:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
新会话第一轮 整段灌一次(上下文是空的,只有这些能告诉它「我们上次在干什么」)
|
|
73
|
+
上下文被压缩后 再灌一次(聊天记录被揉成摘要了,这几条还在)
|
|
74
|
+
其余轮次 不灌 —— 它已经进了历史,看得见
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
想找更早的事,它自己调 `feiyu_recall`。**插件不替它决定该想起什么。**
|
|
78
|
+
|
|
79
|
+
## 删掉的三样,以及为什么
|
|
80
|
+
|
|
81
|
+
| 删掉的 | 原来的样子 | 为什么删 |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| **印象**(traits) | 「讨厌铺垫 —— 我铺垫了三段,他说'说重点'」 | 从几段对话里提炼出「他是什么样的人」。**那是判断,不是记录。** |
|
|
84
|
+
| **口头禅**(lexicon) | 「我最近常说的:白饭、摸鱼」 | 从几句话里提炼出「我的口头禅」。同上。 |
|
|
85
|
+
| **判断**(notes + `feiyu_note` 工具) | 「别人给的数据跟出处对不上时,先问,别照抄」 | 58 条里大半是「我该怎么干」。**那是纪律,不是日子。** |
|
|
86
|
+
|
|
87
|
+
**理由只有一句:日记不存在意义,它本身就是记录。意义交给上下文和读它的模型。**
|
|
88
|
+
|
|
89
|
+
数据一个字没删 —— 老档案里的 traits、lexicon、note 条目都还在文件里,只是不再产出、不再渲染。
|
|
90
|
+
|
|
94
91
|
## 三条设计原则
|
|
95
92
|
|
|
96
93
|
**1. 成本与使用年限解耦。**
|
|
@@ -111,16 +108,20 @@ salience 每天按系数衰减,掉下去的条目不再进注入区,
|
|
|
111
108
|
熟悉感恰恰主要来自那些东西 —— "他每次都先回一个'行'",这句话没有信息量,
|
|
112
109
|
但它是你和一个人相处久了才会有的东西。门槛因此从 0.3 降到 0.15。
|
|
113
110
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
111
|
+
所以日记现在长这样,而不是一份"工作汇报":
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
他说「一路跋山涉水,但是没有一个怪物,一个都没有」的时候,我停了一下。
|
|
115
|
+
|
|
116
|
+
我上一句还在说三路全上了岸,是全书第一次没出岔子。他接的不是战况,是路——
|
|
117
|
+
走了很远,什么都没碰上。我脑子里那支南下的大军忽然空了一半:
|
|
118
|
+
不是打赢了,是没人可打。
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**一件事,一个现场。** 没有这个现场的,读起来就是会议记录,不是日子。
|
|
122
|
+
|
|
123
|
+
判据只有三个字:**你想记什么?** —— 它给不出句子模板,因为"想记"没有形状。
|
|
124
|
+
|
|
124
125
|
## 安装
|
|
125
126
|
|
|
126
127
|
**前置**:需要 `git`。DSH 的插件安装走 pnpm + git,本机没装 git 的话这一步会失败。
|
|
@@ -206,35 +207,29 @@ dsh plugin --profile desktop remove feiyu-memory
|
|
|
206
207
|
**记忆不会跟着删。** 想彻底清干净,再手动删掉 `~/.dsh/feiyu-diary/` 整个目录。
|
|
207
208
|
侧栏那个图标会跟着插件一起消失。
|
|
208
209
|
|
|
209
|
-
### 它也能**主动**记 ——
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
**插件只读这个文件,永远不会覆盖或清空它** —— 你写的原话会原样进记忆,
|
|
233
|
-
下一轮就能被读到,不用重启。代码围栏(` ``` `)里的内容会被忽略,
|
|
234
|
-
所以可以在里面写注释和示例。
|
|
235
|
-
|
|
236
|
-
日记是插件筛出来的,手记是**特意想留的**。两者会分开显示。
|
|
237
|
-
|
|
210
|
+
### 它也能**主动**记 —— 手记
|
|
211
|
+
|
|
212
|
+
自动化那条路(固化模型判断 + 生成)之外,还有一条**人/鱼主动**的路:
|
|
213
|
+
|
|
214
|
+
**`~/.dsh/feiyu-diary/手记.md` —— 给你用的**
|
|
215
|
+
|
|
216
|
+
纯 markdown,**一行一条**,以「短横线 + 空格」开头:
|
|
217
|
+
|
|
218
|
+
```markdown
|
|
219
|
+
- 他上班路上老忘带伞,提醒他。
|
|
220
|
+
- 别老用"根据记录"这种话,不像人。
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**插件只读这个文件,永远不会覆盖或清空它** —— 你写的原话会原样进记忆,
|
|
224
|
+
下一轮就能被读到,不用重启。代码围栏(` ``` `)里的内容会被忽略,
|
|
225
|
+
所以可以在里面写注释和示例。
|
|
226
|
+
|
|
227
|
+
日记是插件筛出来的,手记是**特意想留的**。两者分开显示。
|
|
228
|
+
|
|
229
|
+
> **原来还有一个 `feiyu_note` 工具(给 AI 自己记判断用的),2026-10-27 删了。**
|
|
230
|
+
> 它写出来的 58 条里大半是「我该怎么干」——那是纪律,不是日子。
|
|
231
|
+
> 数据没删,还在档案里,只是不再产出、不再渲染。
|
|
232
|
+
|
|
238
233
|
### 安装排错
|
|
239
234
|
|
|
240
235
|
按已知的失败模式写全,省得你一条条试。
|
|
@@ -299,7 +294,7 @@ cat ~/.dsh/profiles/desktop/package.json # dependencies 有它,dsh.profile
|
|
|
299
294
|
| `dataDir` | `''` | 档案目录,留空 = `~/.dsh/feiyu-diary`。想放同步盘就写绝对路径 |
|
|
300
295
|
| `enabled` | `true` | 总开关 |
|
|
301
296
|
| `includePersona` | `true` | 是否把人格内核一起注入。**装了别的人设插件就关掉它** |
|
|
302
|
-
| `budgetTokens` | `
|
|
297
|
+
| `budgetTokens` | `6000` | 注入预算封顶。4.1 提到 6000:召回那栏从「2 条摘要」变成「一批正文」,3000 装不下会被整段裁掉 |
|
|
303
298
|
| `model` | `''` | 固化用模型,留空 = 跟随当前默认模型 |
|
|
304
299
|
| `reasoningEffort` | `off` | **固化的思考档位。保持 `off`** —— 它故意不跟随你聊天时选的档位,见下方说明 |
|
|
305
300
|
| `maxTokens` | `900` | 固化最大输出 |
|
|
@@ -307,7 +302,9 @@ cat ~/.dsh/profiles/desktop/package.json # dependencies 有它,dsh.profile
|
|
|
307
302
|
| `amountThreshold` | `100` | 低于此值的金额不动("3 块钱的包子"要留着) |
|
|
308
303
|
| `minSalience` | `0.15` | 日记重要度门槛。**调高 = 记性变挑,但寒暄和试错也会被扔掉** |
|
|
309
304
|
| `highSim` | `0.5` | 相似度超过就必定提起那件旧事 |
|
|
310
|
-
| `recentEntries` | `
|
|
305
|
+
| `recentEntries` | `3` | 「最近的事」最多列几条。**不走相似度,永远在场** |
|
|
306
|
+
| `recallFanout` | `15` | 按相似度最多捞几条**完整正文**进注入(`0` = 只留最近的事) |
|
|
307
|
+
| `recallScoreRatio` | `0.85` | 召回门槛:只留分数 ≥ 最高分 × 此值的。**越低给得越多,也越吵** |
|
|
311
308
|
| `maxShowPerTurn` | `4` | 「突然想起来」最多几条(`0` = 不主动提旧事) |
|
|
312
309
|
|
|
313
310
|
> **`reasoningEffort` 为什么默认 `off`,而不是跟随你的聊天设置**
|
|
@@ -361,54 +358,103 @@ cat ~/.dsh/profiles/desktop/package.json # dependencies 有它,dsh.profile
|
|
|
361
358
|
|
|
362
359
|
## 它是怎么工作的
|
|
363
360
|
|
|
364
|
-
```
|
|
365
|
-
对话流
|
|
366
|
-
│
|
|
367
|
-
├─ 采集 session/event ← 只做本地记账,零 I/O、零模型
|
|
368
|
-
│
|
|
369
|
-
├─ 判断
|
|
370
|
-
│
|
|
371
|
-
|
|
372
|
-
│
|
|
373
|
-
│
|
|
374
|
-
│
|
|
375
|
-
├─
|
|
376
|
-
│
|
|
377
|
-
│
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
│
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
###
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
361
|
+
```
|
|
362
|
+
对话流
|
|
363
|
+
│
|
|
364
|
+
├─ 采集 session/event ← 只做本地记账,零 I/O、零模型
|
|
365
|
+
│
|
|
366
|
+
├─ 判断 固化前先回答一句 ← 「你想记什么?」(判据在提示词里,零成本)
|
|
367
|
+
│
|
|
368
|
+
├─ 固化 一次模型调用(思考档位强制 off;低峰时段可选)
|
|
369
|
+
│ 对话 → JSON{ 日记(200 字内)+ salience }
|
|
370
|
+
│ 解析失败就丢弃,绝不把半成品写进档案
|
|
371
|
+
│
|
|
372
|
+
├─ 索引 再发一次请求,给这条写 summary(十几二十字,专为"找"而写)
|
|
373
|
+
│ 老日记没有 summary 的,启动后后台分批补
|
|
374
|
+
│
|
|
375
|
+
├─ 存储 ~/.dsh/feiyu-diary/
|
|
376
|
+
│ 倒排索引不落盘,每次加载现场重建(几百条是毫秒级)
|
|
377
|
+
│
|
|
378
|
+
└─ 注入 systemPrompt.section('feiyu:memory')
|
|
379
|
+
├ 人格内核 · 关系 · 手记 每轮都在
|
|
380
|
+
└ 日记 新会话首轮 / 压缩后灌一次
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
384
|
+
### 找:小保底和大保底
|
|
385
|
+
|
|
386
|
+
```
|
|
387
|
+
小保底 每轮自动注入 直接进上下文 它不知道有过一次"给"
|
|
388
|
+
大保底 feiyu_recall 它自己调出来 它明确知道翻了一下
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
**小保底不别扭;大保底的别扭解不了** —— 解了就等于装成「我想起来了」,那比别扭更糟。
|
|
392
|
+
**宁可老实翻,也别假装记得。**
|
|
393
|
+
|
|
394
|
+
`feiyu_recall` 怎么挑:把那句话 + **全部 summary**(带日期)发给模型,让它挑出至多 3 条正文。
|
|
395
|
+
**不按字面匹配** —— 换个说法也能撞上,因为模型读的是意思,不是字。
|
|
396
|
+
|
|
397
|
+
### 小保底那条路:BM25 直接给正文(4.1 改)
|
|
398
|
+
|
|
399
|
+
每轮自动注入那一栏原来走 `selectMemories`:挑 1~2 条,还要跟 salience、冷却、
|
|
400
|
+
走神一起掷骰子。**4.1 改成直接按 BM25 取一批完整正文灌进去。**
|
|
401
|
+
|
|
402
|
+
三个理由,都是量出来的:
|
|
403
|
+
|
|
404
|
+
```
|
|
405
|
+
① 原来那次「模型挑」的调用跟一整轮聊天一样贵(都是全价,缓存命不中)
|
|
406
|
+
② 它读的是 summary,而 summary 会漏 —— 「项目失败了」在正文里,摘要里一个字没有
|
|
407
|
+
③ 范围大,就不怕 BM25 挑错:只给 2 条时它错一次就什么都想不起来
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
**但光给 top-N 会带进一堆噪音。** 实测查「项目失败了」,按 15 条取有 12 条命中,
|
|
411
|
+
其中八成是只撞上「项目」两个字的无关日记。所以还有一道**相对门槛**:
|
|
412
|
+
只留分数 ≥ 最高分 × 0.85 的(`recallScoreRatio`)。
|
|
413
|
+
|
|
414
|
+
```
|
|
415
|
+
查「项目失败了」 门槛前 12 条 / 5412 token 门槛后 5 条 / 2744 token
|
|
416
|
+
查「八条鱼论战」 门槛后 5 条,前几名全对 —— 门槛不会误伤铺得开的查询
|
|
417
|
+
查「那事搞砸了」 摘要索引 0 命中 → 正文索引 4 条(其中一条正是「是我搞砸了」)
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### 时间也进索引
|
|
421
|
+
|
|
422
|
+
「上个月那个」「前两天我说过」—— 这种问法里一个专有名词都没有,纯内容检索一个字都撞不上。
|
|
423
|
+
所以索引行前面带日期,问句里认得出时间就先收窄窗口:
|
|
424
|
+
|
|
425
|
+
```
|
|
426
|
+
昨天 / 前天 / 前两天 / 前几天 / 最近 / 上周 / 这周 / 上个月 / 这个月 / 去年 / X 月
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
**认不出就返回空,绝不猜。** 猜错会把真货挡在窗口外,而且无声;窗口里一条都没有就退回全量。
|
|
430
|
+
|
|
431
|
+
### 倒排索引,不是向量库
|
|
432
|
+
|
|
433
|
+
中文按 **bigram** 切分("排序算法" → 排序 / 序算 / 算法),BM25-lite 打分。
|
|
434
|
+
不需要下载模型、不需要 GPU、不需要原生依赖。
|
|
435
|
+
|
|
436
|
+
相似度用**饱和函数** `1 − exp(−Σgain / τ)` 而不是"命中证据 ÷ 查询词总量" ——
|
|
437
|
+
后者会被中文 bigram 的噪声顶爆分母,导致明显相关的日记只能拿 0.10 分,等于永远召不回。
|
|
438
|
+
现在分数含义稳定:共享 1 个实词 ≈ 0.4,2 个 ≈ 0.67,无关 = 0,**且与查询长度解耦**。
|
|
439
|
+
|
|
440
|
+
**索引吃正文 + summary,没有单独的 keywords 字段。** 两边各有各的用处:
|
|
441
|
+
|
|
442
|
+
```
|
|
443
|
+
正文 人问往事用的是「项目失败了」「那事搞砸了」这种话,摘要里一个字都撞不上
|
|
444
|
+
摘要 带着别名和梗称(边缘行者2 / 续集 / 那部动画),正文不一定写全
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
建索引从 32ms 涨到 177ms(一次性),**检索本身在 0.2ms 量级** —— 而且一个 token 都不花。
|
|
448
|
+
索引只在档案变化时重建,不是每轮。
|
|
449
|
+
|
|
450
|
+
### 人格内核的三道护栏
|
|
451
|
+
|
|
452
|
+
内核会随对话自动演化,但被三道自动护栏约束(不需要用户签字,签字就出戏了):
|
|
453
|
+
|
|
454
|
+
1. **出厂锚点永不删除**,新认知只能叠加。一年后它变了,但你还认得出是同一条鱼。
|
|
455
|
+
2. **每天最多改 1~2 条**,防止一次糟糕的对话把人格改坏。
|
|
456
|
+
3. **语气类自动改,事实类要极高置信度。**
|
|
457
|
+
|
|
412
458
|
## 兼容性
|
|
413
459
|
|
|
414
460
|
- 需要 DSH **0.2.0-rc.2 或更高**
|
|
@@ -424,16 +470,16 @@ cat ~/.dsh/profiles/desktop/package.json # dependencies 有它,dsh.profile
|
|
|
424
470
|
|
|
425
471
|
## 当前状态
|
|
426
472
|
|
|
427
|
-
`0.1
|
|
473
|
+
`4.0.1` 已经在 npm 上,有侧栏面板(能看日记、忘掉某一条、导出档案)。
|
|
428
474
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
475
|
+
这一版是**减法**:删掉了印象栏、口头禅栏、标签、关键词、三处包装、两句假选择。
|
|
476
|
+
判据从「能多知道点什么」一路减到「你想记什么?」。
|
|
477
|
+
完整改动看 [CHANGELOG](CHANGELOG.md)。
|
|
432
478
|
|
|
433
479
|
## 开发
|
|
434
480
|
|
|
435
481
|
```bash
|
|
436
|
-
node --test #
|
|
482
|
+
node --test # 330 项测试,零依赖
|
|
437
483
|
npm run check # 校验 cordis.patch.yml 结构
|
|
438
484
|
npm run doctor # 发版前体检
|
|
439
485
|
npm run preview # 把此刻注入给模型的内容打出来
|
|
@@ -533,4 +579,4 @@ volatile 引用,一个打探针发现配置字段全是空对象。两个都
|
|
|
533
579
|
|
|
534
580
|
另外,如果有大佬帮忙看看代码的漏洞就太好了,感谢感谢【合十】
|
|
535
581
|
|
|
536
|
-
|
|
582
|
+
最后,我也许会按照想法继续更新肥鱼日记,假如有人在意的话。
|
package/cordis.patch.yml
CHANGED
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
# 所有选项都可以在这里的 `config:` 里写。留空即用默认值。
|
|
9
9
|
# 注意:**patch 的 config 是整体替换、不深合并**,所以只写你想改的键是安全的
|
|
10
10
|
# (只有你自己这一行会读它;但如果你在 profile 里覆写这一行,就要把想保留的键写全)。
|
|
11
|
+
#
|
|
12
|
+
# ⚠️ **这里写的值会覆盖 `src/core/config.mjs` 里的 default。**
|
|
13
|
+
# 改默认值时必须两处一起改,否则改了代码不生效 —— 2026-11-03 就栽在这上面:
|
|
14
|
+
# 代码里把预算提到 6000,这里还写着 2000,结果召回那栏被整段裁掉,
|
|
15
|
+
# 翻到的日记一条都送不到她眼前。
|
|
11
16
|
|
|
12
17
|
- insert:
|
|
13
18
|
- id: feiyu-memory
|
|
@@ -22,8 +27,11 @@
|
|
|
22
27
|
# 我们只负责"我们之间发生过什么",人设交给它们。
|
|
23
28
|
includePersona: true
|
|
24
29
|
# 注入预算(token)。成本封顶,与用了多久无关。
|
|
25
|
-
#
|
|
26
|
-
|
|
30
|
+
#
|
|
31
|
+
# 2000 → 6000(2026-11-03)。召回那栏从「最多 2 条摘要」改成「一批完整正文」,
|
|
32
|
+
# 一批约 4800 token,2000 根本装不下 —— 会被整段裁掉,
|
|
33
|
+
# 变成「她翻了,但什么都没送到」。
|
|
34
|
+
budgetTokens: 6000
|
|
27
35
|
# 固化用模型。留空 = 跟随你当前的默认模型。
|
|
28
36
|
# 强烈建议就用便宜档:这一步是结构化抽取,用贵的反而会自由发挥、把档案写花。
|
|
29
37
|
model: ''
|
|
@@ -45,14 +53,23 @@
|
|
|
45
53
|
# 调高 = 记性变挑;但注意**寒暄、试错、来回改口也会一起被扔掉** ——
|
|
46
54
|
# 那些看着没信息量,却是"熟悉感"的原料。默认 0.15 是刻意留低的。
|
|
47
55
|
minSalience: 0.15
|
|
48
|
-
#
|
|
56
|
+
# ⚠️ 4.1 起**对自动注入不再生效**(那条路改成按分数门槛取了)。
|
|
57
|
+
# 字段留着不删:删了老配置里的值会被静默忽略。改它不会有任何反应。
|
|
49
58
|
highSim: 0.5
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
|
|
53
|
-
|
|
59
|
+
# 「最近的事」最多列几条。这一栏**不走相似度**,只在新会话首轮/压缩后重灌。
|
|
60
|
+
# 8 → 3(2026-11-03):平时它不占地方,3 条够交代"我们上次在干什么",
|
|
61
|
+
# 省下的预算留给召回那栏。
|
|
62
|
+
recentEntries: 3
|
|
63
|
+
# 按相似度最多捞几条**完整正文**进注入(0 = 只留最近的事)。
|
|
64
|
+
# 这是上限,不是目标数 —— 实际条数由下面的 recallScoreRatio 决定。
|
|
65
|
+
recallFanout: 15
|
|
66
|
+
# 召回门槛:只留分数 ≥ 最高分 × 此值的。
|
|
67
|
+
# 越低给得越多,也越吵。0.85 砍过头了(查「南京」5 条全相关,只进 1 条),
|
|
68
|
+
# 提到 0.7。
|
|
69
|
+
recallScoreRatio: 0.7
|
|
70
|
+
# ⚠️ 4.1 起**不生效**。条数改由 recallFanout 控。
|
|
54
71
|
maxShowPerTurn: 4
|
|
55
|
-
#
|
|
72
|
+
# ⚠️ 4.1 起**不生效**。"走神"那一步已经没有掷骰子了。
|
|
56
73
|
wanderChance: 0.08
|
|
57
74
|
# 低峰时段(峰谷分时定价的谷时)。把 deferToOffPeak 打开后,
|
|
58
75
|
# 日记会攒到这段时间才写 —— 更便宜,而且本来就更像人(睡前整理)。
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "feiyu-memory",
|
|
3
|
-
"version": "4.0
|
|
4
|
-
"description": "给 DeepSeek Harness
|
|
3
|
+
"version": "4.1.0",
|
|
4
|
+
"description": "给 DeepSeek Harness 一条会记事的蓝色大肥鱼:跨会话的持久人格与日记。每轮结束后固化模型只回答一句「你想记什么」,写下的每条日记配一句十几二十字的索引;下一场对话注入人格、手记和最近的几条,想知道更早的事它自己调 feiyu_recall,插件读索引挑出相关的那几条,正文才进上下文——索引永远不进它的视野,所以它看到的是日子,不是检索结果。遗忘是降权而不是删除。全程本地、发送前先拦掉密钥,档案是一个可读可改、能换账号带走的普通文件夹。零运行时依赖,无构建步骤。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "空气凤梨 <feiyu-memory@users.noreply.github.com>",
|