kids-coding-tutor-mcp 1.3.0__tar.gz
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.
- kids_coding_tutor_mcp-1.3.0/LICENSE.txt +21 -0
- kids_coding_tutor_mcp-1.3.0/MANIFEST.in +2 -0
- kids_coding_tutor_mcp-1.3.0/PKG-INFO +99 -0
- kids_coding_tutor_mcp-1.3.0/README.md +76 -0
- kids_coding_tutor_mcp-1.3.0/pyproject.toml +39 -0
- kids_coding_tutor_mcp-1.3.0/setup.cfg +4 -0
- kids_coding_tutor_mcp-1.3.0/src/kids_coding_tutor_mcp/__init__.py +9 -0
- kids_coding_tutor_mcp-1.3.0/src/kids_coding_tutor_mcp/__main__.py +8 -0
- kids_coding_tutor_mcp-1.3.0/src/kids_coding_tutor_mcp/data/records.jsonl +191 -0
- kids_coding_tutor_mcp-1.3.0/src/kids_coding_tutor_mcp/server.py +281 -0
- kids_coding_tutor_mcp-1.3.0/src/kids_coding_tutor_mcp.egg-info/PKG-INFO +99 -0
- kids_coding_tutor_mcp-1.3.0/src/kids_coding_tutor_mcp.egg-info/SOURCES.txt +14 -0
- kids_coding_tutor_mcp-1.3.0/src/kids_coding_tutor_mcp.egg-info/dependency_links.txt +1 -0
- kids_coding_tutor_mcp-1.3.0/src/kids_coding_tutor_mcp.egg-info/entry_points.txt +2 -0
- kids_coding_tutor_mcp-1.3.0/src/kids_coding_tutor_mcp.egg-info/requires.txt +1 -0
- kids_coding_tutor_mcp-1.3.0/src/kids_coding_tutor_mcp.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 yiten hua
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kids-coding-tutor-mcp
|
|
3
|
+
Version: 1.3.0
|
|
4
|
+
Summary: 给 5-16 岁孩子、家长和老师的编程学习助手:Scratch 概念白话讲解、1-4 级考级考点与备考、Python 项目实战指导、课程备课。191 条蒸馏知识,纯中文作答。
|
|
5
|
+
Author: yiten hua
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/yiten5885-a11y/kids-coding-tutor
|
|
8
|
+
Project-URL: Source, https://github.com/yiten5885-a11y/kids-coding-tutor
|
|
9
|
+
Keywords: mcp,kids-coding,scratch,python,education
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Education
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Education
|
|
17
|
+
Classifier: Natural Language :: Chinese (Simplified)
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE.txt
|
|
21
|
+
Requires-Dist: mcp<2,>=1.0
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# 少儿编程通 MCP 服务器
|
|
25
|
+
|
|
26
|
+
把「少儿编程通」skill(191 条记录)变成可远程调用的 MCP 服务器,供 Qoder 这类远端 agent 靠 URL 调用。只读原 skill 的数据文件,不改动原 skill 包。
|
|
27
|
+
|
|
28
|
+
- 数据来源:`/home/hatch/workspace/skills/kids-coding-tutor/assets/records.jsonl`(可用环境变量 `KIDS_CODING_RECORDS` 换路径)
|
|
29
|
+
- 检索/规划/刷题逻辑与原 skill 的 `search.py`、`answer.py`、`plan.py`、`quiz.py`、`videos.py` 一致
|
|
30
|
+
- 技术栈:官方 mcp SDK(v1,FastMCP)+ streamable-http transport,Python 3.12,依赖装在项目自带 `.venv`,不动系统全局
|
|
31
|
+
|
|
32
|
+
## 本地启动
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
cd ~/workspace/mcp-servers/kids-coding-tutor
|
|
36
|
+
.venv/bin/python server.py --host 127.0.0.1 --port 8000
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
启动后 MCP 端点:`http://127.0.0.1:8000/mcp`
|
|
40
|
+
|
|
41
|
+
自测(需另开一个终端,先启动服务器再跑):
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
cd ~/workspace/mcp-servers/kids-coding-tutor
|
|
45
|
+
.venv/bin/python test_client.py http://127.0.0.1:8000/mcp
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Qoder 配置示例(mcp.json,URL 形式)
|
|
49
|
+
|
|
50
|
+
端口先写占位,部署到服务器后把 `<服务器地址>` 和 `<端口>` 换成实际值:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"mcpServers": {
|
|
55
|
+
"kids-coding-tutor": {
|
|
56
|
+
"url": "http://<服务器地址>:<端口>/mcp",
|
|
57
|
+
"transport": "streamable-http"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
在 Qoder 里添加远程 MCP 服务器时粘贴上面这个 URL 即可(若 Qoder 界面字段名不同,按其提示填 URL 与 streamable-http 类型)。
|
|
64
|
+
|
|
65
|
+
注意:本服务器的 streamable-http 端点本身不带鉴权,不要直接裸暴露到公网;远端部署时请放在反向代理后加鉴权,或只放在内网/可信网络使用。
|
|
66
|
+
|
|
67
|
+
## 工具(7 个,全部返回纯中文正文)
|
|
68
|
+
|
|
69
|
+
| 工具 | 说明 |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| `search_knowledge(query, type?, limit?=5)` | 按关键词搜记录,计分规则与原 search.py 一致(标题命中 x3、关键词精确命中 +5);type 可限定 concept、exam_point、lesson、project、faq、practice、tutorial、parent_guide、video_lesson |
|
|
72
|
+
| `get_record(id)` | 按编号取一条全文(含 title/body/example/tip) |
|
|
73
|
+
| `list_exam_points(level)` | 列某考级(1-4)全部考级考点(实测:1 级 7 条、2 级 8 条、3 级 7 条、4 级 5 条) |
|
|
74
|
+
| `get_practice(level, n?=8)` | 取某级别原创模拟题(各级别 8 道),每题含题目、选项、答案与解析,并注明“原创模拟题,非真题” |
|
|
75
|
+
| `study_plan(age, goal?, level?)` | 学习规划,goal 三选一:兴趣 / 考级 / 转python,含路线、课程清单与 4 周节奏 |
|
|
76
|
+
| `list_video_courses()` | 列全部视频课(实测 3 门:打砖块 13 节、Boss 战射击游戏 7 节、scratch2.0 零散 4 节) |
|
|
77
|
+
| `get_video_lesson(course?, id?)` | 按课程关键词列章节,或按编号取某节配套讲解全文 |
|
|
78
|
+
|
|
79
|
+
## 实测结果(2026-10-26,MCP SDK 客户端实际调通)
|
|
80
|
+
|
|
81
|
+
| 调用 | 结果 |
|
|
82
|
+
| --- | --- |
|
|
83
|
+
| search_knowledge(搜“变量”) | 成功,1016 字,首条【三级考点:变量】 |
|
|
84
|
+
| search_knowledge(搜“变量”,type=concept) | 成功,1222 字,首条【Scratch:变量是什么】 |
|
|
85
|
+
| get_record(exam-L1-overview) | 成功,231 字 |
|
|
86
|
+
| list_exam_points(level=1) | 成功,1459 字,1 级共 7 条 |
|
|
87
|
+
| get_practice(level=1) | 成功,1073 字,8 道,含答案、解析与“非真题”注明 |
|
|
88
|
+
| study_plan(age=9,goal=考级,level=0) | 成功,503 字,含 4 周节奏与备考指引 |
|
|
89
|
+
| list_video_courses | 成功,3 门课 |
|
|
90
|
+
| get_video_lesson(course=打砖块) | 成功,13 节章节列表 |
|
|
91
|
+
| get_video_lesson(id=vl-brick-11) | 成功,161 字配套讲解全文 |
|
|
92
|
+
|
|
93
|
+
7 个工具全部调通,无失败项。
|
|
94
|
+
|
|
95
|
+
## 目录
|
|
96
|
+
|
|
97
|
+
- `server.py`:服务器本体(FastMCP + streamable-http)
|
|
98
|
+
- `test_client.py`:SDK 客户端实测脚本
|
|
99
|
+
- `.venv/`:项目自带虚拟环境(mcp 1.30.0,FastMCP)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# 少儿编程通 MCP 服务器
|
|
2
|
+
|
|
3
|
+
把「少儿编程通」skill(191 条记录)变成可远程调用的 MCP 服务器,供 Qoder 这类远端 agent 靠 URL 调用。只读原 skill 的数据文件,不改动原 skill 包。
|
|
4
|
+
|
|
5
|
+
- 数据来源:`/home/hatch/workspace/skills/kids-coding-tutor/assets/records.jsonl`(可用环境变量 `KIDS_CODING_RECORDS` 换路径)
|
|
6
|
+
- 检索/规划/刷题逻辑与原 skill 的 `search.py`、`answer.py`、`plan.py`、`quiz.py`、`videos.py` 一致
|
|
7
|
+
- 技术栈:官方 mcp SDK(v1,FastMCP)+ streamable-http transport,Python 3.12,依赖装在项目自带 `.venv`,不动系统全局
|
|
8
|
+
|
|
9
|
+
## 本地启动
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
cd ~/workspace/mcp-servers/kids-coding-tutor
|
|
13
|
+
.venv/bin/python server.py --host 127.0.0.1 --port 8000
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
启动后 MCP 端点:`http://127.0.0.1:8000/mcp`
|
|
17
|
+
|
|
18
|
+
自测(需另开一个终端,先启动服务器再跑):
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
cd ~/workspace/mcp-servers/kids-coding-tutor
|
|
22
|
+
.venv/bin/python test_client.py http://127.0.0.1:8000/mcp
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Qoder 配置示例(mcp.json,URL 形式)
|
|
26
|
+
|
|
27
|
+
端口先写占位,部署到服务器后把 `<服务器地址>` 和 `<端口>` 换成实际值:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"mcpServers": {
|
|
32
|
+
"kids-coding-tutor": {
|
|
33
|
+
"url": "http://<服务器地址>:<端口>/mcp",
|
|
34
|
+
"transport": "streamable-http"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
在 Qoder 里添加远程 MCP 服务器时粘贴上面这个 URL 即可(若 Qoder 界面字段名不同,按其提示填 URL 与 streamable-http 类型)。
|
|
41
|
+
|
|
42
|
+
注意:本服务器的 streamable-http 端点本身不带鉴权,不要直接裸暴露到公网;远端部署时请放在反向代理后加鉴权,或只放在内网/可信网络使用。
|
|
43
|
+
|
|
44
|
+
## 工具(7 个,全部返回纯中文正文)
|
|
45
|
+
|
|
46
|
+
| 工具 | 说明 |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| `search_knowledge(query, type?, limit?=5)` | 按关键词搜记录,计分规则与原 search.py 一致(标题命中 x3、关键词精确命中 +5);type 可限定 concept、exam_point、lesson、project、faq、practice、tutorial、parent_guide、video_lesson |
|
|
49
|
+
| `get_record(id)` | 按编号取一条全文(含 title/body/example/tip) |
|
|
50
|
+
| `list_exam_points(level)` | 列某考级(1-4)全部考级考点(实测:1 级 7 条、2 级 8 条、3 级 7 条、4 级 5 条) |
|
|
51
|
+
| `get_practice(level, n?=8)` | 取某级别原创模拟题(各级别 8 道),每题含题目、选项、答案与解析,并注明“原创模拟题,非真题” |
|
|
52
|
+
| `study_plan(age, goal?, level?)` | 学习规划,goal 三选一:兴趣 / 考级 / 转python,含路线、课程清单与 4 周节奏 |
|
|
53
|
+
| `list_video_courses()` | 列全部视频课(实测 3 门:打砖块 13 节、Boss 战射击游戏 7 节、scratch2.0 零散 4 节) |
|
|
54
|
+
| `get_video_lesson(course?, id?)` | 按课程关键词列章节,或按编号取某节配套讲解全文 |
|
|
55
|
+
|
|
56
|
+
## 实测结果(2026-10-26,MCP SDK 客户端实际调通)
|
|
57
|
+
|
|
58
|
+
| 调用 | 结果 |
|
|
59
|
+
| --- | --- |
|
|
60
|
+
| search_knowledge(搜“变量”) | 成功,1016 字,首条【三级考点:变量】 |
|
|
61
|
+
| search_knowledge(搜“变量”,type=concept) | 成功,1222 字,首条【Scratch:变量是什么】 |
|
|
62
|
+
| get_record(exam-L1-overview) | 成功,231 字 |
|
|
63
|
+
| list_exam_points(level=1) | 成功,1459 字,1 级共 7 条 |
|
|
64
|
+
| get_practice(level=1) | 成功,1073 字,8 道,含答案、解析与“非真题”注明 |
|
|
65
|
+
| study_plan(age=9,goal=考级,level=0) | 成功,503 字,含 4 周节奏与备考指引 |
|
|
66
|
+
| list_video_courses | 成功,3 门课 |
|
|
67
|
+
| get_video_lesson(course=打砖块) | 成功,13 节章节列表 |
|
|
68
|
+
| get_video_lesson(id=vl-brick-11) | 成功,161 字配套讲解全文 |
|
|
69
|
+
|
|
70
|
+
7 个工具全部调通,无失败项。
|
|
71
|
+
|
|
72
|
+
## 目录
|
|
73
|
+
|
|
74
|
+
- `server.py`:服务器本体(FastMCP + streamable-http)
|
|
75
|
+
- `test_client.py`:SDK 客户端实测脚本
|
|
76
|
+
- `.venv/`:项目自带虚拟环境(mcp 1.30.0,FastMCP)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "kids-coding-tutor-mcp"
|
|
7
|
+
version = "1.3.0"
|
|
8
|
+
description = "给 5-16 岁孩子、家长和老师的编程学习助手:Scratch 概念白话讲解、1-4 级考级考点与备考、Python 项目实战指导、课程备课。191 条蒸馏知识,纯中文作答。"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "yiten hua" }]
|
|
13
|
+
keywords = ["mcp", "kids-coding", "scratch", "python", "education"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Education",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.10",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Topic :: Education",
|
|
22
|
+
"Natural Language :: Chinese (Simplified)",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"mcp>=1.0,<2",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.scripts]
|
|
29
|
+
kids-coding-tutor-mcp = "kids_coding_tutor_mcp.server:main"
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/yiten5885-a11y/kids-coding-tutor"
|
|
33
|
+
Source = "https://github.com/yiten5885-a11y/kids-coding-tutor"
|
|
34
|
+
|
|
35
|
+
[tool.setuptools.packages.find]
|
|
36
|
+
where = ["src"]
|
|
37
|
+
|
|
38
|
+
[tool.setuptools.package-data]
|
|
39
|
+
kids_coding_tutor_mcp = ["data/records.jsonl"]
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
{"id": "exam-L1-overview", "type": "exam_point", "title": "Scratch 一级考试总览", "track": "scratch", "level": 1, "keywords": ["一级", "考级", "总览", "题型", "分值"], "body": "一级考的是 Scratch 基本功:会用软件、会拼顺序结构的程序。题型是单选 25 题(50 分)+ 判断 10 题(20 分)+ 编程 2 题(30 分),满分 100。编程大题只有 2 道,分值重,要重点练。", "example": "", "tip": "一级的编程题考顺序结构:移动、旋转、换造型、放声音、换背景按要求排好就行,别用没学过的积木。"}
|
|
2
|
+
{"id": "exam-L1-env", "type": "exam_point", "title": "一级考点:熟悉编程软件", "track": "scratch", "level": 1, "keywords": ["一级", "界面", "舞台区", "角色区", "脚本区", "保存"], "body": "要认得 Scratch 界面的五大区域:舞台区(看效果)、角色区(管演员)、模块区/积木区(拿积木)、脚本区(拼程序),还有造型、声音、背景三个标签页。会新建作品、保存作品、打开旧作品,会点绿旗运行、点红点停止。这一块在“平台操作”14 分里。", "example": "", "tip": "常考:点哪个按钮运行程序?答:舞台区上方的绿旗。"}
|
|
3
|
+
{"id": "exam-L1-sprite", "type": "exam_point", "title": "一级考点:角色的导入与设置", "track": "scratch", "level": 1, "keywords": ["一级", "角色", "导入", "库", "绘制", "大小"], "body": "会三种加角色的方法:从角色库里选、自己画一个、从本地文件导入。会调角色大小。角色导入和大小设置属于顺序结构流程的一部分,编程题里经常要求“添加某个角色并设置大小”。", "example": "", "tip": "从本地导入时注意文件格式,考场一般用库导入和绘制两种。"}
|
|
4
|
+
{"id": "exam-L1-bg", "type": "exam_point", "title": "一级考点:背景的认识与切换", "track": "scratch", "level": 1, "keywords": ["一级", "背景", "切换", "舞台"], "body": "会给舞台选合适的背景,会用“换成背景”积木切换背景。记住背景和角色的区别:背景是舞台的布,角色是台上的演员,背景不能移动旋转,只能换。造型与背景切换共 30 分,是第一大分值块。", "example": "", "tip": "背景切换积木在“外观”分类里,别和“换造型”搞混:换造型是角色换衣服,换背景是舞台换布景。"}
|
|
5
|
+
{"id": "exam-L1-motion", "type": "exam_point", "title": "一级考点:角色的操作", "track": "scratch", "level": 1, "keywords": ["一级", "移动", "旋转", "造型", "外观"], "body": "会让角色移动(移动几步、移到哪里)、旋转(左转右转、面向方向)、切换造型。角色操作共 30 分,和背景并列第一大块。编程题几乎必考:让角色按要求走一段路、中途换几次造型。", "example": "", "tip": "“面向 90 度”是朝右,这是 Scratch 的默认方向,考选择题常考。"}
|
|
6
|
+
{"id": "exam-L1-sound", "type": "exam_point", "title": "一级考点:声音", "track": "scratch", "level": 1, "keywords": ["一级", "声音", "背景音乐", "音量"], "body": "会导入声音、把声音设成背景音乐、调音量、播放和停止声音。声音共 16 分。编程题常要求“播放一段音频作为背景音乐”,记得用“播放声音直到结束”还是“播放声音”的区别:前者等播完再往下走,后者播着播着就往下走了。", "example": "", "tip": "要背景音乐一直响,用“重复播放声音”或把播放放在循环里。"}
|
|
7
|
+
{"id": "exam-L1-logic", "type": "exam_point", "title": "一级考点:逻辑推理与编程数学", "track": "scratch", "level": 1, "keywords": ["一级", "逻辑推理", "图形推理", "数学"], "body": "一级会考简单的逻辑推理和图形推理(比如找规律、图形缺哪一块),共 10 分,没有编程题。这部分靠平时多做找规律的题,不用死记。", "example": "", "tip": "图形推理先看行再看列,找数量、形状、颜色的变化规律。"}
|
|
8
|
+
{"id": "exam-L2-overview", "type": "exam_point", "title": "Scratch 二级考试总览", "track": "scratch", "level": 2, "keywords": ["二级", "考级", "总览", "题型", "分值"], "body": "二级在“会拼”的基础上考“会想”:分支和循环。题型不变(单选 25 + 判断 10 + 编程 2,共 100 分)。新增选择语句、循环语句、侦测、画笔、多角色设置,运算要求也提高了。", "example": "", "tip": "二级编程 2 题共 30 分,一般一道分支一道循环,考前把“如果…那么…否则”和“重复执行”练熟。"}
|
|
9
|
+
{"id": "exam-L2-multi", "type": "exam_point", "title": "二级考点:多角色设置", "track": "scratch", "level": 2, "keywords": ["二级", "多角色", "图层", "坐标", "大小", "特效"], "body": "理解舞台图层(谁在前谁在后)、舞台坐标系(x/y 定位)。会把角色移到鼠标位置或固定坐标,会调大小、加特效(鱼眼、马赛克等)、显示隐藏角色。这一块 10 分。", "example": "", "tip": "坐标系中心是 (0,0),右为 x 正,上为 y 正。考题常问:移到 (0,0) 是舞台哪里?答:正中间。"}
|
|
10
|
+
{"id": "exam-L2-pen", "type": "exam_point", "title": "二级考点:画笔", "track": "scratch", "level": 2, "keywords": ["二级", "画笔", "抬笔", "落笔", "粗细"], "body": "会用画笔落笔/抬笔、设画笔粗细和颜色、擦除。画笔 10 分,编程题常考画正方形、三角形:落笔→重复 4 次(移动+旋转 90 度)→抬笔。", "example": "", "tip": "画正多边形:旋转角度 = 360 ÷ 边数。画笔类考题先想好“走几步、转几度”。"}
|
|
11
|
+
{"id": "exam-L2-if", "type": "exam_point", "title": "二级考点:选择语句", "track": "scratch", "level": 2, "keywords": ["二级", "如果", "分支", "选择结构", "流程图"], "body": "会用“如果…那么…”和“如果…那么…否则…”,会画选择结构流程图(菱形是判断)。选择语句 18 分。关键是把条件想清楚:满足条件走哪条路,不满足走哪条。", "example": "", "tip": "流程图里菱形框出两条线:是/否。选择题常给流程图问输出,先代入条件走一遍。"}
|
|
12
|
+
{"id": "exam-L2-op", "type": "exam_point", "title": "二级考点:运算", "track": "scratch", "level": 2, "keywords": ["二级", "运算", "数学运算", "关系运算", "逻辑运算", "四舍五入"], "body": "会数学运算(加减乘除、随机数不算)、关系运算(大于、小于、等于)、逻辑运算(且、或、不成立),会字符连接、四舍五入。运算常和选择语句搭着考:比如“如果分数>90 那么说优秀”。", "example": "", "tip": "“且”是两个条件都成立,“或”是一个成立就行,“不成立”是反过来。逻辑题把条件列成表格再判断。"}
|
|
13
|
+
{"id": "exam-L2-sense", "type": "exam_point", "title": "二级考点:侦测", "track": "scratch", "level": 2, "keywords": ["二级", "侦测", "碰到", "颜色", "键盘", "鼠标"], "body": "会用侦测积木:碰到鼠标指针/角色/边缘、碰到颜色、按到键盘按键。侦测 14 分,编程题常考键盘控制角色移动(当按下上移键→y 增加)或碰到边缘反弹。", "example": "", "tip": "键盘控制模板:当绿旗被点击→重复执行→如果按下右移键→x 增加 10。四个方向照着写。"}
|
|
14
|
+
{"id": "exam-L2-loop", "type": "exam_point", "title": "二级考点:循环语句", "track": "scratch", "level": 2, "keywords": ["二级", "循环", "重复执行", "重复固定次数", "流程图"], "body": "会三种循环:重复执行(无限)、重复固定次数、重复直到…。会画循环结构流程图。循环 18 分。记住:能数得清次数用“重复 N 次”,数不清、看条件用“重复直到”。", "example": "", "tip": "“重复直到碰到边缘”比“重复 100 次”更稳,考编程题优先用条件循环。"}
|
|
15
|
+
{"id": "exam-L2-sound2", "type": "exam_point", "title": "二级考点:声音的进阶", "track": "scratch", "level": 2, "keywords": ["二级", "录音", "声音截取", "音量"], "body": "会录入自己的声音、调音量、截取声音片段、设播放时长和特效。声音和运算合在一起 20 分。操作题常考:录一段 3 秒的声音并在程序里播放。", "example": "", "tip": "录音前先试麦,截取时只留下需要的部分,考场上别录太长。"}
|
|
16
|
+
{"id": "exam-L3-overview", "type": "exam_point", "title": "Scratch 三级考试总览", "track": "scratch", "level": 3, "keywords": ["三级", "考级", "总览", "题型", "分值"], "body": "三级是分水岭:变量、广播、克隆、随机数全来了,还考嵌套。题型单选 25(50 分)+ 判断 10(20 分)+ 编程 3 题(30 分)。编程题变成 3 道,综合度明显上升。", "example": "", "tip": "三级备考先啃变量和广播克隆这三座大山,它们占了 40 分(变量 12 + 广播 14 + 克隆 14)。"}
|
|
17
|
+
{"id": "exam-L3-rand", "type": "exam_point", "title": "三级考点:随机数", "track": "scratch", "level": 3, "keywords": ["三级", "随机数", "在…到…间取随机数"], "body": "理解随机数,会用“在 1 到 10 间取随机数”。随机数 8 分,常和变量、克隆搭着考:比如随机位置冒出敌人、随机抽奖。", "example": "", "tip": "随机数每次运行结果不一样,调试时多运行几次看范围对不对。"}
|
|
18
|
+
{"id": "exam-L3-var", "type": "exam_point", "title": "三级考点:变量", "track": "scratch", "level": 3, "keywords": ["三级", "变量", "新建变量", "计分", "作用域", "滑杆"], "body": "会新建/删除/改名变量,会设定和增减变量值,会在舞台显示隐藏变量,理解变量作用域(所有角色用还是单个角色用)。变量 12 分,是计分、计时、生命值的核心。考前必练:做一个带分数变量的小游戏。", "example": "", "tip": "变量名用中文也行,但建议用英文+中文注释的习惯,方便以后学 Python。"}
|
|
19
|
+
{"id": "exam-L3-nest", "type": "exam_point", "title": "三级考点:循环与选择的嵌套", "track": "scratch", "level": 3, "keywords": ["三级", "嵌套", "循环嵌套", "选择嵌套", "跳出循环"], "body": "会选择语句嵌套(如果里面再套如果)、循环嵌套,还会用“停止这个脚本”跳出循环。嵌套 18 分 + 循环 12 分,是三级最难啃的骨头。练法:先把外层逻辑写对,再一层层往里加。", "example": "", "tip": "嵌套别超过 3 层,超过就容易晕。每一层缩进对齐,读代码从外往里一层层问“条件成立吗”。"}
|
|
20
|
+
{"id": "exam-L3-pen2", "type": "exam_point", "title": "三级考点:画笔进阶", "track": "scratch", "level": 3, "keywords": ["三级", "画笔", "图章", "颜色特效"], "body": "在二级基础上加:画笔颜色(亮度/饱和度)、图章、擦除图章。画笔 12 分。图章适合做“印章盖图案”的效果,考操作题时注意先落笔再盖章。", "example": "", "tip": "图章是把角色当前造型“盖”在舞台上,和画笔线条配合能做很多花样。"}
|
|
21
|
+
{"id": "exam-L3-broadcast", "type": "exam_point", "title": "三级考点:广播", "track": "scratch", "level": 3, "keywords": ["三级", "广播", "广播并等待", "多角色交互"], "body": "会用广播让角色之间传消息、配合干活。理解“广播”和“广播并等待”的区别:前者发完就走不等回音,后者要等对方干完才继续。广播 14 分,多角色游戏必用。", "example": "", "tip": "口诀:广播是喊话,并等待是喊话等人回话。接力赛用广播,颁奖仪式用广播并等待。"}
|
|
22
|
+
{"id": "exam-L3-clone", "type": "exam_point", "title": "三级考点:克隆", "track": "scratch", "level": 3, "keywords": ["三级", "克隆", "克隆体", "删除克隆体"], "body": "会克隆自己、设置“当作为克隆体启动时”的脚本、删除克隆体。克隆 14 分,是做子弹、敌人、雨滴这类“批量出现”效果的利器。记住克隆体太多会卡,及时删除。", "example": "", "tip": "子弹模板:当克隆体启动→重复直到碰到边缘→移动→删除克隆体。"}
|
|
23
|
+
{"id": "exam-L4-overview", "type": "exam_point", "title": "Scratch 四级考试总览", "track": "scratch", "level": 4, "keywords": ["四级", "考级", "总览", "题型", "分值"], "body": "四级是图形化的天花板:字符串、函数、链表、多线程概念、算法过渡全上。题型变成单选 15(30 分)+ 判断 10(20 分)+ 编程 5 题(50 分),编程占了一半。通过四级基本具备转 Python 的能力。", "example": "", "tip": "四级编程 5 题 50 分,得分关键是链表和函数两道大题,考前务必亲手写过增删改查。"}
|
|
24
|
+
{"id": "exam-L4-string", "type": "exam_point", "title": "四级考点:字符串", "track": "scratch", "level": 4, "keywords": ["四级", "字符串", "字符", "加密"], "body": "会字符串的拼接、取第 N 个字符、求长度,还会做简单的加密(比如每个字母后移一位)。字符串 8 分。加密题是四级的特色:先想清楚“原文→密文”的规则,再反向解。", "example": "", "tip": "凯撒加密练一遍:字母+3,z 后面绕回 a,解密就是 -3。"}
|
|
25
|
+
{"id": "exam-L4-func", "type": "exam_point", "title": "四级考点:函数(自制积木)", "track": "scratch", "level": 4, "keywords": ["四级", "函数", "自制积木", "参数", "返回值"], "body": "会创建带参数、有返回值的自制积木,理解函数的作用范围(积木里定义的变量外面用不了)。函数 12 分。思想就一句话:把重复的代码打包成一个新积木,以后直接调用。", "example": "", "tip": "自制积木=函数:输入是参数,输出是返回值。考题常让把一段重复代码改成自制积木。"}
|
|
26
|
+
{"id": "exam-L4-list", "type": "exam_point", "title": "四级考点:链表(列表)", "track": "scratch", "level": 4, "keywords": ["四级", "链表", "列表", "增删改查", "最值"], "body": "会新建链表,会增(添加)、删(删除)、改(替换)、查(第 N 项),会求最值和平均值。链表 10 分 + 编程大题。这是四级的核心数据结构,也是 Python 里列表的前身。", "example": "", "tip": "链表题三步走:先建表存数据,再循环遍历,最后算结果。求平均值=总和÷长度。"}
|
|
27
|
+
{"id": "exam-L4-algo", "type": "exam_point", "title": "四级考点:逻辑与算法过渡", "track": "scratch", "level": 4, "keywords": ["四级", "算法", "递归", "爬楼梯", "程序优化"], "body": "考结合分析计算的情境题,比如爬楼梯有几种走法(递归思想),以及用已学知识优化程序。逻辑 12 分 + 算法 10 分 + 程序优化 10 分。递归的理解口诀:大事化小,小事化了,自己调用自己,要有出口。", "example": "", "tip": "爬楼梯:到第 n 阶的方法 = 到 n-1 阶 + 到 n-2 阶。先手算 n=1,2,3 找规律。"}
|
|
28
|
+
{"id": "lesson-xb01", "type": "lesson", "title": "第 1 课:走进 Scratch3.0", "track": "scratch", "level": null, "keywords": ["校本", "Scratch3.0", "初识", "界面", "第一个程序"], "body": "认识 Scratch 是什么、能做什么;完成第一个小程序,点绿旗让小猫动起来。", "example": "", "tip": "激发兴趣,建立‘我能编程’的第一印象。"}
|
|
29
|
+
{"id": "lesson-xb02", "type": "lesson", "title": "第 2 课:Scratch3.0 界面认识", "track": "scratch", "level": null, "keywords": ["校本", "Scratch3.0", "界面", "五大区域", "积木分类"], "body": "认清舞台区、角色区、积木区、脚本区;知道积木按颜色分类(运动蓝色、外观紫色等);会保存作品。", "example": "", "tip": "扫盲,后面 19 课都要用的基本功。"}
|
|
30
|
+
{"id": "lesson-xb03", "type": "lesson", "title": "第 3 课:scratch 舞台了解", "track": "scratch", "level": null, "keywords": ["校本", "Scratch3.0", "坐标系", "角度", "背景"], "body": "舞台坐标系(x/y、中心原点)、角度系统(0 朝右、顺正逆负)、角色大小调整、背景设置。", "example": "", "tip": "坐标和角度是二级考级和所有游戏的基础。"}
|
|
31
|
+
{"id": "lesson-xb04", "type": "lesson", "title": "第 4 课:控制角色(事件)", "track": "scratch", "level": null, "keywords": ["校本", "Scratch3.0", "事件", "绿旗", "按键"], "body": "事件驱动思想:当绿旗被点击、当按下空格键、当角色被点击。程序不是从上往下死跑,是被事件叫醒的。", "example": "", "tip": "理解事件=三级广播、四级多线程的前置概念。"}
|
|
32
|
+
{"id": "lesson-xb05", "type": "lesson", "title": "第 5 课:让角色动起来(运动)", "track": "scratch", "level": null, "keywords": ["校本", "Scratch3.0", "移动", "坐标定位", "方向"], "body": "三种走法:按方向走(面向+移动)、按坐标走(移到 x/y)、自由走(跟随鼠标);直线、曲线行走。", "example": "", "tip": "一级‘角色操作’30 分的主战场。"}
|
|
33
|
+
{"id": "lesson-xb06", "type": "lesson", "title": "第 6 课:看看运动的轨迹(画笔)", "track": "scratch", "level": null, "keywords": ["校本", "Scratch3.0", "画笔", "落笔抬笔", "正多边形"], "body": "落笔/抬笔、画笔粗细颜色;画正方形:重复 4 次(走 100 步+转 90 度);推广到正多边形 360÷边数。", "example": "", "tip": "二级画笔 10 分 + 循环的第一次实战。"}
|
|
34
|
+
{"id": "lesson-xb07", "type": "lesson", "title": "第 7 课:绘制角色与背景", "track": "scratch", "level": null, "keywords": ["校本", "Scratch3.0", "造型编辑", "绘制背景"], "body": "用造型编辑器画角色、画背景;理解矢量造型缩放不模糊。", "example": "", "tip": "做原创作品的第一步,告别只用库角色。"}
|
|
35
|
+
{"id": "lesson-xb08", "type": "lesson", "title": "第 8 课:多彩的舞台(外观)", "track": "scratch", "level": null, "keywords": ["校本", "Scratch3.0", "特效", "造型切换", "背景切换"], "body": "外观积木:换造型、下一个造型、颜色特效(鱼眼/漩涡/马赛克/亮度)、背景切换。", "example": "", "tip": "一级‘造型与背景切换’30 分的核心。"}
|
|
36
|
+
{"id": "lesson-xb09", "type": "lesson", "title": "第 9 课:加减乘除的计算(运算和变量)", "track": "scratch", "level": null, "keywords": ["校本", "Scratch3.0", "运算", "变量", "计算器"], "body": "四则运算、取随机数;新建变量、变量增减;做一个小小计算器。", "example": "", "tip": "三级变量 12 分的前置课,数学好的孩子会很爽。"}
|
|
37
|
+
{"id": "lesson-xb10", "type": "lesson", "title": "第 10 课:切水果小游戏(条件和循环)", "track": "scratch", "level": null, "keywords": ["校本", "Scratch3.0", "分支", "循环", "克隆"], "body": "如果…那么…、重复执行、克隆;案例:切水果、小鱼吐泡泡、克隆字母墙。", "example": "", "tip": "二级分支循环 + 三级克隆,一课串起三个考点。"}
|
|
38
|
+
{"id": "lesson-xb11", "type": "lesson", "title": "第 11 课:花瓣花朵花枝花团(自制积木)", "track": "scratch", "level": null, "keywords": ["校本", "Scratch3.0", "自制积木", "函数思想"], "body": "把画花瓣的代码打包成自制积木,换参数画出花朵花枝;案例:蜻蜓飞舞。", "example": "", "tip": "四级函数的图形化版本:打包复用。"}
|
|
39
|
+
{"id": "lesson-xb12", "type": "lesson", "title": "第 12 课:大鱼吃小鱼(侦测)", "track": "scratch", "level": null, "keywords": ["校本", "Scratch3.0", "侦测", "碰到", "键盘控制"], "body": "碰到鼠标/边缘/颜色侦测;键盘控制大鱼游动吃小鱼;分数变量。", "example": "", "tip": "二级侦测 14 分的完整游戏案例。"}
|
|
40
|
+
{"id": "lesson-xb13", "type": "lesson", "title": "第 13 课:音乐响起来(扩展)", "track": "scratch", "level": null, "keywords": ["校本", "Scratch3.0", "音乐扩展", "演奏"], "body": "添加音乐扩展,用音符积木演奏旋律;理解节拍和音调。", "example": "", "tip": "把编程和音乐课打通,成就感极强。"}
|
|
41
|
+
{"id": "lesson-xb14", "type": "lesson", "title": "第 14 课:朗读者和翻译家(扩展)", "track": "scratch", "level": null, "keywords": ["校本", "Scratch3.0", "文字朗读", "翻译扩展"], "body": "文字转语音朗读、翻译扩展做英汉互译器。", "example": "", "tip": "扩展积木的玩法,顺带练英语。"}
|
|
42
|
+
{"id": "lesson-xb15", "type": "lesson", "title": "第 15 课:外接设备智能交互(扩展)", "track": "scratch", "level": null, "keywords": ["校本", "Scratch3.0", "扩展", "硬件交互"], "body": "了解 Scratch 能连 micro:bit 等外接设备,做简单的智能交互。", "example": "", "tip": "从屏幕走向现实世界,衔接创客教育。"}
|
|
43
|
+
{"id": "lesson-xb16", "type": "lesson", "title": "第 16 课:小猫扑蝶(动画一)", "track": "scratch", "level": null, "keywords": ["校本", "Scratch3.0", "动画", "造型切换", "广播"], "body": "用造型快速切换做逐帧动画;广播控制动画节奏。", "example": "", "tip": "动画是讲故事,做游戏是做规则,两种思维都练。"}
|
|
44
|
+
{"id": "lesson-xb17", "type": "lesson", "title": "第 17 课:龟兔赛跑(动画二)", "track": "scratch", "level": null, "keywords": ["校本", "Scratch3.0", "随机数", "变量", "竞赛"], "body": "随机数控制兔子睡觉、乌龟匀速;用变量记名次;广播宣布结果。", "example": "", "tip": "三级随机数+变量的趣味综合。"}
|
|
45
|
+
{"id": "lesson-xb18", "type": "lesson", "title": "第 18 课:飞箭击球(游戏一)", "track": "scratch", "level": null, "keywords": ["校本", "Scratch3.0", "克隆", "侦测", "计分"], "body": "克隆批量生成飞箭/气球;碰到侦测计分;难度递增。", "example": "", "tip": "三级克隆 14 分的标准练手项目。"}
|
|
46
|
+
{"id": "lesson-xb19", "type": "lesson", "title": "第 19 课:乒乓对打(游戏二)", "track": "scratch", "level": null, "keywords": ["校本", "Scratch3.0", "反弹", "双人控制", "变量"], "body": "小球碰到挡板/边缘反弹;双人键盘对打;比分变量+胜负判定。", "example": "", "tip": "把变量、侦测、分支串成完整游戏。"}
|
|
47
|
+
{"id": "lesson-xb20", "type": "lesson", "title": "第 20 课:贪吃蛇(游戏三)", "track": "scratch", "level": null, "keywords": ["校本", "Scratch3.0", "列表", "克隆", "综合"], "body": "用列表存蛇身坐标,克隆做食物,吃到变长、撞到结束;毕业级综合项目。", "example": "", "tip": "四级链表思想的图形化预演,学完可转 Python。"}
|
|
48
|
+
{"id": "lesson-cj09", "type": "lesson", "title": "初级 9:涂鸦画板", "track": "scratch", "level": null, "keywords": ["初级", "画笔", "鼠标跟随"], "body": "鼠标移到哪画到哪:绿旗点击后重复执行,如果鼠标按下就落笔并跟到鼠标位置,否则抬笔。再加颜色切换和清屏按钮。", "example": "", "tip": "画笔+事件+分支的第一课,孩子第一次做出‘有用’的工具。"}
|
|
49
|
+
{"id": "lesson-cj10", "type": "lesson", "title": "初级 10:投票统计器", "track": "scratch", "level": null, "keywords": ["初级", "变量", "询问回答", "统计"], "body": "用‘询问并等待’收集投票,用三个变量分别计数,最后算出票数和百分比。变量的第一次正式登场。", "example": "", "tip": "三级变量的启蒙:变量就是‘会变的盒子’。"}
|
|
50
|
+
{"id": "lesson-cj11", "type": "lesson", "title": "初级 11:黑洞传奇", "track": "scratch", "level": null, "keywords": ["初级", "特效", "漩涡", "动画"], "body": "漩涡特效越转越快,角色被吸进去缩小消失;练外观特效的数值变化。", "example": "", "tip": "纯视觉冲击课,适合建立‘特效是数字变的’直觉。"}
|
|
51
|
+
{"id": "lesson-cj12", "type": "lesson", "title": "初级 12:大鱼吃小鱼", "track": "scratch", "level": null, "keywords": ["初级", "键盘控制", "侦测", "变量"], "body": "键盘控制大鱼,吃小鱼长大、碰大鱼 Game Over;分数和生命值两个变量。", "example": "", "tip": "和校本第 12 课呼应,二级侦测+变量的双练。"}
|
|
52
|
+
{"id": "lesson-cj13", "type": "lesson", "title": "初级 13:火上浇油", "track": "scratch", "level": null, "keywords": ["初级", "克隆", "随机数"], "body": "克隆批量生成火焰,点火浇油让火变大;随机数控制火焰位置。", "example": "", "tip": "克隆+随机数的趣味组合,三级考点的提前体验。"}
|
|
53
|
+
{"id": "lesson-cj14", "type": "lesson", "title": "初级 14:赛车挑战", "track": "scratch", "level": null, "keywords": ["初级", "键盘控制", "计时", "赛道侦测"], "body": "键盘开赛车,碰到赛道边缘算犯规,计时变量比快慢。", "example": "", "tip": "颜色侦测的经典用法:碰到赛道颜色=出界。"}
|
|
54
|
+
{"id": "lesson-cj15", "type": "lesson", "title": "初级 15:水果忍者", "track": "scratch", "level": null, "keywords": ["初级", "克隆", "切割侦测", "计分"], "body": "水果克隆体从下方飞起,鼠标划过=切开(碰到鼠标指针侦测),漏接扣命。", "example": "", "tip": "切水果的升级版,节奏更快、判定更细。"}
|
|
55
|
+
{"id": "lesson-cj16", "type": "lesson", "title": "初级 16:打字游戏", "track": "scratch", "level": null, "keywords": ["初级", "字符串", "侦测按键", "计时"], "body": "随机掉字母,敲对键盘字母消灭;练侦测按键+字符串比较+计时。", "example": "", "tip": "四级字符串的图形化预热,顺带练打字。"}
|
|
56
|
+
{"id": "lesson-zj01", "type": "lesson", "title": "中级 1:奔跑的马驹", "track": "scratch", "level": null, "keywords": ["中级", "造型切换", "动画"], "body": "快速切换马腿造型做出奔跑动画,移动+造型切换同步,速度变量可调。", "example": "", "tip": "动画课:造型切换够快,眼睛就信了。"}
|
|
57
|
+
{"id": "lesson-zj02", "type": "lesson", "title": "中级 2:水下拾钻", "track": "scratch", "level": null, "keywords": ["中级", "侦测", "随机数", "计分"], "body": "钻石随机位置出现,潜水员碰到得分,氧气变量倒计时。", "example": "", "tip": "随机数+倒计时的组合,游戏节奏感的第一次训练。"}
|
|
58
|
+
{"id": "lesson-zj03", "type": "lesson", "title": "中级 3:电子点餐器", "track": "scratch", "level": null, "keywords": ["中级", "列表", "询问", "界面"], "body": "用列表存菜单,询问点菜,自动算总价;简单的‘小程序’思维。", "example": "", "tip": "四级链表的启蒙:列表就是‘一串有顺序的盒子’。"}
|
|
59
|
+
{"id": "lesson-zj04", "type": "lesson", "title": "中级 4:旋转的地球", "track": "scratch", "level": null, "keywords": ["中级", "特效", "旋转", "外观"], "body": "地球自转+公转,亮度/虚像特效做昼夜;练外观积木的叠加。", "example": "", "tip": "把科学课搬进编程课,跨学科的甜点。"}
|
|
60
|
+
{"id": "lesson-zj05", "type": "lesson", "title": "中级 5:小猫快飞", "track": "scratch", "level": null, "keywords": ["中级", "重力模拟", "变量", "Flappy"], "body": "Flappy Bird 玩法:空格起飞、重力下落(y 持续减小)、柱子克隆体逼近、碰到即死。", "example": "", "tip": "三级考点的集大成:变量+克隆+侦测+循环全上。"}
|
|
61
|
+
{"id": "lesson-zj06", "type": "lesson", "title": "中级 6:躲避幽灵", "track": "scratch", "level": null, "keywords": ["中级", "随机移动", "侦测", "生命值"], "body": "幽灵随机游走追踪玩家,碰到扣血,三条命;难度随时间增加。", "example": "", "tip": "‘追踪算法’的启蒙:面向玩家+移动几步。"}
|
|
62
|
+
{"id": "lesson-zj07", "type": "lesson", "title": "中级 7:坚持到底", "track": "scratch", "level": null, "keywords": ["中级", "计时", "耐力", "变量"], "body": "在子弹雨里活下去,计时变量记录存活时长,破纪录存最高分。", "example": "", "tip": "变量持久化的思想:最高分=长期记忆。"}
|
|
63
|
+
{"id": "lesson-zj08", "type": "lesson", "title": "中级 8:贪吃蛇", "track": "scratch", "level": null, "keywords": ["中级", "列表", "方向控制", "综合"], "body": "方向键控制蛇头,列表记录蛇身,食物随机刷新,吃到变长撞墙结束。", "example": "", "tip": "校本第 20 课的姊妹篇,四级链表的图形化预演。"}
|
|
64
|
+
{"id": "lesson-zj09", "type": "lesson", "title": "中级 9:保卫家园", "track": "scratch", "level": null, "keywords": ["中级", "塔防", "克隆", "波次"], "body": "敌人按波次克隆进攻,炮塔自动射击,波次变量控制难度曲线。", "example": "", "tip": "游戏设计的进阶:数值策划(波次/血量/伤害)的第一次接触。"}
|
|
65
|
+
{"id": "lesson-zj11", "type": "lesson", "title": "中级 11:坦克大战", "track": "scratch", "level": null, "keywords": ["中级", "双人对战", "子弹克隆", "地图"], "body": "双人键盘对战,子弹克隆体+碰到侦测判定命中,砖块地图可破坏。", "example": "", "tip": "多角色交互的综合:广播同步开局,克隆管子弹。"}
|
|
66
|
+
{"id": "lesson-zj13", "type": "lesson", "title": "中级 13:火箭赛车", "track": "scratch", "level": null, "keywords": ["中级", "加速度", "变量", "赛道"], "body": "按住加速、松开减速的速度变量模拟,惯性手感;赛道颜色侦测防抄近道。", "example": "", "tip": "‘加速度’是变量的绝佳教具:速度本身也是个变量。"}
|
|
67
|
+
{"id": "lesson-zj15", "type": "lesson", "title": "中级 15:拼图游戏", "track": "scratch", "level": null, "keywords": ["中级", "坐标", "拖拽", "判定"], "body": "拼图块拖拽到正确坐标吸附,全部归位判胜;练坐标系+鼠标侦测。", "example": "", "tip": "二级坐标系的实战:每个格子都是一个 (x,y)。"}
|
|
68
|
+
{"id": "proj-py13", "type": "project", "title": "第 13 讲:宠物管理小系统", "track": "python", "level": null, "keywords": ["项目", "实战", "列表", "len", "append", "in", "remove", "sort"], "body": "用列表管一群宠物:查有几只(len)、领养新宠物(append)、查某只在不在(in)、送走(remove)、按名字排序(sort)。", "example": "", "tip": "列表是 Python 第一个‘能装很多东西’的容器,宠物系统是最贴切的教具。"}
|
|
69
|
+
{"id": "proj-py14", "type": "project", "title": "第 14 讲:考勤管理小系统", "track": "python", "level": null, "keywords": ["项目", "实战", "字典", "统计", "文件"], "body": "给全班做考勤:字典存‘名字→出勤次数’,每天打卡累加,月底统计谁全勤、谁迟到最多,结果存文件。", "example": "", "tip": "字典=‘贴了标签的抽屉’,找东西不用数位置,直接叫名字。"}
|
|
70
|
+
{"id": "proj-py15", "type": "project", "title": "第 15 讲:普通话测试小系统", "track": "python", "level": null, "keywords": ["项目", "实战", "random", "计时", "计分"], "body": "随机出绕口令,计时朗读,系统打分:读对加分、超时扣分;错题本记录常错的字。", "example": "", "tip": "random 负责‘出题’,计时+计分是所有测评类程序的通用骨架。"}
|
|
71
|
+
{"id": "proj-py16", "type": "project", "title": "第 16 讲:文件藏哪我来找", "track": "python", "level": null, "keywords": ["项目", "实战", "文件操作", "os", "路径"], "body": "三步管文件:open 打开、读写操作、close 关闭;用 os.listdir 列出文件夹里所有文件名;给文件批量重命名。", "example": "", "tip": "路径是‘从起点到终点的路线’,文件夹是‘分好类的抽屉柜’。"}
|
|
72
|
+
{"id": "proj-py17", "type": "project", "title": "第 17 讲:天气查询助手", "track": "python", "level": null, "keywords": ["项目", "实战", "模块", "数据", "交互"], "body": "输入城市名,查出天气并播报:温度、天气状况、穿衣建议;把常用城市存成列表,一键查询。", "example": "", "tip": "‘助手’类程序的通用结构:输入→查数据→整理→输出,这个结构能复用到查快递、查单词。"}
|
|
73
|
+
{"id": "proj-py18", "type": "project", "title": "第 18 讲:自制手机通讯录", "track": "python", "level": null, "keywords": ["项目", "实战", "字典", "增删改查", "文件"], "body": "完整 CRUD:增(加联系人)、删、改(换号码)、查(按名字/号码搜),退出时存文件,下次打开还在。", "example": "", "tip": "这是第一个‘像真正软件’的项目,增删改查是所有管理系统的 heart。"}
|
|
74
|
+
{"id": "proj-py19", "type": "project", "title": "第 19 讲:PM2.5 查询助手", "track": "python", "level": null, "keywords": ["项目", "实战", "数据查询", "条件判断"], "body": "输入城市查 PM2.5 数值,按数值分档给健康建议:优/良/轻度污染分别该做什么。", "example": "", "tip": "条件分支的现实意义:同一数字,不同区间=不同行动,代码即决策。"}
|
|
75
|
+
{"id": "proj-py20", "type": "project", "title": "第 20 讲:个人小档案", "track": "python", "level": null, "keywords": ["项目", "实战", "类", "对象", "属性"], "body": "用类给每个人建一份档案:姓名、年龄、爱好是属性,自我介绍是方法;全班档案存成列表。", "example": "", "tip": "类=‘档案模板’,对象=‘填好的档案’,这是面向对象的第一次握手。"}
|
|
76
|
+
{"id": "proj-py21", "type": "project", "title": "第 21 讲:词云图生成器", "track": "python", "level": null, "keywords": ["项目", "实战", "第三方库", "分词", "可视化"], "body": "三步做词云:分词(cut 把句子拆成词)、生成词云图(wordcloud)、保存图片(save);词越大=出现次数越多。", "example": "", "tip": "第一次用第三方库:import 就是‘请外援’,站在别人的肩膀上。"}
|
|
77
|
+
{"id": "proj-py22", "type": "project", "title": "第 22 讲:个性对话机器人", "track": "python", "level": null, "keywords": ["项目", "实战", "字符串", "条件分支", "循环"], "body": "机器人陪聊:while 循环一直听,if 判断关键词(‘你好’→问好,‘再见’→结束),答不上来就卖萌。", "example": "", "tip": "聊天机器人的本质=‘关键词→回复’的映射表,理解这点就不神秘了。"}
|
|
78
|
+
{"id": "proj-py23", "type": "project", "title": "第 23 讲:自制电子闹钟", "track": "python", "level": null, "keywords": ["项目", "实战", "time", "循环", "条件"], "body": "设定时间,到点响铃:死循环里不断看当前时间,到点就 print 提醒+响铃;再加个贪睡 5 分钟功能。", "example": "", "tip": "死循环+条件跳出是‘值守’类程序的通用写法:监控、提醒都这么干。"}
|
|
79
|
+
{"id": "proj-py24", "type": "project", "title": "第 24 讲:综合应用大闯关", "track": "python", "level": null, "keywords": ["项目", "实战", "综合", "项目"], "body": "期末大闯关:把前面学的列表、字典、文件、分支循环串起来,做一个完整小系统并上台演示。", "example": "", "tip": "检验标准:不看书写出 50 行以上能跑的程序,就算出师。"}
|
|
80
|
+
{"id": "concept-var-scratch", "type": "concept", "title": "Scratch:变量是什么", "track": "scratch", "level": null, "keywords": ["变量", "计分", "盒子"], "body": "变量就是一个‘会变的盒子’:盒子上有名字(比如‘分数’),里面装着数字。你可以往里放新数字(设为 0)、让数字变大(增加 1)。游戏里的分数、生命值、倒计时全是变量。", "example": "做一个计分游戏:当绿旗被点击→将‘分数’设为 0;每次吃到苹果→将‘分数’增加 10。", "tip": "新手常忘在游戏开始时把变量清零,导致上一局的分数带到下一局。"}
|
|
81
|
+
{"id": "concept-event", "type": "concept", "title": "Scratch:事件驱动", "track": "scratch", "level": null, "keywords": ["事件", "绿旗", "广播"], "body": "Scratch 的程序是被‘叫醒’的,不是排队跑的。‘当绿旗被点击’‘当按下空格键’‘当角色被点击’都是事件。理解这点,广播和多角色配合就顺了。", "example": "两个角色接力跑:小猫跑到终点→广播‘接力’;小狗收到‘接力’→开始跑。", "tip": "广播消息名字别用‘消息1’这种,起‘游戏开始’‘接力’这种一看就懂的名字。"}
|
|
82
|
+
{"id": "concept-loop", "type": "concept", "title": "Scratch:三种循环怎么选", "track": "scratch", "level": null, "keywords": ["循环", "重复执行", "重复N次"], "body": "重复执行=无限循环(游戏主循环);重复 N 次=数得清次数(画正方形重复 4 次);重复直到=看条件(走到碰到边缘才停)。选哪个看你是‘数得清’还是‘看条件’。", "example": "画五角星:重复 5 次(移动 100 步,右转 144 度)。", "tip": "‘重复执行’里不加‘等待’会跑飞快,动画类程序记得加‘等待 0.1 秒’。"}
|
|
83
|
+
{"id": "concept-branch", "type": "concept", "title": "Scratch:分支语句", "track": "scratch", "level": null, "keywords": ["如果", "分支", "条件"], "body": "‘如果…那么…’是单行道:条件成立才干;‘如果…那么…否则…’是岔路口:成立走左边,不成立走右边。条件就是一道判断题,答案只有成立/不成立。", "example": "考试 ≥90 说‘优秀’否则说‘继续努力’:如果‘分数>90’那么说优秀,否则说继续努力。", "tip": "条件里别把‘=’写成赋值,Scratch 里绿色‘=’是比较,问的是‘相等吗’。"}
|
|
84
|
+
{"id": "concept-broadcast", "type": "concept", "title": "Scratch:广播与广播并等待", "track": "scratch", "level": null, "keywords": ["广播", "消息", "多角色"], "body": "广播=大喇叭喊一嗓子,喊完就走;广播并等待=喊完站在原地等大家干完再走。接力用广播,颁奖(等所有人到齐)用广播并等待。", "example": "开场动画:舞台广播‘开场’→所有角色各自表演;想等演完再出字幕就用‘广播并等待’。", "tip": "广播消息满天飞会乱,一个项目里消息别超过 5 个,名字要具体。"}
|
|
85
|
+
{"id": "concept-clone", "type": "concept", "title": "Scratch:克隆", "track": "scratch", "level": null, "keywords": ["克隆", "子弹", "批量"], "body": "克隆=复印角色。子弹、敌人、雨滴这种‘要很多个’的东西都用克隆:本体负责发射克隆体,克隆体出生后自己管自己(移动、消失)。", "example": "发射子弹:当按下空格→克隆自己;当作为克隆体启动→重复直到碰到边缘→移动 10 步→删除克隆体。", "tip": "克隆体不删会越积越多卡死,记住‘谁出生谁负责删’。"}
|
|
86
|
+
{"id": "concept-func", "type": "concept", "title": "Scratch:自制积木就是函数", "track": "scratch", "level": null, "keywords": ["自制积木", "函数", "参数"], "body": "自制积木=你自己发明的新积木。把重复的代码打包,起个名字,以后直接用。还能加参数(比如画 n 边形,n 就是参数)和返回值(算完把结果交出来)。", "example": "画正多边形:自制积木‘画多边形(边数)’,里面重复‘边数’次(移动+转 360/边数度)。", "tip": "函数里的变量外面用不了,这叫作用域,需要的结果要用‘返回值’交出来。"}
|
|
87
|
+
{"id": "concept-list", "type": "concept", "title": "Scratch:列表(链表)", "track": "scratch", "level": null, "keywords": ["列表", "链表", "增删改查"], "body": "列表=一排有编号的盒子,编号从 1 开始(Scratch 的列表从 1 数)。会增(添加)、删(删除第 N 项)、改(替换)、查(第 N 项的值)。贪吃蛇的蛇身、通讯录都靠它。", "example": "存全班名字:添加‘小明’‘小红’…;想找第 3 个→‘第 3 项’。", "tip": "Scratch 列表编号从 1 开始,Python 从 0 开始,转语言时这是第一大坑。"}
|
|
88
|
+
{"id": "concept-sense", "type": "concept", "title": "Scratch:侦测", "track": "scratch", "level": null, "keywords": ["侦测", "碰到", "颜色", "键盘"], "body": "侦测=角色的感官:碰到鼠标/边缘/某个颜色、按下了哪个键。游戏里‘撞到怎么办’‘按什么键干什么’全靠它。", "example": "键盘控制:重复执行里放‘如果按下右移键→x 增加 10’,四个方向照写。", "tip": "颜色侦测对背景很敏感,换背景后要重新取色。"}
|
|
89
|
+
{"id": "concept-pen", "type": "concept", "title": "Scratch:画笔", "track": "scratch", "level": null, "keywords": ["画笔", "落笔", "抬笔"], "body": "画笔=角色的尾巴拖着墨水。落笔开始画,抬笔停下;画笔颜色粗细都能调。画几何图形的本质:走几步、转几度。", "example": "画圆:重复 360 次(移动 1 步,右转 1 度)。", "tip": "画完记得抬笔,不然角色一动就拖出一条线。"}
|
|
90
|
+
{"id": "concept-coord", "type": "concept", "title": "Scratch:坐标系", "track": "scratch", "level": null, "keywords": ["坐标", "x", "y", "定位"], "body": "舞台中心是 (0,0),往右 x 变大,往上 y 变大。x 管左右(-240 到 240),y 管上下(-180 到 180)。所有定位、移动、侦测都离不开它。", "example": "把角色送回家:移到 x:0 y:0。", "tip": "‘移到鼠标指针’和‘移到 x/y’别混:一个是跟人走,一个是去固定地址。"}
|
|
91
|
+
{"id": "concept-rand", "type": "concept", "title": "Scratch/Python:随机数", "track": "scratch", "level": null, "keywords": ["随机数", "random"], "body": "随机数=‘老天爷掷骰子’。Scratch 用‘在 1 到 10 间取随机数’,Python 用 random.randint(1,10)。敌人随机出现、抽奖、洗牌都靠它。", "example": "随机刷怪:克隆体出生时移到 x:在-200到200间取随机数。", "tip": "调试随机程序要多运行几次,只看一次会以为出 bug 了。"}
|
|
92
|
+
{"id": "concept-py-var", "type": "concept", "title": "Python:变量与赋值", "track": "python", "level": null, "keywords": ["变量", "赋值", "="], "body": "Python 的变量不用声明,直接‘名字 = 值’就行:score = 0。= 是‘把右边装进左边’,不是数学的等于。判断相等用 ==(两个等号)。", "example": "score = 0 / score = score + 10 / name = '小明'", "tip": "= 和 == 是新手第一大坑:if 里写 = 会直接报错,记住判断用双等号。"}
|
|
93
|
+
{"id": "concept-py-type", "type": "concept", "title": "Python:数据类型", "track": "python", "level": null, "keywords": ["类型", "整数", "字符串", "input"], "body": "整数 int(3)、小数 float(3.14)、字符串 str('你好')、布尔 bool(True/False)。input() 收到的一律是字符串,想算数要转 int()。", "example": "age = int(input('你几岁?')) # 不转就变成字符串拼接了", "tip": "input 忘转类型是考级和作业里最高频的 bug:'3'+'4'='34' 不是 7。"}
|
|
94
|
+
{"id": "concept-py-list", "type": "concept", "title": "Python:列表", "track": "python", "level": null, "keywords": ["列表", "list", "索引"], "body": "列表=中括号装的一串东西:pets = ['狗','猫']。编号从 0 开始!pets[0] 是‘狗’。增 append、删 remove、查 in、量 len、排序 sort。", "example": "pets = ['斑点狗','聪明猫'] / pets.append('闪电龟') / len(pets)", "tip": "索引从 0 开始是 Python 和 Scratch 最大的不同,写 pets[1] 拿到的是第二个。"}
|
|
95
|
+
{"id": "concept-py-dict", "type": "concept", "title": "Python:字典", "track": "python", "level": null, "keywords": ["字典", "dict", "键值"], "body": "字典=花括号装的‘标签抽屉’:{'小明': 100, '小红': 95},用名字直接找分,不用数第几个。考勤、通讯录、成绩单都用它。", "example": "scores = {'小明': 100} / scores['小红'] = 95 / scores['小明']", "tip": "字典的‘键’不能重复,重复写会覆盖,查不存在的键会报错,先用 in 检查。"}
|
|
96
|
+
{"id": "concept-py-if", "type": "concept", "title": "Python:if 条件语句", "track": "python", "level": null, "keywords": ["if", "条件", "缩进"], "body": "if 分数 >= 90: 下面缩进的才执行。elif 是‘要不’,else 是‘都不行’。冒号+缩进是 Python 的灵魂,缩进不对程序就歪了。", "example": "if score >= 90:\n print('优秀')\nelif score >= 60:\n print('及格')\nelse:\n print('加油')", "tip": "缩进必须统一用 4 个空格,混用 Tab 和空格会报 IndentationError。"}
|
|
97
|
+
{"id": "concept-py-for", "type": "concept", "title": "Python:for 循环", "track": "python", "level": null, "keywords": ["for", "循环", "range"], "body": "for i in range(5): 把 0,1,2,3,4 轮一遍。range(5)=5 次,range(1,11)=1 到 10。遍历列表直接 for pet in pets。", "example": "for i in range(1, 11):\n print(i) # 打印 1 到 10", "tip": "range(10) 是 0-9 不是 1-10,想要 1-10 写 range(1, 11)。"}
|
|
98
|
+
{"id": "concept-py-while", "type": "concept", "title": "Python:while 循环", "track": "python", "level": null, "keywords": ["while", "死循环", "break"], "body": "while 条件: 条件成立就一直干。不知道循环几次时用它(比如猜数字猜对为止)。break 是‘紧急出口’,小心写成死循环。", "example": "while True:\n s = input('猜个数字:')\n if int(s) == 7:\n print('对了!')\n break", "tip": "while 里一定要有让条件变假的机会,不然就是死循环,只能强制停止。"}
|
|
99
|
+
{"id": "concept-py-func", "type": "concept", "title": "Python:函数", "track": "python", "level": null, "keywords": ["函数", "def", "参数", "return"], "body": "def 是‘发明新功能’:def 打招呼(名字): 里面写逻辑,return 把结果交出来。函数让代码从‘流水账’变成‘工具箱’。", "example": "def 加法(a, b):\n return a + b\nprint(加法(3, 4)) # 7", "tip": "函数名别用中文虽然能跑,但建议英文+注释,这是行业习惯。"}
|
|
100
|
+
{"id": "concept-py-str", "type": "concept", "title": "Python:字符串操作", "track": "python", "level": null, "keywords": ["字符串", "拼接", "切片"], "body": "字符串=引号装的文字。拼接用 +,取第几个用 [0],切一段用 [1:4],长度 len(),拆分 split(),in 判断包含。", "example": "s = 'hello' / s[0] # 'h' / s[1:4] # 'ell' / 'ell' in s # True", "tip": "字符串里的数字是文字,'3'+'4'='34',想算数先 int() 转。"}
|
|
101
|
+
{"id": "concept-py-file", "type": "concept", "title": "Python:文件读写", "track": "python", "level": null, "keywords": ["文件", "open", "读写"], "body": "三步:open 打开('w' 写/'r' 读/'a' 追加)、读写操作、close 关闭。通讯录、考勤能‘下次打开还在’,全靠文件。", "example": "f = open('通讯录.txt', 'w')\nf.write('小明 13800000000')\nf.close()", "tip": "写完一定 close,不然数据可能没真正存进去;读不存在的文件会报错。"}
|
|
102
|
+
{"id": "concept-py-module", "type": "concept", "title": "Python:模块与 import", "track": "python", "level": null, "keywords": ["模块", "import", "random"], "body": "import=‘请外援’:import random 请来随机数工具箱,import os 请来文件工具箱。第三方库(如词云库)要先安装再 import。", "example": "import random\nprint(random.randint(1, 10)) # 1到10随机一个", "tip": "import 写在文件最上面,这是规范;from x import y 是‘只请外援里的某个人’。"}
|
|
103
|
+
{"id": "concept-debug", "type": "concept", "title": "通用:程序出 bug 了怎么办", "track": null, "level": null, "keywords": ["bug", "调试", "报错"], "body": "三步调试法:1. 看报错信息,它会告诉你第几行出了什么错;2. 打印大法,在可疑的地方 print 看看到底是多少;3. 缩小范围,把代码一段段注释掉,看哪段一加就错。", "example": "print('到这里了,x=', x) # 随时插眼看变量", "tip": "报错信息是英文的别怕,复制去搜,90% 的错别人都踩过。报错是朋友,不是敌人。"}
|
|
104
|
+
{"id": "faq-age", "type": "faq", "title": "几岁开始学编程合适", "track": null, "level": null, "keywords": ["年龄", "几岁", "启蒙"], "body": "5-7 岁可以玩 Scratch 启蒙(认字不多也能拖积木);8-12 岁是 Scratch 黄金期,可以系统学+考级;12 岁以上、数学基础好就可以转 Python。关键看兴趣,不看年龄。", "example": "", "tip": "别用年龄卡孩子,卡兴趣:他愿意坐下来玩 30 分钟,就是 ready。"}
|
|
105
|
+
{"id": "faq-which-first", "type": "faq", "title": "先学 Scratch 还是直接学 Python", "track": null, "level": null, "keywords": ["先学哪个", "Scratch", "Python", "顺序"], "body": "先 Scratch 后 Python。Scratch 用积木,零打字门槛,先建立‘程序思维’(顺序/分支/循环);有了思维再学 Python,只是换一套‘写法’,两周就能上手。直接上 Python 容易卡在打字和报错上劝退。", "example": "", "tip": "Scratch 学到三级(会变量/广播/克隆)就是转 Python 的最佳时机。"}
|
|
106
|
+
{"id": "faq-exam-use", "type": "faq", "title": "考级证书有什么用", "track": null, "level": null, "keywords": ["考级", "证书", "用处"], "body": "三个用:1. 目标感,备考是最好的学习驱动;2. 升学参考,部分学校科技特长看重;3. 能力证明,1-4 级对应从入门到能转 Python 的完整路径。但别为考而考,考级是副产品,做出作品才是真本事。", "example": "", "tip": "一年考 1-2 级是健康节奏,突击刷题考过等于白考。"}
|
|
107
|
+
{"id": "faq-stuck", "type": "faq", "title": "孩子卡住了/不想学了怎么办", "track": null, "level": null, "keywords": ["放弃", "卡住", "兴趣"], "body": "先降难度:把大项目拆成 3 个小目标,完成一个就很有成就感。再换形式:不爱做题就做游戏,不爱游戏就做动画。最后问一句:是难,还是累?难就拆,累就歇。千万别在卡住的时候讲大道理。", "example": "", "tip": "卡住时最好的问题不是‘你怎么又不会’,而是‘我们离做出来还差几步’。"}
|
|
108
|
+
{"id": "faq-math", "type": "faq", "title": "学编程对数学有帮助吗", "track": null, "level": null, "keywords": ["数学", "帮助", "成绩"], "body": "有,而且是双向的:坐标系、角度、变量、逻辑推理都是数学;反过来,编程里的‘试出来’会反哺数学思维。但别指望学俩月数学就起飞,它是慢变量,半年起效。", "example": "", "tip": "把数学作业里的找规律题拿来当编程热身,一举两得。"}
|
|
109
|
+
{"id": "faq-device", "type": "faq", "title": "学编程需要买什么设备", "track": null, "level": null, "keywords": ["设备", "电脑", "iPad"], "body": "一台能上网的电脑就够了,Scratch 网页版免费,Python 免费。iPad 能玩 Scratch 入门,但打字写 Python 还是得电脑。不用买高配,流畅打开网页就行。", "example": "", "tip": "钱花在‘好课程/好老师’上,别花在设备上。"}
|
|
110
|
+
{"id": "faq-howlong", "type": "faq", "title": "每天学多久合适", "track": null, "level": null, "keywords": ["时间", "多久", "频率"], "body": "小学生每次 30-45 分钟、每周 2-3 次,比周末一次猛学 3 小时强得多。编程是手艺,靠频率不靠时长。每次课保证‘做出一个能跑的东西’,成就感是最好的续航。", "example": "", "tip": "定个小规矩:下课前 5 分钟,孩子给家长演示今天做的东西。"}
|
|
111
|
+
{"id": "faq-parent", "type": "faq", "title": "家长不会编程能陪孩子学吗", "track": null, "level": null, "keywords": ["家长", "陪伴", "不会"], "body": "能,而且你的‘不会’是优势:让孩子当小老师讲给你听,讲明白才是真学会。你负责三件事:保证时间、及时夸具体(‘这个跳跃判定做得真巧’而不是‘你真棒’)、卡住时陪他一起查。", "example": "", "tip": "最好的陪伴是当学生,不是当监工。"}
|
|
112
|
+
{"id": "concept-pen-stamp", "type": "concept", "title": "Scratch:画笔进阶——图章", "track": "scratch", "level": null, "keywords": ["画笔", "图章", "盖章"], "body": "图章=把角色当前造型‘盖’在舞台上,像盖印章。适合做雪花、星星点缀:克隆体移到随机位置→盖章→删除,满天星就出来了。", "example": "下雪效果:重复 50 次(移到随机位置,图章),再加等待 0.1 秒。", "tip": "盖章前先落笔定位,盖完记得清屏从头再来,不然越盖越乱。"}
|
|
113
|
+
{"id": "concept-string-scratch", "type": "concept", "title": "Scratch:字符串处理", "track": "scratch", "level": null, "keywords": ["字符串", "连接", "取字符", "长度"], "body": "Scratch 里也有字符串:‘连接’把两段字拼起来,‘第 N 个字符’取字,‘字符串长度’数字数。配合询问积木,能做暗号验证、名字倒着念。", "example": "暗号锁:如果‘回答’=‘芝麻开门’那么说‘通过’,否则说‘暗号错误’。", "tip": "中文字符也算 1 个长度,中英混排时数清楚。"}
|
|
114
|
+
{"id": "concept-multithread", "type": "concept", "title": "Scratch:多线程概念", "track": "scratch", "level": null, "keywords": ["多线程", "同时运行", "多个脚本"], "body": "一个角色挂多个‘当绿旗被点击’脚本,它们会同时跑——这就是多线程:好比一个人同时左手画圆右手画方。做游戏时‘主循环’和‘计时器’就是两个线程各干各的。", "example": "计时器线程:当绿旗被点击→将计时设为 60→重复直到计时=0(等待 1 秒,计时减 1)。", "tip": "多线程同时改同一个变量会打架,计时/计分这类变量只让一个线程改。"}
|
|
115
|
+
{"id": "concept-recursion", "type": "concept", "title": "通用:递归思想", "track": null, "level": null, "keywords": ["递归", "自己调用自己", "爬楼梯"], "body": "递归=‘大事化小’:解决大问题时,先解决小一号的同类问题。爬楼梯到第 n 阶的方法数 = 到 n-1 阶 + 到 n-2 阶(最后一步要么 1 阶要么 2 阶)。递归必须有‘出口’(n=1 时答案是 1),没出口就是死循环。", "example": "手算:n=1→1 种,n=2→2 种,n=3→3 种,n=4→5 种,看出规律了吗?", "tip": "递归先手算小数字找规律,再写代码;四级算法题的敲门砖。"}
|
|
116
|
+
{"id": "concept-flowchart", "type": "concept", "title": "通用:流程图三件套", "track": null, "level": null, "keywords": ["流程图", "菱形", "方框", "箭头"], "body": "流程图是‘程序的地图’,考级会考画法:椭圆=开始/结束,方框=做一件事,菱形=判断题(出两条路:是/否),箭头=顺序。画流程图=先想清楚再动手,程序员的基本功。", "example": "画‘考试≥90 优秀否则加油’:开始→方框(输入分数)→菱形(≥90?)→是→方框(说优秀)/否→方框(说加油)→结束。", "tip": "菱形必须出一进两出,少一条线就错了,这是考选择题的高频坑。"}
|
|
117
|
+
{"id": "concept-py-try", "type": "concept", "title": "Python:异常处理 try/except", "track": "python", "level": null, "keywords": ["异常", "try", "except", "报错"], "body": "try/except 是程序的‘安全气囊’:把可能翻车的地方包起来,翻车了走 except 里写好的预案,而不是整个程序崩掉。猜数字时用户乱输字母,就靠它兜底。", "example": "try:\n age = int(input('几岁?'))\nexcept:\n print('请输入数字!')", "tip": "except 后面先空着练手可以,但正式代码要写清楚抓哪种错,别‘一抓了之’。"}
|
|
118
|
+
{"id": "concept-py-fstring", "type": "concept", "title": "Python:f-string 格式化输出", "track": "python", "level": null, "keywords": ["f-string", "格式化", "输出"], "body": "f-string 是最顺手的输出写法:字符串前面加个 f,花括号里直接放变量,Python 自动填进去。告别加号拼接的噩梦。", "example": "name = '小明'\nscore = 95\nprint(f'{name} 考了 {score} 分')", "tip": "花括号里是变量名,别加引号;想保留两位小数写 f'{pi:.2f}'。"}
|
|
119
|
+
{"id": "concept-py-dict2", "type": "concept", "title": "Python:字典遍历", "track": "python", "level": null, "keywords": ["字典", "遍历", "items", "keys"], "body": "字典不只能一个个查,还能整锅端:.keys() 拿所有标签,.values() 拿所有值,.items() 拿‘标签+值’对。打印全班成绩单就靠 items()。", "example": "for name, score in scores.items():\n print(f'{name}: {score}')", "tip": "遍历时别在循环里删键,会报错;要删先记下来,循环完再删。"}
|
|
120
|
+
{"id": "concept-py-sort", "type": "concept", "title": "Python:排序 sort() 与 sorted()", "track": "python", "level": null, "keywords": ["排序", "sort", "sorted"], "body": "sort() 是列表‘原地整队’:s.sort() 后 s 自己变有序了;sorted(s) 是‘复印一份排好’,原列表不动。reverse=True 倒序。成绩排名、名字排序都用它。", "example": "s = [80, 92, 70]\ns.sort(reverse=True) # s 变成 [92, 80, 70]", "tip": "sort() 没有返回值,别写成 s = s.sort(),那样 s 会变成 None,经典坑。"}
|
|
121
|
+
{"id": "concept-py-time", "type": "concept", "title": "Python:time 模块", "track": "python", "level": null, "keywords": ["time", "sleep", "计时", "时间"], "body": "time.sleep(1) 让程序‘睡 1 秒’,是做倒计时、动画节奏、闹钟的基础。time.time() 取当前时间戳,两次相减就是‘过了几秒’。", "example": "import time\nfor i in range(3, 0, -1):\n print(i)\n time.sleep(1)\nprint('开始!')", "tip": "sleep 里是秒,小数也行(0.5 秒=0.5);死循环里记得 sleep,不然 CPU 跑满。"}
|
|
122
|
+
{"id": "concept-py-with", "type": "concept", "title": "Python:with open 更安全的写法", "track": "python", "level": null, "keywords": ["with", "文件", "自动关闭"], "body": "with open(...) as f: 是文件操作的安全写法——代码块结束自动关文件,不用记 close,再也不怕忘关。学会它,你的文件代码就毕业了。", "example": "with open('通讯录.txt', 'w') as f:\n f.write('小明 13800000000')\n# 出了这个缩进,文件自动关闭", "tip": "with 只管‘正常结束’自动关,程序中途崩了还是可能丢数据,重要数据写完就存。"}
|
|
123
|
+
{"id": "concept-comment", "type": "concept", "title": "通用:注释习惯", "track": null, "level": null, "keywords": ["注释", "#", "代码规范"], "body": "注释是写给人看的:# 后面写‘这段在干什么、为什么这么干’。好注释=未来的你能看懂。习惯:复杂逻辑先写注释再写代码,调通后注释别删。", "example": "# 交换两个变量的值,不用第三个杯子\na, b = b, a", "tip": "注释写‘为什么’不写‘是什么’:a = a + 1 不用注释‘a 加 1’,要注释‘答对加 10 分’。"}
|
|
124
|
+
{"id": "tut-snake", "type": "tutorial", "title": "手把手:Scratch 贪吃蛇", "track": "scratch", "level": null, "keywords": ["贪吃蛇", "教程", "分步", "列表", "克隆"], "body": "8 步做出贪吃蛇:1. 建列表‘蛇身’存坐标,变量‘方向’;2. 蛇头键盘控制(上下左右改方向变量);3. 蛇头按方向移动,撞墙/撞自己则结束;4. 食物克隆体随机位置出现;5. 蛇头碰到食物→蛇变长(列表加一项)、分数+10、食物换位置;6. 蛇身跟着蛇头走(列表整体前移);7. 速度变量随分数增加;8. Game Over 显示分数。核心就三件套:列表记身体、克隆管食物、变量管方向分数。", "example": "先做‘会动的蛇头’,再做‘吃食物变长’,最后做‘撞墙结束’,三步上线别一次写完。", "tip": "列表编号从 1 开始;蛇身移动用‘在列表开头插入蛇头坐标、删除末尾’实现。"}
|
|
125
|
+
{"id": "tut-flappy", "type": "tutorial", "title": "手把手:小猫快飞(Flappy)", "track": "scratch", "level": null, "keywords": ["Flappy", "教程", "分步", "重力"], "body": "6 步:1. 小猫 y 坐标持续减小模拟重力;2. 按空格 y 增加一大截(起飞);3. 柱子克隆体从右侧生成向左移,上下开口随机;4. 碰到柱子/地面→游戏结束;5. 飞过一根柱子分数+1;6. 难度递增(柱子速度随分数加快)。重力=‘每帧 y 减一点’,这是所有跳跃游戏的灵魂。", "example": "先调出手感:重力太大飞不起来,太小没挑战,多试几个数。", "tip": "柱子用两个克隆体(上+下)中间留开口,开口大小决定难度。"}
|
|
126
|
+
{"id": "tut-ninja", "type": "tutorial", "title": "手把手:水果忍者", "track": "scratch", "level": null, "keywords": ["水果忍者", "教程", "分步", "克隆", "切割"], "body": "6 步:1. 水果克隆体从下方随机 x 位置向上飞(y 增加、x 微摆);2. 飞到顶部掉头下落(重力);3. 鼠标划过水果(碰到鼠标指针)→切开:换成两半造型+分数;4. 水果掉出屏幕底部→删除,漏接扣命;5. 炸弹混入,切到直接结束;6. 3 条命+倒计时 60 秒。‘切’的本质就是碰到鼠标指针侦测。", "example": "先做‘切水果得分’,再做‘炸弹’,最后做‘命和时间’。", "tip": "水果飞行动画用‘y 增加’模拟上抛,配一点 x 摆动更自然。"}
|
|
127
|
+
{"id": "tut-pet-code", "type": "tutorial", "title": "手把手:Python 宠物管理系统", "track": "python", "level": null, "keywords": ["宠物管理", "教程", "代码", "列表"], "body": "代码骨架 5 段:1. pets = ['斑点狗','聪明猫'] 建表;2. while True 主菜单(1 查看/2 领养/3 送走/0 退出);3. 查看:for 打印带编号;4. 领养:input 名字→pets.append;5. 送走:input 名字→if 名字 in pets→pets.remove。排序加分项:pets.sort()。50 行以内搞定,是第一个‘像软件’的程序。", "example": "pets = []\nwhile True:\n print('1.查看 2.领养 3.送走 0.退出')\n c = input('选:')\n if c == '2':\n pets.append(input('名字:'))", "tip": "input 收到的是字符串,编号查宠物时记得 int() 转;先做增查,再做删改。"}
|
|
128
|
+
{"id": "tut-contacts-code", "type": "tutorial", "title": "手把手:Python 手机通讯录", "track": "python", "level": null, "keywords": ["通讯录", "教程", "代码", "字典", "文件"], "body": "代码骨架 6 段:1. book = {} 字典;2. 启动时读文件恢复(try/except 包住,第一次没文件不崩);3. 菜单循环:增(book[name]=phone)/删(del)/改(直接赋值覆盖)/查(in 判断);4. 模糊查:for name in book 找包含关键字的;5. 退出时写文件保存;6. with open 写文件。CRUD+文件=所有管理系统的 heart,学会这个,考勤/成绩单都是换皮。", "example": "with open('book.txt', 'w') as f:\n for n, p in book.items():\n f.write(f'{n} {p}\\n')", "tip": "删除前先 in 检查有没有这个人;文件读写用 with open 防忘关。"}
|
|
129
|
+
{"id": "tut-wordcloud-code", "type": "tutorial", "title": "手把手:Python 词云图生成器", "track": "python", "level": null, "keywords": ["词云", "教程", "代码", "第三方库"], "body": "3 步:1. 准备一段文字(input 输入或读文件);2. 分词:word = cloud.cut('我喜欢编程') 得到词列表;3. 生成并保存:p = cloud.wordcloud(word, '1.jpg'); p.save('词云图.jpg')。词越大=出现次数越多。扩展:统计词频打印 Top10(字典计数),先有数据再有图。", "example": "import ybc_wordcloud as cloud\nword = cloud.cut(input('输一段话:'))\np = cloud.wordcloud(word, '1.jpg')\np.save('词云图.jpg')", "tip": "第三方库先 pip install 再 import;中文分词效果看库,演示够用就行。"}
|
|
130
|
+
{"id": "pg-debug", "type": "parent_guide", "title": "家长指南:陪孩子调试三步", "track": null, "level": null, "keywords": ["家长", "调试", "陪伴"], "body": "孩子卡住时别直接给答案,走三步:1. 复述:‘你跟我说说,程序现在干了什么、你想让它干什么’(一半 bug 在复述时自己冒出来);2. 缩小:‘我们把后一半注释掉,看前一半对不对’;3. 打印:‘加个 print 看看这个变量到底是多少’。你不需要会编程,你需要会提问。", "example": "最好的提问:‘离做出来还差几步?’而不是‘你怎么又不会’。", "tip": "卡住超过 20 分钟就歇 5 分钟,大脑后台会继续想。"}
|
|
131
|
+
{"id": "pg-plan", "type": "parent_guide", "title": "家长指南:定学习计划", "track": null, "level": null, "keywords": ["家长", "计划", "频率"], "body": "计划三要素:频率>时长(每周 3 次 40 分钟 > 周末一次 3 小时)、每次有产出(下课能演示一个能跑的东西)、留白(每月留一周复习/做大项目)。模板:周二新课、周四练习、周六项目;考级前 2 个月加一次模拟考。", "example": "把计划贴墙上,完成一次贴一颗星,仪式感是最好的续航。", "tip": "计划是弹性的:状态好加量,状态差保底(只看 10 分钟视频也算数)。"}
|
|
132
|
+
{"id": "pg-exam-reg", "type": "parent_guide", "title": "家长指南:考级报名指引", "track": null, "level": null, "keywords": ["家长", "考级", "报名"], "body": "全国青少年软件编程等级考试由中国电子学会组织。报名认准两点:1. 查授权:在中国电子学会官网(大纲注明的 kpcb.org.cn / qceit.org.cn)查你城市的授权服务网点;2. 别信‘包过班’:正规考试没有包过,凡承诺包过的都是忽悠。考前 2 个月报名,一年考 1–2 级是健康节奏。", "example": "报名前先做一次全真模拟,分数稳定在 80+ 再去,体验好得多。", "tip": "信息以官网实时公布为准,本文只给方向不给具体链接,防过期。"}
|
|
133
|
+
{"id": "pg-showcase", "type": "parent_guide", "title": "家长指南:作品展示", "track": null, "level": null, "keywords": ["家长", "展示", "作品", "成就感"], "body": "孩子做出作品后做三件事:1. 录屏 1 分钟(操作+讲解);2. 讲给家人听(讲明白=真学会);3. 存档(按日期存文件夹,半年后回看进步巨大)。展示不是炫耀,是学习闭环的最后一环。", "example": "夸要夸具体:‘这个跳跃判定做得真巧’>‘你真棒’。", "tip": "别发朋友圈求赞,孩子的成就感应该来自作品本身,不是点赞数。"}
|
|
134
|
+
{"id": "pg-eyes", "type": "parent_guide", "title": "家长指南:视力与节奏", "track": null, "level": null, "keywords": ["家长", "视力", "休息", "防沉迷"], "body": "规矩先行:每 40 分钟远眺 5 分钟(定闹钟,孩子自己定);屏幕距离一臂;晚上 9 点后不写代码。编程课不是‘多坐会儿’的课,脑子转比手快,累了就歇,歇完 bug 常常自己就想通了。", "example": "把‘休息’写进计划表,而不是‘学不动了才歇’。", "tip": "如果孩子只想玩游戏不想编程,允许他玩 15 分钟自己做的游戏——那是他的作品。"}
|
|
135
|
+
{"id": "pg-choose", "type": "parent_guide", "title": "家长指南:怎么选课/机构", "track": null, "level": null, "keywords": ["家长", "选课", "机构", "避坑"], "body": "看四样:1. 试听(老师是讲还是念?孩子是听还是动手?);2. 师资(有没有作品/考级带考经验,别只看头衔);3. 退费(白纸黑字写清,不敢写的不去);4. 班额(编程课超 12 人基本等于看视频)。避坑:‘包过’‘速成’‘今天不报名就涨价’,三占一就走。", "example": "最好的机构也代替不了每天 10 分钟的练习,机构是教练,练习才是训练。", "tip": "先买 1 期别买年卡,好不好一期就看出来了。"}
|
|
136
|
+
{"id": "vl-brick-11", "type": "video_lesson", "title": "视频配套:打砖块 1.1 项目介绍", "track": "scratch", "level": null, "keywords": ["打砖块", "视频", "项目介绍"], "body": "本节是项目总览:先玩一遍成品,拆出三件套——挡板(玩家控制)、小球(物理运动)、砖块(被消灭的目标)。学项目先看全貌再动手,这是做大项目的第一个习惯。", "example": "", "tip": "看视频前先自己玩 3 分钟,带着‘它是怎么实现的’去看,效果翻倍。", "extra": {"course": "scratch3.0-打砖块", "chapter": "1.1"}}
|
|
137
|
+
{"id": "vl-brick-12", "type": "video_lesson", "title": "视频配套:打砖块 1.2 创建挡板", "track": "scratch", "level": null, "keywords": ["打砖块", "挡板", "角色创建"], "body": "本节创建挡板角色:从角色库选或自己画长条形,放到舞台底部中央(y=-140 附近),大小调到合适。关键步骤:新建角色→调大小→定位到底部。这是所有‘玩家控制物’的标准开局。", "example": "", "tip": "挡板别放太靠边,留出小球下落的‘死亡区’,游戏才有输赢。", "extra": {"course": "scratch3.0-打砖块", "chapter": "1.2"}}
|
|
138
|
+
{"id": "vl-brick-13", "type": "video_lesson", "title": "视频配套:打砖块 1.3 移动挡板", "track": "scratch", "level": null, "keywords": ["打砖块", "挡板", "键盘控制", "移动"], "body": "本节让挡板动起来:两种方案——键盘左右键(x 增减 10)或跟随鼠标 x 坐标。配套练习是‘解答移动挡板’:挡板不动时先查事件挂没挂(当绿旗被点击+重复执行),再查 x 变化写没写。", "example": "", "tip": "跟随鼠标的写法是‘将 x 设为鼠标的 x’,比键盘更跟手,手机党友好。", "extra": {"course": "scratch3.0-打砖块", "chapter": "1.3"}}
|
|
139
|
+
{"id": "vl-brick-14", "type": "video_lesson", "title": "视频配套:打砖块 1.4 创建小球", "track": "scratch", "level": null, "keywords": ["打砖块", "小球", "移动", "方向"], "body": "本节创建小球:圆形角色,‘面向某方向+移动几步’让它动起来。核心是方向系统:0=上、90=右、180=下、-90=左。小球运动=重复执行里不断‘移动+碰到边缘就反弹’。", "example": "", "tip": "小球初始方向别设成正上/正下,斜着发才有游戏性。", "extra": {"course": "scratch3.0-打砖块", "chapter": "1.4"}}
|
|
140
|
+
{"id": "vl-brick-15", "type": "video_lesson", "title": "视频配套:打砖块 1.5 小球贴在挡板", "track": "scratch", "level": null, "keywords": ["打砖块", "小球", "跟随", "状态"], "body": "本节解决‘开局小球在哪’:发射前小球粘在挡板上跟着走(每帧把小球 x 设为挡板 x)。引入‘状态’思想:用变量记录‘准备中/飞行中’,准备中跟随、飞行中自由运动。", "example": "", "tip": "状态变量是游戏编程的通用钥匙:先想‘有几种状态’,再写‘每种状态干什么’。", "extra": {"course": "scratch3.0-打砖块", "chapter": "1.5"}}
|
|
141
|
+
{"id": "vl-brick-16", "type": "video_lesson", "title": "视频配套:打砖块 1.6 自制积木", "track": "scratch", "level": null, "keywords": ["打砖块", "自制积木", "函数"], "body": "本节把‘发射小球’‘小球反弹’等重复逻辑打包成自制积木。学到的是函数思想:起个好名字(发射小球),以后调用一行就行。配套前面的概念库‘自制积木就是函数’一起看。", "example": "", "tip": "自制积木里别放‘当绿旗被点击’,它只能被调用,不能自己启动。", "extra": {"course": "scratch3.0-打砖块", "chapter": "1.6"}}
|
|
142
|
+
{"id": "vl-brick-21", "type": "video_lesson", "title": "视频配套:打砖块 2.1 游戏控制角色", "track": "scratch", "level": null, "keywords": ["打砖块", "游戏控制", "主循环"], "body": "本节搭游戏主循环:一个‘重复执行’管全局——挡板移动、小球运动、碰撞检测都在这里调度。理解‘一帧干三件事’:读输入→更新位置→检测碰撞。", "example": "", "tip": "主循环里每帧的顺序别乱:先动再判,判完再画,不然会‘穿墙’。", "extra": {"course": "scratch3.0-打砖块", "chapter": "2.1"}}
|
|
143
|
+
{"id": "vl-brick-22", "type": "video_lesson", "title": "视频配套:打砖块 2.2 按下鼠标左键", "track": "scratch", "level": null, "keywords": ["打砖块", "鼠标事件", "发射"], "body": "本节用‘当按下鼠标’事件触发发射:点击=发球。事件驱动在这里落地:玩家的点击是一个事件,程序被叫醒后把小球状态从‘准备中’切到‘飞行中’。", "example": "", "tip": "发射前加个小倒计时或‘点击发射’提示,玩家体验好很多。", "extra": {"course": "scratch3.0-打砖块", "chapter": "2.2"}}
|
|
144
|
+
{"id": "vl-brick-23", "type": "video_lesson", "title": "视频配套:打砖块 2.3 小球发射", "track": "scratch", "level": null, "keywords": ["打砖块", "发射", "方向", "速度"], "body": "本节实现发射:给小球一个初始方向(比如 45 度)和速度(移动步数),状态切到飞行中。方向+速度=小球的‘初速度’,后面所有物理都是它的延续。", "example": "", "tip": "发射方向用‘面向(随机数)’也行,但先固定角度调通,再加随机。", "extra": {"course": "scratch3.0-打砖块", "chapter": "2.3"}}
|
|
145
|
+
{"id": "vl-brick-24", "type": "video_lesson", "title": "视频配套:打砖块 2.4 解答小球发射", "track": "scratch", "level": null, "keywords": ["打砖块", "排错", "发射问题"], "body": "本节是排错专场:小球不发射?查三处——事件挂了吗(当按下鼠标)、状态切了吗(准备中→飞行中)、移动积木在循环里吗。小球发射方向怪?查初始面向角度。排错先查‘有没有被叫醒’,再查‘醒来干了什么’。", "example": "", "tip": "90% 的‘不动 bug’都是事件没挂或循环没包住,先看这两处。", "extra": {"course": "scratch3.0-打砖块", "chapter": "2.4"}}
|
|
146
|
+
{"id": "vl-brick-25", "type": "video_lesson", "title": "视频配套:打砖块 2.5 小球反弹", "track": "scratch", "level": null, "keywords": ["打砖块", "反弹", "碰撞", "侦测"], "body": "本节做反弹:碰到边缘用‘碰到边缘就反弹’;碰到挡板/砖块用‘碰到颜色/角色’侦测后转向。简化物理:反弹角度=180-入射角,Scratch 里直接用‘反弹’积木或手动调面向。砖块被撞→删除+分数+1。", "example": "", "tip": "先做边缘反弹,再做挡板反弹,最后做砖块,三步别并一步。", "extra": {"course": "scratch3.0-打砖块", "chapter": "2.5"}}
|
|
147
|
+
{"id": "vl-brick-26", "type": "video_lesson", "title": "视频配套:打砖块 2.6 速度变量", "track": "scratch", "level": null, "keywords": ["打砖块", "速度", "变量", "难度"], "body": "本节把小球速度做成变量:移动步数=速度变量。消掉几排砖就+1,游戏自然变难。变量在这里是‘难度旋钮’,也是三级变量考点的实战。", "example": "", "tip": "速度别加太猛,每次+0.5 试手感;上限封顶,不然变成‘光速球’。", "extra": {"course": "scratch3.0-打砖块", "chapter": "2.6"}}
|
|
148
|
+
{"id": "vl-brick-27", "type": "video_lesson", "title": "视频配套:打砖块 2.7 随机发射角度", "track": "scratch", "level": null, "keywords": ["打砖块", "随机数", "重玩价值"], "body": "本节用随机数让每次开局不一样:发射角度在 30–60 度间随机。小改动大体验:固定套路的游戏玩两次就腻,随机让它百玩不厌。三级随机数考点的活例子。", "example": "", "tip": "随机范围别太大(别 0–180),太离谱的角度开局就暴毙。", "extra": {"course": "scratch3.0-打砖块", "chapter": "2.7"}}
|
|
149
|
+
{"id": "vl-boss-77", "type": "video_lesson", "title": "视频配套:Boss 战 7.7 老板受到玩家飞船伤害", "track": "scratch", "level": null, "keywords": ["Boss战", "血量", "伤害", "射击游戏"], "body": "本节做 Boss 受伤:Boss 有‘血量’变量,玩家子弹克隆体碰到 Boss→血量减少→血条更新。血条做法:用变量数值画条,或‘血量’个克隆体排一排。伤害数字飘字是加分项。", "example": "", "tip": "子弹碰到 Boss 的侦测要放在子弹克隆体自己的脚本里,别放在 Boss 身上,好维护。", "extra": {"course": "scratch3.0-Boss战射击游戏", "chapter": "7.7"}}
|
|
150
|
+
{"id": "vl-boss-78", "type": "video_lesson", "title": "视频配套:Boss 战 7.8 解答问题", "track": "scratch", "level": null, "keywords": ["Boss战", "排错", "子弹穿透"], "body": "本节排错:子弹穿过 Boss 不扣血?三查——子弹速度太快一帧跨过去了(降速或每帧多判几次)、侦测写在本体没写在克隆体、Boss 碰撞体积和造型不一致。‘穿透 bug’是射击游戏的经典 bug,修一次记一辈子。", "example": "", "tip": "高速子弹用‘碰到颜色’比‘碰到角色’更稳,颜色块更大好命中。", "extra": {"course": "scratch3.0-Boss战射击游戏", "chapter": "7.8"}}
|
|
151
|
+
{"id": "vl-boss-81", "type": "video_lesson", "title": "视频配套:Boss 战 8.1 导入失败界面", "track": "scratch", "level": null, "keywords": ["失败界面", "GameOver", "状态机"], "body": "本节做失败界面:玩家血量归零→切换到 Game Over 背景/显示失败角色→停止游戏脚本。引入状态机:playing/dead 两个状态,dead 后主循环不再更新。这是所有游戏‘结束’的标准写法。", "example": "", "tip": "失败界面单独做,别和游戏中画面挤在一起,清爽才有仪式感。", "extra": {"course": "scratch3.0-Boss战射击游戏", "chapter": "8.1"}}
|
|
152
|
+
{"id": "vl-boss-82", "type": "video_lesson", "title": "视频配套:Boss 战 8.2 解答游戏失败后的问题", "track": "scratch", "level": null, "keywords": ["失败重开", "变量重置", "排错"], "body": "本节解决‘重开一局还是死的’:失败后要点绿旗重开,必须重置——血量回满、分数清零、场上克隆体删光、Boss 回初始位置。重开 bug 的本质:上一局的状态没清干净。做个‘初始化’自制积木,重开时调用它。", "example": "", "tip": "所有‘重开’问题,先问:有没有一键重置所有变量和克隆体?", "extra": {"course": "scratch3.0-Boss战射击游戏", "chapter": "8.2"}}
|
|
153
|
+
{"id": "vl-boss-83", "type": "video_lesson", "title": "视频配套:Boss 战 8.3 导入胜利界面", "track": "scratch", "level": null, "keywords": ["胜利界面", "胜利判定"], "body": "本节做胜利:Boss 血量<=0→胜利判定→切换胜利界面+撒花+音效。胜利和失败是镜像逻辑:一个看玩家血量,一个看 Boss 血量。胜利界面记得显示用时/分数,通关才有炫耀资本。", "example": "", "tip": "胜利判定每帧检查一次,别只在子弹命中时查,补刀那一下可能漏判。", "extra": {"course": "scratch3.0-Boss战射击游戏", "chapter": "8.3"}}
|
|
154
|
+
{"id": "vl-boss-84", "type": "video_lesson", "title": "视频配套:Boss 战 8.4 修正 bug", "track": "scratch", "level": null, "keywords": ["修bug", "调试", "回归测试"], "body": "本节讲系统化修 bug 四步:复现(每次都能重现吗)→定位(二分法注释掉一半代码)→修复→回归(修完把之前的功能都玩一遍)。附常见 bug 清单:克隆体不删卡死、变量不清零、事件重复挂载。修 bug 不是玄学,是流程。", "example": "", "tip": "修完一个 bug 先别急着开香槟,把游戏完整玩一遍,修 bug 引入新 bug 是常态。", "extra": {"course": "scratch3.0-Boss战射击游戏", "chapter": "8.4"}}
|
|
155
|
+
{"id": "vl-boss-85", "type": "video_lesson", "title": "视频配套:Boss 战 8.5 玩一遍完整游戏", "track": "scratch", "level": null, "keywords": ["通关测试", "发布检查"], "body": "本节是发布前检查:从头到尾玩一遍,记录 bug 清单逐个消灭。检查表:开局正常?难度曲线合理?胜负界面都进得去?重开干净?音效不炸?通关测试是‘作品’和‘半成品’的分水岭。", "example": "", "tip": "找个人帮你玩一遍,别人总能找到你找不到的 bug,这叫‘小白鼠测试’。", "extra": {"course": "scratch3.0-Boss战射击游戏", "chapter": "8.5"}}
|
|
156
|
+
{"id": "vl-s2-rand", "type": "video_lesson", "title": "视频配套:scratch2.0 第 6 课 随机数", "track": "scratch", "level": null, "keywords": ["随机数", "scratch2.0", "视频"], "body": "本节讲随机数:‘在 1 到 10 间取随机数’,应用是骰子、抽奖、随机出题。2.0 和 3.0 的随机数积木用法一致,学完直接平移。配套概念库‘随机数’一起看。", "example": "", "tip": "随机数+变量=抽奖机,随机数+克隆=随机刷怪,两个公式背下来。", "extra": {"course": "scratch2.0-零散教程", "chapter": "第6课"}}
|
|
157
|
+
{"id": "vl-s2-if", "type": "video_lesson", "title": "视频配套:scratch2.0 判断语句学案", "track": "scratch", "level": null, "keywords": ["判断语句", "如果", "学案", "scratch2.0"], "body": "学案讲‘如果…那么…’判断语句:条件成立才执行。配套练习思路:红绿灯(如果是红灯就停)、成绩判断。判断是分支的起点,后面所有游戏逻辑都从这里长出来。", "example": "", "tip": "判断题先写‘条件是什么’,再写‘成立干什么’,顺序别反。", "extra": {"course": "scratch2.0-零散教程", "chapter": "第二课"}}
|
|
158
|
+
{"id": "vl-s2-music", "type": "video_lesson", "title": "视频配套:scratch2.0 打击乐器+电子琴", "track": "scratch", "level": null, "keywords": ["电子琴", "声音", "事件", "scratch2.0"], "body": "本节用声音+事件做电子琴:按不同键播放不同音调,还能做打击乐器。音乐+编程=‘可玩的代码’,适合当兴趣钩子课。3.0 有音乐扩展可以做出更真的乐器。", "example": "", "tip": "音调和节奏用两个变量分别控制,改编《小星星》当课后作业。", "extra": {"course": "scratch2.0-零散教程", "chapter": "第八课"}}
|
|
159
|
+
{"id": "vl-s2-sea", "type": "video_lesson", "title": "视频配套:scratch2.0 深海大战学案", "track": "scratch", "level": null, "keywords": ["深海大战", "综合项目", "学案", "scratch2.0"], "body": "学案是一个综合项目:深海场景、多角色(潜水员/鱼群/宝箱)、侦测收集、分数变量。是 2.0 时代的‘大综合’,对应 3.0 的水下拾钻+保卫家园思路。拿它当综合复习:能独立做出来=二级水平稳了。", "example": "", "tip": "综合项目先列‘角色清单’再动手,角色超过 5 个就用广播管配合。", "extra": {"course": "scratch2.0-零散教程", "chapter": "第二十九课"}}
|
|
160
|
+
{"id": "prac-L1-ui", "type": "practice", "title": "一级练习:界面认知", "track": "scratch", "level": 1, "keywords": ["练习", "刷题", "模拟题", "界面", "绿旗", "单选"], "body": "【单选】下面哪个按钮能让程序运行起来?\nA. 红色圆点\nB. 绿旗\nC. 垃圾桶\nD. 全屏", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "单选", "question": "下面哪个按钮能让程序运行起来?", "choices": ["红色圆点", "绿旗", "垃圾桶", "全屏"], "answer": "B", "explanation": "绿旗=运行,红点=停止,这是舞台区最基本的两个按钮,一级平台操作必考。"}}
|
|
161
|
+
{"id": "prac-L1-bg", "type": "practice", "title": "一级练习:背景与角色", "track": "scratch", "level": 1, "keywords": ["练习", "刷题", "模拟题", "背景", "角色", "判断"], "body": "【判断】背景和角色一样,都可以移动和旋转。\nA. 对\nB. 错", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "判断", "question": "背景和角色一样,都可以移动和旋转。", "choices": ["对", "错"], "answer": "错", "explanation": "背景是舞台的布,只能换不能动;角色才是台上的演员,能移动旋转。口诀:换造型是换衣服(角色),换背景是换布景(舞台)。"}}
|
|
162
|
+
{"id": "prac-L1-code", "type": "practice", "title": "一级练习:小猫散步", "track": "scratch", "level": 1, "keywords": ["练习", "刷题", "模拟题", "编程", "顺序结构"], "body": "【编程】让小猫从舞台左边走到右边,中途换 2 次造型,到达后播放一段音乐。", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "编程", "question": "让小猫从舞台左边走到右边,中途换 2 次造型,到达后播放一段音乐。", "choices": [], "answer": "自评", "explanation": "参考做法:当绿旗被点击→移到 x:-200→重复走(移动 10 步,下一个造型)直到 x>200→播放音乐。检查清单:顺序对吗?造型换了吗?声音放了吗?保存了吗?"}}
|
|
163
|
+
{"id": "prac-L1-logic", "type": "practice", "title": "一级练习:图形找规律", "track": "scratch", "level": 1, "keywords": ["练习", "刷题", "模拟题", "逻辑推理", "找规律", "单选"], "body": "【单选】○△□ ○△?,问号处是什么图形?\nA. ○\nB. △\nC. □\nD. ☆", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "单选", "question": "○△□ ○△?,问号处是什么图形?", "choices": ["○", "△", "□", "☆"], "answer": "C", "explanation": "一组 3 个图形循环:○△□○△□……第 6 个是□。方法:先找‘一组几个’,再算‘第几个除以组长余几’。"}}
|
|
164
|
+
{"id": "prac-L1-save", "type": "practice", "title": "一级练习:保存作品", "track": "scratch", "level": 1, "keywords": ["练习", "刷题", "模拟题", "保存", "文件", "单选"], "body": "【单选】做完作品第一件事应该做什么?\nA. 直接关掉\nB. 保存作品\nC. 删除角色\nD. 换背景", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "单选", "question": "做完作品第一件事应该做什么?", "choices": ["直接关掉", "保存作品", "删除角色", "换背景"], "answer": "B", "explanation": "没保存=白做。一级考‘新建/保存/打开’是送分题,但每年都有人因为没保存丢编程题的分。"}}
|
|
165
|
+
{"id": "prac-L1-move", "type": "practice", "title": "一级练习:移动积木", "track": "scratch", "level": 1, "keywords": ["练习", "刷题", "模拟题", "移动", "积木", "判断"], "body": "【判断】“移动 10 步”是让角色向前走 10 步。\nA. 对\nB. 错", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "判断", "question": "“移动 10 步”是让角色向前走 10 步。", "choices": ["对", "错"], "answer": "对", "explanation": "移动是沿着角色当前面向的方向走,不是固定向右。想走别的方向,先‘面向’再‘移动’。"}}
|
|
166
|
+
{"id": "prac-L1-sound2", "type": "practice", "title": "一级练习:背景音乐", "track": "scratch", "level": 1, "keywords": ["练习", "刷题", "模拟题", "声音", "背景音乐", "单选"], "body": "【单选】想让音乐播完再往下执行,应该用哪个积木?\nA. 播放声音\nB. 播放声音直到结束\nC. 停止所有声音\nD. 将音量设为", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "单选", "question": "想让音乐播完再往下执行,应该用哪个积木?", "choices": ["播放声音", "播放声音直到结束", "停止所有声音", "将音量设为"], "answer": "B", "explanation": "‘播放声音’是播着播着就往下走了,‘播放声音直到结束’是等播完再走。做背景音乐循环播用前者,做‘播完再说话’用后者。"}}
|
|
167
|
+
{"id": "prac-L1-costume", "type": "practice", "title": "一级练习:换造型", "track": "scratch", "level": 1, "keywords": ["练习", "刷题", "模拟题", "编程", "造型", "等待"], "body": "【编程】让小猫换 3 次造型,每次换完等 1 秒再换下一次。", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "编程", "question": "让小猫换 3 次造型,每次换完等 1 秒再换下一次。", "choices": [], "answer": "自评", "explanation": "参考做法:当绿旗被点击→重复 3 次(下一个造型,等待 1 秒)。注意‘等待’别漏,不然换得太快看不见。"}}
|
|
168
|
+
{"id": "prac-L2-loop", "type": "practice", "title": "二级练习:循环选型", "track": "scratch", "level": 2, "keywords": ["练习", "刷题", "模拟题", "循环", "单选"], "body": "【单选】要让小猫一直追着鼠标跑,应该用哪种循环?\nA. 重复 10 次\nB. 重复执行\nC. 重复直到按下空格\nD. 只跑一次", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "单选", "question": "要让小猫一直追着鼠标跑,应该用哪种循环?", "choices": ["重复 10 次", "重复执行", "重复直到按下空格", "只跑一次"], "answer": "B", "explanation": "‘一直’=无限=重复执行。口诀:数得清用次数,数不清看条件,要一直用重复执行。"}}
|
|
169
|
+
{"id": "prac-L2-key", "type": "practice", "title": "二级练习:键盘控制", "track": "scratch", "level": 2, "keywords": ["练习", "刷题", "模拟题", "编程", "键盘", "侦测"], "body": "【编程】用上下左右键控制小猫移动,碰到边缘说‘撞墙了’。", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "编程", "question": "用上下左右键控制小猫移动,碰到边缘说‘撞墙了’。", "choices": [], "answer": "自评", "explanation": "参考做法:当绿旗被点击→重复执行→4 个如果(上移键→y+10,下移→y-10,左移→x-10,右移→x+10)→如果碰到边缘→说‘撞墙了’2 秒。二级模板题,默写到不用想。"}}
|
|
170
|
+
{"id": "prac-L2-logicop", "type": "practice", "title": "二级练习:且与或", "track": "scratch", "level": 2, "keywords": ["练习", "刷题", "模拟题", "逻辑运算", "判断"], "body": "【判断】“(3>2)且(2>5)”的结果是成立。\nA. 对\nB. 错", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "判断", "question": "“(3>2)且(2>5)”的结果是成立。", "choices": ["对", "错"], "answer": "错", "explanation": "且=两个都成立才成立。2>5 不成立,所以整体不成立。且是‘都要’,或是‘要一个’,不成立是‘反过来’。"}}
|
|
171
|
+
{"id": "prac-L2-pen", "type": "practice", "title": "二级练习:画正六边形", "track": "scratch", "level": 2, "keywords": ["练习", "刷题", "模拟题", "编程", "画笔", "正多边形"], "body": "【编程】用画笔画一个边长 80 的正六边形。", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "编程", "question": "用画笔画一个边长 80 的正六边形。", "choices": [], "answer": "自评", "explanation": "参考做法:落笔→重复 6 次(移动 80 步,右转 60 度)→抬笔。旋转角度=360÷边数。正五边形就是 72 度,考前把 3/4/5/6 边形都画一遍。"}}
|
|
172
|
+
{"id": "prac-L2-until", "type": "practice", "title": "二级练习:重复直到", "track": "scratch", "level": 2, "keywords": ["练习", "刷题", "模拟题", "循环", "重复直到", "单选"], "body": "【单选】“重复直到碰到边缘”这个循环什么时候停?\nA. 重复 10 次后\nB. 碰到边缘时\nC. 按下绿旗时\nD. 永远不停", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "单选", "question": "“重复直到碰到边缘”这个循环什么时候停?", "choices": ["重复 10 次后", "碰到边缘时", "按下绿旗时", "永远不停"], "answer": "B", "explanation": "‘重复直到+条件’=条件满足就停。它比‘重复 100 次’更稳,因为不用猜要几次。"}}
|
|
173
|
+
{"id": "prac-L2-or", "type": "practice", "title": "二级练习:或运算", "track": "scratch", "level": 2, "keywords": ["练习", "刷题", "模拟题", "逻辑运算", "或", "判断"], "body": "【判断】如果(a>5)或(b<3),a=7,b=10,那么条件成立。\nA. 对\nB. 错", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "判断", "question": "如果(a>5)或(b<3),a=7,b=10,那么条件成立。", "choices": ["对", "错"], "answer": "对", "explanation": "或=一个成立就行。a>5 成立了,整个条件就成立,不用看 b。"}}
|
|
174
|
+
{"id": "prac-L2-tri", "type": "practice", "title": "二级练习:画正三角形", "track": "scratch", "level": 2, "keywords": ["练习", "刷题", "模拟题", "编程", "画笔", "正三角形"], "body": "【编程】用画笔画一个边长 100 的正三角形。", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "编程", "question": "用画笔画一个边长 100 的正三角形。", "choices": [], "answer": "自评", "explanation": "参考做法:落笔→重复 3 次(移动 100 步,右转 120 度)→抬笔。注意是 120 度不是 60 度:转的是外角,360÷3=120。"}}
|
|
175
|
+
{"id": "prac-L2-space", "type": "practice", "title": "二级练习:空格跳跃", "track": "scratch", "level": 2, "keywords": ["练习", "刷题", "模拟题", "事件", "空格键", "单选"], "body": "【单选】想实现‘按空格小猫跳一下’,应该用哪个事件积木?\nA. 当绿旗被点击\nB. 当按下空格键\nC. 当角色被点击\nD. 当背景切换", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "单选", "question": "想实现‘按空格小猫跳一下’,应该用哪个事件积木?", "choices": ["当绿旗被点击", "当按下空格键", "当角色被点击", "当背景切换"], "answer": "B", "explanation": "事件要对上玩家的动作:按空格=‘当按下空格键’。跳一下= y 增加再回来,配个等待 0.2 秒。"}}
|
|
176
|
+
{"id": "prac-L3-bc", "type": "practice", "title": "三级练习:广播与等待", "track": "scratch", "level": 3, "keywords": ["练习", "刷题", "模拟题", "广播", "单选"], "body": "【单选】开幕式要求所有演员表演完再放礼花,应该用?\nA. 广播\nB. 广播并等待\nC. 克隆\nD. 变量", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "单选", "question": "开幕式要求所有演员表演完再放礼花,应该用?", "choices": ["广播", "广播并等待", "克隆", "变量"], "answer": "B", "explanation": "‘等干完再继续’=广播并等待。口诀:喊话就走用广播,喊话等人用并等待。"}}
|
|
177
|
+
{"id": "prac-L3-bullet", "type": "practice", "title": "三级练习:克隆子弹", "track": "scratch", "level": 3, "keywords": ["练习", "刷题", "模拟题", "编程", "克隆", "子弹"], "body": "【编程】按空格发射子弹,子弹飞到边缘消失。", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "编程", "question": "按空格发射子弹,子弹飞到边缘消失。", "choices": [], "answer": "自评", "explanation": "参考做法:当按下空格键→克隆自己;当作为克隆体启动→移到发射位置→重复直到碰到边缘(移动 15 步)→删除克隆体。必查:克隆体删了吗?出生位置对吗?"}}
|
|
178
|
+
{"id": "prac-L3-score", "type": "practice", "title": "三级练习:计分器", "track": "scratch", "level": 3, "keywords": ["练习", "刷题", "模拟题", "编程", "变量", "计分"], "body": "【编程】做一个投篮计分器:进一球得 2 分,游戏开始分数清零。", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "编程", "question": "做一个投篮计分器:进一球得 2 分,游戏开始分数清零。", "choices": [], "answer": "自评", "explanation": "参考做法:当绿旗被点击→将‘分数’设为 0;当球碰到篮筐→将‘分数’增加 2→说‘进了!’。新手最高频丢分:忘了开局清零。"}}
|
|
179
|
+
{"id": "prac-L3-nest", "type": "practice", "title": "三级练习:星星阵", "track": "scratch", "level": 3, "keywords": ["练习", "刷题", "模拟题", "编程", "嵌套", "循环"], "body": "【编程】画 3 行 4 列的星星阵(共 12 颗)。", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "编程", "question": "画 3 行 4 列的星星阵(共 12 颗)。", "choices": [], "answer": "自评", "explanation": "参考做法:外层重复 3 次(管行),内层重复 4 次(管列),每颗移到对应坐标盖图章。外层管行内层管列是固定套路,嵌套别超 3 层。"}}
|
|
180
|
+
{"id": "prac-L3-var2", "type": "practice", "title": "三级练习:变量清零", "track": "scratch", "level": 3, "keywords": ["练习", "刷题", "模拟题", "变量", "单选"], "body": "【单选】游戏开始时忘记把分数清零,会发生什么?\nA. 游戏玩不了\nB. 上一局的分数带到下一局\nC. 角色消失\nD. 声音没了", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "单选", "question": "游戏开始时忘记把分数清零,会发生什么?", "choices": ["游戏玩不了", "上一局的分数带到下一局", "角色消失", "声音没了"], "answer": "B", "explanation": "变量不清零=盒子里的旧东西没倒掉。这是三级实操最高频的丢分点,养成习惯:开局第一件事,变量全清零。"}}
|
|
181
|
+
{"id": "prac-L3-clone2", "type": "practice", "title": "三级练习:克隆体清理", "track": "scratch", "level": 3, "keywords": ["练习", "刷题", "模拟题", "克隆", "判断"], "body": "【判断】克隆体用完不删除也没关系,反正看不见。\nA. 对\nB. 错", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "判断", "question": "克隆体用完不删除也没关系,反正看不见。", "choices": ["对", "错"], "answer": "错", "explanation": "克隆体不删会越积越多,最后卡死。记住:谁出生谁负责删,‘删除克隆体’是克隆体的葬礼,必须办。"}}
|
|
182
|
+
{"id": "prac-L3-relay", "type": "practice", "title": "三级练习:广播接力", "track": "scratch", "level": 3, "keywords": ["练习", "刷题", "模拟题", "编程", "广播", "接力"], "body": "【编程】做接力跑:小猫跑到终点后,小狗开始跑。", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "编程", "question": "做接力跑:小猫跑到终点后,小狗开始跑。", "choices": [], "answer": "自评", "explanation": "参考做法:小猫:跑到终点→广播‘接力’;小狗:当收到‘接力’→开始跑。广播消息名字要具体,别用‘消息1’。"}}
|
|
183
|
+
{"id": "prac-L3-rand2", "type": "practice", "title": "三级练习:随机刷怪", "track": "scratch", "level": 3, "keywords": ["练习", "刷题", "模拟题", "随机数", "单选"], "body": "【单选】想让敌人每次出现在随机 x 位置,应该用?\nA. 将 x 设为 100\nB. 将 x 设为在 -200 到 200 间取随机数\nC. 将 x 增加 10\nD. 移到鼠标指针", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "单选", "question": "想让敌人每次出现在随机 x 位置,应该用?", "choices": ["将 x 设为 100", "将 x 设为在 -200 到 200 间取随机数", "将 x 增加 10", "移到鼠标指针"], "answer": "B", "explanation": "随机位置=随机数。‘在 a 到 b 间取随机数’是三级 8 分的考点,刷怪/抽奖/随机出题都靠它。"}}
|
|
184
|
+
{"id": "prac-L4-list", "type": "practice", "title": "四级练习:链表索引", "track": "scratch", "level": 4, "keywords": ["练习", "刷题", "模拟题", "链表", "索引", "单选"], "body": "【单选】链表里有 5 个数,‘第 5 项’是第几个数?\nA. 第 4 个\nB. 第 5 个\nC. 第 6 个\nD. 最后一个的下一个", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "单选", "question": "链表里有 5 个数,‘第 5 项’是第几个数?", "choices": ["第 4 个", "第 5 个", "第 6 个", "最后一个的下一个"], "answer": "B", "explanation": "Scratch 链表编号从 1 开始,第 5 项就是第 5 个数。注意 Python 里是从 0 开始,转语言别混。"}}
|
|
185
|
+
{"id": "prac-L4-avg", "type": "practice", "title": "四级练习:链表求平均", "track": "scratch", "level": 4, "keywords": ["练习", "刷题", "模拟题", "编程", "链表", "平均分"], "body": "【编程】链表存了全班 5 个分数,求平均分并说出来。", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "编程", "question": "链表存了全班 5 个分数,求平均分并说出来。", "choices": [], "answer": "自评", "explanation": "参考做法:将‘总和’设为 0→重复‘链表长度’次(将总和增加‘第 循环次数 项’)→说‘平均分是’连接(总和/链表长度)。三步走:建表→遍历累加→算结果。"}}
|
|
186
|
+
{"id": "prac-L4-func", "type": "practice", "title": "四级练习:自制积木改写", "track": "scratch", "level": 4, "keywords": ["练习", "刷题", "模拟题", "编程", "自制积木", "函数"], "body": "【编程】把‘画正方形’和‘画三角形’两段重复代码,改成一个带参数的自制积木‘画多边形(边数)’。", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "编程", "question": "把‘画正方形’和‘画三角形’两段重复代码,改成一个带参数的自制积木‘画多边形(边数)’。", "choices": [], "answer": "自评", "explanation": "参考做法:定义积木‘画多边形(边数)’→重复‘边数’次(移动 60 步,右转 360/边数 度);调用时分别传 4 和 3。参数就是‘每次不一样的那个数’。"}}
|
|
187
|
+
{"id": "prac-L4-stairs", "type": "practice", "title": "四级练习:爬楼梯", "track": "scratch", "level": 4, "keywords": ["练习", "刷题", "模拟题", "算法", "递归", "单选"], "body": "【单选】楼梯有 5 阶,一次走 1 阶或 2 阶,有几种走法?\nA. 5 种\nB. 6 种\nC. 8 种\nD. 13 种", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "单选", "question": "楼梯有 5 阶,一次走 1 阶或 2 阶,有几种走法?", "choices": ["5 种", "6 种", "8 种", "13 种"], "answer": "C", "explanation": "到 n 阶=到 n-1 阶+到 n-2 阶。n=1→1,n=2→2,n=3→3,n=4→5,n=5→8。递归题先手算小数字找规律。"}}
|
|
188
|
+
{"id": "prac-L4-str", "type": "practice", "title": "四级练习:字符串取字符", "track": "scratch", "level": 4, "keywords": ["练习", "刷题", "模拟题", "字符串", "单选"], "body": "【单选】字符串“hello”的‘第 2 个字符’是?\nA. h\nB. e\nC. l\nD. o", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "单选", "question": "字符串“hello”的‘第 2 个字符’是?", "choices": ["h", "e", "l", "o"], "answer": "B", "explanation": "Scratch 字符串从第 1 个数起,‘hello’的第 2 个是 e。又是那个坑:Scratch 从 1 开始,Python 从 0 开始。"}}
|
|
189
|
+
{"id": "prac-L4-scope", "type": "practice", "title": "四级练习:作用域", "track": "scratch", "level": 4, "keywords": ["练习", "刷题", "模拟题", "函数", "作用域", "判断"], "body": "【判断】自制积木里定义的变量,在积木外面可以直接用。\nA. 对\nB. 错", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "判断", "question": "自制积木里定义的变量,在积木外面可以直接用。", "choices": ["对", "错"], "answer": "错", "explanation": "函数里的变量外面用不了,这叫作用域。需要的结果要用‘返回值’交出来,这是四级函数的核心思想。"}}
|
|
190
|
+
{"id": "prac-L4-max", "type": "practice", "title": "四级练习:链表找最大", "track": "scratch", "level": 4, "keywords": ["练习", "刷题", "模拟题", "编程", "链表", "最值"], "body": "【编程】链表存了 5 个分数,找出最高分并说出来。", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "编程", "question": "链表存了 5 个分数,找出最高分并说出来。", "choices": [], "answer": "自评", "explanation": "参考做法:将‘最高分’设为第 1 项→重复(如果第 循环次数 项>最高分→将最高分设为第 循环次数 项)。打擂台思想:先假设第 1 个最强,一个个来挑战。"}}
|
|
191
|
+
{"id": "prac-L4-algo2", "type": "practice", "title": "四级练习:爬楼梯进阶", "track": "scratch", "level": 4, "keywords": ["练习", "刷题", "模拟题", "算法", "递归", "单选"], "body": "【单选】爬楼梯 n=6 时有几种走法?(接上题规律)\nA. 8 种\nB. 11 种\nC. 13 种\nD. 21 种", "example": "", "tip": "原创模拟题,非真题。", "extra": {"qtype": "单选", "question": "爬楼梯 n=6 时有几种走法?(接上题规律)", "choices": ["8 种", "11 种", "13 种", "21 种"], "answer": "C", "explanation": "n=5→8,n=6=8+5=13。斐波那契数列:1,2,3,5,8,13……后一项=前两项之和。"}}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""少儿编程通 MCP 服务器(FastMCP,PyPI 包 kids-coding-tutor-mcp)。
|
|
4
|
+
|
|
5
|
+
数据来源(按优先级):
|
|
6
|
+
1. 环境变量 KIDS_CODING_RECORDS;
|
|
7
|
+
2. 包内数据文件 data/records.jsonl(importlib.resources 读取,安装后不依赖原 skill 目录);
|
|
8
|
+
3. 回退:原 skill 包 /home/hatch/workspace/skills/kids-coding-tutor/assets/records.jsonl。
|
|
9
|
+
|
|
10
|
+
检索/规划/刷题逻辑与原 skill 的 scripts/search.py、answer.py、plan.py、
|
|
11
|
+
quiz.py、videos.py 保持一致(关键词计分、类型/级别过滤、路线与 4 周节奏)。
|
|
12
|
+
|
|
13
|
+
启动:
|
|
14
|
+
kids-coding-tutor-mcp # 默认 stdio(供 uvx / 百炼部署)
|
|
15
|
+
kids-coding-tutor-mcp --transport streamable-http --host 127.0.0.1 --port 8000
|
|
16
|
+
python -m kids_coding_tutor_mcp # 同默认 stdio
|
|
17
|
+
"""
|
|
18
|
+
import argparse
|
|
19
|
+
import difflib
|
|
20
|
+
import json
|
|
21
|
+
import os
|
|
22
|
+
import re
|
|
23
|
+
|
|
24
|
+
from mcp.server.fastmcp import FastMCP
|
|
25
|
+
|
|
26
|
+
DEFAULT_DB = "/home/hatch/workspace/skills/kids-coding-tutor/assets/records.jsonl"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _resolve_db_path():
|
|
30
|
+
"""按优先级解析数据文件路径。"""
|
|
31
|
+
env = os.environ.get("KIDS_CODING_RECORDS")
|
|
32
|
+
if env:
|
|
33
|
+
return env
|
|
34
|
+
try:
|
|
35
|
+
from importlib.resources import files
|
|
36
|
+
|
|
37
|
+
p = files("kids_coding_tutor_mcp") / "data" / "records.jsonl"
|
|
38
|
+
if p.is_file():
|
|
39
|
+
return str(p)
|
|
40
|
+
except Exception:
|
|
41
|
+
pass
|
|
42
|
+
return DEFAULT_DB
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
DB = _resolve_db_path()
|
|
46
|
+
|
|
47
|
+
mcp = FastMCP("kids-coding-tutor")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def load_records():
|
|
51
|
+
recs = []
|
|
52
|
+
with open(DB, encoding="utf-8") as f:
|
|
53
|
+
for line in f:
|
|
54
|
+
line = line.strip()
|
|
55
|
+
if line:
|
|
56
|
+
recs.append(json.loads(line))
|
|
57
|
+
return recs
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
RECORDS = load_records()
|
|
61
|
+
BY_ID = {r["id"]: r for r in RECORDS}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def fmt_record(r):
|
|
65
|
+
"""与 answer.py 的 show() 一致的纯中文全文格式。"""
|
|
66
|
+
out = ["【%s】" % r["title"]]
|
|
67
|
+
out.append("编号=%s | 类型=%s | 赛道=%s | 级别=%s"
|
|
68
|
+
% (r["id"], r["type"], r.get("track"), r.get("level")))
|
|
69
|
+
out.append("")
|
|
70
|
+
out.append(r.get("body", ""))
|
|
71
|
+
if r.get("example"):
|
|
72
|
+
out.append("")
|
|
73
|
+
out.append("例子:")
|
|
74
|
+
out.append(r["example"])
|
|
75
|
+
if r.get("tip"):
|
|
76
|
+
out.append("")
|
|
77
|
+
out.append("提醒:" + r["tip"])
|
|
78
|
+
return "\n".join(out)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def search_score(rec, terms):
|
|
82
|
+
"""与 search.py 的 score() 一致:标题命中 x3,关键词精确命中 +5。"""
|
|
83
|
+
text = " ".join([rec.get("title", ""), rec.get("body", ""), rec.get("example", ""),
|
|
84
|
+
rec.get("tip", ""), " ".join(rec.get("keywords", []))])
|
|
85
|
+
s = 0
|
|
86
|
+
for t in terms:
|
|
87
|
+
c = text.count(t)
|
|
88
|
+
if c:
|
|
89
|
+
s += c * (3 if t in rec.get("title", "") else 1)
|
|
90
|
+
for kw in rec.get("keywords", []):
|
|
91
|
+
for t in terms:
|
|
92
|
+
if t == kw:
|
|
93
|
+
s += 5
|
|
94
|
+
return s
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@mcp.tool()
|
|
98
|
+
def search_knowledge(query: str, type: str | None = None, limit: int = 5) -> str:
|
|
99
|
+
"""按关键词搜索少儿编程知识库。query 为关键词(空格分隔多词);
|
|
100
|
+
type 可选限定类型:concept、exam_point、lesson、project、faq、practice、
|
|
101
|
+
tutorial、parent_guide、video_lesson。返回匹配记录的全文。"""
|
|
102
|
+
terms = [t for t in re.split(r"\s+", query.strip()) if t]
|
|
103
|
+
if not terms:
|
|
104
|
+
return "请给出关键词,例如:变量、循环、贪吃蛇。"
|
|
105
|
+
hits = []
|
|
106
|
+
for r in RECORDS:
|
|
107
|
+
if type and r.get("type") != type:
|
|
108
|
+
continue
|
|
109
|
+
s = search_score(r, terms)
|
|
110
|
+
if s > 0:
|
|
111
|
+
hits.append((s, r))
|
|
112
|
+
hits.sort(key=lambda x: -x[0])
|
|
113
|
+
if not hits:
|
|
114
|
+
allkw = sorted({kw for r in RECORDS for kw in r.get("keywords", [])})
|
|
115
|
+
sug = difflib.get_close_matches(query.strip(), allkw, n=3, cutoff=0.5)
|
|
116
|
+
if sug:
|
|
117
|
+
return "没找到,要不要试试:" + " / ".join(sug)
|
|
118
|
+
return "没找到,换个关键词试试(比如:变量、循环、贪吃蛇、二级)"
|
|
119
|
+
return "\n\n---\n\n".join(fmt_record(r) for _, r in hits[:max(1, limit)])
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@mcp.tool()
|
|
123
|
+
def get_record(id: str) -> str:
|
|
124
|
+
"""按编号取一条记录的全文。编号如 exam-L1-overview、prac-L1-ui、vl-brick-11。"""
|
|
125
|
+
r = BY_ID.get(id)
|
|
126
|
+
if not r:
|
|
127
|
+
return "未找到编号:%s。请先用 search_knowledge 搜索,拿到正确编号再取。" % id
|
|
128
|
+
return fmt_record(r)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@mcp.tool()
|
|
132
|
+
def list_exam_points(level: int) -> str:
|
|
133
|
+
"""列出某考级(1-4 级)全部考级考点,每条含完整讲解与提醒。"""
|
|
134
|
+
if level not in (1, 2, 3, 4):
|
|
135
|
+
return "级别只能是 1、2、3、4,请重新指定。"
|
|
136
|
+
pts = [r for r in RECORDS if r.get("type") == "exam_point" and r.get("level") == level]
|
|
137
|
+
if not pts:
|
|
138
|
+
return "%d 级暂无考级考点。" % level
|
|
139
|
+
head = "【%d 级考级考点】共 %d 条" % (level, len(pts))
|
|
140
|
+
return head + "\n\n" + "\n\n---\n\n".join(fmt_record(r) for r in pts)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@mcp.tool()
|
|
144
|
+
def get_practice(level: int, n: int = 8) -> str:
|
|
145
|
+
"""取某考级(1-4 级)的原创模拟题。注意:以下均为原创模拟题,非真题,
|
|
146
|
+
仅用于练习,题型与分值框架参考公开考级大纲。每题含题目、选项、答案与解析。"""
|
|
147
|
+
if level not in (1, 2, 3, 4):
|
|
148
|
+
return "级别只能是 1、2、3、4,请重新指定。"
|
|
149
|
+
pool = [r for r in RECORDS if r.get("type") == "practice" and r.get("level") == level]
|
|
150
|
+
if not pool:
|
|
151
|
+
return "%d 级暂无模拟题。" % level
|
|
152
|
+
pool = pool[:max(1, n)]
|
|
153
|
+
blocks = []
|
|
154
|
+
for i, r in enumerate(pool, 1):
|
|
155
|
+
ex = r.get("extra") or {}
|
|
156
|
+
b = ["第 %d 题【%s】%s" % (i, ex.get("qtype", ""), r["title"])]
|
|
157
|
+
b.append(ex.get("question", r.get("body", "")))
|
|
158
|
+
for j, c in enumerate(ex.get("choices") or []):
|
|
159
|
+
b.append(" %s. %s" % (chr(65 + j), c))
|
|
160
|
+
b.append("答案:%s" % ex.get("answer", "(见解析)"))
|
|
161
|
+
b.append("解析:" + ex.get("explanation", ""))
|
|
162
|
+
blocks.append("\n".join(b))
|
|
163
|
+
head = "【%d 级原创模拟题】共 %d 道。注意:以下均为原创模拟题,非真题。" % (level, len(pool))
|
|
164
|
+
return head + "\n\n" + "\n\n---\n\n".join(blocks)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@mcp.tool()
|
|
168
|
+
def study_plan(age: int, goal: str = "兴趣", level: int | None = None) -> str:
|
|
169
|
+
"""生成学习规划。age 为孩子年龄;goal 三选一:兴趣 / 考级 / 转python;
|
|
170
|
+
level 为已通过的考级(0-4),仅考级路线用。逻辑与原 plan.py 一致。"""
|
|
171
|
+
if goal not in ("兴趣", "考级", "转python"):
|
|
172
|
+
return "目标只能是:兴趣、考级、转python,请重新指定。"
|
|
173
|
+
if goal == "兴趣":
|
|
174
|
+
name, why = "兴趣路线:校本 20 课", "零基础图个好玩:从点绿旗小猫动到贪吃蛇,每课都有作品。"
|
|
175
|
+
lessons = ["lesson-xb%02d" % i for i in range(1, 21)]
|
|
176
|
+
elif goal == "考级":
|
|
177
|
+
name, why = "考级路线:项目制 20 讲 + 考点串讲", "目标明确:初级 8 讲打基础,中级 12 讲冲刺,每讲对应考级考点。"
|
|
178
|
+
lessons = ["lesson-cj%02d" % i for i in range(9, 17)] + \
|
|
179
|
+
["lesson-zj%02d" % i for i in (1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15)]
|
|
180
|
+
else:
|
|
181
|
+
name, why = "进阶路线:Python 项目实战 12 讲", "Scratch 已会变量/广播/克隆:12 个真实小系统,从宠物管理到词云图。"
|
|
182
|
+
lessons = ["proj-py%d" % i for i in (13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24)]
|
|
183
|
+
lines = ["【学习规划】%d 岁 · 目标:%s" % (age, goal)]
|
|
184
|
+
if goal == "考级" and level:
|
|
185
|
+
lines[0] += " · 已过 %d 级" % level
|
|
186
|
+
lines.append("推荐:" + name)
|
|
187
|
+
lines.append("为什么:" + why)
|
|
188
|
+
if age < 8 and goal != "兴趣":
|
|
189
|
+
lines.append("提醒:8 岁以下建议先走兴趣路线,打好手感再定目标。")
|
|
190
|
+
if goal == "转python" and age < 10:
|
|
191
|
+
lines.append("提醒:10 岁以下转 Python 建议家长陪跑,打字和报错需要耐心。")
|
|
192
|
+
lines.append("")
|
|
193
|
+
lines.append("课程清单(前 8 讲):")
|
|
194
|
+
for lid in lessons[:8]:
|
|
195
|
+
r = BY_ID.get(lid)
|
|
196
|
+
if r:
|
|
197
|
+
lines.append(" - " + r["title"])
|
|
198
|
+
lines.append(" ……共 %d 讲" % len(lessons))
|
|
199
|
+
lines.append("")
|
|
200
|
+
lines.append("4 周节奏(每周 3 次,每次 40 分钟):")
|
|
201
|
+
for w in ["第 1 周:新课 2 次 + 练习 1 次,目标:独立复述本课 3 个知识点。",
|
|
202
|
+
"第 2 周:新课 2 次 + 项目 1 次,目标:不看教程做出上周的作品。",
|
|
203
|
+
"第 3 周:新课 1 次 + 复习 1 次 + 挑战 1 次,目标:讲给家长听。",
|
|
204
|
+
"第 4 周:综合项目 1 次 + 展示 1 次,目标:录屏 1 分钟作品演示。"]:
|
|
205
|
+
lines.append(" " + w)
|
|
206
|
+
if goal == "考级":
|
|
207
|
+
lv = (level or 0) + 1
|
|
208
|
+
lines.append("")
|
|
209
|
+
lines.append("备考:目标 %d 级,先拉考点清单,再练原创模拟题(非真题):" % lv)
|
|
210
|
+
lines.append(" 工具 list_exam_points(level=%d),get_practice(level=%d)" % (lv, lv))
|
|
211
|
+
return "\n".join(lines)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
@mcp.tool()
|
|
215
|
+
def list_video_courses() -> str:
|
|
216
|
+
"""列出全部视频课(课程名与节数),与原 videos.py --list 一致。"""
|
|
217
|
+
courses = {}
|
|
218
|
+
for r in RECORDS:
|
|
219
|
+
if r.get("type") == "video_lesson":
|
|
220
|
+
courses.setdefault((r.get("extra") or {}).get("course", "未分类"), []).append(r)
|
|
221
|
+
if not courses:
|
|
222
|
+
return "暂无视频课。"
|
|
223
|
+
lines = ["【视频课】共 %d 门:" % len(courses)]
|
|
224
|
+
for c, rs in courses.items():
|
|
225
|
+
lines.append("%s(%d 节)" % (c, len(rs)))
|
|
226
|
+
lines.append("")
|
|
227
|
+
lines.append("查某课程章节:用 get_video_lesson(course=课程关键词,如“打砖块”)。")
|
|
228
|
+
return "\n".join(lines)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
@mcp.tool()
|
|
232
|
+
def get_video_lesson(course: str | None = None, id: str | None = None) -> str:
|
|
233
|
+
"""取某节视频配套讲解。两种用法:按编号取全文(id,如 vl-brick-11),
|
|
234
|
+
或按课程关键词列章节(course,如“打砖块”/“Boss”/“2.0”)。"""
|
|
235
|
+
if id:
|
|
236
|
+
r = BY_ID.get(id)
|
|
237
|
+
if not r or r.get("type") != "video_lesson":
|
|
238
|
+
return "未找到视频课编号:%s。请先用 list_video_courses 看课程,或用 get_video_lesson(course=课程关键词)列章节。" % id
|
|
239
|
+
ex = r.get("extra") or {}
|
|
240
|
+
out = ["【%s】" % r["title"]]
|
|
241
|
+
out.append("课程:%s | 章节:%s" % (ex.get("course"), ex.get("chapter")))
|
|
242
|
+
out.append("")
|
|
243
|
+
out.append(r.get("body", ""))
|
|
244
|
+
if r.get("tip"):
|
|
245
|
+
out.append("")
|
|
246
|
+
out.append("提醒:" + r["tip"])
|
|
247
|
+
return "\n".join(out)
|
|
248
|
+
kw = course or ""
|
|
249
|
+
if not kw:
|
|
250
|
+
return "请指定 course(课程关键词,如:打砖块 / Boss / 2.0)或 id(视频课编号)。"
|
|
251
|
+
hits = [r for r in RECORDS
|
|
252
|
+
if r.get("type") == "video_lesson"
|
|
253
|
+
and (kw in (r.get("extra") or {}).get("course", "") or kw in r.get("title", ""))]
|
|
254
|
+
if not hits:
|
|
255
|
+
return "没找到课程,试试:course=打砖块 / Boss / 2.0,或用 list_video_courses 看全部。"
|
|
256
|
+
cname = (hits[0].get("extra") or {}).get("course")
|
|
257
|
+
lines = ["《%s》共 %d 节:" % (cname, len(hits))]
|
|
258
|
+
for r in hits:
|
|
259
|
+
ex = r.get("extra") or {}
|
|
260
|
+
lines.append(" [%s] %s(%s)" % (ex.get("chapter"), r["title"], r["id"]))
|
|
261
|
+
return "\n".join(lines)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def main(argv=None):
|
|
265
|
+
ap = argparse.ArgumentParser()
|
|
266
|
+
ap.add_argument("--transport", choices=["stdio", "streamable-http"],
|
|
267
|
+
default="stdio",
|
|
268
|
+
help="stdio(默认,供 uvx / 百炼部署)或 streamable-http")
|
|
269
|
+
ap.add_argument("--host", default="127.0.0.1")
|
|
270
|
+
ap.add_argument("--port", type=int, default=8000)
|
|
271
|
+
args = ap.parse_args(argv)
|
|
272
|
+
if args.transport == "stdio":
|
|
273
|
+
mcp.run(transport="stdio")
|
|
274
|
+
else:
|
|
275
|
+
mcp.settings.host = args.host
|
|
276
|
+
mcp.settings.port = args.port
|
|
277
|
+
mcp.run(transport="streamable-http")
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
if __name__ == "__main__":
|
|
281
|
+
main()
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kids-coding-tutor-mcp
|
|
3
|
+
Version: 1.3.0
|
|
4
|
+
Summary: 给 5-16 岁孩子、家长和老师的编程学习助手:Scratch 概念白话讲解、1-4 级考级考点与备考、Python 项目实战指导、课程备课。191 条蒸馏知识,纯中文作答。
|
|
5
|
+
Author: yiten hua
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/yiten5885-a11y/kids-coding-tutor
|
|
8
|
+
Project-URL: Source, https://github.com/yiten5885-a11y/kids-coding-tutor
|
|
9
|
+
Keywords: mcp,kids-coding,scratch,python,education
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Education
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Education
|
|
17
|
+
Classifier: Natural Language :: Chinese (Simplified)
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE.txt
|
|
21
|
+
Requires-Dist: mcp<2,>=1.0
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# 少儿编程通 MCP 服务器
|
|
25
|
+
|
|
26
|
+
把「少儿编程通」skill(191 条记录)变成可远程调用的 MCP 服务器,供 Qoder 这类远端 agent 靠 URL 调用。只读原 skill 的数据文件,不改动原 skill 包。
|
|
27
|
+
|
|
28
|
+
- 数据来源:`/home/hatch/workspace/skills/kids-coding-tutor/assets/records.jsonl`(可用环境变量 `KIDS_CODING_RECORDS` 换路径)
|
|
29
|
+
- 检索/规划/刷题逻辑与原 skill 的 `search.py`、`answer.py`、`plan.py`、`quiz.py`、`videos.py` 一致
|
|
30
|
+
- 技术栈:官方 mcp SDK(v1,FastMCP)+ streamable-http transport,Python 3.12,依赖装在项目自带 `.venv`,不动系统全局
|
|
31
|
+
|
|
32
|
+
## 本地启动
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
cd ~/workspace/mcp-servers/kids-coding-tutor
|
|
36
|
+
.venv/bin/python server.py --host 127.0.0.1 --port 8000
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
启动后 MCP 端点:`http://127.0.0.1:8000/mcp`
|
|
40
|
+
|
|
41
|
+
自测(需另开一个终端,先启动服务器再跑):
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
cd ~/workspace/mcp-servers/kids-coding-tutor
|
|
45
|
+
.venv/bin/python test_client.py http://127.0.0.1:8000/mcp
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Qoder 配置示例(mcp.json,URL 形式)
|
|
49
|
+
|
|
50
|
+
端口先写占位,部署到服务器后把 `<服务器地址>` 和 `<端口>` 换成实际值:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"mcpServers": {
|
|
55
|
+
"kids-coding-tutor": {
|
|
56
|
+
"url": "http://<服务器地址>:<端口>/mcp",
|
|
57
|
+
"transport": "streamable-http"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
在 Qoder 里添加远程 MCP 服务器时粘贴上面这个 URL 即可(若 Qoder 界面字段名不同,按其提示填 URL 与 streamable-http 类型)。
|
|
64
|
+
|
|
65
|
+
注意:本服务器的 streamable-http 端点本身不带鉴权,不要直接裸暴露到公网;远端部署时请放在反向代理后加鉴权,或只放在内网/可信网络使用。
|
|
66
|
+
|
|
67
|
+
## 工具(7 个,全部返回纯中文正文)
|
|
68
|
+
|
|
69
|
+
| 工具 | 说明 |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| `search_knowledge(query, type?, limit?=5)` | 按关键词搜记录,计分规则与原 search.py 一致(标题命中 x3、关键词精确命中 +5);type 可限定 concept、exam_point、lesson、project、faq、practice、tutorial、parent_guide、video_lesson |
|
|
72
|
+
| `get_record(id)` | 按编号取一条全文(含 title/body/example/tip) |
|
|
73
|
+
| `list_exam_points(level)` | 列某考级(1-4)全部考级考点(实测:1 级 7 条、2 级 8 条、3 级 7 条、4 级 5 条) |
|
|
74
|
+
| `get_practice(level, n?=8)` | 取某级别原创模拟题(各级别 8 道),每题含题目、选项、答案与解析,并注明“原创模拟题,非真题” |
|
|
75
|
+
| `study_plan(age, goal?, level?)` | 学习规划,goal 三选一:兴趣 / 考级 / 转python,含路线、课程清单与 4 周节奏 |
|
|
76
|
+
| `list_video_courses()` | 列全部视频课(实测 3 门:打砖块 13 节、Boss 战射击游戏 7 节、scratch2.0 零散 4 节) |
|
|
77
|
+
| `get_video_lesson(course?, id?)` | 按课程关键词列章节,或按编号取某节配套讲解全文 |
|
|
78
|
+
|
|
79
|
+
## 实测结果(2026-10-26,MCP SDK 客户端实际调通)
|
|
80
|
+
|
|
81
|
+
| 调用 | 结果 |
|
|
82
|
+
| --- | --- |
|
|
83
|
+
| search_knowledge(搜“变量”) | 成功,1016 字,首条【三级考点:变量】 |
|
|
84
|
+
| search_knowledge(搜“变量”,type=concept) | 成功,1222 字,首条【Scratch:变量是什么】 |
|
|
85
|
+
| get_record(exam-L1-overview) | 成功,231 字 |
|
|
86
|
+
| list_exam_points(level=1) | 成功,1459 字,1 级共 7 条 |
|
|
87
|
+
| get_practice(level=1) | 成功,1073 字,8 道,含答案、解析与“非真题”注明 |
|
|
88
|
+
| study_plan(age=9,goal=考级,level=0) | 成功,503 字,含 4 周节奏与备考指引 |
|
|
89
|
+
| list_video_courses | 成功,3 门课 |
|
|
90
|
+
| get_video_lesson(course=打砖块) | 成功,13 节章节列表 |
|
|
91
|
+
| get_video_lesson(id=vl-brick-11) | 成功,161 字配套讲解全文 |
|
|
92
|
+
|
|
93
|
+
7 个工具全部调通,无失败项。
|
|
94
|
+
|
|
95
|
+
## 目录
|
|
96
|
+
|
|
97
|
+
- `server.py`:服务器本体(FastMCP + streamable-http)
|
|
98
|
+
- `test_client.py`:SDK 客户端实测脚本
|
|
99
|
+
- `.venv/`:项目自带虚拟环境(mcp 1.30.0,FastMCP)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
LICENSE.txt
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
pyproject.toml
|
|
5
|
+
src/kids_coding_tutor_mcp/__init__.py
|
|
6
|
+
src/kids_coding_tutor_mcp/__main__.py
|
|
7
|
+
src/kids_coding_tutor_mcp/server.py
|
|
8
|
+
src/kids_coding_tutor_mcp.egg-info/PKG-INFO
|
|
9
|
+
src/kids_coding_tutor_mcp.egg-info/SOURCES.txt
|
|
10
|
+
src/kids_coding_tutor_mcp.egg-info/dependency_links.txt
|
|
11
|
+
src/kids_coding_tutor_mcp.egg-info/entry_points.txt
|
|
12
|
+
src/kids_coding_tutor_mcp.egg-info/requires.txt
|
|
13
|
+
src/kids_coding_tutor_mcp.egg-info/top_level.txt
|
|
14
|
+
src/kids_coding_tutor_mcp/data/records.jsonl
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
mcp<2,>=1.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
kids_coding_tutor_mcp
|