banner
约 5,400 字
18 分钟

企业知识库 RAG 系统实践

摘要

本文整理一次企业知识库 RAG 系统实践:先解释 RAG-Challenge-2 冠军方案的整体原理,再拆解文档解析、切块、路由、父页召回、LLM 重排、Prompt 约束和评估方法,最后对比原始方案、企业落地改造版本和个人论文知识库版本。

企业知识库 RAG 系统实践

写在前面

企业知识库问答不是简单的把 PDF 放进向量库。真正的问题在于,文档长、结构复杂、表格多、问题类型多,并且用户希望答案可追溯、可解释、可复查。只做 native RAG,系统通常能回答一些简单问题,但遇到长文档、多文档对比、表格事实和专业概念解释时,很容易检索错、上下文缺失或生成不稳定。

本文按三个层次展开。

第一层是原始方案。以 RAG-Challenge-2 企业知识库冠军方案为例,解释为什么要在 native RAG 基础上加入两个 router、父页召回和 LLM reranking。

第二层是企业落地改造。比赛方案追求评测集高分,实际真实知识库更关注可部署、可调试、可迁移、低成本和稳定运行,因此需要替换解析器、模型 API、索引结构、Prompt 体系和评估方式。

第三层是我自己的论文知识库版本。这个版本把企业知识库框架迁移到博士论文方向,构建 wireless_sensingpassive_radar 两个主题知识库,用本地 Gradio 界面观察路由、检索、重排、引用和耗时。

全文主线如下。

层次

核心问题

解决思路

原始 RAG 方案

长 PDF 和多问题类型导致 native RAG 不稳定

多向量库、数据库路由、Prompt 路由、父页召回、LLM 重排

企业落地改造

比赛方案不能直接用于真实企业环境

替换国内 API、优化解析流程、保留表格信息、增加调试与评估

个人论文知识库

面向研究方向的论文问答和文献复用

主题知识库、MinerU 解析、Qwen embedding、Qwen rerank、可观察界面

一、原始 RAG 原理图

RAG-Challenge-2 的冠军方案可以看成 native RAG 的工程增强版。它不是只做一次向量检索,而是把整个流程拆成文档入库和问题回答两条链路。

RAG-Challenge-2 冠军方案核心流程
RAG-Challenge-2 冠军方案核心流程

这张图需要按上下两部分理解。

上半部分是离线入库。PDF 先经过解析,得到文本、表格和页面结构;然后进行清洗和切块;最后写入多个 vector databases。这里的重点不是有一个向量库,而是有多个向量库。在企业年报问答中,一个公司或一个文档集合可以对应一个独立知识库。这样做的好处是,问题来了以后不必在所有文档里盲目检索。

下半部分是 answering,也就是在线回答。用户提出问题后,系统不是直接检索,而是先做两类路由:第一类路由到相关数据库,第二类路由到相关 Prompt。数据库路由决定去哪里找证据,Prompt 路由决定用什么格式回答。随后系统执行向量检索、父页召回、LLM reranking,把最相关的上下文和对应 Prompt 一起送入最终请求,生成答案。

这套流程比 native RAG 多了三个关键结构。

结构

输入

输出

解决的问题

数据库路由

用户问题

相关向量库

缩小检索空间,避免在全部文档中混检

Prompt 路由

用户问题

回答模板

约束不同问题类型的输出格式

LLM reranking

初始召回候选

重排后的相关上下文

用语义判断过滤表面相似但无用的内容

如果把 RAG 看成一个信息筛选系统,这三个结构分别对应三个问题:从哪里找、按什么形式答、哪些证据最可信。

二、为什么 native RAG 不够

native RAG 的逻辑很清楚:解析文档,切块,向量化,用户提问后做 Top K 相似度检索,再把检索结果交给大模型回答。这个流程适合文档数量少、结构简单、问题范围明确的场景。

企业知识库一般不满足这些条件。

第一,文档很长。年报、制度、合同、技术手册都可能有几十到上千页。切块后,一个问题可能只命中局部文本,但真正的解释在同一页前后段落或表格脚注中。

