ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

RAG项目实战全攻略:从检索增强生成原理到企业知识库问答落地

RAG项目实战全攻略:从检索增强生成原理到企业知识库问答落地

做RAG项目,是我接手一个内部知识库问答需求之后才真正意识到,检索增强生成这件事的复杂度,远不止“把文档切碎塞进向量库,再丢给大模型”这么简单。RAG(Retrieval-Augmented Generation,检索增强生成)的核心思路,是让大模型在回答问题之前,先去检索一批相关资料,再基于这些材料生成答案。如果你正准备搭建企业知识库问答、让AI读取产品手册、或者给业务系统做智能问答,那么这篇文章基本是照着真实的踩坑路径走的,可以当一份可直接抄作业的参考。

做之前我以为最快三天能出Demo,真正动手后才发现,从文档清洗、切分策略、Embedding模型选型,到向量库选型、检索链路调优、生成提示词设计,每一步都有很多隐性成本。而且最关键的并不是某个单独环节,而是整个链路如何配合。这篇文章我会按项目从零到落地的顺序来梳理,包含选型理由、关键参数、实测踩坑记录,以及我最常被问到的几个问题。

1. 项目定位:先想清楚为什么要用RAG

1.1 一条典型的业务需求

场景是这样的:某公司内部沉淀了大量的制度文档、产品手册、历史项目报告,散落在各处。员工每天在内部群问“报销流程是什么”“某个型号的设备的维护周期是多久”“这个项目之前有没有做过类似方案”。原有的搜索引擎只能做关键词匹配,搜出来一堆不相关的文档,大家干脆不搜,直接问人。于是提了一个需求:能不能做一个问答机器人,让它读完全部文档之后,用自然语言回答问题?

这个需求看似直白,但深挖下去你会发现,它背后是三个核心问题:知识分散且量大、用户希望直接用自然语言获取答案、答案必须能追溯到原始出处。这三个问题正好落在RAG能解决的范畴内,所以项目定位很快就确定了。但这里要强调一下,定位确定不代表技术实现简单,后面每一步都是细节。

1.2 为什么是RAG,而不是微调模型或者喂长文本

拿到需求时,最容易想到的三个方案是:把全文塞进问答接口的上下文、微调一个专用大模型、做RAG。我当时列了一张对比表来选型,因为预算和项目周期不允许我拍脑袋决定。

先说“把全文塞进上下文”。很多大模型支持很长的上下文窗口,看起来好像可以直接把所有文档塞进去,让模型自己找答案。但实际覆盖公司几千份文档之后,Token成本会高得吓人,而且超过一定长度后模型对细节的召回能力下降得很厉害,经常抓错重点。这种方法只适合小批量资料,不适合企业知识库这种规模。

再说微调。微调的核心价值是让模型学会某种行为模式或表达风格,并不是让模型“记住知识”。你很难通过微调把一个几千页的规章制度背下来,而且每次文档更新都要重新训练,迭代周期太长,维护成本也高。

对比下来,RAG的优势就很明显了:知识更新只需要重跑索引,不需要重新训练模型;检索可以给出引用的原始文档位置,用户能快速验证答案的可信度;一次性能把海量文档管理起来,成本也可控。所以最终选型定为RAG方案,这个选择在绝大多数企业知识库场景下都是成立的。

1.3 RAG的能力边界,提前说清楚

这个环节我想展开讲,因为很多需求方对接下来的效果有超出合理范围的预期。RAG擅长的是“从已有文档中找答案并归纳总结”,它不擅长做跨文档的深度推理,也不擅长回答文档里根本没有的信息。比如你问“根据过去三年所有项目的成本数据,预测下一年预算”,RAG可以帮你把三年项目报告里关于成本的段落都找出来,但它不会做严谨的数据分析和预测,那是另一个系统的活。

项目启动前,我建议把所有需求列成问题清单,逐条标注“预期能回答”“需要结合规则”“可能回答不了”三档。这样可以避免上线后因为效果不及预期被反复挑战。实际项目中,我们整理了大约一百条典型问题,作为验收测试集,这个测试集在后面调优的时候发挥了非常大的作用。

2. 技术选型:先定组件,再写代码

2.1 Embedding模型怎么选,决定了召回上限

嵌入模型负责把文本变成向量,这个向量要能反映语义相似度。选型时我主要关注三个维度:中文效果、向量维度、是否有针对特定领域的优化模型。

