在教育领域,我们经常需要将教师布置的作业PDF转换为标准化的选择题题库。这个需求源于几个实际痛点:
我开发的process_assignment_pdf()函数就是为了解决这些问题。它能自动完成以下核心工作:
这个工具特别适合需要批量处理作业的教育机构,可以将教师随意编排的作业PDF,自动转换为可直接导入在线考试系统的标准化题库。
整个PDF处理流程分为四个关键阶段:
python复制def process_assignment_pdf(pdf_path):
# 1. 打开PDF并提取文本行
doc = fitz.open(pdf_path)
all_lines = [ln.rstrip() for page in doc for ln in page.get_text().split("\n")]
# 2. 切分题块
blocks = split_into_question_blocks(all_lines)
# 3. 分类并处理每个题块
results = []
for blk in blocks:
qtype = classify_question_type(blk)
processed = convert_question(qtype, blk)
if processed:
results.append(processed)
return results
系统使用多级规则进行题型判断:
python复制def classify_question_type(block):
# 检查是否有选择题选项
if parse_mcq_options(block["lines"]):
return QTYPE_MCQ
# 检查判断题关键词
if any(keyword in block["question_text"].lower()
for keyword in ["true or false", "判断", "对错"]):
return QTYPE_TF
# 检查简答题特征
if (any(verb in block["question_text"].lower()
for verb in ["解释", "证明", "计算"]) or
has_subparts(block["lines"])):
return QTYPE_SHORT
return QTYPE_OTHER
PDF解析使用PyMuPDF库,它能准确保持原始文本的段落结构。题块分割的关键是识别题号行,我们支持多种常见格式:
1. 问题描述1) 问题描述Question 1:问题1:python复制def split_into_question_blocks(lines):
blocks = []
current_block = None
for line in lines:
if is_question_start_line(line):
if current_block:
blocks.append(current_block)
current_block = {
"qnum": extract_question_number(line),
"lines": [line],
"question_text": ""
}
elif current_block:
current_block["lines"].append(line)
if current_block:
blocks.append(current_block)
return blocks
选择题选项识别支持多种格式:
A. 选项内容A) 选项内容(A) 选项内容python复制def parse_mcq_options(lines):
options = {}
option_pattern = r'^\(?\s*([A-E])\s*[\.\)]\s*(.+)$'
for line in lines:
match = re.match(option_pattern, line.strip())
if match:
option_key = match.group(1).upper()
option_text = match.group(2).strip()
if option_text: # 过滤空选项
options[option_key] = option_text
return options if len(options) >= 2 else None # 至少两个有效选项才算选择题
转换流程:
python复制def convert_short_to_mcq(question_text, ref_ans=None):
prompt = f"""将以下简答题转换为选择题,生成{min_options}-{max_options}个选项:
题目:{question_text}
{ref_ans and f"参考答案:{ref_ans}" or ""}
要求:
1. 选项应涵盖常见误解和典型错误
2. 正确选项应有明确依据
3. 选项表述简洁清晰"""
response = llm.generate(prompt)
return validate_mcq_response(response)
固定格式转换:
python复制def convert_tf_to_mcq(question_text, ref_ans=None):
# 标准化参考答案
correct = normalize_tf_answer(ref_ans) if ref_ans else None
# 无参考答案时使用LLM判断
if not correct:
llm_response = llm_judge_tf(question_text)
correct = "A" if llm_response == "true" else "B"
return {
"question": question_text,
"options": {"A": "正确", "B": "错误"},
"correct": correct,
"source": "assignment"
}
通过风险评估决定是否转换:
python复制def handle_other_type(question_text, risk_threshold=0.6):
risk = assess_conversion_risk(question_text)
if risk > risk_threshold:
return None # 丢弃高风险的题目
# 尝试转换为选择题
mcq = llm_convert_to_mcq(question_text)
if not mcq or len(mcq["options"]) < 3:
return None # 转换失败或选项不足
return {
**mcq,
"risk_score": risk,
"source": "assignment"
}
初期测试发现,仅靠关键词匹配的误判率较高。我们通过以下改进提高了准确率:
多特征融合判断:
LLM辅助分类:
当规则引擎置信度低时,调用LLM进行二次判断
python复制def enhanced_classify(question_text, lines):
# 先用规则分类
base_type = basic_classify(question_text, lines)
# 低置信度时使用LLM
if base_type == QTYPE_OTHER and uncertainty_score(question_text) > 0.3:
return llm_classify(question_text)
return base_type
LLM生成的选项可能出现以下问题:
我们的解决方案:
python复制def validate_mcq_options(options, correct_key):
# 1. 检查选项数量
if len(options) < 3:
return False
# 2. 检查正确选项存在
if correct_key not in options:
return False
# 3. 检查选项相似度
if max_option_similarity(options) > 0.7:
return False
# 4. 检查选项长度差异
lengths = [len(opt) for opt in options.values()]
if max(lengths) / min(lengths) > 3:
return False
return True
处理大型PDF时遇到的性能问题:
优化措施:
python复制def batch_convert_to_mcq(questions):
"""批量转换提高LLM效率"""
batch_prompt = "请将以下问题分别转换为选择题:\n"
for i, q in enumerate(questions):
batch_prompt += f"\n问题{i+1}:{q['text']}\n"
response = llm.generate(batch_prompt)
return parse_batch_response(response)
教师作业布置:
历史试卷数字化:
跨平台题目迁移:
我们在100份真实作业PDF上测试的结果:
| 指标 | 转换成功率 | 人工修正率 | 平均处理时间 |
|---|---|---|---|
| 选择题 | 98.2% | 1.5% | 0.2s/题 |
| 判断题 | 95.7% | 3.2% | 0.5s/题 |
| 简答题 | 88.3% | 8.7% | 3.2s/题 |
| 其他题型 | 76.5% | 15.4% | 4.5s/题 |
题目分割错误
选项质量不高
复杂题目转换失败
python复制def handle_special_questions(question):
if contains_image_or_formula(question):
return {
"question": question["text"],
"options": {"A": "[需人工处理]"},
"correct": "A",
"needs_review": True
}
return None
命令行工具:
bash复制python pdf_processor.py input.pdf --output quiz.json
Web服务:
python复制@app.route('/convert', methods=['POST'])
def convert_pdf():
file = request.files['pdf']
result = process_assignment_pdf(file)
return jsonify(result)
批量处理脚本:
python复制for pdf_file in glob.glob('assignments/*.pdf'):
result = process_assignment_pdf(pdf_file)
save_to_database(result)
根据使用场景调整关键参数:
题型转换阈值:
other_type_risk_threshold=0.5other_type_risk_threshold=0.7选项数量控制:
min_options=3, max_options=4min_options=4, max_options=6LLM使用策略:
use_llm=True(默认)use_llm=False(仅使用规则)python复制def future_enhancements():
# 1. 添加OCR支持
if is_scanned_pdf(pdf_path):
text = run_ocr(pdf_path)
# 2. 公式识别
if contains_math_formula(text):
formula = extract_formula(text)
# 3. 反馈机制
if user_correction_available():
train_model(user_corrections)
我们开源了核心处理引擎,欢迎开发者:
项目采用模块化设计,方便扩展:
python复制class QuestionProcessor:
def __init__(self):
self.classifiers = [MCQClassifier(), TFClassifier()]
def add_classifier(self, classifier):
self.classifiers.append(classifier)
def process(self, question):
for classifier in self.classifiers:
if result := classifier.check(question):
return result
return default_processing(question)
在实际部署中,我发现最实用的技巧是建立题目处理流水线,将自动转换与人工审核相结合。对于重要考试题目,建议设置较低的转换阈值并进行人工复核;对于日常练习,可以使用较高的阈值实现全自动处理。同时,保留原始题目和转换结果的对应关系,方便后续追溯和修正。