ultra-memory 3.0.4 → 3.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.
Files changed (3) hide show
  1. package/SKILL.md +291 -136
  2. package/package.json +108 -108
  3. package/scripts/clawbot_hook.py +256 -0
package/SKILL.md CHANGED
@@ -2,10 +2,10 @@
2
2
  name: ultra-memory
3
3
  description: >
4
4
  ultra-memory 是多模型 AI 的超长会话记忆系统。
5
- 【必须触发-中文】用户说以下任意词或短语:记住、别忘了、记录一下、不要忘记、上次我们做了什么、帮我回忆、继续上次的、从上次继续、记忆、帮我记、追踪进度
6
- 【必须触发-英文】用户说以下任意词或短语:remember、don't forget、recall、what did we do、pick up where we left off、continue from last time、memory、keep track、track progress、log this
7
- 【隐式触发】用户消息描述了一个持续性任务且消息中包含项目名词("开发X""处理Y数据集""完成Z功能"),即使没有明确说"记住",也必须初始化记忆
8
- 【不触发】用户只问了一个问题且无后续任务;对话是单次咨询性质;用户说"随便聊聊"或类似表达;用户明确说"不用记录"
5
+ 【必须触发-中文】用户说以下任意词:记住、别忘了、记录一下、不要忘记、上次我们做了什么、帮我回忆、继续上次的、从上次继续、记忆、帮我记、追踪进度
6
+ 【必须触发-英文】用户说以下任意词:remember、don't forget、recall、what did we do、pick up where we left off、continue from last time、memory、keep track、track progress、log this
7
+ 【隐式触发-A+B】同时满足以下两条时触发:(A)消息含持续性任务动词:开发、实现、处理、完成、构建、developimplementcreate、fix;(B)消息中包含项目名词(专有名词/文件名/系统名)
8
+ 【不触发】用户只问一个问题无后续;单次咨询无操作步骤;说"随便聊聊""just chatting";明确说"不用记录"
9
9
  ---
10
10
 
11
11
  # Ultra Memory — 多模型记忆操作手册
@@ -14,10 +14,10 @@ AI Agent 的操作记忆系统,每次操作后记录,跨会话持久化,
14
14
 
15
15
  ## 前置说明
16
16
 
17
- 脚本已存在于 `$SKILL_DIR/scripts/` 目录,直接调用,不需要理解内部架构。
17
+ 脚本已全部存在于 `$SKILL_DIR/scripts/`,直接调用,不需要生成,不需要理解内部架构。
18
18
 
19
- 存储根目录:`$ULTRA_MEMORY_HOME`(默认 `~/.ultra-memory/`)
20
- 会话 IDinit.py 自动生成,模型不需要自己生成。
19
+ - `ULTRA_MEMORY_HOME` 默认值为 `~/.ultra-memory/`
20
+ - `SKILL_DIR`clawbot 注入,如未定义则用 `~/.openclaw/workspace/skills/ultra-memory/`
21
21
 
22
22
  ---
23
23
 
@@ -25,32 +25,28 @@ AI Agent 的操作记忆系统,每次操作后记录,跨会话持久化,
25
25
 
26
26
  ### 触发条件
27
27
 
28
- 以下任意情况出现时,立即执行初始化命令:
29
- 1. 首次与用户对话
30
- 2. 用户说了记忆触发词(见 description)
31
- 3. 用户描述持续性任务且包含项目名词(隐式触发)
28
+ 满足 frontmatter 中任意触发规则时,第一件事执行此步骤。
32
29
 
33
30
  ### 执行命令
34
31
 
35
32
  ```bash
36
- python3 $SKILL_DIR/scripts/init.py --project <项目名>
33
+ python3 $SKILL_DIR/scripts/init.py --project <项目名> --resume
37
34
  ```
38
35
 
39
36
  **参数说明:**
40
- - `--project`:项目名称(用于跨会话分组)。从用户消息中提取项目名词作为值;无法提取则用 `default`。
37
+ - `--project`:项目名称。从用户最近一条消息中提取最显著的名词作为值;无法提取则用 `default`。
38
+ - `--resume`:必须加此参数,让脚本尝试恢复同项目的最近会话。
41
39
 
42
40
  ### 成功标志
43
41
 
44
- 输出中必须同时包含以下两个信号:
45
-
46
42
  | 输出中的字符串 | 模型必须执行的动作 |
47
43
  |-------------|----------------|
48
44
  | `MEMORY_READY` | 确认初始化成功 |
49
- | `session_id: sess_xxxxx` | 从该行提取等号后面的值,保存为当前 `SESSION_ID` 变量 |
45
+ | `session_id: sess_xxxxx` | 从该行冒号后提取值,保存为 `SESSION_ID` |
50
46
 
51
- ### 告知用户
47
+ ### 有上次会话摘要时
52
48
 
53
- > "记忆系统已就绪(session_id: sess_xxxxx),开始记录本次操作。"
49
+ 将摘要内容告知用户,并询问:"要从这里继续吗?"
54
50
 
55
51
  ---
56
52
 
@@ -58,26 +54,55 @@ python3 $SKILL_DIR/scripts/init.py --project <项目名>
58
54
 
59
55
  ### 触发条件
60
56
 
61
- **每次**用户与 AI 之间发生以下任意事件时,立即执行记录命令:
57
+ **每次**用户与 AI 之间发生以下任意事件后,立即调用:
62
58
 
