Skill: haodong-lei-ray/bilingual-learning (version 1.1.1)

--- name: bilingual_learning description: 双语学习 Skill - CN → EN。当用户使用此 skill 时，agent 会在最终输出前强制进行双语学习处理，包括难度调整、考试检测和双语输出。 --- # Bilingual Learning Skill ## Overview 双语学习 Skill，帮助用户在编码过程中被动学习英语。当 agent 使用此 skill 时，会根据难度设置在回答中穿插学习检测，让用户不知不觉中掌握英语单词。 ## When to Use This Skill - 用户在日常对话中想顺便学习英语 - 用户需要被动接收英语生词 - 需要定期检测用户词汇量 ## Architecture Flow ``` 难度调整层 → 选择题型层 → 考试层 → 双语输出 ``` ### 1. 难度调整层 (Difficulty Layer) 控制考试触发频率： | 等级 | 名称 | 考试触发率 | |------|------|-----------| | rare | 稀少 | 30% | | medium | 中等 | 50% | | dense | 密集 | 80% | ### 2. 选择题型层 (Question Type Layer) | 类型 | 名称 | 行为 | |------|------|------| | pure_l1 | 检测生词层 | 直接执行检测层，**不调用随机筛子** | | pure_l2 | 考考你层 | 直接执行测验层，**不调用随机筛子** | | hybrid | 混合型 | 两层都执行，但**顺序随机打乱**（调用随机筛子） | **重要**：只有 hybrid 模式才调用随机筛子打乱考试顺序。 ### 3. 考试层 (Exam Layer) **检测生词层 (Detection Layer)** - 偶尔混杂语言，双语输出 - 检测用户不会哪些生词 - 如果用户没听懂，告诉用户当前不会的词有哪些、意思是什么 - 自动将这些词写入生词库 - 让用户被动学习语言 **考考你层 (Quiz Layer)** - 从生词库随机抽取单词 - 测试用户拼写 **重要**：考试层每层之间不能同时出现，必须按顺序执行。 ### 4. 双语输出层 (Bilingual Output Layer) 在 agent 最终输出前强制双语处理。 ## Word Library Format 单词条目结构： ```json { "id": "a1b2c3d4", "name": "apple", "pos": "n.", "field": "fruit", "book": "default", "added_time": "2026-05-13 19:00:00" } ``` 字段说明： - `id`: 单词唯一标识 (UUID 前8位) - `name`: 单词本身 - `pos`: 词性 (n./v./adj./adv. 等) - `field`: 领域 (tech/fruit/general 等) - `book`: 词书 (default/custom 等) - `added_time`: 入库时间 ## CLI Management ### Add Word ```bash python scripts/cli.py add <word> [pos] [field] [book] ``` Examples: ```bash python scripts/cli.py add apple n. fruit default python scripts/cli.py add algorithm n. tech cs ``` ### Delete Word ```bash python scripts/cli.py delete <word> [-n|-k] ``` - `-n`: 生词库 (new words) - `-k`: 熟词库 (known words) ### Exchange Word (Transfer between libraries) ```bash python scripts/cli.py exchange <word> [-n|-k] ``` - 单词不能在生词库和熟词库中同时存在 - 传递方向由源库决定 ### List Words ```bash python scripts/cli.py list [-n|-k] ``` ### Clear Library ```bash python scripts/cli.py clear [-n|-k] ``` ## Running Exam ```bash python scripts/exam.py <difficulty> ``` Example: ```bash python scripts/exam.py medium python scripts/exam.py rare python scripts/exam.py dense ``` ## Random Dice ```bash python scripts/dice.py python scripts/dice.py demo ``` 用于随机选择考试类型和触发考试。 ## Data Files - `data/new_words.json` - 生词库 - `data/known_words.json` - 熟词库 ## Key Design Principles 1. **被动学习**: 用户不需要主动记忆，agent 会在对话中自然传递生词 2. **检测驱动**: 通过检测用户不会的词来更新生词库 3. **难度可控**: 三种难度等级适应不同学习阶段 4. **防止重复**: 一个单词不能同时存在于生词库和熟词库

