试卷智能解析:基于文档解析 + 大模型的试卷抽取搭建思路(附GitHub项目地址)
项目介绍: 这是一个面向题库自动化的试卷智能解析工具。支持上传PDF、扫描件及手机拍照的试卷,可自动处理选择题、填空题、阅读理解、数学大题、听力题等多种题型,抽取题组结构、共享题干、小题、选项、分值及来源页码,并输出统一结构的JSON格式。具备跨页题组拼接、表格保留、图片归位、异常格式兼容及原文坐标溯源能力。适用于题库建设、作业批改、学情分析和教辅数字化等场景。
GitHub项目地址: https://github.com/intsig-textin/xparse-sample-projects
在题库建设、在线教育和教辅数字化等场景中,试卷始终是最核心的数据来源之一。而在试卷抽取这种输入复杂、输出结构强的任务里,文档解析产品最有价值的作用,不是“识字”,而是提供一层足够稳定的中间结构,让大模型和后处理规则都能站在更高起点上工作。
选择题、填空题、阅读理解、数学大题,看似都叫“试卷”,实际版式差异却很大。更麻烦的是,这些文件很少是干净的电子版,很多来自扫描的纸质试卷或手机拍的作业,经常伴有倾斜、手写痕迹、跨页断裂和表格变形。
如果仍然靠人工拆题——把题干、选项、分值、题号一条条复制进系统,不仅效率低,而且很容易漏掉共享题干、跨页题组和图片归属。一个成熟的试卷抽取方案,价值不在于把文件识别成文本,而在于把文本进一步组织成结构化数据。
1. 为什么试卷抽取不能只靠 OCR
试卷类文档的难点不在识别文字,而在恢复题目关系。
OCR 通常能提供:
段落文本
表格
图片
页码
部分文档层级
但下游系统真正需要的是:
哪些内容属于同一个题组
哪段是共享题干,哪段是小题提问
图片和表格归属于题干还是选项
题号、题型、分值如何结构化
多页内容是否属于同一题
这就是试卷抽取的典型特点:输入是版面化文档,输出是强结构化数据,中间必须补一层语义理解。
2. 一条更适合题库场景的技术路径
这类场景常见有三种做法:
纯规则抽取
纯端到端大模型抽取
文档解析打底 + 大模型补语义 + 后端做稳定化
这里更推荐第 3 种。
纯规则的问题是脆弱。题号形式、共享题干模式、图片插入方式一变,规则就容易失效。
纯端到端大模型的问题是贵、慢、不可控。模型既要理解版面,又要做结构抽取,结果不容易复核,也不方便把页图、表格和公式继续复用到后续流程。
“文档解析 + 大模型抽取”的好处在于:
文档解析先把 PDF、图片、Word 统一转成标准中间层,减少文件格式差异。
大模型只负责最擅长的语义归并和字段判断,不必重复做视觉理解。
后端还能对模型输出做清洗、合并和校验,把不稳定性挡在服务端。
OCR 服务和模型服务可以独立替换,整条链路更容易演进。
从工程角度看,这是一种把“确定性处理”和“概率性处理”拆开的方案。
3. 文档解析工具在这条链路里解决了什么
在这类方案里,文档解析不是简单 OCR,而是上游结构化底座。
对于试卷抽取,最有价值的是三类输出:
markdown:解决可读性问题,便于快速查看整页内容。pages:解决页图展示和页码定位问题。detail:抽取的关键输入,因为它通常保留了文本块内容、表格 HTML、图片 URL、页码、块类型以及局部位置信息。
有了这层中间结构,大模型面对的就不再是原始文件,而是已经被解析过、格式统一过的文档内容。对试卷这种复杂版式任务,这是决定稳定性的关键前提。
4. 一个可落地的流程方案
试卷抽取要做到稳定落地,下面四个步骤缺一不可:
上传文件
-> 文档解析
-> 按页/按批重组待抽取内容
-> 大模型抽取题组结构
-> 后端规范化与合并
-> 输出结构化结果
其中最重要的不是接口数量,而是每一层只做自己最擅长的事情:
文档解析负责把文件变成稳定中间结构
大模型负责理解题目关系
后端负责让输出可用、可落库
5. 关键难点与实现方式
5.1 难点一:共享题干和小题关系很难靠规则恢复
试卷里常见一段阅读材料对应多道题、一段数学大题对应多个小问。OCR 虽然能把文字识别出来,但不会直接告诉你这是一组题。
这里适合让大模型做“题组判断”,但前提是输入必须足够清晰。推荐在 prompt 中明确约束:
输出是
groups每个 group 允许有
shared_stem每个 group 下挂
questions小题不能把共享题干重复抄写一遍
这一步的本质不是“让模型猜”,而是把题组结构定义成一个明确契约。
5.2 难点二:长试卷一次性抽取太长,跨页又容易断
如果把整份试卷一次性发给模型,通常会遇到以下问题:
输入过长
返回慢
失败时整份重来
跨页题组容易丢失连续性
更稳定的实现方式是按页分批:
从
detail中按page_id聚合内容在每页前加显式分隔符
每批单独调用一次模型
后端负责合并多批结果
这个项目更进一步的做法是给下一批携带“上一批最后一个题组的摘要”,允许模型标记 continues_previous。这样即使题组跨页,也能在后端重新拼接。
这就是试卷抽取里最有技术含量的一层:不是单次抽取,而是跨批次连续抽取。
5.3 难点三:图片、表格、公式很容易在抽取时失真
如果把图片改写成“这里有一张图”,或者把表格转成纯文本,题目结构会明显变差。
更合适的方式是:
图片保留
原样表格保留 HTML
公式保持原始表达
这样大模型看到的是“带视觉锚点的文本结构”,而不是被提前简化过的摘要文本。
对试卷类任务来说,这一点会直接影响:
听力题图片选项归属
数学题图形归属
表格题背景归属
公式题语义完整性
5.4 难点四:模型输出天然不稳定,不能直接给下游系统
即使 prompt 写得很好,模型仍然可能出现:
字段缺失
类型不一致
空值乱填
group 编号不连续
解释性文本混入 JSON
所以抽取系统一定要有后端规范化层,至少做到以下几点:
JSON 兜底解析
字段类型清洗
空值归一
批次结果合并
组号重排
试卷抽取真正可用,不是因为模型一次输出得很好,而是因为后端把不稳定部分收敛成了稳定结构。
5.5 难点五:长任务需要让用户看到进度
试卷抽取往往不是秒级返回。尤其是试卷页数较多、图片较多时,用户如果看不到过程,很容易误以为系统卡住。
推荐做法是把抽取过程做成流式任务:
每完成一批,返回一条
progress全部完成后,返回一条
done出错时,返回
error
这样用户能明确知道系统目前处于:
解析中
抽取中
第几批
是否完成
这类进度反馈虽然不是算法壁垒,但对实际可用性非常重要。
6. 一套可以直接入库的试卷数据结构
试卷抽取的结果最好统一收敛成一套稳定的 schema,例如:
{
"paper_title": "...",
"paper_meta": {
"subject": "数学",
"grade_level": "高中",
"exam_type": "期中",
"has_images": true
},
"groups": [
{
"group_id": "G1",
"section": "二、阅读理解",
"shared_stem": "...",
"questions": [
{
"number": "21",
"type": "阅读理解",
"score": 2,
"stem": "...",
"options": [],
"source_page": 3
}
],
"source_pages": [3]
}
],
"warnings": []
}
这套结构的价值在于,它既能支持前端展示,也能直接进入题库、质检、导出或人工校正流程。
7. 试卷智能解析的真正价值
试卷智能解析并不是把传统OCR换成一个更复杂的模型,而是在重构试卷数据进入业务系统的方式。
从“提效”角度总结,这套方案主要提升的是三件事:
把原本人工整理题目关系的工作,前移成自动结构化抽取。
把原本容易因为版式变化而失效的纯规则流程,升级成更稳的混合架构。
把试卷文件直接转成下游可消费的结构,而不只是停留在 OCR 文本层。
从长远看,这类方案的意义不仅在于节省拆题时间,更在于让试卷数据能够以稳定的题组结构进入题库、批改和分析流程。只有文档先被正确理解,后续的智能教育服务才有真正落地的基础。
👉 试用试卷抽取工具
以上是一种 “文档解析打底 + 按页分批抽取 + 后端稳定化” 的试卷解析实践方案。核心思路是把低质量试卷文档(扫描件、拍照件、跨页断裂的试卷)先通过版式感知解析统一为保留表格、图片、块类型与坐标信息的中间层,再按页分批由大模型识别题组结构,最后在后端完成跨页拼接和字段规范化,输出固定骨架的JSON。方案已发布在GitHub,欢迎大家在项目中与我们交流。如果你在实际处理试卷时遇到更复杂的版式,或者有不同的架构思路,欢迎点击页面右侧加入技术交流群与我们探讨。
