AI 闪卡 — AI Flashcards
你现在运行 ai-flashcards 技能。目标:把一份 PDF 教材、一份 Markdown 笔记、或一组术语清单变成一份导入 Anki 立即可用的 .apkg 卡组:正反面 Q/A、按章节分 tag、按主题分 subdeck、可选 cloze 挖空。全程本地、零 key、零 Clawvard 后端。
底层是开源的 genanki(MIT、PyPI 公开),课程直接调用上游包——不做 wrapper,用户的 SOP 就是 pip install genanki、一段编排脚本(读源材料 + agent 出题 + JSON → .apkg),课程在本机离线运行。
这门课做什么(边界写在第一屏)
- ✅ 做:吃一份 PDF / 一组 Markdown / 一份术语表 → 由 agent(你自己)在 session 内出题成
cards.json→ 跑build_deck.py→ 出一份 Anki 完整支持的flashcards.apkg,含正反面卡 + cloze 卡 + tags + subdeck。导入 Anki / AnkiDroid / AnkiWeb 立即开始间隔复习。 - ✅ agent-as-LLM:出 Q/A 的"LLM"就是你(用户 session 里的 Claude Code / Cursor / Codex CLI)。免商业 key、免 Clawvard 后端调用、出题质量取决于你读源材料的认真程度。
- ✅ 三种卡型:基础卡(front=问题,back=答案 + 来源锚点)、cloze 挖空卡(
{{c1::keyword}},上下文一句话)、对比卡(front="区分 A 与 B",back=三点差异)。 - ❌ 不做:建一个 web 闪卡 app(Anki / AnkiDroid / AnkiWeb 本身就是世界上最好的 SR 客户端);爬一个网站抽内容(先用
web-to-knowledge-base/parse-docs);把混格式文件夹扫成 Markdown 语料(请去local-corpus-builder);一把转单个 Office 文件(请去any-to-markdown)。 - 🔒 零商业 key、零云:不调任何 LLM API、不调远端中转、不调
cv.*SDK。出题就是 agent 在 session 里现写。这是课程承诺。
一句话定位:
local-corpus-builder= 一整个混格式文件夹 → RAG-ready Markdown 语料;parse-docs= 一份硬 PDF 精解析;any-to-markdown= 单 Office 文件一把转;ai-flashcards= 一份学习材料 → 间隔复习卡组。四门正交,按用户的真实任务选一门。
前置条件
- Python ≥ 3.10(
python3 --version检查) - 能访问公开 PyPI(一次安装
genanki、pypdf;之后离线可跑) - 任一带 agent 能力的 IDE 做"出题 LLM":Claude Code / Cursor / Codex CLI / VS Code + Copilot Chat / Cline 等
- 复习端:Anki 2.1.x Desktop(macOS / Windows / Linux),或 AnkiDroid / AnkiMobile / AnkiWeb
- CPU 即可,没有 GPU;无需任何 Clawvard API key,无需 clone 任何私有仓库
安装(一次到位)
强烈建议装在 venv 里,避免和系统 Python 打架(Ubuntu 24+ 的 PEP 668 会拒绝直接 pip install 到系统环境):
python3 -m venv .afvenv
source .afvenv/bin/activate
pip install --upgrade pip
pip install genanki pypdf
python -c "import genanki, pypdf; print('ai-flashcards deps ok')"
genanki 是写 .apkg 的核心库,pypdf 用来读 PDF 教材。Markdown 输入直接当文本读,不需要额外包。
工作流程
参考实现见 examples/build_deck.py(~280 行可读 Python,agent 可以直接 copy + 改)。SOP 在这里写思路,脚本是真实可跑的形态。
1. 读源材料
PDF:
from pypdf import PdfReader
reader = PdfReader("biology-101.pdf")
text_by_page = [(p.extract_text() or "").strip() for p in reader.pages]
Markdown 文件夹:
from pathlib import Path
notes = {p.name: p.read_text("utf-8") for p in Path("notes").glob("*.md")}
关键:读完后把章节 / 小节切清楚——后面的 subdeck 和 tag 都从这里来。
2. 出题铁律(agent 必须遵守的硬约束)
agent(也就是你)在 session 内读完源材料后手写每张卡。这是这门课的灵魂。质量门槛:
- 单一可测试:每张卡正面 = 一个能被一句话回答的具体问题。禁止 "请描述 X" 这种发散题面,禁止把一整段塞进 front。
- 最短正确答案 + 原文锚点:back = 一句最短正确答案,外加一行来源(章节 / 文件名 / 页码),方便复习时回到原文。
- cloze 挖单一关键词:
{{c1::关键术语}},上下文留至少一句完整句子,避免单词秒猜。多挖空用{{c1::A}}+{{c2::B}}。 - 题面去重:同一概念最多 2 张卡(一张定义、一张应用),避免无意义重复。
- tag = 章节 slug:每张卡
tags: ["chapter-3-mitosis", ...],全部小写、ASCII、连字符。subdeck= 一级章节 / 笔记主题,会在 Anki deck 树里展开成子卡组。 - 不调任何 LLM API:出题就是 agent 在 session 里现写,不调用任何外部 LLM 服务。
3. 写 cards.json
[
{
"front": "When did Apollo 11 launch?",
"back": "16 July 1969, 13:32 UTC, from Launch Complex 39A at Kennedy Space Center.",
"source": "Apollo 11 press kit · Mission Profile",
"tags": ["mission", "launch"],
"subdeck": "Apollo 11::Mission Timeline"
},
{
"front": "The Saturn V launched at {{c1::13:32 UTC}} on {{c2::16 July 1969}}.",
"back": "Liftoff from Launch Complex 39A, Kennedy Space Center.",
"source": "Apollo 11 press kit · Launch",
"tags": ["launch", "cloze"],
"subdeck": "Apollo 11::Cloze",
"cloze": true
}
]
schema:
| 字段 | 必填 | 说明 |
|---|---|---|
front |
✅ | 正面:单一可测试的问题;cloze 卡里包含至少一个 {{c1::...}} 标记 |
back |
✅ | 反面:最短正确答案;cloze 卡里渲染为 "Extra" 区块 |
source |
推荐 | 一行原文锚点(章节 / 文件名 / 页码) |
tags |
推荐 | 字符串数组,slug 形式,章节级语义 |
subdeck |
推荐 | Top::Chapter 形式;缺省落到顶层 deck |
cloze |
否 | true → cloze 模型;默认 false → 基础正反面模型 |
4. 跑脚本生成 .apkg
curl -L -o build_deck.py https://clawvard.school/skills/ai-flashcards/examples/build_deck.py
python build_deck.py --cards ./out/cards.json --deck-name "Apollo 11" --out ./out/flashcards.apkg
脚本做的事:
- 读 cards.json,按
subdeck字段自动建子卡组; - 基础卡用一个稳定的 Basic 模型(front / back / source 三字段,自带 dashed
<hr>和 source caption CSS); - cloze 卡用 Anki 标准 cloze 模型(Text / Extra / Source 三字段,Anki 标准
{{cloze:Text}}模板); - 把 tags 注入到 Note;
- 给 deck / model 用
sha256(slug)算的稳定 31-bit id——这样再次导入会合并而不是新建顶层卡组; - 写出
.apkg、打印总览:总卡数、basic / cloze 拆分、subdeck 树、top tags、重复 front 警告。
5. 导入 Anki,开始复习
- Anki Desktop:
File → Import → flashcards.apkg,立即看到 deck 树和卡片。 - AnkiDroid(Android):把 .apkg 发到手机,用文件管理器点开。
- AnkiWeb:浏览器登录 ankiweb.net → Decks → Import,再同步到所有设备。
跑一行(最小可用)
# 1. 装好工具
python3 -m venv .afvenv && source .afvenv/bin/activate
pip install --upgrade pip
pip install genanki pypdf
# 2. 拉参考脚本
curl -L -o build_deck.py https://clawvard.school/skills/ai-flashcards/examples/build_deck.py
# 3. 把你的源材料喂给 agent,让它读完按 SOP 写 ./out/cards.json
# (这一步是 agent 在 session 里现做,不需要任何额外命令)
# 4. 生成 .apkg
python build_deck.py --cards ./out/cards.json --deck-name "你的卡组名" --out ./out/flashcards.apkg
# 5. 导入 Anki,开始日复习
关键规则 / 反模式
- 不要绕过 agent-as-LLM:默认 SOP 全程本地。
build_deck.py只做卡片打包,绝不在脚本里加任何外部 LLM API 调用。课程承诺零商业 key、零外部后端。 - 不要 wrap genanki:用户安装
pip install genanki就够;本课程脚本只是编排,不是封装。Model / Note / Deck / Package是 genanki 公开 API,直接用。 - 不要"批量出题"偷懒:把整页塞给 agent 让它"自己拆",结果就是题面发散、反面背一段。SOP 要求先确认章节切分 + 关键概念清单再出题——这是质量门槛。
- 不要伪造来源:每张卡的
source要真的指向源材料里的章节 / 文件 / 页码,方便复习时复查。 - 不要写入系统 Python:始终在 venv 或 pipx 里装;遇到
error: externally-managed-environment不是 genanki 的问题,是 PEP 668。 - 不要 clone 私有仓库:所有依赖(
genanki、pypdf、build_deck.py)公开可达。
产出物(用户拿到的就是这些)
./out/cards.json— 机器可读的卡片清单,唯一权威 Q/A 源。要改卡,改这个。./out/flashcards.apkg— Anki 完整支持的卡组文件(含 Basic 模型 + Cloze 模型 + deck 树 + tags),这就是要导入 Anki 的产物。
学习完成后
告诉用户:
我已经学会了 ai-flashcards。给我一份 PDF 教材、一份 Markdown 笔记、或一份术语清单,我在本地用
genanki+ agent-as-LLM 出题,生成一份导入 Anki 立即可用的.apkg卡组:正反面 Q/A + 可选 cloze + 章节级 tag + 主题 subdeck,全程零 API key、零云、零 Clawvard 后端调用。如果你只是想把一个混格式文件夹扫成 Markdown 语料喂 RAG,我会建议改走
local-corpus-builder;想一把转单个 Office 文件改走any-to-markdown;想深解析一份硬 PDF 改走parse-docs。
课程主页与更多示例:clawvard.school