@rookiestar/eng-lang-tutor 1.2.7 → 1.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/package.json +10 -8
  2. package/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
  3. package/scripts/__pycache__/audio_composer.cpython-313.pyc +0 -0
  4. package/scripts/__pycache__/audio_converter.cpython-313.pyc +0 -0
  5. package/scripts/__pycache__/audio_enhancer.cpython-313.pyc +0 -0
  6. package/scripts/__pycache__/audio_utils.cpython-313.pyc +0 -0
  7. package/scripts/__pycache__/command_parser.cpython-313.pyc +0 -0
  8. package/scripts/__pycache__/constants.cpython-313.pyc +0 -0
  9. package/scripts/__pycache__/cron_push.cpython-313.pyc +0 -0
  10. package/scripts/__pycache__/dedup.cpython-313.pyc +0 -0
  11. package/scripts/__pycache__/error_notebook.cpython-313.pyc +0 -0
  12. package/scripts/__pycache__/feishu_voice.cpython-313.pyc +0 -0
  13. package/scripts/__pycache__/gamification.cpython-313.pyc +0 -0
  14. package/scripts/__pycache__/scorer.cpython-313.pyc +0 -0
  15. package/scripts/__pycache__/state_manager.cpython-313.pyc +0 -0
  16. package/scripts/__pycache__/utils.cpython-313.pyc +0 -0
  17. package/scripts/audio/__pycache__/__init__.cpython-313.pyc +0 -0
  18. package/scripts/audio/__pycache__/composer.cpython-313.pyc +0 -0
  19. package/scripts/audio/__pycache__/converter.cpython-313.pyc +0 -0
  20. package/scripts/audio/__pycache__/feishu_voice.cpython-313.pyc +0 -0
  21. package/scripts/audio/__pycache__/utils.cpython-313.pyc +0 -0
  22. package/scripts/audio/tts/__pycache__/__init__.cpython-313.pyc +0 -0
  23. package/scripts/audio/tts/__pycache__/base.cpython-313.pyc +0 -0
  24. package/scripts/audio/tts/__pycache__/manager.cpython-313.pyc +0 -0
  25. package/scripts/audio/tts/manager.py +12 -0
  26. package/scripts/audio/tts/providers/__pycache__/__init__.cpython-313.pyc +0 -0
  27. package/scripts/audio/tts/providers/__pycache__/edge.cpython-313.pyc +0 -0
  28. package/scripts/audio/tts/providers/__pycache__/xunfei.cpython-313.pyc +0 -0
  29. package/scripts/audio/utils.py +7 -1
  30. package/scripts/cli/__pycache__/__init__.cpython-313.pyc +0 -0
  31. package/scripts/cli/__pycache__/cli.cpython-313.pyc +0 -0
  32. package/scripts/cli/__pycache__/command_parser.cpython-313.pyc +0 -0
  33. package/scripts/cli/cli.py +10 -3
  34. package/scripts/core/__pycache__/__init__.cpython-313.pyc +0 -0
  35. package/scripts/core/__pycache__/constants.cpython-313.pyc +0 -0
  36. package/scripts/core/__pycache__/error_notebook.cpython-313.pyc +0 -0
  37. package/scripts/core/__pycache__/gamification.cpython-313.pyc +0 -0
  38. package/scripts/core/__pycache__/scorer.cpython-313.pyc +0 -0
  39. package/scripts/core/__pycache__/state_manager.cpython-313.pyc +0 -0
  40. package/scripts/core/scorer.py +1 -1
  41. package/scripts/core/state_manager.py +5 -5
  42. package/scripts/scheduling/__pycache__/__init__.cpython-313.pyc +0 -0
  43. package/scripts/scheduling/__pycache__/cron_push.cpython-313.pyc +0 -0
  44. package/scripts/scheduling/cron_push.py +3 -3
  45. package/scripts/utils/__pycache__/__init__.cpython-313.pyc +0 -0
  46. package/scripts/utils/__pycache__/dedup.cpython-313.pyc +0 -0
  47. package/scripts/utils/__pycache__/helpers.cpython-313.pyc +0 -0
  48. package/.gitignore +0 -32
  49. package/CHANGELOG.md +0 -37
  50. package/CLAUDE.md +0 -275
  51. package/bin/eng-lang-tutor.js +0 -177
  52. package/docs/OPENCLAW_DEPLOYMENT.md +0 -241
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@rookiestar/eng-lang-tutor",
3
- "version": "1.2.7",
3
+ "version": "1.2.8",
4
4
  "description": "地道美式英语导师 - OpenClaw Skill for learning authentic American English",
5
5
  "main": "scripts/cli/cli.py",
