ultra-memory 3.0.3 → 3.0.5

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/SKILL.md CHANGED
@@ -1,235 +1,283 @@
1
1
  ---
2
2
  name: ultra-memory
3
3
  description: >
4
- ultra-memory AI Agent 的超长会话记忆系统。
5
- 【必须触发-中文】用户说:记住、别忘了、记录一下、上次我们做了什么、帮我回忆、继续上次、从上次继续、不要忘记、上次、跨会话、继续昨天、还有印象吗、我们上次讨论过、之前那个是怎么写的、还记得吗、这次不要忘、帮我追踪、帮我记着
6
- 【必须触发-英文】用户说:remember this、don't forget、recall、what did we do、pick up where we left off、continue from last time、memory、keep track、log this、what was that、remind me、context lost、continue from yesterday、from last session
7
- 【隐式触发】用户描述了一个持续性任务(开发某项目、处理某数据集、持续性工作),即使未说"记住"也必须初始化记忆;对话操作数超过30条;用户说"继续昨天的任务"/"接着上次做"
8
- 【不触发】单次问答("帮我写个正则"、"查一下天气");纯代码补全、文件格式转换等无状态操作;用户已在 context 中能找到所需信息;用户明确说"不用记录"/"just this once"
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
+ 【隐式触发-A+B】同时满足以下两条时触发:(A)消息含持续性任务动词:开发、实现、处理、完成、构建、develop、implement、create、fix;(B)消息中包含项目名词(专有名词/文件名/系统名)
8
+ 【不触发】用户只问一个问题无后续;单次咨询无操作步骤;说"随便聊聊"、"just chatting";明确说"不用记录"
9
9
  ---
10
10
 
11
- # Ultra Memory — 超长会话记忆
11
+ # Ultra Memory — 多模型记忆操作手册
12
12
 
13
13
  AI Agent 的操作记忆系统,每次操作后记录,跨会话持久化,可检索可进化。
14
14
 
15
15
  ## 前置说明
16
16
 
17
- 所有脚本已存在于 `~/.ultra-memory/scripts/` 或技能目录下:
18
- - `init.py` — 初始化会话
19
- - `log_op.py` — 记录操作
20
- - `recall.py` — 检索记忆
21
- - `summarize.py` — 压缩摘要
22
- - `restore.py` — 恢复会话
23
- - `extract_entities.py` — 提取实体
17
+ 脚本已全部存在于 `$SKILL_DIR/scripts/`,直接调用,不需要生成,不需要理解内部架构。
24
18
 
25
- 存储根目录:`~/.ultra-memory/`(可配置环境变量 `ULTRA_MEMORY_HOME`)
26
- 不需要理解内部架构,只需按步骤调用脚本。
19
+ - `ULTRA_MEMORY_HOME` 默认值为 `~/.ultra-memory/`
20
+ - `SKILL_DIR` 由 clawbot 注入,如未定义则用 `~/.openclaw/workspace/skills/ultra-memory/`
27
21
 
28
22
  ---
29
23
 
30
24
  ## 步骤一:会话初始化
31
25
 
32
- ### 触发时机
26
+ ### 触发条件
33
27
 
34
- 首次与用户对话时,或用户提到记忆相关触发词时,立即执行。
28
+ 满足 frontmatter 中任意触发规则时,第一件事执行此步骤。
35
29
 
36
30
  ### 执行命令
37
31
 
38
32
  ```bash
39
- python3 <skill_dir>/scripts/init.py --project <项目名>
33
+ python3 $SKILL_DIR/scripts/init.py --project <项目名> --resume
40
34
  ```
41
35
 
42
36
  **参数说明:**
43
- - `--project`:项目名称,用于跨会话分组。不填默认为 `default`。
37
+ - `--project`:项目名称。从用户最近一条消息中提取最显著的名词作为值;无法提取则用 `default`。
38
+ - `--resume`:必须加此参数,让脚本尝试恢复同项目的最近会话。
44
39
 
45
- ### 期望输出
40
+ ### 成功标志
46
41
 
47
- 输出包含 `MEMORY_READY` 字样,表示会话初始化成功。同时输出 `session_id:`,记录该会话 ID。
42
+ | 输出中的字符串 | 模型必须执行的动作 |
43
+ |-------------|----------------|
44
+ | `MEMORY_READY` | 确认初始化成功 |
45
+ | `session_id: sess_xxxxx` | 从该行冒号后提取值,保存为 `SESSION_ID` |
48
46
 
49
- ### 告知用户
47
+ ### 有上次会话摘要时
50
48
 
