From 8793cd0c1fba42e988879e0a15bef05fe4fd866a Mon Sep 17 00:00:00 2001
From: TevinClaw <510129976@qq.com>
Date: Tue, 17 Mar 2026 12:10:41 +0800
Subject: [PATCH] 新增:memory-md-learning 学习记录技能

---
 workspace/skills/memory-md-learning/scripts/learning.py |  383 ++++++++++++++++++++++++++++++++++++++
 workspace/skills/memory-md-learning/SKILL.md            |  166 ++++++++++++++++
 2 files changed, 549 insertions(+), 0 deletions(-)

diff --git a/workspace/skills/memory-md-learning/SKILL.md b/workspace/skills/memory-md-learning/SKILL.md
new file mode 100644
index 0000000..45af82d
--- /dev/null
+++ b/workspace/skills/memory-md-learning/SKILL.md
@@ -0,0 +1,166 @@
+---
+name: memory-md-learning
+description: 学习模式技能,用于记录陷阱、教训和解决方案。支持自动触发(解决问题后)和用户主动触发(用户说"学习"时)。将学习内容分层存储到长期记忆,并在 MEMORY.md 中记录学习事件。
+---
+
+# memory-md-learning 技能
+
+用于记录和沉淀学习成果,将经验转化为可复用的知识。
+
+## 触发模式
+
+### 模式1:自动触发
+当 agent 翻过陷阱、学习了教训、解决了问题时,自动调用本技能。
+
+**典型场景**:
+- 修复了一个难以发现的 bug
+- 解决了配置冲突问题
+- 找到了更优的解决方案
+
+### 模式2:用户主动触发
+当用户说"这个问题学习一下"、"记录下来"或类似表达时,调用本技能。
+
+**触发关键词**:
+- "学习一下"
+- "记录下来"
+- "记住这个"
+- "总结经验"
+
+## 学习输出
+
+每次学习会产生三层记录:
+
+### 1. 技术层(长期记忆)
+存储到长期记忆,类别:**事实**,重要性:**≥ 0.8**
+
+格式:
+```
+陷阱:[现象描述]。原因:[根本原因分析]。修复:[解决方案]。预防:[如何避免]
+```
+
+示例:
+```
+陷阱:pip 安装包时出现 SSL 证书错误。原因:系统时间不正确导致证书验证失败。修复:同步系统时间后重试。预防:定期检查系统时间同步状态
+```
+
+### 2. 原则层(长期记忆)
+存储到长期记忆,类别:**决策**,重要性:**≥ 0.85**
+
+格式:
+```
+决策原则([标签]):[行为准则]。触发条件:[何时使用]。行动:[具体做什么]
+```
+
+示例:
+```
+决策原则(依赖安装):遇到 SSL 错误时先检查系统时间。触发条件:安装包出现证书相关错误时。行动:执行 ntpdate 或 timedatectl 同步时间后再重试
+```
+
+### 3. 学习事件(MEMORY.md)
+记录到 MEMORY.md 的【学习事件】区块:
+
+格式:
+```
+- YYYY-MM-DD HH:MM | 一句话概要 | 关键词1,关键词2
+```
+
+示例:
+```
+- 2026-03-17 11:30 | 学习 pip SSL 证书问题解决方案 | 学习,pip,SSL,故障排查
+```
+
+## 文件定位
+
+- **学习脚本**: `~/.openclaw/workspace/skills/memory-md-learning/scripts/learning.py`
+- **目标文件**: `~/.openclaw/workspace/MEMORY.md`
+
+## 使用方法
+
+### 命令行调用
+
+```bash
+# 记录学习
+python3 ~/.openclaw/workspace/skills/memory-md-learning/scripts/learning.py \
+  --trap "现象描述" \
+  --cause "根本原因" \
+  --fix "解决方案" \
+  --prevent "预防措施" \
+  --principle "行为准则" \
+  --trigger "触发条件" \
+  --action "具体行动" \
+  --tag "标签" \
+  --summary "一句话概要" \
+  --keywords "关键词1,关键词2"
+```
+
+### Python 调用
+
+```python
+from skills.memory_md_learning.scripts.learning import record_learning
+
+result = record_learning(
+    trap="现象描述",
+    cause="根本原因",
+    fix="解决方案",
+    prevent="预防措施",
+    principle="行为准则",
+    trigger="触发条件",
+    action="具体行动",
+    tag="标签",
+    summary="一句话概要",
+    keywords="关键词1,关键词2"
+)
+
+if result['success']:
+    print(f"学习记录完成")
+    print(f"  - 技术层记忆: {result['technical_memory_id']}")
+    print(f"  - 原则层记忆: {result['principle_memory_id']}")
+    print(f"  - 学习事件: {result['learning_event']}")
+    if result['recall_verified']:
+        print("  - 记忆召回验证: 通过")
+```
+
+## 学习事件区块
+
+本技能会在 MEMORY.md 中维护一个【学习事件】区块:
+
+```markdown
+## 📚 学习事件
+
+> 记录从陷阱和教训中学习的经验。
+> 所有学习记录永久保留,可使用 memory-md-archive 技能归档瘦身。
+
+- 2026-03-17 11:30 | 学习 pip SSL 证书问题解决方案 | 学习,pip,SSL,故障排查
+- 2026-03-17 14:00 | 掌握 LanceDB 向量索引配置 | 学习,LanceDB,向量数据库
+```
+
+## 验证机制
+
+每次学习记录后,会自动执行 `memory_recall` 验证:
+
+1. 使用技术层关键词搜索,确认能召回刚记录的技术层记忆
+2. 使用原则层关键词搜索,确认能召回刚记录的原则层记忆
+3. 如果召回失败,会记录警告信息
+
+## 最佳实践
+
+1. **即时记录**: 解决问题后立即学习,不要拖延
+2. **简洁准确**: 现象、原因、方案要描述清楚
+3. **可复用**: 原则层要抽象到可复用的程度
+4. **关键词**: 选择能代表问题本质的关键词
+5. **告知用户**: 记录完成后告知用户学习已完成
+
+## 故障排查
+
+### 记忆召回验证失败
+- 检查关键词是否足够代表性
+- 确认记忆存储是否成功
+- 可能需要等待向量索引更新
+
+### MEMORY.md 未更新
+- 检查文件权限
+- 确认学习脚本路径正确
+
+### 长期记忆未存储
+- 检查记忆系统是否正常工作
+- 查看是否有错误日志
diff --git a/workspace/skills/memory-md-learning/scripts/learning.py b/workspace/skills/memory-md-learning/scripts/learning.py
new file mode 100755
index 0000000..bdeaab9
--- /dev/null
+++ b/workspace/skills/memory-md-learning/scripts/learning.py
@@ -0,0 +1,383 @@
+#!/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 and stripped.startswith('#'):
+            in_learning = True
+            continue
+        elif stripped.startswith('#') and in_learning:
+            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': '学习事件已存在'}
+    
+    # 找到学习事件区块并插入新事件
+    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} --:-- | 暂无记录 | --
+
+---
+
+*文件大小: ~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()

--
Gitblit v1.9.1