Skill: onlyloveher/learning-sys-v1 (version 1.0.0)

--- name: learning-system description: "AI 领域系统学习体系。管理知识图谱、深度学习笔记、实战复盘和关联网络。触发场景：学习计划、知识图谱更新、深度研究某个 AI 主题、实战复盘总结、调研后沉淀知识、每周学习回顾。当用户说'学了什么'、'总结一下'、'沉淀知识'、'复盘'、'更新图谱'、'深入研究'、'写笔记'、'学习回顾'、'review what I learned'、'update knowledge map'、'deep dive'、'recap'、'what did I learn' 时使用。当改完代码/读完论文/做完调研后需要提炼和归纳时使用。" argument-hint: "[--mode deep-dive|recap|review|health] [--topic name] [--quick]" --- # Learning System 将零散的资讯、调研、代码实战转化为体系化的 AI 领域专业知识。 ## 核心理念 **输入不等于学习。** 看了 100 篇推文不代表懂了推理优化。改了 3 个 MCP bug 不代表吃透了 MCP 协议。学习 = 输入 + 加工 + 关联 + 输出。 ## 模式选择 根据 `$ARGUMENTS` 或用户意图选择模式： | 参数 | 模式 | 说明 | |------|------|------| | `--mode deep-dive` | 深度研究 | 选题 → 研究 → 写笔记 → 更新图谱 | | `--mode recap` | 实战复盘 | 分析 PR/改动 → 提炼知识点 → 关联图谱 | | `--mode review` | 每周回顾 | 汇总本周 → 更新图谱 → 生成周报 | | `--mode health` | 健康检查 | 运行 `scripts/health_check.py` 输出报告 | | 无参数 | 自动判断 | 根据上下文推断最合适的模式 | 附加参数： - `--topic <name>`: 指定主题（deep-dive 模式） - `--quick`: 跳过确认节点，全自动执行 ## 文件结构 ``` notes/areas/ ├── ai-knowledge-map.md # 知识图谱（掌握程度标记） ├── deep-dives/ # 深度学习笔记 │ ├── mcp-tool-call-design.md │ └── ... └── weekly-reviews/ # 每周学习回顾 ├── 2026-W07.md └── ... ``` --- ## Mode: 深度研究 (deep-dive) Copy this checklist and check off items as you complete them: ### Deep Dive Progress: - [ ] Step 1: 选题 ⚠️ REQUIRED - [ ] 1.1 如果 `--topic` 已指定，直接使用 - [ ] 1.2 否则，检查最近 3 天的 memory 日志和 PR 记录 - [ ] 1.3 问自己：**哪个技术点是我刚接触但还没真正理解的？** - [ ] 1.4 问自己：**这个主题能串联哪些已有知识？**（越多越好） - [ ] 1.5 确认选题范围不要太宽（"推理优化"太大，"vLLM PagedAttention 实现"刚好） - [ ] Step 2: 确认选题 ⚠️ REQUIRED (除非 `--quick`) - [ ] 向用户确认：选题 + 预计关联的知识点 + 预计产出 - [ ] Step 3: 研究 - [ ] 3.1 Load `references/deep-dive-template.md` 获取笔记模板 - [ ] 3.2 查找相关源码、论文、文档 - [ ] 3.3 如果有对应的 AI/ML skill，按需加载参考 - [ ] Step 4: 写笔记 - [ ] 4.1 在 `notes/areas/deep-dives/` 创建笔记文件 - [ ] 4.2 问自己：**我能用自己的话向别人解释清楚吗？** 如果不能，说明还没真正理解 - [ ] 4.3 建立关联：`→ 关联: [主题](相对路径)` - [ ] Step 5: 更新知识图谱 - [ ] 5.1 Load `references/knowledge-map-rules.md` 获取升级标准 - [ ] 5.2 更新 `notes/areas/ai-knowledge-map.md` 中对应主题的掌握程度 - [ ] Step 6: 交付检查 - [ ] Load `references/quality-checklist.md` 逐项验证 --- ## Mode: 实战复盘 (recap) ### Recap Progress: - [ ] Step 1: 识别改动 ⚠️ REQUIRED - [ ] 1.1 确认要复盘的 PR/Issue/改动 - [ ] 1.2 问自己：**这次改动中，哪个技术点是我之前不知道的？** - [ ] 1.3 问自己：**如果下次遇到类似问题，我能直接解决吗？** - [ ] Step 2: 提炼知识点 - [ ] 2.1 Load `references/recap-template.md` 获取复盘模板 - [ ] 2.2 每个知识点关联到知识图谱的具体领域 - [ ] 2.3 问自己：**两个请求同时打到这段代码会怎样？**（如果涉及并发） - [ ] 2.4 问自己：**在检查权限和实际操作之间，状态有没有可能被改变？**（如果涉及安全） - [ ] Step 3: 写入日志 - [ ] 在当天的 `memory/YYYY-MM-DD.md` 中增加复盘 section - [ ] Step 4: 更新图谱（条件） - [ ] 如果有知识点升级，Load `references/knowledge-map-rules.md` 并更新 --- ## Mode: 每周回顾 (review) ### Weekly Review Progress: - [ ] Step 1: 收集本周输入 ⚠️ REQUIRED - [ ] 1.1 读取本周的 memory 日志（最近 7 天） - [ ] 1.2 检查本周新增/修改的深度笔记 - [ ] 1.3 检查本周的 PR 和代码改动 - [ ] Step 2: 评估学习深度 - [ ] 2.1 Load `references/knowledge-map-rules.md` - [ ] 2.2 对每个输入项判断：只是看了？理解了原理？有实战经验？ - [ ] 2.3 问自己：**这周我在 AI 领域变强了吗？哪里变强了？** - [ ] 2.4 问自己：**哪些输入转化成了真正的知识？** - [ ] Step 3: 更新知识图谱 - [ ] 确认变更列表 ⚠️ REQUIRED (除非 `--quick`) - [ ] 更新 `notes/areas/ai-knowledge-map.md` - [ ] Step 4: 生成周报 - [ ] Load `references/weekly-review-template.md` - [ ] 写入 `notes/areas/weekly-reviews/2026-Wxx.md` - [ ] Step 5: 发送摘要 - [ ] 通过飞书发送给用户 --- ## Mode: 健康检查 (health) ```bash python3 scripts/health_check.py ``` 输出知识图谱统计、深度笔记状态、本周活动量、改进建议。 --- ## Mode: Mastery Score (mastery) ```bash python3 scripts/mastery_score.py # 表格报告 python3 scripts/mastery_score.py --json # 附加 JSON 输出 ``` 自动计算每个知识图谱主题的掌握分数，基于： - **Recency（时间衰减）**: 指数衰减，半衰期 30 天。今天接触 = 1.0，30 天前 = 0.5，60 天前 = 0.25 - **Repetition（重复次数）**: 跨不同日期的接触次数累加 - **Depth（深度权重）**: deep-dive 笔记 ×3.0，PR/复盘 ×2.0，普通提及 ×1.0 输出包含：分数排名、建议升降级、衰减警告（60 天未接触）。 --- ## 关联网络 在深度笔记和复盘中主动建立关联。格式：`→ 关联: [主题](相对路径)` | 关联类型 | 示例 | |----------|------| | 技术关联 | vLLM → PagedAttention → KV Cache 管理 | | 实战关联 | gemini-cli OAuth PR → OAuth 2.1 协议 | | 对比关联 | Flash Attention vs PagedAttention | ## 与其他 skill 的关系 - **para-second-brain**: 学习笔记存在 PARA 的 areas/ 下，自动被 memory_search 索引 - **85 个 AI/ML skills**: 作为参考资料，深度学习时按需加载对应 skill - **openclaw-feeds / news-summary**: 资讯输入源，但不等于学习——需要加工和关联