51
- 初始化成功后,立即告知用户:
52
- > "记忆系统已就绪(session_id: xxx),开始记录本次操作。"
49
+ 将摘要内容告知用户,并询问:"要从这里继续吗?"
53
50
 
54
51
  ---
55
52
 
56
53
  ## 步骤二:操作记录
57
54
 
58
- **每次**以下任意事件发生时,立即调用 `log_op.py` 追加写入 ops.jsonl。
55
+ ### 触发条件
59
56
 
60
- ### 操作类型与命令对照表
57
+ **每次**用户与 AI 之间发生以下任意事件后,立即调用:
61
58
 
62
- | 事件 | op_type | 命令示例 |
63
- |------|---------|---------|
64
- | 执行了 shell 命令 | `bash_exec` | `python3 log_op.py --session <id> --type bash_exec --summary "执行了..." --detail '{"cmd":"...","exit_code":0}'` |
65
- | 创建或修改了文件 | `file_write` | `python3 log_op.py --session <id> --type file_write --summary "创建了..." --detail '{"path":"..."}'` |
66
- | 读取了文件 | `file_read` | `python3 log_op.py --session <id> --type file_read --summary "读取了..." --detail '{"path":"..."}'` |
67
- | 进行了重要推理 | `reasoning` | `python3 log_op.py --session <id> --type reasoning --summary "决定用..." --detail '{"confidence":0.9}'` |
68
- | 做出了关键决策 | `decision` | `python3 log_op.py --session <id> --type decision --summary "选用X方案" --detail '{"rationale":"..."}'` |
69
- | 发生了错误或回退 | `error` | `python3 log_op.py --session <id> --type error --summary "报错..." --detail '{"traceback":"..."}'` |
70
- | 用户给了新指令 | `user_instruction` | `python3 log_op.py --session <id> --type user_instruction --summary "用户要求..."` |
71
- | 某个目标已完成 | `milestone` | `python3 log_op.py --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` |
72
70
 
73
- ### 成功标志
71
+ ### 执行命令
74
72
 
75
- 命令返回 exit code 0 即为成功,输出形如:
76
- > `[ultra-memory] [1] bash_exec: 执行了 pip install pandas,安装成功`
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
 
84
109
  ## 步骤三:记忆检索
85
110
 
86
- ### 触发时机
111
+ ### 触发条件
87
112
 
88
- 用户问及"之前做了什么"、"上次那个函数在哪里"、"之前遇到过这个问题吗"等记忆相关问题时执行。
113
+ 用户问及以下任意问题时立即执行:
114
+ 之前、上次、那个函数、那个文件、那个命令、我们做过、怎么写的、什么名字、previously、last time、what was、how did we、that function、that file
89
115
 
90
116
  ### 执行命令
91
117
 
92
118
  ```bash
93
- 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
94
123
  ```
95
124
 
96
- ### 检索范围
97
-
98
- 按以下优先级检索:
99
- 1. 当前会话 `ops.jsonl`(精确匹配)
100
- 2. 当前会话 `summary.md`(摘要快速定位)
101
- 3. 跨会话 `knowledge_base.jsonl`(语义相似)
102
- 4. 跨会话 `user_profile.json`(偏好匹配)
103
-
104
125
  ### 结果展示
105
126
 
106
- 将检索结果直接展示给用户,格式如下:
107
- ```
108
- [RECALL] 找到 N 条相关记录:
109
-
110
- [ops #23 · 14:18] <操作摘要>
111
- [summary · 里程碑] <里程碑内容>
112
- [跨会话 · 日期 · 项目] <历史记录摘要>
113
- ```
127
+ 将脚本输出原样展示给用户,不加工。
114
128
 
115
129
  ---
116
130
 
117
131
  ## 步骤四:摘要压缩
118
132
 
119
- ### 触发时机
133
+ ### 触发条件
120
134
 
121
- 满足以下**任一条件**时,立即执行压缩:
135
+ 满足以下**任意条件**时,立即执行:
122
136
 
123
- 1. 操作日志达到 **50 条**
124
- 2. 距上次压缩超过 **30 分钟**
125
- 3. 用户明确说"总结一下"
126
- 4. 当前 context 占用超过 **60%**
137
+ 1. 步骤二中脚本输出了 `COMPRESS_SUGGESTED`
138
+ 2. 用户说:总结一下、目前进展、Summary、summarize
139
+ 3. 当前对话已超过 **30 轮**
127
140
 
128
141
  ### 执行命令
129
142
 
