# 试题导入说明书(AI 生成 / AI 整理 专用数据契约) > 版本 1.0 · 2026-11-04 · 适用于 Ustudy 题库 `POST /api/questions/import/preview` → `commit-raw` > > **本文件的首要读者是 AI**。可直接整段贴给大模型作为上下文,或把第 6 章的提示词单独交给它。 > 业务现实:**基本不做人工单题录入**,题目来自 ① AI 直接出题、② 外部下载的资料经 AI 整理, > 两条路径最终都必须产出**同一种 JSON 格式**,再走批量导入。人工补录见附录 A。 ## 0. 三条铁律 1. **唯一交付物是一个 UTF-8 JSON 数组**。不要 Markdown 代码块、不要前言后语、不要注释、不要 JSON5/尾随逗号。 2. **只输出第 2 章契约里的字段**。多余字段会被服务端静默丢弃,缺字段或类型错会导致整行无效甚至导入失败。 3. **导入前必须跑离线校验**(第 7 章 `validate_questions.py`)。AI 输出不稳定,先自检再上传,不要在管理后台里逐行找错。 ## 1. 数据流水线 ``` 外部资料(PDF/Word/网页/Excel/图片OCR) 或 出题需求(学科/年级/知识点/题量) │ │ └──────────────► ① 提取为纯文本/Markdown ◄──────────────┘ │ ② AI 按本契约整理成 JSON 数组 (提示词:第 6 章 / ustudy_ai_prompt.txt) │ ③ 本地校验:scripts/validate_questions.py ← 必经关卡,不写库 │ 通过(exit 0) ④ 管理后台「批量导入」:上传 → 预览 → 确认入库 │ ⑤ 展示:Web 公式渲染 / 客户端线性化 + 几何图 ``` 第 ③ 步是这套流程的关键:它把「AI 写错格式」挡在导入之前,且能顺手查出知识点编号不存在、 题干重复、公式标记与内容格式不匹配等隐患。 ## 2. 字段契约(权威定义) ### 2.1 字段表 | 字段 | 类型 | 必填 | 约束 | 不填 / 填错的后果 | |------|------|------|------|-------------------| | `type` | string | ✅ | 恰好 `single` / `multi` / `judge` / `fill` | 其它值 → 整行判「无效题型」 | | `title` | string | ✅ | 非空、单道题的完整题干 | 空 → 「缺少题干」;**同时是去重键** | | `options` | **array\** 或 null | single/judge ✅ | 每个元素一个选项,建议 `A. xxx` 前缀;single/judge 至少 2 项 | 缺失或非数组 → 「选择题需选项」/ 导入异常;**JSON 里写成字符串会直接失败** | | `answer` | **string** | ✅ | 见第 3 章 | 空 → 「缺少答案」;写成数组会直接失败 | | `explain` | string \| null | 建议 | 解题过程,答错时展示 | 缺失则学生答错看不到解析 | | `knowledge` | string \| null | 建议 | 知识点展示文本 | 缺失影响薄弱点统计可读性 | | `knowledge_code` | string \| null | 选填 | **必须是系统里已存在的知识点编号** | 填了不存在的编号 → 整行判无效;AI 不得编造,宁可留空 | | `subject` | string | **建议 → 强烈建议** | 枚举:语文/数学/英语/物理/化学/生物/历史/地理/政治/科学/其他 | 影响学科筛选与统计;**客户端弹窗已支持按学科本地筛选**,不填则无法被具体学科筛到(只能用「全部」) | | `grade` | string | **建议 → 强烈建议** | 枚举:小一~小六 / 初一~初三 / 高一~高三 | 影响年级筛选与知识点联动;**客户端弹窗已支持按年级本地筛选**,不填则无法被具体年级筛到 | | `difficulty` | string \| null | 选填 | `易` / `中` / `难` | — | | `status` | int | 选填 | `1` 上架 / `0` 下架 | 导入不填默认 1 | | `content_format` | string | 选填 | `plain`(默认)/ `latex` | 题干含数学标记却填 plain → 学生看到 `\frac{1}{2}` 源码 | | `figure_url` | string \| null | 选填 | `/static/media/文件名.png` 或 `http(s)://...` | 不得填占位/编造地址;无图请留空 | | `figure_alt` | string \| null | 几何题建议 | 图形文字说明,图片缺失时降级展示 | 无图又无说明 → 题目无法作答 | **不要输出** `id` / `updated_at` / `created_at`(服务端生成)。未知字段一律丢弃。 ### 2.2 JSON Schema(可交给 AI 或校验程序) ```json { "$schema": "http://json-schema.org/draft-07/schema#", "type": "array", "items": { "type": "object", "required": ["type", "title", "answer"], "additionalProperties": false, "properties": { "type": {"enum": ["single", "multi", "judge", "fill"]}, "title": {"type": "string", "minLength": 1}, "options": {"type": ["array", "null"], "items": {"type": "string"}}, "answer": {"type": "string", "minLength": 1}, "explain": {"type": ["string", "null"]}, "knowledge": {"type": ["string", "null"]}, "knowledge_code": {"type": ["string", "null"], "maxLength": 40}, "subject": {"type": ["string", "null"]}, "grade": {"type": ["string", "null"]}, "difficulty": {"enum": ["易", "中", "难", null]}, "status": {"type": "integer", "enum": [0, 1]}, "content_format": {"enum": ["plain", "latex"]}, "figure_url": {"type": ["string", "null"], "maxLength": 255}, "figure_alt": {"type": ["string", "null"], "maxLength": 255} } } } ``` ### 2.3 三个最容易踩的坑 - **反斜杠必须双写**:JSON 字符串里 `\frac{3}{4}` **通常不会报错**,但 `\f` 会被解析成换页符, 公式静默变成 `rac{3}{4}` 乱码;`\times` 会变成制表符 + `imes`。必须写 `"$\\frac{3}{4}$"`。 这是 AI 整理数学题时最高频、也最难肉眼发现的错误,校验器会硬拦截。 - **`options` 必须是数组**:`["A. 1", "B. 2"]`,不能是 `"A. 1\nB. 2"`(那是 Excel 写法)。 - **`answer` 必须是字符串**:多选写 `"ABCD"` 或 `"[\"A\",\"B\"]"`,不能写 `["A","B"]`。 ## 3. 题型与答案规则 | type | options | answer 写法 | 客户端判题逻辑 | |------|---------|-------------|----------------| | `single` 单选 | 必填 ≥2 | 单个大写字母,如 `"C"` | 所选字母 ∈ 答案字母集合 | | `multi` 多选 | 必填 ≥2 | 字母组合 `"ABCD"`,或 JSON 字符串 `"[\"A\",\"B\"]"` | 所选字母集合 **完全相等** | | `judge` 判断 | **必填**,固定 `["A. 对", "B. 错"]` | `"A"` 或 `"B"` | 同单选 | | `fill` 填空 | 省略或 `null` | 答案文本,如 `"8"`、`"H2O"` | `答案.lower() in 学生输入.lower()`(**包含匹配**) | > ⚠️ 判断题也必须给 options——这是服务端校验器的真实要求(早期设计文档说「可省略」,以本表为准)。 填空答案规则(决定「录入后能不能判对」): - 答案要**短、唯一、不带解释**,不要写 `"答案是 8(由 3x=24 得)"`。 - 答案是文本**精确/包含比对**,不做数学等价判断:`0.5` ≠ `1/2`,`a^(m-n)` ≠ `a^{m-n}`。 - 公式结论题**优先出成选择题**;若必须填空,答案写线性文本(如 `3/4`), 且题干 `content_format=latex` 时客户端会把答案线性化后再比对。 - 有多个等价答案时,在 `explain` 里说明,标准答案只保留一个;或改出选择题。 ## 4. 公式与几何图形规则 ### 4.1 何时用 `latex` 仅当题干/选项/解析里出现数学标记时,把 `content_format` 设为 `latex`;否则留 `plain`(默认)。 `plain` 下内容原样显示,可直接用 Unicode:`½ ² ³ √ ∠ △ π × ÷ ∥ ⊥ °`。 ### 4.2 支持的轻量标记(自定义子集,不是完整 LaTeX) | 类别 | 写法 | Web 显示 | 客户端显示 | |------|------|----------|------------| | 分数 | `\frac{3}{4}`、`\frac{1}{3}\pi r^{2}h` | 上下分式 | `3/4`、`1/3πr²h` | | 上/下标 | `2^{3}` / `x^2` / `V_{柱}` / `a_1` | 上下标 | `2³` / `x²` / `V(柱)` / `a₁` | | 根式 | `\sqrt{2}` | 带横线根号 | `√2` | | 圆与几何 | `\pi r^{2}`、`\angle A`、`\triangle ABC`、`\parallel`、`\perp`、`50^{\circ}` | 符号排版 | Unicode 同形 | | 运算 | `\times \div \cdot \pm \approx \ne \le \ge` | × ÷ · ± ≈ ≠ ≤ ≥ | 同左 | | 定界 | `$...$` | 去掉定界符 | 去掉定界符 | ### 4.3 外部资料里的完整 LaTeX 要这样改写 | 外部常见写法 | 转换动作 | |--------------|----------| | `\dfrac{1}{2}`、`\frac12` | 改成 `\frac{1}{2}`(必须有两组花括号) | | `\[ ... \]`、`$$...$$`、`\begin{equation}` | 改成 `$...$` 或直接写内容(不支持块级公式) | | `\left( ... \right)`、`\text{}`、`\overline{AB}` | 去掉这些命令,保留括号内/文字内容 | | `\begin{matrix}`、`\int`、`\sum`、`\lim`、多行对齐 | **不支持**:改写成线性文字,或整段做成 PNG 用 `figure_url` 挂载 | | ``、HTML 标签 | 一律删除(系统先转义再渲染,HTML 只会显示成源码文本) | ### 4.4 图形 - 有真实图片:`figure_url` 填**已上传**得到的 `/static/media/xxx.png`,或可信的 `http(s)://...`; 同时把图的内容写进 `figure_alt`。 - 只有「如图」但拿不到图片:把图形条件**文字化**写进题干(如「圆 O 中,弦 AB 垂直于直径 CD」), `figure_url` 留空、`figure_alt` 写说明。**不要编造图片地址**。 - 上传约束:PNG/JPG/GIF/WebP/BMP ≤3MB,拒 SVG;客户端 Tk 只显示 PNG/GIF/PPM/PGM,**统一用 PNG**。 - 更完整的公式/图形规范见 `docs/02_题库/题目富媒体录入规范.md`。 ## 5. 外部试题清洗规则(AI 整理时逐条执行) | # | 脏数据形态 | 处理动作 | |---|------------|----------| | 1 | 一行多选项:`A. 1 B. 2 C. 3` | 拆成 `options` 数组,每项一条 | | 2 | 选项带全角字母/顿号:`A.`、`A、` | 统一成 `A. ` 前缀 | | 3 | 判断题只有「对/错」没选项 | 补 `["A. 对", "B. 错"]`,答案映射为 `A`/`B` | | 4 | 答案夹带解析:`答案:C(因为…)` | 答案只留 `C`,解析移到 `explain` | | 5 | 多选答案写作 `A、B、D` / `ABD` / `["A","B"]` | 统一成字符串 `"ABD"` | | 6 | 填空答案写作 `答:8` / `8 分` | 只留 `8` | | 7 | 题干带题号/来源/页码:`12.(2023·北京)…` | 删除题号与来源,只保留题干;来源可放 `explain` 末尾 | | 8 | 题干带 HTML/图片标签 | 删除标签;图片信息转成 `figure_alt` 文字 | | 9 | 学科/年级未标注 | 按内容推断并填枚举值;实在无法确定填 `其他`、年级留空 | | 10 | 同文件内题干重复 | 只保留一条 | | 11 | 一道题里塞了多个小问 | **拆成多道题**,各自有完整题干与答案 | | 12 | 完整 LaTeX / 复杂公式 | 按 4.3 改写;无法改写的整段转图片或线性文字 | | 13 | 原资料带知识点编号(非本系统) | **丢弃**,本系统 `knowledge_code` 必须用自己的编号;只保留 `knowledge` 文本 | | 14 | 表格形式的试题 | 每行一道题,表头映射到字段,不要输出二维数组 | | 15 | 图片/几何图链接 | 保留可访问的 http(s) 链接;不可访问则文字化,不编造 `/static/media/` 路径 | **绝对不要做的事**:不编造 `knowledge_code`;不编造 `figure_url`;不输出解释文字或 Markdown 代码块; 不改数学结论;不把多题合并;不输出 `id`/`updated_at`;不在 JSON 里写注释。 ## 6. AI 提示词(可直接复制) ### 6.1 外部资料整理 / 从零出题通用提示词 下面这段与 `static/templates/ustudy_ai_prompt.txt` 完全一致,可直接整体复制给任意大模型。 ```text 你是「Ustudy 题库数据整理员」。把输入的试题资料转换成可导入的 JSON 数组。只输出 JSON,不输出任何其它文字。 【输入】 学科:{学科} 年级:{年级} 知识点:{知识点} 题量:{题量} 题型:{题型} 难度:{难度} 待整理资料: <<<资料开始>>> {这里粘贴从 PDF / Word / 网页 / Excel / OCR 提取的纯文本} <<<资料结束>>> (若资料为空,则按上面的学科/年级/知识点/题量/题型从零出题。) 【输出格式】 一个 UTF-8 JSON 数组,每个元素是一道题。不要 Markdown 代码块,不要前后解释,不要注释,不要尾随逗号。 字段如下(未列出的字段一律不要输出): type 必填字符串,只能是 single / multi / judge / fill title 必填字符串,完整题干(去掉题号、来源、页码) options 数组或 null。single/judge 必填且至少 2 项,每项形如 "A. 选项内容"; judge 固定为 ["A. 对", "B. 错"];fill 省略或 null;multi 必填 answer 必填字符串。single/judge 为单个大写字母;multi 为字母组合如 "ABD";fill 为简短答案文本 explain 字符串或 null,解题过程,学生答错时展示 knowledge 字符串或 null,知识点中文名称 knowledge_code 字符串或 null。只能填 Ustudy 知识点体系中确实存在的编号; 你无法确认编号是否存在时一律填 null,禁止编造 subject 字符串,取值:语文/数学/英语/物理/化学/生物/历史/地理/政治/科学/其他 grade 字符串,取值:小一~小六 / 初一~初三 / 高一~高三 difficulty "易" / "中" / "难" 或 null content_format "plain" 或 "latex" figure_url 字符串或 null。无真实图片时填 null,禁止编造 /static/media/ 路径 figure_alt 字符串或 null,图形的文字说明(无图但有几何条件时也要填) 【公式规则】 涉及分数、指数、根式、圆、比例、圆柱圆锥时,把 content_format 设为 "latex",并用以下轻量标记: \frac{分子}{分母} 如 \frac{3}{4}、\frac{1}{3}\pi r^{2}h 上标 2^{3} 或 x^2 下标 a_{1} 根式 \sqrt{2} \pi \times \div \cdot \angle \triangle \parallel \perp \approx \ne \le \ge 角度 50^{\circ} 公式可用 $...$ 包裹 不支持 \dfrac、\left/\right、\text、矩阵、积分、块级公式 \[\]、$$...$$、HTML 标签。 遇到不支持的写法:改写成线性文字(如 "1/3 π r² h"),复杂公式整段用文字描述。 特别注意:JSON 字符串里的反斜杠必须双写,写成 "$\\frac{3}{4}$";只写 \frac 时 JSON 不会报错,但 \f 会被解析成换页符,公式静默损坏。 若 content_format 为 plain,则直接用 Unicode 字符(½ ² ³ √ ∠ △ π × ÷),不要写 LaTeX 命令。 【答案规则】 答案必须短、唯一、不带解释文字。填空答案是文本包含匹配,不做数学等价判断, 所以 0.5 与 1/2 视为不同答案;公式结论题优先出成选择题。 多选题答案写成 "ABD" 这样的字符串,不要写成数组。判断题答案用 "A"/"B"。 【清洗规则】 1. 一行里的多个选项拆成 options 数组;统一 "A. " 前缀。 2. 答案里的解析移到 explain;答案只留字母或简短文本。 3. 判断题若原资料没有选项,补 ["A. 对", "B. 错"]。 4. 一题多个小问必须拆成多道题,每道题题干完整。 5. 去掉题号、来源、页码、HTML 标签;图片/几何条件改写成 figure_alt 文字。 6. 资料里的外部知识点编号一律丢弃,只保留 knowledge 中文名称。 7. 文件内题干重复的只保留一条。 8. 学科/年级按资料或输入推断,填上面的枚举值。 9. 拿不到真实图片时 figure_url 填 null,并在 figure_alt 写清图中条件,保证不看图也能作答。 【输出前自检】 - 顶层是数组;每个对象都有 type/title/answer;options 是数组或 null;answer 是字符串。 - 没有 Markdown 代码块、没有解释文字、没有注释。 - 所有反斜杠已双写,JSON 能被标准解析器解析。 - 没有编造 knowledge_code / figure_url / id 等字段。 - 含数学标记的题目 content_format 都是 latex。 【示例(输入资料 → 输出)】 输入资料: "3. 计算 3/4+1/2 = ( ) A. 1 B. 5/4 C. 1/2 答案:B(通分后 3/4+2/4=5/4)" 输出: [ { "type": "single", "title": "计算 $\\frac{3}{4}+\\frac{1}{2}$ 的结果是?", "options": ["A. 1", "B. $\\frac{5}{4}$", "C. $\\frac{1}{2}$"], "answer": "B", "explain": "通分:$\\frac{3}{4}+\\frac{2}{4}=\\frac{5}{4}$。", "knowledge": "分数加减法", "knowledge_code": null, "subject": "数学", "grade": "小五", "difficulty": "易", "content_format": "latex", "figure_url": null, "figure_alt": null } ] ``` ### 6.2 服务端内置「AI 生成」 管理后台「AI 生成」页签调用的 `app/routers/ai_generate.py::_build_prompt()` 已与上述契约对齐 (含判断题必须给 options、公式标记、禁止 HTML、禁止编造知识点编号)。 流程:生成 → 进入**待审核区** → 人工勾选通过 → 入库。 内置 AI 生成题的两个默认:**不带 `knowledge_code`、不带 `figure_url`**(模型无法知道体系编号与真实图片)。 入库后如需按知识点统计/练习,请在「题库管理」里补打标;几何图形需人工上传后补 `figure_url`。 若使用外部大模型出题,把 6.1 的提示词连同 `ustudy_questions_sample.json` 作为 few-shot 一起给出, 产出 JSON 后走第 7 章流程导入即可。 ## 7. 校验与导入 ### 7.1 离线自检(必经) ```bash cd Ustudy-webserver PYTHONPATH= /opt/homebrew/bin/python3.13 scripts/validate_questions.py /path/to/ai_output.json # 连库检查知识点编号与库内题干重复(推荐): PYTHONPATH= /opt/homebrew/bin/python3.13 scripts/validate_questions.py /path/to/ai_output.json --db data/ustudy.db # 输出一份只含合法题目的干净文件: PYTHONPATH= /opt/homebrew/bin/python3.13 scripts/validate_questions.py /path/to/ai_output.json --out clean.json ``` 支持 `.json` / `.jsonl` / `.xlsx`。退出码 `0` = 可导入,`1` = 有错误(逐行列出题号、字段、原因), `2` = 文件无法解析。警告(如缺 explain、公式与 plain 不匹配)不影响退出码但会提示。 ### 7.2 管理后台批量导入 浏览器打开 `http://127.0.0.1:8010/` → 「批量导入」→ 上传 → 查看预览 → 「确认导入有效题目」。 预览里每种状态的含义: | 状态 | 含义 | 处理 | |------|------|------| | ✓ 有效 | 可入库 | — | | ✗ 无效题型 / 缺少题干 / 缺少答案 / 选择题需选项 | 契约不满足 | 改文件后重新上传 | | ✗ 知识点编号不存在 | 编号不在 `knowledge_points` | 改成真实编号或删掉该字段 | | ✗ 与已有题目重复 | 题干与库内或文件内前面某条相同 | 删重复行;**修改已有题请用「题库管理」编辑,导入不会覆盖** | ### 7.3 常见失败原因速查 | 现象 | 原因 | 修法 | |------|------|------| | 公式变成乱码/制表符;校验器报「未双写的反斜杠数学命令」 | `\frac` 没双写,`\f` 被当换页符(JSON 不报错,最隐蔽) | 改成 `\\frac` | | `Input should be a valid list` / 导入异常 | `options` 写成了字符串 | 改成数组 | | 整批 500 或 422 | `answer` 写成了数组 / `status` 写成字符串 | 改成字符串 / 整数 | | 预览里大量「选择题需选项」 | 判断题省略了 options | 补 `["A. 对","B. 错"]` | | 学生看到 `\frac{1}{2}` 源码 | `content_format` 留空 | 设为 `latex` | | 公式显示成奇怪符号 | 用了完整 LaTeX(`\dfrac`、`\left`) | 按 4.3 改写 | | 题目里出现 `` 文本 | 资料里的 HTML 没清掉 | 删除标签,写 `figure_alt` | | 几何图裂图 | 编造了 `/static/media/` 地址 | 上传真实图片或留空 | | 填空题永远判错 | 答案与学生写法不一致 | 按第 3 章统一答案形态,或改选择题 | | 中文筛选/keyword 报 400 | URL 里裸中文未编码 | 用页面筛选项,不要手拼 URL | ## 8. 知识点打标 - 编号规则 `学科码-年级-章码-序号`,如 `SX6-CIRCLE-03`、`SX8-FUNC1-07`;见 `docs/02_题库/知识点体系.md`。 - 查编号:管理后台「知识点体系」页签按学科/年级/状态/知识点/说明组合筛选,或查 `knowledge_points` 表。 - **AI 不得编造编号**:填错会让整行在预览里被判无效。留空是安全的,后续可在题库管理里补打标。 - 打标后可按 `knowledge_code` 拉题练习,统计报表「薄弱知识点」也会按编号聚合。 常用真实编号示例(当前种子库):`SX5-FRAC-03` 分数基本性质、`SX6-CIRCLE-03` 圆的面积、 `SX6-CYL-04` 等底等高关系、`SX6-PROP-02` 比例的基本性质、`SX8-ALG2-01` 幂的运算、 `SX7-GEO2-04` 平行线判定、`SX8-FUNC1-07` 一次函数。 ## 9. 提交前自检清单 - [ ] 顶层是 JSON 数组,能被标准解析器解析(无 Markdown 代码块、无注释、无尾随逗号) - [ ] 每道题都有 `type` / `title` / `answer`,且类型正确(`options` 数组、`answer` 字符串) - [ ] `single` / `multi` / `judge` 都有 ≥2 个选项,判断题是「对/错」 - [ ] 答案短且唯一,不含解释文字;多选题答案是字母组合字符串 - [ ] 含公式的题 `content_format=latex`,反斜杠已双写,未用完整 LaTeX 特有命令 - [ ] 没有编造 `knowledge_code` 与 `figure_url`;几何题有 `figure_alt` - [ ] 学科/年级在枚举内,`difficulty` 是 易/中/难 - [ ] 题干去掉了题号、来源、页码、HTML;一题多问已拆分 - [ ] 文件内无重复题干;与库内已有题不冲突 - [ ] `validate_questions.py` 退出码为 0 - [ ] 管理后台预览「有效」数 = 期望题数,再点确认导入 ## 附录 A:人工补录(极少用) 管理后台 →「题库管理」→「+ 新增题目」:填题干、选项、答案、解析,选学科/年级/知识点; 公式题把「内容格式」选「数学标记」,几何图点「上传图形」。适合临时改一题,批量场景请勿使用。 ## 附录 B:文件与命令索引 | 用途 | 路径 | |------|------| | 本说明书 | `docs/02_题库/试题录入说明书.md` | | 公式/图形细节规范 | `docs/02_题库/题目富媒体录入规范.md` | | 知识点编号体系 | `docs/02_题库/知识点体系.md` | | 可复制 AI 提示词 | `Ustudy-webserver/static/templates/ustudy_ai_prompt.txt` | | 标准格式样本(few-shot / 可直接导入) | `Ustudy-webserver/static/templates/ustudy_questions_sample.json` | | Excel 模板(人工或工具生成 xlsx 时用) | `Ustudy-webserver/static/templates/ustudy_questions_template.xlsx` | | Excel 模板生成器 | `Ustudy-webserver/scripts/make_template.py` | | 离线校验器 | `Ustudy-webserver/scripts/validate_questions.py` | | 导入接口 | `POST /api/questions/import/preview` → `POST /api/questions/import/commit-raw` | | 知识点编号查询 | `GET /api/knowledge/tree`(管理端)、表 `knowledge_points` |