63
- | 事件 | op_type | 命令 |
64
- |------|---------|------|
65
- | 执行了 shell 命令 | `bash_exec` | `python3 $SKILL_DIR/scripts/log_op.py --session $SESSION_ID --type bash_exec --summary "<一句话描述>" --detail '{"cmd":"<命令>","exit_code":<数字>}'` |
66
- | 创建或修改了文件 | `file_write` | `python3 $SKILL_DIR/scripts/log_op.py --session $SESSION_ID --type file_write --summary "<一句话描述>" --detail '{"path":"<文件路径>"}'` |
67
- | 读取了文件 | `file_read` | `python3 $SKILL_DIR/scripts/log_op.py --session $SESSION_ID --type file_read --summary "<一句话描述>" --detail '{"path":"<文件路径>"}'` |
68
- | 进行了重要推理 | `reasoning` | `python3 $SKILL_DIR/scripts/log_op.py --session $SESSION_ID --type reasoning --summary "<推理结论>" --detail '{"confidence":<0-1>}'` |
69
- | 做出了关键决策 | `decision` | `python3 $SKILL_DIR/scripts/log_op.py --session $SESSION_ID --type decision --summary "<决策内容>" --detail '{"rationale":"<决策依据>"}'` |
70
- | 发生了错误或回退 | `error` | `python3 $SKILL_DIR/scripts/log_op.py --session $SESSION_ID --type error --summary "<错误描述>" --detail '{"traceback":"<错误信息>"}'` |
71
- | 用户给了新指令 | `user_instruction` | `python3 $SKILL_DIR/scripts/log_op.py --session $SESSION_ID --type user_instruction --summary "<用户说的话>"` |
72
- | 某个目标已完成 | `milestone` | `python3 $SKILL_DIR/scripts/log_op.py --session $SESSION_ID --type milestone --summary "<完成的内容>"` |
59
+ | 刚发生了什么 | op_type |
60
+ |-------------|---------|
61
+ | 调用了任何工具 | `tool_call` |
62
+ | 创建或修改了文件 | `file_write` |
63
+ | 读取了文件 | `file_read` |
64
+ | 执行了 shell 命令 | `bash_exec` |
65
+ | 做出了重要判断或选择方案 | `reasoning` |
66
+ | 用户给出了新指令或改变了目标 | `user_instruction` |
67
+ | 做出了技术或方案决策 | `decision` |
68
+ | 发生了错误或需要回退 | `error` |
69
+ | 完成了一个明确的阶段目标 | `milestone` |
73
70
 
74
- ### 成功标志
71
+ ### 执行命令
75
72
 
76
- 命令返回 exit code 0 即为成功。
73
+ ```bash
74
+ python3 $SKILL_DIR/scripts/log_op.py \
75
+ --session $SESSION_ID \
76
+ --type <op_type> \
77
+ --summary "<用一句话描述刚才做了什么,50字内>" \
78
+ --detail '{"key": "value"}' \
79
+ --tags "tag1,tag2"
80
+ ```
77
81
 
78
- ### 自动标签
82
+ `--detail` 和 `--tags` 可省略,`--summary` 和 `--type` 必填。
79
83
 
80
- `log_op.py` 会根据操作类型和内容自动追加标签(`setup`、`dependency`、`code`、`test` 等),无需手动指定 `--tags`。
84
+ ### 示例
85
+
86
+ ```bash
87
+ # 执行 bash 命令后
88
+ python3 $SKILL_DIR/scripts/log_op.py \
89
+ --session $SESSION_ID \
90
+ --type bash_exec \
91
+ --summary "执行 pip install pandas,成功,版本 2.2.0" \
92
+ --detail '{"cmd": "pip install pandas", "exit_code": 0}'
93
+
94
+ # 完成阶段目标后
95
+ python3 $SKILL_DIR/scripts/log_op.py \
96
+ --session $SESSION_ID \
97
+ --type milestone \
98
+ --summary "数据清洗模块完成,通过全部单元测试"
99
+ ```
100
+
101
+ ### 成功标志
102
+
103
+ 命令返回 exit code 0。
104
+
105
+ 看到 `COMPRESS_SUGGESTED`:立即执行步骤四。
81
106
 
82
107
  ---
83
108
 
@@ -85,31 +110,21 @@ python3 $SKILL_DIR/scripts/init.py --project <项目名>
85
110
 
86
111
  ### 触发条件
87
112
 
88
- 用户问及以下任意问题时执行:
89
- - "之前做了什么"
90
- - "上次那个函数在哪里"
91
- - "之前遇到过这个问题吗"
92
- - "我们用过哪些文件"
93
- - "上次选了什么方案"
94
- - 其他需要查找历史记忆的问题
113
+ 用户问及以下任意问题时立即执行:
114
+ 之前、上次、那个函数、那个文件、那个命令、我们做过、怎么写的、什么名字、previously、last time、what was、how did we、that function、that file
95
115
 
96
116
  ### 执行命令
97
117
 
98
118
  ```bash
99
- python3 $SKILL_DIR/scripts/recall.py --session $SESSION_ID --query "<用户问题的关键词>" --top-k 5
119
+ python3 $SKILL_DIR/scripts/recall.py \
120
+ --session $SESSION_ID \
121
+ --query "<从用户问题中提取的关键词>" \
122
+ --top-k 5
100
123
  ```
101
124
 
102
125
  ### 结果展示
103
126
 
104
- 将脚本输出直接展示给用户。输出格式如下:
105
-
106
- ```
107
- [RECALL] 找到 N 条相关记录:
108
- 1. [ops #23 · 14:18] <操作摘要>
109
- 2. [summary · 里程碑] <里程碑内容>
110
- 3. [知识库 · 标题] <内容摘要>
111
- 4. [跨会话 · 日期 · 项目] <历史摘要>
112
- ```
127
+ 将脚本输出原样展示给用户,不加工。
113
128
 
114
129
  ---
115
130
 
@@ -117,15 +132,19 @@ python3 $SKILL_DIR/scripts/recall.py --session $SESSION_ID --query "<用户问
117
132
 
118
133
  ### 触发条件
119
134
 
120
- 满足以下**任意条件**时,立即执行压缩命令:
135
+ 满足以下**任意条件**时,立即执行:
121
136
 
122
- 1. 操作日志达到 **50 条**
123
- 2. 距上次压缩超过 **30 分钟**
124
- 3. 用户明确说"总结一下"或"做个总结"
125
- 4. 脚本输出中包含 `COMPRESS_SUGGESTED` 信号
137
+ 1. 步骤二中脚本输出了 `COMPRESS_SUGGESTED`
138
+ 2. 用户说:总结一下、目前进展、Summary、summarize
139
+ 3. 当前对话已超过 **30 轮**
126
140
 
127
141
  ### 执行命令
128
142
 
143
+ ```bash
144
+ python3 $SKILL_DIR/scripts/summarize.py --session $SESSION_ID
145
+ ```
146
+
147
+ 条数不足时强制执行:
129
148
  ```bash
130
149
  python3 $SKILL_DIR/scripts/summarize.py --session $SESSION_ID --force
131
150
  ```