130
143
  ```bash
131
- python3 <skill_dir>/scripts/summarize.py --session <session_id> --force
144
+ python3 $SKILL_DIR/scripts/summarize.py --session $SESSION_ID
145
+ ```
146
+
147
+ 条数不足时强制执行:
148
+ ```bash
149
+ python3 $SKILL_DIR/scripts/summarize.py --session $SESSION_ID --force
132
150
  ```
133
151
 
134
- ### 期望输出
152
+ ### 成功标志
135
153
 
136
- 生成/更新 `~/.ultra-memory/sessions/<session_id>/summary.md`,输出包含:
137
- - 已完成里程碑([✅] 标记)
138
- - 当前进行中([ ] 标记)
139
- - 下一步建议([💡] 标记)
140
- - 操作统计([📊] 标记)
154
+ 命令返回 exit code 0,输出包含 `摘要压缩完成`。
141
155
 
142
156
  ---
143
157
 
144
158
  ## 步骤五:跨会话恢复
145
159
 
146
- ### 触发时机
160
+ ### 触发条件
147
161
 
148
- 用户说"继续上次"、"从上次继续"、"记得昨天那个项目吗"等时执行。也在新会话开始时(检测到 session_id 变化)自动执行。
162
+ 用户说以下任意表达时触发:
163
+ 继续昨天的、接着上次、从上次开始、继续之前的、continue from last、pick up where、resume、continue yesterday
149
164
 
150
165
  ### 执行命令
151
166
 
152
167
  ```bash
153
- python3 <skill_dir>/scripts/restore.py --project <项目名>
168
+ python3 $SKILL_DIR/scripts/restore.py --project <项目名>
154
169
  ```
155
170
 
156
- ### 告知用户
171
+ ### 从输出提取信息
157
172
 
158
- 恢复成功后,立即告知用户:
159
- > "我找到了上次会话的记录。上次我们做到了:[里程碑摘要]。当前状态:[进行中任务]。下一步建议:[具体建议]。"
173
+ | 输出中的字符串 | 模型必须执行的动作 |
174
+ |-------------|----------------|
175
+ | `SESSION_ID=sess_xxxxx` | 从等号后提取值,更新为新的 `SESSION_ID` |
176
+ | `TASK_STATUS=complete` | 告知用户"上次任务已完成" |
177
+ | `TASK_STATUS=in_progress` | 告知用户"上次任务进行中" |
178
+ | `💬 <自然语言总结>` | 直接说给用户 |
179
+ | `📌 <继续建议>` | 直接说给用户 |
160
180
 
161
- 如果找到了用户画像,也在回复中体现用户偏好。
181
+ 将恢复内容告知用户,询问:"要从这里继续吗?"
162
182
 
163
183
  ---
164
184
 
165
185
  ## 步骤六:记忆进化
166
186
 
167
- 记忆进化在每次操作中持续进行,不打断主任务。
187
+ 记忆进化在操作间隙进行,不打断主任务。
188
+
189
+ ### 6A:用户画像积累
168
190
 
169
- ### 6.1 用户画像更新
191
+ **触发条件(满足任意一条,立即更新):**
192
+
193
+ 1. 用户纠正了 AI 生成的代码风格
194
+ 2. 用户在两个方案中选择了其中一个
195
+ 3. 用户说出自己的技术栈(含"我用"、"我们用"、"我们的项目用"、"we use"、"our stack")
196
+ 4. 用户表示某种工作方式更顺手
197
+ 5. 用户明确描述了自己的偏好
198
+
199
+ **执行方式 — 方式 A(MCP 工具,推荐):**
170
200
 
171
- **触发时机:**
172
- - 用户纠正了 AI 的代码风格或实现方式
173
- - 用户选择/拒绝了某个技术方案
174
- - 用户明确说出自己的技术栈或偏好
175
- - 用户表示某种工作流程更顺手
201
+ 调用 `memory_profile`,`action=update`,`updates` 填写观察到的偏好字段。
176
202
 
177
- **执行方式(二选一):**
203
+ **执行方式 — 方式 B(直接修改文件):**
178
204
 
179
- 方式 A — 使用 MCP 工具:
180
205
  ```bash
181
- python3 <skill_dir>/scripts/mcp-server.js # 通过 MCP 调用 memory_profile
206
+ # 文件路径:$ULTRA_MEMORY_HOME/semantic/user_profile.json
207
+ # 只更新观察到的字段,不覆盖已有字段,使用 JSON merge 方式
182
208
  ```
183
209
 