我们测试了几类模型,对比维度包括通用语义相似度、金融/制度文档的专业术语理解、以及推理速度。最终的结论是:如果预算允许,优先选已经在大量中文语料上训练过的商用Embedding接口,效果稳定且省心;如果数据涉密必须内网部署,就选开源的国产中文嵌入模型,效果稍逊但可控性强。我们因为数据合规要求选择了内网部署,当时花了不少时间做开源模型的调参,后面会在实操部分详细展开。

维度方面需要注意一点:向量维度越高,不一定代表效果越好,但计算和存储成本会显著上升。如果文档规模在十万级以下,选择小一些的模型输出维度(比如几百维)即可,没必要为了追求上限而牺牲效率。

选Embedding模型千万不要只看榜单分数,一定要拿自己领域内的真实问答对去实测。我见过不少项目用通用榜单高分模型,落地后效果反而一般,因为领域术语和文档表述习惯相差太大。

2.2 向量数据库:轻量还是重量,看规模和团队

向量数据库是RAG项目里最容易陷入选择困难的部分。市面上的选择很多,我把它们粗略分成三类:

第一类是轻量级向量库,直接以库的形式嵌入代码,适合原型验证和单机小规模场景。优点是部署简单,本地开发调试方便,实测小规模数据检索速度也很快。缺点是功能相对单一,分布式扩展、权限管理、数据备份这些能力很弱。

第二类是专业向量数据库服务,功能完整,支持分布式、多租户、高可用、强一致。适合企业级生产环境,但部署和运维成本较高,团队如果没有人熟悉这类基础设施,初期会花不少时间踩坑。

第三类是在传统数据库上启用向量检索扩展。这个选择的最大优势是,如果团队已经有成熟的数据库运维体系,可以复用现有能力,避免多维护一套系统。缺点是向量索引的性能和功能,相比专业向量库还有差距,当数据规模上升到千万级时差异会明显变大。

我当时因为团队运维能力有限,又不想引入太重的组件,最终选择了单体应用里内置的轻量向量库,配合一些补充手段来做元数据过滤。这个方案的优点是上线快、调试方便,缺点是数据量到了几十万条后,检索性能和索引构建速度会出现明显下滑。如果你的项目一开始就已经预估到百万级文档,建议直接上专业服务,不要在后期迁移上浪费时间。

2.3 重排模型:被很多人忽略的关键组件

很多人做RAG只做了两步:检索TopK + 喂给大模型。但实测下来,检索回来的结果,前几个相关,后面几个可能就明显偏了,如果直接把一堆不相关内容塞给模型,生成质量会大幅下降。这时候就需要重排模型。

重排模型的作用,是对向量检索召回的结果做更精细的语义相关性打分。你可以把它理解为初筛和精筛的关系:向量检索负责从全量文档里快速找出候选,重排负责从候选中把真正有用的排在前面。我们项目里加了这一环之后,答案准确率提升非常明显,实测前三位命中率提升了大概十多个百分点。

重排阶段我用了交叉编码器这类模型,它的缺点是速度比向量检索慢,但因为我们只在Top100的小范围内重排,耗时完全可接受。如果项目对延迟要求极高,可以跳过重排或缩减候选数量,这是一个需要根据场景权衡的选择。

2.4 框架选择:不迷信框架,也不硬造轮子

要不要用LangChain这类框架,是很多初学者纠结的问题。我的建议是:先写裸代码,实现最小闭环,再决定要不要引入框架。原因是框架封装层多,出了问题很难定位到底在哪个环节,而RAG的链路里最容易出问题的恰恰是那些被封装的细节。

我一开始尝试用框架搭了一个版本,整体跑通很快,但一旦涉及自定义元数据过滤、自定义切分逻辑,以及需要给特定文档加额外处理规则时,框架就会显得比较笨重。后期我直接用配置文件组合几个组件,自己写了检索和生成之间的胶水代码,代码量不大,但可控性高了很多。这里的经验是:框架适合快速验证和统一范式,但生产级RAG项目最好是自己控制和编排整个管道,框架只做提供初版参考。

3. 实操全流程:从文档到问答的关键步骤

3.1 数据清洗与切分:垃圾进,垃圾出

这个环节是整个RAG项目里最耗时、最不起眼、但决定上限的部分。我见过不少人第一天就急着跑通Demo,结果效果不好,回头才发现问题几乎都出在source数据上。

我们的文档来源包括Word、PDF、Markdown,还有一批导出的表格。清洗阶段做了这些事:去掉页眉页脚、去掉重复的目录页、把表格结构尽量转成描述的文本、统一特殊字符编码。这个阶段看起来像是体力活,但直接关系到后续检索的准确性。比如一份纸质扫描件没做OCR就进了向量库,那检索到的内容基本没法用;一个表格被切碎成了两部分,那么答案就会缺了一半。这些坑我们都踩过,处理办法是单独写了一个解析脚本,在入库前对解析结果做抽检,每种文档类型抽查几条关键信息,确认无误再进入下一步。

