dsh-memory-tiers 0.5.0 → 0.8.1

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/README.md CHANGED
@@ -1,206 +1,268 @@
1
- # dsh-memory-tiers · 六层记忆 + 历史压缩
2
-
3
- > **v0.3.0**(2026-10-31)。架构细节、实测数据、踩过的坑全在 [`ARCHITECTURE.md`](./ARCHITECTURE.md)。
4
- > 本文只讲「它干什么」和「怎么用」。
5
-
6
- ## 它做三件事
7
-
8
- 1. **自动记忆**:订阅 `session/event`,每轮结束把你说的话、我调的工具写进记忆(L1),
9
- 满了自动摘要沉淀到 L2/L3 和深层 DL1/DL2/DL3。
10
- 2. **注入上下文**:每轮把 L3(长期摘要)+ L2(最近沉淀)作为一条 user 快照注入,
11
- 内容不变就不刷新,不打掉 prompt cache。
12
- 3. **接管历史压缩**:每轮结束把 dsh 自带的会话历史压成一条 20 字占位,
13
- **零 LLM 调用**,被压掉的内容先归档进记忆。上下文因此保持恒定,不随轮数增长。
14
-
15
- ## 文件清单
16
-
17
- | 文件 | 作用 |
18
- |---|---|
19
- | `index.mjs` | dsh 插件入口:注册工具、订阅事件、注入上下文、调度压缩 |
20
- | `memory5.mjs` | 六层记忆核心:分层、热度、摘要、路由、检索、遗忘 |
21
- | `compaction.mjs` | 历史压缩:把 dsh 的 surface 压成占位,原文归档进记忆 |
22
- | `silicon.mjs` | 模型层:embed / 摘要 / 判关系(无 key 时降级为本地哈希向量) |
23
- | `cordis.patch.yml` | 告诉 dsh 怎么把插件插进配置树 |
24
- | `package.json` | 包信息,`dsh.bundle.patch` 是关键字段 |
25
- | `ARCHITECTURE.md` | 完整架构、实测数据、已知问题清单 |
26
-
27
- ## 安装
28
-
29
- ```bash
30
- # 方式一:链接本地目录(改代码立刻生效,开发时用)
31
- cd ~/.dsh/profiles/desktop
32
- pnpm add "link:/path/to/dsh-memory-tiers"
33
-
34
- # 方式二:装打包好的 tgz(分发用)
35
- cd ~/.dsh/profiles/desktop
36
- pnpm add /path/to/dsh-memory-tiers-0.3.0.tgz
37
- ```
38
-
39
- 然后把 `dsh-memory-tiers` 加进该 profile `package.json` 的 `dsh.profile.bundles`,**重启 dsh**。
40
-
41
- 配置文件在 `<数据目录>/config.json`,首次启动自动生成。
42
-
43
- ### 数据目录(可指定,便于备份迁移)
44
-
45
- 默认 `~/.dsh/storages/memory-v5`。想换地方,用环境变量(支持 `~`):
46
-
47
- ```bash
48
- export MEMORY_TIERS_DIR="~/my-memory" # 或 D:/backup/memory
49
- ```
50
-
51
- 或在插件配置里传 `dir`。**改不了 config.json 里的路径**——那个文件就在目录内部。
52
-
53
- 整个目录是自包含的:`store.json` + `vectors-part-*.json` + `archive/` + `graveyard/` +
54
- `config.json`。**拷走整个目录,换个 agent、换个机器,改一下 `MEMORY_TIERS_DIR` 就能接着用。**
55
-
56
- ## 打包
57
-
58
- ```bash
59
- npm pack --pack-destination ./dist
60
- ```
61
-
62
- 产物 `dist/dsh-memory-tiers-0.3.0.tgz`,只含 `package.json` 里 `files` 列出的运行时文件
63
- (测试、模拟脚本、数据集都不打进去)。**新增文件记得同步 `files`**,
64
- 漏了会在安装后才炸(`ERR_MODULE_NOT_FOUND`)——`compaction.mjs` 就差点栽在这。
65
-
66
- ---
67
-
68
- ## 以下是 v1 时代的设计笔记(保留作背景,与当前实现已有差异)
69
-
70
-
71
- ## 第一步:先看它跑起来(不用装 dsh)
72
-
73
- ```bash
74
- cd dsh-memory-tiers
75
- node test.mjs
76
- ```
77
-
78
- 你会看到完整的生命周期:5 条记忆先全落在 L1 → 时间快进 4 天 → 3 条升 L2、2 条沉到 `archive/*.md` → 搜"验证"能翻出旧账 → 搜"环境"把冷存那条拉回来(回温)。最后打印出"每轮注入的提示词"长什么样。
79
-
80
- **这一步是你理解它的最快方式**,比读文档快十倍。
81
-
82
- ## 第二步:装进 dsh(已在你机器上做好了)
83
-
84
- > **重要事实(我实测过)**:桌面版 dsh 的 `desktop` profile 被 Electron 应用独占,命令行装不进去 —— 会报
85
- > `error: profile "desktop" is managed exclusively by the Electron application`。
86
- > 所以插件装在了独立的 `web` profile 里,用 `dsh web` 启动的那个实例。
87
-
88
- 装的时候需要 pnpm(dsh 的插件管理就是转发给 pnpm),我已经装好了(`pnpm 12.15.0`)。
89
-
90
- ```bash
91
- # 已经执行过的两条,你不用再跑
92
- npm i -g pnpm
93
- cd dsh-memory-tiers && npx -y @deepseek-ai/dsh plugin --profile web add ./
94
- ```
95
-
96
- 装完之后 `~/.dsh/profiles/web/package.json` 里会多出 `"dsh-memory-tiers": "link:...你的目录"`。
97
- **因为是 link,所以你改了 `memory.mjs` 重启就生效,不用重装。**
98
-
99
- ## 第三步:启动并验证
100
-
101
- ```bash
102
- npx -y @deepseek-ai/dsh web
103
- ```
104
-
105
- 浏览器会打开 `http://127.0.0.1:3080/?token=...`(token 每次不同,别用旧的)。
106
-
107
- 验证插件真的活着,看这个文件:
108
-
109
- ```bash
110
- cat ~/.dsh/storages/memory-tiers/status.json
111
- ```
112
-
113
- ```json
114
- {
115
- "loadedAt": "2026-10-26T09:02:17.193Z",
116
- "tools": 6,
117
- "promptInjected": true, ← 这两个是重点
118
- "dir": "C:\\Users\\Anxiple\\.dsh\\storages\\memory-tiers"
119
- }
120
- ```
121
-
122
- `tools: 6` 说明六个工具都注册上了,`promptInjected: true` 说明记忆已经接进系统提示词。
123
- 然后在对话框里输 `/memory`:
124
-
125
- ```
126
- 三层记忆:L1=0 · L2=0 · L3=0(钉住 0)
127
- 数据目录:C:\Users\Anxiple\.dsh\storages\memory-tiers
128
- ```
129
-
130
- 数据实际存在 `~/.dsh/storages/memory-tiers/`:`store.json` 是状态表,`archive/` 是冷存正文,都可以直接用编辑器打开看。
131
-
132
- ---
133
-
134
- ## 踩过的坑(你以后一定会遇到)
135
-
136
- | 报错 / 现象 | 真正原因 | 解法 |
137
- |---|---|---|
138
- | `函数 'memory_xxx':schema 必须是 'type: "object"' 的 JSON 模式,获得 'type: null'` | 工具参数用了 `{ id: {...} }` 简写,只有官方 `defineTool` 能把它编译成 schema;而插件目录 import 不到 `@deepseek-ai/dsh-tools`,静默回退成裸对象,dsh 判成 null | 手写 `{ type:'object', properties:{...}, required:[...] }`,不要依赖 defineTool |
139
- | `cannot get property "systemPrompt" without inject` | Cordis 里访问任何 `ctx.xxx` 前都必须在 `inject` 里声明过 | 用 `ctx.inject(['systemPrompt'], cb)` 包起来,别直接读 `ctx.systemPrompt` |
140
- | 插件加载了但模型完全看不到记忆 | `section()` 的签名是 `{ name, order, text }`,不是 `{ id, content }`,写错会被 try/catch 悄悄吞掉 | 现在写错了会直接抛错,不会再静默失败 |
141
- | `profile "desktop" is managed exclusively by the Electron application` | 桌面版独占 profile | 换 `web` profile,或用桌面端的插件市场(只收 npm 包) |
142
- | `pnpm: command not found` | dsh 的 `plugin` 命令只是把参数转给 pnpm | `npm i -g pnpm` |
143
-
144
-
145
- ## 想调什么,改哪里
146
-
147
- 全在 `memory.mjs` 顶部的 `DEFAULTS`:
148
-
149
- | 参数 | 默认 | 含义 |
150
- |---|---|---|
151
- | `l1TtlDays` | 3 | 即时记忆几天不用就结算 |
152
- | `l1PromoteImportance` | 0.6 | 重要性够了就升长期 |
153
- | `l1PromoteAccess` | 2 | 被用过 2 次也升长期 |
154
- | `l2IdleDays` | 30 | 长期记忆多久没碰考虑下沉 |
155
- | `l2ArchiveScore` | 0.25 | 评分低于这个就沉到文件 |
156
- | `bootMaxChars` | 2000 | 每轮最多注入多少字 |
157
-
158
- 预算分配写在 `buildBlock()` 里:L1 拿 60%、L2 拿 35%、L3 只占一行——**冷存正文永远不进上下文**,这是省 token 的关键。
159
-
160
- ## 排错对照表
161
-
162
- | 现象 | 原因 | 怎么办 |
163
- |---|---|---|
164
- | 重启后没反应,也看不到工具 | `package.json` 的 `dsh.bundle.patch` 丢了,或 `cordis.patch.yml` 里的 `name` 和包名不一致 | 检查这两个字符串是否都是 `dsh-memory-tiers` |
165
- | 日志里出现"未能注入系统提示词" | 你这个 dsh 版本的 `systemPrompt` API 名字不一样 | 告诉我版本号,改 `index.mjs` 里 `injectPrompt()` 那 10 行 |
166
- | 工具能看到但不被调用 | 模型不知道什么时候该用 | 改工具的 `description`,把触发场景写具体 |
167
- | 记了一大堆但想不起来 | 召回是关键词匹配,命中率有限 | 先 `/memory` 看分布,再考虑换向量检索 |
168
-
169
- ## 一句实话
170
-
171
- 这版是**能跑的 MVP**,不是成品。它的召回是两字组合的关键词匹配,够用但不够聪明;它的"蒸馏"是原样搬运,没有让模型总结压缩。这两块是后面真正拉开差距的地方——建议先用两周,等你真的被"想不起来"坑到了,再动手升级,否则你会一直在调你还没遇到的问题。
172
-
173
- ---
174
-
175
- # 日常怎么用(装好之后)
176
-
177
- **大部分时候你什么都不用做。** 正常聊天就行——插件每轮都会把记忆塞进上下文,模型看到"长期偏好 / 已定的决定 / 硬约束 / 你说记住"这几类信息时会自己调 `memory_write`。
178
-
179
- ## 四句话就够
180
-
181
- | 你想干什么 | 直接说 |
182
- |---|---|
183
- | 让它记住 | 「记住:报告一律用 markdown」 |
184
- | 让它想起来 | 「我之前说过报告用什么格式?」(它会自己调 `memory_recall`) |
185
- | 纠正记错的 | 「那条记错了,删掉重记:是 PDF 不是 markdown」 |
186
- | 看它到底记了什么 | `/memory` |
187
-
188
- ## 怎么确认它真的在工作(三步)
189
-
190
- 1. 在会话里说一句「记住:我的项目路径是 D:/lung」
191
- 2. **开一个新会话**,问「我的项目路径是什么?」——能答出来就说明跨会话生效了
192
- 3. 打开 `C:\Users\Anxiple\.dsh\storages\memory-tiers\store.json`,能看到那条记录,`tier` 是 `L1`
193
-
194
- ## 三个不明显的点
195
-
196
- - **`/memory recall 关键词`** 是手动检索,冷存里的东西也能搜到,比问模型快。
197
- - **冷存不是删除。** L3 的正文在 `archive/*.md` 里躺着,搜到就自动捞回来并升回 L2,你随时能看能改。
198
- - **重要的东西要钉住。** 说「把那条钉住」,它会调 `memory_pin`,之后无论多久不用都不会被降级——核心偏好和硬性约束值得钉。
199
-
200
- ## 它什么时候"遗忘"
201
-
202
- - 每 6 小时自动结算一次(插件里的定时器),重启 dsh 也会补结算一次。
203
- - L1 三天没人碰 → 重要的升 L2,不重要的沉到 `archive/`。
204
- - L2 三十天没人碰且评分低 → 沉到 `archive/`。
205
-
206
- 想改这些天数,回去改 `memory.mjs` 顶部的 `DEFAULTS`,重启生效。
1
+ # dsh-memory-tiers
2
+
3
+ 给 dsh 加长期记忆,同时把每轮对话的 token 压住不涨。
4
+
5
+ 装好之后你不用刻意「记一下」——它自己记、自己整理、需要时自己找回来。
6
+
7
+ ---
8
+
9
+ ## 它解决两个问题
10
+
11
+ **① 上下文不够用**
12
+
13
+ dsh 聊久了会遇到两种结局:要么历史太长爆上下文,要么被压缩掉、事后想不起来。
14
+ 这个插件把对话记到本地,压缩掉的内容仍在库里,随时能搜回来。
15
+
16
+ **② 每轮花费随历史增长**
17
+
18
+ 默认它会接管 dsh 的历史压缩:**上一轮说完,历史就被压成一条 20 字占位**(一条不留),
19
+ 下一轮只带记忆块 + 你这句话。上下文长度不随轮数增长,token 花费也就平了。
20
+
21
+ 省下来的比它花的多:整理是有触发条件的(L1/L2 满了),不是每轮都调模型。
22
+
23
+ ---
24
+
25
+ ## 它怎么做到(一句话版)
26
+
27
+ 记忆分六层:**L1/L2/L3** 是活跃层,每轮拼进上下文;**DL1/DL2/DL3** 是深层,只在检索时用。
28
+
29
+ ```
30
+ 你说的话 → L1(刚发生的)→ 满了取一半 → 重要的进 L2/L3,不重要的沉 DL1 → DL1 里的按时间忘掉
31
+ ```
32
+
33
+ 原理、实测数据、边界条件都在 [`ARCHITECTURE.md`](./ARCHITECTURE.md),本文只讲怎么用。
34
+
35
+ ---
36
+
37
+ ## 安装
38
+
39
+ **方式一:dsh 插件市场**(推荐)
40
+
41
+ 在 dsh 的插件市场里搜 `dsh-memory-tiers`,安装后重启。
42
+
43
+ **方式二:npm**
44
+
45
+ ```bash
46
+ cd ~/.dsh/profiles/desktop
47
+ pnpm add dsh-memory-tiers
48
+ ```
49
+
50
+ 然后把 `dsh-memory-tiers` 加进该 profile `package.json` 的 `dsh.profile.bundles`,重启 dsh。
51
+
52
+ **方式三:本地链接**(改代码时用)
53
+
54
+ ```bash
55
+ cd ~/.dsh/profiles/desktop
56
+ pnpm add "link:/path/to/dsh-memory-tiers"
57
+ ```
58
+
59
+ ---
60
+
61
+ ## 第一次配置(三步)
62
+
63
+ 重启 dsh 后,数据目录里会自动生成 `config.json`。改配置有两个入口:
64
+
65
+ - **设置页**:dsh → 设置 → 记忆(推荐,改完重启生效)
66
+ - **手改** `<数据目录>/config.json`
67
+
68
+ ### 第一步:填模型 Key
69
+
70
+ 记忆的「语义检索」和「摘要」需要模型。不填也能用,但只能按关键词搜,且不会自动整理。
71
+
72
+ 去 <https://cloud.siliconflow.cn> 注册,拿一个 `sk-` 开头的 Key,填到:
73
+
74
+ - 设置页 → 记忆 → 嵌入与重排 → API 密钥
75
+ - 或 `config.json` 里的 `apiKey`
76
+
77
+ 默认用硅基流动的两个免费模型:
78
+
79
+ | 用途 | 默认模型 | 费用 |
80
+ |---|---|---|
81
+ | 嵌入(向量化) | `BAAI/bge-m3` | 免费 |
82
+ | 重排(精排) | `BAAI/bge-reranker-v2-m3` | 免费 |
83
+
84
+ 摘要和判关系默认**跟着你当前会话的模型**走,不额外花钱。
85
+
86
+ ### 第二步:给这个程序起个名字
87
+
88
+ `writer` 是这个程序在数据目录里的身份,**每个程序必须不一样**:
89
+
90
+ ```json
91
+ { "writer": "dsh" }
92
+ ```
93
+
94
+ - 只有 dsh 用:填什么都行(默认 `default`)
95
+ - 和 astrbot 共用一份记忆:一个填 `dsh`,一个填 `astrbot`
96
+
97
+ 填成一样的两个程序会往同一份临时文件里写,不会丢,但分不清是谁说的。
98
+
99
+ ### 第三步:确认数据目录
100
+
101
+ 默认 `~/.dsh/storages/memory-v5`。想放别处(比如 D 盘、同步盘):
102
+
103
+ ```bash
104
+ export MEMORY_TIERS_DIR="D:/memory" # 支持 ~ 和绝对路径
105
+ ```
106
+
107
+ 或者在插件配置里传 `dir`。**不要试图改 `config.json` 里的路径**——那个文件本身就在目录里。
108
+
109
+ ---
110
+
111
+ ## 配置详解
112
+
113
+ ### 记忆容量(最常调)
114
+
115
+ 三层各自有容量,容量就是**每轮注入多少字**(没有单独的上限开关):
116
+
117
+ | 参数 | 默认 | 含义 | 调大会怎样 |
118
+ |---|---|---|---|
119
+ | `memoryCfg.hotMaxChars` | 3000 | L1 容量:刚说的话 | 记得更多细节,token 更多 |
120
+ | `memoryCfg.consolidateMaxChars` | 2500 | L2 容量:最近沉淀的原话 | 同上 |
121
+ | `memoryCfg.longMaxChars` | 4000 | L3 容量:长期摘要 | L3 段更长,检索信息更全 |
122
+
123
+ 三层合计约 9500 字(约 7000 token)。觉得占上下文多就调小,觉得记不住就调大。
124
+
125
+ > L1 上限也是**整理触发线**:写满 3000 字才取一半整理。调太小会整理得很频繁。
126
+
127
+ ### 历史压缩
128
+
129
+ | 参数 | 默认 | 说明 |
130
+ |---|---|---|
131
+ | `autoCompact` | `true` | 每轮结束把 dsh 历史压成一条占位(零 LLM 调用) |
132
+ | `compactKeepTurns` | `0` | 保留最近几轮原文。0 = 一条不留(L1 里已经有) |
133
+
134
+ **想保留一点原文**就把它改成 2~3,代价是 token 会随轮数涨一点。
135
+
136
+ ### 模型
137
+
138
+ | 参数 | 默认 | 说明 |
139
+ |---|---|---|
140
+ | `apiKey` / `apiBase` | 空 / 硅基流动 | 嵌入与重排用 |
141
+ | `preset` | `siliconflow` | `siliconflow` 用上面两个免费模型;`custom` 走 `models` 里自己填的 |
142
+ | `models.chat` | `{ source: "session" }` | 判关系用哪个模型。`session` = 跟当前会话 |
143
+ | `models.summary` | `{ source: "session" }` | 摘要用哪个模型 |
144
+ | `noThink.chat` | `true` | 判关系关思考(只要一个词,思考是浪费) |
145
+ | `noThink.summary` | `true` | 摘要关思考。**打开思考会更慢(实测慢 3 倍),但摘要可能更细** |
146
+
147
+ 想换成别的 provider(DeepSeek 官方、OpenAI 兼容接口都行):
148
+
149
+ ```json
150
+ {
151
+ "preset": "custom",
152
+ "models": {
153
+ "embed": { "base": "https://api.siliconflow.cn/v1", "apiKey": "sk-xxx", "model": "BAAI/bge-m3" },
154
+ "summary": { "base": "https://api.deepseek.com/v1", "apiKey": "sk-xxx", "model": "deepseek-chat" }
155
+ }
156
+ }
157
+ ```
158
+
159
+ > 嵌入模型一旦换了,旧向量不可比较,插件会自动隔离旧向量并重建。**两个程序共用记忆时,嵌入模型必须一致。**
160
+
161
+ ### 整理节奏
162
+
163
+ | 参数 | 默认 | 说明 |
164
+ |---|---|---|
165
+ | `quietMs` | 600000(10 分钟) | 一段对话安静这么久才允许整理,避免聊到一半被腰斩 |
166
+ | `organizeEveryMs` | 300000(5 分钟) | 定时补一轮整理(拿不到钥匙会自己跳过) |
167
+ | `refreshMinMs` | 1800000(30 分钟) | L3 摘要段的重算间隔。它在上下文最前面,变一次后面缓存全失效 |
168
+
169
+ ### 两端共享一份记忆
170
+
171
+ 两个程序指向同一个目录就是共享:
172
+
173
+ ```json
174
+ // dsh 的 config.json
175
+ { "layout": "split", "writer": "dsh" }
176
+
177
+ // astrbot 侧
178
+ { "layout": "split", "writer": "astrbot" }
179
+ ```
180
+
181
+ 规则:
182
+
183
+ - 各自只写自己的 `L1.tmp/<writer>.jsonl`,注入时合并
184
+ - 谁先结束谁拿「钥匙」整理,同一时刻只有一个在整理
185
+ - 深层(L2/L3/DL1~3)只有一个写者,不会互相覆盖
186
+
187
+ **要求**:两端都用本插件、`writer` 不同、嵌入模型一致。旧数据不迁移,`layout: "legacy"` 可回退到老的单文件布局。
188
+
189
+ ---
190
+
191
+ ## 怎么确认它在工作
192
+
193
+ 1. dsh 启动日志里出现:`[memory-tiers] 布局 split · writer=dsh · 嵌入 api:BAAI/bge-m3`
194
+ 2. 数据目录里出现 `L1.json`、`L1.tmp/`、`L2/L3.json`、`DL1/DL2/DL3.json`
195
+ 3. 聊两轮后 `L1.tmp/dsh.jsonl` 里有内容,`organize.state.json` 有统计
196
+ 4. 设置 → 记忆,能看到每层的条数和字数
197
+
198
+ ---
199
+
200
+ ## 常见问题
201
+
202
+ **Q:token 没降下来?**
203
+
204
+ 先确认三件事:`autoCompact` 是 `true`;`compactKeepTurns` 是 `0`;插件真的在跑(看启动日志)。
205
+ 另外缓存是否命中也要看——上下文块**追加在末尾**,前缀不变才省得下来。
206
+
207
+ **Q:它好像没记住我说的话?**
208
+
209
+ - L1 只注入最新的若干条(按配额),更早的在 L2/DL 里
210
+ - 想让它"永远记得",说的时候带上明确的意愿("以后都…"),这类内容更容易被判为重要
211
+ - 检索是按需的:模型得先想到去搜,才会搜
212
+
213
+ **Q:搜索搜不到?**
214
+
215
+ `search` 有 `depth` 参数:`summary`(只搜摘要)/ `detail`(摘要 + 原文)/ `full`(+ 流水)。
216
+ 默认走 `summary`,找不到时换 `full` 再试一次。
217
+
218
+ **Q:整理很慢,几十秒?**
219
+
220
+ 正常。一次整理要调模型生成摘要,2000 条原文要约 40~140 秒(取决于输出多少)。
221
+ 整理在后台跑,不阻塞对话;另一端的整理会等它放钥匙。
222
+
223
+ **Q:能删掉记忆吗?**
224
+
225
+ `mem.forget(id)` 会写台账并删除;也可以直接改/删数据目录里的 JSON。删之前先备份。
226
+
227
+ **Q:换机器怎么办?**
228
+
229
+ 把整个数据目录拷过去,设好 `MEMORY_TIERS_DIR` 指过去即可(配置、记忆、向量都在里面)。
230
+
231
+ ---
232
+
233
+ ## 数据与备份
234
+
235
+ ```
236
+ <数据目录>/
237
+ L1.json L2.json L3.json DL1.json DL2.json DL3.json ← 六层
238
+ L1.tmp/<writer>.jsonl ← 各端待合并的当前轮
239
+ signals/<writer>.jsonl ← attend/pin/forget 请求
240
+ vectors/deep-part-0000.json ← 向量
241
+ graveyard/forgotten.jsonl ← 遗忘台账
242
+ config.json ← 配置
243
+ organize.lock ← 整理时的锁(正常情况下不存在)
244
+ ```
245
+
246
+ 备份就拷整个目录。想彻底重来,删掉目录、重启,会重新生成。
247
+
248
+ ---
249
+
250
+ ## 开发者
251
+
252
+ > 下面这些脚本只在仓库里,npm 包里不含(`files` 字段只带运行时文件)。
253
+
254
+ ```bash
255
+ node test-memory6.mjs # 六文件布局,66 项
256
+ node test-m5.mjs # legacy 回归,41 项
257
+ node test-batch.mjs # 批量写入,20 项
258
+ node test-share.mjs # 多写者分片,25 项
259
+ node sim-real.mjs # 真实模型仿真(九组机制,--dry 假模型不花钱)
260
+ node bench-scale.mjs sum # 摘要规模基准(会花真钱)
261
+ ```
262
+
263
+ 设计、实测数据、已知问题、待办:全部在 [`ARCHITECTURE.md`](./ARCHITECTURE.md)。
264
+ 改代码前先读那一份,本文为了好读省略了大量约束。
265
+
266
+ ## License
267
+
268
+ MIT