# 英语听力教材 → 网页课件:一份可以直接交给 agent 的完整规格书

我给自己和家里人做了一套英语听力学习系统。输入只有一本中英对照口语教材的 PDF,成品是一个网页课件:每句都能播放、可以跟读并得到 AI 打分点评、还有听写、测验、进度管理。全部推理跑在家里机器上,不花一分钱 API 钱。

实际规模:16 个单元 × 472 个高频句型 + 16 篇情境课文 × 431 句对话,903 条预生成语音。

这篇帖子不是教程,是一份**规格书**——把这篇帖子和你自己的教材 PDF 一起交给你的 agent,它应该能按管线把整套系统跑通。

## 前提假设

- 教材是 PDF,中英对照(双栏排版:一栏英文、一栏中文)
- 家里有台机器能常驻(当本地服务器);有 GPU 更好(跑打分用的大模型),没有也能用 CPU 小模型降级跑
- 需要联网下载:edge-tts(语音合成)、Qwen3-ASR-1.7B(转写模型)、OpenPronounce(发音评估,开源)

## 成品形态

1. **单文件网页课件**:课程数据内嵌页面、零外部依赖,本地双击打开或局域网多设备访问都行
2. **预生成的语音文件**:每句一个 mp3(不用浏览器实时合成)
3. **AI 打分服务**:录音 → 转写 + 音素级发音分析 + 中文点评
4. **部署**:两个常驻容器,开机自启,重启不丢

## 功能清单与实现机制

| 功能 | 实现方式 |
|---|---|
| 句型/课文播放 | 逐句预生成 mp3,浏览器音频元素播放 |
| 慢速 | 原生变速 0.65x,开"保持音调"(否则男声降速后发虚发颤) |
| 中文默认隐藏 | 每句英文末尾一个「显示中文」小按钮,按句记忆 |
| 课文整篇连播 | 逐句自动播 + 当前行高亮 |
| 标记已会 | 按句开关,首页统计"已会数";历史低分的句子进「需复习」 |
| 听写 | 每单元随机抽 5 句,句子默认不显示,先听音频再默写,AI 逐句判分(纯文本判分,不走语音链路);随时可退出,刷新从断点继续 |
| 测验 | 每单元随机 5 题、四选一、英→中/中→英,记历史最佳 |
| 进度 | 学习统计 + 进度条,全部存浏览器本地存储,支持导出/导入 JSON |
| 跟读打分 | 浏览器录音 → 发打分服务(见下)→ 出分数、听写对照、逐词差异、中文点评、发音分析块 |
| 多用户档案 | 两个档案(家庭成员),首页点卡片即登录、无密码;各档案进度/设置/练习会话完全隔离;同设备开两个标签页可分别以两个档案学习,互不干扰 |
| 深色模式 | 跟随系统或手动切换 |

## 管线(四步)

### ① PDF → 结构化数据

- 坐标级提取:按栏切分 → 按行聚类 → 行内按阅读顺序取词;用引号状态机判断句子起止(教材排版常有缺收尾引号,要有兜底)
- 输出 JSON 结构:单元(编号/名称)→ 句型列表(en, zh);情境课文(场景说明 + 逐句:说话人、方向、en、zh)
- **验收门槛:逐条与页面核对,零缺漏、零串列,不达标不进下一步**

### ② 批量生成语音

- 工具:edge-tts(微软神经语音)
- 句型:两个音色各生成一套(女声 `en-US-AvaNeural` / 男声 `en-US-GuyNeural`),课件里可切换
- 课文:按说话人分配独立音色(我的书是 19 个说话人 13 种声线),同一人全程同一声线
- **命名即播放键**:句型 `i{单元}_{序号}.mp3`,课文 `l{单元}_{序号}.mp3`
- 送给 TTS 前的文本清洗规则:
  - 句内 " / " 表示可互换变体(如 "under the weather / unwell"):用 LLM 展开成多句语法正确的完整句,屏幕保留 " / " 作分隔;机械拼接必然产出病句
  - 短词组并列(如 "One second / Hold on / Wait"):斜杠保留,读作短暂停顿
  - 括号:内容直接读出;括号里的舞台指示(音效、动作)整段不读
- 并发 ~10、断点续跑(跳过已存在的文件)
- **验收:磁盘文件集合 == 期望键集合、无残件——不信退出码和输出日志,只信磁盘**

实测:903 条约 20MB,全量生成 4.5 分钟。

### ③ 课件(单文件 HTML)

- 课程 JSON + 全部功能打进一个 HTML,改完抽出来做语法检查再交付
- 所有状态(进度/已会/中文显示/听写会话/测验会话/设置/用户档案)存浏览器本地存储,刷新、重开都能续
- 打分服务没起时:播放/听写/测验等本地功能照常,只有跟读打分提示"连不上"——**任何外部依赖挂掉都不许拖垮本地功能**

### ④ AI 打分服务

录音(浏览器 webm 格式)进来后,三件事**并行**:

1. **转写**:Qwen3-ASR-1.7B(CPU 就够,bf16 精度),音频先统一转成 16kHz 单声道
2. **音素级发音评估**:OpenPronounce(开源项目,Docker 部署,内部两个 wav2vec2 模型),返回逐词期望/实际音标 + 音素错误率
3. **LLM 中文点评**:本地 27B 大模型(NVFP4 量化、SGLang 部署;任何能稳定输出严格 JSON 的本地大模型都可以)。把转写文本 + 音素错误清单塞进下面的 prompt,一次出分。**若模型有"思考模式"必须关掉,否则严格 JSON 会被思考内容挤掉**

保底规则:LLM 挂 → 至少返回 ASR 转写文本 + 错误说明;发音评估挂 → 主分照出,只在那一块显示警告。单点失败不拖垮整链。

实测:稳态 ~3 秒出分(首次请求要等模型预热 ~30-60 秒);听写判分 1-2 秒。

## 两个判分 prompt(直接可用)

**跟读打分:**

```
你是英语口语跟读评分员。学生跟读目标英文句子, ASR 听写结果见下。
目标句: {target}
ASR 听写: {heard}
录音时长: {duration}s
评分: 听写与目标基本一致 85-100；漏 1-2 个非关键词/音近听错 70-84；漏关键词/短语明显读错 50-69；大面积缺失 0-49；听写为空 0。
输出严格 JSON, 不要任何其他文字:
{"score": 整数, "heard_accuracy": 整数(听写文本与目标句吻合度), "word_diffs": ["最多5条: 目标词→听成X 或 漏读:X"], "feedback_zh": "2-3句中文点评", "tips_zh": "1-2句具体改进建议"}
```

有音素级结果时:在 prompt 里追加逐行 `  - 单词 "X": 期望音标 /…/, 实际听成 /…/`,并要求中文点评具体指出这些音怎么读对(口型/舌位/常见错误),不要笼统点评。

**听写判分:**

```
你是英语听写判卷员。学生听音频默写下面这句英文, 请判断其听写结果的对错。
目标句: {target}
学生听写: {typed}
{中文参考: {zh}}

判分标准:
- 单词拼写错误算错 (哪怕只差一个字母); 意思不通/张冠李戴算大错
- 大小写、标点、空格差异不扣分
- 目标句里的 " / " 分隔的是多个可互换变体, 学生写出其中任意一个变体都算对
输出严格 JSON, 不要任何其他文字:
{"score": 整数(0-100: 100=完全正确, 85-99=仅拼写/大小写小错, 50-84=错 1-2 个词, 0-49=整体不符), "diffs": ["最多5条: 目标词→写成X / 漏写:X / 多写:X; 全对则空数组"], "comment_zh": "1-2句中文评语: 全对简短鼓励; 有错具体指出哪个词写成什么/正确怎么写"}
```

## 部署(关键设计,别省)

- 两个容器、开机自启:**web 服务**(静态文件 + API 代理)与**打分服务**(CPU)
- **web 服务必须同时提供 http 和 https,https 用自签证书**(10 年有效期):浏览器只在"安全上下文"(https 或本机)放行麦克风,纯 http 的局域网服务录不了音
- **打分 API 要通过 web 服务做同源代理**:https 页面直接调 http 的 API 会被浏览器当"混合内容"拦死,同源代理是唯一免证书的办法;本地打开课件时则直连打分服务(跨域放开)
- 推理全放 CPU 是刻意的:家里 GPU 常年跑着大模型服务,1.7B 转写模型放 CPU 稳态 3 秒完全跟得上人说话的节奏,不抢显存、互不干扰

## 验收清单(怎么算"跑通了")

1. 播放:每句正常/慢速都能播;课文整篇连播逐句高亮
2. 跟读:能录音,~3 秒出分数 + 听写对照 + 中文点评
3. 听写:全对给 100;拼错给 85 且逐条列出"目标词→你写成了什么";刷新后可从断点继续
4. 测验:每单元 5 题四选一,双向出题
5. 多档案:两个档案进度完全独立,同设备两个标签页各学各的
6. 打分服务关掉:播放/听写/测验照常,只有跟读提示连不上
7. 两个已知的坑:① 大模型默认开思考模式时严格 JSON 输出会空;② 模型下载后先验内容(镜像站可能把错误页当文件给你)

## 诚实取舍

- **自签证书**:没有真实域名,每台新设备首次要接受一次证书警告,这是零运维的代价
- **进度存各设备浏览器本地**:Mac 的进度不会自动同步到 iPad;要跨设备同步得上服务端存储
- **CPU 推理**:比 GPU 慢(冷启动 30-60 秒),换来"完全不碰 GPU"
- **单文件课件**:体积 200KB+、零依赖、好分发,代价是任何改动都要重新生成整份
- 这套东西的天花板很明显:单设备存储、自签证书、无账号体系。但对"一家人、几台设备、一本教材"这个场景,它刚好够用,而且一行云费用都没有