切分策略更容易让人头疼。切得太小,每段内容表达的意思不完整,语义匹配效果差;切得太大,一段里混入多个主题,检索出来的结果准确性下降,而且喂给大模型的Token开销也会变高。我当时测了几组参数组合,取了一个适合我们数据的chunk_size和一个overlap比例,也就是相邻片段重叠部分的比例。这个数值不是拍脑袋定的,核心逻辑是让切分后的每一段尽量保持一个完整语义单元,同时通过重叠部分避免关键信息被从中间截断而丢失。

另外加了一个很有用的优化:按文档结构切分。先用Markdown和标题层级识别出章节边界,再在章节内部按token数做二次切分。这样切出来的单元天然就是围绕一个主题的,比纯按长度硬切效果好很多。后来的项目如果文档里有规律的标题结构,我都建议用这种方式。

3.2 索引构建与元数据设计:为检索打通一条快车道

索引构建就是把清洗和切分好的文档块逐块做Embedding,然后写入向量库。这个过程本身不复杂,但有几个容易被忽视的设计细节。

第一个细节是ID设计:每个文档块需要全局唯一且稳定的ID,这个ID要把来源文档ID、章节路径、片段序号都编码进去。当答案出了问题,你可以快速定位到是哪一份文档的哪一小节。没有这个,调试会非常痛苦。

第二个细节是元数据。我们把文档来源、部门、文档类型、发布时间、作者等字段都作为元数据存了下来。实践下来这非常有用,因为很多问答带有隐含的过滤条件,比如“财务部的报销流程”,如果没有部门字段做过滤,系统很可能会把其他部门的流程也检索出来,答案就乱了。我建议元数据要尽量丰富,但过滤条件不用一开始全用上,而是在测试中发现错误结果时,根据错误线索把过滤条件逐渐加上去。

向量索引的选择也很关键。轻量向量库支持多种索引类型,比如暴力检索或近似最近邻。小规模数据暴力检索又快又准,数据量大了之后再切换到近似索引,这里一定要了解索引召回率与性能之间的权衡。我们在测试集中发现,某些近似索引参数调得不好时,会漏掉真正的相关内容,挺隐蔽的。

3.3 检索链路调优:从TopK到混合召回

检索这一步,很多人理解成“向量库查一下,排个序,取前几条”,其实想做出稳定效果,链路比这复杂得多。我们实际采用的检索流程是:先做query理解,必要时拆分为多个子查询,然后对每个子查询进行向量检索和关键词检索,两个结果合并、去重、加权,再做元数据过滤,最后交给重排模型精排。

关键词检索(BM25这类)是我建议一定要加上的。原因是向量检索擅长语义相似,但有时会忽略精确的关键词。比如文档里频繁出现的特定产品型号,向量检索会返回“好像相关但没直接命中”的内容,而关键词检索能直接把型号匹配到对应文档。混合召回之后再用规则把两路结果合并,准确率提升很大。如果你预算有限、不想上重排,那么混合召回是性价比最高的优化手段。

TopK参数也要反复调。K值太小,容易出现漏召回;K值太大,重排和生成阶段的噪声又会增加。我们权衡后的初始值是向量检索取每个查询的前50条,混合去重后保留100条候选,重排后再取前5条作为生成材料。这个数值看起来很简单,却直接决定了生成质量和延迟,我建议你在自己的数据集上多测几组。

3.4 生成策略:提示词和引用机制

生成阶段,我做的第一件事是设计了一套提示词模板,要求模型严格基于给定的文档片段回答问题,禁止编造。如果文档片段里没有相关信息,必须明确回复“根据现有资料无法回答”。这个约束已经能过滤掉相当一部分幻觉问题,但仍然不够,因为模型会尝试把无关片段强行组织成一条看起来合理的逻辑。

因此后面又加了一层约束:在生成时要求模型在关键结论后标注出处,例如“根据某文档某章节”。做这一步的意义不只是给用户看,也方便我们做自动校验——如果模型输出里没有引用任何检索片段,基本可以判定这个答案是自由发挥的,可以在产品层面拦截掉。

生成阶段我还试过一个方法:把检索回来的TopK片段按重排分数排序,并限制生成时最多使用的片段数量。这个数量很关键,太多会导致模型东拼西凑,太少又容易漏信息。我在不同问题类型上测下来,一般3到5个片段是比较均衡的区间。回答泛泛的问题时取3个够用,回答需要多证据支撑的问题时取到5个会更稳妥。