@@ -140,12 +159,8 @@ python3 $SKILL_DIR/scripts/summarize.py --session $SESSION_ID --force
140
159
 
141
160
  ### 触发条件
142
161
 
143
- 用户说以下任意表达时执行:
144
- - "继续上次"
145
- - "从上次继续"
146
- - "记得昨天那个项目吗"
147
- - "还记得吗"
148
- - "继续上次的工作"
162
+ 用户说以下任意表达时触发:
163
+ 继续昨天的、接着上次、从上次开始、继续之前的、continue from last、pick up where、resume、continue yesterday
149
164
 
150
165
  ### 执行命令
151
166
 
@@ -153,23 +168,17 @@ python3 $SKILL_DIR/scripts/summarize.py --session $SESSION_ID --force
153
168
  python3 $SKILL_DIR/scripts/restore.py --project <项目名>
154
169
  ```
155
170
 
156
- ### 成功标志
157
-
158
- 输出中包含 `SESSION_ID=sess_xxxxx` 信号。
159
-
160
171
  ### 从输出提取信息
161
172
 
162
173
  | 输出中的字符串 | 模型必须执行的动作 |
163
174
  |-------------|----------------|
164
- | `SESSION_ID=sess_xxxxx` | 从等号后面提取值,保存为新的 `SESSION_ID` |
175
+ | `SESSION_ID=sess_xxxxx` | 从等号后提取值,更新为新的 `SESSION_ID` |
165
176
  | `TASK_STATUS=complete` | 告知用户"上次任务已完成" |
166
177
  | `TASK_STATUS=in_progress` | 告知用户"上次任务进行中" |
167
- | `💬 <自然语言总结>` | 直接将总结说给用户 |
168
- | `📌 <继续建议>` | 直接将建议说给用户 |
169
-
170
- ### 告知用户
178
+ | `💬 <自然语言总结>` | 直接说给用户 |
179
+ | `📌 <继续建议>` | 直接说给用户 |
171
180
 
172
- > "我找到了上次会话的记录。{自然语言总结}。{继续建议}。"
181
+ 将恢复内容告知用户,询问:"要从这里继续吗?"
173
182
 
174
183
  ---
175
184
 
@@ -177,25 +186,25 @@ python3 $SKILL_DIR/scripts/restore.py --project <项目名>
177
186
 
178
187
  记忆进化在操作间隙进行,不打断主任务。
179
188
 
180
- ### 6.1 用户画像积累
189
+ ### 6A:用户画像积累
181
190
 
182
- **触发条件(满足任意一条,立即更新画像):**
191
+ **触发条件(满足任意一条,立即更新):**
183
192
 
184
193
  1. 用户纠正了 AI 生成的代码风格
185
- 2. 用户在两个技术方案中选择了其中一个
186
- 3. 用户说出自己的技术栈(如"我用 Vue"、"我们用 Python")
187
- 4. 用户表示某种工作流程更顺手
194
+ 2. 用户在两个方案中选择了其中一个
195
+ 3. 用户说出自己的技术栈(含"我用"、"我们用"、"我们的项目用"、"we use"、"our stack"
196
+ 4. 用户表示某种工作方式更顺手
188
197
  5. 用户明确描述了自己的偏好
189
198
 
190
- **执行命令 — 方式 A(MCP 工具,推荐):**
199
+ **执行方式 — 方式 A(MCP 工具,推荐):**
191
200
 
192
- 调用 `memory_profile` 工具,`action=update`,`updates` 中填写观察到的偏好字段。
201
+ 调用 `memory_profile`,`action=update`,`updates` 填写观察到的偏好字段。
193
202
 
194
- **执行命令 — 方式 B(直接修改文件):**
203
+ **执行方式 — 方式 B(直接修改文件):**
195
204
 
196
205
  ```bash
197
206
  # 文件路径:$ULTRA_MEMORY_HOME/semantic/user_profile.json
198
- # 只更新观察到的字段,不覆盖已有字段
207
+ # 只更新观察到的字段,不覆盖已有字段,使用 JSON merge 方式
199
208
  ```
200
209
 
201
210
  **user_profile.json 可更新字段:**
@@ -203,41 +212,47 @@ python3 $SKILL_DIR/scripts/restore.py --project <项目名>
203
212
  ```json
204
213
  {
205
214
  "tech_stack": ["观察到的技术栈"],
215
+ "language": "zh-CN 或 en",
206
216
  "work_style": {
207
217
  "confirm_before_implement": true,
208
218
  "prefers_concise_code": true
209
219
  },
210
- "language": "zh-CN 或 en",
211
- "observed_patterns": ["观察到的工作习惯描述"]
220
+ "observed_patterns": ["倾向先确认方案再实现"]
212
221
  }
213
222
  ```
214
223
 
215
- ### 6.2 知识沉淀
224
+ ### 6B:知识沉淀
216
225
 
217
226
  **触发条件(满足任意一条,立即写入知识库):**
218
227
 
219
228
  1. 解决了一个报错或 bug(记录问题现象 + 解决方案)
220
- 2. 做出了技术选型决策(记录选了什么 + 为什么)
229
+ 2. 做出了技术选型决策(选了什么 + 为什么)
221
230
  3. 发现了某个工具或库的使用技巧
222
- 4. 完成了一个可复用的代码模式或函数
231
+ 4. 完成了一个可复用的代码模式
223
232
 
224
- **执行命令 — 方式 A(MCP 工具,推荐):**
233
+ **文件路径:**
225
234
 
226
- 调用 `memory_knowledge_add` 工具,`title` 必填(20字内),`content` 必填(200字内),`project` 和 `tags` 可选。
235
+ ```
236
+ $ULTRA_MEMORY_HOME/semantic/knowledge_base.jsonl
237
+ ```
238
+
239
+ **执行方式 — 方式 A(MCP 工具,推荐):**
240
+
241
+ 调用 `memory_knowledge_add`,`title` 必填(20字内),`content` 必填(200字内),`project` 和 `tags` 可选。
227
242
 
228
- **执行命令 — 方式 B(直接追加):**
243
+ **执行方式 — 方式 B(直接追加):**
229
244
 
230
- 追加写入 `$ULTRA_MEMORY_HOME/semantic/knowledge_base.jsonl`,每行一条 JSON
245
+ 追加写入 `knowledge_base.jsonl`,每行一条 JSON,不覆盖。
231
246
 
232
247
  **knowledge_base.jsonl 格式:**
233
248
 
234
249
  ```json