184
- 方式 B — 直接读写 JSON:
185
- ```
186
- 文件路径:~/.ultra-memory/semantic/user_profile.json
187
- ```
210
+ **user_profile.json 可更新字段:**
188
211
 
189
- **user_profile.json 格式:**
190
212
  ```json
191
213
  {
192
- "tech_stack": ["Python", "Vue3"],
193
- "work_style": {"prefers_concise_code": true},
194
- "projects": ["ai-data-qa"],
195
- "language": "zh-CN",
196
- "observed_patterns": ["倾向在实现前讨论方案"]
214
+ "tech_stack": ["观察到的技术栈"],
215
+ "language": "zh-CN 或 en",
216
+ "work_style": {
217
+ "confirm_before_implement": true,
218
+ "prefers_concise_code": true
219
+ },
220
+ "observed_patterns": ["倾向先确认方案再实现"]
197
221
  }
198
222
  ```
199
223
 
200
- ### 6.2 知识库写入
224
+ ### 6B:知识沉淀
201
225
 
202
- **触发时机:**
203
- - 解决了一个棘手的 bug(记录问题现象 + 解决方案)
204
- - 做出了重要的技术选型决策(记录选了什么、为什么、放弃了什么)
205
- - 发现了某个工具/库的使用技巧
206
- - 完成了一个可复用的代码模式
226
+ **触发条件(满足任意一条,立即写入知识库):**
227
+
228
+ 1. 解决了一个报错或 bug(记录问题现象 + 解决方案)
229
+ 2. 做出了技术选型决策(选了什么 + 为什么)
230
+ 3. 发现了某个工具或库的使用技巧
231
+ 4. 完成了一个可复用的代码模式
232
+
233
+ **文件路径:**
207
234
 
208
- **执行方式 — 方式 A(MCP 工具,推荐):**
209
- ```bash
210
- # 通过 memory_knowledge_add MCP 工具写入
211
- # 参数:title(必填,100字内)、content(必填,200字内)、project(可选)、tags(可选)
212
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` 可选。
213
242
 
214
243
  **执行方式 — 方式 B(直接追加):**
215
- 直接追加写入 `~/.ultra-memory/semantic/knowledge_base.jsonl`,每行一条 JSON。
244
+
245
+ 追加写入 `knowledge_base.jsonl`,每行一条 JSON,不覆盖。
216
246
 
217
247
  **knowledge_base.jsonl 格式:**
248
+
218
249
  ```json
219
- {"ts": "2026-04-07T10:00:00Z", "project": "项目名", "title": "简短标题", "content": "内容(200字内)", "tags": ["bug-fix", "python"]}
250
+ {"ts": "2026-04-07T10:00:00Z", "project": "项目名", "title": "20字内标题", "content": "200字内描述", "tags": ["tag1"]}
220
251
  ```
221
252
 
222
- ### 6.3 里程碑追踪
253
+ ### 6C:里程碑记录
223
254
 
224
- **触发时机:**
225
- - 用户说"好了"、"完成了"、"搞定了"、"done"、"finished"
226
- - 某个功能/模块通过了测试
227
- - 阶段任务全部完成,用户准备切换下一个子任务
255
+ **触发条件 — 用户说出以下任意表达,立即记录:**
228
256
 
229
- **执行方式:**
230
- 使用 `op_type=milestone` 调用 `log_op.py`(见步骤二表格)。
257
+ 中文:好了、完成了、搞定了、做完了、弄好了、可以了、没问题了、测试通过、上线了
231
258
 