4. 项目落地中的常见问题与排查实录

4.1 结果不对:先定位是召回问题还是生成问题

做RAG项目最常遇到的问题就是:答案不对。这个“不对”背后可能有一百种原因。我养成的排查习惯是:先看检索结果,再定生成责任。

具体做法是,把每次问答过程中的检索片段单独打印出来。如果检索回来的片段本身就不包含正确答案,那问题基本出在文档清洗、切分、Embedding模型或检索参数上;如果检索片段里明明有答案,但生成结果还是错的,那问题就出在提示词或生成策略上。

有一次排查一个“总是答非所问”的案例,绕了半天才发现是文档里有一批以图片形式保存的流程截图,切分后图片无法被检索到。这就是数据源的问题,而不是模型的问题。这类问题很常见,排查思路一定要系统化,遇到问题先看数据,而不是急着换大模型。

4.2 响应太慢:瓶颈在哪里

上线后第一个被吐槽的是响应速度。最初版本从提问到回答要十几秒,排查发现主要耗时出现在三个环节:query向量化、检索、重排。query向量化因为模型加载方向的原因,第一次调用极慢,后面做了常驻和缓存时间有所好转。

重排是最大的瓶颈,最初把Top100全部送去重排,耗时几乎不可接受。后来我把候选取为Top30,并加了一个简单的规则预筛,先把明显不相关的丢掉了再送重排,延迟就压下来了。另外,大模型的生成耗时也要考虑。生成时间长主要受输入Token数量和输出长度影响,输入Token数量又取决于TopK片段数量,所以这一步的优化需要对生成质量和延迟之间做平衡。我当时把“防止用户等待过久”设定为一个硬性指标,把超时自动切断和部分生成结果返回做了兜底,算是比较务实的做法。

4.3 数据更新:知识库会过期

企业文档一个月更新好几轮,如果不能及时同步,那RAG回答的准确性就会逐渐下滑。我们遇到的典型问题是:旧版制度文档刚被废止,新文档还没完成切分入库,问出来的答案还是老政策。

解决思路是给每个文档块增加了版本号和生效日期,检索时加一道过滤条件,只返回当前生效版本的片段。这个方案在日常维护中非常实用。另外还建了一个定时索引重建任务,每次文档增量更新后只做增量入库,避免全量重建带来的资源浪费。需要注意的一点是,增量过程中很容易产生重复片段,ID稳定性在这里帮了大忙。去重策略、版本失效策略和定时重建策略,建议在设计阶段就纳入,不要等上线后再补。

4.4 评估体系:如何判断系统到底行不行

没有评估体系的RAG项目,就像没有验收标准的开发任务,改了一版又一版,却说不清改完有没有变好。我建议建立三层的评测方式:

第一层是检索质量评估,用一批标准问题,检查正确答案是否出现在TopK检索结果中。这个指标最直观,反映了“找得有没有找到”。第二层是生成质量评估,用人工打分或参考答案对比,看最终回答是否准确、完整、有引用。第三层是链路级回归测试,每次改动后跑一遍固定测试集,防止修了一个问题,又引出别的回归错误。

最开始我们靠人工一条条测,效率太低了。后来把评测集做成了JSON格式的测试用例,用脚本批量跑,每次输出一个评测报告。这个流程看着简单,但对质量提升的帮助却非常显著。每个版本的改动是否有收益、哪里退化,从报告里一眼就能看到,再也不用靠感觉做判断。

5. 一点个人体会

项目做完回头看,最大的感受是:RAG项目的难度不在某一个算法,而在整条链路的稳与准。一个环节看起来只影响一点,但多个环节的误差叠加起来,最终效果就会差很多。你很难指望单独换一个更强的模型就能解决所有问题,关键还是把每个环节的数据质量、参数和交互逻辑打磨到位。

尤其想提醒后面做同类项目的朋友:不要在原型阶段投入太多精力,应该留时间给数据清洗和评估体系建设。我做第一个版本时花了大半时间在“能让它跑起来”,真正让效果产生质的飞跃的,反而是后面做数据清洗、加混合检索、建评测集这些“看起来不太酷”的环节。

最后再分享一个个人很喜欢的小做法:给所有检索回来的片段加一个来源指纹,也就是文档ID加章节路径,让最终答案可以一路追溯到原始片段。这样不管是内部调试还是用户质疑,都能快速回应“这个答案的依据是什么”。如果你正在做RAG项目,我建议把这个功能放进第一版,它会在整个生命周期里一直帮你节省时间。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进