第二,表格很重要。企业知识库中大量事实存在于表格里,例如营收、成本、指标、部门职责、合同条款。PDF 表格如果解析不好,后面的检索和生成都会失真。

第三,问题类型不同。用户可能问数值、名称、列表、判断、对比、解释。不同问题需要不同输出约束。数值问题要求简洁和单位,对比问题要求分项,解释问题要求上下文完整。

第四,检索空间太大。如果所有文档进入同一个向量库,语义相近但实体错误的内容很容易被召回。例如用户问 A 公司收入,检索结果可能混入 B 公司收入;用户问某个制度条款,系统可能召回同类制度但不是目标文档。

因此,企业 RAG 的重点不是“大模型更聪明”,而是把文档、检索、Prompt、评估这些工程边界先设计清楚。

三、关键技术细节

1. PDF 解析:决定知识库的上限

PDF 解析是 RAG 的入口。解析质量差,后面模型再强也只能在错误文本上工作。

企业文档和论文 PDF 都有复杂版面。常见问题包括多栏文本顺序错误、表格单元格丢失、跨页表格断裂、页眉页脚污染正文、公式和图注混入段落。原始方案中曾对多种解析器进行比较,最终强调高质量 PDF parser 的重要性。

在实际改造中,我把解析流程迁移到 MinerU。原因是它对论文版面、表格、公式和多栏内容更稳定,也更适合后续把页面结构转为可检索文本。原始的docling更适合英文版的PDF,国内PDF中文居多,所以用功能类似的第3方PDF解析工具MinerU代替,配置好相关API即可。解析后的结果先进入 debug_data/01_parsed_reports,然后再合并为页面级文本。这个中间结果必须保留,方便定位解析错误。

解析阶段的目标不是得到看起来像文本的内容,而是得到后续可以被检索、引用和回溯的结构化页面。

2. 文本切块:不能只追求固定长度

切块的目标是在上下文窗口、召回精度和语义完整性之间做平衡。

块太小,召回会更精确,但容易丢失定义、上下文、表格说明和实验条件。块太大,召回噪声增多,模型输入成本变高。原始方案采用 chunk 召回后再回到 parent page 的方式,比较适合长文档问答。

在我的版本中,切块时保留以下元数据。

字段

作用

document_id

标识来源论文或文档

source_title

用于答案引用和结果展示

authors

辅助区分同主题论文

abstract

帮助模型理解论文背景

page

支持页级引用

text

当前 chunk 文本

parent_text

当前 chunk 所在页面全文

这里最重要的是 parent_text。它让系统可以先用小块做精确召回,再用整页做上下文补全。

3. 多知识库路由:先确定检索边界

数据库路由的作用是先确定问题应该进入哪个知识库。原始企业年报场景中,路由对象可以是公司、报告或文档集合。我的论文知识库版本中,路由对象变成研究主题。

当前有两个主题知识库。

主题库

覆盖范围

wireless_sensing

WiFi sensing、CSI、Fresnel、device-free sensing、呼吸检测、人体活动识别

passive_radar

被动雷达、Passive WiFi Radar、illuminators of opportunity、USRP、reference channel、surveillance channel

当前路由采用关键词匹配,而不是直接使用 LLM router。原因是主题数量少,边界清晰,关键词路由更快、更稳定,也更容易调试。当主题数量增加或出现交叉主题时,可以再升级为 embedding router 或 LLM router。

JSON
{
  "name": "wireless_sensing",
  "root": "wireless_sensing",
  "keywords": [
    "wireless sensing",
    "WiFi感知",
    "CSI",
    "Fresnel",
    "respiration",
    "breathing",
    "呼吸检测",
    "生命体征"
  ]
}

这个模块曾暴露一个实际问题:旧评估集中 WiFi 感知如何用于人体呼吸检测? 没有命中任何主题关键词,系统直接路由失败。这个问题不是生成模型的问题,也不是向量库的问题,而是入口路由没有覆盖“呼吸检测”这类中文表达。补充关键词后,该问题可以正常进入 wireless_sensing 知识库。

4. 检索:向量检索、BM25 和混合检索