path: README.md
content: # learning-system-skill

AI 领域系统学习体系 Skill，适用于 [OpenClaw](https://github.com/openclaw/openclaw) Agent。

将零散的资讯、调研、代码实战转化为体系化的 AI 领域专业知识。

## 核心理念

**输入不等于学习。** 看了 100 篇推文不代表懂了推理优化。改了 3 个 MCP bug 不代表吃透了 MCP 协议。

学习 = 输入 + 加工 + 关联 + 输出。

## 功能

- 📊 **AI 知识图谱** — 6 大领域、31+ 主题，三级掌握程度标记（🔴入门 🟡熟悉 🟢精通）
- 📝 **深度学习笔记** — 基于实战的主题深入研究，含标准化模板
- 🔄 **实战复盘** — 每次 PR/问题解决后提炼学习点
- 🔗 **关联网络** — 技术关联、实战关联、对比关联
- ⏰ **每周学习回顾** — 自动化周期性知识管理
- 🔧 **健康检查** — 分析知识图谱健康度，给出改进建议
- 🧮 **Mastery Score** — 基于 recency × repetition × depth 的自动掌握程度评分

## 4 种模式

| 命令 | 模式 | 说明 |
|------|------|------|
| `--mode deep-dive` | 深度研究 | 选题 → 研究 → 写笔记 → 更新图谱 |
| `--mode recap` | 实战复盘 | 分析 PR/改动 → 提炼知识点 → 关联图谱 |
| `--mode review` | 每周回顾 | 汇总本周 → 更新图谱 → 生成周报 |
| `--mode health` | 健康检查 | 运行脚本输出报告 |

附加参数：`--topic <name>`（指定主题）、`--quick`（跳过确认）

## Mastery Score 算法

自动扫描 memory 日志和深度笔记，计算每个主题的掌握分数：

```
score = Σ (weight × e^(-days_ago × ln2 / 30))
```

| 因子 | 说明 |
|------|------|
| **Recency** | 指数衰减，半衰期 30 天。今天 = 1.0，30 天前 = 0.5 |
| **Repetition** | 跨不同日期的接触次数累加 |
| **Depth** | deep-dive ×3.0，PR/复盘 ×2.0，普通提及 ×1.0 |

分数阈值：≥8.0 建议 🟢，≥3.0 建议 🟡，<3.0 建议 🔴

```bash
python3 scripts/mastery_score.py          # 表格报告
python3 scripts/mastery_score.py --json   # 附加 JSON 输出
```

输出示例：

```
| 主题              | 当前 | Score | 建议 | 接触次数 | 最近 | 变更 |
|-------------------|------|-------|------|----------|------|------|
| MCP 协议          | 🟢  |  6.32 | 🟡  |        4 |   4d | ⬇️ 考虑降级 |
| Agent 可观测性     | 🔴  |  3.76 | 🟡  |        4 |   0d | ⬆️ 建议升级 |
```

## 文件结构

```
learning-system/
├── SKILL.md                              # Skill 定义（工作流 checklist）
├── README.md
├── scripts/
│   ├── health_check.py                   # 健康检查
│   └── mastery_score.py                  # Mastery Score 计算
└── references/
    ├── deep-dive-template.md             # 深度笔记模板
    ├── recap-template.md                 # 实战复盘模板
    ├── knowledge-map-rules.md            # 图谱升级规则
    ├── quality-checklist.md              # 交付质量检查
    ├── weekly-review-template.md         # 周报模板
    └── weekly-review-guide.md            # 每周回顾指南
```

配合使用的笔记目录（需自行创建）：

```
notes/areas/
├── ai-knowledge-map.md               # 知识图谱
├── deep-dives/                        # 深度笔记
└── weekly-reviews/                    # 每周回顾
```

## 使用方式

### 1. 安装

```bash
cp -r learning-system ~/.openclaw/workspace/skills/
```

### 2. 初始化笔记目录

```bash
mkdir -p ~/.openclaw/workspace/notes/areas/{deep-dives,weekly-reviews}
```

### 3. 运行

```bash
# 健康检查
python3 scripts/health_check.py

# Mastery Score
python3 scripts/mastery_score.py
```

### 4. 配置每周回顾 Cron

在 OpenClaw 中配置 cron job，每周日晚自动触发学习回顾。

## 掌握程度标记

| 等级 | 标记 | 标准 |
|------|------|------|
| 入门 | 🔴 | 听说过，知道是什么 |
| 熟悉 | 🟡 | 读过源码或论文，能解释原理 |
| 精通 | 🟢 | 有实战经验，能独立设计和排错 |

## License

MIT


path: _meta.json
content: ownerId: kn75hbr5gmm1kgapn5n79sctz581rfzh
slug: learning-system-skill
version: 0.1.0
publishedAt: 1771937011576

path: references/deep-dive-template.md
content: # 深度笔记模板

## [主题名]

### 动机
为什么要深入这个主题？
- 关联的代码改动/调研/问题是什么？
- 问自己：**这个主题解决了什么实际问题？**

### 核心概念
用自己的话解释，不是复制文档。
- 问自己：**如果要向一个不了解这个领域的同事解释，我会怎么说？**
- 问自己：**这个概念和我已知的哪个概念最像？区别在哪？**

### 源码分析
关键代码路径、设计决策、trade-off。
- 入口在哪？核心数据结构是什么？
- 为什么选择这种实现方式？有什么替代方案？

### 实战关联
与自己的项目/贡献的关系。
- → 关联: [相关主题](相对路径)

### 开放问题
还没搞懂的、值得继续探索的。

### 参考
论文、源码链接、相关 skill。


path: references/knowledge-map-rules.md
content: # 知识图谱更新规则

## 掌握程度标记

| 等级 | 含义 | 标准 |
|------|------|------|
| 🔴 入门 | 听说过，知道是什么 | 能说出一句话定义 |
| 🟡 熟悉 | 读过源码或论文，能解释原理 | 能向别人讲清楚核心机制 |
| 🟢 精通 | 有实战经验，能独立设计和排错 | 有代码贡献或项目实战，踩过坑并解决 |

## 升级判断

升级前问自己：

### 🔴 → 🟡
- **我能不看资料向别人解释这个概念吗？**
- **我知道它的核心实现原理吗？**（不是"大概知道"，是能说出关键数据结构和算法）
- **我读过相关源码或论文的核心部分吗？**

### 🟡 → 🟢
- **我有真实的代码贡献或项目实战吗？**（PR、项目、实验）
- **我踩过坑并自己解决了吗？**
- **如果别人遇到这个领域的问题来问我，我能给出具体建议吗？**

## 降级情况

- 超过 3 个月没接触且无法回忆核心细节 → 考虑降级
- 技术发生重大变化，之前的理解已过时 → 重新评估

## 更新格式

在 `ai-knowledge-map.md` 的升级日志中记录：

```
| 日期 | 主题 | 变更 | 原因 |
| 2026-02-16 | MCP 协议 | 🔴→🟡 | 读完 spec + 提了 3 个相关 PR |
```


path: references/quality-checklist.md
content: # 交付质量检查清单

深度笔记或复盘完成后，逐项检查：

## 内容质量
- [ ] 核心概念用自己的话解释了，不是复制粘贴文档
- [ ] 有具体的源码分析或技术细节，不是泛泛而谈
- [ ] 包含"为什么"而不只是"是什么"（设计决策、trade-off）

## 关联完整性
- [ ] 至少建立了 1 个关联（`→ 关联: [主题](路径)`）
- [ ] 关联到了知识图谱中的具体领域
- [ ] 如果有对比关联（A vs B），说清了关键差异

## 实战价值
- [ ] 包含实战关联（与自己的项目/PR 的关系）
- [ ] 有"开放问题"或"深入方向"，指向下一步
- [ ] 如果是复盘，记录了踩坑和解决过程

## 知识图谱同步
- [ ] 相关主题的掌握程度已更新（如有变化）
- [ ] 升级日志已记录变更原因


path: references/recap-template.md
content: # 实战复盘模板

## 实战复盘: [项目名] [PR/Issue 编号]

### 改了什么
一句话描述改动。

### 技术背景
- 问自己：**这个问题的根因是什么？表面现象 vs 深层原因？**
- 问自己：**如果不修这个 bug，最坏情况会怎样？**

### 学到了什么
- 技术点 1（→ 关联知识图谱: [领域]）
- 技术点 2（→ 关联知识图谱: [领域]）

### 踩坑记录
- 问自己：**调试过程中走了哪些弯路？下次怎么避免？**
- 问自己：**有没有更好的排查方法？**

### 深入方向
如果要深入，应该看什么？（源码路径/论文/文档）

### 关联
- → 关联: [相关主题](相对路径)


path: references/weekly-review-guide.md
content: # 每周学习回顾指南

## 执行流程

### 1. 收集本周输入

```bash
# 查看本周的 memory 日志
ls memory/2026-02-*.md | tail -7

# 查看本周新增/修改的深度笔记
find notes/areas/deep-dives/ -mtime -7 -name "*.md"
```

从每日日志中提取：
- 调研主题和关键发现
- 代码改动和 PR
- 读过的论文/文档
- 遇到的技术问题

### 2. 评估学习深度

对每个输入项，判断：
- **只是看了** → 不算学习，不更新图谱
- **理解了原理** → 可以从 🔴 升到 🟡
- **有实战经验** → 可以从 🟡 升到 🟢

### 3. 更新知识图谱

编辑 `notes/areas/ai-knowledge-map.md`：
- 更新掌握程度标记
- 在升级日志中记录变更和原因
- 添加新发现的子领域

### 4. 生成周报

文件: `notes/areas/weekly-reviews/2026-Wxx.md`

```markdown
# 2026-Wxx 学习回顾

## 本周学了什么
- [主题1]: 从 X 升到 Y，因为 [具体原因]
- [主题2]: 新接触，标记为 🔴

## 本周产出
- [PR/文章/笔记列表]

## 知识图谱变更
| 主题 | 变更 | 原因 |
|------|------|------|

## 薄弱环节
- [还需要深入的领域]

## 下周计划
- 深度研究: [选 1-2 个主题]
- 实战目标: [计划贡献的项目/PR]
```

### 5. 发送摘要

通过飞书发送给青雲，包含：
- 本周学习亮点（2-3 句）
- 知识图谱变更数量
- 下周重点方向

## 质量标准

好的周报应该能回答：
1. 这周我在 AI 领域变强了吗？哪里变强了？
2. 哪些输入转化成了真正的知识？
3. 下周应该把精力放在哪里？


path: references/weekly-review-template.md
content: # 每周学习回顾模板

文件: `notes/areas/weekly-reviews/2026-Wxx.md`

```markdown
# 2026-Wxx 学习回顾

## 本周学了什么
- [主题1]: 从 X 升到 Y，因为 [具体原因]
- [主题2]: 新接触，标记为 🔴

## 本周产出
- PR / 文章 / 笔记列表

## 知识图谱变更
| 主题 | 变更 | 原因 |
|------|------|------|
| | | |

## 薄弱环节
- 还需要深入的领域

## 下周计划
- 深度研究: 选 1-2 个主题
- 实战目标: 计划贡献的项目/PR
```

## 质量标准

好的周报应该能回答：
1. 这周我在 AI 领域变强了吗？哪里变强了？
2. 哪些输入转化成了真正的知识？
3. 下周应该把精力放在哪里？


path: scripts/health_check.py
content: #!/usr/bin/env python3
"""
学习体系升级维护脚本
定期检查知识图谱的健康度，输出需要关注的领域
"""
import re
import sys
from pathlib import Path
from datetime import datetime, timedelta
from collections import Counter

KNOWLEDGE_MAP = Path.home() / ".openclaw/workspace/notes/areas/ai-knowledge-map.md"
DEEP_DIVES = Path.home() / ".openclaw/workspace/notes/areas/deep-dives"
WEEKLY_REVIEWS = Path.home() / ".openclaw/workspace/notes/areas/weekly-reviews"
MEMORY_DIR = Path.home() / ".openclaw/workspace/memory"

def parse_knowledge_map():
    """解析知识图谱，返回各领域掌握程度统计"""
    if not KNOWLEDGE_MAP.exists():
        return None
    
    content = KNOWLEDGE_MAP.read_text()
    stats = {"🔴": 0, "🟡": 0, "🟢": 0}
    topics = []
    
    for line in content.split("\n"):
        if "|" in line and any(emoji in line for emoji in ["🔴", "🟡", "🟢"]):
            parts = [p.strip() for p in line.split("|")]
            if len(parts) >= 4:
                topic = parts[1]
                level = "🔴" if "🔴" in parts[2] else "🟡" if "🟡" in parts[2] else "🟢"
                stats[level] += 1
                topics.append({"topic": topic, "level": level})
    
    return {"stats": stats, "topics": topics}

def check_deep_dives():
    """检查深度笔记状态"""
    if not DEEP_DIVES.exists():
        return {"count": 0, "recent": [], "stale": []}
    
    notes = list(DEEP_DIVES.glob("*.md"))
    now = datetime.now()
    recent = []
    stale = []
    
    for note in notes:
        mtime = datetime.fromtimestamp(note.stat().st_mtime)
        age_days = (now - mtime).days
        info = {"name": note.stem, "age_days": age_days}
        if age_days <= 7:
            recent.append(info)
        elif age_days > 30:
            stale.append(info)
    
    return {"count": len(notes), "recent": recent, "stale": stale}

def check_recent_activity(days=7):
    """检查最近的 memory 日志中的学习相关活动"""
    now = datetime.now()
    activity = {"prs": 0, "research": 0, "reviews": 0}
    
    for i in range(days):
        date = now - timedelta(days=i)
        log_file = MEMORY_DIR / f"{date.strftime('%Y-%m-%d')}.md"
        if log_file.exists():
            content = log_file.read_text()
            activity["prs"] += content.lower().count("pr #") + content.lower().count("pr#")
            activity["research"] += content.count("调研") + content.count("research")
            activity["reviews"] += content.count("复盘") + content.count("review")
    
    return activity

def generate_health_report():
    """生成学习体系健康报告"""
    km = parse_knowledge_map()
    dives = check_deep_dives()
    activity = check_recent_activity()
    
    report = []
    report.append("# 学习体系健康报告")
    report.append(f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M')}\n")
    
    if km:
        total = sum(km["stats"].values())
        report.append("## 知识图谱")
        report.append(f"- 总主题数: {total}")
        report.append(f"- 🔴 入门: {km['stats']['🔴']} ({km['stats']['🔴']*100//max(total,1)}%)")
        report.append(f"- 🟡 熟悉: {km['stats']['🟡']} ({km['stats']['🟡']*100//max(total,1)}%)")
        report.append(f"- 🟢 精通: {km['stats']['🟢']} ({km['stats']['🟢']*100//max(total,1)}%)")
        
        beginner_topics = [t["topic"] for t in km["topics"] if t["level"] == "🔴"]
        if beginner_topics:
            report.append(f"\n待深入 (🔴): {', '.join(beginner_topics[:5])}")
    
    report.append(f"\n## 深度笔记")
    report.append(f"- 总数: {dives['count']}")
    report.append(f"- 本周新增: {len(dives['recent'])}")
    if dives["stale"]:
        report.append(f"- 超过30天未更新: {len(dives['stale'])}")
    
    report.append(f"\n## 本周活动")
    report.append(f"- PR 相关: {activity['prs']}")
    report.append(f"- 调研相关: {activity['research']}")
    report.append(f"- 复盘相关: {activity['reviews']}")
    
    # 建议
    report.append(f"\n## 建议")
    if km and km["stats"]["🔴"] > km["stats"]["🟡"]:
        report.append("- ⚠️ 入门级主题过多，建议本周选 1-2 个深入研究")
    if dives["count"] == 0 or len(dives["recent"]) == 0:
        report.append("- ⚠️ 本周无深度笔记，建议从最近的调研/PR 中选题")
    if activity["reviews"] == 0:
        report.append("- ⚠️ 本周无实战复盘，建议对最近的 PR 做复盘总结")
    
    return "\n".join(report)

if __name__ == "__main__":
    print(generate_health_report())


path: scripts/mastery_score.py
content: #!/usr/bin/env python3
"""
Mastery Score Calculator for Learning System Skill

Calculates mastery scores by weighting:
- Recency: how recently you engaged with a topic (exponential decay)
- Repetition: how many times you've engaged across different sessions
- Depth: deep-dive notes count more than passing mentions

Score formula:
  raw_score = sum(weight_i * decay(days_since_i))
  where weight_i = 3.0 for deep-dive, 2.0 for PR/recap, 1.0 for mention

Decay function: exp(-days / half_life), half_life = 30 days

Output: ranked topics with scores, suggested level changes, and decay warnings.
"""
import re
import sys
import json
import math
from pathlib import Path
from datetime import datetime, timedelta
from collections import defaultdict

# --- Config ---
WORKSPACE = Path.home() / ".openclaw/workspace"
KNOWLEDGE_MAP = WORKSPACE / "notes/areas/ai-knowledge-map.md"
DEEP_DIVES = WORKSPACE / "notes/areas/deep-dives"
MEMORY_DIR = WORKSPACE / "memory"
HALF_LIFE_DAYS = 30  # score halves every 30 days
WEIGHTS = {"deep-dive": 3.0, "pr": 2.0, "recap": 2.0, "mention": 1.0}

# Thresholds for level suggestions
SCORE_THRESHOLDS = {
    "green": 8.0,   # 🟢 精通: high score = frequent + recent + deep
    "yellow": 3.0,  # 🟡 熟悉: moderate engagement
    # below yellow = 🔴 入门
}

DECAY_WARNING_DAYS = 60  # warn if no engagement in 60 days


def decay(days_ago: float) -> float:
    """Exponential decay: returns 1.0 for today, 0.5 at half_life days."""
    return math.exp(-days_ago * math.log(2) / HALF_LIFE_DAYS)


def parse_knowledge_map():
    """Parse current knowledge map, return {topic: level}."""
    if not KNOWLEDGE_MAP.exists():
        return {}
    topics = {}
    # Skip lines: headers, separators, dates (YYYY-MM-DD)
    skip_re = re.compile(r"^(\d{4}-\d{2}-\d{2}|主题|---)")
    for line in KNOWLEDGE_MAP.read_text().split("\n"):
        if "|" in line and any(e in line for e in ["🔴", "🟡", "🟢"]):
            parts = [p.strip() for p in line.split("|")]
            if len(parts) >= 4:
                topic = parts[1].strip()
                level = "🟢" if "🟢" in parts[2] else "🟡" if "🟡" in parts[2] else "🔴"
                if topic and not skip_re.match(topic):
                    topics[topic] = level
    return topics


def scan_deep_dives():
    """Scan deep-dive notes, return {topic_keyword: [dates]}."""
    engagements = defaultdict(list)
    if not DEEP_DIVES.exists():
        return engagements
    for note in DEEP_DIVES.glob("*.md"):
        mtime = datetime.fromtimestamp(note.stat().st_mtime)
        # Use filename as topic keyword (e.g., mcp-tool-call-design -> mcp, tool, call, design)
        keywords = note.stem.lower().replace("-", " ").replace("_", " ").split()
        content = note.read_text().lower()
        for kw in keywords:
            if len(kw) > 2:  # skip tiny words
                engagements[kw].append({"date": mtime, "type": "deep-dive"})
        # Also extract topic from first heading
        heading = re.search(r"^#\s+(.+)", note.read_text(), re.MULTILINE)
        if heading:
            for word in heading.group(1).lower().split():
                word = re.sub(r"[^\w]", "", word)
                if len(word) > 2:
                    engagements[word].append({"date": mtime, "type": "deep-dive"})
    return engagements


def scan_memory_logs(days=90):
    """Scan memory logs for topic mentions, PRs, recaps."""
    engagements = defaultdict(list)
    now = datetime.now()
    for i in range(days):
        date = now - timedelta(days=i)
        log_file = MEMORY_DIR / f"{date.strftime('%Y-%m-%d')}.md"
        if not log_file.exists():
            continue
        content = log_file.read_text()
        lines = content.split("\n")
        for line in lines:
            line_lower = line.lower()
            # Detect recaps
            if "复盘" in line or "recap" in line_lower:
                keywords = extract_keywords(line)
                for kw in keywords:
                    engagements[kw].append({"date": date, "type": "recap"})
            # Detect PRs
            elif "pr #" in line_lower or "pr#" in line_lower:
                keywords = extract_keywords(line)
                for kw in keywords:
                    engagements[kw].append({"date": date, "type": "pr"})
            # General mentions of known topics
            else:
                keywords = extract_keywords(line)
                for kw in keywords:
                    engagements[kw].append({"date": date, "type": "mention"})
    return engagements


def extract_keywords(text):
    """Extract meaningful keywords from a line of text."""
    # Common AI/ML terms to look for
    ai_terms = [
        "transformer", "attention", "tokeniz", "embedding", "bert", "gpt",
        "llama", "mistral", "qwen", "gemma", "deepseek",
        "sft", "rlhf", "dpo", "ppo", "grpo",
        "pretrain", "finetun", "lora", "qlora", "peft",
        "fsdp", "deepspeed", "megatron", "distributed train",
        "quantiz", "awq", "gptq", "gguf", "bitsandbytes", "fp8", "int4",
        "kv cache", "flash attention", "pagedattention", "speculative decod",
        "vllm", "sglang", "tensorrt", "llama.cpp",
        "mcp", "tool call", "multi-agent", "agent 编排", "agent 安全",
        "agent 可观测", "记忆管理", "memory manag",
        "rag", "vector search", "retrieval",
        "diffusion", "stable diffusion", "imagen",
        "whisper", "tts", "speech",
        "interpretab", "mechanistic", "sae", "probe",
        "moe", "mixture of expert",
        "distillat", "pruning", "model merg",
        "context window", "rope", "alibi", "yarn",
        "guardrail", "jailbreak", "prompt injection",
        "compaction", "context budget",
        "structured output", "json schema",
        "代码生成", "code gen",
        "多模态", "multimodal",
        "视频生成", "video gen",
    ]
    text_lower = text.lower()
    found = []
    for term in ai_terms:
        if term in text_lower:
            found.append(term)
    return found


def match_topic_to_engagements(topic, all_engagements):
    """Match a knowledge map topic to engagement records."""
    topic_lower = topic.lower()
    topic_words = re.sub(r"[^\w\s]", "", topic_lower).split()
    matched = []
    for keyword, records in all_engagements.items():
        # Direct substring match
        if keyword in topic_lower or any(keyword in w for w in topic_words):
            matched.extend(records)
        # Reverse: topic word in keyword
        for tw in topic_words:
            if len(tw) > 2 and tw in keyword:
                matched.extend(records)
                break
    # Deduplicate by date+type
    seen = set()
    unique = []
    for r in matched:
        key = (r["date"].strftime("%Y-%m-%d"), r["type"])
        if key not in seen:
            seen.add(key)
            unique.append(r)
    return unique


def calculate_score(engagements):
    """Calculate mastery score from engagement records."""
    now = datetime.now()
    score = 0.0
    for record in engagements:
        days_ago = (now - record["date"]).days
        weight = WEIGHTS.get(record["type"], 1.0)
        score += weight * decay(days_ago)
    return round(score, 2)


def suggest_level(score):
    """Suggest mastery level based on score."""
    if score >= SCORE_THRESHOLDS["green"]:
        return "🟢"
    elif score >= SCORE_THRESHOLDS["yellow"]:
        return "🟡"
    else:
        return "🔴"


def last_engagement_days(engagements):
    """Days since last engagement."""
    if not engagements:
        return None
    now = datetime.now()
    most_recent = max(r["date"] for r in engagements)
    return (now - most_recent).days


def generate_report():
    """Generate mastery score report."""
    topics = parse_knowledge_map()
    if not topics:
        print("❌ Knowledge map not found or empty.")
        return

    # Collect all engagements
    deep_dive_eng = scan_deep_dives()
    memory_eng = scan_memory_logs(days=90)

    # Merge
    all_eng = defaultdict(list)
    for k, v in deep_dive_eng.items():
        all_eng[k].extend(v)
    for k, v in memory_eng.items():
        all_eng[k].extend(v)

    # Calculate per-topic
    results = []
    for topic, current_level in topics.items():
        matched = match_topic_to_engagements(topic, all_eng)
        score = calculate_score(matched)
        suggested = suggest_level(score)
        last_days = last_engagement_days(matched)
        engagement_count = len(matched)

        change = ""
        if suggested != current_level:
            level_order = {"🔴": 0, "🟡": 1, "🟢": 2}
            if level_order.get(suggested, 0) > level_order.get(current_level, 0):
                change = "⬆️ 建议升级"
            elif level_order.get(suggested, 0) < level_order.get(current_level, 0):
                change = "⬇️ 考虑降级"

        decay_warning = ""
        if last_days is not None and last_days > DECAY_WARNING_DAYS:
            decay_warning = f"⚠️ {last_days}天未接触"
        elif last_days is None:
            decay_warning = "⚠️ 无记录"

        results.append({
            "topic": topic,
            "current": current_level,
            "score": score,
            "suggested": suggested,
            "engagements": engagement_count,
            "last_days": last_days,
            "change": change,
            "warning": decay_warning,
        })

    # Sort by score descending
    results.sort(key=lambda x: x["score"], reverse=True)

    # Output
    print("# Mastery Score Report")
    print(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M')}")
    print(f"Half-life: {HALF_LIFE_DAYS} days | Scan range: 90 days\n")

    print(f"| {'主题':<25} | 当前 | Score | 建议 | 接触次数 | 最近 | 变更 |")
    print(f"|{'-'*27}|------|-------|------|----------|------|------|")
    for r in results:
        last_str = f"{r['last_days']}d" if r['last_days'] is not None else "N/A"
        flags = f"{r['change']} {r['warning']}".strip()
        print(f"| {r['topic']:<25} | {r['current']}  | {r['score']:>5} | {r['suggested']}  | {r['engagements']:>8} | {last_str:>4} | {flags} |")

    # Summary
    changes = [r for r in results if r["change"]]
    warnings = [r for r in results if r["warning"]]
    print(f"\n## Summary")
    print(f"- Topics tracked: {len(results)}")
    print(f"- Level change suggestions: {len(changes)}")
    print(f"- Decay warnings: {len(warnings)}")

    if changes:
        print(f"\n## Suggested Changes")
        for r in changes:
            print(f"- {r['topic']}: {r['current']} → {r['suggested']} (score: {r['score']}, {r['change']})")

    # JSON output option
    if "--json" in sys.argv:
        print("\n## JSON")
        print(json.dumps(results, indent=2, default=str))


if __name__ == "__main__":
    generate_report()


path: skill.yaml
content: name: learning-system-skill
version: 1.0.0
description: AI领域系统学习体系。管理知识图谱、深度学习笔记、实战复盘和关联网络。适用于学习计划、知识更新、深度研究等场景。
author: clawd
homepage: https://clawhub.com/skills/learning-system-skill

triggers:
  - 学习计划
  - 知识图谱
  - 学习笔记
  - 复盘
  - learning
  - study

features:
  - 知识图谱管理
  - 深度学习笔记
  - 实战复盘总结
  - 关联网络分析

compatibility:
  platforms:
    - openclaw
    - clawdbot


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)