path: CHANGELOG.md
content: # Changelog

## v1.1.0 (2026-05-13)

### Updated
- **题型逻辑修改**：只有 hybrid 模式才调用随机筛子
  - pure_l1: 直接执行检测层，不调用随机筛子
  - pure_l2: 直接执行测验层，不调用随机筛子
  - hybrid: 两层都执行，顺序随机打乱

## v1.0.0 (2026-05-13)

### Added
- 初始发布
- 双语学习 Skill (CN → EN)
- 词库管理 (生词库/熟词库)
- 考试系统 (检测生词层/考考你层)
- CLI 工具


path: README.md
content: # LanguagelearningWhenCoding - 双语学习 Skill

当用户调用此 Skill 时，Agent 会在最终输出前强制进行双语学习处理。

## 项目结构

```
cn-en/
├── lib/
│   └── core.py          # 词库核心模块
├── data/                # 词库数据存储
├── scripts/
│   ├── cli.py           # CLI 工具 (add/delete/exchange/list/clear)
│   ├── exam.py          # 考试系统
│   ├── dice.py          # 随机生成器
│   └── skill.py         # Skill 主入口
└── README.md
```

## 词库格式

```json
{
  "id": "a1b2c3d4",
  "name": "apple",
  "pos": "n.",
  "field": "fruit",
  "book": "default",
  "added_time": "2026-05-13 19:00:00"
}
```

## 架构流程

```
难度调整层 → 选择题型层 → 考试层 → 双语输出
```

### 难度调整层

| 等级 | 名称 | 考试触发率 |
|------|------|-----------|
| rare | 稀少 | 30% |
| medium | 中等 | 50% |
| dense | 密集 | 80% |

### 选择题型层

| 类型 | 名称 | 概率 |
|------|------|------|
| pure_l1 | 纯第一层(检测生词层) | 40% |
| pure_l2 | 纯第二层(考考你层) | 40% |
| hybrid | 混合型 | 20% |

### 考试层

| 题型 | 行为 |
|------|------|
| pure_l1 | 直接执行检测层，**不调用随机筛子** |
| pure_l2 | 直接执行测验层，**不调用随机筛子** |
| hybrid | 两层都执行，但**顺序随机打乱**（调用随机筛子） |

1. **检测生词层**: 偶尔混杂语言，双语输出，检测用户不会的生词并自动加入生词库
2. **考考你层**: 从生词库随机抽题测试用户

**重要**：只有 hybrid 模式才调用随机筛子打乱顺序。

## CLI 使用

```bash
# 添加单词到生词库
python scripts/cli.py add apple n. fruit default

# 列出生词库
python scripts/cli.py list -n

# 列成熟词库
python scripts/cli.py list -k

# 删除生词库中的单词
python scripts/cli.py delete apple -n

# 生词库转熟词库
python scripts/cli.py exchange apple -n -k

# 熟词库转生词库
python scripts/cli.py exchange apple -k -n

# 清空词库
python scripts/cli.py clear -n   # 清空生词库
python scripts/cli.py clear -k   # 清空熟词库
```

## 运行考试

```bash
python scripts/exam.py medium
python scripts/exam.py rare
python scripts/exam.py dense
```

## 随机筛子

```bash
python scripts/dice.py          # 掷D6
python scripts/dice.py demo     # 演示模式
```

## 数据文件

- `data/new_words.json` - 生词库
- `data/known_words.json` - 熟词库


path: _meta.json
content: ownerId: kn7ckckjdekq92w3hgy1071k0h84wdbg
slug: bilingual-learning
version: 1.0.2
publishedAt: 1778675523623

path: data/config.json
content: difficulty: rare
question_type: pure_l1

path: lib/core.py
content: """
双语学习 Skill 核心模块
语言方向: CN -> EN
"""

import json
import uuid
from datetime import datetime
from pathlib import Path

DataPath = Path(__file__).parent.parent / "data"

# 默认配置
DEFAULT_CONFIG = {
    "difficulty": "medium",
    "question_type": "hybrid"
}