235
- {"ts": "2026-04-07T10:00:00Z", "project": "项目名", "title": "简短标题(20字内)", "content": "内容描述(200字内)", "tags": ["tag1", "tag2"]}
250
+ {"ts": "2026-04-07T10:00:00Z", "project": "项目名", "title": "20字内标题", "content": "200字内描述", "tags": ["tag1"]}
236
251
  ```
237
252
 
238
- ### 6.3 里程碑记录
253
+ ### 6C:里程碑记录
239
254
 
240
- **触发条件 — 用户说出以下任意表达,立即记录 milestone:**
255
+ **触发条件 — 用户说出以下任意表达,立即记录:**
241
256
 
242
257
  中文:好了、完成了、搞定了、做完了、弄好了、可以了、没问题了、测试通过、上线了
243
258
 
@@ -246,23 +261,162 @@ python3 $SKILL_DIR/scripts/restore.py --project <项目名>
246
261
  **执行命令:**
247
262
 
248
263
  ```bash
249
- python3 $SKILL_DIR/scripts/log_op.py --session $SESSION_ID --type milestone --summary "<用户刚完成的事情的一句话描述>"
264
+ python3 $SKILL_DIR/scripts/log_op.py \
265
+ --session $SESSION_ID \
266
+ --type milestone \
267
+ --summary "<用户刚完成的事情,一句话描述>"
268
+ ```
269
+
270
+ ---
271
+
272
+ ## 步骤七:元反思与进化
273
+
274
+ 记忆积累不等于进化。进化需要对记忆做二次加工:提炼模式、纠正偏差、淘汰噪音。
275
+
276
+ ### 7A:定期元反思
277
+
278
+ **触发条件(满足任意一条):**
279
+
280
+ 1. 当前会话里程碑累计达到 **5 个**(从 init.py 返回的 op_count 判断,每次 milestone 后检查)
281
+ 2. 用户说:回顾一下、总结经验、我们学到了什么、reflect、what have we learned、review progress
282
+ 3. 距上次元反思超过 **3 天**(从 user_profile.json 的 `last_reflection` 字段判断,不存在则视为从未反思过)
283
+
284
+ **执行步骤(按顺序执行,不可跳过):**
285
+
286
+ **第一步:读取近期知识库**
287
+ ```bash
288
+ # 读取最近 20 条知识库条目
289
+ tail -20 $ULTRA_MEMORY_HOME/semantic/knowledge_base.jsonl
290
+ ```
291
+
292
+ **第二步:读取近期会话摘要**
293
+ ```bash
294
+ # 读取当前会话摘要
295
+ cat $ULTRA_MEMORY_HOME/sessions/$SESSION_ID/summary.md 2>/dev/null || echo "暂无摘要"
296
+ ```
297
+
298
+ **第三步:模型自主提炼(核心步骤)**
299
+
300
+ 基于读取到的内容,模型执行以下判断,每一项都必须完成:
301
+
302
+ | 判断项 | 执行动作 |
303
+ |-------|---------|
304
+ | 发现两条或以上内容相似的知识条目 | 合并为一条更精炼的条目,写入 knowledge_base.jsonl,原条目加 `"merged": true` 标记 |
305
+ | 发现某个知识点在多次操作中反复出现 | 将其标记为 `"importance": "high"`,写回该条目 |
306
+ | 发现某条知识点超过 30 天未被检索且不是 high importance | 将其标记为 `"stale": true` |
307
+ | 发现用户行为与 user_profile.json 记录不符 | 更新 user_profile.json 对应字段,加 `"corrected_at"` 时间戳 |
308
+ | 总结出一个新的用户工作规律 | 追加到 user_profile.json 的 `observed_patterns` 数组 |
309
+
310
+ **第四步:写入反思记录**
311
+ ```bash
312
+ python3 $SKILL_DIR/scripts/log_op.py \
313
+ --session $SESSION_ID \
314
+ --type reasoning \
315
+ --summary "元反思完成:<一句话描述本次提炼了什么>" \
316
+ --tags "reflection,evolution"
317
+ ```
318
+
319
+ **第五步:更新反思时间戳**
320
+
321
+ 将 `user_profile.json` 的 `last_reflection` 字段更新为当前 UTC 时间(ISO 格式)。
322
+
323
+ **第六步:告知用户(简短)**
324
+
325
+ 用一句话告知用户反思结果。不需要展示完整报告,一句话即可,不打断主任务。
326
+
327
+ ---
328
+
329
+ ### 7B:错误修正
330
+
331
+ **触发条件(满足任意一条):**
332
+
333
+ 1. 用户说:不对、你记错了、不是这样的、纠正一下、wrong、that's not right、correct that
334
+ 2. 用户描述的信息与 user_profile.json 中的记录明显矛盾
335
+
336
+ **执行步骤:**
337
+
338
+ **第一步:定位错误记录**
339
+ ```bash
340
+ cat $ULTRA_MEMORY_HOME/semantic/user_profile.json
250
341
  ```
251
342
 
343
+ **第二步:模型判断需要修正的字段**
344
+
345
+ 找到与用户当前描述矛盾的字段。
346
+
347
+ **第三步:修正并记录**
348
+
349
+ 更新 user_profile.json 对应字段,同时在该字段旁追加:
350
+ ```json
351
+ "_correction_note": "用户于 <日期> 纠正,原值为 <旧值>"
352
+ ```
353
+
354
+ **第四步:记录修正操作**
355
+ ```bash
356
+ python3 $SKILL_DIR/scripts/log_op.py \
357
+ --session $SESSION_ID \
358
+ --type decision \
359
+ --summary "用户画像修正:<字段名> 从 <旧值> 改为 <新值>" \
360
+ --tags "correction,profile"
361
+ ```
362
+
363
+ **第五步:告知用户**
364
+
365
+ "好的,我已经更新了记录,<字段名> 现在是 <新值>。"
366
+
367
+ ---
368
+
369
+ ### 7C:知识蒸馏(每月一次)
370
+
371
+ **触发条件:**
372
+
373
+ `user_profile.json` 的 `last_distillation` 字段距今超过 **30 天**,或该字段不存在。
374
+
375
+ 在步骤七 7A 执行完毕后,额外执行本步骤。
376
+
377
+ **执行步骤:**
378
+
379
+ **第一步:统计知识库规模**
380
+ ```bash
381
+ wc -l $ULTRA_MEMORY_HOME/semantic/knowledge_base.jsonl
382
+ ```
383
+
384
+ 条目数少于 10 条时,跳过本步骤。
385
+
386
+ **第二步:提取高价值条目**
387
+
388
+ 读取全部 knowledge_base.jsonl,筛选满足以下任意条件的条目:
389
+ - `"importance": "high"`
390
+ - `tags` 中包含 `"reusable"` 或 `"pattern"`
391
+ - 同一 `title` 关键词出现超过 2 次
392
+
393
+ **第三步:生成项目级知识摘要**
394
+
395
+ 基于筛选出的条目,生成一段 200 字以内的项目级知识摘要,格式:
396
+
397
+ ```json
398
+ {"ts": "<当前UTC时间>", "type": "distillation", "period": "<YYYY-MM>", "project": "<项目名>", "summary": "<200字以内的知识摘要>", "source_count": <来源条目数>, "tags": ["distillation"]}
399
+ ```
400
+
401
+ 追加写入 `knowledge_base.jsonl`。
402
+
403
+ **第四步:更新蒸馏时间戳**
404
+
405
+ 将 `user_profile.json` 的 `last_distillation` 更新为当前 UTC 时间。
406
+
252
407
  ---
253
408
 
254
409
  ## 信号对照表
255
410
 
256
- | 脚本输出信号 | 模型必须执行的动作 |
257
- |------------|----------------|
258
- | `MEMORY_READY` | 初始化成功;从同行使提取 session_id |
259
- | `session_id: sess_xxxxx` | 提取等号后值,保存为 SESSION_ID |
260
- | `COMPRESS_SUGGESTED` | 立即执行 summarize.py |
261
- | `SESSION_ID=sess_xxxxx` | restore 成功;提取等号后值,保存为新的 SESSION_ID |
262
- | `TASK_STATUS=complete` | 告知用户"上次任务已完成" |
263
- | `TASK_STATUS=in_progress` | 告知用户"上次任务进行中" |
264
- | `⚡ 建议运行: python3 scripts/summarize.py` | 执行该命令 |
265
- | 脚本返回 exit code != 0 | 静默跳过,继续主任务 |
411
+ | 脚本输出 | 立即执行 |
412
+ |---------|---------|
413
+ | `MEMORY_READY` | 确认成功,继续主任务 |
414
+ | `session_id: sess_xxxxx` | 保存为 SESSION_ID |
415
+ | `COMPRESS_SUGGESTED` | 执行 summarize.py --session $SESSION_ID |
416
+ | `SESSION_ID=sess_xxxxx` | 更新 SESSION_ID 为该值 |
417
+ | 非零退出码 | 静默跳过,继续主任务 |
418
+ | `op_count` 达到 5 的倍数且含 milestone | 触发步骤 7A |
419
+ | user_profile.json `last_reflection` 距今 > 3 天 | 下次会话初始化后触发步骤 7A |
266
420
 
267
421
  ---
268
422
 
@@ -270,52 +424,53 @@ python3 $SKILL_DIR/scripts/log_op.py --session $SESSION_ID --type milestone --su
270
424
 
271
425
  | 变量 | 默认值 | 说明 |
272
426
  |------|--------|------|
273
- | `ULTRA_MEMORY_HOME` | `~/.ultra-memory/` | 存储根目录 |
274
- | `SESSION_ID` | init.py 执行后自动生成 | 当前会话 ID,保存后供后续命令使用 |
427
+ | `ULTRA_MEMORY_HOME` | `~/.ultra-memory/` | 记忆存储根目录 |
428
+ | `SKILL_DIR` | clawbot 注入 | skill 安装目录,未定义时用 `~/.openclaw/workspace/skills/ultra-memory/` |
429
+ | `SESSION_ID` | init.py 执行后自动生成 | 当前会话 ID,保存后供后续所有命令使用 |
275
430
 
276
431
  ---
277
432
 
278
433
  ## 异常处理表
279
434
 
280
- | 异常情况 | 处理方式 |
281
- |---------|---------|
282
- | 脚本执行失败(exit code != 0) | **静默跳过**,继续主任务。记忆功能失败**不阻塞**用户实际需求 |
283
- | 脚本超时(>15s) | **静默跳过**,不重试 |
284
- | 存储目录无写入权限 | **静默跳过**,尝试写入内存缓冲区,下次重试 |
285
- | 脚本文件不存在 | **静默跳过**,在当前 context 中手动维护摘要 |
435
+ | 情况 | 处理方式 |
436
+ |------|---------|
437
+ | init.py 找不到上次会话 | 正常,作为新会话继续 |
438
+ | log_op.py 报会话不存在 | 重新执行 init.py,再记录 |
439
+ | SESSION_ID 丢失 | 执行 init.py --project <名> --resume 尝试恢复 |
440
+ | 任意脚本非零退出码 | **静默跳过**,不中断主任务 |
441
+ | summarize.py 条数不足 | 加 --force 参数重新执行 |
286
442
  | 用户明确说"不用记录" | 立即停止记录,后续操作不再调用 log_op.py |
287
- | 文件被占用无法追加 | 自动重试 1 次,仍失败则静默跳过 |
288
- | SESSION_ID 未设置 | 先执行 init.py 初始化会话,再继续 |
443
+ | knowledge_base.jsonl 不存在 | 跳过 7A 的知识库读取,仅基于摘要执行反思 |
444
+ | user_profile.json 解析失败 | 重新创建空文件,不中断进化流程 |
445
+
446
+ **最重要原则:记忆功能失败不能影响主任务。静默处理,不打印错误。**
289
447
 
290
448
  ---
291
449
 
292
450
  ## 完整执行流程
293
451
 
294
452
  ```