232
- **作用:** 里程碑记录在 `summary.md` 中,恢复会话时优先展示,让用户快速找回状态。
259
+ 英文:done、finished、completed、it works、all good、passed、deployed、ready
260
+
261
+ **执行命令:**
262
+
263
+ ```bash
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
+ | `MEMORY_READY` | 确认成功,继续主任务 |
277
+ | `session_id: sess_xxxxx` | 保存为 SESSION_ID |
278
+ | `COMPRESS_SUGGESTED` | 执行 summarize.py --session $SESSION_ID |
279
+ | `SESSION_ID=sess_xxxxx` | 更新 SESSION_ID 为该值 |
280
+ | 非零退出码 | 静默跳过,继续主任务 |
233
281
 
234
282
  ---
235
283
 
@@ -237,48 +285,51 @@ python3 <skill_dir>/scripts/mcp-server.js # 通过 MCP 调用 memory_profile
237
285
 
238
286
  | 变量 | 默认值 | 说明 |
239
287
  |------|--------|------|
240
- | `ULTRA_MEMORY_HOME` | `~/.ultra-memory/` | 存储根目录 |
241
- | `ULTRA_MEMORY_SESSION` | 自动生成 | 当前会话 session_id |
288
+ | `ULTRA_MEMORY_HOME` | `~/.ultra-memory/` | 记忆存储根目录 |
289
+ | `SKILL_DIR` | clawbot 注入 | skill 安装目录,未定义时用 `~/.openclaw/workspace/skills/ultra-memory/` |
290
+ | `SESSION_ID` | init.py 执行后自动生成 | 当前会话 ID,保存后供后续所有命令使用 |
242
291
 
243
292
  ---
244
293
 
245
294
  ## 异常处理表
246
295
 
247
- | 异常情况 | 处理方式 |
248
- |---------|---------|
249
- | 脚本执行失败(exit code != 0) | 静默跳过,继续主任务。记忆功能失败**不阻塞**用户实际需求 |
250
- | 脚本超时(>15s) | 静默跳过,不重试 |
251
- | 存储目录无写入权限 | 静默跳过,尝试写入内存缓冲区,下次重试 |
252
- | 脚本文件不存在 | 静默跳过,退化为在当前 context 中手动维护摘要 |
296
+ | 情况 | 处理方式 |
297
+ |------|---------|
298
+ | init.py 找不到上次会话 | 正常,作为新会话继续 |
299
+ | log_op.py 报会话不存在 | 重新执行 init.py,再记录 |
300
+ | SESSION_ID 丢失 | 执行 init.py --project <名> --resume 尝试恢复 |
301
+ | 任意脚本非零退出码 | **静默跳过**,不中断主任务 |
302
+ | summarize.py 条数不足 | 加 --force 参数重新执行 |
253
303
  | 用户明确说"不用记录" | 立即停止记录,后续操作不再调用 log_op.py |
254
- | 文件被占用无法追加 | 自动重试 1 次,仍失败则静默跳过 |
304
+
305
+ **最重要原则:记忆功能失败不能影响主任务。静默处理,不打印错误。**
255
306
 
256
307
  ---
257
308
 
258
309
  ## 完整执行流程
259
310
 
260
311
  ```
261
- 用户发起对话
262
-
263
- ├─ 首次对话或听到记忆触发词?
264
- └─ 步骤一:init.py → 告知用户"记忆就绪"
265
-
266
- ├─ 用户说记忆相关问题?
267
- │ └─ 是 → 步骤三:recall.py → 展示检索结果
268
-
269
- └─ 每次用户与 AI 交互后:
270
-
271
- ├─ 操作数 % 10 == 0 且 context > 60%?
272
- │ └─ 是 → 步骤四:summarize.py
273
-
274
- ├─ 发现用户偏好 / 解决重要问题 / 完成里程碑?
275
- │ └─ 是 → 步骤六:进化(画像/知识库/里程碑)
276
-
277
- └─ 步骤二:log_op.py(记录本次操作)
312
+ 触发条件满足
313
+ └─ init.py --project <名> --resume
314
+ ├─ 提取 session_id → 保存为 SESSION_ID
315
+ └─ 有上次摘要告知用户,询问是否继续
316
+
317
+ 执行任务(每次操作后)
318
+ ├─ 执行操作
319
+ ├─ log_op.py --session $SESSION_ID --type <type> --summary "..."
320
+ ├─ 输出 COMPRESS_SUGGESTED → summarize.py --session $SESSION_ID
321
+ └─ 观察到用户偏好/完成知识点 → 执行步骤六
322
+
323
+ 用户问"之前做了什么"
324
+ └─ recall.py --session $SESSION_ID --query "关键词"
325
+ └─ 原样展示输出给用户
326
+
327
+ 用户说"继续上次"(新会话)
328
+ └─ restore.py --project <名>
329
+ ├─ 提取新 SESSION_ID
330
+ └─ 告知用户恢复内容,询问是否继续
278
331
  ```
279
332
 
280
333
  ---
281
334
 
282
- ## 进阶配置
283
-
284
- 详细配置项(过滤规则、LanceDB 向量检索升级、团队共享、安全注意事项等)见 `references/advanced-config.md`。
335
+ 进阶配置(过滤规则、LanceDB 向量检索升级、自动 hook 配置、安全注意事项等)见 `references/advanced-config.md`。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultra-memory",
3
- "version": "3.0.3",
3
+ "version": "3.0.5",
4
4
  "description": "超长会话记忆系统 — 5层记忆架构,零外部依赖,支持所有LLM平台(Claude/GPT/Gemini/Qwen等)",
5
5
  "keywords": [
6
6
  "ai",
@@ -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