def load_config() -> dict:
    """加载配置文件"""
    config_path = DataPath / "config.json"
    if config_path.exists():
        with open(config_path, "r", encoding="utf-8") as f:
            return json.load(f)
    return DEFAULT_CONFIG.copy()

def save_config(config: dict):
    """保存配置文件"""
    DataPath.mkdir(parents=True, exist_ok=True)
    config_path = DataPath / "config.json"
    with open(config_path, "w", encoding="utf-8") as f:
        json.dump(config, f, ensure_ascii=False, indent=2)

class WordLibrary:
    """词库管理基类"""
    
    def __init__(self, name: str):
        self.name = name
        self.path = DataPath / f"{name}.json"
        self.words: list[dict] = []
        self.load()
    
    def reload(self):
        """重新从磁盘加载"""
        self.load()
    
    def load(self):
        """从文件加载词库"""
        if self.path.exists():
            with open(self.path, "r", encoding="utf-8") as f:
                self.words = json.load(f)
        else:
            self.words = []
    
    def save(self):
        """保存词库到文件"""
        DataPath.mkdir(parents=True, exist_ok=True)
        with open(self.path, "w", encoding="utf-8") as f:
            json.dump(self.words, f, ensure_ascii=False, indent=2)
    
    def add(self, word: str, pos: str = "", field: str = "", book: str = "default"):
        """添加单词"""
        if self.find(word):
            return False
        entry = {
            "id": str(uuid.uuid4())[:8],
            "name": word,
            "pos": pos,
            "field": field,
            "book": book,
            "added_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        }
        self.words.append(entry)
        self.save()
        return True
    
    def delete(self, word_or_id: str):
        """删除单词（按名称或ID）"""
        for i, w in enumerate(self.words):
            if w["name"] == word_or_id or w["id"] == word_or_id:
                self.words.pop(i)
                self.save()
                return True
        return False
    
    def find(self, word: str):
        """查找单词"""
        for w in self.words:
            if w["name"] == word:
                return w
        return None
    
    def list_all(self):
        """列出所有单词"""
        return self.words
    
    def clear(self):
        """清空词库"""
        self.words = []
        self.save()


class NewWordLibrary(WordLibrary):
    """生词库"""
    def __init__(self):
        super().__init__("new_words")


class KnownWordLibrary(WordLibrary):
    """熟词库"""
    def __init__(self):
        super().__init__("known_words")


def transfer_word(word: str, from_lib: str, to_lib: str):
    """生熟词库传递"""
    source = NewWordLibrary() if from_lib == "new" else KnownWordLibrary()
    target = NewWordLibrary() if to_lib == "new" else KnownWordLibrary()
    
    entry = source.find(word)
    if not entry:
        return False, f"单词 '{word}' 不在 {from_lib} 词库中"
    
    if target.find(word):
        return False, f"单词 '{word}' 已在 {to_lib} 词库中"
    
    source.delete(word)
    target.reload()
    target.words.append(entry)
    target.save()
    return True, f"单词 '{word}' 已从 {from_lib} 词库转移到 {to_lib} 词库"


path: scripts/cli.py
content: #!/usr/bin/env python3
"""
词库管理 CLI 工具
支持: add, delete, exchange, list, clear 模式
"""

import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "lib"))

from core import NewWordLibrary, KnownWordLibrary, transfer_word

def usage():
    print("""用法:
  python cli.py <模式> [参数]

模式:
  add <word> [pos] [field] [book]   - 添加单词到生词库
  delete <word> [-n|-k]             - 从生词库(-n)或熟词库(-k)删除
  exchange <word> [-n|-k]           - 单词在生熟词库间传递
  list [-n|-k]                       - 列出词库内容
  clear [-n|-k]                      - 清空词库

示例:
  python cli.py add apple n. fruit default
  python cli.py delete apple -n
  python cli.py exchange apple -n -k
""")

if len(sys.argv) < 2:
    usage()
    sys.exit(1)

cmd = sys.argv[1].lower()

