TevinClaw
13 hours ago 8793cd0c1fba42e988879e0a15bef05fe4fd866a
新增:memory-md-learning 学习记录技能
2 files added
549 ■■■■■ changed files
workspace/skills/memory-md-learning/SKILL.md 166 ●●●●● patch | view | raw | blame | history
workspace/skills/memory-md-learning/scripts/learning.py 383 ●●●●● patch | view | raw | blame | history
workspace/skills/memory-md-learning/SKILL.md
New file
@@ -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 未更新
- 检查文件权限
- 确认学习脚本路径正确
### 长期记忆未存储
- 检查记忆系统是否正常工作
- 查看是否有错误日志
workspace/skills/memory-md-learning/scripts/learning.py
New file
@@ -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()