refactor(api): 统一响应格式迁移 + 异步任务系统 + 状态码体系完善
- 将全部路由文件(12个)的 jsonify 响应迁移至 success_response/error_response 统一格式 - 修复 sync_routes.py error_response 参数错误(P0) - 新增异步任务系统:task_registry + task_routes - 新增状态码:TASK_NOT_FOUND(4014)、TASK_CONFLICT(4015)、REINDEX_ERROR(5040) - 修正 task_routes/exam_pkg 中语义不匹配的状态码 - 更新 curl 测试手册、后端对接规范文档 - 添加缓存性能报告和 Redis 迁移计划
This commit is contained in:
392
docs/curl测试手册.md
392
docs/curl测试手册.md
@@ -22,6 +22,7 @@
|
||||
- [12. 图片服务](#12-图片服务)
|
||||
- [13. 报告服务](#13-报告服务)
|
||||
- [14. 知识库路由](#14-知识库路由)
|
||||
- [15. 异步任务查询](#15-异步任务查询)
|
||||
- [附录. 已知问题](#附录-已知问题)
|
||||
|
||||
---
|
||||
@@ -587,15 +588,18 @@ curl -s -X POST http://localhost:5001/documents/upload \
|
||||
"size": 18,
|
||||
"replaced": false
|
||||
},
|
||||
"sync_status": "已保存并添加到向量库"
|
||||
"sync_status": "已保存,向量化任务已启动",
|
||||
"task_id": "a1b2c3d4e5f6"
|
||||
},
|
||||
"message": "文件上传成功,已保存并添加到向量库",
|
||||
"message": "文件上传成功,已保存,向量化任务已启动",
|
||||
"status": "success",
|
||||
"status_code": 2002,
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
> **异步说明**:文件保存为同步操作,向量化在后台线程异步执行。响应中的 `task_id` 可用于轮询向量化进度(`GET /tasks/<task_id>`)。
|
||||
>
|
||||
> **同名文件处理**:上传同名文件时,旧版本的切片会被自动清理后覆盖(`replaced: true`),不会生成时间戳后缀文件。
|
||||
|
||||
**验证结果**:✅ 通过
|
||||
@@ -624,7 +628,8 @@ curl -s -X POST http://localhost:5001/documents/batch-upload \
|
||||
{"filename": "file2.txt", "path": "public_kb/file2.txt", "status": "success", "replaced": false}
|
||||
],
|
||||
"success_count": 2,
|
||||
"total": 2
|
||||
"total": 2,
|
||||
"task_id": "b2c3d4e5f6a1"
|
||||
},
|
||||
"message": "批量上传完成,成功 2/2 个文件",
|
||||
"status": "success",
|
||||
@@ -633,6 +638,8 @@ curl -s -X POST http://localhost:5001/documents/batch-upload \
|
||||
}
|
||||
```
|
||||
|
||||
> **异步说明**:批量上传成功后自动触发后台向量化任务。`task_id` 可用于轮询向量化进度(`GET /tasks/<task_id>`)。若无成功上传的文件则不返回 `task_id`。
|
||||
>
|
||||
> **同名文件处理**:与单文件上传相同,批量上传中遇到同名文件也会自动覆盖旧版本(`replaced: true`)。
|
||||
|
||||
**验证结果**:✅ 通过
|
||||
@@ -882,7 +889,7 @@ curl -s -X DELETE "http://localhost:5001/chunks/人员名册.txt_text_0?collecti
|
||||
|
||||
### POST /sync
|
||||
|
||||
触发文档同步。
|
||||
触发文档同步(异步任务)。
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://localhost:5001/sync \
|
||||
@@ -893,24 +900,23 @@ curl -s -X POST http://localhost:5001/sync \
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"result": {
|
||||
"documents_added": 1,
|
||||
"documents_deleted": 1,
|
||||
"documents_modified": 0,
|
||||
"documents_processed": 2,
|
||||
"end_time": "2026-05-04T01:45:42",
|
||||
"errors": [],
|
||||
"start_time": "2026-05-04T01:45:41",
|
||||
"status": "completed"
|
||||
}
|
||||
"task_id": "c3d4e5f6a1b2",
|
||||
"message": "同步任务已启动,通过 GET /tasks/c3d4e5f6a1b2 查询进度"
|
||||
},
|
||||
"message": "同步完成",
|
||||
"message": "同步任务已启动",
|
||||
"status": "success",
|
||||
"status_code": 2010,
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
> **⚠️ 异步变更**:此接口已从同步改为异步。不再直接返回同步结果,而是返回 `task_id`。后端需通过 `GET /tasks/<task_id>` 轮询任务状态,直到 `status` 为 `completed` 或 `failed`。
|
||||
>
|
||||
> **冲突检测**:如果已有同步任务正在运行,返回 HTTP 409:
|
||||
> ```json
|
||||
> {"error": "TASK_RUNNING", "message": "同步任务正在执行中 (task_id: xxx),请等待完成"}
|
||||
> ```
|
||||
|
||||
**验证结果**:✅ 通过
|
||||
|
||||
---
|
||||
@@ -1456,44 +1462,54 @@ curl -s -X POST http://localhost:5001/exam/generate \
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"questions": [
|
||||
{
|
||||
"content": {
|
||||
"stem": "重购率的定义是下列哪一项?",
|
||||
"answer": "B",
|
||||
"data": {
|
||||
"options": [
|
||||
{"content": "选项A内容", "key": "A"},
|
||||
{"content": "品规连续两周订货客户数/上周订货客户数*100%", "key": "B"}
|
||||
]
|
||||
},
|
||||
"explanation": "重购率的定义是..."
|
||||
},
|
||||
"difficulty": 3,
|
||||
"question_type": "single_choice",
|
||||
"source_trace": {
|
||||
"chunk_ids": ["1.docx_76"],
|
||||
"document_name": "public_kb/1.docx",
|
||||
"page_numbers": [1],
|
||||
"sources": [...]
|
||||
}
|
||||
}
|
||||
],
|
||||
"request_id": null,
|
||||
"source_chunks_used": 15,
|
||||
"success": true,
|
||||
"total": 10,
|
||||
"requested_types": {"single_choice": 5, "true_false": 3, "fill_blank": 2},
|
||||
"actual_types": {"single_choice": 5, "true_false": 3, "fill_blank": 2},
|
||||
"warnings": []
|
||||
"task_id": "d4e5f6a1b2c3",
|
||||
"message": "出题任务已启动 (10题),通过 GET /tasks/d4e5f6a1b2c3 查询结果"
|
||||
},
|
||||
"message": "出题成功",
|
||||
"message": "出题任务已启动",
|
||||
"status": "success",
|
||||
"status_code": 2020,
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
> **⚠️ 异步变更**:此接口已从同步改为异步。响应仅返回 `task_id`,后端需通过 `GET /tasks/<task_id>` 轮询任务状态。任务完成后,`result` 字段包含完整出题结果(格式见下方说明)。
|
||||
|
||||
**轮询结果(GET /tasks/\<task_id\> 完成后的 result 字段)**:
|
||||
```json
|
||||
{
|
||||
"questions": [
|
||||
{
|
||||
"content": {
|
||||
"stem": "重购率的定义是下列哪一项?",
|
||||
"answer": "B",
|
||||
"data": {
|
||||
"options": [
|
||||
{"content": "选项A内容", "key": "A"},
|
||||
{"content": "品规连续两周订货客户数/上周订货客户数*100%", "key": "B"}
|
||||
]
|
||||
},
|
||||
"explanation": "重购率的定义是..."
|
||||
},
|
||||
"difficulty": 3,
|
||||
"question_type": "single_choice",
|
||||
"source_trace": {
|
||||
"chunk_ids": ["1.docx_76"],
|
||||
"document_name": "public_kb/1.docx",
|
||||
"page_numbers": [1],
|
||||
"sources": [...]
|
||||
}
|
||||
}
|
||||
],
|
||||
"request_id": null,
|
||||
"source_chunks_used": 15,
|
||||
"success": true,
|
||||
"total": 10,
|
||||
"requested_types": {"single_choice": 5, "true_false": 3, "fill_blank": 2},
|
||||
"actual_types": {"single_choice": 5, "true_false": 3, "fill_blank": 2},
|
||||
"warnings": []
|
||||
}
|
||||
```
|
||||
|
||||
> **说明**:`warnings` 字段在某题型实际生成数量少于请求数量时返回提示信息。
|
||||
|
||||
**验证结果**:✅ 通过(2026-06-05)
|
||||
@@ -1527,31 +1543,41 @@ curl -s -X POST http://localhost:5001/exam/generate-smart \
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"ai_analysis": {
|
||||
"total_knowledge_points": 21,
|
||||
"suitable_types": ["single_choice", "multiple_choice", "true_false", "subjective"],
|
||||
"question_types": {
|
||||
"single_choice": 8,
|
||||
"multiple_choice": 6,
|
||||
"true_false": 4,
|
||||
"fill_blank": 0,
|
||||
"subjective": 3
|
||||
},
|
||||
"reason": "文档包含21个知识点,涵盖术语定义、流程步骤、数值标准..."
|
||||
},
|
||||
"questions": [...],
|
||||
"request_id": null,
|
||||
"source_chunks_used": 15,
|
||||
"success": true,
|
||||
"total": 16
|
||||
"task_id": "e5f6a1b2c3d4",
|
||||
"message": "AI 智能出题任务已启动,通过 GET /tasks/e5f6a1b2c3d4 查询结果"
|
||||
},
|
||||
"message": "AI 智能出题成功",
|
||||
"message": "AI 智能出题任务已启动",
|
||||
"status": "success",
|
||||
"status_code": 2020,
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
> **⚠️ 异步变更**:此接口已从同步改为异步。响应仅返回 `task_id`,后端需通过 `GET /tasks/<task_id>` 轮询任务状态。任务完成后,`result` 字段包含完整出题结果(含 `ai_analysis` 字段)。
|
||||
|
||||
**轮询结果(GET /tasks/\<task_id\> 完成后的 result 字段)**:
|
||||
```json
|
||||
{
|
||||
"ai_analysis": {
|
||||
"total_knowledge_points": 21,
|
||||
"suitable_types": ["single_choice", "multiple_choice", "true_false", "subjective"],
|
||||
"question_types": {
|
||||
"single_choice": 8,
|
||||
"multiple_choice": 6,
|
||||
"true_false": 4,
|
||||
"fill_blank": 0,
|
||||
"subjective": 3
|
||||
},
|
||||
"reason": "文档包含21个知识点,涵盖术语定义、流程步骤、数值标准..."
|
||||
},
|
||||
"questions": [...],
|
||||
"request_id": null,
|
||||
"source_chunks_used": 15,
|
||||
"success": true,
|
||||
"total": 16
|
||||
}
|
||||
```
|
||||
|
||||
**注意事项**:
|
||||
- 实际出题数量 ≤ min(文档知识点数, AI 推荐数量)
|
||||
- 如果文档知识点较少,生成的题目数量会相应减少
|
||||
@@ -1601,33 +1627,43 @@ curl -s -X POST http://localhost:5001/exam/grade \
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"request_id": null,
|
||||
"results": [
|
||||
{
|
||||
"question_id": "q1",
|
||||
"score": 2,
|
||||
"max_score": 2,
|
||||
"grading_status": "success",
|
||||
"details": {
|
||||
"correct": true,
|
||||
"student_answer": "B",
|
||||
"correct_answer": "B",
|
||||
"feedback": "正确!"
|
||||
}
|
||||
}
|
||||
],
|
||||
"score_rate": 100.0,
|
||||
"success": true,
|
||||
"total_max_score": 2,
|
||||
"total_score": 2
|
||||
"task_id": "f6a1b2c3d4e5",
|
||||
"message": "批阅任务已启动 (1题),通过 GET /tasks/f6a1b2c3d4e5 查询结果"
|
||||
},
|
||||
"message": "批阅完成",
|
||||
"message": "批阅任务已启动",
|
||||
"status": "success",
|
||||
"status_code": 2021,
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
> **⚠️ 异步变更**:此接口已从同步改为异步。响应仅返回 `task_id`,后端需通过 `GET /tasks/<task_id>` 轮询任务状态。任务完成后,`result` 字段包含完整批阅结果(格式见下方说明)。
|
||||
|
||||
**轮询结果(GET /tasks/\<task_id\> 完成后的 result 字段)**:
|
||||
```json
|
||||
{
|
||||
"request_id": null,
|
||||
"results": [
|
||||
{
|
||||
"question_id": "q1",
|
||||
"score": 2,
|
||||
"max_score": 2,
|
||||
"grading_status": "success",
|
||||
"details": {
|
||||
"correct": true,
|
||||
"student_answer": "B",
|
||||
"correct_answer": "B",
|
||||
"feedback": "正确!"
|
||||
}
|
||||
}
|
||||
],
|
||||
"score_rate": 100.0,
|
||||
"success": true,
|
||||
"total_max_score": 2,
|
||||
"total_score": 2
|
||||
}
|
||||
```
|
||||
|
||||
**`grading_status` 取值说明**:
|
||||
- `success`:评分成功
|
||||
- `failed`:评分失败(主观题 LLM 解析失败或超时),此时 `details` 包含 `error` 字段
|
||||
@@ -1823,6 +1859,190 @@ curl -s -X POST http://localhost:5001/kb/route \
|
||||
|
||||
---
|
||||
|
||||
## 15. 异步任务查询
|
||||
|
||||
> 所有异步操作(同步、重建索引、上传向量化、出题、批阅)返回的 `task_id` 均可通过以下接口查询进度。
|
||||
|
||||
### GET /tasks
|
||||
|
||||
获取任务列表。
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:5001/tasks
|
||||
```
|
||||
|
||||
**查询参数**:
|
||||
| 参数 | 类型 | 必需 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `status` | string | ❌ | 过滤状态:`pending` / `running` / `completed` / `failed` |
|
||||
| `type` | string | ❌ | 过滤类型:`sync` / `reindex` / `upload` / `batch_upload` / `exam_generate` / `exam_grade` |
|
||||
| `limit` | int | ❌ | 返回数量限制(默认 50) |
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"tasks": [
|
||||
{
|
||||
"task_id": "a1b2c3d4e5f6",
|
||||
"type": "sync",
|
||||
"description": "文档同步",
|
||||
"status": "running",
|
||||
"progress": 45.0,
|
||||
"current": 9,
|
||||
"total": 20,
|
||||
"stage": "处理文件",
|
||||
"message": "已处理: 产品手册.pdf",
|
||||
"created_at": "2026-06-05T10:30:00"
|
||||
}
|
||||
],
|
||||
"total": 1
|
||||
},
|
||||
"message": "查询成功",
|
||||
"status": "success",
|
||||
"status_code": 2000,
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
**验证结果**:✅ 通过
|
||||
|
||||
---
|
||||
|
||||
### GET /tasks/\<task_id\>
|
||||
|
||||
获取单个任务状态(JSON 轮询接口)。
|
||||
|
||||
**后端组推荐使用此接口轮询任务进度,建议间隔 1-2 秒。**
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:5001/tasks/a1b2c3d4e5f6
|
||||
```
|
||||
|
||||
**响应示例(运行中)**:
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"task_id": "a1b2c3d4e5f6",
|
||||
"type": "sync",
|
||||
"description": "文档同步",
|
||||
"status": "running",
|
||||
"progress": 45.0,
|
||||
"current": 9,
|
||||
"total": 20,
|
||||
"stage": "处理文件",
|
||||
"message": "已处理: 产品手册.pdf",
|
||||
"created_at": "2026-06-05T10:30:00",
|
||||
"started_at": "2026-06-05T10:30:01"
|
||||
},
|
||||
"message": "查询成功",
|
||||
"status": "success",
|
||||
"status_code": 2000,
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
**响应示例(已完成)**:
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"task_id": "a1b2c3d4e5f6",
|
||||
"type": "sync",
|
||||
"description": "文档同步",
|
||||
"status": "completed",
|
||||
"progress": 100.0,
|
||||
"current": 20,
|
||||
"total": 20,
|
||||
"stage": "完成",
|
||||
"message": "同步完成",
|
||||
"created_at": "2026-06-05T10:30:00",
|
||||
"started_at": "2026-06-05T10:30:01",
|
||||
"completed_at": "2026-06-05T10:30:15",
|
||||
"duration_ms": 14000,
|
||||
"result": {
|
||||
"documents_processed": 20,
|
||||
"documents_added": 3,
|
||||
"documents_modified": 2,
|
||||
"documents_deleted": 0,
|
||||
"errors": []
|
||||
}
|
||||
},
|
||||
"message": "查询成功",
|
||||
"status": "success",
|
||||
"status_code": 2000,
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
> **任务状态**:
|
||||
> - `pending`:已创建,等待执行
|
||||
> - `running`:正在执行
|
||||
> - `completed`:执行完成,`result` 字段包含完整结果
|
||||
> - `failed`:执行失败,`error` 字段包含错误信息
|
||||
>
|
||||
> **轮询建议**:间隔 1-2 秒,当 `status` 为 `completed` 或 `failed` 时停止轮询。
|
||||
|
||||
**验证结果**:✅ 通过
|
||||
|
||||
---
|
||||
|
||||
### GET /tasks/\<task_id\>/progress
|
||||
|
||||
SSE 流式任务进度推送(dev-ui 前端推荐使用)。
|
||||
|
||||
```bash
|
||||
curl -s -N http://localhost:5001/tasks/a1b2c3d4e5f6/progress
|
||||
```
|
||||
|
||||
**SSE 事件序列**:
|
||||
```
|
||||
data: {"type": "start", "data": {"stage": "扫描文档"}}
|
||||
|
||||
data: {"type": "progress", "data": {"progress": 10.0, "current": 2, "total": 20, "stage": "处理文件", "message": "已处理: file1.pdf"}}
|
||||
|
||||
data: {"type": "progress", "data": {"progress": 25.0, "current": 5, "total": 20, "stage": "处理文件", "message": "已处理: file2.docx"}}
|
||||
|
||||
data: {"type": "complete", "data": {"task_id": "a1b2c3d4e5f6", "status": "completed", "result": {...}}}
|
||||
```
|
||||
|
||||
> **SSE 事件类型**:
|
||||
> - `start`:任务开始
|
||||
> - `progress`:进度更新(包含 progress/current/total/stage/message)
|
||||
> - `complete`:任务完成(data 为完整任务详情,含 result)
|
||||
> - `error`:任务失败(data 包含 message 错误信息)
|
||||
> - 每 1 秒发送一次 `: heartbeat` 保活
|
||||
|
||||
**验证结果**:✅ 通过
|
||||
|
||||
---
|
||||
|
||||
### GET /tasks/stats
|
||||
|
||||
获取任务统计信息。
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:5001/tasks/stats
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"total": 5,
|
||||
"by_status": {"running": 1, "completed": 3, "failed": 1},
|
||||
"by_type": {"sync": 2, "exam_generate": 2, "upload": 1}
|
||||
},
|
||||
"message": "查询成功",
|
||||
"status": "success",
|
||||
"status_code": 2000,
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
**验证结果**:✅ 通过
|
||||
|
||||
---
|
||||
|
||||
## 附录. 已知问题
|
||||
|
||||
### 1. /rag 接口 collections 参数
|
||||
@@ -1951,11 +2171,11 @@ curl -s -X POST http://localhost:5001/kb/route \
|
||||
当代码升级涉及元数据字段变更时(如新增 `chunk_index`、`doc_type`),需要重构向量库:
|
||||
|
||||
```bash
|
||||
# 重构指定知识库(清除哈希记录 → 触发全量同步)
|
||||
# 重构指定知识库(清除哈希记录 → 触发全量同步,异步任务)
|
||||
curl -s -X POST http://127.0.0.1:5001/collections/<kb_name>/reindex
|
||||
```
|
||||
|
||||
> **⚠️ 注意**:reindex 会调用 `sync_now()` 全局同步,期间 gunicorn worker 被阻塞,搜索和问答接口将暂时无响应。建议在低峰期执行。
|
||||
> **⚠️ 注意**:reindex 已改为异步任务,立即返回 `task_id`。通过 `GET /tasks/<task_id>` 轮询进度。不再阻塞 gunicorn worker,搜索和问答接口不受影响。
|
||||
|
||||
### Reranker 配置
|
||||
|
||||
|
||||
255
docs/后端对接规范.md
255
docs/后端对接规范.md
@@ -4,9 +4,33 @@
|
||||
|
||||
## 📋 变更记录(2026-06-05 更新)
|
||||
|
||||
> **本次更新内容**:新增 AI 智能出题端口、更新生产环境测试结果
|
||||
> **本次更新内容**:新增 AI 智能出题端口、更新生产环境测试结果、**长操作改为异步任务**
|
||||
>
|
||||
> **2026-06-05 更新**:出题批卷接口格式优化与输入校验增强
|
||||
>
|
||||
> **2026-06-05 异步任务变更**:同步、上传向量化、出题、批阅等长耗时操作改为异步任务模式,立即返回 `task_id`,通过 `GET /tasks/<task_id>` 轮询结果
|
||||
|
||||
### 异步任务变更(⚠️ 重要,2026-06-05)
|
||||
|
||||
| 端点 | 变更说明 |
|
||||
|------|----------|
|
||||
| `POST /sync` | 改为异步任务,返回 `{"task_id": "xxx"}` 而非同步结果 |
|
||||
| `POST /documents/sync` | 改为异步任务,返回 `{"task_id": "xxx"}` |
|
||||
| `POST /collections/<kb>/reindex` | 改为异步任务,返回 `{"task_id": "xxx"}` |
|
||||
| `POST /documents/upload` | 新增 `task_id` 字段(向量化后台执行) |
|
||||
| `POST /documents/batch-upload` | 新增 `task_id` 字段(批量向量化后台执行) |
|
||||
| `POST /exam/generate` | 改为异步任务,返回 `{"task_id": "xxx"}` |
|
||||
| `POST /exam/generate-smart` | 改为异步任务,返回 `{"task_id": "xxx"}` |
|
||||
| `POST /exam/grade` | 改为异步任务,返回 `{"task_id": "xxx"}` |
|
||||
|
||||
**新增任务查询接口**:
|
||||
|
||||
| 端点 | 方法 | 功能 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `/tasks` | GET | 任务列表 | 支持按 status/type 过滤 |
|
||||
| `/tasks/<task_id>` | GET | 任务状态(JSON) | 后端组推荐轮询接口,建议 1-2 秒间隔 |
|
||||
| `/tasks/<task_id>/progress` | GET | 任务进度(SSE) | dev-ui 前端推荐使用 |
|
||||
| `/tasks/stats` | GET | 任务统计 | 按状态和类型分组统计 |
|
||||
|
||||
### 新增端口
|
||||
|
||||
@@ -123,10 +147,22 @@ RAG服务负责:
|
||||
|
||||
### 2.4 出题系统(可选)
|
||||
|
||||
| 端点 | 方法 | 功能 |
|
||||
|-----|------|------|
|
||||
| `/exam/generate` | POST | 生成题目 |
|
||||
| `/exam/grade` | POST | 批阅答案 |
|
||||
| 端点 | 方法 | 功能 | 说明 |
|
||||
|-----|------|------|------|
|
||||
| `/exam/generate` | POST | 生成题目 | 异步任务,返回 task_id |
|
||||
| `/exam/generate-smart` | POST | AI 智能出题 | 异步任务,返回 task_id |
|
||||
| `/exam/grade` | POST | 批阅答案 | 异步任务,返回 task_id |
|
||||
|
||||
### 2.5 异步任务查询
|
||||
|
||||
> 所有异步操作(同步、重建索引、上传向量化、出题、批阅)返回的 `task_id` 均可通过以下接口查询进度。
|
||||
|
||||
| 端点 | 方法 | 功能 | 说明 |
|
||||
|-----|------|------|------|
|
||||
| `/tasks` | GET | 任务列表 | 支持按 status/type 过滤 |
|
||||
| `/tasks/<task_id>` | GET | 任务状态(JSON) | **后端组推荐轮询接口**,建议 1-2 秒间隔 |
|
||||
| `/tasks/<task_id>/progress` | GET | 任务进度(SSE) | dev-ui 前端推荐使用 |
|
||||
| `/tasks/stats` | GET | 任务统计 | 按状态和类型分组统计 |
|
||||
|
||||
---
|
||||
|
||||
@@ -247,7 +283,7 @@ ENABLE_DIFY_WORKFLOW=false
|
||||
|
||||
---
|
||||
|
||||
**最后更新**: 2026-04-29
|
||||
**最后更新**: 2026-06-05
|
||||
|
||||
> 本文档供后端开发人员参考,用于对接 RAG 知识库服务。
|
||||
|
||||
@@ -1089,17 +1125,24 @@ Content-Type: multipart/form-data
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "文件上传成功,已保存并添加到向量库",
|
||||
"file": {
|
||||
"filename": "document.pdf",
|
||||
"collection": "public_kb",
|
||||
"path": "public_kb/document.pdf",
|
||||
"size": 1024000,
|
||||
"replaced": false
|
||||
"status_code": 2002,
|
||||
"message": "文件上传成功,已保存,向量化任务已启动",
|
||||
"data": {
|
||||
"file": {
|
||||
"filename": "document.pdf",
|
||||
"collection": "public_kb",
|
||||
"path": "public_kb/document.pdf",
|
||||
"size": 1024000,
|
||||
"replaced": false
|
||||
},
|
||||
"sync_status": "已保存,向量化任务已启动",
|
||||
"task_id": "a1b2c3d4e5f6"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **异步说明**:文件保存为同步操作,向量化在后台线程异步执行。响应中的 `task_id` 可用于轮询向量化进度(`GET /tasks/<task_id>`)。当同步服务不可用时,`task_id` 为 `null`,`sync_status` 为 `"已保存,等待手动同步"`。
|
||||
|
||||
**同名文件处理**:上传同名文件时,旧版本的切片会被自动清理后覆盖(`replaced: true`),不会生成时间戳后缀文件。这确保了向量库中不会出现同一文档的新旧切片共存的情况。
|
||||
|
||||
### 5.2 批量上传
|
||||
@@ -1257,26 +1300,51 @@ POST /sync
|
||||
|
||||
**请求体:** 无需传递参数(同步所有知识库)
|
||||
|
||||
**响应:**
|
||||
**响应(异步任务):**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"status": "success",
|
||||
"status_code": 2010,
|
||||
"message": "同步完成",
|
||||
"message": "同步任务已启动",
|
||||
"data": {
|
||||
"result": {
|
||||
"documents_added": 1,
|
||||
"documents_deleted": 1,
|
||||
"documents_modified": 0,
|
||||
"documents_processed": 2,
|
||||
"errors": [],
|
||||
"status": "completed"
|
||||
}
|
||||
"task_id": "c3d4e5f6a1b2",
|
||||
"message": "同步任务已启动,通过 GET /tasks/c3d4e5f6a1b2 查询进度"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **⚠️ 异步变更**:此接口已从同步改为异步。不再直接返回同步结果,而是返回 `task_id`。后端需通过 `GET /tasks/<task_id>` 轮询任务状态,直到 `status` 为 `completed` 或 `failed`。任务完成后,`result` 字段包含完整的同步结果(含 `documents_processed`、`documents_added` 等)。
|
||||
>
|
||||
> **冲突检测**:如果已有同步任务正在运行,返回 HTTP 409:`{"error": "TASK_RUNNING", "message": "同步任务正在执行中 (task_id: xxx),请等待完成"}`
|
||||
|
||||
**后端轮询示例**:
|
||||
|
||||
```python
|
||||
import time
|
||||
import requests
|
||||
|
||||
def trigger_sync_and_wait():
|
||||
"""触发同步并等待完成"""
|
||||
# 1. 触发同步任务
|
||||
resp = requests.post('http://rag-service:5001/sync')
|
||||
task_id = resp.json()['data']['task_id']
|
||||
|
||||
# 2. 轮询任务状态(每 2 秒)
|
||||
while True:
|
||||
time.sleep(2)
|
||||
status_resp = requests.get(f'http://rag-service:5001/tasks/{task_id}')
|
||||
task_data = status_resp.json()['data']
|
||||
|
||||
if task_data['status'] == 'completed':
|
||||
print(f"同步完成: {task_data['result']}")
|
||||
return task_data['result']
|
||||
elif task_data['status'] == 'failed':
|
||||
raise Exception(f"同步失败: {task_data['error']}")
|
||||
else:
|
||||
print(f"同步中: {task_data['progress']}% - {task_data['message']}")
|
||||
```
|
||||
```
|
||||
|
||||
### 6.2 同步状态
|
||||
|
||||
```
|
||||
@@ -1359,7 +1427,7 @@ POST /sync/stop
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"status_code": 3001,
|
||||
"status_code": 2010,
|
||||
"message": "文件监控已启动"
|
||||
}
|
||||
```
|
||||
@@ -1417,26 +1485,24 @@ POST /exam/generate
|
||||
| difficulty | 必须为 1-5 的整数 | HTTP 400 INVALID_PARAMS |
|
||||
| **总题数上限** | **所有题型数量之和不能超过 20** | HTTP 400 INVALID_PARAMS |
|
||||
|
||||
**响应:**
|
||||
**响应(异步任务):**
|
||||
|
||||
**完整响应格式**(包含外层包装):
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"status": "success",
|
||||
"status_code": 2011,
|
||||
"message": "出题完成",
|
||||
"status_code": 2020,
|
||||
"message": "出题任务已启动",
|
||||
"data": {
|
||||
"success": true,
|
||||
"request_id": "xxx",
|
||||
"total": 10,
|
||||
"source_chunks_used": 15,
|
||||
"questions": [...]
|
||||
"task_id": "d4e5f6a1b2c3",
|
||||
"message": "出题任务已启动 (10题),通过 GET /tasks/d4e5f6a1b2c3 查询结果"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**data 内部结构**:
|
||||
> **⚠️ 异步变更**:此接口已从同步改为异步。响应仅返回 `task_id`,后端需通过 `GET /tasks/<task_id>` 轮询任务状态。任务完成后,`result` 字段包含完整出题结果(格式见下方说明)。
|
||||
|
||||
**轮询结果(GET /tasks/\<task_id\> 完成后的 result 字段)**:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
@@ -1635,25 +1701,22 @@ POST /exam/grade
|
||||
|
||||
#### 响应
|
||||
|
||||
**完整响应格式**(包含外层包装):
|
||||
**响应(异步任务):**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"status": "success",
|
||||
"status_code": 2021,
|
||||
"message": "批阅完成",
|
||||
"message": "批阅任务已启动",
|
||||
"data": {
|
||||
"request_id": "可选,原样返回",
|
||||
"success": true,
|
||||
"total_score": 12.5,
|
||||
"total_max_score": 22.0,
|
||||
"score_rate": 56.8,
|
||||
"results": [...]
|
||||
"task_id": "f6a1b2c3d4e5",
|
||||
"message": "批阅任务已启动 (5题),通过 GET /tasks/f6a1b2c3d4e5 查询结果"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**data 内部结构**:
|
||||
> **⚠️ 异步变更**:此接口已从同步改为异步。响应仅返回 `task_id`,后端需通过 `GET /tasks/<task_id>` 轮询任务状态。任务完成后,`result` 字段包含完整批阅结果(格式见下方说明)。
|
||||
|
||||
**轮询结果(GET /tasks/\<task_id\> 完成后的 result 字段)**:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -1817,16 +1880,30 @@ def build_grade_request(answer_list, questions_map):
|
||||
return {"answers": grade_answers}
|
||||
```
|
||||
|
||||
**Step 4: 调用 RAG 批卷接口**
|
||||
**Step 4: 调用 RAG 批卷接口(异步任务)**
|
||||
|
||||
```python
|
||||
import time
|
||||
|
||||
def call_rag_grade(grade_request):
|
||||
"""调用 RAG 批卷接口"""
|
||||
"""调用 RAG 批卷接口并轮询等待结果"""
|
||||
# 1. 提交批阅任务
|
||||
response = requests.post(
|
||||
'http://rag-service:5001/exam/grade',
|
||||
json=grade_request
|
||||
)
|
||||
return response.json()
|
||||
task_id = response.json()['data']['task_id']
|
||||
|
||||
# 2. 轮询任务状态(每 2 秒)
|
||||
while True:
|
||||
time.sleep(2)
|
||||
status_resp = requests.get(f'http://rag-service:5001/tasks/{task_id}')
|
||||
task_data = status_resp.json()['data']
|
||||
|
||||
if task_data['status'] == 'completed':
|
||||
return task_data['result']
|
||||
elif task_data['status'] == 'failed':
|
||||
raise Exception(f"批阅失败: {task_data['error']}")
|
||||
```
|
||||
|
||||
**Step 5: 更新学生成绩**
|
||||
@@ -2430,19 +2507,22 @@ ChromaDB 集合名称限制:
|
||||
|
||||
| 端点 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| `/sync` | POST | 触发同步 |
|
||||
| `/sync` | POST | 触发同步(**异步任务,返回 task_id**) |
|
||||
| `/sync/status` | GET | 同步状态 |
|
||||
| `/sync/history` | GET | 同步历史 |
|
||||
| `/sync/changes` | GET | 变更日志 |
|
||||
| `/sync/start` | POST | 启动文件监控 |
|
||||
| `/sync/stop` | POST | 停止文件监控 |
|
||||
| `/documents/sync` | POST | 触发文档同步(**异步任务,返回 task_id**) |
|
||||
| `/collections/<kb_name>/reindex` | POST | 重建索引(**异步任务,返回 task_id**) |
|
||||
|
||||
### 13.7 出题系统
|
||||
|
||||
| 端点 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| `/exam/generate` | POST | 生成题目 |
|
||||
| `/exam/grade` | POST | 批改答案 |
|
||||
| `/exam/generate` | POST | 生成题目(**异步任务,返回 task_id**) |
|
||||
| `/exam/generate-smart` | POST | AI 智能出题(**异步任务,返回 task_id**) |
|
||||
| `/exam/grade` | POST | 批改答案(**异步任务,返回 task_id**) |
|
||||
| `/exam/health` | GET | 出题服务健康检查 |
|
||||
|
||||
### 13.8 反馈与 FAQ 管理
|
||||
@@ -2497,6 +2577,77 @@ ChromaDB 集合名称限制:
|
||||
|------|------|------|
|
||||
| `/documents/<path>/preview` | GET | 文档预览,按 `chunk_index` 跳转到具体切片(dev-ui 引用溯源用) |
|
||||
|
||||
### 13.13 异步任务查询
|
||||
|
||||
> 所有异步操作(同步、重建索引、上传向量化、出题、批阅)返回的 `task_id` 均可通过以下接口查询进度。
|
||||
|
||||
| 端点 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| `/tasks` | GET | 任务列表(支持 status/type 过滤) |
|
||||
| `/tasks/<task_id>` | GET | 任务状态(JSON 轮询,**后端组推荐使用**) |
|
||||
| `/tasks/<task_id>/progress` | GET | 任务进度(SSE 流式,dev-ui 前端使用) |
|
||||
| `/tasks/stats` | GET | 任务统计 |
|
||||
|
||||
**任务状态字段说明**:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `task_id` | string | 任务唯一标识 |
|
||||
| `type` | string | 类型:sync / reindex / upload / batch_upload / exam_generate / exam_grade |
|
||||
| `status` | string | 状态:pending / running / completed / failed |
|
||||
| `progress` | float | 进度百分比(0-100) |
|
||||
| `current` | int | 当前处理项数 |
|
||||
| `total` | int | 总项数 |
|
||||
| `stage` | string | 当前阶段 |
|
||||
| `message` | string | 当前步骤描述 |
|
||||
| `result` | any | 完成后的结果数据(仅 status=completed 时存在) |
|
||||
| `error` | string | 失败错误信息(仅 status=failed 时存在) |
|
||||
| `duration_ms` | int | 执行耗时毫秒(仅已完成时存在) |
|
||||
|
||||
**后端对接轮询模式**:
|
||||
|
||||
```python
|
||||
import time
|
||||
import requests
|
||||
|
||||
def async_task_poll(task_id, base_url='http://rag-service:5001', interval=2, timeout=300):
|
||||
"""
|
||||
通用异步任务轮询函数
|
||||
|
||||
Args:
|
||||
task_id: 任务 ID
|
||||
base_url: RAG 服务地址
|
||||
interval: 轮询间隔(秒)
|
||||
timeout: 超时时间(秒)
|
||||
|
||||
Returns:
|
||||
任务结果(result 字段)
|
||||
|
||||
Raises:
|
||||
TimeoutError: 超时
|
||||
Exception: 任务失败
|
||||
"""
|
||||
elapsed = 0
|
||||
while elapsed < timeout:
|
||||
time.sleep(interval)
|
||||
elapsed += interval
|
||||
|
||||
resp = requests.get(f'{base_url}/tasks/{task_id}')
|
||||
if resp.status_code == 404:
|
||||
raise Exception(f"任务不存在: {task_id}")
|
||||
|
||||
task = resp.json()['data']
|
||||
|
||||
if task['status'] == 'completed':
|
||||
return task.get('result')
|
||||
elif task['status'] == 'failed':
|
||||
raise Exception(f"任务失败: {task.get('error', '未知错误')}")
|
||||
# 可选:记录进度日志
|
||||
# logger.info(f"任务 {task_id}: {task['progress']}% - {task['message']}")
|
||||
|
||||
raise TimeoutError(f"任务超时: {task_id} (已等待 {timeout}s)")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十四、文件管理服务(后端负责)
|
||||
|
||||
554
docs/异步任务接口变更说明.md
Normal file
554
docs/异步任务接口变更说明.md
Normal file
@@ -0,0 +1,554 @@
|
||||
# 异步任务接口变更说明
|
||||
|
||||
> **变更日期**:2026-06-05
|
||||
>
|
||||
> **变更原因**:同步、向量化、出题、批阅等操作耗时较长(数秒到数十秒),改为异步任务模式后接口立即返回 `task_id`,避免后端调用超时。
|
||||
>
|
||||
> **影响范围**:8 个已有端口的响应格式变更 + 4 个新增任务查询接口
|
||||
|
||||
---
|
||||
|
||||
## 一、核心变更
|
||||
|
||||
所有受影响的接口从 **同步阻塞返回结果** 改为 **异步任务立即返回 `task_id`**。
|
||||
|
||||
**调用流程变更**:
|
||||
|
||||
```
|
||||
旧流程:
|
||||
POST /sync → 等待 10-30s → 返回完整结果
|
||||
|
||||
新流程:
|
||||
POST /sync → 立即返回 {"task_id": "xxx"}
|
||||
GET /tasks/xxx → 轮询(1-2s 间隔)→ status=completed 时取 result
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、受影响的端口(8 个)
|
||||
|
||||
### 1. POST /sync — 触发同步
|
||||
|
||||
**旧响应**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"status_code": 2010,
|
||||
"message": "同步完成",
|
||||
"data": {
|
||||
"result": {
|
||||
"documents_processed": 20,
|
||||
"documents_added": 3,
|
||||
"documents_modified": 2,
|
||||
"documents_deleted": 0,
|
||||
"errors": []
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**新响应**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"status_code": 2010,
|
||||
"message": "同步任务已启动",
|
||||
"data": {
|
||||
"task_id": "c3d4e5f6a1b2",
|
||||
"message": "同步任务已启动,通过 GET /tasks/c3d4e5f6a1b2 查询进度"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**冲突检测**:已有同步任务运行时返回 HTTP 409:
|
||||
```json
|
||||
{"error": "TASK_RUNNING", "message": "同步任务正在执行中 (task_id: xxx),请等待完成"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. POST /documents/sync — 触发文档同步
|
||||
|
||||
**旧响应**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"results": [{"collection": "public_kb", "status": "success"}],
|
||||
"synced_count": 1
|
||||
}
|
||||
```
|
||||
|
||||
**新响应**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"task_id": "xxx",
|
||||
"message": "同步任务已启动,通过 GET /tasks/xxx 查询进度"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. POST /collections/\<kb_name\>/reindex — 重建索引
|
||||
|
||||
**旧响应**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "重新索引完成: 处理 20 个文档",
|
||||
"documents_processed": 20,
|
||||
"documents_added": 3
|
||||
}
|
||||
```
|
||||
|
||||
**新响应**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"task_id": "xxx",
|
||||
"message": "重建索引任务已启动: kb_name,通过 GET /tasks/xxx 查询进度"
|
||||
}
|
||||
```
|
||||
|
||||
**冲突检测**:已有重建任务运行时返回 HTTP 409。
|
||||
|
||||
---
|
||||
|
||||
### 4. POST /documents/upload — 上传单个文件
|
||||
|
||||
**旧响应**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"status_code": 2002,
|
||||
"message": "文件上传成功,已保存并添加到向量库",
|
||||
"data": {
|
||||
"file": {
|
||||
"filename": "test.txt",
|
||||
"collection": "public_kb",
|
||||
"path": "public_kb/test.txt",
|
||||
"size": 1024,
|
||||
"replaced": false
|
||||
},
|
||||
"sync_status": "已保存并添加到向量库"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**新响应**(新增 `task_id` 字段):
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"status_code": 2002,
|
||||
"message": "文件上传成功,已保存,向量化任务已启动",
|
||||
"data": {
|
||||
"file": {
|
||||
"filename": "test.txt",
|
||||
"collection": "public_kb",
|
||||
"path": "public_kb/test.txt",
|
||||
"size": 1024,
|
||||
"replaced": false
|
||||
},
|
||||
"sync_status": "已保存,向量化任务已启动",
|
||||
"task_id": "a1b2c3d4e5f6"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **注意**:文件保存仍为同步操作(毫秒级),仅向量化部分异步执行。`task_id` 为 `null` 表示同步服务不可用,需手动同步。
|
||||
|
||||
---
|
||||
|
||||
### 5. POST /documents/batch-upload — 批量上传
|
||||
|
||||
**旧响应**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"status_code": 2003,
|
||||
"data": {
|
||||
"results": [...],
|
||||
"success_count": 2,
|
||||
"total": 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**新响应**(新增 `task_id` 字段):
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"status_code": 2003,
|
||||
"data": {
|
||||
"results": [...],
|
||||
"success_count": 2,
|
||||
"total": 2,
|
||||
"task_id": "b2c3d4e5f6a1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **注意**:批量上传成功后自动触发后台向量化任务。无成功上传时不返回 `task_id`。
|
||||
|
||||
---
|
||||
|
||||
### 6. POST /exam/generate — 生成题目
|
||||
|
||||
**旧响应**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"status_code": 2020,
|
||||
"message": "出题完成",
|
||||
"data": {
|
||||
"success": true,
|
||||
"total": 10,
|
||||
"questions": [...],
|
||||
"warnings": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**新响应**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"status_code": 2020,
|
||||
"message": "出题任务已启动",
|
||||
"data": {
|
||||
"task_id": "d4e5f6a1b2c3",
|
||||
"message": "出题任务已启动 (10题),通过 GET /tasks/d4e5f6a1b2c3 查询结果"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**轮询完成后的 `result` 字段**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"total": 10,
|
||||
"source_chunks_used": 15,
|
||||
"requested_types": {"single_choice": 5, "true_false": 3, "fill_blank": 2},
|
||||
"actual_types": {"single_choice": 5, "true_false": 3, "fill_blank": 2},
|
||||
"warnings": [],
|
||||
"questions": [...]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7. POST /exam/generate-smart — AI 智能出题
|
||||
|
||||
**旧响应**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"status_code": 2020,
|
||||
"message": "AI 智能出题成功",
|
||||
"data": {
|
||||
"ai_analysis": {...},
|
||||
"questions": [...],
|
||||
"total": 16
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**新响应**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"status_code": 2020,
|
||||
"message": "AI 智能出题任务已启动",
|
||||
"data": {
|
||||
"task_id": "e5f6a1b2c3d4",
|
||||
"message": "AI 智能出题任务已启动,通过 GET /tasks/e5f6a1b2c3d4 查询结果"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**轮询完成后的 `result` 字段**:与旧 `data` 格式相同(含 `ai_analysis`、`questions`、`total`)。
|
||||
|
||||
---
|
||||
|
||||
### 8. POST /exam/grade — 批阅答案
|
||||
|
||||
**旧响应**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"status_code": 2021,
|
||||
"message": "批阅完成",
|
||||
"data": {
|
||||
"success": true,
|
||||
"total_score": 12.5,
|
||||
"total_max_score": 22.0,
|
||||
"score_rate": 56.8,
|
||||
"results": [...]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**新响应**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"status_code": 2021,
|
||||
"message": "批阅任务已启动",
|
||||
"data": {
|
||||
"task_id": "f6a1b2c3d4e5",
|
||||
"message": "批阅任务已启动 (5题),通过 GET /tasks/f6a1b2c3d4e5 查询结果"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**轮询完成后的 `result` 字段**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"total_score": 12.5,
|
||||
"total_max_score": 22.0,
|
||||
"score_rate": 56.8,
|
||||
"results": [
|
||||
{
|
||||
"question_id": "uuid-001",
|
||||
"score": 0,
|
||||
"max_score": 2.0,
|
||||
"grading_status": "success",
|
||||
"details": {
|
||||
"correct": false,
|
||||
"student_answer": "A",
|
||||
"correct_answer": "B",
|
||||
"feedback": "正确答案: B"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、新增接口(4 个)
|
||||
|
||||
### GET /tasks
|
||||
|
||||
获取任务列表。
|
||||
|
||||
**查询参数**:`status`(过滤状态)、`type`(过滤类型)、`limit`(返回数量,默认 50)
|
||||
|
||||
**响应**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"status_code": 2000,
|
||||
"data": {
|
||||
"tasks": [
|
||||
{
|
||||
"task_id": "a1b2c3d4e5f6",
|
||||
"type": "sync",
|
||||
"description": "文档同步",
|
||||
"status": "running",
|
||||
"progress": 45.0,
|
||||
"current": 9,
|
||||
"total": 20,
|
||||
"stage": "处理文件",
|
||||
"message": "已处理: 产品手册.pdf",
|
||||
"created_at": "2026-06-05T10:30:00"
|
||||
}
|
||||
],
|
||||
"total": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### GET /tasks/\<task_id\>
|
||||
|
||||
获取单个任务状态(**后端组推荐使用的轮询接口**)。
|
||||
|
||||
**建议轮询间隔**:1-2 秒,当 `status` 为 `completed` 或 `failed` 时停止。
|
||||
|
||||
**响应**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"status_code": 2000,
|
||||
"data": {
|
||||
"task_id": "a1b2c3d4e5f6",
|
||||
"type": "sync",
|
||||
"description": "文档同步",
|
||||
"status": "completed",
|
||||
"progress": 100.0,
|
||||
"current": 20,
|
||||
"total": 20,
|
||||
"stage": "完成",
|
||||
"message": "同步完成",
|
||||
"created_at": "2026-06-05T10:30:00",
|
||||
"started_at": "2026-06-05T10:30:01",
|
||||
"completed_at": "2026-06-05T10:30:15",
|
||||
"duration_ms": 14000,
|
||||
"result": {
|
||||
"documents_processed": 20,
|
||||
"documents_added": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**任务状态**:
|
||||
|
||||
| 状态 | 说明 |
|
||||
|------|------|
|
||||
| `pending` | 已创建,等待执行 |
|
||||
| `running` | 正在执行 |
|
||||
| `completed` | 执行完成,`result` 字段包含完整结果 |
|
||||
| `failed` | 执行失败,`error` 字段包含错误信息 |
|
||||
|
||||
**任务类型(type)**:`sync` / `reindex` / `upload` / `batch_upload` / `exam_generate` / `exam_grade`
|
||||
|
||||
---
|
||||
|
||||
### GET /tasks/\<task_id\>/progress
|
||||
|
||||
SSE 流式任务进度推送(dev-ui 前端推荐使用,后端组通常不需要此接口)。
|
||||
|
||||
**Content-Type**:`text/event-stream`
|
||||
|
||||
---
|
||||
|
||||
### GET /tasks/stats
|
||||
|
||||
获取任务统计信息。
|
||||
|
||||
**响应**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"status_code": 2000,
|
||||
"data": {
|
||||
"total": 5,
|
||||
"by_status": {"running": 1, "completed": 3, "failed": 1},
|
||||
"by_type": {"sync": 2, "exam_generate": 2, "upload": 1}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、后端对接代码参考
|
||||
|
||||
### 通用轮询函数
|
||||
|
||||
```python
|
||||
import time
|
||||
import requests
|
||||
|
||||
def async_task_poll(task_id, base_url='http://rag-service:5001', interval=2, timeout=300):
|
||||
"""
|
||||
通用异步任务轮询函数
|
||||
|
||||
Args:
|
||||
task_id: 任务 ID(从 POST 响应中获取)
|
||||
base_url: RAG 服务地址
|
||||
interval: 轮询间隔(秒),建议 1-2 秒
|
||||
timeout: 超时时间(秒)
|
||||
|
||||
Returns:
|
||||
任务结果(result 字段内容)
|
||||
|
||||
Raises:
|
||||
TimeoutError: 超时
|
||||
Exception: 任务失败
|
||||
"""
|
||||
elapsed = 0
|
||||
while elapsed < timeout:
|
||||
time.sleep(interval)
|
||||
elapsed += interval
|
||||
|
||||
resp = requests.get(f'{base_url}/tasks/{task_id}')
|
||||
if resp.status_code == 404:
|
||||
raise Exception(f"任务不存在: {task_id}")
|
||||
|
||||
task = resp.json()['data']
|
||||
|
||||
if task['status'] == 'completed':
|
||||
return task.get('result')
|
||||
elif task['status'] == 'failed':
|
||||
raise Exception(f"任务失败: {task.get('error', '未知错误')}")
|
||||
|
||||
raise TimeoutError(f"任务超时: {task_id} (已等待 {timeout}s)")
|
||||
```
|
||||
|
||||
### 出题流程示例
|
||||
|
||||
```python
|
||||
def generate_exam(file_path, collection, question_types, difficulty=3):
|
||||
"""异步出题流程"""
|
||||
# 1. 提交出题任务
|
||||
resp = requests.post('http://rag-service:5001/exam/generate', json={
|
||||
'file_path': file_path,
|
||||
'collection': collection,
|
||||
'question_types': question_types,
|
||||
'difficulty': difficulty
|
||||
})
|
||||
task_id = resp.json()['data']['task_id']
|
||||
|
||||
# 2. 轮询等待结果
|
||||
result = async_task_poll(task_id)
|
||||
|
||||
# 3. result 包含完整的出题结果
|
||||
questions = result['questions']
|
||||
return questions
|
||||
```
|
||||
|
||||
### 批阅流程示例
|
||||
|
||||
```python
|
||||
def grade_exam(answers):
|
||||
"""异步批阅流程"""
|
||||
# 1. 提交批阅任务
|
||||
resp = requests.post('http://rag-service:5001/exam/grade', json={
|
||||
'answers': answers
|
||||
})
|
||||
task_id = resp.json()['data']['task_id']
|
||||
|
||||
# 2. 轮询等待结果
|
||||
result = async_task_poll(task_id)
|
||||
|
||||
# 3. result 包含完整的批阅结果
|
||||
return result
|
||||
```
|
||||
|
||||
### 同步流程示例
|
||||
|
||||
```python
|
||||
def trigger_sync():
|
||||
"""异步同步流程"""
|
||||
# 1. 提交同步任务
|
||||
resp = requests.post('http://rag-service:5001/sync')
|
||||
|
||||
if resp.status_code == 409:
|
||||
print("已有同步任务在运行,跳过")
|
||||
return
|
||||
|
||||
task_id = resp.json()['data']['task_id']
|
||||
|
||||
# 2. 轮询等待结果
|
||||
result = async_task_poll(task_id)
|
||||
print(f"同步完成: 处理 {result['documents_processed']} 个文档")
|
||||
return result
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、注意事项
|
||||
|
||||
1. **超时时间**:异步任务完成后保留 1 小时,超时后自动清理。请在任务完成后及时获取结果。
|
||||
|
||||
2. **并发限制**:同一类型的任务(如 `sync`)同一时间只能有一个运行中,重复提交返回 HTTP 409。
|
||||
|
||||
3. **向后兼容**:如果 RAG 服务未升级(旧版本),POST 接口仍返回旧的同步结果格式。后端可通过检查响应中是否包含 `task_id` 字段来判断版本。
|
||||
|
||||
4. **轮询频率**:建议 1-2 秒间隔,过于频繁的轮询会增加服务器负担。
|
||||
|
||||
5. **错误处理**:任务可能因 LLM 超时、文件解析失败等原因失败,`status` 变为 `failed`,`error` 字段包含错误描述。
|
||||
Reference in New Issue
Block a user