feat(exam): 出题批卷 API 增强 — 格式统一、校验、跨调用去重
- grader: 多策略 JSON 提取 + 3 次重试 + prompt 优化,修复 LLM 输出不稳定问题 - grader: question_content → content 统一字段名,与出题接口一致 - api: 新增入参校验(题型枚举、difficulty、总题数上限 20、grade question_type) - api/generator/manager: 新增 exclude_stems 参数,支持跨调用去重
This commit is contained in:
@@ -1114,7 +1114,8 @@ def generate_questions_structured_v2(
|
||||
chunks: List[Dict],
|
||||
document_name: str,
|
||||
question_types: Dict[str, int],
|
||||
difficulty: int = 3
|
||||
difficulty: int = 3,
|
||||
exclude_stems: List[str] = None
|
||||
) -> List[Dict]:
|
||||
"""
|
||||
🔥 重构版:结构化出题 v2
|
||||
@@ -1130,6 +1131,7 @@ def generate_questions_structured_v2(
|
||||
document_name: 文档名称
|
||||
question_types: 题型及数量 {"single_choice": 5, ...}
|
||||
difficulty: 难度 1-5
|
||||
exclude_stems: 需要排除的已有题目题干列表(跨调用去重)
|
||||
|
||||
Returns:
|
||||
题目列表
|
||||
@@ -1169,7 +1171,7 @@ def generate_questions_structured_v2(
|
||||
logger.warning("[v2] 知识点提取失败,降级使用 chunks 出题")
|
||||
fallback_questions = generator._generate_questions_fallback(chunks, document_name, question_types, difficulty)
|
||||
# 降级路径也执行去重和数量平衡
|
||||
deduped = _deduplicate_questions(fallback_questions)
|
||||
deduped = _deduplicate_questions(fallback_questions, exclude_stems=exclude_stems)
|
||||
final = _balance_question_types(deduped, question_types)
|
||||
logger.info(f" [降级] 最终题目数: {len(final)}")
|
||||
return final
|
||||
@@ -1225,9 +1227,9 @@ def generate_questions_structured_v2(
|
||||
# ========== Phase 4: 质量校验 ==========
|
||||
logger.info("[v2] Phase 4: 质量校验")
|
||||
|
||||
# 4.1 去重
|
||||
deduped = _deduplicate_questions(all_questions)
|
||||
logger.info(f" 去重: {len(all_questions)} → {len(deduped)}")
|
||||
# 4.1 去重(含跨调用排除已有题目)
|
||||
deduped = _deduplicate_questions(all_questions, exclude_stems=exclude_stems)
|
||||
logger.info(f" 去重: {len(all_questions)} → {len(deduped)}" + (f"(排除已有 {len(exclude_stems)} 道)" if exclude_stems else ""))
|
||||
|
||||
# 4.2 数量校正
|
||||
final = _balance_question_types(deduped, question_types)
|
||||
@@ -1381,22 +1383,31 @@ def _retrieve_kp_chunks_v2(
|
||||
return result
|
||||
|
||||
|
||||
def _deduplicate_questions(questions: List[Dict]) -> List[Dict]:
|
||||
def _deduplicate_questions(questions: List[Dict], exclude_stems: List[str] = None) -> List[Dict]:
|
||||
"""
|
||||
题目去重
|
||||
|
||||
策略:
|
||||
1. 相同题干去重(前 80 字)
|
||||
2. 相同知识点 + 相同题型去重
|
||||
3. 排除已有题目(跨调用去重)
|
||||
|
||||
Args:
|
||||
questions: 原始题目列表
|
||||
exclude_stems: 需要排除的已有题目题干列表(用于跨调用去重)
|
||||
|
||||
Returns:
|
||||
去重后的题目列表
|
||||
"""
|
||||
seen_stems = set()
|
||||
seen_kp_type = set()
|
||||
|
||||
# 预填已有题目的题干前缀,使新生成的题目与已有题目冲突时被过滤
|
||||
if exclude_stems:
|
||||
for stem in exclude_stems:
|
||||
seen_stems.add(stem[:80])
|
||||
seen_kp_type.add(f"{stem[:30]}_") # 通配题型匹配
|
||||
|
||||
deduped = []
|
||||
|
||||
for q in questions:
|
||||
|
||||
Reference in New Issue
Block a user