向量检索适合语义相近的问题,例如“CSI 通常表征什么信息”和“channel state information captures what”。BM25 更适合关键词明确的问题,例如 USRPCAFreference channel 这类专有词。混合检索则把两者结合,适合既有语义解释又有明确术语的问题。

当前代码支持三种模式。

模式

适用场景

风险

vector

概念解释、跨语言问答、同义表达

可能漏掉精确术语

bm25

专有名词、缩写、设备型号、固定术语

对改写问题不敏感

hybrid

同时包含语义和关键词的问题

参数需要调,结果解释更复杂

当前默认以 vector 为主,同时保留 BM25 和 hybrid 作为调试选项。这样设计的好处是,先用简单稳定的路径跑通,再在特定问题上比较检索策略。

5. 父页召回:解决 chunk 上下文不足

父页召回是原始方案中很关键的设计。

整合后的检索器流程
整合后的检索器流程

它的流程是:用户问题先向量化,在向量库中找到 Top K chunk;然后根据 chunk 元数据找到对应页面;再对页面去重;最后把这些页面交给 LLM reranker 排序。

这样做的原因很直接:chunk 适合检索,page 适合阅读。检索需要粒度细,回答需要上下文完整。特别是论文问答中,一个概念可能在前半页定义,后半页解释实验结果,只给模型一个局部 chunk 容易造成回答片面。

6. LLM reranking:用语义判断重排证据

向量相似度只衡量文本和问题在 embedding 空间中的距离,并不等价于“这段文本能回答问题”。LLM reranking 的作用是让模型逐条判断候选上下文和问题的相关性。

当前版本使用 qwen3.5-flash 做 reranker,并关闭思考模式。

Python
response = self.compatible_client.chat.completions.create(
    model=self.model,
    messages=messages,
    temperature=0,
    extra_body={"enable_thinking": False},
)

这里关闭思考模式是有必要的。重排任务不需要长链推理,它需要快速、稳定地给出相关性分数。启用长推理会增加耗时,也可能让模型输出多余解释,影响结构化解析。

7. Prompt 与结构化输出:控制答案形态

原始企业年报问答中,不同问题类型使用不同 Prompt 和 schema,例如数值、布尔、名称、名称列表、对比问题。这个设计很重要,因为企业问答不是开放式聊天,答案要能被检查和提交。

论文知识库问答中,问题更多是解释型、总结型和方法对比型,因此当前主要使用 str 类型输出。它要求模型返回分析过程摘要、引用页面、来源论文标题和最终答案。

这种约束有两个作用。第一,减少模型自由发挥。第二,让系统可以把引用页和最终答案一起展示,方便人工复查。

四、案例一:原始企业知识库方案

原始方案解决的是企业年报类问答。假设用户问:

纯文本
What is Apple's revenue?

如果使用 native RAG,系统可能直接在全部年报中做向量检索。这种做法有两个隐患:一是可能召回其他公司的 revenue 信息,二是可能只召回表格附近的局部文本,缺少单位、年份和上下文。

冠军方案的处理路径更稳。

第一步,问题进入数据库路由。系统根据问题中的公司名、报告名或实体信息,把问题路由到 Apple 对应的向量库,而不是所有企业文档。

第二步,在对应向量库中检索 Top K chunk。这个阶段只负责找到可能相关的局部片段。

第三步,根据 chunk 元数据回到 parent page。这样可以拿到完整页面,包括表格标题、单位、脚注和上下文说明。

第四步,LLM reranking 对候选页面重新评分。它判断页面是否真的能回答 revenue 问题,而不只是包含 revenue 这个词。

第五步,Prompt 路由选择数值类回答模板。最终输出需要包含数值、单位、年份和引用页,不能写成泛泛解释。

这个案例说明,原始方案的核心不是某个模型,而是把企业问答拆成了可控链路:先选库,再检索,再补上下文,再重排,再按问题类型生成。

五、案例二:面向企业落地的改造版本

比赛方案可以取得高分,但真实企业环境还要考虑另一类问题:能不能部署,能不能调试,能不能迁移到国内 API,能不能处理企业内部文档,能不能解释错误来源。

因此,落地版需要做几类改造。

改造点

原始比赛思路

落地版处理

文档解析

选择高分 parser

保留解析中间结果,方便人工检查

