知识库参考文件解析技术方案¶
- 版本:v1.0
- 日期:2026-04-12
- 作者:Codex
- 适用项目:
Tender_Documents
1. 背景¶
当前知识库文件链路已经支持:
- 前端通过
presigned-url直传 OSS。 - 上传完成后调用
confirm-upload(file_key)。 - 服务端校验对象存在并创建
KnowledgeFile记录。 - 列表接口基于
knowledge_files表返回文件列表。
当前知识库链路还没有做的是:
- 上传确认后异步解析文件。
- 解析基础信息并回写状态。
- 生成 Markdown、切块、索引等后续处理。
现状里 KnowledgeFile 已经有 status 字段,但尚未形成真正的任务驱动状态流转;而标书解析已经具备完整的 API -> 入队 -> ARQ worker -> 状态回写 -> 结果落库 模式。
2. 目标与非目标¶
2.1 目标¶
- 知识库文件上传确认后自动触发异步解析。
- 一期先产出“基础信息”,不在本期生成 Markdown。
- 后续可平滑扩展到 Markdown、切块、索引、检索准备。
- 复用现有 ARQ/Redis/Worker 基础设施,尽量避免重复造轮子。
- 保持
confirm-upload快速返回,不把重活放在同步接口里。
2.2 非目标¶
- 本期不直接复用 Tender 的业务落库模型。
- 本期不做向量化、召回、知识问答。
- 本期不做文件内容去重与跨库复用。
- 本期不做前端实时逐页 SSE 展示。
- 更新(2026-05-21):已补充文件级任务进度 SSE 接口
GET /v1/knowledges/{kb_id}/files/{file_id}/parse-job/events,采用 Redis Pub/Sub 推送阶段事件,并用 DB 快照兜底重连。
3. 方案结论¶
结论:知识库文件解析应采用“确认上传后立即入队,异步 worker 解析”的模式,但不直接复用现有 Tender 解析服务的业务实现。
推荐原则:
- 复用现有 ARQ 队列、Redis 连接、worker 部署方式。
- 不直接复用当前
DocumentIngestionService的 Tender 业务收口逻辑。 - 知识库单独设计“文件解析任务”和“文件解析结果回写”。
confirm-upload只做“校验对象 + 落库 + 入队”,不要同步解析。
原因:
- 当前
ingestion_tasks明显偏标书场景,和Tender关联较深。 - 知识库文件未来扩展方向与标书解析不同,强行复用会让耦合越来越重。
- 即便一期只做基础信息,后面大概率也会扩到 Markdown、切块和索引,异步任务模型更稳妥。
4. 当前代码落点¶
与本方案直接相关的现有代码:
- 知识库上传与确认:
app/routes/v1/knowledges/file.pyapp/services/knowledge/file_service.py- 知识库文件模型:
app/models/knowledge.py- OSS 访问能力:
app/services/oss_service.py- 队列与 worker 基础设施:
app/services/arq_queue_service.pyapp/worker/arq_jobs.py- 标书解析现有参考实现:
app/services/ingestion/index.pyapp/services/task_service.pyapp/models/ingestion_tasks.py
5. 一期范围¶
一期只做知识库文件“异步基础信息解析”,不生成 Markdown。
建议产出以下基础信息:
file_keycontent_typeextsizepage_countparse_errorparsed_atparser_version
说明:
size、content_type可以直接来自HeadObject。page_count对 PDF 可直接解析;对 DOCX 可尽量提取,若成本过高可先留空。parser_version用于后续重跑与兼容性治理。
6. 总体架构¶
sequenceDiagram
participant U as Browser
participant API as FastAPI
participant OSS as ObjectStorage
participant DB as Postgres
participant Q as ARQQueue
participant W as KnowledgeWorker
U->>API: 1) 获取 presigned-url
API-->>U: 2) 返回 upload_url + file_key
U->>OSS: 3) PUT 文件
U->>API: 4) confirm-upload(file_key)
API->>OSS: 5) HeadObject 校验
API->>DB: 6) 创建 KnowledgeFile(status=pending)
API->>DB: 7) 创建解析任务
API->>Q: 8) enqueue knowledge_file_parse
API-->>U: 9) 返回文件记录
Q->>W: 10) 消费任务
W->>OSS: 11) 下载文件
W->>W: 12) 提取基础信息
W->>DB: 13) 更新 KnowledgeFile(status=success/failed)
W->>DB: 14) 更新任务状态
7. 数据模型设计¶
7.1 KnowledgeFile 扩展建议¶
当前 KnowledgeFile 只有:
namefile_urlsizedirectory_idstatusremarkis_deleted
建议补充字段:
file_key: strcontent_type: Optional[str]ext: Optional[str]page_count: Optional[int]parse_error: Optional[str]parsed_at: Optional[datetime]parser_version: Optional[str]task_id: Optional[UUID]
推荐原因:
file_key是对象存储里的真实主键,应显式存库,不要每次从file_url反推。status只表示文件当前解析状态,细节错误要落到parse_error。- 后续重跑、版本升级、增量修复都需要
parser_version和parsed_at。
7.2 独立任务表建议¶
推荐新增独立表,例如:knowledge_file_parse_tasks
建议字段:
idknowledge_file_idfile_keystatusstageerror_messageretry_countstarted_atfinished_atresult_metaparser_version
不建议直接复用 ingestion_tasks 的主要原因:
ingestion_tasks当前命名和语义都偏“文档摄入到 Tender”。result_data是按页进度设计,知识库一期并不需要逐页流式结果。- 模型已经带
Tender关系,后面继续复用会越来越绕。 - 知识库未来很可能有“重新解析”“重新切块”“重新建索引”等任务类型,独立任务表更清晰。
8. 状态机设计¶
8.1 KnowledgeFile.status¶
建议统一为:
pendingprocessingsuccessfailed
含义:
pending:已确认上传,待 worker 消费。processing:worker 正在解析。success:基础信息解析完成。failed:解析失败,需要人工重试或系统补偿。
8.2 任务状态¶
任务表建议保留更细粒度:
PENDINGPROCESSINGCOMPLETEDFAILEDCANCELLED
以及阶段:
QUEUEDDOWNLOADINGEXTRACTING_METACOMPLETEDFAILEDCANCELLED
9. 接口设计建议¶
9.1 confirm-upload¶
现有接口保留:
POST /v1/knowledges/{kb_id}/files/confirm-upload
请求体:
服务端流程调整为:
- 校验
file_key前缀。 HeadObject校验对象存在。- 读取真实
size/content_type。 - 创建
KnowledgeFile。 - 创建解析任务。
- 入队
knowledge_file_parse。 - 返回文件记录,状态为
pending。
9.2 文件列表¶
保留:
GET /v1/knowledges/{kb_id}/files
建议返回补充字段:
content_typeextpage_countparse_errorparsed_at
这样前端不需要额外查任务详情,也能展示解析状态。
9.3 重试接口¶
建议预留:
POST /v1/knowledges/{kb_id}/files/{file_id}/reparse
用途:
- 失败文件重新入队。
- 解析器升级后批量重跑。
- 后续切块和索引切换时做补偿。
10. Worker 设计¶
建议新增知识库专用 worker handler,例如:
knowledge_file_parse
其职责:
- 根据
task_id加载任务与KnowledgeFile - 下载 OSS 文件
- 根据扩展名选择解析器
- 仅提取基础信息
- 更新
KnowledgeFile.status - 更新任务状态与错误信息
建议不要直接调用当前 DocumentIngestionService 的原因:
- 其内部会校验任务是否仍属于
Tender - 会在结束时回写
TenderContent - 会推进按页
result_data - 业务收口与知识库文件并不一致
推荐做法:
- 复用
OSSService.download_bytes - 复用现有 ARQ worker 部署方式
- 复用通用 parser 能力时,只抽取底层文档解析器
- 知识库单独实现自己的 service 和结果回写逻辑
11. 基础信息提取策略¶
11.1 PDF¶
建议优先提取:
- 页数
- 文档标题元数据
- 文件大小
- MIME
- 扩展名
工具建议:
fitz或现有 PDF 能力
11.2 DOCX¶
建议优先提取:
- 文件大小
- MIME
- 扩展名
- 文档核心属性
- 页数如果实现成本高,可暂缓
11.3 DOC¶
建议一期保守处理:
- 只提取对象元数据
- 页数可留空
- 后续统一通过转格式或专门解析器增强
12. 幂等与一致性¶
需要重点处理以下场景:
- 前端重复调用
confirm-upload - 同一文件重复点击上传确认
- worker 重试导致重复回写
- 文件被删除时任务仍在运行
建议策略:
KnowledgeFile.file_key加唯一约束,或在同目录下做唯一校验。- 若已存在相同
file_key的未删除记录,confirm-upload直接返回已有记录。 - 若同一
KnowledgeFile已有pending/processing任务,则禁止重复入队。 - worker 写库前先检查文件是否已删除,已删除则取消任务。
13. 删除与补偿策略¶
当前知识库/图库删除是软删除,因此解析链路也应遵守软删除语义。
建议:
- 文件软删除时,不立即删 OSS 对象。
- 若文件被软删除且任务未完成,worker 应停止后续写入。
- 真正的 OSS 物理删除交给后续“清理任务”或“彻底删除”接口。
这样可以避免:
- 刚删除就无法恢复。
- 正在解析时源文件被提前删掉。
- 文件记录还在但 OSS 已丢失的脏状态。
14. 后续扩展路径¶
Phase 1:基础信息解析¶
confirm-upload后创建解析任务- worker 提取基础信息
- 文件列表展示解析状态
Phase 2:Markdown 生成¶
- 在任务完成基础信息后增加
EXTRACTING_MARKDOWN阶段 - 落库
source_markdown - 支持失败重跑
Phase 3:切块与索引¶
- 基于 Markdown 或原文生成 chunk
- 记录 chunk 版本
- 建向量索引或全文索引
Phase 4:知识库引用与检索增强¶
- 支持按文件、按段落引用
- 支持检索召回
- 支持重新建索引与增量更新
15. 分阶段落地建议¶
第一步¶
完成数据模型补充:
KnowledgeFile增加file_key等解析相关字段- 新增
knowledge_file_parse_tasks
第二步¶
完成上传确认与入队:
confirm-upload创建文件记录- 同事务或补偿方式创建任务记录
- 提交 ARQ 队列
第三步¶
完成知识库专用 worker:
- 新增
knowledge_file_parsehandler - 失败时回写
parse_error - 成功时回写基础信息
第四步¶
补查询与重试能力:
- 列表返回完整解析状态
- 提供 reparse 接口
- 视需要增加任务查询接口
16. 风险与注意事项¶
DOC解析能力通常弱于PDF/DOCX,一期不要承诺太高。- 若未来 Markdown 生成很重,不应继续复用“基础信息任务”,而应拆分阶段。
- 已提供文件级任务 SSE:
started、parse_started、parse_done、chunk_done、embedding_started、embedding_done、index_started、index_done、completed、error。当前粒度到解析/切块/向量化/索引阶段,逐页解析事件仍未实现。 - 如果未来有多种解析器版本,必须从一开始记录
parser_version。
17. 最终建议¶
建议采用以下路线:
- 知识库文件确认上传后立即入队异步解析。
- 一期只做基础信息,不做 Markdown。
- 新增知识库专用任务表,不直接复用当前
ingestion_tasks。 - 复用现有 ARQ/Redis/OSS 基础设施,但知识库独立实现自己的解析 service。
这样做的收益是:
- 同步接口足够轻。
- 状态机清晰。
- 后续扩展到 Markdown、切块、索引时不需要推翻重做。
- 不会把 Tender 业务耦合带进知识库模块。