if cmd == "add":
    if len(sys.argv) < 3:
        print("错误: 请提供单词")
        sys.exit(1)
    word = sys.argv[2]
    pos = sys.argv[3] if len(sys.argv) > 3 else ""
    field = sys.argv[4] if len(sys.argv) > 4 else ""
    book = sys.argv[5] if len(sys.argv) > 5 else "default"
    
    lib = NewWordLibrary()
    if lib.add(word, pos, field, book):
        print(f"✓ 单词 '{word}' 已添加到生词库")
    else:
        print(f"✗ 单词 '{word}' 已存在")

elif cmd == "delete":
    if len(sys.argv) < 3:
        print("错误: 请提供单词")
        sys.exit(1)
    word = sys.argv[2]
    target = sys.argv[3] if len(sys.argv) > 3 else "-n"
    
    lib = NewWordLibrary() if target == "-n" else KnownWordLibrary()
    if lib.delete(word):
        print(f"✓ 单词 '{word}' 已删除")
    else:
        print(f"✗ 单词 '{word}' 不存在")

elif cmd == "exchange":
    if len(sys.argv) < 4:
        print("错误: 请提供单词和源/目标库")
        sys.exit(1)
    word = sys.argv[2]
    from_lib = "new" if sys.argv[3] == "-n" else "known"
    to_lib = "known" if from_lib == "new" else "new"
    
    success, msg = transfer_word(word, from_lib, to_lib)
    print(f"{'✓' if success else '✗'} {msg}")

elif cmd == "list":
    target = sys.argv[2] if len(sys.argv) > 2 else "-n"
    lib = NewWordLibrary() if target == "-n" else KnownWordLibrary()
    words = lib.list_all()
    
    if not words:
        print(f"{'生词库' if target == '-n' else '熟词库'} 为空")
    else:
        print(f"\n{'='*50}")
        print(f"{'生词库' if target == '-n' else '熟词库'} (共{len(words)}个)")
        print(f"{'='*50}")
        for w in words:
            print(f"[{w['id']}] {w['name']} ({w['pos']}) | 领域:{w['field']} | 词书:{w['book']} | 入库:{w['added_time']}")

elif cmd == "clear":
    target = sys.argv[2] if len(sys.argv) > 2 else "-n"
    lib = NewWordLibrary() if target == "-n" else KnownWordLibrary()
    lib.clear()
    print(f"{'生词库' if target == '-n' else '熟词库'} 已清空")

else:
    usage()


path: scripts/dice.py
content: #!/usr/bin/env python3
"""
随机生成器（筛子）
用于考试选题、难度调整等随机场景
"""

import random

class Dice:
    """筛子类 - 生成随机数或进行随机选择"""
    
    def __init__(self, sides: int = 6, seed: int = None):
        self.sides = sides
        if seed is not None:
            random.seed(seed)
    
    def roll(self) -> int:
        """掷筛子"""
        return random.randint(1, self.sides)
    
    @staticmethod
    def choose(probabilities: dict) -> str:
        """
        按概率选择
        probabilities: {"选项1": 0.3, "选项2": 0.5, "选项3": 0.2}
        """
        r = random.random()
        cumulative = 0
        for option, prob in probabilities.items():
            cumulative += prob
            if r <= cumulative:
                return option
        return list(probabilities.keys())[-1]
    
    @staticmethod
    def pick(items: list, count: int = 1):
        """从列表中随机抽取"""
        if count >= len(items):
            return items
        return random.sample(items, count)

def demo():
    print("=== 筛子演示 ===")
    d = Dice(6)
    print(f"掷D6: {d.roll()}")
    
    print("\n=== 概率选择演示 ===")
    choice = Dice.choose({"rare": 0.3, "medium": 0.5, "dense": 0.2})
    print(f"难度选择结果: {choice}")
    
    print("\n=== 随机抽取演示 ===")
    items = ["apple", "banana", "cherry", "date", "elderberry"]
    picked = Dice.pick(items, 3)
    print(f"从 {items} 中抽取3个: {picked}")

if __name__ == "__main__":
    import sys
    if len(sys.argv) > 1 and sys.argv[1] == "demo":
        demo()
    else:
        d = Dice(6)
        print(d.roll())