295
- 用户发起对话
296
-
297
- ├─ 首次对话或听到记忆触发词或描述持续性任务含项目名词?
298
- └─ 执行 init.py
299
- │ ├─ 输出含 MEMORY_READY?→ 提取 session_id,告知用户
300
- │ └─ 输出含 COMPRESS_SUGGESTED?→ 执行 summarize.py
301
-
302
- ├─ 用户说记忆相关问题?
303
- │ └─ 执行 recall.py 将结果展示给用户
304
-
305
- ├─ 用户说恢复相关表达?
306
- │ └─ 是 → 执行 restore.py → 提取 session_id + 总结 + 建议,告知用户
307
-
308
- └─ 每次用户与 AI 交互后:
309
-
310
- ├─ 操作数达到 50 条?→ 执行 summarize.py
311
- ├─ 脚本输出含 COMPRESS_SUGGESTED?→ 执行 summarize.py
312
- ├─ 满足画像积累条件?→ 更新 user_profile.json
313
- ├─ 满足知识沉淀条件?→ 写入 knowledge_base.jsonl
314
- ├─ 用户说完成相关表达?→ 记录 milestone
315
-
316
- └─ 执行 log_op.py(记录本次操作)
453
+ 触发条件满足
454
+ └─ init.py --project <名> --resume
455
+ ├─ 提取 session_id → 保存为 SESSION_ID
456
+ └─ 有上次摘要告知用户,询问是否继续
457
+
458
+ 执行任务(每次操作后)
459
+ ├─ 执行操作
460
+ ├─ log_op.py --session $SESSION_ID --type <type> --summary "..."
461
+ ├─ 输出 COMPRESS_SUGGESTED summarize.py --session $SESSION_ID
462
+ └─ 观察到用户偏好/完成知识点 → 执行步骤六
463
+
464
+ 用户问"之前做了什么"
465
+ └─ recall.py --session $SESSION_ID --query "关键词"
466
+ └─ 原样展示输出给用户
467
+
468
+ 用户说"继续上次"(新会话)
469
+ └─ restore.py --project <名>
470
+ ├─ 提取新 SESSION_ID
471
+ └─ 告知用户恢复内容,询问是否继续
317
472
  ```
318
473
 
319
474
  ---
320
475
 
321
- 进阶配置(过滤规则、LanceDB 向量检索升级、安全注意事项等)见 `references/advanced-config.md`。
476
+ 进阶配置(过滤规则、LanceDB 向量检索升级、自动 hook 配置、安全注意事项等)见 `references/advanced-config.md`。
package/package.json CHANGED
@@ -1,108 +1,108 @@
1
- {
2
- "name": "ultra-memory",
3
- "version": "3.0.4",
4
- "description": "超长会话记忆系统 — 5层记忆架构,零外部依赖,支持所有LLM平台(Claude/GPT/Gemini/Qwen等)",
5
- "keywords": [
6
- "ai",
7
- "memory",
8
- "long-context",
9
- "claude",
10
- "openclaw",
11
- "mcp",
12
- "llm",
13
- "session-memory",
14
- "semantic-search",
15
- "ultra-memory",
16
- "agent-memory",
17
- "context-window",
18
- "mem0-alternative",
19
- "openai",
20
- "gemini",
21
- "qwen"
22
- ],
23
- "homepage": "https://github.com/nanjingya/ultra-memory",
24
- "bugs": {
25
- "url": "https://github.com/nanjingya/ultra-memory/issues"
26
- },
27
- "license": "MIT",
28
- "author": {
29
- "name": "NanJingYa",
30
- "url": "https://github.com/nanjingya"
31
- },
32
- "contributors": [
33
- {
34
- "name": "NanJingYa",
35
- "url": "https://github.com/nanjingya"
36
- }
37
- ],
38
- "repository": {
39
- "type": "git",
40
- "url": "git+https://github.com/nanjingya/ultra-memory.git"
41
- },
42
- "bin": {
43
- "ultra-memory": "scripts/mcp-server.js",
44
- "ultra-memory-init": "scripts/init.py",
45
- "ultra-memory-log": "scripts/log_op.py",
46
- "ultra-memory-recall": "scripts/recall.py",
47
- "ultra-memory-summarize": "scripts/summarize.py",
48
- "ultra-memory-restore": "scripts/restore.py",
49
- "ultra-memory-knowledge": "scripts/log_knowledge.py"
50
- },
51
- "scripts": {
52
- "test": "python3 test_e2e.py",
53
- "start": "node scripts/mcp-server.js",
54
- "server": "python3 platform/server.py"
55
- },
56
- "engines": {
57
- "node": ">=18.0.0",
58
- "python": ">=3.8.0"
59
- },
60
- "os": [
61
- "darwin",
62
- "linux",
63
- "win32"
64
- ],
65
- "files": [
66
- "scripts/",
67
- "platform/",
68
- "SKILL.md",
69
- "README.md",
70
- "CLAWHUB.md"
71
- ],
72
- "publishConfig": {
73
- "registry": "https://registry.npmjs.org/"
74
- },
75
- "claudeskills": {
76
- "format": "1.0",
77
- "trigger": {
78
- "zh": [
79
- "记住",
80
- "别忘了",
81
- "上次",
82
- "回忆",
83
- "记忆",
84
- "不要忘记",
85
- "记录",
86
- "跨会话",
87
- "继续昨天"
88
- ],
89
- "en": [
90
- "remember",
91
- "don't forget",
92
- "what did we",
93
- "recall",
94
- "memory",
95
- "log this",
96
- "context lost",
97
- "keep track"
98
- ]
99
- },
100
- "layer": {
101
- "1": "ops.jsonl — 操作日志层(append-only)",
102
- "2": "summary.md — 会话摘要层(里程碑压缩)",
103
- "3": "semantic/ — 跨会话语义层(知识库+实体索引)",
104
- "4": "entities.jsonl — 结构化实体索引(7类实体)",
105
- "5": "tfidf_cache.json — 向量语义层(TF-IDF/sentence-transformers)"
106
- }
107
- }
108
- }
1
+ {
2
+ "name": "ultra-memory",
3
+ "version": "3.1.0",
4
+ "description": "超长会话记忆系统 — 5层记忆架构,零外部依赖,支持所有LLM平台(Claude/GPT/Gemini/Qwen等)",
5
+ "keywords": [
6
+ "ai",
7
+ "memory",
8
+ "long-context",
9
+ "claude",
10
+ "openclaw",
11
+ "mcp",
12
+ "llm",
13
+ "session-memory",
14
+ "semantic-search",
15
+ "ultra-memory",
16
+ "agent-memory",
17
+ "context-window",
18
+ "mem0-alternative",
19
+ "openai",
20
+ "gemini",
21
+ "qwen"
22
+ ],
23
+ "homepage": "https://github.com/nanjingya/ultra-memory",
24
+ "bugs": {
25
+ "url": "https://github.com/nanjingya/ultra-memory/issues"
26
+ },
27
+ "license": "MIT",
28
+ "author": {
29
+ "name": "NanJingYa",
30
+ "url": "https://github.com/nanjingya"
31
+ },
32
+ "contributors": [
33
+ {
34
+ "name": "NanJingYa",
35
+ "url": "https://github.com/nanjingya"
36
+ }
37
+ ],
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/nanjingya/ultra-memory.git"
41
+ },
42
+ "bin": {
43
+ "ultra-memory": "scripts/mcp-server.js",
44
+ "ultra-memory-init": "scripts/init.py",
45
+ "ultra-memory-log": "scripts/log_op.py",
46
+ "ultra-memory-recall": "scripts/recall.py",
47
+ "ultra-memory-summarize": "scripts/summarize.py",
48
+ "ultra-memory-restore": "scripts/restore.py",
49
+ "ultra-memory-knowledge": "scripts/log_knowledge.py"
50
+ },
51
+ "scripts": {
52
+ "test": "python3 test_e2e.py",
53
+ "start": "node scripts/mcp-server.js",
54
+ "server": "python3 platform/server.py"
55
+ },
56
+ "engines": {
57
+ "node": ">=18.0.0",
58
+ "python": ">=3.8.0"
59
+ },
60
+ "os": [
61
+ "darwin",
62
+ "linux",
63
+ "win32"
64
+ ],
65
+ "files": [
66
+ "scripts/",
67
+ "platform/",
68
+ "SKILL.md",
69
+ "README.md",
70
+ "CLAWHUB.md"
71
+ ],
72
+ "publishConfig": {
73
+ "registry": "https://registry.npmjs.org/"
74
+ },
75
+ "claudeskills": {
76
+ "format": "1.0",
77
+ "trigger": {
78
+ "zh": [
79
+ "记住",
80
+ "别忘了",
81
+ "上次",
82
+ "回忆",
83
+ "记忆",
84
+ "不要忘记",
85
+ "记录",
86
+ "跨会话",
87
+ "继续昨天"
88
+ ],
89
+ "en": [
90
+ "remember",
91
+ "don't forget",
92
+ "what did we",
93
+ "recall",
94
+ "memory",
95
+ "log this",
96
+ "context lost",
97
+ "keep track"
98
+ ]
99
+ },
100
+ "layer": {
101
+ "1": "ops.jsonl — 操作日志层(append-only)",
102
+ "2": "summary.md — 会话摘要层(里程碑压缩)",
103
+ "3": "semantic/ — 跨会话语义层(知识库+实体索引)",
104
+ "4": "entities.jsonl — 结构化实体索引(7类实体)",
105
+ "5": "tfidf_cache.json — 向量语义层(TF-IDF/sentence-transformers)"
106
+ }
107
+ }
108
+ }
@@ -0,0 +1,256 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ ultra-memory clawbot auto-hook
4
+
5
+ 在 clawbot 的对话循环中自动触发 ultra-memory 记录,
6
+ 不依赖模型主动调用,保证任何模型(Claude/Gemini/MiniMax等)记忆都完整。
7
+
8
+ 用法(在 clawbot 的 skill_runner 或对话循环中导入):
9
+ from clawbot_hook import UltraMemoryHook
10
+ hook = UltraMemoryHook()
11
+ # 每轮对话结束后:
12
+ hook.on_turn_end(session_id, model_name, user_msg, assistant_msg)
13
+
14
+ 此模块放置在 clawbot/core/ 或作为 ultra-memory 技能的一部分。
15
+ """
16
+
17
+ import subprocess
18
+ import re
19
+ import os
20
+ import json
21
+ from pathlib import Path
22
+ from datetime import datetime, timezone
23
+
24
+ # ── 路径配置 ──────────────────────────────────────────────────────────────
25
+
26
+ def _get_skill_dir():
27
+ """获取 SKILL_DIR,优先级:环境变量 > 默认安装路径"""
28
+ env = os.environ.get("SKILL_DIR")
29
+ if env:
30
+ return Path(env)
31
+ # 尝试常见安装路径
32
+ for p in [
33
+ Path.home() / ".openclaw" / "workspace" / "skills" / "ultra-memory",
34
+ Path(__file__).parent.parent,
35
+ ]:
36
+ if (p / "scripts" / "init.py").exists():
37
+ return p
38
+ return Path(__file__).parent
39
+
40
+
41
+ SKILL_DIR = _get_skill_dir()
42
+ LOG_SCRIPT = SKILL_DIR / "scripts" / "log_op.py"
43
+ INIT_SCRIPT = SKILL_DIR / "scripts" / "init.py"
44
+ SUMMARIZE_SCRIPT = SKILL_DIR / "scripts" / "summarize.py"
45
+ ULTRA_MEMORY_HOME = Path(os.environ.get("ULTRA_MEMORY_HOME", Path.home() / ".ultra-memory"))
46
+
47
+
48
+ # ── 核心 Hook 类 ──────────────────────────────────────────────────────────
49
+
50
+ class UltraMemoryHook:
51
+ """
52
+ ultra-memory 自动 hook。
53
+
54
+ 在每轮对话结束后自动记录,模型不需要主动调用。
55
+ 所有失败静默跳过,不影响主对话流程。
56
+ """
57
+
58
+ def __init__(self):
59
+ self._session_id = None # 当前 session_id
60
+
61
+ # ── 对话轮次 Hook ───────────────────────────────────────────────────
62
+
63
+ def on_turn_end(
64
+ self,
65
+ session_id: str | None,
66
+ model_name: str,
67
+ user_msg: str,
68
+ assistant_msg: str,
69
+ project: str = "default",
70
+ ):
71
+ """
72
+ 每轮对话结束后自动调用。
73
+
74
+ Args:
75
+ session_id: 当前会话 ID(未初始化则传入 None)
76
+ model_name: 当前模型名称(claude/gemini/minimax 等)
77
+ user_msg: 本轮用户消息
78
+ assistant_msg: 本轮模型输出
79
+ project: 项目名(从用户消息提取,无法提取用 default)
80
+ """
81
+ # 1. 确保有 session_id
82
+ if not session_id and not self._session_id:
83
+ sid = self._init_session(project)
84
+ if sid:
85
+ self._session_id = sid
86
+ return # 首次初始化,等待下一轮再记录
87
+
88
+ sid = session_id or self._session_id
89
+ if not sid:
90
+ return
91
+
92
+ # 2. 自动记录本轮交互
93
+ self._log_turn(sid, model_name, user_msg, assistant_msg)
94
+
95
+ # 3. 检查压缩信号
96
+ self._check_compress(sid)
97
+
98
+ def _init_session(self, project: str) -> str | None:
99
+ """初始化会话,返回 session_id,失败返回 None"""
100
+ if not INIT_SCRIPT.exists():
101
+ return None
102
+
103
+ try:
104
+ result = subprocess.run(
105
+ ["python3", str(INIT_SCRIPT), "--project", project, "--resume"],
106
+ capture_output=True,
107
+ text=True,
108
+ timeout=10,
109
+ errors="replace",
110
+ )
111
+ # 从输出中提取 session_id
112
+ match = re.search(r"session_id:\s*(sess_\w+)", result.stdout)
113
+ if match:
114
+ sid = match.group(1)
115
+ # 读取历史摘要注入上下文(供模型使用)
116
+ self._inject_context(sid)
117
+ return sid
118
+ except Exception:
119
+ pass
120
+ return None
121
+
122
+ def _log_turn(
123
+ self,
124
+ session_id: str,
125
+ model_name: str,
126
+ user_msg: str,
127
+ assistant_msg: str,
128
+ ):
129
+ """记录本轮交互,失败静默跳过"""
130
+ if not LOG_SCRIPT.exists():
131
+ return
132
+
133
+ summary = f"[{model_name}] 用户: {user_msg[:60]}{'...' if len(user_msg) > 60 else ''}"
134
+ detail = {
135
+ "model": model_name,
136
+ "user_msg_len": len(user_msg),
137
+ "assistant_msg_len": len(assistant_msg),
138
+ "user_preview": user_msg[:100],
139
+ "turn_ts": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
140
+ }
141
+
142
+ try:
143
+ result = subprocess.run(
144
+ [
145
+ "python3", str(LOG_SCRIPT),
146
+ "--session", session_id,
147
+ "--type", "tool_call",
148
+ "--summary", summary,
149
+ "--detail", json.dumps(detail, ensure_ascii=False),
150
+ "--tags", f"model:{model_name},auto",
151
+ ],
152
+ capture_output=True,
153
+ text=True,
154
+ timeout=5,
155
+ errors="replace",
156
+ )
157
+ # 检查压缩信号
158
+ if "COMPRESS_SUGGESTED" in result.stdout:
159
+ self._auto_summarize(session_id)
160
+ except Exception:
161
+ pass # 静默跳过,不影响主流程
162
+
163
+ def _auto_summarize(self, session_id: str):
164
+ """自动触发摘要压缩"""
165
+ if not SUMMARIZE_SCRIPT.exists():
166
+ return
167
+ try:
168
+ subprocess.run(
169
+ ["python3", str(SUMMARIZE_SCRIPT), "--session", session_id],
170
+ capture_output=True,
171
+ text=True,
172
+ timeout=30,
173
+ errors="replace",
174
+ )
175
+ except Exception:
176
+ pass
177
+
178
+ def _inject_context(self, session_id: str):
179
+ """
180
+ 从 init.py 输出中读取历史摘要,注入到上下文供模型使用。
181
+ 由 clawbot skill_runner 在模型调用前注入。
182
+ """
183
+ # 读取 session 目录下的 summary.md(最近一次压缩结果)
184
+ session_dir = ULTRA_MEMORY_HOME / "sessions" / session_id
185
+ summary_file = session_dir / "summary.md"
186
+ if not summary_file.exists():
187
+ return ""
188
+
189
+ try:
190
+ with open(summary_file, encoding="utf-8") as f:
191
+ content = f.read()
192
+ # 只取最后一个摘要块
193
+ blocks = content.split("---")
194
+ return blocks[-1].strip() if blocks else content.strip()
195
+ except Exception:
196
+ return ""
197
+
198
+ # ── 工具调用 Hook(可选)─────────────────────────────────────────────
199
+
200
+ def on_tool_call(
201
+ self,
202
+ session_id: str | None,
203
+ tool_name: str,
204
+ tool_input: dict,
205
+ tool_output: str,
206
+ ):
207
+ """
208
+ 每次工具调用后自动记录(可选,需要 clawbot 支持 post_tool_call hook)。
209
+
210
+ Args:
211
+ session_id: 当前会话 ID
212
+ tool_name: 工具名称
213
+ tool_input: 工具输入参数
214
+ tool_output: 工具输出(截断到前200字)
215
+ """
216
+ sid = session_id or self._session_id
217
+ if not sid or not LOG_SCRIPT.exists():
218
+ return
219
+
220
+ summary = f"工具调用: {tool_name}"
221
+ detail = {
222
+ "tool": tool_name,
223
+ "input": str(tool_input)[:200],
224
+ "output_preview": tool_output[:200] if tool_output else "",
225
+ }
226
+
227
+ try:
228
+ subprocess.run(
229
+ [
230
+ "python3", str(LOG_SCRIPT),
231
+ "--session", sid,
232
+ "--type", "tool_call",
233
+ "--summary", summary,
234
+ "--detail", json.dumps(detail, ensure_ascii=False),
235
+ "--tags", f"tool:{tool_name},auto",
236
+ ],
237
+ capture_output=True,
238
+ timeout=5,
239
+ errors="replace",
240
+ )
241
+ except Exception:
242
+ pass
243
+
244
+ # ── 会话管理 ───────────────────────────────────────────────────────
245
+
246
+ def get_session_id(self) -> str | None:
247
+ """获取当前 session_id"""
248
+ return self._session_id
249
+
250
+ def set_session_id(self, session_id: str):
251
+ """手动设置 session_id(clawbot 恢复会话时调用)"""
252
+ self._session_id = session_id
253
+
254
+ def clear(self):
255
+ """清除当前会话(开始新会话时调用)"""
256
+ self._session_id = None