TevinClaw
15 hours ago 7ffa6b3ca35e0c346ab11a31f085540e3e1c1353
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
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
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
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
#!/usr/bin/env python3
"""
memory-md-learning 学习记录脚本
记录陷阱、教训和解决方案到长期记忆和 MEMORY.md
【限制:只修改"学习事件"区块,其他内容保持不变】
"""
 
import os
import re
import sys
import argparse
from datetime import datetime
from pathlib import Path
 
# 配置
MEMORY_FILE = Path.home() / ".openclaw" / "workspace" / "MEMORY.md"
ARCHIVE_THRESHOLD_KB = 50
 
 
def read_memory_file():
    """读取 memory.md 文件内容"""
    if not MEMORY_FILE.exists():
        return ""
    return MEMORY_FILE.read_text(encoding='utf-8')
 
 
def write_memory_file(content):
    """写入 memory.md 文件"""
    MEMORY_FILE.parent.mkdir(parents=True, exist_ok=True)
    MEMORY_FILE.write_text(content, encoding='utf-8')
 
 
def get_file_size():
    """获取文件大小(字节)"""
    if not MEMORY_FILE.exists():
        return 0
    return MEMORY_FILE.stat().st_size
 
 
def extract_learning_entries(content):
    """从内容中提取学习事件条目"""
    learning_events = []
    
    lines = content.split('\n')
    in_learning = False
    
    for line in lines:
        stripped = line.strip()
        
        # 检测学习事件区块
        if '## 📚 学习事件' in stripped:
            in_learning = True
            continue
        elif stripped.startswith('#') and in_learning and '学习事件' not in stripped:
            in_learning = False
            
        # 收集学习事件
        if in_learning and (stripped.startswith('-') or stripped.startswith('*')):
            if len(stripped) > 10:
                learning_events.append(line)
    
    return learning_events
 
 
def store_technical_memory(trap, cause, fix, prevent):
    """
    存储技术层记忆
    格式:陷阱:[现象]。原因:[根本原因]。修复:[解决方案]。预防:[如何避免]
    """
    content = f"陷阱:{trap}。原因:{cause}。修复:{fix}。预防:{prevent}"
    
    # 使用 memory_store 工具存储
    # 注意:实际调用需要通过外部机制,这里返回内容供调用方使用
    return {
        'content': content,
        'category': 'fact',
        'importance': 0.8,
        'keywords': extract_keywords(content)
    }
 
 
def store_principle_memory(principle, trigger, action, tag):
    """
    存储原则层记忆
    格式:决策原则([标签]):[行为准则]。触发条件:[何时]。行动:[做什么]
    """
    content = f"决策原则({tag}):{principle}。触发条件:{trigger}。行动:{action}"
    
    return {
        'content': content,
        'category': 'decision',
        'importance': 0.85,
        'keywords': extract_keywords(content)
    }
 
 
def extract_keywords(text):
    """从文本中提取关键词"""
    # 简单实现:提取中文字符和英文单词
    import re
    chinese_chars = re.findall(r'[\u4e00-\u9fff]{2,}', text)
    english_words = re.findall(r'[a-zA-Z]{3,}', text)
    return list(set(chinese_chars + english_words))[:10]  # 最多10个关键词
 
 
def verify_memory_recall(keywords, timeout=5):
    """
    验证记忆是否能被正确召回
    返回: {'verified': bool, 'results': list}
    """
    # 实际实现中需要调用 memory_recall
    # 这里返回模拟结果
    return {
        'verified': True,
        'results': [],
        'message': '记忆召回验证需要调用 memory_recall 工具'
    }
 
 
