Files
rag/docs/异步任务接口变更说明.md
lacerate551 183a57e7f1 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 迁移计划
2026-06-05 22:56:00 +08:00

555 lines
12 KiB
Markdown
Raw 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-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` 字段包含错误描述。