从并发模型、LLM 调用、OCR、断点续跑和关系推断等方面,系统优化知识库后台分析性能。
随着知识库资料规模增长,后台分析耗时会逐渐成为影响用户体验的关键因素。尤其是长文档、扫描版 PDF,以及多人同时提交任务时,串行执行模式会放大模型调用、OCR 和失败重试带来的延迟。
本文结合 AiCat 当前知识图谱分析流程,从任务调度、文本分块、大模型调用、OCR、关系推断和结果持久化等方面,给出一套分阶段、可验证的性能优化方案。
一、当前分析流程
知识库后台分析大致分为以下几个阶段:
- 用户上传 PDF、Word、Markdown、文本文件或 Git 仓库。
- 后台 Worker 从数据库领取一个排队中的项目。
- 提取文件正文;扫描 PDF 的弱文本页进入 OCR。
- 将正文切分成约 4500 字的文本块。
- 逐块调用大模型,抽取知识节点和局部关系。
- 合并并去重节点,补充跨分块关系。
- 清理无效关系和前置关系环路,生成图谱布局。
- 将节点、关系和质量报告写入数据库。
- 更新项目状态,并发送飞书完成通知。
现有流程能够保证证据约束和结果完整性,但在任务调度与模型调用层面仍以串行为主,因此存在较大的优化空间。
二、主要性能瓶颈
1. 项目之间串行执行
当前后台只有一个知识库 Worker。同一时间只能分析一个项目,后续任务必须等待前一个项目完全结束。
当多人同时提交资料时,即使单个任务耗时没有变化,用户感知到的排队时间也会快速增长。
2. 单个项目的分块串行调用模型
正文被切分为多个文本块后,系统会等待当前分块分析结束,再处理下一个分块。因此,一份文档的模型阶段耗时近似为:
总耗时 ≈ 分块 1 耗时 + 分块 2 耗时 + ... + 分块 N 耗时
例如,一份文档被切成 20 块,单块平均需要 15 秒,仅分块抽取阶段理论上就需要约 300 秒。
3. 单次模型输出较重
当前每个分块要求模型生成较多知识节点、描述、证据和关系,同时允许较大的输出 token 上限。输出内容越长,模型生成时间和发生格式错误的概率越高。
4. 失败重试会成倍放大耗时
模型请求失败后最多重试三次;如果仍然失败,还会把原分块拆成更小的子块并重新分析。
该机制有助于提高正文覆盖率,但网络超时、限流或 JSON 格式错误都可能让一个分块的耗时增加数倍。
5. 全量关系推断上下文过大
分块抽取完成后,系统会将全部节点交给模型进行关系补充。大文档产生的节点越多,这一步的输入上下文和输出内容就越大,可能成为后期的新瓶颈。
6. 扫描 PDF 的 OCR 逐页执行
系统已经采用“原生文本优先、弱文本页才 OCR”的策略,但需要 OCR 的页面仍然逐页处理。扫描版 PDF 页数较多时,模型分析开始前就可能消耗较长时间。
7. HTTP 连接没有充分复用
如果每次模型调用都重新建立 HTTP 连接,会额外产生 TCP、TLS 和连接初始化成本。分块较多时,这些小开销会持续累积。
三、优化目标
本次优化建议围绕以下目标展开:
- 普通文本 PDF 的总分析耗时降低 50%~70%。
- 支持至少两个知识库项目同时分析。
- 服务重启后能够从已完成的分块继续执行。
- 减少无效重试和重复模型调用。
- 保持现有节点质量、证据覆盖率和关系完整度。
- 让用户能够看到真实进度、队列位置和预计剩余时间。
四、第一阶段:低风险快速优化
第一阶段优先处理改动小、收益明显的部分,预计开发时间为 1~2 天。
1. 增加分阶段性能指标
首先应记录各阶段的真实耗时,包括:
- 文件解析耗时;
- OCR 总耗时及单页耗时;
- 文本分块数量;
- 每个分块的模型调用耗时;
- 模型输入、输出 token 数量;
- 重试次数及错误类型;
- 关系推断耗时;
- 数据库保存耗时;
- 项目排队时间与总处理时间。
只有建立稳定的性能基线,才能判断优化是否真正有效,避免只凭主观感受调整参数。
2. 对文本分块进行有界并发
各文本块之间基本相互独立,可以并发调用模型。建议增加配置:
KNOWLEDGE_CHUNK_CONCURRENCY=4
推荐初始值:
| 模型类型 | 建议并发数 | 说明 |
|---|---|---|
| DeepSeek / Agnes 等云端 API | 3~4 | 可根据服务限流逐步提高到 6~8 |
| Ollama 单 GPU | 1~2 | 并发过高可能争抢显存并降低吞吐量 |
| Ollama 多 GPU 或高性能推理服务 | 2~4 | 需要通过压测确定最优值 |
并发完成后,系统仍应按照原始分块序号合并结果,以保证结果可复现,并继续执行现有的证据校验、节点去重和覆盖率门禁。
3. 复用 HTTP 连接
使用共享的 httpx.Client 或 httpx.AsyncClient 调用云端模型,让请求复用连接池,减少重复建立连接的开销。
连接池还应配置:
- 最大连接数;
- 最大空闲连接数;
- 连接超时;
- 读取超时;
- 客户端关闭与服务退出处理。
4. 缩减不必要的模型输出
分块抽取阶段可以将输出上限从 8192 tokens 调整到约 3000~4096 tokens,再根据真实截断率校准。
同时可以精简提示词中的重复描述,并控制单块最多节点数。目标不是简单减少知识点,而是避免模型输出冗长、重复的说明和低价值关系。
5. 优化重试策略
重试应根据错误类型决定,而不是所有异常都完整重试:
- 超时、HTTP 429 和服务端 5xx:指数退避后重试;
- 网络瞬断:有限重试;
- JSON 格式错误:优先进行一次轻量修复或格式纠正;
- 参数错误、鉴权失败和额度不足:立即失败并明确提示;
- 内容超过上下文限制:直接缩小分块后重试。
建议采用带随机抖动的指数退避:
1 秒 → 2 秒 → 4 秒
这样可以减少限流期间多个请求同时重试造成的“惊群”问题。
五、第二阶段:提升多用户吞吐量
第二阶段预计开发时间为 2~4 天,重点解决多个项目排队的问题。
1. 将单 Worker 改为 Worker 池
建议增加以下配置:
KNOWLEDGE_WORKER_CONCURRENCY=2
KNOWLEDGE_CHUNK_CONCURRENCY=4
当前数据库使用 FOR UPDATE SKIP LOCKED 领取任务,已经具备支持多 Worker 安全抢占任务的基础。多个 Worker 可以各自领取不同项目,而不会重复处理同一条任务。
2. 建立全局并发限制
项目并发和分块并发不能简单相乘后无限增长。例如两个项目分别开启四个分块并发,系统可能同时发出八个模型请求。
因此应设置全局信号量或令牌桶:
KNOWLEDGE_LLM_MAX_CONCURRENCY=6
所有项目共享该上限,防止超过云端 API 限流,也避免本地 GPU 显存被同时占满。
3. 按资源类型拆分队列
建议把任务拆成两类资源队列:
- CPU/OCR 队列:负责文件解析、页面渲染和 OCR,并发较低;
- LLM 队列:负责知识抽取和关系生成,根据 API 或 GPU 容量设置并发。
这样可以避免扫描 PDF 长时间占用唯一 Worker,也能让已经完成解析的文本项目更快进入模型阶段。
六、第三阶段:分块持久化与断点续跑
第三阶段预计开发时间为 3~5 天,是长期收益最大的一项改造。
1. 新增分块任务表
可以新增 knowledge_project_chunks 表,保存以下信息:
- 项目 ID;
- 分块序号;
- 起止位置;
- 文本内容或文本哈希;
- 状态;
- 尝试次数;
- 使用的模型;
- 提示词版本;
- 节点和关系提取结果;
- 开始时间、结束时间;
- 错误类型和错误信息。
2. 实现断点续跑
Worker 只领取未完成或允许重试的分块。服务重启后,已经成功的分块不再调用模型,只继续执行剩余任务。
3. 增加内容缓存
缓存键可以由以下内容共同组成:
文本哈希 + 模型名称 + 提示词版本 + 抽取参数版本
这样可以支持:
- 相同资料重复提交时复用结果;
- 文档只修改部分内容时,仅重新分析变化的分块;
- 修改提示词或模型后,自动使旧缓存失效;
- 对失败分块进行单独重试。
4. 保证任务幂等性
分块任务应使用明确的状态流转:
pending → processing → succeeded
↘ retry
↘ failed
领取任务时记录租约时间。如果 Worker 异常退出,超过租约的 processing 任务可以安全地回到 retry 状态。
七、第四阶段:分层关系推断
当节点数量增加时,不应把全部节点一次性交给模型。建议将关系推断改成分层流程:
- 每个文本块提取局部节点和关系;
- 按章节合并并去重节点;
- 分章节补充内部关系;
- 为每个章节生成简短摘要和关键节点列表;
- 只使用章节摘要与关键节点推断跨章节关系;
- 最后在本地完成去重、置信度过滤、环路清理和布局。
这种 Map-Reduce 式处理可以显著降低单次请求的上下文长度,并避免节点较多时关系推断超时。
还可以根据以下规则减少不必要的候选节点组合:
- 优先处理相同章节和相邻章节;
- 使用关键词、向量相似度或实体类型筛选候选节点;
- 已经存在高置信度直接关系的节点不重复推断;
- 跨章节只保留教学意义较强的前置、包含和对比关系。
八、OCR 优化方案
OCR 应作为独立阶段统计和优化,避免将 OCR 慢误判为大模型慢。
1. 保留混合提取策略
继续使用“原生文本优先,仅弱文本页 OCR”的方案。可靠文本页不应进入 OCR,也不应初始化不必要的 OCR 推理过程。
2. 增加页面缓存
OCR 缓存可以采用以下键:
文件哈希 + 页码 + OCR 模型版本 + DPI
重复分析同一份 PDF 时,可以直接复用页面识别结果。
3. 谨慎增加并发
PDF 页面渲染可以使用 2~4 个工作线程,但 OCR 推理需要结合引擎线程安全和硬件资源进行控制。
如果 OCR 实例不能安全地跨线程共享,应使用独立进程或每个 Worker 独立实例,不能直接在多个线程中共用同一个实例。
4. 提供快速模式
对速度优先的场景,可以将 OCR DPI 从 160 调整为 130~150,并用固定样本验证识别准确率是否仍然满足要求。
九、前端体验优化
即使后台性能得到提升,用户仍需要明确知道任务进行到哪一步。建议展示:
- 当前队列位置;
- 已完成分块数和总分块数;
- 当前阶段:解析、OCR、知识抽取、关系整理或保存;
- 已耗时和预计剩余时间;
- 是否发生重试;
- 任务失败时的可操作建议。
预计剩余时间可以基于同类型任务的历史 P50 或当前任务已完成分块的平均耗时动态计算。
十、推荐实施顺序
| 优先级 | 改造项 | 预期效果 | 风险 | 预计工作量 |
|---|---|---|---|---|
| P0 | 增加各阶段耗时与 token 指标 | 找到真实瓶颈,建立基线 | 很低 | 0.5~1 天 |
| P0 | 复用 HTTP 连接 | 降低云端调用开销 | 很低 | 0.5 天 |
| P0 | 分块有界并发 | 单项目提速约 2~4 倍 | 中 | 1 天 |
| P0 | 优化输出上限和重试策略 | 降低单块耗时与异常放大 | 低 | 1 天 |
| P1 | Worker 池并行项目 | 提升多人使用时的吞吐量 | 中 | 1~2 天 |
| P1 | 全局并发和限流控制 | 保证服务稳定性 | 中 | 1 天 |
| P1 | 分块持久化与断点续跑 | 避免重复分析 | 中 | 3~5 天 |
| P2 | 分层关系推断 | 优化超大文档 | 中 | 2~3 天 |
| P2 | OCR 缓存和安全并发 | 加速扫描 PDF | 中 | 2~3 天 |
十一、验收与压测方案
建议准备三类固定样本:
- 20 页、带可靠文本层的 PDF;
- 100 页、带可靠文本层的 PDF;
- 50 页扫描版 PDF。
改造前后分别执行多轮测试,并记录:
- 排队时间;
- 文件解析时间;
- OCR 时间;
- 分块数量;
- 分块调用平均耗时与 P95 耗时;
- 输入和输出 token 数量;
- 重试次数;
- 关系推断时间;
- 数据库保存时间;
- 项目总耗时;
- 节点数与关系数;
- 来源覆盖率;
- API 失败率和限流率;
- CPU、内存、GPU 和显存峰值。
建议首期验收标准如下:
- 普通文本 PDF 总耗时降低 50%~70%;
- 至少两个项目可以同时分析;
- 服务重启后不重复处理已成功分块;
- 不出现超出供应商限流的大量失败;
- 节点数量、关系数量和来源覆盖率不低于现有版本;
- 在相同测试资料下,分析结果质量保持稳定。
十二、风险与控制措施
API 限流风险
分块并发可能触发云端模型限流。必须设置全局并发上限,并对 HTTP 429 使用带随机抖动的指数退避。
本地 GPU 资源竞争
Ollama 并发过高可能导致显存不足或单请求延迟上升。应通过压测确定最优并发,而不是直接套用云端 API 的配置。
结果合并不稳定
并发任务完成顺序不固定。合并时必须使用原始分块序号,并保证去重和关系清理逻辑具有确定性。
数据一致性风险
多 Worker 模式下,应继续使用数据库事务和 FOR UPDATE SKIP LOCKED 领取任务。分块结果写入需要唯一约束和幂等更新,避免重复数据。
优化导致质量下降
减少输出 token、扩大文本分块或减少重试都可能影响结果质量。因此每项性能调整都需要同时比较节点数、关系数、证据有效率和正文覆盖率。
十三、总结
AiCat 知识库分析慢的核心原因并不是单一模型性能问题,而是项目调度、分块调用、失败重试、OCR 和关系推断整体以串行方式组合在一起。
最适合优先落地的一组改造是:
- 增加分阶段耗时和 token 指标;
- 使用共享 HTTP Client;
- 实现文本分块的有界并发;
- 优化输出上限和错误分类重试;
- 增加全局并发保护。
这一阶段改动范围相对可控,通常可以获得第一轮最大的性能收益。随后再推进 Worker 池、分块持久化、断点续跑和分层关系推断,逐步把当前单机串行流程演进为可并发、可恢复、可观测的后台分析系统。