Files
rag/docs/出题批题后端对接指南.md

19 KiB
Raw Blame History

出题批题接口 - 后端对接指南

一、接口概览

接口 方法 功能 超时建议
/exam/generate POST 生成题目(手动指定题型数量) 读取超时至少 650 秒
/exam/generate-smart POST 生成题目AI 自动分析文档结构出题) 读取超时至少 650 秒
/exam/grade POST 批阅答案 60秒

2026-07-19 变更

  1. 文件未完成向量化时不再返回“成功但 0 道题”,统一返回 HTTP 409、FILE_NOT_INDEXED、业务状态码 4016
  2. /exam/generate-smart 会在 AI 分析前检查文件状态,未索引时不会调用模型
  3. 正常成功响应保持不变Gunicorn 出题超时调整为 600 秒

2026-07-03 变更

  1. 移除出题总题数 20 道上限,超过 50 道时返回警告(不影响出题)
  2. 填空题批阅增加 student_answer 格式校验(必须为字符串列表)
  3. 认证错误状态码修正(仅 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服务端原样返回当前不提供幂等缓存
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-tracing"
}

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 中找不到时,两个出题接口都返回 HTTP 409

{
  "success": false,
  "status": "failed",
  "error_code": "FILE_NOT_INDEXED",
  "status_code": 4016,
  "message": "文件未向量化: 文件 1.docx 未在向量库中找到,可能正在向量化或未上传",
  "data": {
    "request_id": "uuid-for-tracing",
    "file_status": "not_found",
    "chunk_count": 0
  }
}

后端处理:收到 HTTP 409 且 error_code=FILE_NOT_INDEXED 时,提示用户文件尚未完成向量化;不要把该响应当作成功题目入库,也不要立即高频重试。

当前版本要求后端传入有效的 collection。不存在的向量库暂时也可能表现为 FILE_NOT_INDEXED

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 等)
FILE_NOT_INDEXED 409 文件未完成向量化,业务状态码为 4016
EXAM_ERROR 500 出题过程异常LLM 调用失败、解析错误等)

注意:即使文件已索引,模型也可能少生成题目。成功响应仍应检查 data.totaldata.warnings;部分生成属于成功响应,不应与 FILE_NOT_INDEXED 混淆。

2.6 智能出题接口(/exam/generate-smart

/exam/generate 的区别:不需要传 question_typesAI 自动分析文档后决定题型和数量。

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

请求示例:

{
  "file_path": "111/卷烟货源组织管理办法.docx",
  "collection": "111",
  "difficulty": 3,
  "max_total": 10,
  "request_id": "smart-uuid-xxx"
}

响应: 成功时与 /exam/generate 格式相同,并额外包含 ai_analysis;文件未索引时同样返回 HTTP 409且不会执行 AI 分析。


认证模式说明

RAG 服务支持两种认证模式,由环境变量 DEV_MODE 控制:

模式 DEV_MODE 认证行为
生产模式 false 认证网关直接放行,不校验 Header。权限由后端服务完全控制
开发模式 true 支持 Authorization: Bearer mock-token-admin 模拟登录,无 Header 时使用默认测试用户

当前服务器为生产模式DEV_MODE=false),因此:

  • Authorization Header 会被忽略
  • 不会返回 401 UNAUTHORIZED403 FORBIDDEN
  • 后端服务应自行完成用户认证和权限校验,再调用 RAG 出题/批阅接口

开发模式下,认证错误会返回正确的业务状态码(UNAUTHORIZED=4001FORBIDDEN=4002HTTP 状态码分别为 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 道建议分批调用
  • 后端 HTTP 客户端读取超时设置为至少 650 秒

Q6: 填空题学生答案传错了会怎样?

2026-07-03 起,填空题增加了格式校验:

  • student_answer 不是列表 → 该题得 0 分,grading_status = "failed"
  • 列表中某项不是字符串 → 该题得 0 分
  • 校验失败不影响同一请求中其他题目的批阅
  • 接口整体仍返回 success: true,需检查每道题的 grading_status

文档版本: v1.3 更新时间: 2026-07-19 相关文档: 后端对接规范.md