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

12 KiB
Raw Blame History

异步任务接口变更说明

变更日期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 — 触发同步

旧响应

{
  "success": true,
  "status_code": 2010,
  "message": "同步完成",
  "data": {
    "result": {
      "documents_processed": 20,
      "documents_added": 3,
      "documents_modified": 2,
      "documents_deleted": 0,
      "errors": []
    }
  }
}

新响应

{
  "success": true,
  "status_code": 2010,
  "message": "同步任务已启动",
  "data": {
    "task_id": "c3d4e5f6a1b2",
    "message": "同步任务已启动,通过 GET /tasks/c3d4e5f6a1b2 查询进度"
  }
}

冲突检测:已有同步任务运行时返回 HTTP 409

{"error": "TASK_RUNNING", "message": "同步任务正在执行中 (task_id: xxx),请等待完成"}

2. POST /documents/sync — 触发文档同步

旧响应

{
  "success": true,
  "results": [{"collection": "public_kb", "status": "success"}],
  "synced_count": 1
}

新响应

{
  "success": true,
  "task_id": "xxx",
  "message": "同步任务已启动,通过 GET /tasks/xxx 查询进度"
}

3. POST /collections/<kb_name>/reindex — 重建索引

旧响应

{
  "success": true,
  "message": "重新索引完成: 处理 20 个文档",
  "documents_processed": 20,
  "documents_added": 3
}

新响应

{
  "success": true,
  "task_id": "xxx",
  "message": "重建索引任务已启动: kb_name通过 GET /tasks/xxx 查询进度"
}

冲突检测:已有重建任务运行时返回 HTTP 409。


4. POST /documents/upload — 上传单个文件

旧响应

{
  "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 字段):

{
  "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_idnull 表示同步服务不可用,需手动同步。


5. POST /documents/batch-upload — 批量上传

旧响应

{
  "success": true,
  "status_code": 2003,
  "data": {
    "results": [...],
    "success_count": 2,
    "total": 2
  }
}

新响应(新增 task_id 字段):

{
  "success": true,
  "status_code": 2003,
  "data": {
    "results": [...],
    "success_count": 2,
    "total": 2,
    "task_id": "b2c3d4e5f6a1"
  }
}

注意:批量上传成功后自动触发后台向量化任务。无成功上传时不返回 task_id


6. POST /exam/generate — 生成题目

旧响应

{
  "success": true,
  "status_code": 2020,
  "message": "出题完成",
  "data": {
    "success": true,
    "total": 10,
    "questions": [...],
    "warnings": []
  }
}

新响应

{
  "success": true,
  "status_code": 2020,
  "message": "出题任务已启动",
  "data": {
    "task_id": "d4e5f6a1b2c3",
    "message": "出题任务已启动 (10题),通过 GET /tasks/d4e5f6a1b2c3 查询结果"
  }
}

轮询完成后的 result 字段

{
  "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 智能出题

旧响应

{
  "success": true,
  "status_code": 2020,
  "message": "AI 智能出题成功",
  "data": {
    "ai_analysis": {...},
    "questions": [...],
    "total": 16
  }
}

新响应

{
  "success": true,
  "status_code": 2020,
  "message": "AI 智能出题任务已启动",
  "data": {
    "task_id": "e5f6a1b2c3d4",
    "message": "AI 智能出题任务已启动,通过 GET /tasks/e5f6a1b2c3d4 查询结果"
  }
}

轮询完成后的 result 字段:与旧 data 格式相同(含 ai_analysisquestionstotal)。


8. POST /exam/grade — 批阅答案

旧响应

{
  "success": true,
  "status_code": 2021,
  "message": "批阅完成",
  "data": {
    "success": true,
    "total_score": 12.5,
    "total_max_score": 22.0,
    "score_rate": 56.8,
    "results": [...]
  }
}

新响应

{
  "success": true,
  "status_code": 2021,
  "message": "批阅任务已启动",
  "data": {
    "task_id": "f6a1b2c3d4e5",
    "message": "批阅任务已启动 (5题),通过 GET /tasks/f6a1b2c3d4e5 查询结果"
  }
}

轮询完成后的 result 字段

{
  "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

响应

{
  "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 秒,当 statuscompletedfailed 时停止。

响应

{
  "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 字段包含错误信息

任务类型typesync / reindex / upload / batch_upload / exam_generate / exam_grade


GET /tasks/<task_id>/progress

SSE 流式任务进度推送dev-ui 前端推荐使用,后端组通常不需要此接口)。

Content-Typetext/event-stream


GET /tasks/stats

获取任务统计信息。

响应

{
  "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}
  }
}

四、后端对接代码参考

通用轮询函数

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)")

出题流程示例

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

批阅流程示例

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

同步流程示例

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 变为 failederror 字段包含错误描述。