IELTS Reading Review Skill
Purpose
帮用户把雅思阅读做题结果变成结构化复盘笔记(HTML + PDF)和结构化数据(JSON),追踪分数进步趋势。
Architecture (v3.2 — Offline + Web Hand-off)
Skill 纯离线执行——所有错题分析、文件生成都在本地完成,不发起任何网络请求。
产出物:
- 复盘 HTML — 专业排版的复盘笔记,双击浏览器打开
- 复盘 PDF — 从 HTML 生成的 PDF 文件,便于存档和分享
- 结构化 JSON — 成绩、错题、词汇、同义替换的全量数据(v3.0 schema)
产出物与 Web 端(tuyaya.online)的对接方式详见下方 Step 6: Apply to Web。
When to Activate
- 用户发做题截图/答案,提到"复盘""错题分析""阅读复盘"
- 用户问成绩、分数、进步趋势
- 用户要生成复盘笔记或 PDF
Workflow
Step 1: Collect Input
确保以下信息齐全(缺什么问什么):
- 来源:哪本书、哪套题、哪篇(如剑5 Test1 Passage2)
- 原文或答题上下文
- 正确答案
- 用户答案及错题
- 🔴 用时(MUST ASK):做题用时(格式
MM:SS,如28:01)。必问项,不能标"可选"——Web 端进步趋势图依赖此字段。用户若没计时,明确询问"这套大概做了多久"让其估算,别直接跳过 - 可选:翻译、自我反思
Step 1b: Screenshot Wrong Answer Protocol (CRITICAL)
用户发答题截图时必须执行 3 步:
- 逐题读截图标记:每题是红色(错)还是绿色(对),不能跳题,不能用自己的判断代替截图标记
- 先报错题清单等确认:输出"根据截图,错题为 QX/QY/QZ(共N道),请确认",确认后才能写分析
- 截图标记是唯一真相:截图 vs 自己判断冲突时,信截图
禁止:跳过确认直接写分析、用 answer comparison 覆盖截图标记。
Step 2: Generate Review HTML
基于 assets/review-template.html 模板,使用 references/ 下的规范生成完整复盘 HTML 文件。
🔴 文件命名强制规范(MUST FOLLOW):
文件名格式:剑{book}-Test{test}-Passage{passage}-{titleCN}复盘.html
{titleCN}必须与 JSON 里的source.titleCN字段完全一致(同一个字符串,一字不差)- 必须以"复盘"两字结尾(不是"积累"、不是直接
.html) - 禁止:空格、下划线、英文连字符中混中文
- 示例:
- ✅
剑5-Test1-Passage2-鲸鱼感官复盘.html - ✅
剑6-Test4-Passage2-识字女性与育儿复盘.html - ❌
剑4-Test3-Passage2-火山专题积累.html(缺"复盘"两字) - ❌
剑6-Test4-Passage2-识字女性育儿复盘.html(和 title 里"与"字不一致)
- ✅
命名一致性自检(生成前必做):
- 决定
titleCN后,HTML 文件名、JSON 文件名、JSON 内source.titleCN三者必须用完全相同的中文串 - 生成完成后,自查输出"
{文件名}和source.titleCN='{titleCN}'一致 ✅" - 如果篇目已在
site/answer-key.json里存在,直接复用其title字段作为titleCN,不要自创新表述(避免和已有文件/数据库漂移)
遵循 references/review-style-guide.md 的设计规范(V2 紫色渐变主题、Lucide 图标、卡片布局)。
Step 3: Generate Review Data JSON
在生成 HTML 的同时,输出一份结构化 JSON 文件,供后续导入 Web 系统:
输出文件命名规则:剑X-TestX-PassageX-中文主题复盘.json
命名必须与 Step 2 的 HTML 文件名主干完全一致(只差后缀),否则会触发 Web 端路径错乱。
示例:
- HTML:
剑5-Test1-Passage2-鲸鱼感官复盘.html - JSON:
剑5-Test1-Passage2-鲸鱼感官复盘.json source.titleCN:"鲸鱼感官"
命名说明:文件名使用中文标题(
source.titleCN),方便用户在文件管理器中一眼识别内容。Web 端导入时通过 JSON 内的source.book/test/passage识别篇目,不依赖文件名。
🔴 timing 字段必须填充:
minutes:数值型分钟(支持小数,如28.0/35.4)formatted:"MM:SS"字符串(如"28:01")- MM:SS → minutes 换算:
分钟 + 秒/60,保留 1 位小数 - 用户实在给不出用时,才能置
null,但必须在回复里提醒"缺用时,进步图将缺一个点"
{
"version": "3.0.0",
"generatedAt": "2026-04-20T23:00:00.000Z",
"source": {
"book": 5,
"test": 1,
"passage": 2,
"title": "English Title",
"titleCN": "中文标题"
},
"score": {
"correct": 9,
"total": 13,
"band": "5.0",
"breakdown": {
"fillBlank": { "correct": 4, "total": 6 },
"tfng": { "correct": 3, "total": 4 },
"matching": { "correct": 2, "total": 3 }
}
},
"timing": {
"minutes": 25,
"formatted": "25:00"
},
"date": "2026-04-20",
"wrongQuestions": [
{
"q": 3,
"type": "tfng",
"myAnswer": "TRUE",
"correctAnswer": "NOT GIVEN",
"errorCategory": "ng-false-confusion",
"analysis": "错因分析文字",
"lesson": "教训一句话"
}
],
"synonyms": [
{
"original": "原文表达",
"replacement": "题目表达",
"meaning": "中文释义",
"questionRef": "Q3"
}
],
"vocabulary": [
{
"word": "exemplify",
"phonetic": "/ɪɡˈzemplɪfaɪ/",
"pos": "v.",
"definition": "举例说明",
"ieltsFreq": 3,
"source": "538 #42",
"appearance": "剑4T3P1"
}
],
"problems": [
{
"type": "同义替换识别失败",
"detail": "具体表现",
"questions": "Q3, Q7",
"improvement": "改进方法"
}
]
}
Step 3b: Generate Bilingual Data & Page (MANDATORY)
每次复盘必须同时完成两件事,无需用户提醒:
3b-1: 追加双语数据到 bilingual_data.json(最关键!)
在 site/bilingual_data.json 中追加一条结构化数据:
{
"book": 7, "test": 1, "passage": 1,
"title_cn": "中文标题",
"title_en": "English Title",
"subtitle": "剑X · Test Y · Passage Z · 双语逐段对照",
"source_info": "Cambridge IELTS X, Test Y, Reading Passage Z",
"review_link": "../reviews/剑X-TestY-PassageZ-{titleCN}复盘.html",
"paragraphs": [
{ "label": "Paragraph A", "en": "English text with <span class=\"vocab-highlight\" title=\"释义\">highlighted</span> words", "cn": "中文翻译" }
],
"vocab_words": [
{ "word": "example", "meaning": "释义", "example": "Example sentence." }
]
}
规则:
- 词汇高亮:用正则替换英文段落中首次出现的词汇为
<span class="vocab-highlight" title="释义">word</span> - 追加后按
(book, test, passage)排序 - ⚠️ Python 脚本中中文引号会被当作字符串终止符——中文
""必须用\u201c\u201d转义
3b-2: 生成双语对照 HTML(可选,如有模板)
基于 assets/bilingual-template.html 模板,为当前复盘篇目生成双语逐段对照 HTML 文件。
文件命名:剑{book}-Test{test}-Passage{passage}-{titleCN}双语对照.html
{titleCN}与复盘 HTML 和 JSON 中的source.titleCN完全一致。
生成规则:
- 逐段拆分原文:按原文自然段落拆分(保留段落标号 A/B/C... 或 1/2/3...,如有)
- 逐段翻译:每段英文下方紧跟中文翻译,翻译力求准确通顺,专业术语保留英文并括号注释
- 词汇高亮:将 Step 3 JSON 中
vocabulary列表里的词汇在英文段落中用<span class="vocab-highlight" title="中文释义">word</span>标记 - 导航链接:顶部返回复盘的链接指向对应的复盘 HTML 文件(
../reviews/剑X-TestX-PassageX-{titleCN}复盘.html) - 样式:使用模板中的暗色主题 CSS,不要自创样式
输出位置:与复盘 HTML 同目录。
Step 6 的引导表格中需包含此文件:
| 文件 | 用途 |
|---|---|
剑X-TestX-PassageX-主题复盘.html | 复盘笔记 |
剑X-TestX-PassageX-主题双语对照.html | 双语逐段对照阅读 |
剑X-TestX-PassageX-主题复盘.json | 导入 Web 端同步成绩 |
Step 4: Generate PDF (Optional)
如果用户需要 PDF:
node scripts/generate-pdf.js 剑X-TestX-PassageX-主题复盘.html
需要 puppeteer-core + 本地 Chrome。PDF 输出到同目录。
Step 5: Update Memory
复盘完成后更新 working memory:新增的错误模式、词汇、成绩数据。
Step 6: Apply to Web (User-Initiated)
复盘生成完成后,输出以下引导:
📤 复盘文件已生成!
| 文件 | 用途 |
|---|---|
剑X-TestX-PassageX-主题复盘.html | 双击打开即可阅读,可打印 |
剑X-TestX-PassageX-主题双语对照.html | 双语逐段对照,复习原文 |
剑X-TestX-PassageX-主题复盘.json | 导入到 Web 端同步成绩 |
一键同步到 Web 端 👉 点此上传 JSON
上传页面会自动从 JSON 中识别出篇目信息(如「剑5 Test1 Passage2 · 鲸鱼感官」),确认后点击「导入」即可。
💡 JSON 文件在你当前的工作目录中,文件名如
剑5-Test1-Passage2-鲸鱼感官复盘.json
其他同步方式
方式 B:Skill 伴侣脚本(私有部署场景)
如果有 ielts-server-sync skill(个人专用),可命令行批量上传:
# 单文件上传
node ~/.workbuddy/skills/ielts-server-sync/scripts/upload.js 剑5-T1-P2.json
# 批量上传目录
node ~/.workbuddy/skills/ielts-server-sync/scripts/upload.js --batch ./reviews/
方式 C:纯离线
直接双击 .html 文件即可阅读 / 打印,不依赖任何服务器。
重要:Skill 本身 不执行任何网络请求。所有上传操作由用户主动发起,数据隐私可控。
Step 7: Deploy Checklist (MANDATORY — DO NOT SKIP)
🔴 每次复盘完成后,必须逐项执行以下检查清单。不能靠记忆,必须逐条过。
这是 2026-04-27 踩了6个坑后总结的教训——文件遗漏、部署遗漏、数据遗漏。
7a. 本地文件归位
- 复盘 HTML 已复制到
site/reviews/(不是根目录!) - 复盘 JSON 已复制到
site/reviews/ -
answer-key.json已更新(新增本篇条目) -
bilingual_data.json已新增本篇双语数据(英中逐段对照 + 词汇列表) -
generate_vocab_synonym.py已运行,更新 dict_full.json + synonym_data.json
7b. SCP 部署到线上
所有以下文件必须通过 Cloudflare Tunnel SCP 部署到 /var/www/ielts/:
site/reviews/剑X-TestX-PassageX-主题复盘.html → /var/www/ielts/reviews/
site/reviews/剑X-TestX-PassageX-主题复盘.json → /var/www/ielts/reviews/
site/answer-key.json → /var/www/ielts/
site/bilingual_data.json → /var/www/ielts/
site/dict_full.json → /var/www/ielts/
site/synonym_data.json → /var/www/ielts/
7c. 后端重启
ssh openclaw-tunnel "sudo systemctl restart ielts-api"
必须重启——后端启动时 buildReviewFileIndex() 扫描 reviews/ 目录建索引。不重启 = 新文件不出现在首页。
7d. 线上验证
-
getReadingPageDataAPI 返回新篇目数据 -
bilingual.html?book=X&test=Y&passage=Z能正常显示双语内容 - 复盘链接可正常访问(reviews/剑X-TestX-PassageX-主题复盘.html)
7e. HTML 模板检查
新生成的复盘 HTML 必须满足:
- hero-nav 使用
/ielts/reading.html(绝对路径,不是../reading.html) - 按钮文字为"目录"(不是"首页")
- icon 为
list(不是home)
曾犯的典型遗漏(引以为戒):
- 文件生成在根目录没 cp 到 site/reviews/
- bilingual_data.json 本地更新了忘了 SCP
- 只做了 P1 双语没做同套题的 P2/P3 双语
- 没重启后端导致 review 索引没刷新
- hero-nav 按钮用了旧模板(相对路径 + "首页")
- JSON 文件没 SCP 到 reviews/
Batch Import Mode (v3.2 — Legacy Review Folder → JSON)
触发场景:用户说"帮我把 XX 目录下的历史复盘都转成 JSON"、"批量导入我以前的复盘笔记"、"扫一下我电脑里的复盘"、"帮我找出所有历史笔记"等。
此模式下 Buddy 自主循环,无需用户自己找路径、无需一篇篇喂。
Step B0: Auto-Discovery(🔍 推荐默认起点)
不要开口就问用户"复盘文件夹在哪?"——先自动扫常见位置:
node ~/.workbuddy/skills/ielts-reading-review/scripts/scan-legacy-reviews.js --auto
脚本会扫描以下位置并按命中数推荐:
- 当前工作目录 (cwd)
~/Documents、~/Documents/个人、~/Documents/个人/WorkBuddy~/Desktop、~/Downloads~/Library/Mobile Documents/com~apple~CloudDocs(iCloud)~/WorkBuddy、~/WorkBuddy/Claw
输出 discoveries(去重后的真实命中目录,命中数多的子目录优先)和 recommended(首选目录)。
把发现结果呈现给用户:
我扫了你电脑常见位置,找到你的复盘应该在这里:
🎯 推荐:/Users/xxx/Documents/个人/WorkBuddy/雅思学习(60 个候选文件)
样例:剑6-Test3-Passage3-抗衰老药物复盘.html / 剑4-Test1-听力Part2-河滨工业村复盘.html …
其他候选:
- /Users/xxx/Downloads(4 个)
要用推荐目录还是选其他的?
用户点头后进入 Step B1 做精扫。只有当自动发现完全找不到候选(discoveries 为空)时,才问用户要具体路径。
Step B1: Scan the Folder
调用扫描脚本生成候选清单:
# 默认只扫顶层
node ~/.workbuddy/skills/ielts-reading-review/scripts/scan-legacy-reviews.js <目录> --out=/tmp/ielts-scan.json
# 需要递归子目录
node ~/.workbuddy/skills/ielts-reading-review/scripts/scan-legacy-reviews.js <目录> --deep --out=/tmp/ielts-scan.json
输出 JSON 结构(groups 按篇目聚合):
{
"totalFiles": 18,
"identifiedPassages": 6,
"groups": [
{
"passage": "C5-T1-P2",
"fileCount": 3,
"files": [
{ "path": "...", "ext": ".html", "hints": { "book": 5, "test": 1, "passage": 2, "title": "鲸鱼感官" } },
{ "path": "...", "ext": ".md" },
{ "path": "...", "ext": ".png" }
]
},
{ "passage": "__unknown__", "files": [...] }
]
}
Step B2: Show Plan & Confirm
读取 scan 结果后,必须先给用户一份执行计划,不要直接开干:
扫描完成,找到 18 个候选文件,识别出 6 篇复盘:
✅ 可识别:
1. 剑5-T1-P2 · 鲸鱼感官(HTML + MD + 截图,共 3 个文件)
2. 剑5-T1-P3 · 儿童认知(HTML)
3. 剑6-T2-P1 · ...
...
⚠️ 无法识别篇目(需你人工分配):
- notes-2026-03-15.md
- 错题整理.docx
我将逐篇处理可识别的 6 篇,每篇生成一个 JSON。预计 10-15 分钟。
确认开始?
用户点头后才进入 Step B3。
Step B3: Loop — Generate JSON for Each Passage
逐篇循环,每次处理一组:
- 读取该组所有文件内容(HTML 提纯文字、MD 直读、图片用视觉 OCR)
- 从内容中提取:原文/正确答案/用户答案/错题/时长/日期
- 关键兜底:
- 内容里找不到"正确答案" → 查
site/answer-key.json(本地答案库,401 题全覆盖) - 找不到用户答案 → 标注
score.correct = null让用户后续补 - 错题列表不清晰 → 只生成基础成绩单 JSON,
wrongQuestions: []
- 内容里找不到"正确答案" → 查
- 按 v3.0 schema 生成 JSON,文件名
剑X-TestX-PassageX-中文主题复盘.json - 写入用户指定的输出目录(默认
./batch-output/) - 输出一行进度:
✅ [3/6] 剑5-T1-P3 · 儿童认知 → 剑5-Test1-Passage3-儿童认知复盘.json
Step B4: Summary Report
全部完成后输出总结:
批量导入完成!
✅ 成功:5 篇(已生成 5 个 JSON 到 ./batch-output/)
⚠️ 部分数据缺失:1 篇(剑6-T2-P1 找不到用户答案,score.correct 置空)
❌ 跳过:0 篇
下一步:
👉 打开 https://tuyaya.online/ielts/submit.html?mode=json
👉 把 ./batch-output/ 里所有 JSON 拖进去,一键导入
Batch Mode Rules (MUST FOLLOW)
- 永远先 scan + confirm,绝不跳过计划确认
- 每篇独立处理,失败不阻塞下一篇(捕获异常,记录到 skip 列表)
- 绝不编造数据:用户答案缺失就置 null 或空数组,不要瞎填
- 同义替换/词汇/错因分析是可选项:老笔记里没有就不生成,不要硬凑
- 答案库优先:正确答案一律从
site/answer-key.json核对,笔记里的可能是老婆写错的 - 产物隔离:批量输出统一放
./batch-output/,不污染用户工作目录
Error Analysis Rules
TRUE / FALSE / NOT GIVEN 三步法
- 找话题 — 文章有没有讨论题目中的对象?→ 没讨论 = NOT GIVEN
- 找立场 — 讨论了的话,是同意还是矛盾?→ TRUE / FALSE
- 验证 — "如果选 TRUE/FALSE,能指出原文哪句话吗?" 指不出来 → 大概率 NOT GIVEN
关键区分:
- FALSE 需要直接矛盾证据,"没提到"= NG 不是 FALSE
- 概括性表达覆盖题目对象 = 算讨论过,不是 NG
however + adj=no matter how(让步),不是因果
Fill-in-the-blank
- 答案不能重复题干已有的词
- 填完必须通读:语法/词性/语义/字数 四项检查
such as ___→ 必须填具体例子the ___ of X→ 必须填能和 "of X" 搭配的名词
Common Pitfalls
- 过度推理:只看作者明确写了什么,不推导
- 被绝对词吓到:all/never 不一定错,看原文证据
- 人名观点混淆:先在文中标注每个人说了什么
- 邻近干扰词:从定位句提取答案,不要被旁边的句子污染
- Heading 复用 Example:做 Heading 题第一步永远是划掉 Example 已用的选项
- Although 从句看错重点:主句才是作者立场,让步从句只是背景
- 双重否定读反了:not unusual=usual, not uncommon=common, rarely+isn't=几乎总是。看到双重否定立刻翻译成肯定
- Summary 填空凭感觉:必须先回原文找对应句,再从选项中匹配。不回原文 = 全靠蒙
- 对比信号词忽略:"difference from X" 本身就是对比框架,后面的内容就是证据
- 选择题选了和全文论点相反的:选完后反问"这个选项和作者核心主张一致吗?"
Error Categories
参见 references/error-taxonomy.md,共 12 类错误分类。JSON 中 errorCategory 字段使用以下 ID:
| ID | 错误类型 |
|---|---|
synonym-failure | 同义替换识别失败 |
ng-false-confusion | NOT GIVEN / FALSE 混淆 |
over-inference | 过度推理 |
stem-repetition | 填空重复题干词 |
grammar-mismatch | 语法/让步句理解错 |
incomplete-option | 选项不完全匹配 |
vocab-gap | 词汇缺口 |
carelessness | 粗心/时间压力 |
word-form-error | 填空词形/词性错 |
scope-confusion | 跨代/范围混淆 |
category-reasoning | 类别推理误判 |
adjacent-distractor | 邻近干扰词 |
heading-example-reuse | Heading匹配复用Example已用选项 |
concessive-clause-confusion | Although/While让步从句混淆主句 |
double-negative-misread | 双重否定读不出肯定 |
summary-no-source | Summary填空没回原文定位 |
comparison-signal-ignored | 对比信号词(difference from等)忽略 |
selection-contradicts-thesis | 选择题选了和全文论点矛盾的选项 |
Style Guidelines
- 简洁直接,不废话
- 错题分析直说问题,不糖衣炮弹
- 中文为主,英语术语保留原文