Files
rag/docs/出题系统测试报告.md
lacerate551 a31ee4bba0 feat(exam_pkg): 优化出题系统稳定性与推理模型适配
- core/llm_utils: MiMo模型自动注入thinking=disabled参数,全局生效
- config: 新增LLM_DISABLE_THINKING配置项(默认true)
- generator: 推理模型自适应max_tokens(1.5x)、429限流重试+指数退避、v2管线补题机制
- generator: analyze_document_for_exam新增max_total参数控制AI出题上限
- generator: validate_questions_schema兼容type和question_type字段
- grader: 主观题max_tokens从1000提升至2000、fuzzy_match增加编辑距离容错
- manager: results变量初始化防NameError、透传max_total参数
- api: /exam/generate-smart支持max_total请求参数
2026-06-22 18:51:11 +08:00

191 lines
8.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 出题系统测试报告
**测试日期**: 2026-06-22
**测试环境**: mimo-v2.5关闭推理模式、2.docx文明吸烟环境建设标准
**测试方式**: curl 接口测试 + 代码审查
---
## 一、测试总览
| # | 测试项 | 接口 | 结果 | 耗时 |
|---|--------|------|------|------|
| 1 | 参数出题 | POST /exam/generate | ✅ 成功 | ~87s (4题) |
| 2 | AI一键出题 | POST /exam/generate-smart | ⚠️ 成功但有上限失控 | ~244s (50题) |
| 3 | 偏门题型(纯主观) | POST /exam/generate | ✅ 成功 | ~87s (5题) |
| 4 | 偏门题型(多选+填空) | POST /exam/generate | ✅ 成功 | ~60s (6题) |
| 5 | 跨调用去重 | POST /exam/generate + exclude_stems | ✅ 去重有效 | ~60s (5题) |
| 6 | 批题(4题型混合) | POST /exam/grade | ✅ 全部成功 | ~30s |
---
## 二、测试详情
### Test 1: 参数出题 (single_choice:2, fill_blank:1, true_false:1)
- **结果**: 4题全部生成成功题型匹配
- **问题**:
- ❌ 题目多样性不足4题中3题考同一个知识点"标准的解释机构"
- ❌ source_trace 数据缺失chunk_id=?, page=?
### Test 2: AI一键出题 (generate-smart)
- **AI分析**: 检测到54个知识点推荐50道题20单选+15判断+10多选+5填空
- **结果**: 50题全部生成12个不同章节0题干重复
- **问题**:
-**P0 上限失控**: prompt 要求"不超过20"但AI返回50代码没有 enforce 上限校验
- ⚠️ 50题意味着20-30次LLM调用极易触发429限流
### Test 3: 纯主观题 (subjective:5)
- **结果**: 5题全部生成覆盖不同章节scoring_points 完整
- **耗时**: 87秒关闭推理模式后正常
### Test 4: 多选+填空 (multiple_choice:3, fill_blank:3)
- **结果**: 6题全部生成
- **问题**:
-**填空题答案格式不一致**: `content.answer``[["消费水平较高"]]` (list of list),但没有 `data.reference_answer` 字段,只有 `data.blank_count`
- ⚠️ 与 grader 期望的格式可能不匹配
### Test 5: 跨调用去重 (exclude_stems)
- **结果**: 3个排除题干全部未出现去重有效 ✅
### Test 6: 批题 (grade)
- **单选题答错**: score=0, correct=false ✅
- **判断题答对**: score=2, correct=true ✅
- **填空题部分对**: score=4.0 (满分4), blank_scores=[4.0] ✅
- **主观题(低质量回答)**: score=2.0/10, 4个scoring_point逐项评分 ✅
- **总评**: 得分率44.4%,评分合理
---
## 三、发现的问题(按严重度排序)
### P0 - 严重问题
#### 1. 🚨 AI一键出题上限失控 (generator.py:1053-1068)
**位置**: `analyze_document_for_exam()` 函数
**现象**: prompt 写了"所有数量之和不要超过 min(total_knowledge_points * 2, 20)",但 LLM 返回50题代码直接采纳
**根因**: 代码只做了"题型合法性"校验,没有对总数做上限 enforce
```python
# 当前代码 (generator.py:1053-1059) - 只校验单题型合法性,无总数限制
valid_types = ['single_choice', 'multiple_choice', 'true_false', 'fill_blank', 'subjective']
question_types = {}
for q_type in valid_types:
count = result.get('question_types', {}).get(q_type, 0)
if isinstance(count, int) and count >= 0:
question_types[q_type] = count # 直接采纳,无上限
```
**建议**: 增加总数上限校验,超过 `max_questions`建议20时按比例缩减
```python
total = sum(question_types.values())
max_questions = 20
if total > max_questions:
ratio = max_questions / total
question_types = {k: max(0, round(v * ratio)) for k, v in question_types.items()}
```
#### 2. 🚨 LLM 调用无限流机制429 风暴 (generator.py:290-303, 540-543)
**位置**: `generate_questions_structured()` 主循环 + `_generate_with_retry()` 重试
**现象**: 并发出题时大量429错误重试退避太短(1s/2s/4s)3次全败后放弃该知识点
**根因**:
- 主循环 for 逐知识点调用 LLM无请求间隔补题函数有 sleep(1),主循环没有)
- 429 重试退避 `2^attempt` 秒 (1/2/4s) 对 API 限流不够
- 无全局限流器(令牌桶/漏桶)
**建议**:
1. 主循环每次 LLM 调用后加 `time.sleep(1.5)` 最小间隔
2. 429 退避改为指数+抖动: `min(30, 2 ** attempt + random.uniform(0, 2))`
3. 长期: 引入 `tenacity` 或自实现令牌桶限流器
#### 3. 🚨 validate_questions_schema 只认 `type` 不认 `question_type` (generator.py:154)
**位置**: `validate_questions_schema()` 函数
**现象**: LLM 返回的题目可能用 `question_type` 字段,但校验只检查 `q.get('type')`,导致有效题目被丢弃
**对比**: `_validate_question_types()` (第448行) 做了兼容: `q_type = q.get('question_type') or q.get('type')`
```python
# 当前代码 (generator.py:154) - 不兼容
if q.get('type') not in VALID_TYPES:
continue # question_type 字段的题目被丢弃!
# 应改为
q_type = q.get('question_type') or q.get('type')
if q_type not in VALID_TYPES:
continue
```
### P1 - 中等问题
#### 4. ⚠️ 填空题答案格式不一致
**现象**: 出题返回 `content.answer = [["答案1"], ["答案2"]]` (list of list),但无 `data.reference_answer`
**影响**: 前端/本地数据库可能期望 `reference_answer` 字段
**建议**: 统一在 `data` 中增加 `reference_answer` 字段,与 `content.answer` 保持一致
#### 5. ⚠️ grader max_tokens 对推理模型不足 (grader.py:438)
**位置**: `_grade_subjective()` 方法
**现象**: `max_tokens=_get_effective_max_tokens(1000, self.model)`
- 推理模型关闭推理时: 1000 tokens 勉强够
- 推理模型开启推理时: 思考链消耗 800+ tokenscontent 为空
**当前状态**: 关闭推理模式后本次测试通过,但**开启推理模式会再次失败**
**建议**: 基础 max_tokens 从 1000 提升至 2000
#### 6. ⚠️ local_db._detect_question_type 使用旧格式 (local_db.py:378-383)
**位置**: `_detect_question_type()` 方法
**现象**: 检测 `options``reference_answer` 字段来判断题型,但新格式用 `content.data.options``content.answer`
```python
# 当前代码 - 检查顶层 options
if 'options' in question and question['options']:
return 'choice'
elif 'reference_answer' in question:
return 'short_answer'
# 应改为检查 content 内部结构
content = question.get('content', {})
data = content.get('data', {})
if data.get('options'):
return 'choice'
elif question.get('question_type') in ('fill_blank', 'subjective', ...):
return question['question_type']
```
#### 7. ⚠️ 题目多样性不足
**现象**: Test 1 中 4 题有 3 题考同一知识点("标准的解释机构"
**根因**: `_assign_questions_to_kps()` 可能将多个题型分配给同一知识点
**建议**: 增加"同一知识点最多出 N 道题"的限制(建议 N=2
### P2 - 低优先级
#### 8. 💡 source_trace 数据偶尔缺失
**现象**: Test 1 中部分题目的 chunk_id 和 page 显示为 `?`
**可能原因**: `find_referenced_chunks` 匹配失败时的 fallback 显示
#### 9. 💡 多选题答案格式
**现象**: 多选题 answer 为 `['A', 'B']``['A', 'B', 'D']` (list),前端需处理
**建议**: 在 API 文档中明确多选题 answer 格式为 list
#### 10. 💡 补题机制未与已有题目交叉去重
**现象**: 补题时只检查本次生成的题干,不检查已有题目
**建议**: 补题函数接受 `exclude_stems` 参数
---
## 四、测试通过项 ✅
1. **基础出题功能**: 5种题型均可正常生成
2. **AI智能分析**: 文档分析→题型推荐→出题流程完整
3. **补题机制**: 知识点出题失败后自动补题
4. **跨调用去重**: exclude_stems 功能正常,排除的题干不再出现
5. **批题功能**: 客观题精确匹配填空题部分给分主观题LLM逐项评分
6. **source_trace**: 大部分题目有完整的溯源信息chunk_id, section, snippet
7. **JSON解析**: mimo-v2.5 返回的 JSON 格式(含 markdown 包裹)可正常解析
8. **题目格式**: 单选题 options 为 list of dict `[{key, content}]`,格式规范
---
## 五、建议修复优先级
| 优先级 | 问题 | 修复难度 | 影响范围 |
|--------|------|----------|----------|
| P0 | AI出题上限失控 | 简单 | smart 出题 |
| P0 | LLM 429 限流 | 中等 | 全部出题 |
| P0 | validate_questions_schema type 字段 | 简单 | 全部出题 |
| P1 | 填空题答案格式统一 | 简单 | 填空题+批题 |
| P1 | grader max_tokens | 简单 | 主观题批题 |
| P1 | local_db 旧格式 | 中等 | 本地存储 |
| P1 | 题目多样性控制 | 中等 | 出题质量 |