- 移除20题上限说明,改为无上限+50题警告 - 修正文件/向量库不存在时的响应格式(total=0,非404) - 添加认证模式说明(生产模式直接放行) - 添加智能出题接口文档(/exam/generate-smart) - 添加填空题student_answer格式校验说明 - 更新错误码表,移除不存在的FILE_NOT_FOUND - 添加Q5/Q6常见问题 - 响应示例修正status_code为2020
18 KiB
出题批题接口 - 后端对接指南
一、接口概览
| 接口 | 方法 | 功能 | 超时建议 |
|---|---|---|---|
/exam/generate |
POST | 生成题目(手动指定题型数量) | 120秒 |
/exam/generate-smart |
POST | 生成题目(AI 自动分析文档结构出题) | 120秒 |
/exam/grade |
POST | 批阅答案 | 60秒 |
2026-07-03 变更:
- 移除出题总题数 20 道上限,超过 50 道时返回警告(不影响出题)
- 填空题批阅增加
student_answer格式校验(必须为字符串列表)- 认证错误状态码修正(仅
DEV_MODE=true时生效,见 认证说明)2026-06-05 变更:
/exam/grade请求字段question_content已重命名为content(破坏性变更)。详见 出题批阅接口变更说明(2026-06-05)。
二、出题接口
2.1 请求
POST /exam/generate
Content-Type: application/json
请求体字段说明:
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
file_path |
string | ✅ | 文档路径(相对于 documents 目录) |
collection |
string 或 string[] | ✅ | 向量库名称,支持数组(按优先级顺序检索,找到文件即停止) |
question_types |
object | ✅ | 题型及数量,无上限(超过 50 道时服务端返回警告但仍正常出题) |
difficulty |
int | ❌ | 难度等级 1-5,默认 3 |
request_id |
string | ❌ | 请求ID,相同ID返回缓存结果(幂等性) |
exclude_stems |
string[] | ❌ | 排除已有题目的题干列表,避免重复出题(最多 100 条) |
请求示例:
{
"file_path": "薪酬制度.docx",
"collection": ["dept_hr", "public_kb"],
"question_types": {
"single_choice": 3,
"multiple_choice": 2,
"true_false": 2,
"fill_blank": 2,
"subjective": 1
},
"difficulty": 3,
"request_id": "uuid-for-idempotency"
}
collection 检索逻辑说明:
- 出题接口采用按优先级顺序检索策略
- 先在
dept_hr中查找文件,找到则停止- 未找到则继续在
public_kb中查找- 这与 RAG 问答接口的并行检索+融合策略不同
- 原因:出题需要特定文件的完整内容,而非广泛搜索
2.2 响应
成功响应:
{
"success": true,
"status": "success",
"status_code": 2020,
"message": "出题成功",
"data": {
"request_id": "uuid-xxx",
"total": 10,
"source_chunks_used": 25,
"requested_types": {"single_choice": 3, "fill_blank": 2},
"actual_types": {"single_choice": 3, "fill_blank": 2},
"warnings": [],
"questions": [
{
"question_type": "single_choice",
"difficulty": 3,
"content": {
"stem": "题干内容",
"data": {
"options": [
{"key": "A", "content": "选项A"},
{"key": "B", "content": "选项B"},
{"key": "C", "content": "选项C"},
{"key": "D", "content": "选项D"}
]
},
"answer": "B",
"explanation": "答案解析"
},
"source_trace": {
"document_name": "薪酬制度.docx",
"page_numbers": [1],
"sources": [{"chunk_id": "...", "page": 1, "section": "...", "snippet": "..."}]
}
}
]
}
}
⚠️ 文件/向量库不存在时的响应:
当 file_path 在指定 collection 中找不到,或 collection 不存在时,接口不会返回错误,而是返回空结果:
{
"success": true,
"status": "success",
"status_code": 2020,
"message": "出题成功",
"data": {
"request_id": null,
"total": 0,
"questions": [],
"requested_types": {"single_choice": 1},
"actual_types": {},
"warnings": ["single_choice: 请求 1 道,实际生成 0 道"]
}
}
后端注意:必须检查
data.total是否 > 0 来判断出题是否成功,不能仅依赖success字段。
### 2.3 返回字段说明
**RAG 服务返回的字段(后端需存储):**
| 字段 | 类型 | 说明 | 后端是否需要存储 |
|------|------|------|------------------|
| `question_type` | string | 题型 | ✅ 存储 |
| `difficulty` | int | 难度 1-5 | ✅ 存储 |
| `content` | object | 题目内容 | ✅ 存储 |
| `content.stem` | string | 题干 | ✅ |
| `content.data` | object | 附加数据(选项、评分标准等) | ✅ |
| `content.answer` | any | 正确答案 | ✅ 批阅时需要 |
| `content.explanation` | string | 答案解析 | ✅ |
| `source_trace` | object | 来源追踪 | ✅ 存储 |
**后端需要自己生成的字段:**
| 字段 | 说明 |
|------|------|
| `question_id` | UUID,唯一标识 |
| `score` | 满分,根据业务需求设定 |
| `tags` | 标签 |
| `status` | 状态(待审核/已通过/已拒绝) |
> **重要**:RAG 服务不返回 `score` 字段,分值由后端在入库时指定。
### 2.4 题型格式对照表
| 题型 | question_type | content.answer 格式 | content.data |
|------|---------------|---------------------|--------------|
| 单选题 | `single_choice` | `"B"` | `{options: [{key, content}]}` |
| 多选题 | `multiple_choice` | `["A", "C"]` | `{options: [{key, content}]}` |
| 判断题 | `true_false` | `"T"` 或 `"F"` | 无 |
| 填空题 | `fill_blank` | `[["答案1"], ["答案2", "同义词"]]` | `{blank_count: 2}` |
| 简答题 | `subjective` | `"参考范文..."` | `{scoring_points: [...]}` |
### 2.5 错误码
| 错误码 | HTTP | 说明 |
|--------|------|------|
| `MISSING_PARAMS` | 400 | 缺少必填参数(file_path / collection / question_types) |
| `INVALID_PARAMS` | 400 | 参数校验失败(题型无效、难度越界、总题数≤0 等) |
| `EXAM_ERROR` | 500 | 出题过程异常(LLM 调用失败、解析错误等) |
> **注意**:文件不存在或向量库不存在时,不会返回错误码,而是返回 `success=true` + `total=0` + `warnings`(见上方说明)。后端应检查 `total > 0`。
### 2.6 智能出题接口(/exam/generate-smart)
与 `/exam/generate` 的区别:不需要传 `question_types`,AI 自动分析文档后决定题型和数量。
POST /exam/generate-smart Content-Type: application/json
**请求体字段说明:**
| 字段 | 类型 | 必需 | 说明 |
|------|------|------|------|
| `file_path` | string | ✅ | 文档路径 |
| `collection` | string 或 string[] | ✅ | 向量库名称 |
| `difficulty` | int | ❌ | 难度等级 1-5,默认 3 |
| `max_total` | int | ❌ | AI 出题总数上限,不传则不限制 |
| `exclude_stems` | string[] | ❌ | 排除已有题目的题干列表 |
| `request_id` | string | ❌ | 请求ID |
**请求示例:**
```json
{
"file_path": "111/卷烟货源组织管理办法.docx",
"collection": "111",
"difficulty": 3,
"max_total": 10,
"request_id": "smart-uuid-xxx"
}
响应: 与 /exam/generate 格式相同,额外包含 ai_analysis 字段(AI 分析结果)。
认证模式说明
RAG 服务支持两种认证模式,由环境变量 DEV_MODE 控制:
| 模式 | DEV_MODE | 认证行为 |
|---|---|---|
| 生产模式 | false |
认证网关直接放行,不校验 Header。权限由后端服务完全控制 |
| 开发模式 | true |
支持 Authorization: Bearer mock-token-admin 模拟登录,无 Header 时使用默认测试用户 |
当前服务器为生产模式(DEV_MODE=false),因此:
AuthorizationHeader 会被忽略- 不会返回
401 UNAUTHORIZED或403 FORBIDDEN - 后端服务应自行完成用户认证和权限校验,再调用 RAG 出题/批阅接口
开发模式下,认证错误会返回正确的业务状态码(
UNAUTHORIZED=4001、FORBIDDEN=4002),HTTP 状态码分别为 401/403。
三、批阅接口
3.1 请求
POST /exam/grade
Content-Type: application/json
请求体字段说明:
| 字段 | 类型 | 必需 | 说明 |
|---|---|---|---|
request_id |
string | ❌ | 请求ID,用于追踪 |
answers |
array | ✅ | 答案列表 |
answers 数组中每个对象的字段:
| 字段 | 类型 | 必需 | 说明 | 来源 |
|---|---|---|---|---|
question_id |
string | ✅ | 题目ID | 后端数据库 |
question_type |
string | ✅ | 题型 | 后端数据库 |
content |
object | ✅ | 题目内容(含正确答案) | 后端数据库 |
student_answer |
any | ✅ | 学生答案 | 学生提交 |
max_score |
number | ✅ | 满分 | 后端数据库 |
填空题
student_answer格式要求(2026-07-03 新增校验):
- 必须为 字符串数组,如
["答案1", "答案2"]- 不是列表(如传了字符串
"答案1")→ 该题直接得 0 分,grading_status为"failed"- 列表中某项不是字符串(如传了数字
123)→ 该题直接得 0 分- 校验失败不影响其他题目的正常批阅
请求示例:
{
"request_id": "grade-uuid-xxx",
"answers": [
{
"question_id": "q-001",
"question_type": "single_choice",
"content": {
"stem": "根据公司规定,员工薪资由哪几部分组成?",
"data": {
"options": [
{"key": "A", "content": "基本工资+奖金"},
{"key": "B", "content": "基本工资+绩效奖金+津贴补贴"},
{"key": "C", "content": "基本工资+加班费"},
{"key": "D", "content": "基本工资"}
]
},
"answer": "B"
},
"student_answer": "B",
"max_score": 5.0
},
{
"question_id": "q-002",
"question_type": "multiple_choice",
"content": {
"stem": "以下哪些属于绩效奖金的评定因素?",
"data": {
"options": [
{"key": "A", "content": "工作质量"},
{"key": "B", "content": "工作态度"},
{"key": "C", "content": "考勤情况"},
{"key": "D", "content": "团队协作"}
]
},
"answer": ["A", "B", "D"]
},
"student_answer": ["A", "B"],
"max_score": 4.0
},
{
"question_id": "q-003",
"question_type": "true_false",
"content": {
"stem": "公司规定员工每月绩效奖金上限为工资的20%。",
"answer": "F"
},
"student_answer": "T",
"max_score": 2.0
},
{
"question_id": "q-004",
"question_type": "fill_blank",
"content": {
"stem": "员工薪资由___、___和___三部分组成。",
"data": {"blank_count": 3},
"answer": [["基本工资"], ["绩效奖金", "绩效"], ["津贴补贴", "补贴"]]
},
"student_answer": ["基本工资", "绩效奖金", "交通补贴"],
"max_score": 6.0
},
{
"question_id": "q-005",
"question_type": "subjective",
"content": {
"stem": "请简述公司薪酬制度的核心原则。",
"data": {
"scoring_points": [
{"point": "公平性", "weight": 0.3},
{"point": "激励性", "weight": 0.3},
{"point": "竞争力", "weight": 0.2},
{"point": "合法性", "weight": 0.2}
]
},
"answer": "公司薪酬制度遵循公平、激励、竞争、合法四大原则..."
},
"student_answer": "我认为公司的薪酬制度应该公平公正...",
"max_score": 10.0
}
]
}
3.2 响应
成功响应:
{
"success": true,
"status": "success",
"status_code": 2012,
"message": "批阅完成",
"data": {
"request_id": "grade-uuid-xxx",
"total_score": 14.0,
"total_max_score": 27.0,
"score_rate": 51.85,
"results": [
{
"question_id": "q-001",
"question_type": "single_choice",
"score": 5.0,
"max_score": 5.0,
"is_correct": true,
"student_answer": "B",
"correct_answer": "B",
"feedback": "回答正确"
},
{
"question_id": "q-002",
"question_type": "multiple_choice",
"score": 2.0,
"max_score": 4.0,
"is_correct": false,
"student_answer": ["A", "B"],
"correct_answer": ["A", "B", "D"],
"feedback": "少选了 D 选项,正确答案: A, B, D"
},
{
"question_id": "q-003",
"question_type": "true_false",
"score": 0.0,
"max_score": 2.0,
"is_correct": false,
"student_answer": "T",
"correct_answer": "F",
"feedback": "正确答案: F"
},
{
"question_id": "q-004",
"question_type": "fill_blank",
"score": 4.0,
"max_score": 6.0,
"is_correct": false,
"student_answer": ["基本工资", "绩效奖金", "交通补贴"],
"correct_answer": [["基本工资"], ["绩效奖金", "绩效"], ["津贴补贴", "补贴"]],
"feedback": "第1、2空正确,第3空答案应为:津贴补贴或补贴"
},
{
"question_id": "q-005",
"question_type": "subjective",
"score": 3.0,
"max_score": 10.0,
"is_correct": false,
"student_answer": "我认为公司的薪酬制度应该公平公正...",
"correct_answer": "公司薪酬制度遵循公平、激励、竞争、合法四大原则...",
"feedback": "得分点:提到公平性概念,但未展开说明激励性和竞争力原则"
}
]
}
}
3.3 返回字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
total_score |
number | 总得分 |
total_max_score |
number | 总满分 |
score_rate |
number | 得分率(百分比) |
results |
array | 每道题的批阅结果 |
results[].question_id |
string | 题目ID |
results[].score |
number | 该题得分 |
results[].max_score |
number | 该题满分 |
results[].is_correct |
boolean | 是否完全正确 |
results[].feedback |
string | 批阅反馈 |
3.4 批阅规则
| 题型 | 批阅方式 | 得分规则 |
|---|---|---|
| 单选题 | 精确匹配 | 正确得满分,错误得 0 |
| 多选题 | 集合比对 | 全对满分,少选得一半,错选得 0 |
| 判断题 | 精确匹配 | 正确得满分,错误得 0 |
| 填空题 | 多答案匹配 | 每空独立评分,支持同义词匹配 |
| 简答题 | LLM 评分 | 根据得分点评分,不超过 max_score |
3.5 错误码
| 错误码 | HTTP | 说明 |
|---|---|---|
MISSING_PARAMS |
400 | 缺少 answers 字段 |
INVALID_PARAMS |
400 | answers 非数组、question_type 无效等 |
GRADE_ERROR |
500 | 批阅过程出错 |
填空题格式错误不返回错误码:当
student_answer格式不合法时,该题返回grading_status: "failed"+score: 0,但接口整体仍返回success: true。后端应检查每道题的grading_status字段。
四、完整调用流程
4.1 出题流程
后端调用 /exam/generate
│
▼
RAG 返回题目
(question_type, difficulty, content, source_trace)
│
▼
后端生成 question_id
后端设置 score (满分)
后端设置 status
│
▼
后端存入数据库
4.2 批阅流程
学生提交答案
│
▼
后端从数据库查询:
- question_id
- question_type
- content (含正确答案,原 question_content 已弃用)
- score (满分)
│
▼
后端组装请求调用 /exam/grade
│
▼
RAG 返回批阅结果
(score, feedback, is_correct)
│
▼
后端存入成绩表
五、字段来源速查表
5.1 出题接口
| 字段 | 返回方 | 存储方 | 说明 |
|---|---|---|---|
| question_id | ❌ 不返回 | 后端生成 | UUID |
| question_type | ✅ RAG | 后端存储 | 题型 |
| difficulty | ✅ RAG | 后端存储 | 难度 1-5 |
| content | ✅ RAG | 后端存储 | 题目内容 |
| source_trace | ✅ RAG | 后端存储 | 来源追踪 |
| score | ❌ 不返回 | 后端设定 | 满分 |
| tags | ❌ 不返回 | 后端设定 | 标签 |
| status | ❌ 不返回 | 后端设定 | 状态 |
5.2 批阅接口
| 字段 | 来源 | 说明 |
|---|---|---|
| question_id | 后端数据库 | 题目唯一标识 |
| question_type | 后端数据库 | 题型 |
| content | 后端数据库 | 题目内容(含正确答案,原 question_content 已弃用) |
| student_answer | 学生提交 | 学生作答 |
| max_score | 后端数据库 | 满分(决定得分上限) |
六、常见问题
Q1: 出题接口返回的题目需要审核吗?
建议后端设置 status 字段进行审核流程,审核通过后再用于组卷。
Q2: 分值如何动态调整?
后端在入库时自由设定 score 字段,批阅时传入 max_score 即可。RAG 服务不干预分值。
Q3: 填空题同义词匹配是如何实现的?
填空题答案格式为 [["主答案", "同义词1", "同义词2"], ...],学生答案匹配任意一个即视为正确。
Q4: 批阅接口超时怎么办?
建议:
- 单次批阅不超过 50 道题
- 设置 60 秒超时
- 主观题较多时适当延长
Q5: 出题数量有上限吗?
2026-07-03 起,出题接口不再限制总题数。服务端会在请求数超过 50 道时记录警告日志,但不影响出题。建议:
- 单次出题不超过 30 道(LLM 生成耗时与题数成正比,30 道约需 5-8 分钟)
- 超过 30 道建议分批调用
- 设置 120 秒以上超时
Q6: 填空题学生答案传错了会怎样?
2026-07-03 起,填空题增加了格式校验:
student_answer不是列表 → 该题得 0 分,grading_status = "failed"- 列表中某项不是字符串 → 该题得 0 分
- 校验失败不影响同一请求中其他题目的批阅
- 接口整体仍返回
success: true,需检查每道题的grading_status
文档版本: v1.2
更新时间: 2026-07-03
相关文档: 后端对接规范.md