- FastAPI + Qdrant + Redis + Ollama 技术栈 - L1→L2→L3→chunk 四层分层检索(dense + sparse RRF 融合) - 文档三级总结与 2.5 级回退 - query 解析路由与分类 - /admin 管理页面
5.1 KiB
入库异步任务化 Spec
Why
当前 POST /api/v1/documents 同步等待整条入库流水线(总结→分类→切分→向量化→写库),真实 Ollama 冒烟单篇耗时 39s+,长文档分钟级,客户端/网关易超时,批量导入不可用。改为后台任务模式:提交即返回 task_id,状态可查,失败任务长期保留便于归因。
What Changes
- BREAKING:
POST /api/v1/documents由同步返回 IngestionResult(200)改为立即返回{task_id, status}(HTTP 202),流水线在后台 asyncio 任务中执行 - 新增
GET /api/v1/documents/tasks/{task_id}:查询任务状态(pending/各阶段/done/failed)、完成后的 IngestionResult、失败时的 stage 与错误信息 - 新增
app/core/ingest_tasks.pyIngestTaskManager:提交登记 → 信号量限流并发 → 后台执行 → 状态机推进 → 结果/错误落储 - 状态存储:Redis 为主(key
ingest_task:{task_id},JSON);done TTL 24h,failed TTL 7 天(便于失败归因);Redis 不可用时降级进程内字典(重启丢失,日志告警),服务不因此拒绝入库 - 管理页:入库区块改为「提交 → 轮询任务状态 → 展示进度/结果/失败详情」
- 配置新增:
ingest_max_concurrency(默认 2)、ingest_task_ttl_done(默认 86400s)、ingest_task_ttl_failed(默认 604800s,7 天) - CLAUDE.md API 清单同步更新(标注 POST /documents 行为变更)
Impact
- Affected specs: 文档入库 API、管理页面、任务状态存储
- Affected code:
- 修改:document.py(POST 改 202 + 新增任务查询端点)、config.py、.env.example、admin.html(入库区块轮询)、CLAUDE.md
- 新增:
app/core/ingest_tasks.py、tests/test_ingest_tasks.py、tests/test_ingest_task_api.py
ADDED Requirements
Requirement: 异步入库提交
系统 SHALL 将 POST /api/v1/documents 改为异步任务模式:校验通过后立即返回 HTTP 202 与 {task_id, status: "pending"},入库流水线在后台任务执行;并发后台任务数受 ingest_max_concurrency 信号量限制,超限任务保持 pending 排队。请求体与校验规则(text 非空等)与现状一致,校验失败仍同步返回 1001。
Scenario: 提交即返回
- WHEN 提交合法文档
- THEN 响应 202 + task_id,无需等待流水线完成;后台任务按并发额度开始执行
Requirement: 任务状态查询
系统 SHALL 提供 GET /api/v1/documents/tasks/{task_id}:返回 {task_id, status, created_at, updated_at};status 取值 pending | summarizing | classifying | embedding | writing | done | failed(与流水线阶段一致推进);done 时附 result(完整 IngestionResult:document_id/category/tags/总结层级/chunks_count);failed 时附 error: {stage, message}(stage 来自 IngestionError,已产出总结不丢失一并附在 error.partial_summary)。task_id 不存在返回 code=1004。
Scenario: 生命周期
- WHEN 提交后轮询 task_id
- THEN 依次观察到 pending→阶段状态→done(result 可查,文档已可检索);失败任务观察到 failed + error.stage
Requirement: 状态存储与保留
任务状态 SHALL 写入 Redis(ingest_task:{task_id},JSON):done TTL=ingest_task_ttl_done(默认 24h),failed TTL=ingest_task_ttl_failed(默认 7 天),进行中状态 TTL 取 done 值。Redis 不可用时降级进程内字典并日志告警,入库与查询照常(重启后历史任务丢失可接受,接口语义不变)。
Scenario: Redis 宕机降级
- WHEN Redis 连接失败
- THEN 提交与查询仍正常,仅日志 warning;进程重启后任务状态丢失
Requirement: 管理页入库区块
管理页入库表单 SHALL 改为:提交后展示 task_id 与状态进度条/文本,每 2s 轮询任务接口,done 展示分类/标签/总结层级/chunks_count,failed 展示 error.stage 与 message;轮询期间禁止重复提交,超时(5 分钟)停止轮询并提示可稍后手动查询。
Requirement: 并发控制
后台入库 SHALL 用 asyncio 信号量限制并发(默认 2),避免多任务同时打满 Ollama/embedding;排队任务状态保持 pending。
MODIFIED Requirements
Requirement: POST /api/v1/documents(行为变更)
原同步返回 IngestionResult 的行为废弃,改为 202 + task_id。所有既有调用方(管理页、既有测试)同步适配;响应体统一响应包装不变({code:0, data:{task_id,status}, message:"ok"},HTTP 状态码 202)。
REMOVED Requirements
Requirement: 同步入库等待
Reason:长耗时流水线导致客户端超时、无法批量导入。
Migration:调用方改为提交后轮询 GET /api/v1/documents/tasks/{task_id};管理页已内置轮询。