API 调用

可使用国外模型和服务

迁移到 DashScope/Qwen,降低环境依赖

表格处理

追求评测集效果

表格可选序列化,避免无意义增加成本

检索方式

固定竞赛参数

支持 vector、BM25、hybrid 调试

Prompt

面向固定题型

按企业实际问题类型重写

调试方式

关注最终提交文件

展示路由、召回、重排、引用、耗时

一个实际企业需求可以这样理解:企业内部有制度文档、产品手册、合同模板、财务报告和项目总结。员工提问时,系统不仅要回答,还要指出答案来自哪份文档、哪一页,并且不能把相似部门、相似产品或相似年份的信息混在一起。

这种场景下,推荐流程如下。

纯文本
企业文档
  -> PDF/Word/Markdown 解析
  -> 文档类型识别
  -> 按部门、业务线或文档类型建库
  -> 问题路由到相关知识库
  -> 向量检索 + BM25 检索
  -> 父文档或父页召回
  -> LLM reranking
  -> 按问题类型生成答案
  -> 返回引用与置信依据

这里的关键变化是,知识库不再只是所有文档的集合,而是按照业务边界组织。对企业来说,业务边界比模型参数更重要。人事制度、财务报表、技术手册、合同条款混在同一个库里,检索质量通常会下降。

落地版还需要保留可观察性。一次失败回答至少要能判断下面几个问题。

观察点

判断的问题

路由结果

问题是否进入正确知识库

初始召回

是否找到相关文档

父页召回

是否补齐上下文

rerank 分数

相关证据是否排到前面

引用页面

答案是否可追溯

耗时统计

系统是否具备交互性

如果没有这些中间信息,RAG 调优只能靠猜。企业知识库中最常见的问题不是模型完全不会答,而是答得像对的,但引用错、年份错、部门错或依据不充分。

六、案例三:我的论文知识库版本

在企业知识库框架基础上,我把项目迁移到个人论文知识库。这个版本的目标不是问公司财报,而是服务于博士论文方向的文献阅读和方法复用。

当前知识库包含两个主题:wireless_sensingpassive_radar。每个主题目录下放多篇论文 PDF,每个主题最终生成一个 FAISS 向量库和一个 BM25 索引。用户提问后,系统先根据中英文关键词选择主题库,再做检索、父页召回、Qwen reranking 和 Qwen 回答。

改造后的论文知识库 RAG 流程(图由AI辅助绘制)
改造后的论文知识库 RAG 流程(图由AI辅助绘制)

核心配置如下。

Python
DEFAULT_DASHSCOPE_LLM_MODEL = "qwen3.5-flash"
DEFAULT_DASHSCOPE_RERANKING_MODEL = "qwen3.5-flash"
DEFAULT_DASHSCOPE_EMBEDDING_MODEL = "text-embedding-v4"
DEFAULT_DASHSCOPE_EMBEDDING_DIMENSION = 1024

离线构建过程如下。

powershell
cd C:\Users\songm\Desktop\企业知识库\wireless_sensing
python ..\RAG-Challenge-2-main\main.py parse-pdfs --parallel --chunk-size 2 --max-workers 2
python ..\RAG-Challenge-2-main\main.py process-reports --config default

被动雷达主题执行同样流程。

powershell
cd C:\Users\songm\Desktop\企业知识库\passive_radar
python ..\RAG-Challenge-2-main\main.py parse-pdfs --parallel --chunk-size 2 --max-workers 2
python ..\RAG-Challenge-2-main\main.py process-reports --config default

构建完成后,父目录通过 knowledge_bases.json 管理多个主题库。问答时运行跨主题处理流程,或直接使用 Gradio 调试台。

论文 RAG 调试台界面
论文 RAG 调试台界面

示例 3.1:WiFi 呼吸检测

第一个问题用于验证无线感知主题。

纯文本
WiFi 感知如何用于人体呼吸检测?

这个问题需要系统找到三个层次的信息:WiFi 感知中的 CSI 或信号变化,人体呼吸造成的胸腔微动,Fresnel 区或相位变化对检测机制的解释。也就是说,它不是只问一个术语,而是问一个物理机制。