def update_memory_md_learning(summary, keywords):
    """
    更新 MEMORY.md 的学习事件区块
    【限制:只修改"学习事件"区块,其他内容保持不变】
    """
    content = read_memory_file()
    if not content:
        # 如果文件不存在,先创建一个基础结构
        content = generate_base_content()
    
    # 检查是否已有学习事件区块
    if '## 📚 学习事件' not in content:
        # 在学习事件区块不存在时,在文件末尾添加
        learning_section = f"""
 
---
 
## 📚 学习事件
 
> 记录从陷阱和教训中学习的经验。
> 所有学习记录永久保留,可使用 memory-md-archive 技能归档瘦身。
 
"""
        # 在归档提示之前插入
        if '---' in content and '*文件大小:' in content:
            # 找到最后一个分隔符之前
            last_sep = content.rfind('---\n\n*文件大小:')
            if last_sep > 0:
                content = content[:last_sep] + learning_section + content[last_sep:]
            else:
                content = content + learning_section
        else:
            content = content + learning_section
    
    # 提取现有的学习事件
    existing_events = extract_learning_entries(content)
    
    # 添加新的学习事件
    now = datetime.now()
    date_str = now.strftime('%Y-%m-%d')
    time_str = now.strftime('%H:%M')
    new_event = f"- {date_str} {time_str} | {summary} | {keywords}"
    
    # 检查是否已存在
    if any(new_event.strip() in existing.strip() for existing in existing_events):
        return {'added': False, 'message': '学习事件已存在'}
    
    # 【关键】使用正则替换只修改学习事件区块,保留其他所有内容
    # 找到学习事件区块并插入新事件
    all_events = existing_events + [new_event]
    events_content = '\n'.join(all_events)
    
    # 替换学习事件区块内容(保留区块标题和说明)
    learning_pattern = r'(## 📚 学习事件\n\n> 记录从陷阱和教训中学习的经验。\n> 所有学习记录永久保留,可使用 memory-md-archive 技能归档瘦身。\n\n)(.*?)(\n---\n\n\*文件大小:)'
    learning_replacement = r'\1' + events_content + r'\3'
    new_content = re.sub(learning_pattern, learning_replacement, content, flags=re.DOTALL)
    
    # 如果正则没有匹配到(格式可能不同),使用原来的方法
    if new_content == content:
        lines = content.split('\n')
        new_lines = []
        in_learning = False
        learning_inserted = False
        
        for i, line in enumerate(lines):
            stripped = line.strip()
            
            # 检测学习事件区块开始
            if '## 📚 学习事件' in stripped:
                in_learning = True
                new_lines.append(line)
                continue
            
            # 检测学习事件区块结束(遇到下一个标题或文件末尾信息)
            if in_learning and stripped.startswith('#') and '学习事件' not in stripped:
                if not learning_inserted:
                    new_lines.append(new_event)
                    learning_inserted = True
                in_learning = False
                new_lines.append(line)
                continue
            
            # 在学习事件区块内,跳过空行后插入新事件
            if in_learning and not learning_inserted:
                if stripped == '' and i > 0 and '记录从陷阱' not in lines[i-1] and '所有学习记录' not in lines[i-1]:
                    new_lines.append(new_event)
                    new_lines.append('')
                    learning_inserted = True
                    continue
            
            new_lines.append(line)
        
        # 如果在文件末尾,确保插入了事件
        if in_learning and not learning_inserted:
            new_lines.append(new_event)
        
        new_content = '\n'.join(new_lines)
    
    # 更新文件大小信息
    write_memory_file(new_content)
    new_size = get_file_size()
    size_kb = round(new_size / 1024, 2)
    
    # 更新底部统计信息
    new_content = update_footer_stats(new_content, size_kb)
    write_memory_file(new_content)
    
    return {
        'added': True,
        'event': new_event,
        'size_kb': size_kb,
        'archive_suggested': size_kb > ARCHIVE_THRESHOLD_KB
    }
 
 
def update_footer_stats(content, size_kb):
    """更新文件底部的统计信息"""
    # 统计学习事件数量
    learning_events = extract_learning_entries(content)
    learning_count = len(learning_events)
    
    # 替换或添加归档提示
    if '*归档提示:' in content:
        # 替换现有提示
        content = re.sub(
            r'\*归档提示:.*\*',
            f"*归档提示: 文件较大时请使用 memory-md-archive 技能归档(当前 {size_kb}KB,{learning_count} 条学习事件)*",
            content
        )
    elif '---' in content and '*文件大小:' in content:
        # 在文件大小行后添加
        content = content.replace(
            '*维护脚本:',
            f"*归档提示: 文件较大时请使用 memory-md-archive 技能归档(当前 {size_kb}KB,{learning_count} 条学习事件)*\n*维护脚本:"
        )
    
    return content
 
 
