尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

RAG完整流程详解:从离线建库到线上召回的工程实践

发布时间:2026/9/28 16:57:41

资讯中心
01
ARTICLE

RAG完整流程详解:从离线建库到线上召回的工程实践

RAG完整流程详解:从离线建库到线上召回的工程实践
上个月面字节一面面试官上来就问了一个我以为很基础、但真讲起来特别容易翻车的问题RAG完整流程从离线建库到线上召回让我从头到尾讲一遍。我当时心里其实有点庆幸因为前阵子刚把公司内部的知识库检索项目从零跑通从PDF解析、文档清洗、文本切分、向量化入库到线上query改写、混合召回、重排、生成完整链路都实操过。这问题看着是送分题但只有真正跑过一遍的人才能在被追问细节时不露怯。这篇就把这条链路完整拆开讲一遍给同样在准备大模型岗位面试、或者打算自己搭一套RAG知识库的朋友做个参考。1. 先从面试题说起RAG到底解决什么问题1.1 大模型的“知识天花板”和RAG的解题思路面试官问RAG第一层想确认的其实是你知不知道为什么要做RAG而不是直接去微调模型。大模型的知识来源是训练语料训练结束后知识就固定了顶多靠上下文窗口临时塞点内容。问题很明显私有知识它没学过新知识它有截止日期细粒度业务数据它答不准而且一旦超出知识边界就开始一本正经地胡说八道。微调能解决一部分问题但微调的本质是改变模型的行为模式和表达风格不是给它装一个实时更新的知识库。把几千篇业务文档拿去微调成本高、周期长而且知识更新一次就要重新训练一次。RAG的思路完全反过来不逼模型记住所有知识而是给模型配一个“外挂资料库”。用户提问时先从资料库中检索出相关片段再把片段和问题一并交给模型生成答案。这个设计其实很朴素类比一下微调是逼着员工把厚厚的手册背下来RAG是给员工配一个资料室遇到不确定的问题先翻资料再回答。资料室更新只需要替换书架上的书不用重新培训员工这就是RAG在知识更新和维护成本上的核心优势。所以面试时如果只背一句“RAG是检索增强生成”拿不到分数。你要能说出它解决了什么、和微调的分工边界在哪甚至能点出什么时候该用RAG、什么时候该用微调、什么时候两者结合才算过了第一层。1.2 完整流程的全景拆解离线建库与线上召回RAG的完整链路可以分成两段离线建库和线上召回。离线建库是把原始文档变成可以被检索的向量索引。流程大致是文档采集、格式解析、内容清洗、文本切分、向量化、索引构建最后写入向量数据库。这个过程不是一次性任务知识库内容更新、文档版本迭代、切分策略调整都需要重新走一遍。线上召回是把用户的query变成一段可作答的上下文。流程大致是query预处理与改写、向量检索、关键词检索、多路结果融合、重排、Prompt组装、大模型生成。线上链路对延迟和稳定性要求高每一步都可能成为瓶颈。很多人只盯着向量检索和Prompt生成忽略了离线阶段对效果的影响。我自己的实操体会是离线建库决定了RAG效果的上限线上召回只是尽可能逼近这个上限。面试的时候如果能把这条链路完整讲出来再补几个“为什么这么做”的细节基本就能让面试官觉得你手里有真东西。2. 离线建库第一步决定最终效果的上限2.1 文档解析与清洗PDF、表格这些坑怎么填离线建库的第一步不是切分而是把原始文档变成干净的纯文本。这一步看着简单实际上坑最多。PDF是最常见的格式但PDF分两类文本型PDF和扫描版PDF。文本型PDF可以直接用pdfplumber、PyMuPDF提取文字速度和精度都不错。扫描版PDF本质是图片必须先走OCR我实际项目里用的是PaddleOCR中文识别效果比较稳尤其是印刷体。表格是另一个容易翻车的点复杂表格直接按文本流提取会全乱掉。我的做法是先用pdfplumber把表格区域识别出来再转成Markdown格式每一行用管道符分隔这样既保留结构也方便后续大模型理解。如果你用的是Unstructured这类工具它对表格的处理会友好一些但速度偏慢文档量大时需要考虑分片处理。清洗环节很多人会跳过但脏数据是检索效果差的隐形杀手。常见问题包括页眉页脚重复出现、PDF水印文字混入正文、文档里残留的URL和乱码、同一份文档的多个版本混在一个目录里。我踩过一个典型的坑某批文档页脚有固定的公司名称和页码切分后很多chunk都带着这段重复文本导致检索时经常召回大量相似但无意义的片段。后来在清洗层把页眉页脚、水印位置通过规则过滤掉召回质量明显提升。清洗阶段还有一个容易被忽视的点元数据。每个文档入库前最好都打上来源、标题、作者、更新时间、所属业务线这些标签。别小看这些元数据线上召回后做过滤、重排时它们非常有用比如同一个问题在两个部门文档中都能命中元数据可以帮助优先选择权威性更高的来源。2.2 文本切分chunk size不是拍脑袋定出来的文本解析完成后下一步就是切分。切分这件事直接决定检索的粒度。chunk太小单个片段语义不完整检索结果碎片化模型拿去生成时缺少上下文。chunk太大单个片段包含太多无关信息向量化之后语义被稀释检索精度下降。我自己的经验中文场景下chunk size在300到500个字符之间比较合适英文或代码场景可以按token算大概200到400个token。但这只是起点具体数值要看文档类型产品文档可以大一点因为每个段落本身相对独立FAQ问答对可以整个作为chunk长篇幅的规章制度类内容我会按二级标题拆成逻辑块再在块内做细切分。切分方式上最简单的固定长度切分我基本不推荐因为它会在句子中间硬切把语义拦腰截断。LangChain的RecursiveCharacterTextSplitter是很多人的默认选择它按分隔符优先级逐级切分先按段落、再按句号、最后按逗号尽量保证每段是完整的语义单元。如果文档本身有清晰的Markdown标题结构我会优先按标题层级做结构化切分比如##下面一级作为切分边界这和人类阅读习惯一致检索命中率也更高。还有一个参数容易被忽略overlap相邻chunk之间的重叠量。它用来解决一个场景如果答案恰好落在两个chunk的边界附近没有overlap就会两边都检索不全。我通常设置重叠量为chunk size的10%到15%。比如chunk size是400字符overlap设50字符左右。overlap不是越大越好太大了chunk之间重复内容多向量索引膨胀召回结果也容易出现大量重复片段。2.3 向量化与索引构建选对embedding和索引结构清洗切分完之后就是核心环节向量化。embedding模型的选择直接决定检索效果。我这里列一下实际对比模型向量维度中文效果部署成本适用场景OpenAI text-embedding-3-small1536可降维中上API调用英文为主/多语言BGE-large-zh1024好本地GPU可跑中文知识库bge-m31024好本地GPU可跑中英混合/多语言召回M3E-base768中上CPU也可跑轻量级中文场景text2vec-large-chinese1024中本地GPU老牌中文模型我实际项目里中文知识库用的bge-m3它在中文语义匹配和长文本泛化上比较均衡而且支持多语言后面如果加英文文档不用换模型。小规模项目也可以用M3E占用资源低效果够用。需要强调一点embedding模型要和检索场景匹配不能用对话模型的embedding接口直接顶替不同模型的向量空间不一致混用会导致检索结果莫名其妙。向量化完成后就要建索引。向量数据库的选择取决于数据规模。数据量在百万级以下FAISS完全够用。它是Facebook开源的向量检索库轻量、灵活、没有额外服务依赖单机就能跑适合快速验证和中小项目。数据量大且需要在线服务、多副本、权限控制再上Milvus或者Qdrant。Milvus功能全支持多种索引类型和元数据过滤但运维成本高部署一套集群足够写两周日志。Qdrant轻量一些Rust写的单机性能和易用性平衡得不错。Elasticsearch这边比较特殊如果项目里已经有用ES做关键词检索的基础可以顺便用ES的dense_vector字段做向量检索这样关键词和向量检索在一个系统里搞定不用额外维护两套存储。索引结构上最常用的HNSW。它通过分层图结构做近似最近邻搜索速度和召回率的平衡好。两个关键参数要理解M控制每个节点的最大连接数越大召回率越高但内存占用和构建时间也越大efConstruction控制构建时候选集的动态大小越大图质量越高。我一般设置M为16到32efConstruction为200到400。数据量小的时候用flat暴力检索效果最好因为数据集规模不大全量计算花费的时间也能接受而且精度是100%。数据量上来后再切HNSW。向量入库时还有个细节metadata过滤条件要提前设计好。比如按时间范围过滤、按文档类型过滤、按部门权限过滤这些在线上检索时非常常用。如果入库时没设计好元数据字段后面想过滤会发现无从下手只能重新跑一遍入库流程。3. 线上召回把问题变成候选集的完整链路3.1 查询改写先让“问题”变得可检索线上召回的第一步往往不是检索而是先看一眼query本身。真实用户输入千奇百怪口语化表达、缺字、有多轮对话上下文、夹杂错别字。直接把原始query拿去做向量检索效果经常不理想。query改写的目的就是把用户输入转换成更适合检索的形式。最简单的一类是上下文补全多轮对话中用户说“那这个方案的成本呢”如果不结合上一轮提到的方案名这轮query检索出来一定是乱的。做法是把最近几轮对话拼进query让模型重写成一个独立完整的问题。另一类是专有名词归一化比如“上个月的销售额”这种相对时间表达最好解析成具体的月份范围再检索。还有一类是同义扩展用户在ERP场景里说“库存明细”但业务文档里写的是“仓储台账”不做改写或扩展就很难命中。改写方式有两条路一是用大模型改写效果好但多一次LLM调用会引入延迟和额外成本二是规则加词典的轻量改写比如维护一个同义词表、把口语词映射到标准业务词速度极快。我实际项目里的做法是两者结合先用规则处理时间表达和同义词处理不了再上LLM改写这样大部分请求不用多走一次模型调用。HyDE是另一个值得提的技巧。它的思路是先让LLM根据query生成一段“假想文档”再用这段假想文档的向量去检索。因为假想文档里包含更多与query相关的关键词和上下文在部分场景下检索命中率更高。但HyDE不是免费的它也要多一次LLM生成调用而且如果模型生成的假想内容偏了反而会把检索带偏。我个人只在query特别简短、直接检索效果差时才用。3.2 混合检索向量召回和关键词召回缺一不可向量检索擅长捕捉语义相关性比如“王者荣耀怎么出装”和“鲁班七号新手玩法攻略”这种语义相近但字面完全不同的表达。但纯向量有一个明显弱项精确匹配。业务场景里的订单号、设备编号、型号、人名、API函数名这些内容对语义不敏感向量化之后距离不一定最近。所以线上召回普遍用混合检索一路是向量检索负责语义扩展另一路是BM25关键词检索负责精确匹配。BM25是经典的信息检索算法相比TF-IDF多做了词频饱和和文档长度归一化处理在短文本检索上表现稳定。Elasticsearch本身就是BM25的实现如果离线阶段已经用了ES的dense_vector这一步可以直接在同一套ES里做bool查询。两路结果怎么融合我推荐RRFReciprocal Rank Fusion。它的公式很简单对每路结果按排名计算score 1 / (k rank)k一般取60然后把多路分数加和再按总分排序。RRF不需要依赖分数归一化对两路结果数量差异大也很稳定。我实际项目里向量召回取top50BM25召回取top50经过RRF融合后再进重排比单独一路的召回效果好很多。top_k怎么定我见过很多人直接top10拉满然后塞给模型这其实浪费了重排阶段。正确的思路是召回阶段要“宽进”候选集要多比如50条甚至100条保证真正相关的内容没有被漏掉重排阶段再“严出”压缩到5到10条。召回阶段的成本主要是向量检索的耗时和候选集的内存占用这个规模下都不是问题。3.3 重排与融合从粗排候选集筛选出真正的答案向量检索和BM25召回都是粗排它们的共同问题是只能做“语句层面”的相关性匹配没有真正理解问题和文档之间的逻辑关系。embedding模型算出的相似度本质是把两段文本映射到向量空间后的距离或内积但“文本相似”不等于“能回答问题”。候选集里经常出现检索结果读起来很相关但翻遍全文找不到答案的情况。所以上线阶段需要一级重排。主力模型是Cross-Encoder结构的reranker比如bge-reranker-large。它把query和每个候选文档拼接成一个序列一起送进模型输出一个相关性分数。和Bi-Encoder相比它让query和文档之间做了充分的交叉注意力计算精度高很多但推理速度慢得多不可能对整个知识库全量跑所以只能放在候选集上做精排。重排后的截取数量取决于生成阶段的需求。我一般重排后保留3到6个chunk太少了上下文可能缺少关键信息太多了无关上下文会干扰模型生成。具体可以做个实验在固定数据集上分别测top1、top3、top5、top10的answer准确率找一个拐点作为线上参数。4. 生成、评估与排查RAG上线后才是真正开始4.1 Prompt组装与上下文窗口的精细管理重排完成后就到了生成阶段。这一阶段最常见的误区是以为把检索到的chunk一股脑塞进Prompt就行。Prompt组装要解决几个问题。第一是格式要让模型知道哪些内容是“参考资料”哪些是“用户提问”。我常用的结构是固定开头说明“请根据下面的参考资料回答问题如果参考资料中没有足够信息请回答‘资料中未提及’”然后逐条列出chunk每条前面标注序号和来源最后是用户问题。来源标注特别重要它能让模型在回答时引用出处用户也好追溯验证。第二是上下文长度管理。大模型上下文窗口有上限不能无脑塞chunk。假设上下文窗口是4k tokenquery占200 token系统提示词占300 token留给资料的大约是3500 token。如果每个chunk平均500 token最多塞7个chunk但还要留出生成答案的空间所以实际我控制在4到6个。这里面有个容易被忽视的点通过API调用大模型时token数要按模型的分词器计算简单用len(text)算中文字符数是不准的。中文场景里1个token约等于1到2个汉字不同分词器略有差别最好实测。第三是防止幻觉。RAG不是加上就一定能消除幻觉模型在生成时仍然可能脱离资料自由发挥。我的经验是Prompt里明确约束“只能依据资料内容回答”如果资料不包含答案要明说同时把温度降到0.2以下。评估时发现回答里有资料之外的“私货”还需要在后续迭代中持续压紧。4.2 评估指标别只用“感觉”判断RAG好坏RAG项目最容易踩的坑是“demo效果好一上线就崩”。原因是做demo的时候靠人工看了几条case主观上觉得还行但没有建立可量化的评估体系。常用的评估指标有这些指标计算方式关注点Hit Rate标准答案所在的chunk是否出现在召回结果中召回是否覆盖关键信息MRR第一个相关结果排名的倒数取平均相关结果是否足够靠前NDCG按排名衰减加权衡量排序质量重排后顺序是否合理Faithfulness生成答案中的每个陈述是否被资料支持是否忠实于检索内容Answer Relevancy生成答案对问题的直接相关度是否答即所问我实际项目里最重要的两个指标是Hit Rate和Faithfulness。Hit Rate衡量“检索到底有没有把答案找回来”如果这块低后面改什么都没用。Faithfulness衡量“模型有没有照着资料说人话”回答可以很全面但编了资料里没有的数字就是不合格。评估数据集怎么来需要一套标准的QA对问题覆盖知识库常见场景答案标注出对应chunk的来源。这个标注工作很枯燥但必须做。没有评估集任何参数优化都是在盲调。搭建好评估集后我用脚本自动化跑指标修改一次chunk策略、换一个embedding模型、调一次reranker参数都在同一套评估集上跑多个指标对比用数据而不是感觉做决策。4.3 常见问题排查实录与面试复盘最后整理一下实际运行中高频出现的几个问题和排查思路。第一个是“检索结果相关但找不到答案”。通常是切分粒度问题答案被切到了两个chunk的边界或者一个chunk里的关键信息被冗余文本淹没。排查步骤先看Hit Rate如果低优先调chunk策略和overlap再看query改写是否把问题带偏了。第二个是“多个文档内容重复召回结果全是同质化片段”。典型场景是知识库里同一主题有多篇相似文档。我用MMR最大边际相关性做去重或者在重排后按文本相似度过滤掉冗余内容。第三个是“知识库更新后线上结果没变化”。大概率是增量的chunk没有触发索引重建或者查询的向量DN实例还在用旧索引。这里建议把离线建库做成可监控的流水线每次入库记录文档版本和索引构建偏移量线上查询时带上版本号避免模型和文档版本不一致。第四个是“RAG效果不如直接问大模型”。这种情况往往不是RAG的问题而是检索链路没做好。要么是chunk太碎导致上下文缺失要么是embedding模型选得不好要么是重排环节被跳过了。用评估集一测就知道短板在哪。字节这轮面试到后面面试官往深里追问了几个点怎么评估检索质量、chunk大小怎么调、混合检索怎么融合。这些全是我实操中反复验证过的东西没有一个是背书能背出来的。我当时的体会是面试官其实不指望你能讲出什么高深算法而是想确认你有没有在真实项目里处理过这些麻烦事。最后分享一个小经验如果你准备面试大模型岗位别只花时间看论文真的找一批业务文档把RAG全流程跑一遍。哪怕是个几十MB的本地知识库只要完整跑通一遍你对chunk、embedding、召回、重排这些环节的认知会完全不一样。面试的时候讲自己踩过的坑比背一百篇RAG综述都有说服力。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。