当前版本的执行结果如下。

步骤

结果

主题路由

命中 wireless_sensing

初始检索

返回 9 条候选

LLM 重排

保留 6 条上下文

上下文长度

13689 字符

回答生成

完成

总耗时

18.804 秒

WiFi 呼吸检测问答结果
WiFi 呼吸检测问答结果

答案解释了呼吸引起胸腔起伏,进而导致反射路径长度和相位变化,最终在 WiFi 信号中表现为可检测的周期性变化。更重要的是,系统给出了引用来源,包括呼吸检测、毫米级 WiFi 感知和泛在无线感知相关论文页面。

这个例子也说明了评估的重要性。旧版本中该问题曾因为关键词缺失而路由失败。修复后,系统不仅能回答,还能展示路由、检索、重排和引用。

示例 3.2:Passive radar 机会照射源

第二个问题用于验证被动雷达主题。

纯文本
Passive radar 通常如何利用 illuminators of opportunity?

这个问题的关键不是解释 radar 这个词,而是解释 passive radar 的系统机制:它不主动发射信号,而是利用环境中已有的 FM 广播、电视、蜂窝基站、卫星信号或 WiFi AP 作为机会照射源;系统通常通过 reference channel 接收直达信号,通过 surveillance channel 接收目标反射回波,再用信号处理方法完成检测或跟踪。

当前版本执行结果如下。

步骤

结果

主题路由

命中 passive_radar

初始检索

返回 10 条候选

LLM 重排

保留 6 条上下文

上下文长度

32780 字符

回答生成

完成

总耗时

19.576 秒

Passive radar 问答结果
Passive radar 问答结果

这个例子验证了跨主题路由的必要性。问题中可能出现 WiFi AP,但系统仍然应进入 passive_radar,因为问题核心是被动雷达的机会照射源机制,而不是一般 WiFi 感知。

七、评估方法

RAG 不能只看最终答案。最终答案正确时,可能只是模型猜对了;最终答案错误时,也需要知道错误来自解析、路由、检索、重排还是生成。

当前评估拆成五类指标。

指标

含义

用途

route_correct

预测主题是否等于期望主题

评估路由是否正确

context_term_recall

检索上下文是否覆盖期望关键词

评估证据召回

context_block_precision

返回块中相关块比例

评估上下文噪声

answer_term_recall

答案是否覆盖期望关键词

评估回答要点

latency_seconds

单问题耗时

评估交互可用性

评估集包含 24 个问题,覆盖无线感知和被动雷达两个主题。第一版本的结果中,23 个问题正确路由,1 个问题因为“呼吸检测”关键词缺失失败;上下文关键词召回均值为 0.875,上下文块精度均值为 0.8889,答案关键词召回均值为 0.6667。

路由错误的原因是,第一版本设计了2个主题的数据库,然后通过关键词去路由,如果关键词没有命中,则不选择任何数据库,直接导致检索失败,解决方案是让主题关键词模糊化,设计更多的关键词,同时设计多路由方案,一个问题可以同时路由多个数据库,最终结果符合预期,在当前较少的数据库下可以达到100%正确路由。

这组结果不能简单理解为系统最终准确率,而应理解为一次调试定位:检索和重排整体可用,但路由关键词表需要补齐,答案关键词召回仍有提升空间。后续可以继续扩展评估集,引入人工标准答案和引用页标注,再分别计算 Hit@K、MRR、nDCG、faithfulness 和 citation accuracy。

小结

企业知识库 RAG 的核心是工程链路设计,而不是单点模型调用。原始冠军方案的价值在于,它把 native RAG 拆成了可控模块:文档入库、数据库路由、Prompt 路由、父页召回、LLM 重排和结构化生成。真实落地时,需要进一步考虑解析质量、知识库边界、API 可用性、调试界面、评估集和引用追溯。

我的论文知识库版本在这个框架上做了主题化迁移。当前系统已经可以对无线感知和被动雷达论文进行问答,能够展示路由、检索、重排、引用和耗时。后续继续扩展时,优先方向不是盲目增加模型能力,而是增加主题库、完善路由策略、补充人工评估集,并把引用页准确性作为核心指标之一。

END