Command routing
Check args:
"update"→ skill update."final"or"final "start → question generation, seeexampass-final.md。"graph"or"graph "start → knowledge graph, seeexampass-graph.md。"grade"or"批改"start → AI grading(below).- Other → default flow(below).
Files you need to read:
agents/skeleton-agent.md、agents/notes-agent.md、agents/item-agent.md(Part 4 of each is the actual prompt). Writing guidelines are inexampass-writing.md(read when needed).
Default flow
Per-chapter outputone merged page HTML: top tabs "📖 Knowledge Checklist | 📝 Chapter Test". The knowledge checklist includes a right-side PPT cross-reference column (full-page rendered images + original text), table of contents, four-color labels, error-prone blockquotes, and collapsible self-test answers. The chapter test features interactive answering + one-click grading.
Phase 0 · Extraction
cd <skill目录>
python scripts/run_exampass.py <课程目录>
Produce .epa_work/chapter_manifest.json + per-chapter chapters/<章名>/_extraction_bundle.json. Each manifest entry has source_files(absolute path to that chapter's source PDF).
Phase 1 · Skeleton
Read agents/skeleton-agent.md Part 4. Core rules:
- Read the manifest + each chapter bundle's merged_text to understand the content
- Output
knowledge_skeleton.json:{title, chapters:[{id:"ch1",label:"章名",kcs:[{id,label,type:"fact|concept|procedure|principle",importance:"must|key|freq|info",deps:[],is_hub:bool,source_refs:["PDF名/页码范围"]}]}]} - source_refs page numbers must be real(cannot exceed the PDF's actual page count; confirm the PDF page count before writing page numbers)
- Then write the slices:
slices/<章id>_skeleton.json+slices/<章id>_extract.json
Phase 2 · Notes + Questions (parallel per chapter)
Notes Agent prompt (condensed):
你是讲解设计师。读 [骨架切片路径] 和 [提取切片路径]。
为每个知识组件写一段讲解,输出到 [.epa_work/notes/<章id>.html]。
规则:
1. 叙事弧:Hook(一句话场景/谜题)→ TL;DR(<div class="tldr">粗体一句话结论</div>)→ Why → What → How → Checkpoint自测(<div class="checkpoint">问题<details class="cp-answer"><summary>参考答案</summary>答案</details></div>)
2. 核心概念包 <span class="kp">…</span>,解释包 <span class="exp">…</span>
3. 每个H3末尾标四色标签(中文:"必考""重点""高频""了解"),class用 tag-must/tag-key/tag-freq/tag-info
4. 易错用 <blockquote>易错:…</blockquote>
5. 公式 $$…$$ 或 $…$,引号用「」,箭头 -->
6. 每个H2/H3末尾加 <span data-slides="页码"></span>(从 source_refs 取该KC的起始页码)
7. 只写H2/H3,不写H1。不要输出 <!DOCTYPE>/<html>/<head>/<style>/<body> 标签——只输出 body 内部片段
8. 每个概念必答"是什么/为什么需要它/怎么用"
Questions Agent prompt (condensed):
你是命题专家。读 [骨架切片路径] 和 [提取切片路径]。
为该章出一套题目,输出到 [.epa_work/questions/<章id>.json]。
规则:
1. 题型按学科选——算法课必须包含 calc 和 code,文科不出 calc/code
2. 题量:每个KC至少1题,枢纽概念至少2题。整章≥8题
3. choice:4个选项,正确答案 A/B/C/D 均匀分布(各字母次数差≤1),干扰项对应真实误解
4. 文本里不等号用 < 和 > 或 $...$,禁止裸 <
5. JSON字符串内引号用「」,禁止裸 "
6. 每题含 kc_id/cognitive_level(remember|understand|apply|analyze)/difficulty(basic|medium|hard)
7. 写完用 json.loads() 自检
JSON结构:{"chapter_id":"chX","questions":[{"type":"choice|tf|fill|calc|code|short","points":N,"kc_id":"kcX.Y","question":"...","options":["A","B","C","D"],"answer":0,"explanation":"...","cognitive":"...","difficulty":"...","pitfall":"..."}]}
对于 tf(判断)题:answer: true 表示命题正确(正确),answer: false 表示命题错误(错误)。禁止对 tf 题使用数字 0/1 —— 只用布尔值 true/false。
File paths in the above prompts: skeleton slices are in
.epa_work/slices/<章id>_skeleton.json, extraction slices are in.epa_work/slices/<章id>_extract.json. Notes are written to.epa_work/notes/<章id>.html, questions are written to.epa_work/questions/<章id>.json。If you can finish writing chapter by chapter yourself (without spawning sub-agents),just write directly— quality first, don't parallelize just for the sake of parallelizing. If you spawn sub-agents, make sure each sub-agent only gets its own chapter's slices and doesn't read the entire bundle.
Phase 3 · Review + Revision (optional)
If time permits, you can spawn reviewer-agent + solver-agent to review chapter by chapter. Butit is not required— if the notes and questions in Phase 2 were written carefully, it's fine to skip review and render directly.
Phase 4 · Rendering
python scripts/render_chapters.py <课程目录> full
This will render each chapter's PDF all pagesinto images and paste them into the right column (full density = PPT cross-reference column contains all original pages). If the file is too large and you want to include only key pages, change full to key. Rendering output is in <课程目录>/EPA/, one merged HTML page per chapter.
Open any HTML under EPA/ in a browser to use it.
AI grading flow
args is in the form "grade <JSON路径>" or "批改 <JSON路径>"。
Read the answers JSON(generated by the "AI one-click grading" button on the interactive test page, downloaded to the downloads folder by default):
{
"chapter": "数据结构 - 第3章",
"saved_at": "2026-06-25T14:30:00",
"questions": [
{
"index": 0, "type": "tf", "question": "...",
"options": null, "correct_answer": true,
"user_answer": 0, "points": 2,
"explanation": "...", "kc_id": "kc3.1", "images": []
}
]
}
Step 1 · Automatic grading of objective questions(choice / multi / tf / fill):
- Using the same logic as
test_js_template.js(tfAnswerIndex/choiceAnswerIndex/normText) compare answers - Scoring: all correct = full marks, multiple-choice partially correct = prorated, fill-in-the-blank graded per blank
Step 2 · AI grading of subjective questions(short / calc / code / essay / comprehensive):
- For each question: read the question + user's answer + analysis
images(if any) - Give a score from 0 to full marks and a brief grading comment
- Refer to the
explanationfield to provide the standard explanation
Step 3 · Statistics:
- Calculate total_score / objective_score / subjective_score
- Group by kc_id to compute the mastery level of each knowledge point (score/full marks)
Step 4 · Generate report:
from scripts.template_engine import save_grade_report_html
import os, json
from datetime import datetime
# 构造 grade_data(见 save_grade_report_html 文档)
grade_data = {
"chapter": answers["chapter"],
"submitted_at": answers["saved_at"],
"graded_at": datetime.now().isoformat(),
"grade_count": 1, # 根据已有批改报告数量+1
"total_score": ...,
"max_score": ...,
"objective_score": ..., "objective_max": ...,
"subjective_score": ..., "subjective_max": ...,
"results": [...], # 每题: index/type/question/options/user_answer/correct_answer/score/max_points/verdict/comment/explanation/kc_id/images
"kc_mastery": {...}, # kc_id → {label, score, max, pct}
"wrong_questions": [...] # wrong/partial 题的 index 列表
}
# 输出到 answers JSON 所在目录
json_dir = os.path.dirname(json_path)
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
ch = answers["chapter"].replace(" ", "_").replace("/", "_")[:30]
out_path = os.path.join(json_dir, f"{ch}_批改_{ts}.html")
save_grade_report_html(grade_data, out_path)
print(f"报告已生成:{out_path}")
Determine grade_count: scan the same directory for the number of *_批改_*.html files + 1.
Note: the correct_answer field of some tf questions may have data issues (the answer is filled in reversed); during grading, if it is confirmed that correct_answer and explanation contradict each other, take explanation as authoritative, and in comment note "the data may be incorrect, please review".
Skill update
When running /exampass update , execute:
$SkillDir = "$env:USERPROFILE\.claude\skills\exampass"
Push-Location $SkillDir
git fetch origin master 2>&1
$Behind = [int](git rev-list --count HEAD..origin/master 2>&1)
Write-Host "落后 origin/master:$Behind 个提交"
if ($Behind -eq 0) { Write-Host "已是最新!"; Pop-Location; return }
$Status = git status --porcelain
if ($Status) { git stash push --include-untracked -m "exampass-update-$(Get-Date -Format yyyyMMdd-HHmmss)" }
git pull origin master
git stash pop 2>$null
pip install -r requirements.txt
Write-Host "更新完成"; git log --oneline -5
Pop-Location