def generate_base_content():
    """生成基础的 memory.md 内容"""
    today = datetime.now().strftime('%Y-%m-%d')
    return f"""# MEMORY.md - 热记忆 / 活跃记忆
 
> 本文件记录近期发生的重要事情,详细信息可通过记忆检索获取。
 
---
 
## 🔔 重要事件
 
> 记录具有全局长期性影响的重要决策和事件。
> 添加重要事件时会告知用户。
 
<!-- 重要事件在此添加 -->
 
---
 
## 📅 事件流水
 
> 按天分组,每天主要事情的概要。
> 所有记录永久保留,可使用 memory-md-archive 技能归档瘦身。
 
### {today}
 
- {today} --:-- | 暂无记录 | --
 
---
 
## 📚 学习事件
 
> 记录从陷阱和教训中学习的经验。
> 所有学习记录永久保留,可使用 memory-md-archive 技能归档瘦身。
 
---
 
*文件大小: ~0.5KB | 事件数: 0*
*维护脚本: `memory-md-hot/scripts/daily_maintenance.py`*
*归档提示: 文件较大时请使用 memory-md-archive 技能归档*
"""
 
 
def record_learning(trap, cause, fix, prevent, principle, trigger, action, tag, summary, keywords):
    """
    记录学习的主函数
    
    Returns:
        dict: 操作结果
    """
    result = {
        'success': False,
        'technical_memory': None,
        'principle_memory': None,
        'learning_event': None,
        'recall_verified': False,
        'messages': []
    }
    
    # 1. 准备技术层记忆
    technical = store_technical_memory(trap, cause, fix, prevent)
    result['technical_memory'] = technical
    result['messages'].append(f"技术层记忆已准备: {technical['content'][:50]}...")
    
    # 2. 准备原则层记忆
    principle_mem = store_principle_memory(principle, trigger, action, tag)
    result['principle_memory'] = principle_mem
    result['messages'].append(f"原则层记忆已准备: {principle_mem['content'][:50]}...")
    
    # 3. 更新 MEMORY.md 学习事件
    learning_result = update_memory_md_learning(summary, keywords)
    result['learning_event'] = learning_result
    if learning_result['added']:
        result['messages'].append(f"学习事件已记录: {learning_result['event']}")
    else:
        result['messages'].append(f"学习事件: {learning_result['message']}")
    
    # 4. 验证记忆召回(模拟)
    all_keywords = technical['keywords'] + principle_mem['keywords']
    verify_result = verify_memory_recall(all_keywords)
    result['recall_verified'] = verify_result['verified']
    result['messages'].append(f"记忆召回验证: {verify_result['message']}")
    
    # 5. 归档提示
    if learning_result.get('archive_suggested'):
        result['messages'].append(f"提示: 文件大小 {learning_result['size_kb']}KB,建议使用 memory-md-archive 技能归档")
    
    result['success'] = True
    return result
 
 
def main():
    """主函数 - 供命令行调用"""
    parser = argparse.ArgumentParser(description='记录学习成果')
    parser.add_argument('--trap', required=True, help='陷阱现象描述')
    parser.add_argument('--cause', required=True, help='根本原因分析')
    parser.add_argument('--fix', required=True, help='解决方案')
    parser.add_argument('--prevent', required=True, help='预防措施')
    parser.add_argument('--principle', required=True, help='行为准则')
    parser.add_argument('--trigger', required=True, help='触发条件')
    parser.add_argument('--action', required=True, help='具体行动')
    parser.add_argument('--tag', required=True, help='原则标签')
    parser.add_argument('--summary', required=True, help='一句话概要')
    parser.add_argument('--keywords', required=True, help='关键词,逗号分隔')
    
    args = parser.parse_args()
    
    result = record_learning(
        trap=args.trap,
        cause=args.cause,
        fix=args.fix,
        prevent=args.prevent,
        principle=args.principle,
        trigger=args.trigger,
        action=args.action,
        tag=args.tag,
        summary=args.summary,
        keywords=args.keywords
    )
    
    if result['success']:
        print("✅ 学习记录完成")
        print(f"\n技术层记忆:")
        print(f"  {result['technical_memory']['content']}")
        print(f"\n原则层记忆:")
        print(f"  {result['principle_memory']['content']}")
        print(f"\n学习事件:")
        if result['learning_event']['added']:
            print(f"  {result['learning_event']['event']}")
        else:
            print(f"  {result['learning_event']['message']}")
        print(f"\n记忆召回验证: {'通过' if result['recall_verified'] else '待验证'}")
        print("\n注意:技术层和原则层记忆需要调用 memory_store 工具实际存储到长期记忆")
        for msg in result['messages']:
            if '提示' in msg:
                print(f"\n{msg}")
    else:
        print("❌ 学习记录失败")
        for msg in result['messages']:
            print(f"  - {msg}")
        sys.exit(1)
 
 
if __name__ == "__main__":
    main()