diff --git a/Doc/COURSE-PACK-JSON.md b/Doc/COURSE-PACK-JSON.md
index 7a766e2..ad5f0f0 100644
--- a/Doc/COURSE-PACK-JSON.md
+++ b/Doc/COURSE-PACK-JSON.md
@@ -1,6 +1,6 @@
# 课程内容包 JSON 格式
-> 对应《学习引擎规格》第 5 节。本文说明 A1–B1 单元内容包的文件位置、字段和程序校验规则。A0 仍按《A0 初始核心课程包》,不在此格式内。
+> 对应《学习引擎规格》第 5 节。本文说明 A0–B1 单元内容包的文件位置、字段和程序校验规则。所有等级共用这一套结构,由 `CourseRepository` 用同一套规则转换成学习和复习数据;A0 的内容标准仍以《A0 初始核心课程包》为准(见 4.10)。
## 1. 文件与工具
@@ -8,6 +8,7 @@
|---|---|
| `kouyu_english/assets/courses/course-map.json` | 主题地图:等级、单元顺序、每级的数量范围、名称白名单 |
| `kouyu_english/assets/courses/<等级>/<单元ID>.json` | 单元内容包,一个单元一个文件 |
+| `kouyu_english/assets/courses/A0/assessment.json` | A0 阶段测评题(见 4.11) |
| `kouyu_english/tool/courses/lexicon/` | 锁定的 CEFR-J 数据与 `SOURCES.md` |
| `kouyu_english/tool/courses/pools/` | 等级词池(`build_word_pools.py` 生成) |
| `kouyu_english/tool/courses/validate.dart` | 校验全部内容包,报告词池覆盖率 |
@@ -20,17 +21,18 @@ dart run tool/courses/validate.dart --uncovered B1 # 按主题列出未覆盖
dart run tool/courses/validate.dart --levels B1-U14 # 打印条目的计算等级
```
-内容包目前只是资源文件,尚未加入 `pubspec.yaml`,运行时也还不读取。
+运行时由 `lib/core/courses/course_repository.dart` 读取:`main` 启动前先加载 A0,`AppState.load` 再加载其余等级。学习(教学段、对话、写作、独立表达)和复习(产出核心的 `match` 与 `review`)都只读这些 JSON,代码里不再写死课程数据。`test/course_self_consistency_test.dart` 要求每个示范答案都能通过它自己的判定规则。
-## 2. 当前内容(2026-09-17,全部为 `draft`)
+## 2. 当前内容(2026-09-18,A1–B1 为 `draft`)
| 等级 | 单元 | 产出核心 | 理解词 | 听读材料 | 词池覆盖 |
|---|---|---|---|---|---|
+| A0 | 10 | 见 A0 标准 | — | — | —(`approved`,不参与词池校验) |
| A1 | 21 | 265 | 598 | 42 | 840/933(90.0%) |
| A2 | 15 | 217 | 862 | 44 | 1040/1078(96.5%) |
| B1 | 14 | 206 | 1121 | 42 | 1345/1552(86.7%) |
-全部由 AI 起草、程序校验通过,尚未经过独立审核和用户确认,不能冻结。
+A1–B1 全部由 AI 起草、程序校验通过,尚未经过独立审核和用户确认,不能冻结。
## 3. course-map.json
@@ -48,6 +50,7 @@ dart run tool/courses/validate.dart --levels B1-U14 # 打印条目的计算等
"coreRange": [10, 15],
"receptiveRange": [70, 110],
"materials": {"count": [2, 3], "listeningWords": [250, 550], "readingWords": [300, 500]},
+ "assessment": "A0/assessment.json", // 可选:本级阶段测评题,目前只有 A0 有
// A1 的材料较短,且可选 "unknownTolerance"(默认 0.02)放宽超纲词占比
"units": [{"id": "B1-U01", "file": "B1/B1-U01.json", "title": "…", "categories": ["旅行"], "canDo": "…"}]
}]
@@ -202,6 +205,58 @@ A1 材料短、面向零基础,`unknownTolerance` 设为 0.08(听读材料
至少 2 个维度,每维至少 3 档;`pass` 是及格档在 `bands` 中的下标(从 0 起),须大于 0。
+### 4.9 可选字段:显式规则优先
+
+以下字段都可省略;省略时由 `itemIds` 引用的产出核心推导(`match` 合并成一组“或”,示范取 `en`),写了就以写的为准。规则写法见 `lib/core/courses/answer_rules.dart`:外层“且”、内层“或”,内层首项可写 `#min:N` 表示至少用上 N 项;词条可写 `a + b`、`/正则/`,以及 `#spelling`、`#digit`、`#word`、`#question`、`#numbers:N`、`#words:N` 这些结构。
+
+| 位置 | 字段 | 作用 | 省略时 |
+|---|---|---|---|
+| 单元 | `status` | `approved` / `draft` 等 | `draft` |
+| 单元 | `vocabulary` | 整课词表(离线查词) | 产出核心 + 理解词 |
+| 教学段 | `grammarNote` | 本段语法提示 | 不显示 |
+| 教学段 | `vocabulary` | 预习词表 `[{id, word, meaning, example, exampleMeaning}]` | 本段产出核心 |
+| 教学段 | `sceneId` | 本段对话用的场景 | 第 N 段用第 N 个场景,没有则用 main 场景 |
+| 教学段 | `writing.accept` / `writing.hint` | 写作判定规则 / 未通过时的提示 | 由 `writing.itemIds` 推导 |
+| 教学段 | `independent.accept` / `independent.help` | 独立表达的完整规则 / 帮助 | 由 `independent.itemIds` 推导;为空则任何真实尝试都算 |
+| 场景 | `goal` | 对话目标 | `setting`,再没有则 `title` |
+| 场景 | `practice` | 同时作为自由练习场景:`{title, summary, recapPrompt, alwaysOpen}`,都可省略;写 `{}` 即可 | 不进入自由练习 |
+| 轮次 | `aiZh` | `ai` 这句的中文(`zh` 是示范回答的中文) | 不提供翻译 |
+| 轮次 | `accept` | 本轮判定用的一组“或” | 由本轮 `itemIds` 推导 |
+
+自由练习场景在其所属单元完成后解锁,`alwaysOpen: true` 则一开始就开放。
+
+### 4.10 A0 单元
+
+A0 用同一结构,文件为 `A0/a0-01.json` … `a0-10.json`,在 `course-map.json` 中排在最前。为兼容已保存的学习进度,A0 保留原有 ID:单元 `a0-01`,教学段 `a0-01-a`(拆段课为 `a0-04-a/b/c`、`a0-08-a/b/c`),产出核心 `A0-W01` / `A0-P01`。A0 的规则、提示和对话都用上面的显式字段写明;`validate.dart` 只把 A0 的产出核心和听写句当作“已教的词”,不按 A1–B1 的数量范围和词池校验 A0。
+
+### 4.11 阶段测评(assessment.json)
+
+等级在 `course-map.json` 里写了 `assessment`,就由 `CourseRepository` 加载对应文件,运行时代码在 `lib/core/courses/assessment.dart`。目前只有 A0 的 `A0/assessment.json`:
+
+```jsonc
+{
+ "schemaVersion": 1,
+ "packs": [
+ {"id": "A0-E1", "level": "A0", "tasks": [
+ {"id": "A0-A-L1", "skill": "listening", "prompt": "选择正确答案",
+ "audio": "Hello. I’m Mia.", "choices": ["自我介绍", "买东西", "问时间"], "answerIndex": 0},
+ {"id": "A0-A-W1", "skill": "writing", "prompt": "用英语介绍自己。",
+ "accept": [["hello", "hi"], ["i am", "my name is"]],
+ "guide": "试着同时写一句问候和姓名,例如:Hello. I’m …"}
+ ]},
+ {"id": "A0-E1R", "level": "A0", "replaces": "A0-E1", "tasks": […]}
+ ]
+}
+```
+
+- `skill` 取 `listening` / `reading` / `writing` / `speaking`。听力、阅读是选择题,写 `choices` 和 `answerIndex`(从 0 开始);听力另写 `audio`。
+- 写作、口语是开放题,`accept` 用 4.9 的规则写法,`guide` 是答错后展示的要求。
+- 没有 `replaces` 的套题按文件顺序解锁:第一套直接开放,之后每套要在上一套通过 24 小时后才开放。
+- 有 `replaces` 的是替换题,用不同的人名、地点、数字考同样的能力,成绩记在原题 ID 上。
+- 题目和套题 ID 写进学习记录,发布后不要改。
+
+`test/course_self_consistency_test.dart` 检查:每道开放题都有 `accept` 和 `guide`,每道选择题的 `answerIndex` 有效,`replaces` 指向存在的套题。
+
## 5. 文本用词检查
“已知词”包括:`alwaysAllowedWords`、功能词、A0 已教的词、推荐顺序在前的单元的产出核心和理解词、本单元的产出核心和理解词(均按词形还原),以及全局与本单元的 `names`。数字不计。
diff --git a/Doc/learning-app-prototype.html b/Doc/learning-app-prototype.html
index 1a039d9..832d41b 100644
--- a/Doc/learning-app-prototype.html
+++ b/Doc/learning-app-prototype.html
@@ -24,6 +24,7 @@
#learning-app-prototype .screen-list { display:flex; flex-direction:column; gap:3px; margin-top:15px; }
#learning-app-prototype .screen-link { text-align:left; border:0; border-radius:10px; color:var(--ka-ink); background:transparent; padding:9px 10px; font-size:13px; }
#learning-app-prototype .screen-link[aria-pressed="true"] { background:var(--ka-soft-green); color:var(--ka-green); font-weight:500; }
+ #learning-app-prototype .screen-link.sub { padding-left:22px; font-size:12px; }
#learning-app-prototype .legend-title { font-size:13px; font-weight:500; margin:0 0 10px; }
#learning-app-prototype .flow { display:flex; flex-direction:column; gap:8px; }
#learning-app-prototype .flow-row { border-left:2px solid var(--ka-line); padding:3px 0 3px 9px; font-size:12px; line-height:1.35; }
@@ -115,6 +116,62 @@
#learning-app-prototype .mac-main h1 { font-size:23px; }
#learning-app-prototype .mac-grid { display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-top:16px; }
#learning-app-prototype .mac-grid .slot { margin:0; min-height:104px; }
+ #learning-app-prototype .me-card { background:var(--ka-surface); border:1px solid var(--ka-line); border-radius:18px; padding:14px; }
+ #learning-app-prototype .me-head { display:flex; align-items:center; gap:12px; }
+ #learning-app-prototype .avatar { width:48px; height:48px; border-radius:50%; display:grid; place-items:center; background:var(--ka-soft-green); color:var(--ka-green); font-size:19px; font-weight:500; }
+ #learning-app-prototype .me-name { font-size:17px; font-weight:500; }
+ #learning-app-prototype .level-badge { display:inline-block; margin-left:6px; padding:2px 7px; border-radius:999px; background:var(--ka-soft-green); color:var(--ka-green); font-size:11px; vertical-align:2px; }
+ #learning-app-prototype .stats { display:grid; grid-template-columns:repeat(4,1fr); margin-top:13px; padding-top:12px; border-top:1px solid var(--ka-line); }
+ #learning-app-prototype .stat { border:0; background:transparent; color:var(--ka-ink); text-align:center; padding:0; }
+ #learning-app-prototype .stat b { display:block; font-size:19px; font-weight:500; }
+ #learning-app-prototype .stat span { color:var(--ka-muted); font-size:11px; }
+ #learning-app-prototype .stat.due b { color:var(--ka-warm-ink); }
+ #learning-app-prototype .goal-card { width:100%; display:block; text-align:left; border:0; border-radius:16px; padding:13px 14px; margin-top:11px; background:var(--ka-soft-green); color:var(--ka-ink); }
+ #learning-app-prototype .goal-card .meter { margin:9px 0 8px; background:light-dark(#c9e4d3, #2a4a37); }
+ #learning-app-prototype .group-title { color:var(--ka-muted); font-size:12px; margin:17px 4px 6px; }
+ #learning-app-prototype .menu { background:var(--ka-surface); border:1px solid var(--ka-line); border-radius:15px; overflow:hidden; }
+ #learning-app-prototype .menu-row { width:100%; display:flex; align-items:center; gap:11px; border:0; border-bottom:1px solid var(--ka-line); background:transparent; color:var(--ka-ink); padding:11px 12px; text-align:left; font-size:14px; }
+ #learning-app-prototype .menu-row:last-child { border-bottom:0; }
+ #learning-app-prototype .menu-row .ico { width:28px; height:28px; flex:none; display:grid; place-items:center; border-radius:8px; background:var(--ka-surface-2); font-size:14px; }
+ #learning-app-prototype .menu-row .label { flex:1; }
+ #learning-app-prototype .menu-row .label small { display:block; color:var(--ka-muted); font-size:11px; margin-top:1px; }
+ #learning-app-prototype .menu-row .val { color:var(--ka-muted); font-size:12px; }
+ #learning-app-prototype .menu-row .chev { color:var(--ka-muted); font-size:16px; }
+ #learning-app-prototype .menu-row.danger { color:light-dark(#b3261e, #ffb4ab); justify-content:center; }
+ #learning-app-prototype .version { text-align:center; color:var(--ka-muted); font-size:11px; margin:12px 0 0; }
+ #learning-app-prototype .scroll { height:593px; overflow-y:auto; }
+ #learning-app-prototype .ability { padding:11px 0; border-bottom:1px solid var(--ka-line); }
+ #learning-app-prototype .ability:last-child { border-bottom:0; }
+ #learning-app-prototype .ability .meter { margin:7px 0 0; }
+ #learning-app-prototype details { margin-top:11px; padding:11px 12px; background:var(--ka-surface); border:1px solid var(--ka-line); border-radius:15px; font-size:13px; }
+ #learning-app-prototype details p { color:var(--ka-muted); font-size:12px; line-height:1.55; margin:8px 0 0; }
+ #learning-app-prototype .tag { display:inline-block; padding:2px 7px; border-radius:999px; font-size:11px; font-weight:400; background:var(--ka-surface-2); color:var(--ka-muted); }
+ #learning-app-prototype .tag.ok { background:var(--ka-soft-green); color:var(--ka-green); }
+ #learning-app-prototype .tag.help { background:var(--ka-warm); color:var(--ka-warm-ink); }
+ #learning-app-prototype .day { color:var(--ka-muted); font-size:12px; margin:14px 4px 6px; }
+ #learning-app-prototype .field { padding:10px 12px; border-bottom:1px solid var(--ka-line); font-size:13px; }
+ #learning-app-prototype .field span { display:block; color:var(--ka-muted); font-size:11px; margin-bottom:3px; }
+ #learning-app-prototype .segmented { display:flex; padding:3px; border-radius:12px; background:var(--ka-surface-2); margin-top:9px; }
+ #learning-app-prototype .segmented button { flex:1; border:0; border-radius:9px; padding:8px 0; background:transparent; color:var(--ka-muted); font-size:13px; }
+ #learning-app-prototype .segmented button.on { background:var(--ka-surface); color:var(--ka-ink); font-weight:500; box-shadow:0 1px 3px rgba(0,0,0,.12); }
+ #learning-app-prototype .row2 { display:flex; gap:8px; } #learning-app-prototype .row2 > * { flex:1; }
+ #learning-app-prototype .state-switch { display:flex; flex-wrap:wrap; align-items:center; gap:5px; margin:10px 0 2px; padding:7px 8px; border:1px dashed var(--ka-line); border-radius:11px; font-size:11px; color:var(--ka-muted); }
+ #learning-app-prototype .state-switch button { border:1px solid var(--ka-line); border-radius:999px; padding:3px 8px; background:var(--ka-surface); color:var(--ka-muted); font-size:11px; }
+ #learning-app-prototype .state-switch button.on { border-color:var(--ka-green); color:var(--ka-green); }
+ #learning-app-prototype .slot[hidden] { display:none; }
+ #learning-app-prototype .path-head { display:flex; justify-content:space-between; align-items:baseline; margin-top:20px; font-size:15px; }
+ #learning-app-prototype .path-head b { font-weight:500; }
+ #learning-app-prototype .path { margin-top:8px; background:var(--ka-surface); border:1px solid var(--ka-line); border-radius:15px; overflow:hidden; }
+ #learning-app-prototype .lesson-row { display:flex; align-items:center; gap:11px; padding:11px 12px; border-bottom:1px solid var(--ka-line); }
+ #learning-app-prototype .lesson-row:last-child { border-bottom:0; }
+ #learning-app-prototype .lesson-row .label { flex:1; }
+ #learning-app-prototype .lesson-row b { display:block; font-size:14px; font-weight:500; }
+ #learning-app-prototype .lesson-row small { color:var(--ka-muted); font-size:11px; }
+ #learning-app-prototype .lesson-ico { width:26px; height:26px; flex:none; display:grid; place-items:center; border-radius:50%; font-style:normal; font-size:12px; background:var(--ka-surface-2); color:var(--ka-muted); }
+ #learning-app-prototype .lesson-row.done .lesson-ico { background:var(--ka-green); color:var(--ka-green-ink); }
+ #learning-app-prototype .lesson-row.current { background:var(--ka-soft-green); }
+ #learning-app-prototype .lesson-row.current .lesson-ico { background:var(--ka-green); color:var(--ka-green-ink); }
+ #learning-app-prototype .lesson-row.locked b { color:var(--ka-muted); }
@media (max-width: 880px) { #learning-app-prototype .studio { grid-template-columns:180px minmax(300px, 1fr); } #learning-app-prototype .flow-panel { display:none; } }
@media (max-width: 590px) { #learning-app-prototype .studio { display:block; } #learning-app-prototype .panel { margin-bottom:15px; } #learning-app-prototype .screen-list { display:grid; grid-template-columns:1fr 1fr; } #learning-app-prototype .phone { width:100%; max-width:352px; } #learning-app-prototype .phone.mac { width:100%; max-width:430px; } }
@@ -125,8 +182,7 @@
-
-
+
@@ -135,8 +191,15 @@
-
-
+
+
+
+
+
+
+
+
+
@@ -167,23 +230,25 @@
9:41● ● ●
-
-
早上好,Shen
今天,说 3 句英语。
第 1 课已完成。今天把姓名介绍说得更自然。
-
A0 起步 · 已完成 3 个核心任务
-
第 2 课 · 拼读姓名12 分钟
先认识 4 个词 · 听 · 说 · 读 · 写
-
快速练习 · 初次见面
3 分钟 AI 对话 · 介绍姓名与来自哪里
+
+
早上好,Shen
今天,说几句能用的英语。
+
原型状态:
+
今天先复习 3 项6 分钟
昨天练过的关键句,今天换个情境再用一次。复习完再上新课。
+
第 3 课 · 来自哪里 · 第 2/3 段12 分钟
预热词汇 → 听说读写 → 对话 → 独立尝试
+
A0 阶段评估 · A0-E115 分钟
可使用 48/48 · 已掌握 30/30。两套评估间隔至少 24 小时。
+
A0 课程2 / 8 完成
+
+
+
✓第 1 课 · 你好,我是……介绍姓名并回应问候
+
+
▶第 3 课 · 来自哪里说出来自哪里 · 小段 1/3
+
🔒第 4 课 · 数字与电话号码说 0–10 与简单号码
+
+
🔒…… 第 8 课A0 课程结束后进入巩固与阶段评估
+
+
已完成的课可以随时点开重练;有复习积压时,新课会暂时锁住。
-
-
-
- 9:41● ● ●
A0 起步
- 学习地图 · 已完成 3 个任务
从认识到开口
核心课建立能力;达到出口任务才进入下一阶段。
-
✓ 第 1 课 · 你好,我是……
介绍姓名并回应问候
-
第 2 课 · 拼读姓名 进行中
听懂拼写,并说出自己的名字字母
-
-
第 4 课 · 数字与电话号码
说 0–10 与简单号码
-
-
+
9:41● ● ●
第 2 课 · 拼读姓名
@@ -221,7 +286,7 @@
初次见面 A0
介绍姓名和来自哪里 · 4 轮
-
+
9:41● ● ●
初次见面 · 2 / 4
@@ -244,19 +309,69 @@
9:41● ● ●
今天复习 · 3 项
昨天的对话
再回答一次。
这次换成新的情境,也能用上同一句话。
AI:Hi! Where are you from?
按住说话 ● 或输入文字
完成后还有 2 项 · 每项约 1 分钟
-
+
- 9:41● ● ●
我的进步
- 当前:A0 起步
你已经能做到
进步以“能完成什么”呈现,而非单纯的分数。
-
-
+
9:41● ● ●
我的
+
+
S
ShenA0 起步
每天 20 分钟 · 已坚持 6 天
+
+
+
学习
+
+
设置
+
+
+
开口英语 1.0.0
-
+
+
+
+ 9:41● ● ●
升级进度
+ A0 → A1
已完成 52%
进度来自掌握证据,不是上完固定课数就升级。
+
已掌握(间隔复习) 9 / 30
达到四次间隔复习要求
两套四技能评估 0 / 2
两套题组需间隔至少 24 小时
+
进入下一阶段的条件
60 项固定核心内容中至少 48 项可使用、30 项已掌握,且两套不同题组的听说读写评估都通过并间隔至少 24 小时。
+
+
+
+ 9:41● ● ●
四技能评估
+ 评估不提供翻译或句框。两套都通过,且间隔 24 小时以上,才计入升级。
+
A0-E1 未开始
听 · 说 · 读 · 写,各 2 题 · 约 12 分钟
+
A0-E2 未解锁
A0-E1 通过 24 小时后可开始
+
完成后可在这里查看得分和错题回顾。
+
+
+ 9:41● ● ●
学习记录
+
+
今天
+
昨天
+
记录只保存在本机,用于解释进度。
+
+
+ 9:41● ● ●
我的录音
+
+
+
- 9:41● ● ●
学习设置
- AI 对话服务
Gemini API · 可切换兼容反代
›
+ 9:41● ● ●
学习偏好
+
+
+
+ 9:41● ● ●
外观
+ 主题
选择“跟随系统”时,随手机的深色模式自动切换。
+
+
+
+ 9:41● ● ●
AI 服务
+
+
+
AI 只提供建议,不会直接决定你的掌握程度。
9:41● ● ●
初次见面 · 2 / 4
@@ -264,12 +379,12 @@
开口英语◉ Shen
- 早上好,Shen
今天,说 3 句英语。
Mac 复用手机端的课程与进度,本机也能独立练习。
第 2 课 · 拼读姓名
12 分钟 · 先认识 4 个词 · 听说读写
AI 对话 · 初次见面
3 分钟 · 介绍姓名与地点
今天复习 · 3 项
昨天的关键句将在新情境中再次练习。
+ 早上好,Shen
今天,说 3 句英语。
Mac 复用手机端的课程与进度,本机也能独立练习。
第 2 课 · 拼读姓名
12 分钟 · 先认识 4 个词 · 听说读写
AI 对话 · 初次见面
3 分钟 · 介绍姓名与地点
今天复习 · 3 项
昨天的关键句将在新情境中再次练习。
diff --git a/kouyu_english/.gitignore b/kouyu_english/.gitignore
index 3820a95..9363a88 100644
--- a/kouyu_english/.gitignore
+++ b/kouyu_english/.gitignore
@@ -43,3 +43,6 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release
+
+# Local AI service config — contains a real API key, never commit
+assets/config/ai_config.json
diff --git a/kouyu_english/assets/config/ai_config.example.json b/kouyu_english/assets/config/ai_config.example.json
new file mode 100644
index 0000000..27aae08
--- /dev/null
+++ b/kouyu_english/assets/config/ai_config.example.json
@@ -0,0 +1,8 @@
+{
+ "description": "复制此文件为 ai_config.json 并填入真实 apiKey;ai_config.json 已被 .gitignore 忽略,切勿提交。",
+ "provider": "compatible",
+ "endpoint": "https://api.deepseek.com/v1/chat/completions",
+ "model": "deepseek-chat",
+ "reasoningEffort": "low",
+ "apiKey": "sk-REPLACE_WITH_YOUR_OWN_KEY"
+}
diff --git a/kouyu_english/assets/config/ai_config.json b/kouyu_english/assets/config/ai_config.json
deleted file mode 100644
index 163f531..0000000
--- a/kouyu_english/assets/config/ai_config.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "provider": "compatible",
- "endpoint": "https://kmwq8ckvr0ehsgqyudcer1.slcydia.fun/v1/responses",
- "model": "gemini-3.7-flash-high",
- "reasoningEffort": "low",
- "apiKey": "***REMOVED***",
- "description": "默认 AI 对话服务配置。provider 可选: compatible (OpenAI 兼容/CLIProxyAPI/OneAPI), openAi, gemini, mock"
-}
diff --git a/kouyu_english/assets/courses/A0/a0-01.json b/kouyu_english/assets/courses/A0/a0-01.json
new file mode 100644
index 0000000..dc18d27
--- /dev/null
+++ b/kouyu_english/assets/courses/A0/a0-01.json
@@ -0,0 +1,342 @@
+{
+ "id": "a0-01",
+ "level": "A0",
+ "status": "approved",
+ "title": "你好,我是……",
+ "canDo": "介绍姓名并回应问候",
+ "categories": [],
+ "coreItems": [
+ {
+ "id": "A0-W36",
+ "type": "word",
+ "en": "hello",
+ "zh": "你好",
+ "match": [
+ [
+ "hello"
+ ]
+ ],
+ "review": {
+ "prompt": "把“你好”写成英文。",
+ "dictation": {
+ "sentence": "Hello, how are you?",
+ "meaning": "你好,你怎么样?"
+ }
+ }
+ },
+ {
+ "id": "A0-W37",
+ "type": "word",
+ "en": "hi",
+ "zh": "嗨",
+ "match": [
+ [
+ "hi"
+ ]
+ ],
+ "review": {
+ "prompt": "把“嗨”写成英文。",
+ "dictation": {
+ "sentence": "Hi, what’s your name?",
+ "meaning": "嗨,你叫什么名字?"
+ }
+ }
+ },
+ {
+ "id": "A0-W40",
+ "type": "word",
+ "en": "thanks",
+ "zh": "谢谢",
+ "match": [
+ [
+ "thanks"
+ ]
+ ],
+ "review": {
+ "prompt": "把“谢谢”写成英文。",
+ "dictation": {
+ "sentence": "I’m okay, thanks.",
+ "meaning": "我还可以,谢谢。"
+ }
+ }
+ },
+ {
+ "id": "A0-P01",
+ "type": "phrase",
+ "en": "I'm [name].",
+ "match": [
+ [
+ "my name is",
+ "/\\bi am (?!from\\b|good\\b|okay\\b|ok\\b|fine\\b|great\\b|tired\\b)[a-z]/"
+ ]
+ ],
+ "review": {
+ "prompt": "向新同学介绍你的名字。",
+ "hint": "用 I’m / I am 或 My name is 介绍一个名字。",
+ "dictation": {
+ "sentence": "I’m Tom.",
+ "meaning": "我是 Tom。"
+ }
+ }
+ },
+ {
+ "id": "A0-P02",
+ "type": "phrase",
+ "en": "What's your name?",
+ "match": [
+ [
+ "what is your name"
+ ]
+ ],
+ "review": {
+ "prompt": "问对方叫什么名字。",
+ "hint": "试着问:What’s your name?",
+ "dictation": {
+ "sentence": "What’s your name?",
+ "meaning": "你叫什么名字?"
+ }
+ }
+ },
+ {
+ "id": "A0-P03",
+ "type": "phrase",
+ "en": "Nice to meet you.",
+ "match": [
+ [
+ "nice to meet you"
+ ]
+ ],
+ "review": {
+ "prompt": "对初次见面的人说“很高兴认识你”。",
+ "dictation": {
+ "sentence": "Nice to meet you.",
+ "meaning": "很高兴认识你。"
+ }
+ }
+ }
+ ],
+ "segments": [
+ {
+ "id": "a0-01-a",
+ "title": "你好,我是……",
+ "minutes": 12,
+ "itemIds": [
+ "A0-W36",
+ "A0-W37",
+ "A0-W40",
+ "A0-P01",
+ "A0-P02",
+ "A0-P03"
+ ],
+ "grammarNote": "介绍自己时,把名字放在 I’m 后面:I’m Mia. 不需要先学复杂时态。",
+ "vocabulary": [
+ {
+ "id": "a0-01-hello",
+ "word": "Hello",
+ "meaning": "你好",
+ "example": "Hello. I’m Mia.",
+ "exampleMeaning": "你好。我是 Mia。",
+ "ipa": "/həˈləʊ/"
+ },
+ {
+ "id": "a0-01-name",
+ "word": "name",
+ "meaning": "名字",
+ "example": "My name is Shen.",
+ "exampleMeaning": "我的名字是 Shen。",
+ "ipa": "/neɪm/"
+ },
+ {
+ "id": "a0-01-nice",
+ "word": "Nice to meet you",
+ "meaning": "很高兴认识你",
+ "example": "Nice to meet you, too.",
+ "exampleMeaning": "我也很高兴认识你。"
+ },
+ {
+ "id": "a0-01-thanks",
+ "word": "thanks",
+ "meaning": "谢谢",
+ "example": "Thanks. Nice to meet you, too.",
+ "exampleMeaning": "谢谢。我也很高兴认识你。",
+ "ipa": "/θæŋks/"
+ }
+ ],
+ "sceneId": "a0-01-a",
+ "listening": {
+ "text": "Hello. I’m Mia. Nice to meet you.",
+ "question": "Mia 在做什么?",
+ "options": [
+ "自我介绍",
+ "买东西",
+ "问时间"
+ ]
+ },
+ "speaking": [
+ {
+ "text": "Hello. I’m Shen. Nice to meet you.",
+ "tip": ""
+ }
+ ],
+ "reading": {
+ "text": "Mia: Hello. I’m Mia. What’s your name?\nShen: Hi. I’m Shen.\nMia: Nice to meet you.\nShen: Thanks. Nice to meet you, too.",
+ "question": "Shen 说的最后一句是什么?",
+ "options": [
+ "Nice to meet you, too.",
+ "What’s your name?",
+ "Hi. I’m Shen."
+ ]
+ },
+ "writing": {
+ "prompt": "写一句问候和姓名介绍。",
+ "example": "Hello. I’m Shen.",
+ "accept": [
+ [
+ "hello",
+ "hi"
+ ],
+ [
+ "i am",
+ "my name is"
+ ]
+ ],
+ "hint": "试着同时写一句问候和姓名,例如:Hello. I’m …"
+ },
+ "independent": {
+ "prompt": "不看句框,介绍你的名字并回应“Nice to meet you”。",
+ "help": "Hi. I’m … / Nice to meet you, too.",
+ "accept": [
+ [
+ "i'm + #word",
+ "i am + #word",
+ "my name is",
+ "my name's"
+ ],
+ [
+ "nice to meet you"
+ ]
+ ]
+ }
+ }
+ ],
+ "scenes": [
+ {
+ "id": "a0-01-a",
+ "kind": "main",
+ "title": "你好,我是……",
+ "goal": "问候、介绍姓名并回应见面问候",
+ "turns": [
+ {
+ "ai": "Hi! I’m Mia. What’s your name?",
+ "aiZh": "嗨!我是 Mia。你叫什么名字?",
+ "goal": "说出你的名字",
+ "model": "I’m Alex.",
+ "accept": [
+ "i'm + #word",
+ "i am + #word",
+ "my name is",
+ "my name's",
+ "name is"
+ ]
+ },
+ {
+ "ai": "Nice to meet you. Say: Nice to meet you, too.",
+ "aiZh": "很高兴认识你。请说:Nice to meet you, too(我也很高兴认识你)。",
+ "goal": "回应 Nice to meet you",
+ "model": "Nice to meet you, too.",
+ "accept": [
+ "nice to meet you"
+ ]
+ },
+ {
+ "ai": "Great! Say hello one more time.",
+ "aiZh": "太棒了!再打一次招呼吧。",
+ "goal": "再打一次招呼",
+ "model": "Hello!",
+ "accept": [
+ "hello",
+ "hi",
+ "hey"
+ ]
+ },
+ {
+ "ai": "Now ask me my name!",
+ "aiZh": "现在请问我的名字!",
+ "goal": "反问对方的名字",
+ "model": "What’s your name?",
+ "accept": [
+ "what's your name",
+ "what is your name",
+ "your name",
+ "#question"
+ ]
+ }
+ ]
+ },
+ {
+ "id": "a0-meet",
+ "kind": "variant",
+ "title": "初次见面",
+ "goal": "姓名、地点、状态或喜好,并反问",
+ "turns": [
+ {
+ "ai": "Hi! My name is Mia. What’s your name?",
+ "aiZh": "嗨!我叫 Mia。你叫什么名字?",
+ "goal": "介绍姓名",
+ "model": "My name is Alex.",
+ "accept": [
+ "i'm + #word",
+ "i am + #word",
+ "my name is",
+ "my name's",
+ "name is"
+ ]
+ },
+ {
+ "ai": "Nice to meet you. Where are you from?",
+ "aiZh": "很高兴认识你。你来自哪里?",
+ "goal": "说明来自哪里",
+ "model": "I’m from Hong Kong.",
+ "accept": [
+ "i'm from",
+ "i am from",
+ "from + #word"
+ ]
+ },
+ {
+ "ai": "Great! How are you today? Or what do you like?",
+ "aiZh": "很好!你今天怎么样?或者你喜欢什么?",
+ "goal": "表达状态或喜好",
+ "model": "I’m good, thanks. / I like coffee.",
+ "accept": [
+ "good",
+ "okay",
+ "ok",
+ "fine",
+ "great",
+ "tired",
+ "i like",
+ "i love"
+ ]
+ },
+ {
+ "ai": "I’m good, thanks. Now ask me one question!",
+ "aiZh": "我很好,谢谢。现在请问我一个问题!",
+ "goal": "反问对方",
+ "model": "What’s your name? / Where are you from?",
+ "accept": [
+ "#question"
+ ]
+ }
+ ],
+ "practice": {
+ "title": "初次见面",
+ "summary": "介绍姓名、地点、状态或喜好,并反问对方",
+ "recapPrompt": "再用英语介绍一次自己。",
+ "alwaysOpen": true
+ }
+ }
+ ],
+ "tasks": [],
+ "materials": []
+}
diff --git a/kouyu_english/assets/courses/A0/a0-02.json b/kouyu_english/assets/courses/A0/a0-02.json
new file mode 100644
index 0000000..2366b0d
--- /dev/null
+++ b/kouyu_english/assets/courses/A0/a0-02.json
@@ -0,0 +1,170 @@
+{
+ "id": "a0-02",
+ "level": "A0",
+ "status": "approved",
+ "title": "拼读我的名字",
+ "canDo": "听懂拼写,并说出名字字母",
+ "categories": [],
+ "coreItems": [
+ {
+ "id": "A0-P04",
+ "type": "phrase",
+ "en": "How do you spell that?",
+ "match": [
+ [
+ "how do you spell"
+ ]
+ ],
+ "review": {
+ "prompt": "问对方一个词怎样拼写。",
+ "hint": "问对方怎样拼写:How do you spell …?",
+ "dictation": {
+ "sentence": "How do you spell that?",
+ "meaning": "那个怎么拼写?"
+ }
+ }
+ }
+ ],
+ "segments": [
+ {
+ "id": "a0-02-a",
+ "title": "拼读我的名字",
+ "minutes": 12,
+ "itemIds": [
+ "A0-P04"
+ ],
+ "grammarNote": "My name is 后面接名字;拼读时每个字母之间稍微停顿。",
+ "vocabulary": [
+ {
+ "id": "a0-02-spell",
+ "word": "spell",
+ "meaning": "拼写",
+ "example": "How do you spell that?",
+ "exampleMeaning": "那个怎么拼?",
+ "ipa": "/spel/"
+ },
+ {
+ "id": "a0-02-name",
+ "word": "name",
+ "meaning": "名字",
+ "example": "My name is Shen.",
+ "exampleMeaning": "我的名字是 Shen。",
+ "ipa": "/neɪm/"
+ },
+ {
+ "id": "a0-02-question",
+ "word": "How do you spell that?",
+ "meaning": "那个怎么拼?",
+ "example": "How do you spell your name?",
+ "exampleMeaning": "你的名字怎么拼?"
+ }
+ ],
+ "sceneId": "a0-02-a",
+ "listening": {
+ "text": "How do you spell your name?",
+ "question": "这句话是什么意思?",
+ "options": [
+ "你的名字怎么拼?",
+ "你的名字是什么?",
+ "你来自哪里?"
+ ]
+ },
+ "speaking": [
+ {
+ "text": "S H E N",
+ "tip": "字母之间留一个短停顿:S /es/ - H /eɪtʃ/ - E /iː/ - N /en/。先清楚,不必快。"
+ }
+ ],
+ "reading": {
+ "text": "Mia: I’m Mia. M-I-A.\nShen: Hi, Mia. I’m Shen. S-H-E-N.\nMia: How do you spell Sam?\nShen: S-A-M.",
+ "question": "Shen 自己的名字怎么拼写?",
+ "options": [
+ "S-H-E-N",
+ "M-I-A",
+ "S-A-M"
+ ]
+ },
+ "writing": {
+ "prompt": "把名字和拼写写完整。",
+ "example": "My name is Shen.\nS-H-E-N.",
+ "accept": [
+ [
+ "my name",
+ "i am"
+ ]
+ ],
+ "hint": "写出姓名并拼写一遍,例如 My name is Shen. S-H-E-N."
+ },
+ "independent": {
+ "prompt": "不看句框,用英文介绍名字并拼读它。",
+ "help": "My name is … / A-B-C.",
+ "accept": [
+ [
+ "i'm + #word",
+ "i am + #word",
+ "my name is",
+ "my name's"
+ ],
+ [
+ "#spelling"
+ ]
+ ]
+ }
+ }
+ ],
+ "scenes": [
+ {
+ "id": "a0-02-a",
+ "kind": "main",
+ "title": "拼读我的名字",
+ "goal": "介绍姓名并完整拼读名字",
+ "turns": [
+ {
+ "ai": "Hi! What’s your name?",
+ "aiZh": "嗨!你叫什么名字?",
+ "goal": "说出你的名字",
+ "model": "My name is Alex.",
+ "accept": [
+ "i'm + #word",
+ "i am + #word",
+ "my name is",
+ "my name's",
+ "name is"
+ ]
+ },
+ {
+ "ai": "How do you spell that?",
+ "aiZh": "那个怎么拼写?",
+ "goal": "拼读你的名字",
+ "model": "A-L-E-X.",
+ "accept": [
+ "#spelling"
+ ]
+ },
+ {
+ "ai": "Thank you. Spell it one more time.",
+ "aiZh": "谢谢。请再拼写一次。",
+ "goal": "再拼读一次",
+ "model": "A-L-E-X.",
+ "accept": [
+ "#spelling"
+ ]
+ },
+ {
+ "ai": "Now ask me my name!",
+ "aiZh": "现在请问我的名字!",
+ "goal": "反问对方的名字",
+ "model": "What’s your name?",
+ "accept": [
+ "what's your name",
+ "what is your name",
+ "your name",
+ "#question"
+ ]
+ }
+ ]
+ }
+ ],
+ "tasks": [],
+ "materials": []
+}
diff --git a/kouyu_english/assets/courses/A0/a0-03.json b/kouyu_english/assets/courses/A0/a0-03.json
new file mode 100644
index 0000000..4c42010
--- /dev/null
+++ b/kouyu_english/assets/courses/A0/a0-03.json
@@ -0,0 +1,266 @@
+{
+ "id": "a0-03",
+ "level": "A0",
+ "status": "approved",
+ "title": "你好吗?",
+ "canDo": "问候并表达简单状态",
+ "categories": [],
+ "coreItems": [
+ {
+ "id": "A0-W33",
+ "type": "word",
+ "en": "good",
+ "zh": "好",
+ "match": [
+ [
+ "good"
+ ]
+ ],
+ "review": {
+ "prompt": "把“好”写成英文。",
+ "dictation": {
+ "sentence": "I’m good.",
+ "meaning": "我很好。"
+ }
+ }
+ },
+ {
+ "id": "A0-W34",
+ "type": "word",
+ "en": "okay",
+ "zh": "还可以",
+ "match": [
+ [
+ "okay"
+ ]
+ ],
+ "review": {
+ "prompt": "把“还可以”写成英文。",
+ "dictation": {
+ "sentence": "I’m okay.",
+ "meaning": "我还可以。"
+ }
+ }
+ },
+ {
+ "id": "A0-W35",
+ "type": "word",
+ "en": "tired",
+ "zh": "累",
+ "match": [
+ [
+ "tired"
+ ]
+ ],
+ "review": {
+ "prompt": "把“累”写成英文。",
+ "dictation": {
+ "sentence": "I’m tired.",
+ "meaning": "我累了。"
+ }
+ }
+ },
+ {
+ "id": "A0-P05",
+ "type": "phrase",
+ "en": "How are you?",
+ "match": [
+ [
+ "how are you"
+ ]
+ ],
+ "review": {
+ "prompt": "问对方今天怎么样。",
+ "dictation": {
+ "sentence": "How are you?",
+ "meaning": "你怎么样?"
+ }
+ }
+ },
+ {
+ "id": "A0-P06",
+ "type": "phrase",
+ "en": "I'm [state].",
+ "match": [
+ [
+ "i am",
+ "my name is"
+ ],
+ [
+ "good",
+ "okay",
+ "tired"
+ ]
+ ],
+ "review": {
+ "prompt": "说出你今天的状态。",
+ "dictation": {
+ "sentence": "I’m tired today.",
+ "meaning": "我今天很累。"
+ }
+ }
+ }
+ ],
+ "segments": [
+ {
+ "id": "a0-03-a",
+ "title": "你好吗?",
+ "minutes": 12,
+ "itemIds": [
+ "A0-W33",
+ "A0-W34",
+ "A0-W35",
+ "A0-P05",
+ "A0-P06"
+ ],
+ "grammarNote": "说状态用 I’m + 状态:I’m good / okay / tired。",
+ "vocabulary": [
+ {
+ "id": "a0-03-good",
+ "word": "good",
+ "meaning": "好的",
+ "example": "I’m good, thanks.",
+ "exampleMeaning": "我很好,谢谢。",
+ "ipa": "/ɡʊd/"
+ },
+ {
+ "id": "a0-03-tired",
+ "word": "tired",
+ "meaning": "累的",
+ "example": "I’m tired today.",
+ "exampleMeaning": "我今天有点累。",
+ "ipa": "/ˈtaɪəd/"
+ },
+ {
+ "id": "a0-03-how",
+ "word": "How are you?",
+ "meaning": "你好吗?",
+ "example": "How are you today?",
+ "exampleMeaning": "你今天怎么样?"
+ }
+ ],
+ "sceneId": "a0-03-a",
+ "listening": {
+ "text": "How are you today?",
+ "question": "对方在问什么?",
+ "options": [
+ "你的状态",
+ "你的号码",
+ "你的地点"
+ ]
+ },
+ "speaking": [
+ {
+ "text": "How are you? I’m good, thanks.",
+ "tip": ""
+ }
+ ],
+ "reading": {
+ "text": "Mia: Hi, Shen. How are you?\nShen: I’m good, thanks. How are you?\nMia: I’m tired today.",
+ "question": "Mia 说自己今天怎么样?",
+ "options": [
+ "I’m tired today.",
+ "I’m good, thanks.",
+ "I’m okay."
+ ]
+ },
+ "writing": {
+ "prompt": "写一句今天的状态。",
+ "example": "I’m okay today.",
+ "accept": [
+ [
+ "i am",
+ "my name is"
+ ],
+ [
+ "good",
+ "okay",
+ "tired"
+ ]
+ ],
+ "hint": "用 I’m / I am 加上你的状态,例如 good 或 tired。"
+ },
+ "independent": {
+ "prompt": "不看句框,问候并说出你的状态。",
+ "help": "How are you? / I’m …",
+ "accept": [
+ [
+ "hello",
+ "hi",
+ "hey",
+ "how are you"
+ ],
+ [
+ "good",
+ "okay",
+ "ok",
+ "fine",
+ "great",
+ "tired"
+ ]
+ ]
+ }
+ }
+ ],
+ "scenes": [
+ {
+ "id": "a0-03-a",
+ "kind": "main",
+ "title": "你好吗?",
+ "goal": "询问状态、回答并反问",
+ "turns": [
+ {
+ "ai": "Hi! How are you today?",
+ "aiZh": "嗨!你今天好吗?",
+ "goal": "说出你今天的状态",
+ "model": "I’m good, thanks.",
+ "accept": [
+ "good",
+ "okay",
+ "ok",
+ "fine",
+ "great",
+ "tired"
+ ]
+ },
+ {
+ "ai": "Good! Ask me: How are you?",
+ "aiZh": "很好!请问我:How are you(你好吗)?",
+ "goal": "反问对方的状态",
+ "model": "How are you?",
+ "accept": [
+ "how are you",
+ "#question"
+ ]
+ },
+ {
+ "ai": "I’m okay. Say your state one more time.",
+ "aiZh": "我还好。请再说一次你的状态。",
+ "goal": "再说一次你的状态",
+ "model": "I’m okay.",
+ "accept": [
+ "good",
+ "okay",
+ "ok",
+ "fine",
+ "great",
+ "tired"
+ ]
+ },
+ {
+ "ai": "Now say goodbye!",
+ "aiZh": "现在请说再见!",
+ "goal": "说再见",
+ "model": "Bye!",
+ "accept": [
+ "bye",
+ "goodbye",
+ "see you"
+ ]
+ }
+ ]
+ }
+ ],
+ "tasks": [],
+ "materials": []
+}
diff --git a/kouyu_english/assets/courses/A0/a0-04.json b/kouyu_english/assets/courses/A0/a0-04.json
new file mode 100644
index 0000000..6af343c
--- /dev/null
+++ b/kouyu_english/assets/courses/A0/a0-04.json
@@ -0,0 +1,762 @@
+{
+ "id": "a0-04",
+ "level": "A0",
+ "status": "approved",
+ "title": "数字与电话号码",
+ "canDo": "说 0–10 与虚拟号码",
+ "categories": [],
+ "coreItems": [
+ {
+ "id": "A0-W01",
+ "type": "word",
+ "en": "zero",
+ "zh": "零",
+ "match": [
+ [
+ "zero"
+ ]
+ ],
+ "review": {
+ "prompt": "把“零”写成英文。",
+ "dictation": {
+ "sentence": "Zero, one, two.",
+ "meaning": "零、一、二。"
+ }
+ }
+ },
+ {
+ "id": "A0-W02",
+ "type": "word",
+ "en": "one",
+ "zh": "一",
+ "match": [
+ [
+ "one"
+ ]
+ ],
+ "review": {
+ "prompt": "把“一”写成英文。",
+ "dictation": {
+ "sentence": "My number is one-two-three.",
+ "meaning": "我的号码是一二三。"
+ }
+ }
+ },
+ {
+ "id": "A0-W03",
+ "type": "word",
+ "en": "two",
+ "zh": "二",
+ "match": [
+ [
+ "two"
+ ]
+ ],
+ "review": {
+ "prompt": "把“二”写成英文。",
+ "dictation": {
+ "sentence": "It’s two o’clock.",
+ "meaning": "现在两点。"
+ }
+ }
+ },
+ {
+ "id": "A0-W04",
+ "type": "word",
+ "en": "three",
+ "zh": "三",
+ "match": [
+ [
+ "three"
+ ]
+ ],
+ "review": {
+ "prompt": "把“三”写成英文。",
+ "dictation": {
+ "sentence": "It’s three o’clock.",
+ "meaning": "现在三点。"
+ }
+ }
+ },
+ {
+ "id": "A0-W05",
+ "type": "word",
+ "en": "four",
+ "zh": "四",
+ "match": [
+ [
+ "four"
+ ]
+ ],
+ "review": {
+ "prompt": "把“四”写成英文。",
+ "dictation": {
+ "sentence": "It’s four o’clock.",
+ "meaning": "现在四点。"
+ }
+ }
+ },
+ {
+ "id": "A0-W06",
+ "type": "word",
+ "en": "five",
+ "zh": "五",
+ "match": [
+ [
+ "five"
+ ]
+ ],
+ "review": {
+ "prompt": "把“五”写成英文。",
+ "dictation": {
+ "sentence": "It’s five o’clock.",
+ "meaning": "现在五点。"
+ }
+ }
+ },
+ {
+ "id": "A0-W07",
+ "type": "word",
+ "en": "six",
+ "zh": "六",
+ "match": [
+ [
+ "six"
+ ]
+ ],
+ "review": {
+ "prompt": "把“六”写成英文。",
+ "dictation": {
+ "sentence": "It’s six o’clock.",
+ "meaning": "现在六点。"
+ }
+ }
+ },
+ {
+ "id": "A0-W08",
+ "type": "word",
+ "en": "seven",
+ "zh": "七",
+ "match": [
+ [
+ "seven"
+ ]
+ ],
+ "review": {
+ "prompt": "把“七”写成英文。",
+ "dictation": {
+ "sentence": "It’s seven o’clock.",
+ "meaning": "现在七点。"
+ }
+ }
+ },
+ {
+ "id": "A0-W09",
+ "type": "word",
+ "en": "eight",
+ "zh": "八",
+ "match": [
+ [
+ "eight"
+ ]
+ ],
+ "review": {
+ "prompt": "把“八”写成英文。",
+ "dictation": {
+ "sentence": "It’s eight o’clock.",
+ "meaning": "现在八点。"
+ }
+ }
+ },
+ {
+ "id": "A0-W10",
+ "type": "word",
+ "en": "nine",
+ "zh": "九",
+ "match": [
+ [
+ "nine"
+ ]
+ ],
+ "review": {
+ "prompt": "把“九”写成英文。",
+ "dictation": {
+ "sentence": "It’s nine o’clock.",
+ "meaning": "现在九点。"
+ }
+ }
+ },
+ {
+ "id": "A0-W11",
+ "type": "word",
+ "en": "ten",
+ "zh": "十",
+ "match": [
+ [
+ "ten"
+ ]
+ ],
+ "review": {
+ "prompt": "把“十”写成英文。",
+ "dictation": {
+ "sentence": "It’s ten o’clock.",
+ "meaning": "现在十点。"
+ }
+ }
+ },
+ {
+ "id": "A0-W20",
+ "type": "word",
+ "en": "phone",
+ "zh": "电话",
+ "match": [
+ [
+ "phone"
+ ]
+ ],
+ "review": {
+ "prompt": "把“电话”写成英文。",
+ "dictation": {
+ "sentence": "It’s a phone.",
+ "meaning": "这是一部电话。"
+ }
+ }
+ },
+ {
+ "id": "A0-P07",
+ "type": "phrase",
+ "en": "What's your phone number?",
+ "match": [
+ [
+ "what is your phone number"
+ ]
+ ],
+ "review": {
+ "prompt": "问对方的电话号码。",
+ "dictation": {
+ "sentence": "What’s your phone number?",
+ "meaning": "你的电话号码是多少?"
+ }
+ }
+ },
+ {
+ "id": "A0-P08",
+ "type": "phrase",
+ "en": "My number is [digits].",
+ "match": [
+ [
+ "#numbers:3"
+ ]
+ ],
+ "review": {
+ "prompt": "报出一个虚构的三位号码。",
+ "hint": "说出至少三个英文数字,也可用 My number is … 开头。",
+ "dictation": {
+ "sentence": "My number is one-three-eight.",
+ "meaning": "我的号码是一三八。"
+ }
+ }
+ }
+ ],
+ "vocabulary": [
+ {
+ "id": "a0-04-three",
+ "word": "three",
+ "meaning": "三",
+ "example": "One, three, eight.",
+ "exampleMeaning": "一、三、八。",
+ "ipa": "/θriː/"
+ },
+ {
+ "id": "a0-04-number",
+ "word": "number",
+ "meaning": "号码",
+ "example": "My number is 1-3-8.",
+ "exampleMeaning": "我的号码是 138。",
+ "ipa": "/ˈnʌmbə(r)/"
+ },
+ {
+ "id": "a0-04-phone",
+ "word": "phone",
+ "meaning": "电话",
+ "example": "What’s your phone number?",
+ "exampleMeaning": "你的电话号码是多少?"
+ }
+ ],
+ "segments": [
+ {
+ "id": "a0-04-a",
+ "title": "数字与电话号码",
+ "minutes": 12,
+ "itemIds": [
+ "A0-W01",
+ "A0-W02",
+ "A0-W03",
+ "A0-W04",
+ "A0-W05",
+ "A0-W06"
+ ],
+ "grammarNote": "数字单独说就可以:zero, one, two。每个词之间稍微停顿。",
+ "vocabulary": [
+ {
+ "id": "A0-W01",
+ "word": "zero",
+ "meaning": "零",
+ "example": "Zero, one, two.",
+ "exampleMeaning": "零、一、二。",
+ "ipa": "/ˈzɪrəʊ/"
+ },
+ {
+ "id": "A0-W02",
+ "word": "one",
+ "meaning": "一",
+ "example": "One, two, three.",
+ "exampleMeaning": "一、二、三。",
+ "ipa": "/wʌn/"
+ },
+ {
+ "id": "A0-W03",
+ "word": "two",
+ "meaning": "二",
+ "example": "Two and three.",
+ "exampleMeaning": "二和三。",
+ "ipa": "/tuː/"
+ },
+ {
+ "id": "A0-W04",
+ "word": "three",
+ "meaning": "三",
+ "example": "Three, four, five.",
+ "exampleMeaning": "三、四、五。",
+ "ipa": "/θriː/"
+ },
+ {
+ "id": "A0-W05",
+ "word": "four",
+ "meaning": "四",
+ "example": "Four and five.",
+ "exampleMeaning": "四和五。",
+ "ipa": "/fɔː/"
+ },
+ {
+ "id": "A0-W06",
+ "word": "five",
+ "meaning": "五",
+ "example": "Five, please.",
+ "exampleMeaning": "五,请。",
+ "ipa": "/faɪv/"
+ }
+ ],
+ "sceneId": "a0-04-a",
+ "listening": {
+ "text": "One, two, three.",
+ "question": "听到哪一组数字?",
+ "options": [
+ "123",
+ "132",
+ "213"
+ ]
+ },
+ "speaking": [
+ {
+ "text": "Zero, one, two, three.",
+ "tip": ""
+ }
+ ],
+ "reading": {
+ "text": "Mia: One, two, three.\nShen: Four, five.",
+ "question": "Shen 接着说了哪两个数字?",
+ "options": [
+ "Four, five",
+ "One, two",
+ "Three, four"
+ ]
+ },
+ "writing": {
+ "prompt": "把 0、1、2、3 写成英文。",
+ "example": "zero, one, two, three",
+ "accept": [
+ [
+ "zero"
+ ],
+ [
+ "one"
+ ],
+ [
+ "two"
+ ],
+ [
+ "three"
+ ]
+ ],
+ "hint": "请写出 zero、one、two、three 四个数字。"
+ },
+ "independent": {
+ "prompt": "不看帮助,说出三个 0 到 5 的数字。",
+ "help": "zero / one / two / three / four / five",
+ "accept": [
+ [
+ "#min:3",
+ "zero",
+ "one",
+ "two",
+ "three",
+ "four",
+ "five"
+ ]
+ ]
+ }
+ },
+ {
+ "id": "a0-04-b",
+ "title": "数字与电话号码",
+ "minutes": 12,
+ "itemIds": [
+ "A0-W07",
+ "A0-W08",
+ "A0-W09",
+ "A0-W10",
+ "A0-W11",
+ "A0-W20"
+ ],
+ "grammarNote": "认物品可以说 It is a phone. 口语里也常说 It’s a phone.",
+ "vocabulary": [
+ {
+ "id": "A0-W07",
+ "word": "six",
+ "meaning": "六",
+ "example": "Six, seven, eight.",
+ "exampleMeaning": "六、七、八。",
+ "ipa": "/sɪks/"
+ },
+ {
+ "id": "A0-W08",
+ "word": "seven",
+ "meaning": "七",
+ "example": "Seven, eight, nine.",
+ "exampleMeaning": "七、八、九。",
+ "ipa": "/ˈsevən/"
+ },
+ {
+ "id": "A0-W09",
+ "word": "eight",
+ "meaning": "八",
+ "example": "Eight, nine, ten.",
+ "exampleMeaning": "八、九、十。",
+ "ipa": "/eɪt/"
+ },
+ {
+ "id": "A0-W10",
+ "word": "nine",
+ "meaning": "九",
+ "example": "Nine and ten.",
+ "exampleMeaning": "九和十。",
+ "ipa": "/naɪn/"
+ },
+ {
+ "id": "A0-W11",
+ "word": "ten",
+ "meaning": "十",
+ "example": "Ten, please.",
+ "exampleMeaning": "十,请。",
+ "ipa": "/ten/"
+ },
+ {
+ "id": "A0-W20",
+ "word": "phone",
+ "meaning": "电话;手机",
+ "example": "It is a phone.",
+ "exampleMeaning": "它是一部手机。",
+ "ipa": "/fəʊn/"
+ }
+ ],
+ "sceneId": "a0-04-b",
+ "listening": {
+ "text": "Six, seven, eight.",
+ "question": "听到哪一组数字?",
+ "options": [
+ "678",
+ "687",
+ "768"
+ ]
+ },
+ "speaking": [
+ {
+ "text": "My phone is here.",
+ "tip": ""
+ }
+ ],
+ "reading": {
+ "text": "Mia: Six, seven, eight.\nShen: Nine, ten.",
+ "question": "Shen 最后说了哪两个数字?",
+ "options": [
+ "Nine, ten",
+ "Six, seven",
+ "Seven, eight"
+ ]
+ },
+ "writing": {
+ "prompt": "把 6、7、8 写成英文。",
+ "example": "six, seven, eight",
+ "accept": [
+ [
+ "six"
+ ],
+ [
+ "seven"
+ ],
+ [
+ "eight"
+ ]
+ ],
+ "hint": "请写出 six、seven、eight 三个数字。"
+ },
+ "independent": {
+ "prompt": "不看帮助,说出三个 6 到 10 的数字。",
+ "help": "six / seven / eight / nine / ten",
+ "accept": [
+ [
+ "#min:3",
+ "six",
+ "seven",
+ "eight",
+ "nine",
+ "ten"
+ ]
+ ]
+ }
+ },
+ {
+ "id": "a0-04-c",
+ "title": "数字与电话号码",
+ "minutes": 12,
+ "itemIds": [
+ "A0-P07",
+ "A0-P08"
+ ],
+ "grammarNote": "报号码时一个数字一个数字说:one-three-eight,不把它读成一百三十八。",
+ "vocabulary": [
+ {
+ "id": "A0-P07",
+ "word": "What's your phone number?",
+ "meaning": "你的电话号码是多少?",
+ "example": "What's your phone number?",
+ "exampleMeaning": "你的电话号码是多少?"
+ },
+ {
+ "id": "A0-P08",
+ "word": "My number is one-three-eight.",
+ "meaning": "我的号码是 138。",
+ "example": "My number is one-three-eight.",
+ "exampleMeaning": "我的号码是 138。"
+ }
+ ],
+ "sceneId": "a0-04-c",
+ "listening": {
+ "text": "My number is one-three-eight.",
+ "question": "号码是多少?",
+ "options": [
+ "138",
+ "183",
+ "318"
+ ]
+ },
+ "speaking": [
+ {
+ "text": "My number is one-three-eight.",
+ "tip": "号码一个数字一个数字地读,数字之间稍停:one - three - eight。"
+ }
+ ],
+ "reading": {
+ "text": "Mia: What’s your phone number?\nShen: My number is five-zero-two.\nMia: Five-two-zero?\nShen: No. Five-zero-two.",
+ "question": "Shen 最后确认的号码是哪一个?",
+ "options": [
+ "502",
+ "520",
+ "205"
+ ]
+ },
+ "writing": {
+ "prompt": "把虚拟号码 139 写成英文。",
+ "example": "one-three-nine",
+ "accept": [
+ [
+ "#numbers:3"
+ ]
+ ],
+ "hint": "用英文一个一个写出三个数字,例如 one-three-nine。"
+ },
+ "independent": {
+ "prompt": "不看句框,说出一个三位虚拟号码。",
+ "help": "My number is one-two-three.",
+ "accept": [
+ [
+ "#min:3",
+ "one",
+ "two",
+ "three",
+ "four",
+ "five",
+ "six",
+ "seven",
+ "eight",
+ "nine",
+ "zero"
+ ]
+ ]
+ }
+ }
+ ],
+ "scenes": [
+ {
+ "id": "a0-04-a",
+ "kind": "main",
+ "title": "数字与电话号码",
+ "goal": "听辨并说出 0 到 5",
+ "turns": [
+ {
+ "ai": "Say zero, one, two.",
+ "aiZh": "请说 zero, one, two(0,1,2)。",
+ "goal": "说出 zero, one, two",
+ "model": "zero, one, two",
+ "accept": [
+ "zero",
+ "one",
+ "two"
+ ]
+ },
+ {
+ "ai": "Now say three, four, five.",
+ "aiZh": "现在请说 three, four, five(3,4,5)。",
+ "goal": "说出 three, four, five",
+ "model": "three, four, five",
+ "accept": [
+ "three",
+ "four",
+ "five"
+ ]
+ }
+ ]
+ },
+ {
+ "id": "a0-04-b",
+ "kind": "main",
+ "title": "数字与电话号码",
+ "goal": "听辨 6 到 10 并认识 phone",
+ "turns": [
+ {
+ "ai": "Say six, seven, eight.",
+ "aiZh": "请说 six, seven, eight(6,7,8)。",
+ "goal": "说出 six, seven, eight",
+ "model": "six, seven, eight",
+ "accept": [
+ "six",
+ "seven",
+ "eight"
+ ]
+ },
+ {
+ "ai": "What is this? Say: It is a phone.",
+ "aiZh": "这是什么?请说:It is a phone(这是一部手机)。",
+ "goal": "说出 It is a phone.",
+ "model": "It is a phone.",
+ "accept": [
+ "it is a phone",
+ "it's a phone"
+ ]
+ }
+ ]
+ },
+ {
+ "id": "a0-04-c",
+ "kind": "main",
+ "title": "数字与电话号码",
+ "goal": "询问并报告三位号码",
+ "turns": [
+ {
+ "ai": "What is your phone number?",
+ "aiZh": "你的电话号码是多少?",
+ "goal": "报出你的三位号码",
+ "model": "My number is one-three-eight.",
+ "accept": [
+ "my number is + #digit"
+ ]
+ },
+ {
+ "ai": "Say a three-digit number again.",
+ "aiZh": "请再说一次三位数字。",
+ "goal": "再说一次三位数字",
+ "model": "one-three-eight",
+ "accept": [
+ "one",
+ "two",
+ "three",
+ "four",
+ "five",
+ "six",
+ "seven",
+ "eight",
+ "nine",
+ "zero"
+ ]
+ }
+ ]
+ },
+ {
+ "id": "a0-number",
+ "kind": "variant",
+ "title": "留个电话",
+ "goal": "问候并交换一个虚拟电话号码",
+ "turns": [
+ {
+ "ai": "Hi! How are you today?",
+ "aiZh": "嗨!你今天好吗?",
+ "goal": "说出你今天的状态",
+ "model": "I’m good, thanks.",
+ "accept": [
+ "good",
+ "okay",
+ "ok",
+ "fine",
+ "great",
+ "tired"
+ ]
+ },
+ {
+ "ai": "Good. What’s your phone number? Use a fake number.",
+ "aiZh": "好的。你的电话号码是多少?用一个虚拟号码。",
+ "goal": "报出一个号码",
+ "model": "My number is one-three-eight.",
+ "accept": [
+ "#digit"
+ ]
+ },
+ {
+ "ai": "Sorry, please say it again.",
+ "aiZh": "抱歉,请再说一遍。",
+ "goal": "再说一次号码",
+ "model": "One-three-eight.",
+ "accept": [
+ "#digit"
+ ]
+ },
+ {
+ "ai": "Thanks! Now ask me one question.",
+ "aiZh": "谢谢!现在问我一个问题。",
+ "goal": "反问对方",
+ "model": "What’s your phone number?",
+ "accept": [
+ "#question"
+ ]
+ }
+ ],
+ "practice": {
+ "title": "留个电话",
+ "summary": "问候、报出号码、再说一遍,并反问对方",
+ "recapPrompt": "用英语报一次你的虚拟电话号码。"
+ }
+ }
+ ],
+ "tasks": [],
+ "materials": []
+}
diff --git a/kouyu_english/assets/courses/A0/a0-05.json b/kouyu_english/assets/courses/A0/a0-05.json
new file mode 100644
index 0000000..42a8757
--- /dev/null
+++ b/kouyu_english/assets/courses/A0/a0-05.json
@@ -0,0 +1,284 @@
+{
+ "id": "a0-05",
+ "level": "A0",
+ "status": "approved",
+ "title": "这是什么?",
+ "canDo": "询问并说出常见物品",
+ "categories": [],
+ "coreItems": [
+ {
+ "id": "A0-W19",
+ "type": "word",
+ "en": "book",
+ "zh": "书",
+ "match": [
+ [
+ "book"
+ ]
+ ],
+ "review": {
+ "prompt": "把“书”写成英文。",
+ "dictation": {
+ "sentence": "It’s a book.",
+ "meaning": "这是一本书。"
+ }
+ }
+ },
+ {
+ "id": "A0-W21",
+ "type": "word",
+ "en": "pen",
+ "zh": "笔",
+ "match": [
+ [
+ "pen"
+ ]
+ ],
+ "review": {
+ "prompt": "把“笔”写成英文。",
+ "dictation": {
+ "sentence": "It’s a pen.",
+ "meaning": "这是一支笔。"
+ }
+ }
+ },
+ {
+ "id": "A0-W22",
+ "type": "word",
+ "en": "bag",
+ "zh": "包",
+ "match": [
+ [
+ "bag"
+ ]
+ ],
+ "review": {
+ "prompt": "把“包”写成英文。",
+ "dictation": {
+ "sentence": "It’s a bag.",
+ "meaning": "这是一个包。"
+ }
+ }
+ },
+ {
+ "id": "A0-W23",
+ "type": "word",
+ "en": "key",
+ "zh": "钥匙",
+ "match": [
+ [
+ "key"
+ ]
+ ],
+ "review": {
+ "prompt": "把“钥匙”写成英文。",
+ "dictation": {
+ "sentence": "It’s a key.",
+ "meaning": "这是一把钥匙。"
+ }
+ }
+ },
+ {
+ "id": "A0-P09",
+ "type": "phrase",
+ "en": "What's this?",
+ "match": [
+ [
+ "what is this"
+ ]
+ ],
+ "review": {
+ "prompt": "指着身边的东西问“这是什么”。",
+ "dictation": {
+ "sentence": "What’s this?",
+ "meaning": "这是什么?"
+ }
+ }
+ },
+ {
+ "id": "A0-P10",
+ "type": "phrase",
+ "en": "It's a [object].",
+ "match": [
+ [
+ "it is"
+ ],
+ [
+ "book",
+ "pen",
+ "bag",
+ "key",
+ "phone"
+ ]
+ ],
+ "review": {
+ "prompt": "说出“这是一件物品”。",
+ "hint": "用 It’s / It is 加上一个物品。",
+ "dictation": {
+ "sentence": "It’s a pen.",
+ "meaning": "这是一支笔。"
+ }
+ }
+ }
+ ],
+ "segments": [
+ {
+ "id": "a0-05-a",
+ "title": "这是什么?",
+ "minutes": 12,
+ "itemIds": [
+ "A0-W19",
+ "A0-W21",
+ "A0-W22",
+ "A0-W23",
+ "A0-P09",
+ "A0-P10"
+ ],
+ "grammarNote": "问物品用 What’s this?;回答用 It’s a + 物品。",
+ "vocabulary": [
+ {
+ "id": "a0-05-book",
+ "word": "book",
+ "meaning": "书",
+ "example": "It’s a book.",
+ "exampleMeaning": "这是一本书。",
+ "ipa": "/bʊk/"
+ },
+ {
+ "id": "a0-05-pen",
+ "word": "pen",
+ "meaning": "笔",
+ "example": "It’s a pen.",
+ "exampleMeaning": "这是一支笔。",
+ "ipa": "/pen/"
+ },
+ {
+ "id": "a0-05-this",
+ "word": "What’s this?",
+ "meaning": "这是什么?",
+ "example": "What’s this? It’s a key.",
+ "exampleMeaning": "这是什么?这是钥匙。"
+ },
+ {
+ "id": "a0-05-bag",
+ "word": "bag",
+ "meaning": "包",
+ "example": "It’s a bag.",
+ "exampleMeaning": "这是一个包。",
+ "ipa": "/bæɡ/"
+ }
+ ],
+ "sceneId": "a0-05-a",
+ "listening": {
+ "text": "What’s this? It’s a key.",
+ "question": "这是什么?",
+ "options": [
+ "钥匙",
+ "书",
+ "水"
+ ]
+ },
+ "speaking": [
+ {
+ "text": "What’s this? It’s a pen.",
+ "tip": ""
+ }
+ ],
+ "reading": {
+ "text": "Mia: What’s this? Is it a pen?\nShen: No. It’s a book.\nMia: And what’s this?\nShen: It’s a bag.",
+ "question": "Shen 说第一件东西是什么?",
+ "options": [
+ "A book",
+ "A pen",
+ "A bag"
+ ]
+ },
+ "writing": {
+ "prompt": "写一句“这是一支笔”。",
+ "example": "It’s a pen.",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "book",
+ "pen",
+ "bag",
+ "key"
+ ]
+ ],
+ "hint": "用 It’s / It is 加一个物品,例如 a pen。"
+ },
+ "independent": {
+ "prompt": "不看句框,说出一个身边物品。",
+ "help": "It’s a …",
+ "accept": [
+ [
+ "it's a",
+ "it is a",
+ "it's an",
+ "it is an"
+ ]
+ ]
+ }
+ }
+ ],
+ "scenes": [
+ {
+ "id": "a0-05-a",
+ "kind": "main",
+ "title": "这是什么?",
+ "goal": "询问并说出一个物品",
+ "turns": [
+ {
+ "ai": "What’s this? It is a pen.",
+ "aiZh": "这是什么?这是一支笔。",
+ "goal": "说出这个物品",
+ "model": "It’s a pen.",
+ "accept": [
+ "it's a",
+ "it is a",
+ "it's an",
+ "it is an"
+ ]
+ },
+ {
+ "ai": "Now say: It’s a pen.",
+ "aiZh": "现在请说:It’s a pen(这是一支笔)。",
+ "goal": "完整说出 It’s a … 句型",
+ "model": "It’s a pen.",
+ "accept": [
+ "it's a",
+ "it is a",
+ "it's an",
+ "it is an"
+ ]
+ },
+ {
+ "ai": "Ask me: What’s this?",
+ "aiZh": "请问我:What’s this(这是什么)?",
+ "goal": "反问 What’s this",
+ "model": "What’s this?",
+ "accept": [
+ "what's this",
+ "what is this"
+ ]
+ },
+ {
+ "ai": "Great! Say one more object.",
+ "aiZh": "太棒了!再说一个物品吧。",
+ "goal": "再说一个物品",
+ "model": "It’s a book.",
+ "accept": [
+ "it's a",
+ "it is a",
+ "it's an",
+ "it is an"
+ ]
+ }
+ ]
+ }
+ ],
+ "tasks": [],
+ "materials": []
+}
diff --git a/kouyu_english/assets/courses/A0/a0-06.json b/kouyu_english/assets/courses/A0/a0-06.json
new file mode 100644
index 0000000..d533b3f
--- /dev/null
+++ b/kouyu_english/assets/courses/A0/a0-06.json
@@ -0,0 +1,187 @@
+{
+ "id": "a0-06",
+ "level": "A0",
+ "status": "approved",
+ "title": "我来自哪里?",
+ "canDo": "说明来自哪里并反问",
+ "categories": [],
+ "coreItems": [
+ {
+ "id": "A0-P11",
+ "type": "phrase",
+ "en": "Where are you from?",
+ "match": [
+ [
+ "where are you from"
+ ]
+ ],
+ "review": {
+ "prompt": "问对方来自哪里。",
+ "dictation": {
+ "sentence": "Where are you from?",
+ "meaning": "你来自哪里?"
+ }
+ }
+ },
+ {
+ "id": "A0-P12",
+ "type": "phrase",
+ "en": "I'm from [place].",
+ "match": [
+ [
+ "i am",
+ "my name is"
+ ],
+ [
+ "/\\bfrom [a-z]{2,}/"
+ ]
+ ],
+ "review": {
+ "prompt": "说出你来自哪里。",
+ "hint": "用 I’m from … 说出一个地点。",
+ "dictation": {
+ "sentence": "I’m from China.",
+ "meaning": "我来自中国。"
+ }
+ }
+ }
+ ],
+ "segments": [
+ {
+ "id": "a0-06-a",
+ "title": "我来自哪里?",
+ "minutes": 12,
+ "itemIds": [
+ "A0-P11",
+ "A0-P12"
+ ],
+ "grammarNote": "说地点用 I’m from + 地点:I’m from Hong Kong。",
+ "vocabulary": [
+ {
+ "id": "a0-06-from",
+ "word": "from",
+ "meaning": "来自",
+ "example": "I’m from Hong Kong.",
+ "exampleMeaning": "我来自香港。",
+ "ipa": "/frəm/"
+ },
+ {
+ "id": "a0-06-place",
+ "word": "place",
+ "meaning": "地点",
+ "example": "Where are you from?",
+ "exampleMeaning": "你来自哪里?"
+ }
+ ],
+ "sceneId": "a0-06-a",
+ "listening": {
+ "text": "Where are you from?",
+ "question": "对方在问什么?",
+ "options": [
+ "来自哪里",
+ "叫什么",
+ "喜欢什么"
+ ]
+ },
+ "speaking": [
+ {
+ "text": "I’m from Hong Kong.",
+ "tip": ""
+ }
+ ],
+ "reading": {
+ "text": "Mia: I’m from London. Where are you from?\nShen: I’m from Hong Kong.",
+ "question": "Shen 来自哪里?",
+ "options": [
+ "Hong Kong",
+ "London",
+ "Beijing"
+ ]
+ },
+ "writing": {
+ "prompt": "写一句你来自哪里。",
+ "example": "I’m from Hong Kong.",
+ "accept": [
+ [
+ "i am",
+ "my name is"
+ ],
+ [
+ "/\\bfrom [a-z]{2,}/"
+ ]
+ ],
+ "hint": "用 I’m from … 写成一个完整句子。"
+ },
+ "independent": {
+ "prompt": "不看句框,说来自哪里并反问对方。",
+ "help": "I’m from … / Where are you from?",
+ "accept": [
+ [
+ "i'm from",
+ "i am from"
+ ],
+ [
+ "where are you from",
+ "#question"
+ ]
+ ]
+ }
+ }
+ ],
+ "scenes": [
+ {
+ "id": "a0-06-a",
+ "kind": "main",
+ "title": "我来自哪里?",
+ "goal": "说来自哪里并反问",
+ "turns": [
+ {
+ "ai": "Where are you from?",
+ "aiZh": "你来自哪里?",
+ "goal": "说出你来自哪里",
+ "model": "I’m from Hong Kong.",
+ "accept": [
+ "i'm from",
+ "i am from",
+ "from + #word"
+ ]
+ },
+ {
+ "ai": "Nice! Ask me: Where are you from?",
+ "aiZh": "很好!请问我:Where are you from(你来自哪里)?",
+ "goal": "反问对方来自哪里",
+ "model": "Where are you from?",
+ "accept": [
+ "where are you from",
+ "where're you from",
+ "where are you"
+ ]
+ },
+ {
+ "ai": "Say where you are from one more time.",
+ "aiZh": "请再说一次你来自哪里。",
+ "goal": "再说一次你来自哪里",
+ "model": "I’m from Hong Kong.",
+ "accept": [
+ "i'm from",
+ "i am from",
+ "from + #word"
+ ]
+ },
+ {
+ "ai": "Say goodbye!",
+ "aiZh": "请说再见!",
+ "goal": "说再见",
+ "model": "Bye!",
+ "accept": [
+ "bye",
+ "goodbye",
+ "see you"
+ ]
+ }
+ ]
+ }
+ ],
+ "tasks": [],
+ "materials": []
+}
diff --git a/kouyu_english/assets/courses/A0/a0-07.json b/kouyu_english/assets/courses/A0/a0-07.json
new file mode 100644
index 0000000..7755b7d
--- /dev/null
+++ b/kouyu_english/assets/courses/A0/a0-07.json
@@ -0,0 +1,305 @@
+{
+ "id": "a0-07",
+ "level": "A0",
+ "status": "approved",
+ "title": "我的家人",
+ "canDo": "介绍一位家人",
+ "categories": [],
+ "coreItems": [
+ {
+ "id": "A0-W25",
+ "type": "word",
+ "en": "mother",
+ "zh": "妈妈",
+ "match": [
+ [
+ "mother"
+ ]
+ ],
+ "review": {
+ "prompt": "把“妈妈”写成英文。",
+ "dictation": {
+ "sentence": "This is my mother.",
+ "meaning": "这是我妈妈。"
+ }
+ }
+ },
+ {
+ "id": "A0-W26",
+ "type": "word",
+ "en": "father",
+ "zh": "爸爸",
+ "match": [
+ [
+ "father"
+ ]
+ ],
+ "review": {
+ "prompt": "把“爸爸”写成英文。",
+ "dictation": {
+ "sentence": "This is my father.",
+ "meaning": "这是我爸爸。"
+ }
+ }
+ },
+ {
+ "id": "A0-W27",
+ "type": "word",
+ "en": "sister",
+ "zh": "姐妹",
+ "match": [
+ [
+ "sister"
+ ]
+ ],
+ "review": {
+ "prompt": "把“姐妹”写成英文。",
+ "dictation": {
+ "sentence": "This is my sister.",
+ "meaning": "这是我的姐妹。"
+ }
+ }
+ },
+ {
+ "id": "A0-W28",
+ "type": "word",
+ "en": "brother",
+ "zh": "兄弟",
+ "match": [
+ [
+ "brother"
+ ]
+ ],
+ "review": {
+ "prompt": "把“兄弟”写成英文。",
+ "dictation": {
+ "sentence": "This is my brother.",
+ "meaning": "这是我的兄弟。"
+ }
+ }
+ },
+ {
+ "id": "A0-P13",
+ "type": "phrase",
+ "en": "This is my [person].",
+ "match": [
+ [
+ "/\\bthis is my [a-z]{2,}/"
+ ]
+ ],
+ "review": {
+ "prompt": "介绍一位家人。",
+ "dictation": {
+ "sentence": "This is my sister.",
+ "meaning": "这是我的姐妹。"
+ }
+ }
+ }
+ ],
+ "segments": [
+ {
+ "id": "a0-07-a",
+ "title": "我的家人",
+ "minutes": 12,
+ "itemIds": [
+ "A0-W25",
+ "A0-W26",
+ "A0-W27",
+ "A0-W28",
+ "A0-P13"
+ ],
+ "grammarNote": "介绍人用 This is my + 人:This is my mother。",
+ "vocabulary": [
+ {
+ "id": "a0-07-family",
+ "word": "family",
+ "meaning": "家人",
+ "example": "This is my family.",
+ "exampleMeaning": "这是我的家人。",
+ "ipa": "/ˈfæməli/"
+ },
+ {
+ "id": "a0-07-mother",
+ "word": "mother",
+ "meaning": "母亲",
+ "example": "This is my mother.",
+ "exampleMeaning": "这是我的妈妈。"
+ },
+ {
+ "id": "a0-07-sister",
+ "word": "sister",
+ "meaning": "姐妹",
+ "example": "This is my sister.",
+ "exampleMeaning": "这是我的姐姐(或妹妹)。",
+ "ipa": "/ˈsɪstə(r)/"
+ },
+ {
+ "id": "a0-07-brother",
+ "word": "brother",
+ "meaning": "兄弟",
+ "example": "This is my brother.",
+ "exampleMeaning": "这是我的哥哥(或弟弟)。",
+ "ipa": "/ˈbrʌðə(r)/"
+ }
+ ],
+ "sceneId": "a0-07-a",
+ "listening": {
+ "text": "Who is this? This is my mother.",
+ "question": "这个人是谁?",
+ "options": [
+ "妈妈",
+ "朋友",
+ "老师"
+ ]
+ },
+ "speaking": [
+ {
+ "text": "This is my family.",
+ "tip": ""
+ }
+ ],
+ "reading": {
+ "text": "Mia: Who is this? Is this your father?\nShen: No. This is my mother.\nMia: And who is this? Your sister?\nShen: No. This is my brother.",
+ "question": "Shen 先介绍的是哪一位家人?",
+ "options": [
+ "My mother",
+ "My brother",
+ "My father"
+ ]
+ },
+ "writing": {
+ "prompt": "介绍一位家人或朋友。",
+ "example": "This is my mother.",
+ "accept": [
+ [
+ "this is my"
+ ],
+ [
+ "mother",
+ "father",
+ "sister",
+ "brother",
+ "friend"
+ ]
+ ],
+ "hint": "用 This is my … 介绍一位家人或朋友。"
+ },
+ "independent": {
+ "prompt": "不看句框,介绍一位家人或朋友。",
+ "help": "This is my …",
+ "accept": [
+ [
+ "this is my"
+ ]
+ ]
+ }
+ }
+ ],
+ "scenes": [
+ {
+ "id": "a0-07-a",
+ "kind": "main",
+ "title": "我的家人",
+ "goal": "介绍一位家人或朋友",
+ "turns": [
+ {
+ "ai": "Who is this?",
+ "aiZh": "这是谁?",
+ "goal": "介绍一位家人或朋友",
+ "model": "This is my mother.",
+ "accept": [
+ "this is my",
+ "this is"
+ ]
+ },
+ {
+ "ai": "Good. Say: This is my mother.",
+ "aiZh": "很好。请说:This is my mother(这是我的妈妈)。",
+ "goal": "完整说出 This is my … 句型",
+ "model": "This is my mother.",
+ "accept": [
+ "this is my",
+ "this is"
+ ]
+ },
+ {
+ "ai": "Ask me: Who is this?",
+ "aiZh": "请问我:Who is this(这是谁)?",
+ "goal": "反问 Who is this",
+ "model": "Who is this?",
+ "accept": [
+ "who is this",
+ "who's this"
+ ]
+ },
+ {
+ "ai": "Introduce one person again.",
+ "aiZh": "请再介绍一个人。",
+ "goal": "再介绍一个人",
+ "model": "This is my friend.",
+ "accept": [
+ "this is my",
+ "this is"
+ ]
+ }
+ ]
+ },
+ {
+ "id": "a0-photo",
+ "kind": "variant",
+ "title": "看照片聊天",
+ "goal": "介绍照片里的人和物品",
+ "turns": [
+ {
+ "ai": "Nice photo! Who is this?",
+ "aiZh": "照片真好!这是谁?",
+ "goal": "介绍照片里的人",
+ "model": "This is my sister.",
+ "accept": [
+ "this is my",
+ "this is"
+ ]
+ },
+ {
+ "ai": "And what’s this in the photo?",
+ "aiZh": "照片里这个是什么?",
+ "goal": "说出照片里的物品",
+ "model": "It’s a bag.",
+ "accept": [
+ "it's a",
+ "it is a",
+ "it's an",
+ "it is an"
+ ]
+ },
+ {
+ "ai": "Where are you from?",
+ "aiZh": "你来自哪里?",
+ "goal": "说出你来自哪里",
+ "model": "I’m from Hong Kong.",
+ "accept": [
+ "i'm from",
+ "i am from",
+ "from + #word"
+ ]
+ },
+ {
+ "ai": "Cool! Now ask me one question.",
+ "aiZh": "真酷!现在问我一个问题。",
+ "goal": "反问对方",
+ "model": "Who is this? / What’s this?",
+ "accept": [
+ "#question"
+ ]
+ }
+ ],
+ "practice": {
+ "title": "看照片聊天",
+ "summary": "介绍照片里的人和东西,说喜好,并反问对方",
+ "recapPrompt": "用英语介绍一位家人或朋友。"
+ }
+ }
+ ],
+ "tasks": [],
+ "materials": []
+}
diff --git a/kouyu_english/assets/courses/A0/a0-08.json b/kouyu_english/assets/courses/A0/a0-08.json
new file mode 100644
index 0000000..59225b9
--- /dev/null
+++ b/kouyu_english/assets/courses/A0/a0-08.json
@@ -0,0 +1,603 @@
+{
+ "id": "a0-08",
+ "level": "A0",
+ "status": "approved",
+ "title": "星期与时间",
+ "canDo": "说今天和整点",
+ "categories": [],
+ "coreItems": [
+ {
+ "id": "A0-W12",
+ "type": "word",
+ "en": "Monday",
+ "zh": "星期一",
+ "match": [
+ [
+ "monday"
+ ]
+ ],
+ "review": {
+ "prompt": "把“星期一”写成英文。",
+ "dictation": {
+ "sentence": "It’s Monday.",
+ "meaning": "今天星期一。"
+ }
+ }
+ },
+ {
+ "id": "A0-W13",
+ "type": "word",
+ "en": "Tuesday",
+ "zh": "星期二",
+ "match": [
+ [
+ "tuesday"
+ ]
+ ],
+ "review": {
+ "prompt": "把“星期二”写成英文。",
+ "dictation": {
+ "sentence": "It’s Tuesday.",
+ "meaning": "今天星期二。"
+ }
+ }
+ },
+ {
+ "id": "A0-W14",
+ "type": "word",
+ "en": "Wednesday",
+ "zh": "星期三",
+ "match": [
+ [
+ "wednesday"
+ ]
+ ],
+ "review": {
+ "prompt": "把“星期三”写成英文。",
+ "dictation": {
+ "sentence": "It’s Wednesday.",
+ "meaning": "今天星期三。"
+ }
+ }
+ },
+ {
+ "id": "A0-W15",
+ "type": "word",
+ "en": "Thursday",
+ "zh": "星期四",
+ "match": [
+ [
+ "thursday"
+ ]
+ ],
+ "review": {
+ "prompt": "把“星期四”写成英文。",
+ "dictation": {
+ "sentence": "It’s Thursday.",
+ "meaning": "今天星期四。"
+ }
+ }
+ },
+ {
+ "id": "A0-W16",
+ "type": "word",
+ "en": "Friday",
+ "zh": "星期五",
+ "match": [
+ [
+ "friday"
+ ]
+ ],
+ "review": {
+ "prompt": "把“星期五”写成英文。",
+ "dictation": {
+ "sentence": "It’s Friday.",
+ "meaning": "今天星期五。"
+ }
+ }
+ },
+ {
+ "id": "A0-W17",
+ "type": "word",
+ "en": "Saturday",
+ "zh": "星期六",
+ "match": [
+ [
+ "saturday"
+ ]
+ ],
+ "review": {
+ "prompt": "把“星期六”写成英文。",
+ "dictation": {
+ "sentence": "It’s Saturday.",
+ "meaning": "今天星期六。"
+ }
+ }
+ },
+ {
+ "id": "A0-W18",
+ "type": "word",
+ "en": "Sunday",
+ "zh": "星期日",
+ "match": [
+ [
+ "sunday"
+ ]
+ ],
+ "review": {
+ "prompt": "把“星期日”写成英文。",
+ "dictation": {
+ "sentence": "It’s Sunday.",
+ "meaning": "今天星期日。"
+ }
+ }
+ },
+ {
+ "id": "A0-P14",
+ "type": "phrase",
+ "en": "What day is it today?",
+ "match": [
+ [
+ "what day is it"
+ ]
+ ],
+ "review": {
+ "prompt": "问今天星期几。",
+ "dictation": {
+ "sentence": "What day is it today?",
+ "meaning": "今天星期几?"
+ }
+ }
+ },
+ {
+ "id": "A0-P15",
+ "type": "phrase",
+ "en": "It's [weekday].",
+ "match": [
+ [
+ "it is"
+ ],
+ [
+ "monday",
+ "tuesday",
+ "wednesday",
+ "thursday",
+ "friday",
+ "saturday",
+ "sunday"
+ ]
+ ],
+ "review": {
+ "prompt": "说出今天星期几。",
+ "dictation": {
+ "sentence": "It’s Friday.",
+ "meaning": "今天星期五。"
+ }
+ }
+ },
+ {
+ "id": "A0-P16",
+ "type": "phrase",
+ "en": "What time is it?",
+ "match": [
+ [
+ "what time is it"
+ ]
+ ],
+ "review": {
+ "prompt": "问现在几点。",
+ "dictation": {
+ "sentence": "What time is it?",
+ "meaning": "现在几点?"
+ }
+ }
+ },
+ {
+ "id": "A0-P17",
+ "type": "phrase",
+ "en": "It's [hour] o'clock.",
+ "match": [
+ [
+ "it is"
+ ],
+ [
+ "o'clock",
+ "oclock",
+ "clock"
+ ]
+ ],
+ "review": {
+ "prompt": "说出一个整点时间。",
+ "hint": "用 It’s … o’clock 说整点时间。",
+ "dictation": {
+ "sentence": "It’s three o’clock.",
+ "meaning": "现在三点。"
+ }
+ }
+ }
+ ],
+ "vocabulary": [
+ {
+ "id": "a0-08-monday",
+ "word": "Monday",
+ "meaning": "星期一",
+ "example": "It is Monday.",
+ "exampleMeaning": "今天是星期一。"
+ },
+ {
+ "id": "a0-08-oclock",
+ "word": "o’clock",
+ "meaning": "整点",
+ "example": "It’s three o’clock.",
+ "exampleMeaning": "现在三点整。"
+ }
+ ],
+ "segments": [
+ {
+ "id": "a0-08-a",
+ "title": "星期与时间",
+ "minutes": 12,
+ "itemIds": [
+ "A0-W12",
+ "A0-W13",
+ "A0-W14",
+ "A0-P14",
+ "A0-P15"
+ ],
+ "grammarNote": "问星期用 What day is it?;回答用 It is / It’s + 星期。",
+ "vocabulary": [
+ {
+ "id": "A0-W12",
+ "word": "Monday",
+ "meaning": "星期一",
+ "example": "It is Monday.",
+ "exampleMeaning": "今天是星期一。",
+ "ipa": "/ˈmʌndeɪ/"
+ },
+ {
+ "id": "A0-W13",
+ "word": "Tuesday",
+ "meaning": "星期二",
+ "example": "It is Tuesday.",
+ "exampleMeaning": "今天是星期二。",
+ "ipa": "/ˈtjuːzdeɪ/"
+ },
+ {
+ "id": "A0-W14",
+ "word": "Wednesday",
+ "meaning": "星期三",
+ "example": "It is Wednesday.",
+ "exampleMeaning": "今天是星期三。",
+ "ipa": "/ˈwenzdeɪ/"
+ },
+ {
+ "id": "A0-P14",
+ "word": "What day is it today?",
+ "meaning": "今天星期几?",
+ "example": "What day is it today?",
+ "exampleMeaning": "今天星期几?"
+ },
+ {
+ "id": "A0-P15",
+ "word": "It's Monday.",
+ "meaning": "今天是星期一。",
+ "example": "It's Monday.",
+ "exampleMeaning": "今天是星期一。"
+ }
+ ],
+ "sceneId": "a0-08-a",
+ "listening": {
+ "text": "It is Monday.",
+ "question": "今天星期几?",
+ "options": [
+ "星期一",
+ "星期二",
+ "星期三"
+ ]
+ },
+ "speaking": [
+ {
+ "text": "It is Monday.",
+ "tip": ""
+ }
+ ],
+ "reading": {
+ "text": "Mia: What day is it today? Is it Monday?\nShen: No. It is Tuesday.",
+ "question": "Mia 猜错了,今天其实是星期几?",
+ "options": [
+ "Tuesday",
+ "Monday",
+ "Wednesday"
+ ]
+ },
+ "writing": {
+ "prompt": "写一句今天是星期一、二或三。",
+ "example": "It is Monday.",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "monday",
+ "tuesday",
+ "wednesday"
+ ]
+ ],
+ "hint": "用 It is / It’s 加 Monday、Tuesday 或 Wednesday。"
+ },
+ "independent": {
+ "prompt": "不看帮助,说出星期一、二或三。",
+ "help": "It is Monday / Tuesday / Wednesday.",
+ "accept": [
+ [
+ "monday",
+ "tuesday",
+ "wednesday"
+ ]
+ ]
+ }
+ },
+ {
+ "id": "a0-08-b",
+ "title": "星期与时间",
+ "minutes": 12,
+ "itemIds": [
+ "A0-W15",
+ "A0-W16",
+ "A0-W17",
+ "A0-W18"
+ ],
+ "grammarNote": "星期四到星期日也沿用 It is / It’s + 星期这个句型。",
+ "vocabulary": [
+ {
+ "id": "A0-W15",
+ "word": "Thursday",
+ "meaning": "星期四",
+ "example": "It is Thursday.",
+ "exampleMeaning": "今天是星期四。",
+ "ipa": "/ˈθɜːzdeɪ/"
+ },
+ {
+ "id": "A0-W16",
+ "word": "Friday",
+ "meaning": "星期五",
+ "example": "It is Friday.",
+ "exampleMeaning": "今天是星期五。",
+ "ipa": "/ˈfraɪdeɪ/"
+ },
+ {
+ "id": "A0-W17",
+ "word": "Saturday",
+ "meaning": "星期六",
+ "example": "It is Saturday.",
+ "exampleMeaning": "今天是星期六。",
+ "ipa": "/ˈsætədeɪ/"
+ },
+ {
+ "id": "A0-W18",
+ "word": "Sunday",
+ "meaning": "星期日",
+ "example": "It is Sunday.",
+ "exampleMeaning": "今天是星期日。",
+ "ipa": "/ˈsʌndeɪ/"
+ }
+ ],
+ "sceneId": "a0-08-b",
+ "listening": {
+ "text": "It is Friday.",
+ "question": "今天星期几?",
+ "options": [
+ "星期五",
+ "星期四",
+ "星期日"
+ ]
+ },
+ "speaking": [
+ {
+ "text": "It is Friday.",
+ "tip": ""
+ }
+ ],
+ "reading": {
+ "text": "Mia: Is it Saturday today?\nShen: No. It is Sunday.",
+ "question": "Shen 说今天是哪一天?",
+ "options": [
+ "Sunday",
+ "Saturday",
+ "Friday"
+ ]
+ },
+ "writing": {
+ "prompt": "写一句今天是星期四到日。",
+ "example": "It is Friday.",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "thursday",
+ "friday",
+ "saturday",
+ "sunday"
+ ]
+ ],
+ "hint": "用 It is / It’s 加 Thursday、Friday、Saturday 或 Sunday。"
+ },
+ "independent": {
+ "prompt": "不看帮助,说出星期四到日。",
+ "help": "It is Thursday / Friday / Saturday / Sunday.",
+ "accept": [
+ [
+ "thursday",
+ "friday",
+ "saturday",
+ "sunday"
+ ]
+ ]
+ }
+ },
+ {
+ "id": "a0-08-c",
+ "title": "星期与时间",
+ "minutes": 12,
+ "itemIds": [
+ "A0-P16",
+ "A0-P17"
+ ],
+ "grammarNote": "说整点用 It is / It’s + 数字 + o’clock,例如 It’s three o’clock。",
+ "vocabulary": [
+ {
+ "id": "A0-P16",
+ "word": "What time is it?",
+ "meaning": "现在几点?",
+ "example": "What time is it?",
+ "exampleMeaning": "现在几点?"
+ },
+ {
+ "id": "A0-P17",
+ "word": "It's three o'clock.",
+ "meaning": "现在三点。",
+ "example": "It's three o'clock.",
+ "exampleMeaning": "现在三点。"
+ }
+ ],
+ "sceneId": "a0-08-c",
+ "listening": {
+ "text": "It is three o’clock.",
+ "question": "是什么时间?",
+ "options": [
+ "三点",
+ "一点",
+ "星期三"
+ ]
+ },
+ "speaking": [
+ {
+ "text": "It is three o’clock.",
+ "tip": ""
+ }
+ ],
+ "reading": {
+ "text": "Mia: What time is it? Is it two o’clock?\nShen: No. It is three o’clock.",
+ "question": "Shen 说现在是几点?",
+ "options": [
+ "Three o’clock",
+ "Two o’clock",
+ "One o’clock"
+ ]
+ },
+ "writing": {
+ "prompt": "写一句整点时间。",
+ "example": "It is three o’clock.",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "o'clock",
+ "oclock"
+ ]
+ ],
+ "hint": "用 It is / It’s 加数字和 o’clock,例如 It’s three o’clock。"
+ },
+ "independent": {
+ "prompt": "不看帮助,说一个整点时间。",
+ "help": "It is … o’clock.",
+ "accept": [
+ [
+ "o'clock",
+ "oclock"
+ ]
+ ]
+ }
+ }
+ ],
+ "scenes": [
+ {
+ "id": "a0-08-a",
+ "kind": "main",
+ "title": "星期与时间",
+ "goal": "询问并说出星期一到三",
+ "turns": [
+ {
+ "ai": "What day is it?",
+ "aiZh": "今天星期几?",
+ "goal": "问今天星期几",
+ "model": "What day is it?",
+ "accept": [
+ "what day"
+ ]
+ },
+ {
+ "ai": "Say Monday, Tuesday, or Wednesday.",
+ "aiZh": "请说 Monday, Tuesday, 或 Wednesday(周一、周二或周三)。",
+ "goal": "说出周一到周三中的一天",
+ "model": "It is Monday.",
+ "accept": [
+ "monday",
+ "tuesday",
+ "wednesday"
+ ]
+ }
+ ]
+ },
+ {
+ "id": "a0-08-b",
+ "kind": "main",
+ "title": "星期与时间",
+ "goal": "说出星期四到日",
+ "turns": [
+ {
+ "ai": "What day is it?",
+ "aiZh": "今天星期几?",
+ "goal": "问今天星期几",
+ "model": "What day is it?",
+ "accept": [
+ "what day"
+ ]
+ },
+ {
+ "ai": "Say Thursday, Friday, Saturday, or Sunday.",
+ "aiZh": "请说 Thursday, Friday, Saturday, 或 Sunday(周四、周五、周六或周日)。",
+ "goal": "说出周四到周日中的一天",
+ "model": "It is Friday.",
+ "accept": [
+ "thursday",
+ "friday",
+ "saturday",
+ "sunday"
+ ]
+ }
+ ]
+ },
+ {
+ "id": "a0-08-c",
+ "kind": "main",
+ "title": "星期与时间",
+ "goal": "询问并说出整点",
+ "turns": [
+ {
+ "ai": "What time is it?",
+ "aiZh": "现在几点了?",
+ "goal": "问现在几点",
+ "model": "What time is it?",
+ "accept": [
+ "what time"
+ ]
+ },
+ {
+ "ai": "Say one full time sentence.",
+ "aiZh": "请说一个完整的时间句子。",
+ "goal": "说一个完整的时间句子",
+ "model": "It is three o’clock.",
+ "accept": [
+ "o'clock",
+ "oclock"
+ ]
+ }
+ ]
+ }
+ ],
+ "tasks": [],
+ "materials": []
+}
diff --git a/kouyu_english/assets/courses/A0/a0-09.json b/kouyu_english/assets/courses/A0/a0-09.json
new file mode 100644
index 0000000..5578689
--- /dev/null
+++ b/kouyu_english/assets/courses/A0/a0-09.json
@@ -0,0 +1,373 @@
+{
+ "id": "a0-09",
+ "level": "A0",
+ "status": "approved",
+ "title": "我喜欢……",
+ "canDo": "表达喜好并回答",
+ "categories": [],
+ "coreItems": [
+ {
+ "id": "A0-W29",
+ "type": "word",
+ "en": "coffee",
+ "zh": "咖啡",
+ "match": [
+ [
+ "coffee"
+ ]
+ ],
+ "review": {
+ "prompt": "把“咖啡”写成英文。",
+ "dictation": {
+ "sentence": "I like coffee.",
+ "meaning": "我喜欢咖啡。"
+ }
+ }
+ },
+ {
+ "id": "A0-W30",
+ "type": "word",
+ "en": "tea",
+ "zh": "茶",
+ "match": [
+ [
+ "tea"
+ ]
+ ],
+ "review": {
+ "prompt": "把“茶”写成英文。",
+ "dictation": {
+ "sentence": "Do you like tea?",
+ "meaning": "你喜欢茶吗?"
+ }
+ }
+ },
+ {
+ "id": "A0-W31",
+ "type": "word",
+ "en": "music",
+ "zh": "音乐",
+ "match": [
+ [
+ "music"
+ ]
+ ],
+ "review": {
+ "prompt": "把“音乐”写成英文。",
+ "dictation": {
+ "sentence": "I like music.",
+ "meaning": "我喜欢音乐。"
+ }
+ }
+ },
+ {
+ "id": "A0-W32",
+ "type": "word",
+ "en": "movies",
+ "zh": "电影",
+ "match": [
+ [
+ "movies"
+ ]
+ ],
+ "review": {
+ "prompt": "把“电影”写成英文。",
+ "dictation": {
+ "sentence": "Do you like movies?",
+ "meaning": "你喜欢电影吗?"
+ }
+ }
+ },
+ {
+ "id": "A0-W38",
+ "type": "word",
+ "en": "yes",
+ "zh": "是",
+ "match": [
+ [
+ "yes"
+ ]
+ ],
+ "review": {
+ "prompt": "把“是”写成英文。",
+ "dictation": {
+ "sentence": "Yes, I do.",
+ "meaning": "是的,我喜欢。"
+ }
+ }
+ },
+ {
+ "id": "A0-W39",
+ "type": "word",
+ "en": "no",
+ "zh": "不",
+ "match": [
+ [
+ "no"
+ ]
+ ],
+ "review": {
+ "prompt": "把“不”写成英文。",
+ "dictation": {
+ "sentence": "No, I don’t.",
+ "meaning": "不,我不喜欢。"
+ }
+ }
+ },
+ {
+ "id": "A0-P18",
+ "type": "phrase",
+ "en": "I like [thing].",
+ "match": [
+ [
+ "/\\bi (like|love) [a-z]{2,}/"
+ ]
+ ],
+ "review": {
+ "prompt": "说出你喜欢的一样东西。",
+ "hint": "用 I like … 说一项喜好。",
+ "dictation": {
+ "sentence": "I like music.",
+ "meaning": "我喜欢音乐。"
+ }
+ }
+ },
+ {
+ "id": "A0-P19",
+ "type": "phrase",
+ "en": "Do you like [thing]?",
+ "match": [
+ [
+ "do you like"
+ ]
+ ],
+ "review": {
+ "prompt": "问对方是否喜欢一样东西。",
+ "dictation": {
+ "sentence": "Do you like coffee?",
+ "meaning": "你喜欢咖啡吗?"
+ }
+ }
+ }
+ ],
+ "segments": [
+ {
+ "id": "a0-09-a",
+ "title": "我喜欢……",
+ "minutes": 12,
+ "itemIds": [
+ "A0-W29",
+ "A0-W30",
+ "A0-W31",
+ "A0-W32",
+ "A0-W38",
+ "A0-W39",
+ "A0-P18",
+ "A0-P19"
+ ],
+ "grammarNote": "说喜好用 I like + 东西;一般问句用 Do you like + 东西?",
+ "vocabulary": [
+ {
+ "id": "a0-09-like",
+ "word": "like",
+ "meaning": "喜欢",
+ "example": "I like tea.",
+ "exampleMeaning": "我喜欢茶。",
+ "ipa": "/laɪk/"
+ },
+ {
+ "id": "a0-09-tea",
+ "word": "tea",
+ "meaning": "茶",
+ "example": "Do you like tea?",
+ "exampleMeaning": "你喜欢茶吗?"
+ },
+ {
+ "id": "a0-09-music",
+ "word": "music",
+ "meaning": "音乐",
+ "example": "I like music.",
+ "exampleMeaning": "我喜欢音乐。",
+ "ipa": "/ˈmjuːzɪk/"
+ },
+ {
+ "id": "a0-09-movies",
+ "word": "movies",
+ "meaning": "电影",
+ "example": "Do you like movies?",
+ "exampleMeaning": "你喜欢看电影吗?",
+ "ipa": "/ˈmuːviz/"
+ }
+ ],
+ "sceneId": "a0-09-a",
+ "listening": {
+ "text": "Do you like tea? Yes, I do.",
+ "question": "对方喜欢茶吗?",
+ "options": [
+ "喜欢",
+ "不喜欢",
+ "不知道"
+ ]
+ },
+ "speaking": [
+ {
+ "text": "I like tea. Do you like tea?",
+ "tip": ""
+ }
+ ],
+ "reading": {
+ "text": "Mia: I like tea. Do you like tea?\nShen: No, I don’t. I like coffee and music.\nMia: Do you like movies?\nShen: Yes, I do.",
+ "question": "Shen 喜欢喝什么?",
+ "options": [
+ "Coffee",
+ "Tea",
+ "Music"
+ ]
+ },
+ "writing": {
+ "prompt": "写一句你的喜好。",
+ "example": "I like tea.",
+ "accept": [
+ [
+ "like"
+ ],
+ [
+ "#words:3"
+ ]
+ ],
+ "hint": "用 I like … 写出一种喜好。"
+ },
+ "independent": {
+ "prompt": "不看句框,说一个喜好并反问。",
+ "help": "I like … / Do you like …?",
+ "accept": [
+ [
+ "i like",
+ "i love"
+ ],
+ [
+ "do you like",
+ "#question"
+ ]
+ ]
+ }
+ }
+ ],
+ "scenes": [
+ {
+ "id": "a0-09-a",
+ "kind": "main",
+ "title": "我喜欢……",
+ "goal": "表达喜好、回答和反问",
+ "turns": [
+ {
+ "ai": "What do you like?",
+ "aiZh": "你喜欢什么?",
+ "goal": "说出你喜欢什么",
+ "model": "I like tea.",
+ "accept": [
+ "i like",
+ "i love"
+ ]
+ },
+ {
+ "ai": "Do you like tea?",
+ "aiZh": "你喜欢茶吗?",
+ "goal": "回答是否喜欢",
+ "model": "Yes, I do.",
+ "accept": [
+ "yes",
+ "no",
+ "i do",
+ "i don't",
+ "i do not"
+ ]
+ },
+ {
+ "ai": "Now ask me what I like.",
+ "aiZh": "现在请问我喜欢什么。",
+ "goal": "反问对方的喜好",
+ "model": "Do you like tea?",
+ "accept": [
+ "do you like",
+ "#question"
+ ]
+ },
+ {
+ "ai": "Say your like one more time.",
+ "aiZh": "请再说一次你的喜好。",
+ "goal": "再说一次你的喜好",
+ "model": "I like tea.",
+ "accept": [
+ "i like",
+ "i love"
+ ]
+ }
+ ]
+ },
+ {
+ "id": "a0-plan",
+ "kind": "variant",
+ "title": "约个时间",
+ "goal": "说明星期、时间和喜好",
+ "turns": [
+ {
+ "ai": "Hi! What day is it today?",
+ "aiZh": "嗨!今天星期几?",
+ "goal": "说出今天星期几",
+ "model": "It’s Friday.",
+ "accept": [
+ "monday",
+ "tuesday",
+ "wednesday",
+ "thursday",
+ "friday",
+ "saturday",
+ "sunday"
+ ]
+ },
+ {
+ "ai": "What time is it now?",
+ "aiZh": "现在几点?",
+ "goal": "说出现在几点",
+ "model": "It’s three o’clock.",
+ "accept": [
+ "o'clock",
+ "oclock",
+ "it's + #digit",
+ "it is + #digit"
+ ]
+ },
+ {
+ "ai": "Do you like coffee or tea?",
+ "aiZh": "你喜欢咖啡还是茶?",
+ "goal": "说出你的喜好",
+ "model": "I like tea.",
+ "accept": [
+ "i like",
+ "i love",
+ "yes",
+ "no",
+ "i don't"
+ ]
+ },
+ {
+ "ai": "Me too! Now ask me one question.",
+ "aiZh": "我也是!现在问我一个问题。",
+ "goal": "反问对方",
+ "model": "Do you like music?",
+ "accept": [
+ "#question"
+ ]
+ }
+ ],
+ "practice": {
+ "title": "约个时间",
+ "summary": "说星期和整点,回答喜好,并反问对方",
+ "recapPrompt": "用英语说今天星期几和现在几点。"
+ }
+ }
+ ],
+ "tasks": [],
+ "materials": []
+}
diff --git a/kouyu_english/assets/courses/A0/a0-10.json b/kouyu_english/assets/courses/A0/a0-10.json
new file mode 100644
index 0000000..8706baf
--- /dev/null
+++ b/kouyu_english/assets/courses/A0/a0-10.json
@@ -0,0 +1,207 @@
+{
+ "id": "a0-10",
+ "level": "A0",
+ "status": "approved",
+ "title": "A0 综合任务",
+ "canDo": "没听清时请对方重复,并完成自我介绍、喜好和反问",
+ "categories": [],
+ "coreItems": [
+ {
+ "id": "A0-W24",
+ "type": "word",
+ "en": "water",
+ "zh": "水",
+ "match": [
+ [
+ "water"
+ ]
+ ],
+ "review": {
+ "prompt": "把“水”写成英文。",
+ "dictation": {
+ "sentence": "I like water.",
+ "meaning": "我喜欢喝水。"
+ }
+ }
+ },
+ {
+ "id": "A0-P20",
+ "type": "phrase",
+ "en": "Please say that again.",
+ "match": [
+ [
+ "please say that again",
+ "please speak slowly"
+ ]
+ ],
+ "review": {
+ "prompt": "请对方再说一次。",
+ "dictation": {
+ "sentence": "Please say that again.",
+ "meaning": "请再说一遍。"
+ }
+ }
+ }
+ ],
+ "segments": [
+ {
+ "id": "a0-10-a",
+ "title": "A0 综合任务",
+ "minutes": 12,
+ "itemIds": [
+ "A0-W24",
+ "A0-P20"
+ ],
+ "grammarNote": "礼貌请求以 Please 开头:Please say that again.",
+ "vocabulary": [
+ {
+ "id": "a0-10-water",
+ "word": "water",
+ "meaning": "水",
+ "example": "I like water.",
+ "exampleMeaning": "我喜欢喝水。",
+ "ipa": "/ˈwɔːtər/"
+ },
+ {
+ "id": "a0-10-again",
+ "word": "Please say that again.",
+ "meaning": "请再说一遍。",
+ "example": "Please say that again.",
+ "exampleMeaning": "请再说一遍。"
+ },
+ {
+ "id": "a0-10-slowly",
+ "word": "Please speak slowly.",
+ "meaning": "请说慢一点。",
+ "example": "Please speak slowly.",
+ "exampleMeaning": "请说慢一点。"
+ }
+ ],
+ "sceneId": "a0-10-a",
+ "listening": {
+ "text": "Do you like water? Sorry, please say that again.",
+ "question": "第二个人希望对方做什么?",
+ "options": [
+ "再说一遍",
+ "说慢一点",
+ "给他一杯水"
+ ]
+ },
+ "speaking": [
+ {
+ "text": "Sorry, please say that again. I like water.",
+ "tip": "water 的 t 常读得很轻,像轻轻的 d。先清楚,不必快。"
+ }
+ ],
+ "reading": {
+ "text": "Alex: Hi! I’m Alex. Where are you from?\nShen: Sorry? Please say that again.\nAlex: Where are you from?\nShen: I’m from Hong Kong. Do you like coffee?\nAlex: No, I don’t. I like water.",
+ "question": "Alex 喜欢喝什么?",
+ "options": [
+ "Water",
+ "Coffee",
+ "Tea"
+ ]
+ },
+ "writing": {
+ "prompt": "写姓名、地点和喜好三句,喜好用 water。",
+ "example": "I’m Shen.\nI’m from Hong Kong.\nI like water.",
+ "accept": [
+ [
+ "i am",
+ "my name is"
+ ],
+ [
+ "from"
+ ],
+ [
+ "like"
+ ],
+ [
+ "water"
+ ]
+ ],
+ "hint": "分别写姓名、来自哪里和喜好三部分,喜好用 water:I like water."
+ },
+ "independent": {
+ "prompt": "不看帮助:先请对方再说一遍,再说姓名、喜欢喝什么,并反问对方。",
+ "help": "Please say that again. / I’m … / I like water. / Do you like …?",
+ "accept": [
+ [
+ "say that again",
+ "speak slowly",
+ "pardon"
+ ],
+ [
+ "i'm + #word",
+ "i am + #word",
+ "my name is",
+ "my name's"
+ ],
+ [
+ "i like",
+ "i love"
+ ],
+ [
+ "#question"
+ ]
+ ]
+ }
+ }
+ ],
+ "scenes": [
+ {
+ "id": "a0-10-a",
+ "kind": "main",
+ "title": "A0 综合任务",
+ "goal": "请求重复或放慢语速,并完成基础沟通",
+ "turns": [
+ {
+ "ai": "Say: Please say that again.",
+ "aiZh": "请说:Please say that again(请再说一遍)。",
+ "goal": "请求对方重复",
+ "model": "Please say that again.",
+ "accept": [
+ "say that again",
+ "again",
+ "pardon"
+ ]
+ },
+ {
+ "ai": "Say: Please speak slowly.",
+ "aiZh": "请说:Please speak slowly(请说慢一点)。",
+ "goal": "请求对方放慢语速",
+ "model": "Please speak slowly.",
+ "accept": [
+ "slowly",
+ "slow"
+ ]
+ },
+ {
+ "ai": "Now ask me your name or where I am from.",
+ "aiZh": "现在请问我的名字或我来自哪里。",
+ "goal": "反问名字或来自哪里",
+ "model": "What’s your name?",
+ "accept": [
+ "what's your name",
+ "what is your name",
+ "where are you from",
+ "your name",
+ "#question"
+ ]
+ },
+ {
+ "ai": "Tell me what you like to drink.",
+ "aiZh": "告诉我你喜欢喝什么。",
+ "goal": "说出你喜欢喝什么",
+ "model": "I like water.",
+ "accept": [
+ "i like",
+ "i love"
+ ]
+ }
+ ]
+ }
+ ],
+ "tasks": [],
+ "materials": []
+}
diff --git a/kouyu_english/assets/courses/A0/assessment.json b/kouyu_english/assets/courses/A0/assessment.json
new file mode 100644
index 0000000..e8c26f7
--- /dev/null
+++ b/kouyu_english/assets/courses/A0/assessment.json
@@ -0,0 +1,1612 @@
+{
+ "schemaVersion": 1,
+ "note": "A0 阶段测评:两套正式题(A0-E1、A0-E2,按顺序解锁)和各自的替换题(replaces 指向原题,用不同的人名、地点、数字等,考同样的能力)。开放题的 accept 用 answer_rules.dart 的规则写法,guide 是答错后展示的要求。",
+ "packs": [
+ {
+ "id": "A0-E1",
+ "level": "A0",
+ "tasks": [
+ {
+ "id": "A0-A-L1",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "Hello. I’m Mia.",
+ "choices": [
+ "自我介绍",
+ "买东西",
+ "问时间"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-L2",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "How do you spell your name?",
+ "choices": [
+ "问姓名拼写",
+ "问地点",
+ "问时间"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-L3",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "I’m tired today.",
+ "choices": [
+ "很好",
+ "累",
+ "来自哪里"
+ ],
+ "answerIndex": 1
+ },
+ {
+ "id": "A0-A-L4",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "seven",
+ "choices": [
+ "6",
+ "7",
+ "8"
+ ],
+ "answerIndex": 1
+ },
+ {
+ "id": "A0-A-L5",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "one-three-eight",
+ "choices": [
+ "138",
+ "183",
+ "318"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-L6",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "It’s a key.",
+ "choices": [
+ "书",
+ "钥匙",
+ "包"
+ ],
+ "answerIndex": 1
+ },
+ {
+ "id": "A0-A-L7",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "I’m from Hong Kong.",
+ "choices": [
+ "香港",
+ "伦敦",
+ "北京"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-L8",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "This is my mother.",
+ "choices": [
+ "妈妈",
+ "姐姐",
+ "朋友"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-L9",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "It is Monday.",
+ "choices": [
+ "星期一",
+ "星期二",
+ "星期三"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-L10",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "I like tea.",
+ "choices": [
+ "茶",
+ "咖啡",
+ "电影"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-R1",
+ "skill": "reading",
+ "prompt": "Mia: I’m from Hong Kong.\nShen: I’m good.\nMia: My number is one-three-eight.",
+ "choices": [
+ "Mia 来自哪里?",
+ "Hong Kong"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-R2",
+ "skill": "reading",
+ "prompt": "Mia: It is Monday.\nShen: It is three o’clock.",
+ "choices": [
+ "几点?",
+ "three"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-R3",
+ "skill": "reading",
+ "prompt": "Mia: This is my brother.\nShen: Nice to meet you.",
+ "choices": [
+ "谁是家人?",
+ "brother"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-R4",
+ "skill": "reading",
+ "prompt": "Mia: I like music.\nShen: I like tea.",
+ "choices": [
+ "Shen 喜欢什么?",
+ "tea"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-R5",
+ "skill": "reading",
+ "prompt": "Mia: What’s this?\nShen: It’s a pen.",
+ "choices": [
+ "这是什么?",
+ "pen"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-W1",
+ "skill": "writing",
+ "prompt": "用英语介绍自己。",
+ "accept": [
+ [
+ "hello",
+ "hi"
+ ],
+ [
+ "i am",
+ "my name is"
+ ]
+ ],
+ "guide": "试着同时写一句问候和姓名,例如:Hello. I’m …"
+ },
+ {
+ "id": "A0-A-W2",
+ "skill": "writing",
+ "prompt": "用英语说明你来自哪里。",
+ "accept": [
+ [
+ "i am",
+ "my name is"
+ ],
+ [
+ "/\\bfrom [a-z]{2,}/"
+ ]
+ ],
+ "guide": "用 I’m from … 写成一个完整句子。"
+ },
+ {
+ "id": "A0-A-W3",
+ "skill": "writing",
+ "prompt": "用英语说一个喜好。",
+ "accept": [
+ [
+ "like"
+ ],
+ [
+ "#words:3"
+ ]
+ ],
+ "guide": "用 I like … 写出一种喜好。"
+ },
+ {
+ "id": "A0-A-W4",
+ "skill": "writing",
+ "prompt": "用英语说“这是一支笔”。",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "book",
+ "pen",
+ "bag",
+ "key"
+ ]
+ ],
+ "guide": "用 It’s / It is 加一个物品,例如 a pen。"
+ },
+ {
+ "id": "A0-A-W5",
+ "skill": "writing",
+ "prompt": "用英语说明星期或整点。",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "monday",
+ "tuesday",
+ "wednesday",
+ "thursday",
+ "friday",
+ "saturday",
+ "sunday",
+ "clock",
+ "oclock"
+ ]
+ ],
+ "guide": "用 It’s … 写一个星期或整点时间。"
+ },
+ {
+ "id": "A0-A-S1",
+ "skill": "speaking",
+ "prompt": "介绍姓名、地点、状态/喜好并反问。",
+ "accept": [
+ [
+ "hello",
+ "hi"
+ ],
+ [
+ "i am",
+ "my name is"
+ ],
+ [
+ "from"
+ ],
+ [
+ "like",
+ "good",
+ "okay",
+ "tired"
+ ],
+ [
+ "what",
+ "where",
+ "how",
+ "do"
+ ]
+ ],
+ "guide": "需要:打招呼 + I'm / My name is + from + 状态或喜好 + 一句反问,如 Hello, I'm Alex. I'm from China. I like tea. What about you?"
+ },
+ {
+ "id": "A0-A-S2",
+ "skill": "speaking",
+ "prompt": "完整拼读一个四字母姓名。",
+ "accept": [
+ [
+ "/[a-z](?:[ -]?[a-z]){3,}/"
+ ]
+ ],
+ "guide": "需要逐个字母拼出一个四字母姓名,如 A-L-E-X。"
+ },
+ {
+ "id": "A0-A-S3",
+ "skill": "speaking",
+ "prompt": "说一个三位虚拟号码。",
+ "accept": [
+ [
+ "#numbers:3"
+ ]
+ ],
+ "guide": "需要说出至少三个英文数字,如 one-three-eight。"
+ },
+ {
+ "id": "A0-A-S4",
+ "skill": "speaking",
+ "prompt": "介绍一位家人。",
+ "accept": [
+ [
+ "this is my"
+ ],
+ [
+ "mother",
+ "father",
+ "sister",
+ "brother"
+ ]
+ ],
+ "guide": "需要:This is my + 家人,如 This is my mother."
+ },
+ {
+ "id": "A0-A-S5",
+ "skill": "speaking",
+ "prompt": "说明星期。",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "monday",
+ "tuesday",
+ "wednesday",
+ "thursday",
+ "friday",
+ "saturday",
+ "sunday"
+ ]
+ ],
+ "guide": "需要:It's + 星期,如 It's Monday."
+ },
+ {
+ "id": "A0-A-S6",
+ "skill": "speaking",
+ "prompt": "说明整点。",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "clock",
+ "oclock"
+ ]
+ ],
+ "guide": "需要:It's + 整点,如 It's three o'clock."
+ },
+ {
+ "id": "A0-A-S7",
+ "skill": "speaking",
+ "prompt": "请求对方重复或放慢。",
+ "accept": [
+ [
+ "please"
+ ],
+ [
+ "say",
+ "speak",
+ "repeat",
+ "slow",
+ "slowly"
+ ]
+ ],
+ "guide": "需要:please + say / speak / repeat / slowly,如 Please say that again."
+ },
+ {
+ "id": "A0-A-S8",
+ "skill": "speaking",
+ "prompt": "说出一个物品。",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "book",
+ "pen",
+ "bag",
+ "key",
+ "water"
+ ]
+ ],
+ "guide": "需要:It's + 物品,如 It's a pen."
+ }
+ ]
+ },
+ {
+ "id": "A0-E2",
+ "level": "A0",
+ "tasks": [
+ {
+ "id": "A0-B-L1",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "Hi. My name is Leo.",
+ "choices": [
+ "自我介绍",
+ "买东西",
+ "问号码"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-B-L2",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "How do you spell that?",
+ "choices": [
+ "问姓名拼写",
+ "问喜好",
+ "问家人"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-B-L3",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "I’m okay.",
+ "choices": [
+ "很好",
+ "还可以",
+ "累"
+ ],
+ "answerIndex": 1
+ },
+ {
+ "id": "A0-B-L4",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "four",
+ "choices": [
+ "3",
+ "4",
+ "5"
+ ],
+ "answerIndex": 1
+ },
+ {
+ "id": "A0-B-L5",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "two-nine-six",
+ "choices": [
+ "296",
+ "269",
+ "926"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-B-L6",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "It’s a bag.",
+ "choices": [
+ "笔",
+ "包",
+ "水"
+ ],
+ "answerIndex": 1
+ },
+ {
+ "id": "A0-B-L7",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "I’m from London.",
+ "choices": [
+ "伦敦",
+ "香港",
+ "北京"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-B-L8",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "This is my sister.",
+ "choices": [
+ "姐姐",
+ "妈妈",
+ "朋友"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-B-L9",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "It is Friday.",
+ "choices": [
+ "星期五",
+ "星期一",
+ "星期日"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-B-L10",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "I like coffee.",
+ "choices": [
+ "茶",
+ "咖啡",
+ "音乐"
+ ],
+ "answerIndex": 1
+ },
+ {
+ "id": "A0-B-R1",
+ "skill": "reading",
+ "prompt": "Mia: I’m from London.\nLeo: I’m okay.\nMia: My number is two-nine-six.",
+ "choices": [
+ "号码是多少?",
+ "296"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-B-R2",
+ "skill": "reading",
+ "prompt": "Mia: It is Friday.\nLeo: It is six o’clock.",
+ "choices": [
+ "几点?",
+ "six"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-B-R3",
+ "skill": "reading",
+ "prompt": "Mia: This is my father.\nLeo: Hi!",
+ "choices": [
+ "谁是家人?",
+ "father"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-B-R4",
+ "skill": "reading",
+ "prompt": "Mia: I like movies.\nLeo: I like coffee.",
+ "choices": [
+ "Leo 喜欢什么?",
+ "coffee"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-B-R5",
+ "skill": "reading",
+ "prompt": "Mia: What’s this?\nLeo: It’s water.",
+ "choices": [
+ "这是什么?",
+ "water"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-B-W1",
+ "skill": "writing",
+ "prompt": "用英语介绍自己。",
+ "accept": [
+ [
+ "hello",
+ "hi"
+ ],
+ [
+ "i am",
+ "my name is"
+ ]
+ ],
+ "guide": "试着同时写一句问候和姓名,例如:Hello. I’m …"
+ },
+ {
+ "id": "A0-B-W2",
+ "skill": "writing",
+ "prompt": "用英语说明你来自哪里。",
+ "accept": [
+ [
+ "i am",
+ "my name is"
+ ],
+ [
+ "/\\bfrom [a-z]{2,}/"
+ ]
+ ],
+ "guide": "用 I’m from … 写成一个完整句子。"
+ },
+ {
+ "id": "A0-B-W3",
+ "skill": "writing",
+ "prompt": "用英语说一个喜好。",
+ "accept": [
+ [
+ "like"
+ ],
+ [
+ "#words:3"
+ ]
+ ],
+ "guide": "用 I like … 写出一种喜好。"
+ },
+ {
+ "id": "A0-B-W4",
+ "skill": "writing",
+ "prompt": "用英语说“这是一个包”。",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "book",
+ "pen",
+ "bag",
+ "key"
+ ]
+ ],
+ "guide": "用 It’s / It is 加一个物品,例如 a pen。"
+ },
+ {
+ "id": "A0-B-W5",
+ "skill": "writing",
+ "prompt": "用英语说明星期或整点。",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "monday",
+ "tuesday",
+ "wednesday",
+ "thursday",
+ "friday",
+ "saturday",
+ "sunday",
+ "clock",
+ "oclock"
+ ]
+ ],
+ "guide": "用 It’s … 写一个星期或整点时间。"
+ },
+ {
+ "id": "A0-B-S1",
+ "skill": "speaking",
+ "prompt": "介绍姓名、地点、状态/喜好并反问。",
+ "accept": [
+ [
+ "hello",
+ "hi"
+ ],
+ [
+ "i am",
+ "my name is"
+ ],
+ [
+ "from"
+ ],
+ [
+ "like",
+ "good",
+ "okay",
+ "tired"
+ ],
+ [
+ "what",
+ "where",
+ "how",
+ "do"
+ ]
+ ],
+ "guide": "需要:打招呼 + I'm / My name is + from + 状态或喜好 + 一句反问,如 Hello, I'm Alex. I'm from China. I like tea. What about you?"
+ },
+ {
+ "id": "A0-B-S2",
+ "skill": "speaking",
+ "prompt": "完整拼读一个四字母姓名。",
+ "accept": [
+ [
+ "/[a-z](?:[ -]?[a-z]){3,}/"
+ ]
+ ],
+ "guide": "需要逐个字母拼出一个四字母姓名,如 A-L-E-X。"
+ },
+ {
+ "id": "A0-B-S3",
+ "skill": "speaking",
+ "prompt": "说一个三位虚拟号码。",
+ "accept": [
+ [
+ "#numbers:3"
+ ]
+ ],
+ "guide": "需要说出至少三个英文数字,如 one-three-eight。"
+ },
+ {
+ "id": "A0-B-S4",
+ "skill": "speaking",
+ "prompt": "介绍一位家人。",
+ "accept": [
+ [
+ "this is my"
+ ],
+ [
+ "mother",
+ "father",
+ "sister",
+ "brother"
+ ]
+ ],
+ "guide": "需要:This is my + 家人,如 This is my mother."
+ },
+ {
+ "id": "A0-B-S5",
+ "skill": "speaking",
+ "prompt": "说明星期。",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "monday",
+ "tuesday",
+ "wednesday",
+ "thursday",
+ "friday",
+ "saturday",
+ "sunday"
+ ]
+ ],
+ "guide": "需要:It's + 星期,如 It's Monday."
+ },
+ {
+ "id": "A0-B-S6",
+ "skill": "speaking",
+ "prompt": "说明整点。",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "clock",
+ "oclock"
+ ]
+ ],
+ "guide": "需要:It's + 整点,如 It's three o'clock."
+ },
+ {
+ "id": "A0-B-S7",
+ "skill": "speaking",
+ "prompt": "请求对方重复或放慢。",
+ "accept": [
+ [
+ "please"
+ ],
+ [
+ "say",
+ "speak",
+ "repeat",
+ "slow",
+ "slowly"
+ ]
+ ],
+ "guide": "需要:please + say / speak / repeat / slowly,如 Please say that again."
+ },
+ {
+ "id": "A0-B-S8",
+ "skill": "speaking",
+ "prompt": "说出一个物品。",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "book",
+ "pen",
+ "bag",
+ "key",
+ "water"
+ ]
+ ],
+ "guide": "需要:It's + 物品,如 It's a pen."
+ }
+ ]
+ },
+ {
+ "id": "A0-E1R",
+ "level": "A0",
+ "replaces": "A0-E1",
+ "tasks": [
+ {
+ "id": "A0-A-L1",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "Hello. I’m Nora.",
+ "choices": [
+ "自我介绍",
+ "买东西",
+ "问时间"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-L2",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "How do you spell your name?",
+ "choices": [
+ "问姓名拼写",
+ "问地点",
+ "问时间"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-L3",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "I’m tired today.",
+ "choices": [
+ "很好",
+ "累",
+ "来自哪里"
+ ],
+ "answerIndex": 1
+ },
+ {
+ "id": "A0-A-L4",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "seven",
+ "choices": [
+ "6",
+ "7",
+ "8"
+ ],
+ "answerIndex": 1
+ },
+ {
+ "id": "A0-A-L5",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "two-four-seven",
+ "choices": [
+ "247",
+ "183",
+ "318"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-L6",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "It’s a bag.",
+ "choices": [
+ "书",
+ "钥匙",
+ "包"
+ ],
+ "answerIndex": 1
+ },
+ {
+ "id": "A0-A-L7",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "I’m from London.",
+ "choices": [
+ "香港",
+ "伦敦",
+ "北京"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-L8",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "This is my father.",
+ "choices": [
+ "妈妈",
+ "姐姐",
+ "朋友"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-L9",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "It is Friday.",
+ "choices": [
+ "星期一",
+ "星期二",
+ "星期三"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-L10",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "I like coffee.",
+ "choices": [
+ "茶",
+ "咖啡",
+ "电影"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-R1",
+ "skill": "reading",
+ "prompt": "Nora: I’m from London.\nKai: I’m good.\nNora: My number is two-four-seven.",
+ "choices": [
+ "Nora 来自哪里?",
+ "London"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-R2",
+ "skill": "reading",
+ "prompt": "Nora: It is Friday.\nKai: It is six o’clock.",
+ "choices": [
+ "几点?",
+ "three"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-R3",
+ "skill": "reading",
+ "prompt": "Nora: This is my brother.\nKai: Nice to meet you.",
+ "choices": [
+ "谁是家人?",
+ "brother"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-R4",
+ "skill": "reading",
+ "prompt": "Nora: I like music.\nKai: I like coffee.",
+ "choices": [
+ "Kai 喜欢什么?",
+ "coffee"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-R5",
+ "skill": "reading",
+ "prompt": "Nora: What’s this?\nKai: It’s a pen.",
+ "choices": [
+ "这是什么?",
+ "pen"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-A-W1",
+ "skill": "writing",
+ "prompt": "用英语介绍自己。",
+ "accept": [
+ [
+ "hello",
+ "hi"
+ ],
+ [
+ "i am",
+ "my name is"
+ ]
+ ],
+ "guide": "试着同时写一句问候和姓名,例如:Hello. I’m …"
+ },
+ {
+ "id": "A0-A-W2",
+ "skill": "writing",
+ "prompt": "用英语说明你来自哪里。",
+ "accept": [
+ [
+ "i am",
+ "my name is"
+ ],
+ [
+ "/\\bfrom [a-z]{2,}/"
+ ]
+ ],
+ "guide": "用 I’m from … 写成一个完整句子。"
+ },
+ {
+ "id": "A0-A-W3",
+ "skill": "writing",
+ "prompt": "用英语说一个喜好。",
+ "accept": [
+ [
+ "like"
+ ],
+ [
+ "#words:3"
+ ]
+ ],
+ "guide": "用 I like … 写出一种喜好。"
+ },
+ {
+ "id": "A0-A-W4",
+ "skill": "writing",
+ "prompt": "用英语说“这是一支笔”。",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "book",
+ "pen",
+ "bag",
+ "key"
+ ]
+ ],
+ "guide": "用 It’s / It is 加一个物品,例如 a pen。"
+ },
+ {
+ "id": "A0-A-W5",
+ "skill": "writing",
+ "prompt": "用英语说明星期或整点。",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "monday",
+ "tuesday",
+ "wednesday",
+ "thursday",
+ "friday",
+ "saturday",
+ "sunday",
+ "clock",
+ "oclock"
+ ]
+ ],
+ "guide": "用 It’s … 写一个星期或整点时间。"
+ },
+ {
+ "id": "A0-A-S1",
+ "skill": "speaking",
+ "prompt": "介绍姓名、地点、状态/喜好并反问。",
+ "accept": [
+ [
+ "hello",
+ "hi"
+ ],
+ [
+ "i am",
+ "my name is"
+ ],
+ [
+ "from"
+ ],
+ [
+ "like",
+ "good",
+ "okay",
+ "tired"
+ ],
+ [
+ "what",
+ "where",
+ "how",
+ "do"
+ ]
+ ],
+ "guide": "需要:打招呼 + I'm / My name is + from + 状态或喜好 + 一句反问,如 Hello, I'm Alex. I'm from China. I like tea. What about you?"
+ },
+ {
+ "id": "A0-A-S2",
+ "skill": "speaking",
+ "prompt": "完整拼读一个四字母姓名。",
+ "accept": [
+ [
+ "/[a-z](?:[ -]?[a-z]){3,}/"
+ ]
+ ],
+ "guide": "需要逐个字母拼出一个四字母姓名,如 A-L-E-X。"
+ },
+ {
+ "id": "A0-A-S3",
+ "skill": "speaking",
+ "prompt": "说一个三位虚拟号码。",
+ "accept": [
+ [
+ "#numbers:3"
+ ]
+ ],
+ "guide": "需要说出至少三个英文数字,如 one-three-eight。"
+ },
+ {
+ "id": "A0-A-S4",
+ "skill": "speaking",
+ "prompt": "介绍一位家人。",
+ "accept": [
+ [
+ "this is my"
+ ],
+ [
+ "mother",
+ "father",
+ "sister",
+ "brother"
+ ]
+ ],
+ "guide": "需要:This is my + 家人,如 This is my mother."
+ },
+ {
+ "id": "A0-A-S5",
+ "skill": "speaking",
+ "prompt": "说明星期。",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "monday",
+ "tuesday",
+ "wednesday",
+ "thursday",
+ "friday",
+ "saturday",
+ "sunday"
+ ]
+ ],
+ "guide": "需要:It's + 星期,如 It's Monday."
+ },
+ {
+ "id": "A0-A-S6",
+ "skill": "speaking",
+ "prompt": "说明整点。",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "clock",
+ "oclock"
+ ]
+ ],
+ "guide": "需要:It's + 整点,如 It's three o'clock."
+ },
+ {
+ "id": "A0-A-S7",
+ "skill": "speaking",
+ "prompt": "请求对方重复或放慢。",
+ "accept": [
+ [
+ "please"
+ ],
+ [
+ "say",
+ "speak",
+ "repeat",
+ "slow",
+ "slowly"
+ ]
+ ],
+ "guide": "需要:please + say / speak / repeat / slowly,如 Please say that again."
+ },
+ {
+ "id": "A0-A-S8",
+ "skill": "speaking",
+ "prompt": "说出一个物品。",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "book",
+ "pen",
+ "bag",
+ "key",
+ "water"
+ ]
+ ],
+ "guide": "需要:It's + 物品,如 It's a pen."
+ }
+ ]
+ },
+ {
+ "id": "A0-E2R",
+ "level": "A0",
+ "replaces": "A0-E2",
+ "tasks": [
+ {
+ "id": "A0-B-L1",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "Hi. My name is Bo.",
+ "choices": [
+ "自我介绍",
+ "买东西",
+ "问号码"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-B-L2",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "How do you spell that?",
+ "choices": [
+ "问姓名拼写",
+ "问喜好",
+ "问家人"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-B-L3",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "I’m okay.",
+ "choices": [
+ "很好",
+ "还可以",
+ "累"
+ ],
+ "answerIndex": 1
+ },
+ {
+ "id": "A0-B-L4",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "four",
+ "choices": [
+ "3",
+ "4",
+ "5"
+ ],
+ "answerIndex": 1
+ },
+ {
+ "id": "A0-B-L5",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "five-one-four",
+ "choices": [
+ "514",
+ "269",
+ "926"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-B-L6",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "It’s a book.",
+ "choices": [
+ "笔",
+ "包",
+ "水"
+ ],
+ "answerIndex": 1
+ },
+ {
+ "id": "A0-B-L7",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "I’m from Hong Kong.",
+ "choices": [
+ "伦敦",
+ "香港",
+ "北京"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-B-L8",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "This is my brother.",
+ "choices": [
+ "姐姐",
+ "妈妈",
+ "朋友"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-B-L9",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "It is Tuesday.",
+ "choices": [
+ "星期五",
+ "星期一",
+ "星期日"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-B-L10",
+ "skill": "listening",
+ "prompt": "选择正确答案",
+ "audio": "I like music.",
+ "choices": [
+ "茶",
+ "咖啡",
+ "音乐"
+ ],
+ "answerIndex": 1
+ },
+ {
+ "id": "A0-B-R1",
+ "skill": "reading",
+ "prompt": "Ava: I’m from Hong Kong.\nBo: I’m okay.\nAva: My number is five-one-four.",
+ "choices": [
+ "号码是多少?",
+ "514"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-B-R2",
+ "skill": "reading",
+ "prompt": "Ava: It is Tuesday.\nBo: It is two o’clock.",
+ "choices": [
+ "几点?",
+ "six"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-B-R3",
+ "skill": "reading",
+ "prompt": "Ava: This is my father.\nBo: Hi!",
+ "choices": [
+ "谁是家人?",
+ "father"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-B-R4",
+ "skill": "reading",
+ "prompt": "Ava: I like movies.\nBo: I like music.",
+ "choices": [
+ "Bo 喜欢什么?",
+ "music"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-B-R5",
+ "skill": "reading",
+ "prompt": "Ava: What’s this?\nBo: It’s water.",
+ "choices": [
+ "这是什么?",
+ "water"
+ ],
+ "answerIndex": 0
+ },
+ {
+ "id": "A0-B-W1",
+ "skill": "writing",
+ "prompt": "用英语介绍自己。",
+ "accept": [
+ [
+ "hello",
+ "hi"
+ ],
+ [
+ "i am",
+ "my name is"
+ ]
+ ],
+ "guide": "试着同时写一句问候和姓名,例如:Hello. I’m …"
+ },
+ {
+ "id": "A0-B-W2",
+ "skill": "writing",
+ "prompt": "用英语说明你来自哪里。",
+ "accept": [
+ [
+ "i am",
+ "my name is"
+ ],
+ [
+ "/\\bfrom [a-z]{2,}/"
+ ]
+ ],
+ "guide": "用 I’m from … 写成一个完整句子。"
+ },
+ {
+ "id": "A0-B-W3",
+ "skill": "writing",
+ "prompt": "用英语说一个喜好。",
+ "accept": [
+ [
+ "like"
+ ],
+ [
+ "#words:3"
+ ]
+ ],
+ "guide": "用 I like … 写出一种喜好。"
+ },
+ {
+ "id": "A0-B-W4",
+ "skill": "writing",
+ "prompt": "用英语说“这是一个包”。",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "book",
+ "pen",
+ "bag",
+ "key"
+ ]
+ ],
+ "guide": "用 It’s / It is 加一个物品,例如 a pen。"
+ },
+ {
+ "id": "A0-B-W5",
+ "skill": "writing",
+ "prompt": "用英语说明星期或整点。",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "monday",
+ "tuesday",
+ "wednesday",
+ "thursday",
+ "friday",
+ "saturday",
+ "sunday",
+ "clock",
+ "oclock"
+ ]
+ ],
+ "guide": "用 It’s … 写一个星期或整点时间。"
+ },
+ {
+ "id": "A0-B-S1",
+ "skill": "speaking",
+ "prompt": "介绍姓名、地点、状态/喜好并反问。",
+ "accept": [
+ [
+ "hello",
+ "hi"
+ ],
+ [
+ "i am",
+ "my name is"
+ ],
+ [
+ "from"
+ ],
+ [
+ "like",
+ "good",
+ "okay",
+ "tired"
+ ],
+ [
+ "what",
+ "where",
+ "how",
+ "do"
+ ]
+ ],
+ "guide": "需要:打招呼 + I'm / My name is + from + 状态或喜好 + 一句反问,如 Hello, I'm Alex. I'm from China. I like tea. What about you?"
+ },
+ {
+ "id": "A0-B-S2",
+ "skill": "speaking",
+ "prompt": "完整拼读一个四字母姓名。",
+ "accept": [
+ [
+ "/[a-z](?:[ -]?[a-z]){3,}/"
+ ]
+ ],
+ "guide": "需要逐个字母拼出一个四字母姓名,如 A-L-E-X。"
+ },
+ {
+ "id": "A0-B-S3",
+ "skill": "speaking",
+ "prompt": "说一个三位虚拟号码。",
+ "accept": [
+ [
+ "#numbers:3"
+ ]
+ ],
+ "guide": "需要说出至少三个英文数字,如 one-three-eight。"
+ },
+ {
+ "id": "A0-B-S4",
+ "skill": "speaking",
+ "prompt": "介绍一位家人。",
+ "accept": [
+ [
+ "this is my"
+ ],
+ [
+ "mother",
+ "father",
+ "sister",
+ "brother"
+ ]
+ ],
+ "guide": "需要:This is my + 家人,如 This is my mother."
+ },
+ {
+ "id": "A0-B-S5",
+ "skill": "speaking",
+ "prompt": "说明星期。",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "monday",
+ "tuesday",
+ "wednesday",
+ "thursday",
+ "friday",
+ "saturday",
+ "sunday"
+ ]
+ ],
+ "guide": "需要:It's + 星期,如 It's Monday."
+ },
+ {
+ "id": "A0-B-S6",
+ "skill": "speaking",
+ "prompt": "说明整点。",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "clock",
+ "oclock"
+ ]
+ ],
+ "guide": "需要:It's + 整点,如 It's three o'clock."
+ },
+ {
+ "id": "A0-B-S7",
+ "skill": "speaking",
+ "prompt": "请求对方重复或放慢。",
+ "accept": [
+ [
+ "please"
+ ],
+ [
+ "say",
+ "speak",
+ "repeat",
+ "slow",
+ "slowly"
+ ]
+ ],
+ "guide": "需要:please + say / speak / repeat / slowly,如 Please say that again."
+ },
+ {
+ "id": "A0-B-S8",
+ "skill": "speaking",
+ "prompt": "说出一个物品。",
+ "accept": [
+ [
+ "it is"
+ ],
+ [
+ "book",
+ "pen",
+ "bag",
+ "key",
+ "water"
+ ]
+ ],
+ "guide": "需要:It's + 物品,如 It's a pen."
+ }
+ ]
+ }
+ ]
+}
diff --git a/kouyu_english/assets/courses/A1/A1-U01.json b/kouyu_english/assets/courses/A1/A1-U01.json
index 02dc658..497d9a9 100644
--- a/kouyu_english/assets/courses/A1/A1-U01.json
+++ b/kouyu_english/assets/courses/A1/A1-U01.json
@@ -682,7 +682,8 @@
"A1-U01-P08"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A1-U01-C2",
diff --git a/kouyu_english/assets/courses/A1/A1-U02.json b/kouyu_english/assets/courses/A1/A1-U02.json
index 537299d..3dbbfd7 100644
--- a/kouyu_english/assets/courses/A1/A1-U02.json
+++ b/kouyu_english/assets/courses/A1/A1-U02.json
@@ -614,7 +614,8 @@
"A1-U02-P06"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A1-U02-C2",
diff --git a/kouyu_english/assets/courses/A1/A1-U03.json b/kouyu_english/assets/courses/A1/A1-U03.json
index d8abc0a..0f394a2 100644
--- a/kouyu_english/assets/courses/A1/A1-U03.json
+++ b/kouyu_english/assets/courses/A1/A1-U03.json
@@ -653,7 +653,8 @@
"A1-U03-P06"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A1-U03-C2",
diff --git a/kouyu_english/assets/courses/A1/A1-U04.json b/kouyu_english/assets/courses/A1/A1-U04.json
index cc22715..733a72e 100644
--- a/kouyu_english/assets/courses/A1/A1-U04.json
+++ b/kouyu_english/assets/courses/A1/A1-U04.json
@@ -698,7 +698,8 @@
"zh": "太好了,非常感谢!",
"itemIds": []
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A1-U04-C2",
diff --git a/kouyu_english/assets/courses/A1/A1-U05.json b/kouyu_english/assets/courses/A1/A1-U05.json
index 9364ff8..5a49c21 100644
--- a/kouyu_english/assets/courses/A1/A1-U05.json
+++ b/kouyu_english/assets/courses/A1/A1-U05.json
@@ -673,7 +673,8 @@
"A1-U05-P05"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A1-U05-C2",
diff --git a/kouyu_english/assets/courses/A1/A1-U06.json b/kouyu_english/assets/courses/A1/A1-U06.json
index 32938ae..3740d7b 100644
--- a/kouyu_english/assets/courses/A1/A1-U06.json
+++ b/kouyu_english/assets/courses/A1/A1-U06.json
@@ -736,7 +736,8 @@
"A1-U06-W07"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A1-U06-C2",
diff --git a/kouyu_english/assets/courses/A1/A1-U07.json b/kouyu_english/assets/courses/A1/A1-U07.json
index 00110f3..60bd38a 100644
--- a/kouyu_english/assets/courses/A1/A1-U07.json
+++ b/kouyu_english/assets/courses/A1/A1-U07.json
@@ -683,7 +683,8 @@
"zh": "谢谢!",
"itemIds": []
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A1-U07-C2",
diff --git a/kouyu_english/assets/courses/A1/A1-U08.json b/kouyu_english/assets/courses/A1/A1-U08.json
index 9931eec..f5eac32 100644
--- a/kouyu_english/assets/courses/A1/A1-U08.json
+++ b/kouyu_english/assets/courses/A1/A1-U08.json
@@ -682,7 +682,8 @@
"A1-U08-P07"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A1-U08-C2",
diff --git a/kouyu_english/assets/courses/A1/A1-U09.json b/kouyu_english/assets/courses/A1/A1-U09.json
index dfd06be..d981742 100644
--- a/kouyu_english/assets/courses/A1/A1-U09.json
+++ b/kouyu_english/assets/courses/A1/A1-U09.json
@@ -721,7 +721,8 @@
"A1-U09-W06"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A1-U09-C2",
diff --git a/kouyu_english/assets/courses/A1/A1-U10.json b/kouyu_english/assets/courses/A1/A1-U10.json
index 46be323..c924aea 100644
--- a/kouyu_english/assets/courses/A1/A1-U10.json
+++ b/kouyu_english/assets/courses/A1/A1-U10.json
@@ -657,7 +657,8 @@
"A1-U10-P07"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A1-U10-C2",
diff --git a/kouyu_english/assets/courses/A1/A1-U11.json b/kouyu_english/assets/courses/A1/A1-U11.json
index 83494e4..ad2e96e 100644
--- a/kouyu_english/assets/courses/A1/A1-U11.json
+++ b/kouyu_english/assets/courses/A1/A1-U11.json
@@ -706,7 +706,8 @@
"A1-U11-W03"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A1-U11-C2",
diff --git a/kouyu_english/assets/courses/A1/A1-U12.json b/kouyu_english/assets/courses/A1/A1-U12.json
index 423d514..e330a3e 100644
--- a/kouyu_english/assets/courses/A1/A1-U12.json
+++ b/kouyu_english/assets/courses/A1/A1-U12.json
@@ -723,7 +723,8 @@
"zh": "我要了。",
"itemIds": []
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A1-U12-C2",
diff --git a/kouyu_english/assets/courses/A1/A1-U13.json b/kouyu_english/assets/courses/A1/A1-U13.json
index 7d56bba..66959a8 100644
--- a/kouyu_english/assets/courses/A1/A1-U13.json
+++ b/kouyu_english/assets/courses/A1/A1-U13.json
@@ -728,7 +728,8 @@
"A1-U13-P09"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A1-U13-C2",
diff --git a/kouyu_english/assets/courses/A1/A1-U14.json b/kouyu_english/assets/courses/A1/A1-U14.json
index bd987dd..64f00da 100644
--- a/kouyu_english/assets/courses/A1/A1-U14.json
+++ b/kouyu_english/assets/courses/A1/A1-U14.json
@@ -704,7 +704,8 @@
"A1-U14-P11"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A1-U14-C2",
diff --git a/kouyu_english/assets/courses/A1/A1-U15.json b/kouyu_english/assets/courses/A1/A1-U15.json
index 13c3310..bea5353 100644
--- a/kouyu_english/assets/courses/A1/A1-U15.json
+++ b/kouyu_english/assets/courses/A1/A1-U15.json
@@ -666,7 +666,8 @@
"A1-U15-P09"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A1-U15-C2",
diff --git a/kouyu_english/assets/courses/A1/A1-U16.json b/kouyu_english/assets/courses/A1/A1-U16.json
index da2b324..51db9c3 100644
--- a/kouyu_english/assets/courses/A1/A1-U16.json
+++ b/kouyu_english/assets/courses/A1/A1-U16.json
@@ -704,7 +704,8 @@
"A1-U16-P07"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A1-U16-C2",
diff --git a/kouyu_english/assets/courses/A1/A1-U17.json b/kouyu_english/assets/courses/A1/A1-U17.json
index c445d18..9935982 100644
--- a/kouyu_english/assets/courses/A1/A1-U17.json
+++ b/kouyu_english/assets/courses/A1/A1-U17.json
@@ -666,7 +666,8 @@
"A1-U17-P11"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A1-U17-C2",
diff --git a/kouyu_english/assets/courses/A1/A1-U18.json b/kouyu_english/assets/courses/A1/A1-U18.json
index e7b1fed..009a14c 100644
--- a/kouyu_english/assets/courses/A1/A1-U18.json
+++ b/kouyu_english/assets/courses/A1/A1-U18.json
@@ -681,7 +681,8 @@
"A1-U18-P09"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A1-U18-C2",
diff --git a/kouyu_english/assets/courses/A1/A1-U19.json b/kouyu_english/assets/courses/A1/A1-U19.json
index c7c89e6..37ae3a5 100644
--- a/kouyu_english/assets/courses/A1/A1-U19.json
+++ b/kouyu_english/assets/courses/A1/A1-U19.json
@@ -661,7 +661,8 @@
"A1-U19-P04"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A1-U19-C2",
diff --git a/kouyu_english/assets/courses/A1/A1-U20.json b/kouyu_english/assets/courses/A1/A1-U20.json
index 3139ff5..698871f 100644
--- a/kouyu_english/assets/courses/A1/A1-U20.json
+++ b/kouyu_english/assets/courses/A1/A1-U20.json
@@ -612,7 +612,8 @@
"A1-U20-P11"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A1-U20-C2",
diff --git a/kouyu_english/assets/courses/A1/A1-U21.json b/kouyu_english/assets/courses/A1/A1-U21.json
index 53f0c9d..ae2610c 100644
--- a/kouyu_english/assets/courses/A1/A1-U21.json
+++ b/kouyu_english/assets/courses/A1/A1-U21.json
@@ -704,7 +704,8 @@
"A1-U21-P10"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A1-U21-C2",
diff --git a/kouyu_english/assets/courses/A2/A2-U01.json b/kouyu_english/assets/courses/A2/A2-U01.json
index 23cf01f..bc82e3c 100644
--- a/kouyu_english/assets/courses/A2/A2-U01.json
+++ b/kouyu_english/assets/courses/A2/A2-U01.json
@@ -864,7 +864,8 @@
"A2-U01-W01"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A2-U01-C2",
diff --git a/kouyu_english/assets/courses/A2/A2-U02.json b/kouyu_english/assets/courses/A2/A2-U02.json
index 6353926..d1e8c79 100644
--- a/kouyu_english/assets/courses/A2/A2-U02.json
+++ b/kouyu_english/assets/courses/A2/A2-U02.json
@@ -896,7 +896,8 @@
"A2-U02-P11"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A2-U02-C2",
diff --git a/kouyu_english/assets/courses/A2/A2-U03.json b/kouyu_english/assets/courses/A2/A2-U03.json
index 372ec8b..ca55808 100644
--- a/kouyu_english/assets/courses/A2/A2-U03.json
+++ b/kouyu_english/assets/courses/A2/A2-U03.json
@@ -867,7 +867,8 @@
"A2-U03-P09"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A2-U03-C2",
diff --git a/kouyu_english/assets/courses/A2/A2-U04.json b/kouyu_english/assets/courses/A2/A2-U04.json
index 7e7d5bc..e05a2de 100644
--- a/kouyu_english/assets/courses/A2/A2-U04.json
+++ b/kouyu_english/assets/courses/A2/A2-U04.json
@@ -907,7 +907,8 @@
"A2-U04-P04"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A2-U04-C2",
diff --git a/kouyu_english/assets/courses/A2/A2-U05.json b/kouyu_english/assets/courses/A2/A2-U05.json
index 6e38784..1a18c67 100644
--- a/kouyu_english/assets/courses/A2/A2-U05.json
+++ b/kouyu_english/assets/courses/A2/A2-U05.json
@@ -898,7 +898,8 @@
"zh": "那我们不要沙拉了,谢谢。",
"itemIds": []
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A2-U05-C2",
diff --git a/kouyu_english/assets/courses/A2/A2-U06.json b/kouyu_english/assets/courses/A2/A2-U06.json
index a4f3e9e..437350d 100644
--- a/kouyu_english/assets/courses/A2/A2-U06.json
+++ b/kouyu_english/assets/courses/A2/A2-U06.json
@@ -875,7 +875,8 @@
"A2-U06-P08"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A2-U06-C2",
diff --git a/kouyu_english/assets/courses/A2/A2-U07.json b/kouyu_english/assets/courses/A2/A2-U07.json
index f722f88..fad45f9 100644
--- a/kouyu_english/assets/courses/A2/A2-U07.json
+++ b/kouyu_english/assets/courses/A2/A2-U07.json
@@ -909,7 +909,8 @@
"zh": "太好了,谢谢。",
"itemIds": []
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A2-U07-C2",
diff --git a/kouyu_english/assets/courses/A2/A2-U08.json b/kouyu_english/assets/courses/A2/A2-U08.json
index cb0a619..c10531e 100644
--- a/kouyu_english/assets/courses/A2/A2-U08.json
+++ b/kouyu_english/assets/courses/A2/A2-U08.json
@@ -853,7 +853,8 @@
"zh": "太好了,谢谢。",
"itemIds": []
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A2-U08-C2",
diff --git a/kouyu_english/assets/courses/A2/A2-U09.json b/kouyu_english/assets/courses/A2/A2-U09.json
index 76773dd..9fe39ad 100644
--- a/kouyu_english/assets/courses/A2/A2-U09.json
+++ b/kouyu_english/assets/courses/A2/A2-U09.json
@@ -883,7 +883,8 @@
"A2-U09-W03"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A2-U09-C2",
diff --git a/kouyu_english/assets/courses/A2/A2-U10.json b/kouyu_english/assets/courses/A2/A2-U10.json
index bf23dcd..ebba0ad 100644
--- a/kouyu_english/assets/courses/A2/A2-U10.json
+++ b/kouyu_english/assets/courses/A2/A2-U10.json
@@ -895,7 +895,8 @@
"A2-U10-W01"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A2-U10-C2",
diff --git a/kouyu_english/assets/courses/A2/A2-U11.json b/kouyu_english/assets/courses/A2/A2-U11.json
index 0bd0124..2d255aa 100644
--- a/kouyu_english/assets/courses/A2/A2-U11.json
+++ b/kouyu_english/assets/courses/A2/A2-U11.json
@@ -905,7 +905,8 @@
"A2-U11-P10"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A2-U11-C2",
diff --git a/kouyu_english/assets/courses/A2/A2-U12.json b/kouyu_english/assets/courses/A2/A2-U12.json
index af0e50e..b414ee8 100644
--- a/kouyu_english/assets/courses/A2/A2-U12.json
+++ b/kouyu_english/assets/courses/A2/A2-U12.json
@@ -847,7 +847,8 @@
"A2-U12-W03"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A2-U12-C2",
diff --git a/kouyu_english/assets/courses/A2/A2-U13.json b/kouyu_english/assets/courses/A2/A2-U13.json
index 4cb3bc3..e484c9e 100644
--- a/kouyu_english/assets/courses/A2/A2-U13.json
+++ b/kouyu_english/assets/courses/A2/A2-U13.json
@@ -893,7 +893,8 @@
"A2-U13-P07"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A2-U13-C2",
diff --git a/kouyu_english/assets/courses/A2/A2-U14.json b/kouyu_english/assets/courses/A2/A2-U14.json
index 986e94e..0535458 100644
--- a/kouyu_english/assets/courses/A2/A2-U14.json
+++ b/kouyu_english/assets/courses/A2/A2-U14.json
@@ -875,7 +875,8 @@
"A2-U14-P09"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A2-U14-C2",
diff --git a/kouyu_english/assets/courses/A2/A2-U15.json b/kouyu_english/assets/courses/A2/A2-U15.json
index c6635c4..f26d23d 100644
--- a/kouyu_english/assets/courses/A2/A2-U15.json
+++ b/kouyu_english/assets/courses/A2/A2-U15.json
@@ -810,7 +810,8 @@
"A2-U15-P10"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "A2-U15-C2",
diff --git a/kouyu_english/assets/courses/B1/B1-U01.json b/kouyu_english/assets/courses/B1/B1-U01.json
index 7871f00..bb426da 100644
--- a/kouyu_english/assets/courses/B1/B1-U01.json
+++ b/kouyu_english/assets/courses/B1/B1-U01.json
@@ -1085,7 +1085,8 @@
"B1-U01-P09"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "B1-U01-C2",
diff --git a/kouyu_english/assets/courses/B1/B1-U02.json b/kouyu_english/assets/courses/B1/B1-U02.json
index 18e11a7..f4413a0 100644
--- a/kouyu_english/assets/courses/B1/B1-U02.json
+++ b/kouyu_english/assets/courses/B1/B1-U02.json
@@ -1054,7 +1054,8 @@
"B1-U02-P04"
]
}
- ]
+ ],
+ "practice": {}
},
{
"id": "B1-U02-C2",
diff --git a/kouyu_english/assets/courses/B1/B1-U03.json b/kouyu_english/assets/courses/B1/B1-U03.json
index c074217..a3ae7b4 100644
--- a/kouyu_english/assets/courses/B1/B1-U03.json
+++ b/kouyu_english/assets/courses/B1/B1-U03.json
@@ -1044,7 +1044,8 @@
"zh": "谢谢你的帮助。",
"itemIds": []
}
- ]
+ ],
+ "practice": {}
},
{
"id": "B1-U03-C2",
diff --git a/kouyu_english/assets/courses/B1/B1-U04.json b/kouyu_english/assets/courses/B1/B1-U04.json
index f5070d2..805038e 100644
--- a/kouyu_english/assets/courses/B1/B1-U04.json
+++ b/kouyu_english/assets/courses/B1/B1-U04.json
@@ -1096,7 +1096,8 @@
"zh": "太好了。谢谢你的帮助。",
"itemIds": []
}
- ]
+ ],
+ "practice": {}
},
{
"id": "B1-U04-C2",
diff --git a/kouyu_english/assets/courses/B1/B1-U05.json b/kouyu_english/assets/courses/B1/B1-U05.json
index ee782d0..402f342 100644
--- a/kouyu_english/assets/courses/B1/B1-U05.json
+++ b/kouyu_english/assets/courses/B1/B1-U05.json
@@ -1013,7 +1013,8 @@
"zh": "非常感谢。",
"itemIds": []
}
- ]
+ ],
+ "practice": {}
},
{
"id": "B1-U05-C2",
diff --git a/kouyu_english/assets/courses/B1/B1-U06.json b/kouyu_english/assets/courses/B1/B1-U06.json
index 6dac08b..0126b0c 100644
--- a/kouyu_english/assets/courses/B1/B1-U06.json
+++ b/kouyu_english/assets/courses/B1/B1-U06.json
@@ -1015,7 +1015,8 @@
"zh": "好的,谢谢医生。",
"itemIds": []
}
- ]
+ ],
+ "practice": {}
},
{
"id": "B1-U06-C2",
diff --git a/kouyu_english/assets/courses/B1/B1-U07.json b/kouyu_english/assets/courses/B1/B1-U07.json
index a6af7fd..b841ae7 100644
--- a/kouyu_english/assets/courses/B1/B1-U07.json
+++ b/kouyu_english/assets/courses/B1/B1-U07.json
@@ -1005,7 +1005,8 @@
"zh": "谢谢,感谢你的帮助。",
"itemIds": []
}
- ]
+ ],
+ "practice": {}
},
{
"id": "B1-U07-C2",
diff --git a/kouyu_english/assets/courses/B1/B1-U08.json b/kouyu_english/assets/courses/B1/B1-U08.json
index 6a5e557..efb951d 100644
--- a/kouyu_english/assets/courses/B1/B1-U08.json
+++ b/kouyu_english/assets/courses/B1/B1-U08.json
@@ -1012,7 +1012,8 @@
"zh": "周六见,再见。",
"itemIds": []
}
- ]
+ ],
+ "practice": {}
},
{
"id": "B1-U08-C2",
diff --git a/kouyu_english/assets/courses/B1/B1-U09.json b/kouyu_english/assets/courses/B1/B1-U09.json
index e407b54..872054c 100644
--- a/kouyu_english/assets/courses/B1/B1-U09.json
+++ b/kouyu_english/assets/courses/B1/B1-U09.json
@@ -978,7 +978,8 @@
"zh": "好的,谢谢你的帮助。",
"itemIds": []
}
- ]
+ ],
+ "practice": {}
},
{
"id": "B1-U09-C2",
diff --git a/kouyu_english/assets/courses/B1/B1-U10.json b/kouyu_english/assets/courses/B1/B1-U10.json
index 7938aba..fcba357 100644
--- a/kouyu_english/assets/courses/B1/B1-U10.json
+++ b/kouyu_english/assets/courses/B1/B1-U10.json
@@ -1003,7 +1003,8 @@
"zh": "太好了,我相信你会喜欢的。",
"itemIds": []
}
- ]
+ ],
+ "practice": {}
},
{
"id": "B1-U10-C2",
diff --git a/kouyu_english/assets/courses/B1/B1-U11.json b/kouyu_english/assets/courses/B1/B1-U11.json
index 79015cf..cfdec6e 100644
--- a/kouyu_english/assets/courses/B1/B1-U11.json
+++ b/kouyu_english/assets/courses/B1/B1-U11.json
@@ -1016,7 +1016,8 @@
"zh": "好的,我给百分之十。",
"itemIds": []
}
- ]
+ ],
+ "practice": {}
},
{
"id": "B1-U11-C2",
diff --git a/kouyu_english/assets/courses/B1/B1-U12.json b/kouyu_english/assets/courses/B1/B1-U12.json
index a2e1e00..d08f311 100644
--- a/kouyu_english/assets/courses/B1/B1-U12.json
+++ b/kouyu_english/assets/courses/B1/B1-U12.json
@@ -1010,7 +1010,8 @@
"zh": "真羡慕你!多给我讲讲。",
"itemIds": []
}
- ]
+ ],
+ "practice": {}
},
{
"id": "B1-U12-C2",
diff --git a/kouyu_english/assets/courses/B1/B1-U13.json b/kouyu_english/assets/courses/B1/B1-U13.json
index b1a5c66..b331b30 100644
--- a/kouyu_english/assets/courses/B1/B1-U13.json
+++ b/kouyu_english/assets/courses/B1/B1-U13.json
@@ -1167,7 +1167,8 @@
"zh": "好,走吧。",
"itemIds": []
}
- ]
+ ],
+ "practice": {}
},
{
"id": "B1-U13-C2",
diff --git a/kouyu_english/assets/courses/B1/B1-U14.json b/kouyu_english/assets/courses/B1/B1-U14.json
index 4bbb84c..7f1fc45 100644
--- a/kouyu_english/assets/courses/B1/B1-U14.json
+++ b/kouyu_english/assets/courses/B1/B1-U14.json
@@ -967,7 +967,8 @@
"zh": "好的,我今天就发。",
"itemIds": []
}
- ]
+ ],
+ "practice": {}
},
{
"id": "B1-U14-C2",
diff --git a/kouyu_english/assets/courses/course-map.json b/kouyu_english/assets/courses/course-map.json
index 90d1340..c5cdcba 100644
--- a/kouyu_english/assets/courses/course-map.json
+++ b/kouyu_english/assets/courses/course-map.json
@@ -98,6 +98,83 @@
"atm"
],
"levels": [
+ {
+ "id": "A0",
+ "title": "零基础起步",
+ "assessment": "A0/assessment.json",
+ "units": [
+ {
+ "id": "a0-01",
+ "file": "A0/a0-01.json",
+ "title": "你好,我是……",
+ "categories": [],
+ "canDo": "介绍姓名并回应问候"
+ },
+ {
+ "id": "a0-02",
+ "file": "A0/a0-02.json",
+ "title": "拼读我的名字",
+ "categories": [],
+ "canDo": "听懂拼写,并说出名字字母"
+ },
+ {
+ "id": "a0-03",
+ "file": "A0/a0-03.json",
+ "title": "你好吗?",
+ "categories": [],
+ "canDo": "问候并表达简单状态"
+ },
+ {
+ "id": "a0-04",
+ "file": "A0/a0-04.json",
+ "title": "数字与电话号码",
+ "categories": [],
+ "canDo": "说 0–10 与虚拟号码"
+ },
+ {
+ "id": "a0-05",
+ "file": "A0/a0-05.json",
+ "title": "这是什么?",
+ "categories": [],
+ "canDo": "询问并说出常见物品"
+ },
+ {
+ "id": "a0-06",
+ "file": "A0/a0-06.json",
+ "title": "我来自哪里?",
+ "categories": [],
+ "canDo": "说明来自哪里并反问"
+ },
+ {
+ "id": "a0-07",
+ "file": "A0/a0-07.json",
+ "title": "我的家人",
+ "categories": [],
+ "canDo": "介绍一位家人"
+ },
+ {
+ "id": "a0-08",
+ "file": "A0/a0-08.json",
+ "title": "星期与时间",
+ "categories": [],
+ "canDo": "说今天和整点"
+ },
+ {
+ "id": "a0-09",
+ "file": "A0/a0-09.json",
+ "title": "我喜欢……",
+ "categories": [],
+ "canDo": "表达喜好并回答"
+ },
+ {
+ "id": "a0-10",
+ "file": "A0/a0-10.json",
+ "title": "A0 综合任务",
+ "categories": [],
+ "canDo": "没听清时请对方重复,并完成自我介绍、喜好和反问"
+ }
+ ]
+ },
{
"id": "A1",
"title": "固定的生活与旅行互动",
diff --git a/kouyu_english/lib/core/a0_core.dart b/kouyu_english/lib/core/a0_core.dart
deleted file mode 100644
index d0fb31c..0000000
--- a/kouyu_english/lib/core/a0_core.dart
+++ /dev/null
@@ -1,275 +0,0 @@
-/// Frozen A0 v1 upgrade denominator from A0-STAGE-STANDARD.md.
-/// IDs are intentionally separate from generated wording and never change
-/// when a course uses an accepted slot value or contraction.
-final Map a0CoreItems = Map.unmodifiable({
- for (final entry in _numbered('A0-W', const [
- 'zero',
- 'one',
- 'two',
- 'three',
- 'four',
- 'five',
- 'six',
- 'seven',
- 'eight',
- 'nine',
- 'ten',
- 'Monday',
- 'Tuesday',
- 'Wednesday',
- 'Thursday',
- 'Friday',
- 'Saturday',
- 'Sunday',
- 'book',
- 'phone',
- 'pen',
- 'bag',
- 'key',
- 'water',
- 'mother',
- 'father',
- 'sister',
- 'brother',
- 'coffee',
- 'tea',
- 'music',
- 'movies',
- 'good',
- 'okay',
- 'tired',
- 'hello',
- 'hi',
- 'yes',
- 'no',
- 'thanks',
- ]))
- entry.key: entry.value,
- for (final entry in _numbered('A0-P', const [
- "I'm [name].",
- "What's your name?",
- 'Nice to meet you.',
- 'How do you spell that?',
- 'How are you?',
- "I'm [state].",
- "What's your phone number?",
- 'My number is [digits].',
- "What's this?",
- "It's a [object].",
- 'Where are you from?',
- "I'm from [place].",
- 'This is my [person].',
- 'What day is it today?',
- "It's [weekday].",
- 'What time is it?',
- "It's [hour] o'clock.",
- 'I like [thing].',
- 'Do you like [thing]?',
- 'Please say that again.',
- ]))
- entry.key: entry.value,
-});
-
-Iterable> _numbered(
- String prefix,
- List values,
-) sync* {
- for (var index = 0; index < values.length; index++) {
- yield MapEntry(
- '$prefix${(index + 1).toString().padLeft(2, '0')}',
- values[index],
- );
- }
-}
-
-class CoreReviewTemplate {
- const CoreReviewTemplate({
- required this.prompt,
- required this.hint,
- required this.skill,
- });
-
- final String prompt;
- final String hint;
- final String skill;
-}
-
-/// Offline, reviewed prompts keep initial reviews usable without an AI service.
-/// The prompt supplies a meaning or situation, never the English target.
-CoreReviewTemplate coreReviewTemplate(String id) {
- final target = a0CoreItems[id] ?? id;
- const meanings = {
- 'A0-W01': '零',
- 'A0-W02': '一',
- 'A0-W03': '二',
- 'A0-W04': '三',
- 'A0-W05': '四',
- 'A0-W06': '五',
- 'A0-W07': '六',
- 'A0-W08': '七',
- 'A0-W09': '八',
- 'A0-W10': '九',
- 'A0-W11': '十',
- 'A0-W12': '星期一',
- 'A0-W13': '星期二',
- 'A0-W14': '星期三',
- 'A0-W15': '星期四',
- 'A0-W16': '星期五',
- 'A0-W17': '星期六',
- 'A0-W18': '星期日',
- 'A0-W19': '书',
- 'A0-W20': '电话',
- 'A0-W21': '笔',
- 'A0-W22': '包',
- 'A0-W23': '钥匙',
- 'A0-W24': '水',
- 'A0-W25': '妈妈',
- 'A0-W26': '爸爸',
- 'A0-W27': '姐妹',
- 'A0-W28': '兄弟',
- 'A0-W29': '咖啡',
- 'A0-W30': '茶',
- 'A0-W31': '音乐',
- 'A0-W32': '电影',
- 'A0-W33': '好',
- 'A0-W34': '还可以',
- 'A0-W35': '累',
- 'A0-W36': '你好',
- 'A0-W37': '嗨',
- 'A0-W38': '是',
- 'A0-W39': '不',
- 'A0-W40': '谢谢',
- };
- final phrasePrompt = switch (id) {
- 'A0-P01' => '向新同学介绍你的名字。',
- 'A0-P02' => '问对方叫什么名字。',
- 'A0-P03' => '对初次见面的人说“很高兴认识你”。',
- 'A0-P04' => '问对方一个词怎样拼写。',
- 'A0-P05' => '问对方今天怎么样。',
- 'A0-P06' => '说出你今天的状态。',
- 'A0-P07' => '问对方的电话号码。',
- 'A0-P08' => '报出一个虚构的三位号码。',
- 'A0-P09' => '指着身边的东西问“这是什么”。',
- 'A0-P10' => '说出“这是一件物品”。',
- 'A0-P11' => '问对方来自哪里。',
- 'A0-P12' => '说出你来自哪里。',
- 'A0-P13' => '介绍一位家人。',
- 'A0-P14' => '问今天星期几。',
- 'A0-P15' => '说出今天星期几。',
- 'A0-P16' => '问现在几点。',
- 'A0-P17' => '说出一个整点时间。',
- 'A0-P18' => '说出你喜欢的一样东西。',
- 'A0-P19' => '问对方是否喜欢一样东西。',
- 'A0-P20' => '请对方再说一次。',
- _ => null,
- };
- if (phrasePrompt != null) {
- return CoreReviewTemplate(
- prompt: phrasePrompt,
- hint: target,
- skill: '回忆表达',
- );
- }
- return CoreReviewTemplate(
- prompt: '把“${meanings[id] ?? '这个已学词'}”写成英文。',
- hint: target,
- skill: '词汇回忆',
- );
-}
-
-/// Rotates an approved offline prompt family without changing the core target
-/// ID. The rotation changes the skill, not just the wording: recall in
-/// writing, dictation from audio, then saying it aloud.
-CoreReviewTemplate coreReviewVariant(String id, int variantIndex) {
- final base = coreReviewTemplate(id);
- final dictation = a0DictationSentences[id];
- return switch (variantIndex % 3) {
- 1 when dictation != null => CoreReviewTemplate(
- prompt: '听一句话,把听到的整句英文写下来。',
- hint: dictation.meaning,
- skill: dictationSkill,
- ),
- 2 => CoreReviewTemplate(
- prompt: '用英文说出来:${base.prompt}',
- hint: base.hint,
- skill: spokenRecallSkill,
- ),
- _ => base,
- };
-}
-
-const dictationSkill = '听写';
-const spokenRecallSkill = '口头回忆';
-
-/// A concrete sentence built only from A0 language, used to play a core item
-/// in context and to check dictation.
-typedef DictationSentence = ({String sentence, String meaning});
-
-const Map a0DictationSentences = {
- 'A0-W01': (sentence: 'Zero, one, two.', meaning: '零、一、二。'),
- 'A0-W02': (sentence: 'My number is one-two-three.', meaning: '我的号码是一二三。'),
- 'A0-W03': (sentence: 'It’s two o’clock.', meaning: '现在两点。'),
- 'A0-W04': (sentence: 'It’s three o’clock.', meaning: '现在三点。'),
- 'A0-W05': (sentence: 'It’s four o’clock.', meaning: '现在四点。'),
- 'A0-W06': (sentence: 'It’s five o’clock.', meaning: '现在五点。'),
- 'A0-W07': (sentence: 'It’s six o’clock.', meaning: '现在六点。'),
- 'A0-W08': (sentence: 'It’s seven o’clock.', meaning: '现在七点。'),
- 'A0-W09': (sentence: 'It’s eight o’clock.', meaning: '现在八点。'),
- 'A0-W10': (sentence: 'It’s nine o’clock.', meaning: '现在九点。'),
- 'A0-W11': (sentence: 'It’s ten o’clock.', meaning: '现在十点。'),
- 'A0-W12': (sentence: 'It’s Monday.', meaning: '今天星期一。'),
- 'A0-W13': (sentence: 'It’s Tuesday.', meaning: '今天星期二。'),
- 'A0-W14': (sentence: 'It’s Wednesday.', meaning: '今天星期三。'),
- 'A0-W15': (sentence: 'It’s Thursday.', meaning: '今天星期四。'),
- 'A0-W16': (sentence: 'It’s Friday.', meaning: '今天星期五。'),
- 'A0-W17': (sentence: 'It’s Saturday.', meaning: '今天星期六。'),
- 'A0-W18': (sentence: 'It’s Sunday.', meaning: '今天星期日。'),
- 'A0-W19': (sentence: 'It’s a book.', meaning: '这是一本书。'),
- 'A0-W20': (sentence: 'It’s a phone.', meaning: '这是一部电话。'),
- 'A0-W21': (sentence: 'It’s a pen.', meaning: '这是一支笔。'),
- 'A0-W22': (sentence: 'It’s a bag.', meaning: '这是一个包。'),
- 'A0-W23': (sentence: 'It’s a key.', meaning: '这是一把钥匙。'),
- 'A0-W24': (sentence: 'I like water.', meaning: '我喜欢喝水。'),
- 'A0-W25': (sentence: 'This is my mother.', meaning: '这是我妈妈。'),
- 'A0-W26': (sentence: 'This is my father.', meaning: '这是我爸爸。'),
- 'A0-W27': (sentence: 'This is my sister.', meaning: '这是我的姐妹。'),
- 'A0-W28': (sentence: 'This is my brother.', meaning: '这是我的兄弟。'),
- 'A0-W29': (sentence: 'I like coffee.', meaning: '我喜欢咖啡。'),
- 'A0-W30': (sentence: 'Do you like tea?', meaning: '你喜欢茶吗?'),
- 'A0-W31': (sentence: 'I like music.', meaning: '我喜欢音乐。'),
- 'A0-W32': (sentence: 'Do you like movies?', meaning: '你喜欢电影吗?'),
- 'A0-W33': (sentence: 'I’m good.', meaning: '我很好。'),
- 'A0-W34': (sentence: 'I’m okay.', meaning: '我还可以。'),
- 'A0-W35': (sentence: 'I’m tired.', meaning: '我累了。'),
- 'A0-W36': (sentence: 'Hello, how are you?', meaning: '你好,你怎么样?'),
- 'A0-W37': (sentence: 'Hi, what’s your name?', meaning: '嗨,你叫什么名字?'),
- 'A0-W38': (sentence: 'Yes, I do.', meaning: '是的,我喜欢。'),
- 'A0-W39': (sentence: 'No, I don’t.', meaning: '不,我不喜欢。'),
- 'A0-W40': (sentence: 'I’m okay, thanks.', meaning: '我还可以,谢谢。'),
- 'A0-P01': (sentence: 'I’m Tom.', meaning: '我是 Tom。'),
- 'A0-P02': (sentence: 'What’s your name?', meaning: '你叫什么名字?'),
- 'A0-P03': (sentence: 'Nice to meet you.', meaning: '很高兴认识你。'),
- 'A0-P04': (sentence: 'How do you spell that?', meaning: '那个怎么拼写?'),
- 'A0-P05': (sentence: 'How are you?', meaning: '你怎么样?'),
- 'A0-P06': (sentence: 'I’m tired today.', meaning: '我今天很累。'),
- 'A0-P07': (sentence: 'What’s your phone number?', meaning: '你的电话号码是多少?'),
- 'A0-P08': (sentence: 'My number is one-three-eight.', meaning: '我的号码是一三八。'),
- 'A0-P09': (sentence: 'What’s this?', meaning: '这是什么?'),
- 'A0-P10': (sentence: 'It’s a pen.', meaning: '这是一支笔。'),
- 'A0-P11': (sentence: 'Where are you from?', meaning: '你来自哪里?'),
- 'A0-P12': (sentence: 'I’m from China.', meaning: '我来自中国。'),
- 'A0-P13': (sentence: 'This is my sister.', meaning: '这是我的姐妹。'),
- 'A0-P14': (sentence: 'What day is it today?', meaning: '今天星期几?'),
- 'A0-P15': (sentence: 'It’s Friday.', meaning: '今天星期五。'),
- 'A0-P16': (sentence: 'What time is it?', meaning: '现在几点?'),
- 'A0-P17': (sentence: 'It’s three o’clock.', meaning: '现在三点。'),
- 'A0-P18': (sentence: 'I like music.', meaning: '我喜欢音乐。'),
- 'A0-P19': (sentence: 'Do you like coffee?', meaning: '你喜欢咖啡吗?'),
- 'A0-P20': (sentence: 'Please say that again.', meaning: '请再说一遍。'),
-};
-
-/// What review audio should play for [item]: the dictation sentence for a
-/// core item, otherwise the target itself when it has no slot to fill.
-String? reviewAudioText(String id, String target) =>
- a0DictationSentences[id]?.sentence ??
- (target.contains('[') ? null : target);
diff --git a/kouyu_english/lib/core/ai_config.dart b/kouyu_english/lib/core/ai_config.dart
index a9c1897..2544888 100644
--- a/kouyu_english/lib/core/ai_config.dart
+++ b/kouyu_english/lib/core/ai_config.dart
@@ -27,10 +27,11 @@ class AiConfigFile {
(p) => p.name.toLowerCase() == providerStr.toLowerCase(),
orElse: () => AiProviderType.compatible,
);
- final effort = (json['reasoningEffort'] as String? ??
- json['reasoning_effort'] as String? ??
- 'low')
- .trim();
+ final effort =
+ (json['reasoningEffort'] as String? ??
+ json['reasoning_effort'] as String? ??
+ 'low')
+ .trim();
return AiConfigFile(
provider: provider,
endpoint: (json['endpoint'] as String? ?? '').trim(),
diff --git a/kouyu_english/lib/core/ai_service.dart b/kouyu_english/lib/core/ai_service.dart
index 84e2872..736632a 100644
--- a/kouyu_english/lib/core/ai_service.dart
+++ b/kouyu_english/lib/core/ai_service.dart
@@ -2,6 +2,7 @@ import 'sherpa_stt_service.dart';
import 'dart:io';
import 'dart:convert';
+import 'package:flutter/foundation.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:http/http.dart' as http;
@@ -139,12 +140,17 @@ class AiService {
if (key == null || key.isEmpty || uri == null) return null;
final ext = filePath.split('.').last.toLowerCase();
- final format = (ext == 'wav' || ext == 'mp3' || ext == 'm4a' || ext == 'aac') ? ext : 'm4a';
+ final format =
+ (ext == 'wav' || ext == 'mp3' || ext == 'm4a' || ext == 'aac')
+ ? ext
+ : 'm4a';
final base64Data = base64Encode(bytes);
try {
if (provider == AiProviderType.gemini) {
- final mimeType = format == 'wav' ? 'audio/wav' : (format == 'mp3' ? 'audio/mp3' : 'audio/mp4');
+ final mimeType = format == 'wav'
+ ? 'audio/wav'
+ : (format == 'mp3' ? 'audio/mp3' : 'audio/mp4');
final response = await _postJson(
provider: provider,
key: key,
@@ -155,21 +161,17 @@ class AiService {
{
'parts': [
{
- 'text': 'Transcribe the spoken English speech in this audio file accurately. Return ONLY the transcribed English words. If silence or unintelligible, output nothing.',
+ 'text':
+ 'Transcribe the spoken English speech in this audio file accurately. Return ONLY the transcribed English words. If silence or unintelligible, output nothing.',
},
{
- 'inline_data': {
- 'mime_type': mimeType,
- 'data': base64Data,
- }
- }
- ]
- }
+ 'inline_data': {'mime_type': mimeType, 'data': base64Data},
+ },
+ ],
+ },
],
'generationConfig': {
- 'thinkingConfig': {
- 'thinkingBudget': 1024,
- },
+ 'thinkingConfig': {'thinkingBudget': 1024},
},
},
);
@@ -189,17 +191,15 @@ class AiService {
'content': [
{
'type': 'text',
- 'text': 'Transcribe the spoken English speech in this audio file accurately. Output ONLY the raw transcribed English words without quotes, punctuation tags, or commentary. If silence or noise, return nothing.',
+ 'text':
+ 'Transcribe the spoken English speech in this audio file accurately. Output ONLY the raw transcribed English words without quotes, punctuation tags, or commentary. If silence or noise, return nothing.',
},
{
'type': 'input_audio',
- 'input_audio': {
- 'data': base64Data,
- 'format': format,
- },
- }
+ 'input_audio': {'data': base64Data, 'format': format},
+ },
],
- }
+ },
],
'reasoning_effort': 'low',
'temperature': 0.1,
@@ -219,7 +219,8 @@ class AiService {
}
}
- static bool _isHttpUri(Uri uri) => uri.scheme == 'https' || uri.scheme == 'http';
+ static bool _isHttpUri(Uri uri) =>
+ uri.scheme == 'https' || uri.scheme == 'http';
static bool _isDeepSeek(Uri uri) {
final host = uri.host.toLowerCase();
@@ -295,12 +296,31 @@ class AiService {
),
);
if (!_isSuccess(response)) return null;
+ if (kDebugMode) _logUsage(response.body);
return _extractResponseContent(provider, response.body);
} catch (_) {
return null;
}
}
+ /// Debug-only prompt cache report. DeepSeek reports hits and misses
+ /// directly; OpenAI-style endpoints report cached tokens in the details.
+ static void _logUsage(String body) {
+ try {
+ final usage = (jsonDecode(body) as Map)['usage'];
+ if (usage is! Map) return;
+ final hit =
+ usage['prompt_cache_hit_tokens'] ??
+ (usage['prompt_tokens_details'] as Map?)?['cached_tokens'];
+ final miss = usage['prompt_cache_miss_tokens'];
+ final prompt = usage['prompt_tokens'] ?? usage['input_tokens'];
+ debugPrint(
+ 'AI usage: prompt=$prompt cacheHit=$hit cacheMiss=$miss '
+ 'completion=${usage['completion_tokens'] ?? usage['output_tokens']}',
+ );
+ } catch (_) {}
+ }
+
Future _requestPrompt({
required AiProviderType provider,
required String endpoint,
@@ -436,9 +456,7 @@ class AiService {
'temperature': ?temperature,
'maxOutputTokens': ?maxOutputTokens,
'responseMimeType': ?responseMimeType,
- 'thinkingConfig': {
- 'thinkingBudget': thinkingBudget,
- },
+ 'thinkingConfig': {'thinkingBudget': thinkingBudget},
};
}
@@ -718,15 +736,9 @@ class AiService {
if (_isSuccess(response)) {
final content = _extractResponseContent(provider, response.body);
if (_decodeDialogueResponse(content) != null) {
- return const AiConnectionResult(
- ok: true,
- message: '连接成功,AI 对话服务可用!',
- );
+ return const AiConnectionResult(ok: true, message: '连接成功,AI 对话服务可用!');
}
- return const AiConnectionResult(
- ok: true,
- message: '连接成功,接口响应正常。',
- );
+ return const AiConnectionResult(ok: true, message: '连接成功,接口响应正常。');
}
if (response.statusCode == 401 || response.statusCode == 403) {
return AiConnectionResult(
@@ -761,6 +773,10 @@ class AiService {
/// [aiGoal] is what Mia's own next line has to do; [learnerTask] is what the
/// learner has to say afterwards. They used to be the same string, so the
/// model was told to perform the learner's job.
+ ///
+ /// The system prompt holds only what stays fixed for the whole conversation
+ /// and the per-turn goal goes at the very end, so each turn repeats the
+ /// previous request as a prefix and DeepSeek can serve it from its cache.
Future dialogueReply({
required AiProviderType provider,
required String endpoint,
@@ -768,51 +784,85 @@ class AiService {
required List