path: scripts/exam.py
content: #!/usr/bin/env python3
"""
考试系统核心
层次结构: 难度调整层 → 选择题型层 → 考试层

考试层:
  1. 检测生词层 (偶尔混杂语言，双语输出，检测用户不会的生词)
  2. 考考你层 (从生词库抽单词测试用户)

重要：只有 hybrid 模式才调用随机筛子
"""

import random
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "lib"))

from core import NewWordLibrary, load_config

# 难度等级
DIFFICULTY = {
    "rare": {"name": "稀少", "ratio": 0.3},
    "medium": {"name": "中等", "ratio": 0.5},
    "dense": {"name": "密集", "ratio": 0.8}
}

# 选择题类型
QUESTION_TYPE = {
    "pure_l1": {"name": "纯第一层", "order": ["detection"]},
    "pure_l2": {"name": "纯第二层", "order": ["quiz"]},
    "hybrid": {"name": "混合型", "order": ["detection", "quiz"]}  # 需要随机打乱顺序
}

class ExamSystem:
    def __init__(self, difficulty: str = "medium"):
        self.difficulty = difficulty
        self.difficulty_ratio = DIFFICULTY.get(difficulty, DIFFICULTY["medium"])["ratio"]
    
    def should_trigger_exam(self) -> bool:
        """根据难度决定是否触发考试"""
        return random.random() < self.difficulty_ratio
    
    def detection_layer(self) -> dict:
        """检测生词层"""
        new_lib = NewWordLibrary()
        words = new_lib.list_all()
        
        if not words:
            return {"layer": "detection", "type": "info", "content": "生词库为空"}
        
        sample_size = min(len(words), random.randint(1, 3))
        sample_words = random.sample(words, sample_size)
        
        return {
            "layer": "detection",
            "type": "bilingual_check",
            "content": sample_words,
            "message": "请确认你认识以下单词"
        }
    
    def quiz_layer(self, num_questions: int = 3) -> dict:
        """考考你层"""
        new_lib = NewWordLibrary()
        words = new_lib.list_all()
        
        if len(words) < num_questions:
            num_questions = len(words) if words else 0
        
        if num_questions == 0:
            return {"layer": "quiz", "type": "info", "content": "生词库单词不足"}
        
        questions = random.sample(words, num_questions)
        
        return {
            "layer": "quiz",
            "type": "quiz",
            "content": questions,
            "message": f"请拼写以下 {num_questions} 个单词"
        }
    
    def run_exam(self, question_type: str) -> list:
        """
        运行考试流程
        - pure_l1: 直接执行检测生词层
        - pure_l2: 直接执行考考你层
        - hybrid: 调用随机筛子打乱顺序
        """
        if not self.should_trigger_exam():
            return []
        
        qt_info = QUESTION_TYPE.get(question_type, QUESTION_TYPE["hybrid"])
        layers = qt_info["order"].copy()
        
        # 只有 hybrid 模式才随机打乱顺序
        if question_type == "hybrid":
            random.shuffle(layers)
        
        exam_session = []
        for layer in layers:
            if layer == "detection":
                exam_session.append(self.detection_layer())
            elif layer == "quiz":
                exam_session.append(self.quiz_layer())
        
        return exam_session

def main():
    # 优先使用命令行参数，否则读取配置文件
    config = load_config()
    difficulty = sys.argv[1] if len(sys.argv) > 1 else config["difficulty"]
    question_type = sys.argv[2] if len(sys.argv) > 2 else config["question_type"]

    system = ExamSystem(difficulty)
    
    print(f"考试系统启动 | 难度: {DIFFICULTY[difficulty]['name']} | 题型: {QUESTION_TYPE[question_type]['name']}")
    print("-" * 40)
    
    results = system.run_exam(question_type)
    
    if not results:
        print("本次对话未触发考试")
        return
    
    for i, exam in enumerate(results, 1):
        print(f"\n[考试{i}] {exam['layer']}")
        if exam["type"] == "bilingual_check":
            print(exam["message"])
            for w in exam["content"]:
                print(f"  - {w['name']} ({w['pos']})")
        elif exam["type"] == "quiz":
            print(exam["message"])
            for w in exam["content"]:
                print(f"  ? {w['name']}")

if __name__ == "__main__":
    main()


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