Files
English/Doc/COURSE-PACK-JSON.md
T
shenleiandClaude Opus 4.8 febfd30f49 feat: A1 短材料复现本单元理解词,并加入黑夜模式
课程内容
- 重写全部 21 个 A1 单元的听读材料,把本单元理解词织进听/读文本,
  单元内理解词复现率从约 20% 提升到约 77%(各单元 56–96%)。
- 修正 U01 房间号与机场大巴同为 thirty 的撞车(改为 17/30/40)。
- 校验器容差按级别读取(A1 为 8%),materials 覆盖率、字数、
  选项子串等校验全部通过;course_content_test 通过。

黑夜模式
- app_theme 拆分明/暗两套调色板,AppColors 随亮度切换;
  main 用 theme/darkTheme/themeMode + builder 镜像已解析亮度;
  主题偏好持久化到快照;进度页新增“外观主题”切换。

文档
- COURSE-PACK-JSON.md 更新 A1 词池覆盖(840/933)与材料复现约定。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-18 11:16:09 +09:00

225 lines
9.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 课程内容包 JSON 格式
> 对应《学习引擎规格》第 5 节。本文说明 A1–B1 单元内容包的文件位置、字段和程序校验规则。A0 仍按《A0 初始核心课程包》,不在此格式内。
## 1. 文件与工具
| 路径 | 内容 |
|---|---|
| `kouyu_english/assets/courses/course-map.json` | 主题地图:等级、单元顺序、每级的数量范围、名称白名单 |
| `kouyu_english/assets/courses/<等级>/<单元ID>.json` | 单元内容包,一个单元一个文件 |
| `kouyu_english/tool/courses/lexicon/` | 锁定的 CEFR-J 数据与 `SOURCES.md` |
| `kouyu_english/tool/courses/pools/` | 等级词池(`build_word_pools.py` 生成) |
| `kouyu_english/tool/courses/validate.dart` | 校验全部内容包,报告词池覆盖率 |
| `kouyu_english/test/course_content_test.dart` | 在 `flutter test` 中要求校验零错误 |
```sh
cd kouyu_english
dart run tool/courses/validate.dart # 错误和覆盖率
dart run tool/courses/validate.dart --uncovered B1 # 按主题列出未覆盖的词池词
dart run tool/courses/validate.dart --levels B1-U14 # 打印条目的计算等级
```
内容包目前只是资源文件,尚未加入 `pubspec.yaml`,运行时也还不读取。
## 2. 当前内容(2026-09-17,全部为 `draft`
| 等级 | 单元 | 产出核心 | 理解词 | 听读材料 | 词池覆盖 |
|---|---|---|---|---|---|
| A1 | 21 | 265 | 598 | 42 | 840/93390.0% |
| A2 | 15 | 217 | 862 | 44 | 1040/107896.5% |
| B1 | 14 | 206 | 1121 | 42 | 1345/155286.7% |
全部由 AI 起草、程序校验通过,尚未经过独立审核和用户确认,不能冻结。
## 3. course-map.json
```jsonc
{
"schemaVersion": 1,
"goal": "日常生活与旅行",
"sources": ["CEFR-J Vocabulary Profile 1.5(…)", "CEFR-J Grammar Profile 20180315(…)"],
"minimumPoolCoverage": 0.85, // 等级词池覆盖下限
"alwaysAllowedWords": ["oh", "ok", ], // 任何文本都可用的语气词
"names": ["tom", "london", "mr", ], // 全局专名、缩写
"levels": [{
"id": "B1",
"title": "独立应对大多数情况",
"coreRange": [10, 15],
"receptiveRange": [70, 110],
"materials": {"count": [2, 3], "listeningWords": [250, 550], "readingWords": [300, 500]},
// A1 的材料较短,且可选 "unknownTolerance"(默认 0.02)放宽超纲词占比
"units": [{"id": "B1-U01", "file": "B1/B1-U01.json", "title": "…", "categories": ["旅行"], "canDo": "…"}]
}]
}
```
各等级范围:
| 等级 | 产出核心 | 理解词 | 听读材料 | 听力词数 | 阅读词数 |
|---|---|---|---|---|---|
| A1 | 1015 | 2035 | 2 篇 | 60120 | 80150 |
| A2 | 1015 | 4065 | 23 篇 | 120280 | 150250 |
| B1 | 1015 | 70110 | 23 篇 | 250550 | 300500 |
A1 材料短、面向零基础,`unknownTolerance` 设为 0.08(听读材料默认 0.02):允许更多“课程尚未正式教、但零基础者需要”的常用词,前提是每个都写进 `glosses` 给出中文,问题选项用中文以免受“选项须在原文出现”的限制。
`units` 的顺序就是推荐学习顺序,校验“已教的词”时按此顺序累积。
## 4. 单元文件
### 4.1 ID 规则
所有 ID 都以单元 ID 开头,序号从 1 连续编号:
| 前缀 | 对象 | 例子 |
|---|---|---|
| `W` | 产出核心:单词 | `B1-U14-W01` |
| `P` | 产出核心:词块或句型 | `B1-U14-P01` |
| `R` | 理解词 | `B1-U14-R01` |
| `S` | 教学段 | `B1-U14-S1` |
| `C` | 场景 | `B1-U14-C1` |
| `T` | 任务 | `B1-U14-T1` |
| `M` | 听读材料 | `B1-U14-M1` |
### 4.2 顶层字段
```jsonc
{
"schemaVersion": 1,
"id": "B1-U14",
"revision": 1,
"level": "B1",
"title": "写说明与投诉", // title/categories/canDo 必须与 course-map 一致
"categories": ["日常", "旅行"],
"canDo": "写邮件说明经过并提出要求",
"status": "draft", // draft / reviewed / frozen
"meta": {
"draftedBy": "claude-opus-5",
"draftedAt": "2026-09-17",
"sources": ["CEFR-J Vocabulary Profile 1.5", "CEFR-J Grammar Profile 20180315"],
"reviews": [], // 审核记录
"frozenAt": null
},
"names": ["seaview", "palace"], // 本单元专名,小写
"grammar": [{"id": "142", "use": "active"}, {"id": "215", "use": "receptive", "reason": "…"}],
"coreItems": [], "receptiveWords": [], "segments": [], "scenes": [], "tasks": [],
"materials": [], // A1 起(A1 为短材料)
"rubric": {"dimensions": []}, // A2 起(A1 无开放任务评分标准)
"excluded": [{"en": "…", "reason": "…"}]
}
```
### 4.3 coreItems(产出核心)
```json
{
"id": "B1-U14-P08",
"type": "phrase",
"en": "I would like to request a full refund.",
"zh": "我想申请全额退款。",
"sceneWord": false,
"match": [["request a full refund", "request a refund"]],
"example": {"en": "I would like to request a refund for the second night.", "zh": "我想申请退还第二晚的费用。"},
"review": {"prompt": "在邮件里提出退款要求。"}
}
```
- `type``word` / `phrase` / `pattern`
- `match`:判定用户是否用到本项。外层数组之间为“且”,内层为“或”;每个词条按连续短语匹配(缩写展开、忽略大小写和标点)。`en``example.en` 都必须满足。
- `sceneWord`:高于本等级或 CEFR-J 未收录、但场景必需的词句设为 `true`,并写 `sceneReason`;每单元不超过产出核心的 1/3。
- `review.prompt`:复习时的中文任务,不能直接包含英文答案。
### 4.4 receptiveWords(理解词)
```json
{"id": "B1-U14-R01", "en": "furthermore", "zh": "此外"}
```
- 不高于本等级加一级(B1 单元最高 B2)。
- 不能是 A0 或之前单元已教的词(按词形还原比较)。
- 必须出现在本单元的教学段、场景或听读材料文本中。
### 4.5 segments(教学段)
每单元 2–4 段,每段 8–15 分钟、最多 6 个新产出核心:
```jsonc
{
"id": "B1-U14-S1", "title": "说明写信目的", "minutes": 12,
"itemIds": ["B1-U14-P01", "B1-U14-P02"],
"listening": {"text": "…", "question": "…", "options": ["正确", "干扰", "干扰"]},
"speaking": [{"text": "…", "tip": "可选的发音提示"}],
"reading": {"text": "多行用 \n", "question": "…", "options": ["…", "…", "…"]},
"writing": {"prompt": "…", "example": "…", "itemIds": ["B1-U14-P01"]},
"independent": {"prompt": "…", "itemIds": ["B1-U14-P02"]}
}
```
- 选项恰好 3 个,第一个是正确答案(界面随机排序);选项不能互为子串。英文选项至少两个在原文出现,且正确选项必须出现。
- `writing.example` 必须用到它引用的每个产出核心。
### 4.6 scenes 与 tasks
```jsonc
{
"id": "B1-U14-C1", "kind": "main", // 恰好 1 个 main,另有 23 个 variant
"title": "…", "learnerRole": "客人", "aiRole": "酒店客服经理", "setting": "…",
"turns": [ // 47 轮
{"ai": "AI 说的话", "goal": "本轮目标", "model": "学习者示范回答", "zh": "示范的中文", "itemIds": ["B1-U14-P01"]}
]
}
```
```json
{"id": "B1-U14-T1", "sceneId": "B1-U14-C1", "goal": "…", "itemIds": ["B1-U14-P01"], "slots": ["目的", "经过", "结果", "要求"]}
```
- `model` 必须用到本轮 `itemIds` 引用的产出核心。
- 每个场景至少有一个任务,每个任务至少 1 个信息槽。
- 每个产出核心都要出现在教学段、场景示范和任务中。
### 4.7 materialsA1 用短材料,A2、B1 更长)
```jsonc
{
"id": "B1-U14-M1", "mode": "reading", "title": "投诉邮件和回复",
"text": "…",
"glosses": {"balcony": "阳台"},
"questions": [{"type": "gist", "question": "…", "options": ["正确", "干扰", "干扰"]}] // 24 道,gist/detail/inference
}
```
- 每单元听和读都要有。
- 超纲词(见第 5 节)按词次不超过该级 `materials.unknownTolerance`(默认 2%,A1 为 8%),每个超纲词都要写进 `glosses`
- A1 材料应尽量把本单元的理解词织进听读文本,让理解词在教学段之外再复现一次(本级平均单元内复现率约 77%);本单元的产出核心与理解词都算已教词,写进材料不计超纲。
### 4.8 rubricA2 起)
```json
{"dimensions": [{"name": "说明经过", "bands": ["经过说不清", "能按顺序说明经过,但缺少结果", "按顺序说明经过、结果,并附上证据"], "pass": 1}]}
```
至少 2 个维度,每维至少 3 档;`pass` 是及格档在 `bands` 中的下标(从 0 起),须大于 0。
## 5. 文本用词检查
“已知词”包括:`alwaysAllowedWords`、功能词、A0 已教的词、推荐顺序在前的单元的产出核心和理解词、本单元的产出核心和理解词(均按词形还原),以及全局与本单元的 `names`。数字不计。
| 文本 | 允许的未知词 |
|---|---|
| 教学段的听、说、读、写示范,场景的 AI 句和示范句 | 0 |
| 听读材料 | ≤ `materials.unknownTolerance`(默认 2%,A1 为 8%),且全部有 `glosses` |
注意:等级低于本单元、但还没有被任何单元教过的词(如 `history``culture`)同样算未知词。要在文本中使用,须改写,或把它加为本单元的理解词。
## 6. 等级与覆盖
- 语法:`active` 语法项不高于本等级,否则写 `reason`
- 产出核心中的词不高于本等级,除非是场景词。
- 覆盖率:等级词池(`tool/courses/pools/`)中,被产出核心、`match` 词块或理解词覆盖的词(含 A0 已教)占比须 ≥ `minimumPoolCoverage`
## 7. 状态流转
`draft`AI 起草、校验通过)→ `reviewed`(独立审核后,在 `meta.reviews` 记录审核模型、日期和问题清单)→ `frozen`(用户确认,填 `frozenAt`)。已冻结单元的 ID 不复用;修改须提升 `revision` 并遵循《学习引擎规格》5.5 的 stageVersion 规则。