试卷生成 · 题库抽题组卷
这是什么
组卷这件事,难的从来不是"写题",而是配比:这一章考几个知识点、易中难各占多少、每题几分、总分对不对得上、答案有没有漏、两个班要不要出 B 卷。
这个技能不替你出题。它做的是你出题之后必须做的那些机械活:
> 把你已经攒好的题库(Excel / CSV / 文本都行),按你写的双向细目表,确定性地抽出来一套卷,然后排版成
> 试卷 docx + 参考答案与评分标准 docx + 答题卡 docx + 双向细目表 xlsx + 组卷体检报告 md。
确定性是这个技能的核心:同一份题库 + 同一张细目表 + 同一个 --seed,抽出来的卷子永远是同一套。改一个参数,换一套,但可复现、可解释、可对比。这跟"让大模型编一套题"是两件事——后者每次都不一样,也没法向教研组长解释为什么这套卷子长这样。
什么时候用它
- 单元测验 / 月考 / 期中期末:从章节题库里按配比抽一套,直接打印
- 教研组统一命题:细目表统一,各班用同一
--seed出同一套卷;要防抄袭就--ab出 A/B 卷 - 企业内训考核:入职培训、岗位认证、制度宣贯,题库就是那 100 条制度要点
- 题库治理:先跑
check,看题库里到底有多少道题是"废的"(缺答案、答案不在选项、选项重复),再决定补题方向
快速开始
# 1) 只看效果:用内置虚构题库出一套完整样卷(不碰你的数据)
python3 juan_cli.py demo --out ./out
# 2) 体检你的题库
python3 juan_cli.py check --bank 我的题库.xlsx --md 题库体检.md
# 3) 正式组卷
python3 juan_cli.py build --bank 我的题库.xlsx --blueprint 细目表.json --out ./out --seed 20260101
# 4) 双班同考:一次出 A/B 两套(不重题)
python3 juan_cli.py build --bank 我的题库.xlsx --blueprint 细目表.json --out ./out --ab
# 5) 自测(12 组合法输入 + 11 组缺陷输入 + 端到端回读)
python3 juan_cli.py selftest
demo 跑完会在 ./out 得到一份 27 题 / 100 分的完整样卷,可以直接打开看排版。
命令速查
| 命令 | 作用 | 关键参数 |
|---|---|---|
check |
题库体检,不出卷 | --bank 必填;--md 写报告;--json 供流水线用 |
build |
组卷 + 出全套成品 | --bank --blueprint --out --seed --strict --ab --no-card |
demo |
虚构题库跑通全流程 | --out --seed |
selftest |
内置回归 + 端到端回读 | 无 |
--strict:抽题不够或有阻断项就不出文件(适合正式考试,宁可报错也不出残卷)。
不加则先出卷,缺的题在报告里标出来(适合先看排版)。
题库怎么准备
支持 xlsx / csv / tsv / md / txt。表格类自动认列名,中英文都认:
| 规范字段 | 可用的列名 |
|---|---|
| 题型 | 题型、类型、题目类型、type、qtype |
| 知识点 | 知识点、考点、章节、知识模块、kp |
| 难度 | 难度、难易度、难度层级、difficulty(易/中/难 或 1-5) |
| 题干 | 题干、题目、试题、题目内容、stem、question |
| 选项 | 选项A…选项H、A…H、选项1…选项8 |
| 答案 | 答案、正确答案、参考答案、answer |
| 解析 | 解析、答案解析、详解、explanation |
| 分值 | 分值、分数、score |
| 题号 | 题号、编号、序号、no、id |
最简可用只要求三列:题干 + 答案 + 题型。其余缺了也能跑,只是体检会提示。
文本题库(md/txt)两种写法都行,详见 题库格式说明:
[单选][示例知识点甲][易][3分] 示例题干,选出最恰当的一项( )
A. 甲项
B. 乙项
C. 丙项
D. 丁项
答案:C
解析:示例解析
细目表怎么填
{
"subject": "示例学科",
"grade": "示例年级",
"purpose": "diagnostic",
"duration_min": 45,
"total_score": 100,
"sections": [
{"type": "single", "name": "单项选择题", "count": 10, "score_each": 3},
{"type": "multi", "name": "多项选择题", "count": 4, "score_each": 4},
{"type": "judge", "name": "判断题", "count": 6, "score_each": 2},
{"type": "blank", "name": "填空题", "count": 5, "score_each": 4},
{"type": "essay", "name": "解答题", "count": 2, "score_each": 11}
]
}
purpose决定默认难度配比:diagnostic诊断 70/25/5、final终结性 60/30/10、selection选拔性 40/40/20- 每个大题可用
"difficulty": {"easy": 0.7, "medium": 0.25, "hard": 0.05}单独覆盖 - 需要按知识点配额,加
"knowledge": {"示例知识点甲": 3, "示例知识点乙": 3} - 各题
score_each × count之和必须等于total_score,否则直接判阻断,不出卷
字段全解与常见填错见 细目表指南。
产出物
build 一次给你五个文件:
| 文件 | 里面有什么 |
|---|---|
试卷.docx |
A4 卷头(学科/年级/满分/时长/姓名栏/密封线提示)、大题分组、连续题号、每题分值、选项自适应 1/2/4 列、主观题按分值留答题行 |
参考答案与评分标准.docx |
每题答案 + 知识点与难度标注 + 解析;解答题按「要点一…;要点二…」逐条列给分点 |
答题卡.docx |
表头信息框、客观题涂卡格(A□B□C□D□)、主观题按分值给书写行 |
双向细目表.xlsx |
3 个 sheet:逐题登记、知识点×题型分值矩阵、难度分布与时长核对 |
组卷报告.md |
抽题结构、缺口清单、难度与知识点分布、体检结论、需要人判断的地方 |
体检规则(都是确定性判断)
题库级(check 与 build 都会跑):
| 代码 | 级别 | 含义 |
|---|---|---|
| B00 | 阻断 | 找不到表头 |
| B01 | 阻断 | 题干为空 |
| B03 | 阻断 | 选择题选项少于 2 个 |
| B04 | 提醒 | 有重复选项 |
| B05 | 阻断 | 选择题缺答案 |
| B06 | 阻断 | 答案字母不在选项里 |
| B07 | 提醒 | 低质选项("以上都对""都不对") |
| B08 | 提示 | 难度无法识别,按「中」处理 |
| B10 | 提示 | 缺分值,按大题统一分值处理 |
| B11 | 阻断 | 判断题答案不是对/错 |
| B12 | 提醒 | 题干高度相似(≥95%)且选项一致,疑似重复收录 |
试卷级(build 后):
| 代码 | 级别 | 含义 |
|---|---|---|
| P01 | 阻断 | 卷面合计 ≠ 目标总分 |
| P02 | 提醒 | 某选项作为答案占比 >40%(能蒙) |
| P03 | 提醒 | 连续 4 题答案相同 |
| P04 | 阻断 | 题号不连续 |
| P05 | 提醒 | 单知识点占分 >40%(覆盖偏窄) |
| P06 | 提醒 | 解答题没有解析/给分点 |
| P07 | 提醒 | 题量与时长的经验估算偏差过大 |
| P08 | 阻断 | 有题缺答案 |
| P00 | 提醒/阻断 | 抽题不足(--strict 下升为阻断) |
有阻断项的题不会进入试卷(不是"提示一下照样出"),所以不会出现答案缺失的题被印到卷子上。
抽题是怎么算的
- 按大题把
count用最大余数法拆到易/中/难三层,保证三层之和正好等于题数(不会因为四舍五入少一道) - 有知识点配额时,先在知识点内部再拆难度
- 每个槽位用
sha256(seed + 题号 + 大题名)排序取第一个可用题 —— 这就是"确定性"的来源:不依赖 Python 随机数实现,跨机器、跨版本结果一致 - 优先取当前用得少的知识点,避免一套卷子全压在一个知识点上
- 某槽位没有可用题 → 记缺口;
--strict直接失败,不--strict则留空并在报告标注 --ab时 B 卷用seed+1并排除 A 卷已用题,保证两套不重题
五个典型场景
1. 八年级单元测验(45 分钟 / 100 分)
写细目表 10 单选 + 4 多选 + 6 判断 + 5 填空 + 2 解答 → check 看题库够不够 →
build --strict 出卷 → 打开 组卷报告.md 确认难度分布符合预期 → 打印。
2. 期末统一命题,六个班同一套
--seed 20260601 固定,六个班都用同一个 seed,出的是同一套卷,教研组长可复核。
3. 两个班连考,防邻座抄袭
--ab:A 卷 seed,B 卷 seed+1 且不含 A 卷题目。报告里两套的难度与知识点分布都能对上。
4. 企业内训考核(100 条制度要点当题库)
题库列为「题型=判断/单选、知识点=制度章节」,细目表按章节配额,出的就是按章节覆盖的考核卷。
5. 题库治理
check --md 体检.md,看 B05/B06 有多少条 → 按报告逐条修 → 修完再 build。
一个 200 题的题库通常能揪出 10-30 条"看着有、其实不能用"的题。
不适用 / 限制(先说清楚)
- 不生成题目。它只从你给的题库里选。没有题库就没法用——这不是缺点,是定位。
- 不判断题目对不对、难不难。难度标签来自你的标注,脚本只核对标签与细目表是否一致。题目质量、是否超纲、答案是否有争议,都要人复核。
- 不画图形、不排公式。理科图形与复杂公式请在 Word 里补(本技能不生成图片,也不依赖 LaTeX——所以不需要装 TeX 环境)。
- 不做押题、不预测真题。这类需求直接拒绝。
- 排版是 Word 原生排版:A4 纵版、单栏。需要 A3 三栏/8 开/双栏的印刷级排版,本技能不覆盖。
- 答题卡是"涂卡格 + 书写行"的通用版,不含机器阅卷需要的定位点/条码。
运行环境
- Python 3.9+,
python-docx、openpyxl(都已装则开箱可用) - 零网络、零 API Key、零大模型调用,全程不联网
- 单次组卷实测 <0.2 秒(42 题题库出 27 题卷,含四份文件写出)
自测
python3 juan_cli.py selftest
# 23/23 通过(12 组合法输入 0 误报 + 11 组缺陷输入全部命中)
# ✓ 试卷回读 / 答案卷回读 / 答题卡回读 / 细目表回读 端到端:通过
回读是重新用 python-docx / openpyxl 打开生成的文件,确认段落数、表格数、sheet 行数真的写进去了——不是"脚本没报错"就算过。