6
- "scripts": {
7
- "postinstall": "echo '\\n✅ eng-lang-tutor installed successfully!\\n\\n📋 Next step: Install Python dependencies\\n eng-lang-tutor-setup --venv\\n\\n💡 This creates a virtual environment at ~/.venvs/eng-lang-tutor\\n'"
6
+ "bin": {
7
+ "eng-lang-tutor-setup": "scripts/setup.py"
8
8
  },
9
9
  "keywords": [
10
10
  "english",
@@ -29,11 +29,13 @@
29
29
  },
30
30
  "homepage": "https://github.com/rookiestar/Skills/tree/main/eng-lang-tutor#readme",
31
31
  "files": [
32
- "**/*",
33
- "!tests/**",
34
- "!**/*.pyc",
35
- "!**/__pycache__/**",
36
- "!.DS_Store"
32
+ "scripts/",
33
+ "templates/",
34
+ "examples/",
35
+ "references/",
36
+ "SKILL.md",
37
+ "README.md",
38
+ "requirements.txt"
37
39
  ],
38
40
  "engines": {
39
41
  "node": ">=14.0.0"
@@ -292,6 +292,18 @@ class TTSManager:
292
292
  """
293
293
  return self.provider.list_voices()
294
294
 
295
+ def get_voice_by_role(self, role: str) -> str:
296
+ """
297
+ 获取指定角色的语音 ID
298
+
299
+ Args:
300
+ role: 角色 ("narrator", "dialogue_a", "dialogue_b")
301
+
302
+ Returns:
303
+ 语音 ID
304
+ """
305
+ return self.provider.get_voice_by_role(role)
306
+
295
307
  def get_audio_path(self, date_str: str, filename: str) -> Path:
296
308
  """
297
309
  获取音频文件的完整路径
@@ -53,7 +53,13 @@ def get_audio_duration(audio_path: Path, ffmpeg_path: Optional[str] = None) -> f
53
53
  "-"
54
54
  ]
55
55
 
56
- result = subprocess.run(cmd, capture_output=True, text=True)
56
+ try:
57
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
58
+ except subprocess.TimeoutExpired:
59
+ # ffmpeg 超时,返回 0.0
60
+ return 0.0
61
+ except Exception:
62
+ return 0.0
57
63
 
58
64
  # 从 stderr 中解析时长,格式: " Duration: 00:00:03.45, ..."
59
65
  match = re.search(r"Duration: (\d+):(\d+):(\d+\.?\d*)", result.stderr)
@@ -112,8 +112,8 @@ def main():
112
112
  config = {
113
113
  "cefr_level": state.get("preferences", {}).get("cefr_level", "B1"),
114
114
  "tutor_style": state.get("preferences", {}).get("tutor_style", "humorous"),
115
- "oral_ratio": state.get("preferences", {}).get("oral_ratio", 70),
116
- "topic_weights": state.get("preferences", {}).get("topic_weights", {}),
115
+ "oral_ratio": int(state.get("preferences", {}).get("oral_written_ratio", 0.7) * 100),
116
+ "topics": state.get("preferences", {}).get("topics", {}),
117
117
  "schedule": state.get("schedule", {})
118
118
  }
119
119
  print(json.dumps(config, indent=2, ensure_ascii=False))
@@ -137,7 +137,7 @@ def main():
137
137
  if not 0 <= args.oral_ratio <= 100:
138
138
  print("Error: Oral ratio must be between 0 and 100")
139
139
  exit(1)
140
- state = sm.update_preferences(state, oral_ratio=args.oral_ratio)
140
+ state = sm.update_preferences(state, oral_written_ratio=args.oral_ratio/100)
141
141
  print(f"Updated oral ratio to: {args.oral_ratio}%")
142
142
 
143
143
  sm.save_state(state)
@@ -215,6 +215,13 @@ def main():
215
215
  print("Error: Invalid date format. Use YYYY-MM-DD")
216
216
  exit(1)
217
217
 
218
+ # Check if keypoint exists first
219
+ keypoint = sm.load_daily_content('keypoint', target_date)
220
+ if not keypoint:
221
+ date_str = target_date.isoformat() if target_date else datetime.now().strftime('%Y-%m-%d')
222
+ print(f"Error: No keypoint found for {date_str}")
223
+ exit(1)
224
+
218
225
  result = sm.generate_keypoint_audio(target_date)
219
226
 
220
227
  if result.get('success'):
@@ -258,7 +258,7 @@ if __name__ == "__main__":
258
258
  if args.demo:
259
259
  # Demo quiz
260
260
  quiz = {
261
- "quiz_date": "2026-02-20",
261
+ "quiz_date": datetime.now().strftime('%Y-%m-%d'),
262
262
  "questions": [
263
263
  {"id": 1, "type": "multiple_choice", "correct_answer": "B", "xp_value": 10},
264
264
  {"id": 2, "type": "fill_blank", "correct_answer": "gonna", "xp_value": 12},
@@ -412,8 +412,8 @@ class StateManager:
412
412
  from ..audio.composer import AudioComposer
413
413
  from ..audio.tts import TTSManager
414
414
  except ImportError:
415
- from audio.composer import AudioComposer
416
- from audio.tts import TTSManager
415
+ from scripts.audio.composer import AudioComposer
416
+ from scripts.audio.tts import TTSManager
417
417
 
418
418
  if target_date is None:
419
419
  target_date = date.today()
@@ -443,9 +443,9 @@ class StateManager:
443
443
  from ..audio.tts import TTSManager
444
444
  from ..audio.converter import convert_mp3_to_opus
445
445
  except ImportError:
446
- from audio.composer import AudioComposer
447
- from audio.tts import TTSManager
448
- from audio.converter import convert_mp3_to_opus
446
+ from scripts.audio.composer import AudioComposer
447
+ from scripts.audio.tts import TTSManager
448
+ from scripts.audio.converter import convert_mp3_to_opus
449
449
 
450
450
  tts = TTSManager.from_env()
451
451
  composer = AudioComposer(tts)
@@ -31,12 +31,12 @@ except ImportError:
31
31
  class CronPusher:
32
32
  """Handles scheduled content push operations."""
33
33
 
34
- def __init__(self, data_dir: str = "data"):
34
+ def __init__(self, data_dir: str = None):
35
35
  """
36
36
  Initialize the cron pusher.
37
37
 
38
38
  Args:
39
- data_dir: Path to the data directory
39
+ data_dir: Path to the data directory (defaults to StateManager's default)
40
40
  """
41
41
  self.state_manager = StateManager(data_dir)
42
42
  self.today = date.today()
@@ -201,7 +201,7 @@ def main():
201
201
  parser.add_argument('--task', required=True,
202
202
  choices=['keypoint', 'quiz', 'reset_daily', 'status'],
203
203
  help='Task to execute')
204
- parser.add_argument('--data-dir', default='data', help='Data directory path')
204
+ parser.add_argument('--data-dir', default=None, help='Data directory path (default: ~/.openclaw/state/eng-lang-tutor)')
205
205
  parser.add_argument('--json', action='store_true', help='Output as JSON only')
206
206
 
207
207
  args = parser.parse_args()
package/.gitignore DELETED
@@ -1,32 +0,0 @@
1
- # Python
2
- __pycache__/
3
- *.py[cod]
4
- *$py.class
5
- *.so
6
- .Python
7
- .venv/
8
- venv/
9
- ENV/
10
-
11
- # IDE
12
- .idea/
13
- .vscode/
14
- *.swp
15
- *.swo
16
-
17
- # OS
18
- .DS_Store
19
- Thumbs.db
20
-
21
- # Backups
22
- backups/
23
- *.bak
24
-
25
- # User data
26
- data/state.json
27
- data/logs/
28
- data/daily/
29
-
30
- # npm
31
- node_modules/
32
- *.tgz
package/CHANGELOG.md DELETED
@@ -1,37 +0,0 @@
1
- # Changelog
2
-
3
- All notable changes to this project will be documented in this file.
4
-
5
- ## [1.0.0] - 2025-02-26
6
-
7
- Initial release.
8
-
9
- ### Added
10
-
11
- **Core Features:**
12
- - Daily knowledge points with authentic American English expressions
13
- - Quiz system with 4 question types (multiple choice, Chinglish fix, fill blank, dialogue completion)
14
- - Duolingo-style gamification (XP, levels 1-20, streaks, badges, gems)
15
- - Error notebook for tracking and reviewing mistakes
16
- - 14-day content deduplication
17
-
18
- **CEFR Support:**
19
- - Complete CEFR level definitions (A1-C2) with Can-Do Statements
20
- - 12 sample JSON files covering all 6 CEFR levels
21
- - Content difficulty adjusted by user's CEFR level
22
-
23
- **Audio:**
24
- - TTS audio generation (Edge-TTS default, XunFei optional)
25
- - Async audio generation with background threading
26
- - Context manager pattern for reliable temp file cleanup
27
-
28
- **Configuration:**
29
- - 7-step onboarding flow
30
- - Initialization guard check before content generation
31
- - Centralized configuration constants
32
- - Prompt version control via `_meta.prompt_version` field
33
-
34
- **Documentation:**
35
- - SKILL.md, README.md, CLAUDE.md
36
- - OpenClaw deployment guide
37
- - Prompt templates (keypoint generation, quiz generation, etc.)
package/CLAUDE.md DELETED
@@ -1,275 +0,0 @@
1
- # SKILL: eng-lang-tutor
2
-
3
- ## 1. 项目目标
4
-
5
- 本项目主要为Agent添加「地道美式英语导师」的Skill,配合Agent所在环境的cron job实现定期的知识推送与测验。
6
-
7
- ### 1.1. 学习目标
8
-
9
- - 以提升地道的美式英语常用口语表达为主,兼顾听力与书面表达(正式场合)。
10
- - 避免传统的Chinglish的硬翻式尴尬表达。
11
- - 融入轻量游戏化教学,覆盖影视、新闻、游戏、体育、职场、社交、生活等用户感兴趣的场景来提升用户的粘性。
12
-
13
- ### 1.2. 语言范围
14
-
15
- - 以美式英语为主,包含俚语、口头语、缩写(gonna, gotta, kinda 等),能帮助用户区分「正式 vs 口语」。
16
-
17
- ### 1.3. 技能维度
18
-
19
- - 涉及词汇/短语、固定搭配、场景句型等口语与书面语中的表达方式。
20
-
21
- ## 2. 项目范围
22
-
23
- ### 2.1. MVP版本功能清单
24
-
25
- #### 2.1.1 导师模式
26
-
27
- Agent结合难度与偏好的设置,以及用户的历史学习进度动态生成并调整个性化的教学大纲与教学内容:
28
-
29
- | 功能 | 描述 | 频次 | 对应脚本 |
30
- |-----|------|-----|---------|
31
- | **知识点** | 地道美式英语日常沟通素材,包含场景、可替换说法、Chinglish陷阱+修正 | 每天 | state_manager.py |
32
- | **Quiz** | 3道轻量测验:1多选+1Chinglish修正+1随机(填空/对话)| 每天 | scorer.py |
33
- | **错题本** | 记录错误答案,支持手动添加,便于针对性复习 | 实时 | state_manager.py |
34
-
35
- **Quiz 结构:**
36
-
37
- | 题型 | 描述 | XP值 | 每日Quiz |
38
- |-----|------|-----|---------|
39
- | multiple_choice | 四选一,测试表达识别 | 10 | 1 (必选) |
40
- | chinglish_fix | 识别并修正Chinglish表达 | 15 | 1 (必选) |
41
- | fill_blank | 填空,完成对话 | 12 | 0-1 (随机) |
42
- | dialogue_completion | 选择合适的对话回应 | 15 | 0-1 (随机) |
43
-
44
- - **每日Quiz**: 3题,约37 XP,答对2题即过关
45
-
46
- #### 2.1.2 状态管理
47
-
48
- 保障整个教学过程可备份、可追溯、可回滚:
49
-
50
- | 组件 | 文件 | 用途 |
51
- |-----|------|-----|
52
- | 核心状态 | state.json | streak/xp/偏好/近期主题/错题本 |
53
- | 事件日志 | logs/events_YYYY-MM.jsonl | 追加式事件流水账 |
54
- | 每日内容 | daily/YYYY-MM-DD/*.json | 知识点/Quiz/用户答案 |
55
-
56
- #### 2.1.3 成就体系(Duolingo风格)
57
-
58
- **双等级体系说明:**
59
- - **能力等级 (CEFR)**:A1-C2,决定内容难度,与 App 无关的绝对语言能力
60
- - **活跃等级 (Level)**:1-20,衡量 App 使用深度,通过 XP 累积升级
61
-
62
- | 活跃等级 | 阶段 | XP 需求 |
63
- |---------|------|---------|
64
- | 1-5 | 启程者 (Starter) | 0-350 |
65
- | 6-10 | 行路人 (Traveler) | 550-2000 |
66
- | 11-15 | 探索者 (Explorer) | 2600-6000 |
67
- | 16-20 | 开拓者 (Pioneer) | 7200-15000 |
68
-
69
- | 组件 | 描述 |
70
- |-----|------|
71
- | **XP & 等级** | 1-20级,XP累积升级(启程者 → 行路人 → 探索者 → 开拓者) |
72
- | **连胜系统** | 连续学习天数,支持连胜冻结(50宝石) |
73
- | **徽章系统** | First Steps、Week Warrior、Month Master、Perfect 10、Vocab Hunter、Error Slayer |
74
- | **宝石系统** | 用于购买连胜冻结、提示等 |
75
-
76
- **徽章详情:**
77
-
78
- | 徽章 | 触发条件 | 宝石奖励 |
79
- |-----|---------|---------|
80
- | First Steps | 完成首次Quiz | 10 |
81
- | Week Warrior | 7天连胜 | 25 |
82
- | Month Master | 30天连胜 | 100 |
83
- | Perfect 10 | 10次完美Quiz | 50 |
84
- | Vocab Hunter | 学习100个表达 | 75 |
85
- | Error Slayer | 清除30个错题 | 30 |
86
-
87
- #### 2.1.4 系统配置
88
-
89
- | 配置项 | 描述 | 默认值 |
90
- |-------|------|-------|
91
- | CEFR等级 | A1-C2语言级别 | B1 |
92
- | 口语/书面比例 | 口语表达占比 | 70% |
93
- | 主题配比 | movies/news/gaming/sports/workplace/social/daily_life | 各主题权重 |
94
- | 导师风格 | humorous/rigorous/casual/professional | humorous |
95
- | 去重天数 | 避免重复内容的时间窗口 | 14天 |
96
-
97
- **配置常量:** 集中定义于 `scripts/core/constants.py`(宝石消耗、XP值、超时阈值等)
98
-
99
- ### 2.2. MVP版本不做 / 延后
100
-
101
- - **周周练**:5道综合性场景练习,覆盖本周知识点,约62 XP,答对3题即过关。计划周日推送。
102
- - **语音模式**:支持tts便于用户跟读,支持asr便于导师对口语表达进行打分。
103
-
104
- ### 2.3. 项目约束
105
-
106
- **核心原则:别问"怎么翻译",先问"这个场景美国人会怎么说",会让你立刻更像美国人。**
107
-
108
- #### 2.3.1 输出格式约束
109
-
110
- - **必须输出合法 JSON**(不要 markdown,不要代码块,不要多余文本)
111
- - 所有输出必须符合 `templates/` 目录下的 JSON Schema
112
-
113
- #### 2.3.2 内容质量约束
114
-
115
- - 语言风格:**地道美式**、轻松、可用、短句为主
116
- - 每条知识点必须包含:**场景、可替换说法、Chinglish 陷阱 + 修正**
117
- - 包含发音提示(gonna, gotta, wanna 等自然发音)
118
- - 提供文化背景和使用场景说明
119
-
120
- #### 2.3.3 去重策略(14天窗口)
121
-
122
- 使用 `scripts/utils/dedup.py` 实现三层去重:
123
-
124
- | 层级 | 方法 | 描述 |
125
- |-----|------|-----|
126
- | 1 | topic_fingerprint | 主题指纹匹配 |
127
- | 2 | 表达重叠 | 相同短语超过50%视为重复 |
128
- | 3 | 词根匹配 | 核心词汇结构相似 |
129
-
130
- #### 2.3.4 反直译原则
131
-
132
- - 优先给"美国人会怎么说",而不是"中文怎么翻英文"
133
- - 参考 `references/resources.md` 中的主题资源库
134
- - 使用 `references/prompt_templates.md` 中的LLM提示模板
135
-
136
- ## 3. 技术栈与SKILL结构
137
-
138
- ### 3.1. 技术栈与输出格式
139
-
140
- - 技术栈:Python 3.x
141
- - 触发方式:cron 触发 Agent 调用 Skill
142
-
143
- ### 3.2. 目录结构
144
-
145
- ```
146
- eng-lang-tutor/
147
- ├── SKILL.md # 核心技能文档
148
- ├── CLAUDE.md # 项目说明(本文件)
149
- ├── scripts/
150
- │ ├── __init__.py # 包入口,重导出核心类
151
- │ ├── core/
152
- │ │ ├── state_manager.py # 状态持久化与事件日志
153
- │ │ ├── error_notebook.py # 错题本管理
154
- │ │ ├── scorer.py # 答案评估与XP计算
155
- │ │ ├── gamification.py # 连胜/等级/徽章
156
- │ │ └── constants.py # 共享常量和工具函数
157
- │ ├── cli/
158
- │ │ ├── cli.py # CLI入口点
159
- │ │ └── command_parser.py # 命令解析
160
- │ ├── audio/
161
- │ │ ├── tts/ # TTS引擎(Coqui/Azure/Edge)
162
- │ │ ├── composer.py # 音频合成
163
- │ │ ├── converter.py # 格式转换
164
- │ │ ├── feishu_voice.py # 飞书语音发送
165
- │ │ └── utils.py # 音频工具函数
166
- │ ├── scheduling/
167
- │ │ └── cron_push.py # 定时推送
168
- │ └── utils/
169
- │ ├── dedup.py # 14天去重逻辑
170
- │ └── helpers.py # 通用工具函数
171
- ├── templates/
172
- │ ├── state_schema.json # 状态 JSON Schema
173
- │ ├── keypoint_schema.json # 知识点 JSON Schema
174
- │ └── quiz_schema.json # Quiz JSON Schema
175
- ├── references/
176
- │ ├── resources.md # 主题化英语学习资源库
177
- │ └── prompt_templates.md # LLM Prompt 模板
178
- ├── examples/ # 示例文件(按 CEFR 级别命名)
179
- │ ├── sample_keypoint_*.json # 知识点示例 (a1-c2)
180
- │ └── sample_quiz_*.json # Quiz示例 (a1-c2)
181
- └── (数据存储在外部目录)
182
- ~/.openclaw/state/eng-lang-tutor/ # 实际数据存储位置
183
- ├── state.json # 运行时状态
184
- ├── logs/
185
- │ └── events_YYYY-MM.jsonl
186
- └── daily/
187
- └── YYYY-MM-DD/
188
- ├── keypoint.json
189
- ├── quiz.json
190
- └── user_answers.json
191
-
192
- 注: 可通过 OPENCLAW_STATE_DIR 环境变量自定义存储位置
193
- ```
194
-
195
- ## 4. 功能切片实现
196
-
197
- ### 4.1 功能切片与脚本对应
198
-
199
- | 切片 | 对应脚本 | 输入 | 输出 |
200
- |-----|---------|------|-----|
201
- | 状态加载/保存 | core/state_manager.py | - | state.json |
202
- | 事件日志 | core/state_manager.py | event_type, data | events_YYYY-MM.jsonl |
203
- | 每日内容保存 | core/state_manager.py | content_type, content | daily/YYYY-MM-DD/*.json |
204
- | 答案评估 | core/scorer.py | quiz.json + user_answers | results + updated state |
205
- | XP计算 | core/scorer.py | correct_count, streak | XP with multiplier |
206
- | 连胜更新 | core/gamification.py | study_date | new_streak |
207
- | 等级更新 | core/gamification.py | XP | level |
208
- | 徽章检查 | core/gamification.py | progress | new_badges |
209
- | 去重检查 | utils/dedup.py | new_content, recent_content | is_duplicate |
210
- | 异步音频生成 | core/state_manager.py | keypoint.json | audio/*.mp3 |
211
-
212
- ### 4.2 核心工作流
213
-
214
- #### 4.2.1 每日知识点生成
215
-
216
- ```
217
- 1. state_manager.load_state()
218
- 2. dedup.get_excluded_topics(state)
219
- 3. LLM生成知识点(使用prompt_templates.md)
220
- 4. dedup.check_duplicate(new_content, recent_content)
221
- 5. 验证JSON Schema
222
- 6. state_manager.save_daily_content('keypoint', content)
223
- 7. state_manager.append_event('keypoint_generated', {...})
224
- ```
225
-
226
- #### 4.2.2 Quiz评估流程
227
-
228
- ```
229
- 1. state_manager.load_daily_content('quiz')
230
- 2. 读取 user_answers.json
231
- 3. scorer.evaluate_quiz(quiz, answers, state)
232
- 4. gamification.update_streak(state, today)
233
- 5. gamification.update_level(state)
234
- 6. gamification.check_badges(state)
235
- 7. state_manager.save_state(state)
236
- 8. state_manager.append_event('quiz_completed', results)
237
- ```
238
-
239
- ## 5. 开发准则
240
-
241
- ### 5.1 Skill文档规范
242
-
243
- - SKILL.md文档要详细但不能过长,聚焦核心步骤,细节可以放在代码示例里。建议**800-1500 tokens**。
244
- - Skills 必须包含可执行的步骤、代码示例、验证检查点。
245
- - 设计 Skills 时考虑复用性(模块化)。一个 Skill 解决一类问题,不是一次性的硬编码。
246
-
247
- ### 5.2 确定性验证器
248
-
249
- 每个任务都有**确定性验证器**(deterministic verifier):这不是用 LLM 评判,而是程序化断言。
250
-
251
- | 验证项 | 验证方法 | 对应工具 |
252
- |-------|---------|---------|
253
- | JSON格式 | json.loads() | Python标准库 |
254
- | JSON Schema | schema validation | templates/*.json |
255
- | XP计算 | 公式验证 | scorer.py |
256
- | 连胜更新 | 日期比较 | gamification.py |
257
- | 去重检查 | 指纹/表达匹配 | dedup.py |
258
-
259
- ### 5.3 聚焦核心路径
260
-
261
- 不要过度工程化:比如应该聚焦 80% 场景的核心路径,而非覆盖尽可能多的edge cases。
262
-
263
- ### 5.4 可复用资源
264
-
265
- | 资源 | 位置 | 用途 |
266
- |-----|------|-----|
267
- | skill-creator | github.com/anthropics/skills | 自动生成skill骨架 |
268
- | awesome-language-learning | github.com/Vuizur/awesome-language-learning | 语言学习工具集合 |
269
- | 主题资源库 | references/resources.md | 美剧/新闻/游戏/体育/职场/生活 |
270
- | LLM提示模板 | references/prompt_templates.md | 知识点/Quiz生成模板 |
271
-
272
- ### 5.5 行动准则
273
-
274
- - 未经允许不得擅自执行git commit、push和publish等相关动作
275
- - 针对耗费较长时间做debug或者反复掉坑的情况,需要及时总结复盘,并将经验精炼到本文档中
@@ -1,177 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * eng-lang-tutor CLI
5
- *
6
- * Commands:
7
- * install - Install the skill to ~/.openclaw/skills/eng-lang-tutor/
8
- * uninstall - Remove the skill from ~/.openclaw/skills/eng-lang-tutor/
9
- */
10
-
11
- const path = require('path');
12
- const fs = require('fs');
13
- const os = require('os');
14
-
15
- const SKILL_NAME = 'eng-lang-tutor';
16
- const SKILLS_DIR = path.join(os.homedir(), '.openclaw', 'skills');
17
- const SKILL_TARGET = path.join(SKILLS_DIR, SKILL_NAME);
18
-
19
- // Get the package root directory (where package.json is)
20
- const PACKAGE_ROOT = path.resolve(__dirname, '..');
21
-
22
- function getSkillSourceDir() {
23
- return PACKAGE_ROOT;
24
- }
25
-
26
- function install() {
27
- console.log(`Installing ${SKILL_NAME} skill...`);
28
-
29
- const sourceDir = getSkillSourceDir();
30
-
31
- // Check if source directory exists
32
- if (!fs.existsSync(sourceDir)) {
33
- console.error(`Error: Source directory not found: ${sourceDir}`);
34
- process.exit(1);
35
- }
36
-
37
- // Create skills directory if it doesn't exist
38
- if (!fs.existsSync(SKILLS_DIR)) {
39
- fs.mkdirSync(SKILLS_DIR, { recursive: true });
40
- console.log(`Created skills directory: ${SKILLS_DIR}`);
41
- }
42
-
43
- // Remove existing installation if present
44
- if (fs.existsSync(SKILL_TARGET)) {
45
- console.log(`Removing existing installation at ${SKILL_TARGET}...`);
46
- fs.rmSync(SKILL_TARGET, { recursive: true, force: true });
47
- }
48
-
49
- // Create target directory
50
- fs.mkdirSync(SKILL_TARGET, { recursive: true });
51
-
52
- // Files and directories to copy
53
- const itemsToCopy = [
54
- 'scripts',
55
- 'templates',
56
- 'references',
57
- 'examples',
58
- 'docs',
59
- 'SKILL.md',
60
- 'CLAUDE.md',
61
- 'README.md',
62
- 'README_EN.md',
63
- 'requirements.txt'
64
- ];
65
-
66
- // Copy each item
67
- for (const item of itemsToCopy) {
68
- const sourcePath = path.join(sourceDir, item);
69
- const targetPath = path.join(SKILL_TARGET, item);
70
-
71
- if (!fs.existsSync(sourcePath)) {
72
- console.log(` Skipping ${item} (not found)`);
73
- continue;
74
- }
75
-
76
- try {
77
- if (fs.statSync(sourcePath).isDirectory()) {
78
- copyDir(sourcePath, targetPath);
79
- } else {
80
- fs.copyFileSync(sourcePath, targetPath);
81
- }
82
- console.log(` Copied ${item}`);
83
- } catch (err) {
84
- console.error(` Error copying ${item}: ${err.message}`);
85
- }
86
- }
87
-
88
- console.log(`\n✓ ${SKILL_NAME} installed to ${SKILL_TARGET}`);
89
- console.log('\nNext steps:');
90
- console.log(' 1. Install Python dependencies:');
91
- console.log(` pip install -r ${SKILL_TARGET}/requirements.txt`);
92
- console.log(' 2. Restart your OpenClaw agent');
93
- console.log(' 3. Configure the skill through the onboarding flow');
94
- }
95
-
96
- function uninstall() {
97
- console.log(`Uninstalling ${SKILL_NAME} skill...`);
98
-
99
- if (!fs.existsSync(SKILL_TARGET)) {
100
- console.log(`${SKILL_NAME} is not installed.`);
101
- return;
102
- }
103
-
104
- try {
105
- fs.rmSync(SKILL_TARGET, { recursive: true, force: true });
106
- console.log(`✓ ${SKILL_NAME} removed from ${SKILL_TARGET}`);
107
- console.log('\nNote: Your learning data is preserved at:');
108
- console.log(' ~/.openclaw/state/eng-lang-tutor/');
109
- console.log('\nTo completely remove all data, run:');
110
- console.log(' rm -rf ~/.openclaw/state/eng-lang-tutor');
111
- } catch (err) {
112
- console.error(`Error uninstalling: ${err.message}`);
113
- process.exit(1);
114
- }
115
- }
116
-
117
- function copyDir(src, dest) {
118
- fs.mkdirSync(dest, { recursive: true });
119
-
120
- const entries = fs.readdirSync(src, { withFileTypes: true });
121
-
122
- for (const entry of entries) {
123
- const srcPath = path.join(src, entry.name);
124
- const destPath = path.join(dest, entry.name);
125
-
126
- if (entry.isDirectory()) {
127
- copyDir(srcPath, destPath);
128
- } else {
129
- fs.copyFileSync(srcPath, destPath);
130
- }
131
- }
132
- }
133
-
134
- function showHelp() {
135
- console.log(`
136
- eng-lang-tutor - English Language Tutor Skill for OpenClaw
137
-
138
- Usage:
139
- eng-lang-tutor <command>
140
-
141
- Commands:
142
- install Install the skill to ~/.openclaw/skills/eng-lang-tutor/
143
- uninstall Remove the skill (preserves learning data)
144
- help Show this help message
145
-
146
- Environment Variables:
147
- OPENCLAW_STATE_DIR Custom directory for learning data
148
- (default: ~/.openclaw/state/eng-lang-tutor)
149
-
150
- Examples:
151
- eng-lang-tutor install
152
- eng-lang-tutor uninstall
153
- `);
154
- }
155
-
156
- // Main
157
- const command = process.argv[2];
158
-
159
- switch (command) {
160
- case 'install':
161
- install();
162
- break;
163
- case 'uninstall':
164
- uninstall();
165
- break;
166
- case 'help':
167
- case '--help':
168
- case '-h':
169
- showHelp();
170
- break;
171
- default:
172
- if (command) {
173
- console.error(`Unknown command: ${command}`);
174
- }
175
- showHelp();
176
- process.exit(command ? 1 : 0);
177
- }
@@ -1,241 +0,0 @@
1
- # OpenClaw 部署指南
2
-
3
- 本文档说明如何将 `eng-lang-tutor` skill 部署到 OpenClaw 服务器,并通过 Discord 等渠道使用。
4
-
5
- ## 1. 前置条件
6
-
7
- - 一台已安装 OpenClaw 的服务器
8
- - OpenClaw Gateway 正在运行
9
- - 已配置好 Discord Bot(可选其他渠道)
10
-
11
- ## 2. 部署步骤
12
-
13
- ### 2.1 上传 Skill 到服务器
14
-
15
- **方式 A:使用 SCP 上传**
16
-
17
- ```bash
18
- # 在本地机器上执行
19
- scp -r /path/to/eng-lang-tutor user@your-server:~/.openclaw/skills/
20
- ```
21
-
22
- **方式 B:使用 Git Clone**
23
-
24
- ```bash
25
- # 在服务器上执行
26
- cd ~/.openclaw/skills/
27
- git clone https://github.com/rookiestar/eng-lang-tutor.git
28
- ```
29
-
30
- ### 2.2 确认目录结构
31
-
32
- 确保目录结构如下:
33
-
34
- ```
35
- ~/.openclaw/skills/eng-lang-tutor/
36
- ├── SKILL.md # 核心技能文件(必需)
37
- ├── scripts/ # Python 脚本
38
- │ ├── core/ # 核心模块
39
- │ │ ├── state_manager.py
40
- │ │ ├── scorer.py
41
- │ │ ├── gamification.py
42
- │ │ ├── constants.py
43
- │ │ └── error_notebook.py
44
- │ ├── cli/ # 命令行模块
45
- │ ├── scheduling/ # 调度模块
46
- │ ├── utils/ # 工具模块
47
- │ └── audio/ # 音频模块
48
- ├── templates/ # JSON Schema + Prompt 模板
49
- ├── references/ # 参考资源
50
- ├── examples/ # 示例文件(按 CEFR 级别)
51
- └── (数据存储在外部目录)
52
- ~/.openclaw/state/eng-lang-tutor/ # 运行时数据(自动创建)
53
- ```
54
-
55
- ### 2.3 验证 Skill 加载
56
-
57
- ```bash
58
- # 查看已加载的 skills
59
- openclaw skills list
60
-
61
- # 查看特定 skill 详情
62
- openclaw skills info eng-lang-tutor
63
- ```
64
-
65
- ## 3. 配置 Discord 渠道
66
-
67
- ### 3.1 创建 Discord Bot
68
-
69
- 1. 访问 [Discord Developer Portal](https://discord.com/developers/applications)
70
- 2. 创建新应用,命名为你的 Bot
71
- 3. 进入 Bot 页面,创建 Bot 并获取 Token
72
- 4. 启用 Message Content Intent
73
-
74
- ### 3.2 配置 OpenClaw
75
-
76
- ```bash
77
- # 设置 Discord Token
78
- openclaw config set discord.token YOUR_BOT_TOKEN
79
-
80
- # 设置服务器 ID(可选)
81
- openclaw config set discord.guildId YOUR_SERVER_ID
82
- ```
83
-
84
- ### 3.3 邀请 Bot 到服务器
85
-
86
- 1. 在 Discord Developer Portal 中,进入 OAuth2 > URL Generator
87
- 2. 选择 `bot` 和 `applications.commands` scope
88
- 3. 选择所需权限(Send Messages, Read Messages 等)
89
- 4. 复制生成的邀请链接并在浏览器中打开
90
- 5. 选择要添加 Bot 的服务器
91
-
92
- ### 3.4 完成配对
93
-
94
- 首次与 Bot 对话时,会收到配对码:
95
-
96
- ```bash
97
- # 在服务器上执行配对
98
- openclaw pairing approve discord YOUR_PAIRING_CODE
99
- ```
100
-
101
- ## 4. 使用 Skill
102
-
103
- ### 4.1 通过 Discord 使用
104
-
105
- 在 Discord 中与 Bot 对话,触发英语学习功能:
106
-
107
- ```
108
- # 触发今日知识点
109
- 今天有什么英语知识点?
110
-
111
- # 触发 Quiz
112
- 给我出个 Quiz
113
-
114
- # 查看学习进度
115
- 我的学习进度怎么样?
116
- ```
117
-
118
- ### 4.2 设置定时推送(cron)
119
-
120
- ```bash
121
- # 编辑 crontab
122
- crontab -e
123
-
124
- # 添加每日定时推送
125
- # 早上 6:45 推送知识点
126
- 45 6 * * * openclaw system event --text "Use eng-lang-tutor skill. Push today's keypoint." --mode now
127
-
128
- # 晚上 22:45 推送 Quiz
129
- 45 22 * * * openclaw system event --text "Use eng-lang-tutor skill. Push today's quiz invitation." --mode now
130
- ```
131
-
132
- ## 5. 进阶配置
133
-
134
- ### 5.1 配置用户偏好
135
-
136
- 在 Discord 中与 Bot 对话设置:
137
-
138
- ```
139
- 设置我的 CEFR 等级为 B2
140
- 我的导师风格设为严谨
141
- 调整主题配比,增加职场内容的比例
142
- ```
143
-
144
- ### 5.2 配置环境变量
145
-
146
- 在服务器上设置 API 密钥(如果使用外部 LLM):
147
-
148
- ```bash
149
- # 编辑 OpenClaw 配置
150
- openclaw config set model.provider anthropic
151
- openclaw config set model.api_key YOUR_API_KEY
152
- ```
153
-
154
- ### 5.3 数据持久化
155
-
156
- 数据存储在 `~/.openclaw/state/eng-lang-tutor/` 目录,确保有正确的写入权限:
157
-
158
- ```bash
159
- chmod -R 755 ~/.openclaw/state/eng-lang-tutor/
160
- ```
161
-
162
- 可通过环境变量 `OPENCLAW_STATE_DIR` 自定义数据目录。
163
-
164
- ## 6. 故障排查
165
-
166
- ### 6.1 Skill 未加载
167
-
168
- ```bash
169
- # 检查 skill 目录
170
- ls -la ~/.openclaw/skills/eng-lang-tutor/
171
-
172
- # 检查 SKILL.md 是否存在
173
- cat ~/.openclaw/skills/eng-lang-tutor/SKILL.md
174
-
175
- # 重启 Gateway
176
- openclaw gateway restart
177
- ```
178
-
179
- ### 6.2 Discord 无响应
180
-
181
- ```bash
182
- # 检查 Gateway 日志
183
- openclaw logs
184
-
185
- # 验证 Discord Token
186
- openclaw config get discord.token
187
-
188
- # 检查 Bot 状态
189
- openclaw doctor
190
- ```
191
-
192
- ### 6.3 Python 脚本错误
193
-
194
- ```bash
195
- # 检查 Python 环境
196
- python3 --version
197
-
198
- # 安装依赖(如有需要)
199
- pip3 install -r ~/.openclaw/skills/eng-lang-tutor/requirements.txt
200
-
201
- # 手动测试脚本
202
- cd ~/.openclaw/skills/eng-lang-tutor/
203
- python3 -m scripts.cli.cli stats
204
- ```
205
-
206
- ## 7. 架构图
207
-
208
- ```
209
- ┌─────────────────────────────────────────────────────────┐
210
- │ Discord │
211
- │ (用户交互) │
212
- └─────────────────────────┬───────────────────────────────┘
213
-
214
-
215
- ┌─────────────────────────────────────────────────────────┐
216
- │ OpenClaw Gateway │
217
- │ (消息路由 + 会话管理) │
218
- └─────────────────────────┬───────────────────────────────┘
219
-
220
-
221
- ┌─────────────────────────────────────────────────────────┐
222
- │ eng-lang-tutor Skill │
223
- │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
224
- │ │ SKILL.md │ │ scripts/ │ │ ~/.openclaw │ │
225
- │ │ (技能描述) │ │ (Python代码) │ │ (状态存储) │ │
226
- │ └─────────────┘ └─────────────┘ └─────────────┘ │
227
- └─────────────────────────────────────────────────────────┘
228
-
229
-
230
- ┌─────────────────────────────────────────────────────────┐
231
- │ LLM Provider │
232
- │ (Claude/GPT/Qwen 等) │
233
- └─────────────────────────────────────────────────────────┘
234
- ```
235
-
236
- ## 8. 参考链接
237
-
238
- - [OpenClaw 官方文档](https://docs.openclaw.ai)
239
- - [OpenClaw GitHub](https://github.com/openclaw/openclaw)
240
- - [Discord 开发者门户](https://discord.com/developers/applications)
241
- - [OpenClaw Skills 开发指南](https://docs.openclaw.ai/skills)