19 KiB
19 KiB
LLM 自动生成问题 + 测试 + 审核方案
版本: v1.0
日期: 2026-04-21
目标: 基于知识库 MD 文件,自动生成测试问题,经过查重和质量审核后,直接送入单跳召回测试
一、整体流程
┌──────────────┐
│ 上传 MD 文件 │
└──────┬───────┘
↓
┌──────────────────────────┐
│ LLM 按章节生成 Q&A │
│ - 每个 section 生成 N 个 │
│ - 同时生成参考答案 │
│ - 记录答案来源原文片段 │
└──────┬───────────────────┘
↓
┌─────────────────────────────────┐
│ 审核流程 │
│ ┌─────────────────────────┐ │
│ │ 1. 批次内查重 │ │
│ │ - 精确查重(hash) │ │
│ │ - 语义查重(embedding)│ │
│ └─────────────────────────┘ │
│ ┌─────────────────────────┐ │
│ │ 2. 跨历史问题库查重 │ │
│ │ - 与已审核问题对比 │ │
│ └─────────────────────────┘ │
│ ┌─────────────────────────┐ │
│ │ 3. 问题质量自动评分 │ │
│ │ - 可回答性 │ │
│ │ - 问题清晰度 │ │
│ │ - 答案准确性 │ │
│ │ - 独特性 │ │
│ └─────────────────────────┘ │
│ ┌─────────────────────────┐ │
│ │ 4. 人工确认/编辑/删除 │ │
│ │ - 自动通过高质量问题 │ │
│ │ - 标记低质量/重复问题 │ │
│ └─────────────────────────┘ │
└─────────┬───────────────────────┘
↓
┌──────────────────────────┐
│ 导出为标准 MD 格式 │
│ (与现有单跳测试格式一致) │
└──────┬───────────────────┘
↓
┌──────────────────────────┐
│ 直接送入单跳召回测试 │
└──────────────────────────┘
二、模块设计
2.1 生成模块(/api/qa-gen)
API 设计
POST /api/qa-gen/task # 创建生成任务
GET /api/qa-gen/task/list # 任务列表
GET /api/qa-gen/task/{id} # 任务详情(含进度)
DELETE /api/qa-gen/task/{id} # 删除任务
GET /api/qa-gen/task/{id}/questions # 获取生成的问题列表
POST /api/qa-gen/question/{id}/approve # 通过问题
POST /api/qa-gen/question/{id}/reject # 拒绝问题
PUT /api/qa-gen/question/{id} # 编辑问题
POST /api/qa-gen/task/{id}/export-md # 导出已通过问题为 MD
生成策略
输入:
- MD 文件(与单跳测试相同格式)
- 配置参数:
model: LLM 模型(默认 gpt-4o-mini)questions_per_section: 每章节生成问题数(默认 5)quality_threshold: 质量阈值(默认 0.6)judge_config_id: 评分模型配置
处理流程:
- 按
## section切分文档 - 对每个 section:
- 提取章节标题和内容
- 调用 LLM 生成 N 个问题
- 每个问题包含:
- 问题文本
- 参考答案
- 答案来源原文片段(用于质量审核)
- 后台异步执行,支持进度回调
Prompt 模板:
你是一个专业的技术文档测试问题生成专家。
任务:根据以下技术文档章节内容,生成 {N} 个测试问题。
章节标题:{section_path}
章节内容:
{content}
要求:
1. 问题必须能从该章节内容直接回答(不要生成需要跨文档才能回答的问题)
2. 问题应覆盖章节的关键知识点
3. 问题表述清晰,无歧义
4. 答案准确,与原文一致
5. 标注答案来源的原文片段(用于后续审核)
输出格式(JSON):
[
{
"question": "问题文本",
"answer": "参考答案",
"source_chunk": "答案来源的原文片段(50-200字)"
},
...
]
2.2 查重模块
两层查重机制
| 层级 | 方法 | 阈值 | 说明 |
|---|---|---|---|
| 精确查重 | 问题文本 hash | 完全相同 | 快速过滤完全重复 |
| 语义查重 | embedding 余弦相似度 | > 0.92 | 识别语义相似问题 |
查重范围
- 批次内查重:当前生成任务内的问题互相查重
- 跨历史查重:与
qa_approved_question表中已审核通过的问题查重
实现细节
Embedding 计算:
- 使用
text-embedding-3-small或配置的 embedding 模型 - 问题生成后立即计算 embedding 并存储
- embedding 存储为 JSON 字符串(1536 维向量)
查重流程:
# 1. 精确查重
question_hash = hashlib.md5(question.strip().lower().encode()).hexdigest()
if question_hash in existing_hashes:
mark_as_duplicate()
# 2. 语义查重
question_embedding = get_embedding(question)
similarities = cosine_similarity(question_embedding, all_embeddings)
if max(similarities) > 0.92:
mark_as_similar(most_similar_question_id)
2.3 质量审核模块
自动质量评分
每条生成的问题自动打分(0-1),综合以下维度:
| 维度 | 权重 | 评分方法 |
|---|---|---|
| 可回答性 | 30% | LLM 判断:答案是否能从 source_chunk 推导出 |
| 问题清晰度 | 25% | LLM 判断:问题是否有歧义、表述是否清晰 |
| 答案准确性 | 30% | LLM 判断:参考答案是否与 source_chunk 一致 |
| 独特性 | 15% | 计算:与最相似问题的语义距离(1 - max_similarity) |
质量评分 Prompt:
评估以下测试问题的质量,从 0-1 打分。
问题:{question}
参考答案:{answer}
答案来源原文:{source_chunk}
评估维度:
1. 可回答性(0-1):答案是否能从原文推导出?
2. 问题清晰度(0-1):问题是否清晰无歧义?
3. 答案准确性(0-1):参考答案是否与原文一致?
输出格式(JSON):
{
"answerable": 0.9,
"clarity": 0.85,
"accuracy": 0.95,
"reasoning": "简短说明"
}
审核状态流转
pending(待审核)
↓
├─→ approved(通过)→ 进入 qa_approved_question 表
├─→ rejected(拒绝)→ 不进入测试
└─→ edited(编辑后)→ 重新计算 embedding 和质量分
自动通过规则:
quality_score >= threshold(默认 0.6)- 且
dup_of IS NULL(非重复) - 自动标记为
approved
需人工审核:
quality_score < threshold- 或
dup_of IS NOT NULL(疑似重复)
2.4 数据库设计
新增表
-- 生成任务表
CREATE TABLE qa_gen_task (
id TEXT PRIMARY KEY,
name TEXT,
status TEXT NOT NULL DEFAULT 'pending', -- pending/running/done/failed
model TEXT NOT NULL, -- 使用的 LLM 模型
judge_config_id TEXT, -- 评分模型配置
questions_per_section INTEGER DEFAULT 5,
quality_threshold REAL DEFAULT 0.6,
progress INTEGER DEFAULT 0,
total INTEGER DEFAULT 0,
error_message TEXT,
created_at TEXT NOT NULL,
finished_at TEXT
);
-- 生成的问题表(待审核池)
CREATE TABLE qa_gen_question (
id TEXT PRIMARY KEY,
task_id TEXT NOT NULL,
section_path TEXT NOT NULL,
question TEXT NOT NULL,
reference_answer TEXT NOT NULL,
source_chunk TEXT, -- 答案来源原文片段
quality_score REAL, -- 自动质量评分(0-1)
quality_detail TEXT, -- JSON: {answerable, clarity, accuracy, reasoning}
dup_of TEXT, -- 重复问题的 id(如果是重复的)
dup_similarity REAL, -- 与重复问题的相似度
status TEXT NOT NULL DEFAULT 'pending', -- pending/approved/rejected/edited
embedding TEXT, -- JSON 向量,用于查重
created_at TEXT NOT NULL,
updated_at TEXT
);
-- 已审核通过的问题库(用于查重基准 + 导出测试)
CREATE TABLE qa_approved_question (
id TEXT PRIMARY KEY,
gen_question_id TEXT NOT NULL, -- 关联 qa_gen_question.id
section_path TEXT NOT NULL,
question TEXT NOT NULL,
reference_answer TEXT NOT NULL,
embedding TEXT NOT NULL, -- 用于后续查重
source_task_id TEXT NOT NULL,
quality_score REAL,
approved_at TEXT NOT NULL,
approved_by TEXT DEFAULT 'auto' -- auto/manual
);
-- 索引
CREATE INDEX idx_qa_gen_question_task_id ON qa_gen_question(task_id);
CREATE INDEX idx_qa_gen_question_status ON qa_gen_question(status);
CREATE INDEX idx_qa_approved_question_section ON qa_approved_question(section_path);
三、前端设计
3.1 页面结构
新增"问题生成"一级菜单,包含两个子页面:
问题生成
├─ 生成任务
└─ 问题审核
3.2 生成任务页
布局: 类似单跳测试的任务列表页
功能:
- 上传 MD 文件
- 配置生成参数:
- 模型选择(下拉)
- 每章节问题数(数字输入,默认 5)
- 质量阈值(滑块,0-1,默认 0.6)
- 评分模型配置(下拉,复用 judge_config)
- 任务列表:
- 任务名称、状态、进度、创建时间
- 操作:查看问题、删除任务
任务状态展示:
┌────────────────────────────────────────────────────┐
│ 任务名称:evb_linux_development │
│ 状态:运行中 进度:45/107 章节 │
│ 已生成:225 个问题 自动通过:180 待审核:45 │
│ [查看问题] [停止任务] │
└────────────────────────────────────────────────────┘
3.3 问题审核页(核心交互)
布局: 左右分栏
┌─────────────────────────────────────────────────────────────┐
│ 筛选:[全部] [待审核] [重复] [低质量] [已通过] [已拒绝] │
│ 任务:[下拉选择任务] │
├──────────┬──────────────────────────────────────────────────┤
│ │ 批量操作:[全部通过] [通过高质量(>0.6)] [导出MD] │
│ ├──────────────────────────────────────────────────┤
│ 章节列表 │ 问题列表 │
│ │ ┌────────────────────────────────────────────┐ │
│ □ 全选 │ │ ✅ Q1: 如何配置 DDR 参数? 质量分: 0.85 │ │
│ □ ch1 │ │ A: 通过修改 xxx 配置文件... │ │
│ (12/15) │ │ 来源: linux_development/ddr/config │ │
│ │ │ [通过] [拒绝] [编辑] │ │
│ □ ch2 │ └────────────────────────────────────────────┘ │
│ (8/10) │ ┌────────────────────────────────────────────┐ │
│ │ │ ⚠️ Q2: DDR 配置文件在哪? 质量分: 0.45 │ │
│ □ ch3 │ │ A: 在 /etc/ddr.conf │ │
│ (5/8) │ │ ⚠️ 与"Q1"相似度 0.94(疑似重复) │ │
│ │ │ [通过] [拒绝] [编辑] [查看原问题] │ │
│ │ └────────────────────────────────────────────┘ │
│ │ ┌────────────────────────────────────────────┐ │
│ │ │ ❌ Q3: xxx? 质量分: 0.32 │ │
│ │ │ A: xxx │ │
│ │ │ ⚠️ 低质量:问题不清晰 │ │
│ │ │ [通过] [拒绝] [编辑] │ │
│ │ └────────────────────────────────────────────┘ │
└──────────┴──────────────────────────────────────────────────┘
交互细节:
-
问题卡片状态标识:
- ✅ 绿色:已通过(quality_score >= threshold 且非重复)
- ⚠️ 黄色:待审核(低质量或疑似重复)
- ❌ 红色:已拒绝
-
批量操作:
- "全部通过":将当前筛选结果中所有 pending 问题标记为 approved
- "通过高质量":仅通过 quality_score >= threshold 且非重复的问题
- "导出 MD":导出已通过问题为标准 MD 格式
-
编辑问题:
- 弹出对话框,可修改问题、答案
- 保存后重新计算 embedding 和质量分
- 状态变为
edited
-
查看原问题:
- 点击"查看原问题"跳转到重复问题的卡片
- 高亮显示相似部分
3.4 导出 MD 格式
导出的 MD 文件格式与单跳测试输入格式完全一致:
## section_path / doc_name
## Q1: 问题文本
**A1:** 参考答案
## Q2: 问题文本
**A2:** 参考答案
---
## section_path2 / doc_name2
## Q1: 问题文本
**A1:** 参考答案
导出后可直接上传到"单跳召回测试"模块进行测试。
四、实现优先级
P0(核心功能,1-2 周)
| 模块 | 功能 | 工作量 |
|---|---|---|
| 后端 | 生成任务 API(上传 MD → LLM 生成 Q&A → 存库) | 1-2 天 |
| 后端 | 问题列表 API + 通过/拒绝/编辑 API | 0.5 天 |
| 后端 | 导出 MD API | 0.5 天 |
| 前端 | 生成任务页(上传 + 配置 + 任务列表) | 1 天 |
| 前端 | 问题审核页(列表 + 基础交互) | 1-2 天 |
| 数据库 | 新增 3 张表 + schema 迁移 | 0.5 天 |
P1(查重 + 质量评分,1 周)
| 模块 | 功能 | 工作量 |
|---|---|---|
| 后端 | 批次内查重(hash + embedding) | 1 天 |
| 后端 | 质量自动评分(LLM 评分) | 1 天 |
| 前端 | 问题卡片状态标识(质量分、重复标记) | 0.5 天 |
| 前端 | 批量操作(全部通过、通过高质量) | 0.5 天 |
P2(跨历史查重 + 优化,3-5 天)
| 模块 | 功能 | 工作量 |
|---|---|---|
| 后端 | 跨历史问题库查重 | 0.5 天 |
| 前端 | 查看原问题跳转 | 0.5 天 |
| 前端 | 编辑问题对话框 | 0.5 天 |
| 优化 | embedding 批量计算优化 | 0.5 天 |
| 优化 | 生成任务并发控制 | 0.5 天 |
五、技术选型
LLM 模型
| 用途 | 推荐模型 | 备选 |
|---|---|---|
| 问题生成 | gpt-4o-mini | gpt-4o, claude-3.5-sonnet |
| 质量评分 | gpt-4o-mini | gpt-4o |
| Embedding | text-embedding-3-small | text-embedding-3-large |
依赖库
- 后端: 复用现有
judge_config表的 OpenAI 配置 - Embedding: 使用 OpenAI SDK 或
sentence-transformers(如果需要本地部署) - 相似度计算:
numpy.dot+numpy.linalg.norm(余弦相似度)
六、风险与注意事项
6.1 成本控制
- 问题生成: 每个 section 约 500-2000 tokens 输入,生成 5 个问题约 500 tokens 输出
- 估算:12,000 条问题(2,400 sections × 5)≈ 3M tokens input + 1M tokens output
- 成本(gpt-4o-mini):约 $0.6
- 质量评分: 每个问题约 300 tokens 输入 + 100 tokens 输出
- 估算:12,000 条问题 ≈ 3.6M tokens input + 1.2M tokens output
- 成本(gpt-4o-mini):约 $0.7
- Embedding: 每个问题约 20 tokens
- 估算:12,000 条问题 ≈ 240K tokens
- 成本(text-embedding-3-small):约 $0.005
总成本: 约 $1.3 / 12,000 条问题
6.2 性能优化
- 并发控制: 生成任务使用
asyncio.Semaphore限制并发数(默认 5) - 批量 embedding: 每次最多 100 个问题批量计算 embedding
- 查重优化: 使用 numpy 向量化计算,避免循环
6.3 数据一致性
- 事务保护: 问题通过/拒绝操作使用数据库事务
- 幂等性: 重复提交生成任务时检查是否已存在相同任务
七、后续扩展
7.1 高级功能
- 问题难度分级: 自动标注问题难度(简单/中等/困难)
- 知识点标签: 自动提取问题涉及的知识点标签
- 多轮对话问题: 生成需要多轮交互的复杂问题
- 负样本生成: 生成故意错误的答案,用于测试模型鲁棒性
7.2 集成优化
- 与单跳测试联动: 审核通过后自动创建单跳测试任务
- 测试结果反馈: 单跳测试失败的问题自动标记为"需优化"
- 持续迭代: 根据测试结果自动调整生成策略
八、总结
本方案提供了一个完整的"生成 → 查重 → 审核 → 测试"闭环,核心优势:
- 自动化程度高: 90% 的高质量问题可自动通过,人工仅需审核 10%
- 质量可控: 多维度质量评分 + 查重机制保证问题质量
- 无缝集成: 导出格式与现有单跳测试完全兼容
- 可扩展性强: 模块化设计,易于后续扩展
预期效果: 将问题生成效率提升 10 倍,从人工编写 1 小时 10 条问题,提升到 LLM 生成 1 小时 1000+ 条问题(含审核)。