RAG 这个词这两年从论文里一路杀到面试间,几乎成了大模型应用岗位的必考题。我前阵子帮几个朋友做模拟面试,发现一个很普遍的现象:很多人能把 RAG 的流程背得滚瓜烂熟,什么"检索增强生成""向量数据库""chunk 切分",但面试官稍微往深里追问一层,比如"你怎么评估检索质量""召回率上不去怎么办""为什么加了重排反而变慢了",立马就卡壳。这说明大家缺的不是概念,而是把原理、工程细节和优化手段串成一条线的能力。
这篇东西我打算按面试官最常问的六个方向来拆,从最基础的原理讲起,一路讲到工程优化和踩坑经验。不管你是准备面试的求职者,还是刚接手 RAG 项目想补基础的工程师,都能从里面找到能直接用的东西。我会尽量把每个"为什么"讲透,而不是只丢结论,因为面试官真正想听的,是你对这套系统运行逻辑的理解深度。
1. RAG 到底是什么,为什么需要它
1.1 从大模型的三个硬伤说起
要理解 RAG 存在的意义,得先搞清楚大模型本身有哪些绕不过去的毛病。我把它归纳成三个硬伤,这三个也是面试里最常被拿来当切入点的。
第一个是知识截止。任何模型都有个训练数据的时间边界,边界之后发生的事情它一概不知。你问它某个上个月刚发布的框架怎么用,它要么瞎编,要么老实说不知道。这个问题的本质是模型的参数是静态的,训练完就冻结了,没法自动更新。
第二个是幻觉。模型本质是在做"下一个 token 的概率预测",它追求的是语言上的通顺合理,而不是事实上的正确。所以在它不确定的地方,它会一本正经地编出一个看起来很像那么回事的答案。这个特性在闲聊场景无所谓,但在客服、医疗、法律这类场景里是致命的。
第三个是私有知识缺失。企业内部的文档、产品手册、客户资料,这些数据从来没进过公开训练集,模型自然不可能知道。你不可能为了让它懂公司业务就重新训练一遍模型,成本高到离谱。
RAG 就是冲着这三个问题来的。它的核心思路特别朴素:既然模型不知道,那我就在它回答问题之前,先把相关资料找出来塞给它,让它"看着资料回答"。这就好比开卷考试,考生(模型)脑子里的知识有限,但我允许你翻书(检索),翻到相关内容再作答,正确率自然就上去了。
1.2 RAG 和微调到底该选哪个
这是面试里出现频率极高的一道对比题。很多人一上来就说"RAG 比微调好",这个回答太绝对了,面试官一听就知道你没真正做过选型。
我的经验是,这两个东西解决的不是同一类问题,选哪个取决于你的需求落在哪个象限。下面这张表是我自己总结的选型参考,实际项目里基本能覆盖大部分决策场景。
| 维度 | RAG | 微调 |
|---|---|---|
| 解决的核心问题 | 知识时效性、私有知识注入 | 输出风格、任务格式、领域语气 |
| 数据更新成本 | 低,改文档库即可 | 高,要重新训练 |
| 幻觉抑制 | 有明显帮助 | 帮助有限,甚至可能加重 |
| 对算力要求 | 推理时略增,训练无 | 训练阶段算力需求大 |
| 可解释性 | 强,能追溯到原文 | 弱,藏在参数里 |
| 适合场景 | 知识问答、文档助手 | 风格模仿、结构化抽取 |
实际项目里我更多是两者结合。比如一个法律咨询助手,用 RAG 保证它引用的法条是准确的、最新的,同时用微调让它的回答语气更专业、更符合行业表达习惯。面试时如果你能说出"这俩不是二选一,而是各管一段"这个观点,基本就能加分。
1.3 RAG 的完整链路长什么样
很多人对 RAG 的理解停留在"检索+生成"两步,实际上一条完整的链路要细得多。我把它拆成离线和在线两个阶段来讲,这样更贴近工程实际。
离线阶段(数据准备)主要做三件事:文档解析、切分、向量化入库。文档解析要把 PDF、Word、HTML 这些格式统一转成纯文本,这一步坑特别多,后面会细讲。切分是把长文档切成合适大小的片段,向量化是用 embedding 模型把每个片段转成向量存进向量库。
在线阶段(查询响应)的链路是:用户提问 → 查询改写 → 向量化 → 检索召回 → 重排 → 拼装上下文 → 交给大模型生成 → 返回答案。这里面每一步都有优化空间,也是面试官最爱追问的地方。
提示:面试时如果被问"RAG 流程",别只答"检索+生成",把离线在线两个阶段分开讲,再点出查询改写和重排这两个容易被忽略的环节,深度立刻就出来了。
2. 文档切分:最容易被低估的关键环节
2.1 为什么切分策略直接决定检索上限
切分这件事,新手往往觉得随便切切就行,但实际上它是整个 RAG 系统里最影响最终效果的环节之一。道理很简单:检索的最小单位就是切分出来的 chunk,如果 chunk 切得不好,后面检索再精准、重排再厉害,也救不回来。这就像你把一本书撕成碎片去查资料,如果碎片撕得乱七八糟,一句话被拦腰截断,你查到了也读不懂。
我见过一个真实案例,某团队做产品手册问答,一开始用固定长度 500 字符硬切,结果用户问"XX 功能的参数怎么设置",检索出来的片段全是半截话,模型拼出来的答案驴唇不对马嘴。后来改成按段落和标题层级切,效果立刻上了一个台阶。所以切分不是技术活,是对文档结构的理解活。
2.2 四种主流切分策略的取舍
我把常见的切分策略整理成下面这张表,方便你根据文档类型快速选型。
| 策略 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 固定长度切分 | 按字符/token 数硬切 | 实现简单、速度快 | 易截断语义 | 结构松散的纯文本 |
| 递归切分 | 按分隔符优先级逐层切 | 尽量保持语义完整 | 参数需调 | 通用文档 |
| 语义切分 | 按语义相似度找边界 | 语义最完整 | 计算成本高 | 高质量要求场景 |
| 结构化切分 | 按标题/段落/表格切 | 保留文档结构 | 依赖解析质量 | 手册、论文、合同 |
实际项目里我用得最多的是递归切分,因为它是个很好的平衡点。它的逻辑是:先尝试用段落分隔符(比如双换行)切,如果某段还是太长,就用单换行切,再长就用句号切,以此类推。这样能最大程度保证语义完整,同时控制 chunk 大小。
2.3 chunk 大小和重叠怎么定
这是面试高频追问点。chunk 太大,检索时噪声多,模型容易被无关信息干扰;chunk 太小,语义不完整,检索到的片段可能缺上下文。那到底多大合适?
我的经验值是256 到 512 个 token之间,具体要看文档密度。技术文档信息密度高,可以小一点;叙述性文档可以大一点。但这个不是拍脑袋定的,正确做法是做 A/B 测试:准备一批标准问答对,用不同 chunk 大小跑一遍,看召回率和最终答案质量,选最优的。
重叠(overlap)的作用是防止关键信息正好卡在切分边界上被切断。一般设置成 chunk 大小的10% 到 20%。比如 chunk 是 500 token,overlap 设 50 到 100 token。overlap 太大浪费存储和检索开销,太小起不到保护作用。
# 递归切分的典型参数配置(以常见文本切分库为例) chunk_size = 512 # 每个 chunk 的目标 token 数 chunk_overlap = 64 # 相邻 chunk 的重叠 token 数 separators = ["\n\n", "\n", "。", "!", "?", " ", ""] # 分隔符优先级注意:中文和英文的切分逻辑不一样。英文按空格和标点切很自然,中文没有空格,得靠标点符号和语义。如果你的文档是中英混排,分隔符列表里中英文标点都要放进去,否则切出来的 chunk 会很奇怪。
2.4 结构化文档的特殊处理
PDF 和表格是切分里的老大难。PDF 解析出来经常是乱的,表格更是重灾区。我的处理原则是:表格单独处理,不要和正文混在一起切。
具体做法是,解析阶段就把表格识别出来,转成 Markdown 表格或者自然语言描述,单独作为一个 chunk 存储,并在元数据里标记类型为"table"。检索时如果命中表格 chunk,可以给更高的权重,因为表格往往包含精确的参数和数值,用户问这类问题时命中表格的答案质量远高于正文。
对于带层级标题的文档(比如产品手册),我会在切分时把标题路径也带上,比如"第三章 > 3.2 参数配置 > 3.2.1 超时设置",作为 chunk 的元数据。这样检索时不仅能匹配内容,还能匹配标题,召回精度会明显提升。
3. 向量化与检索:召回质量的核心战场
3.1 embedding 模型怎么选
embedding 模型负责把文本转成向量,它的质量直接决定检索的准不准。选型时我主要看三个维度:语言支持、维度、性能。
中文场景下,我一般优先考虑在中文语料上训练过的模型,因为纯英文模型对中文语义的捕捉会打折扣。维度方面,常见的有 768、1024、1536 等,维度越高表达能力越强,但存储和计算成本也越高。实际项目里 768 到 1024 维基本够用,除非你的语料特别复杂。
还有一个容易被忽略的点是查询和文档要用同一个模型。有些团队图省事,文档用 A 模型编码,查询用 B 模型,结果向量空间对不上,检索效果惨不忍睹。这个坑一定要避开。
3.2 向量检索和关键词检索的互补
纯向量检索有个天然短板:它对精确匹配不敏感。比如用户问"错误码 E5021 怎么解决",向量检索可能召回一堆语义相近但错误码不同的内容,因为向量捕捉的是语义相似度,不是字面匹配。
解决办法是混合检索(Hybrid Search),把向量检索和关键词检索(比如 BM25)结合起来。向量检索负责语义召回,关键词检索负责精确召回,两路结果融合后效果通常比单路好很多。融合方式常见的有加权求和和 RRF(Reciprocal Rank Fusion,倒数排名融合)。
RRF 的好处是不需要归一化分数,直接按排名融合,实现简单且鲁棒。它的公式是每个文档的得分等于它在各检索结果中排名的倒数之和,排名越靠前得分越高。
# RRF 融合的简化实现 def rrf_fusion(vector_results, keyword_results, k=60): scores = {} for rank, doc_id in enumerate(vector_results): scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank + 1) for rank, doc_id in enumerate(keyword_results): scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank + 1) # 按融合得分降序返回 return sorted(scores.items(), key=lambda x: x[1], reverse=True)3.3 重排为什么能显著提升效果
检索阶段为了速度,通常用的是双塔模型(查询和文档分别编码,算向量相似度),这种架构快但精度有限。重排阶段就可以上交叉编码器(Cross-Encoder),把查询和文档拼在一起送进模型打分,精度高得多,代价是慢。
所以标准做法是:检索阶段召回 Top 50 到 100 个候选,重排阶段用交叉编码器精排出 Top 3 到 5 个送给大模型。这样既保证了速度,又保证了精度。我实测下来,加了重排之后,最终答案的准确率通常能提升 10 到 20 个百分点,这个投入产出比非常高。
提示:重排模型的选择也要看语言。中文场景选中文重排模型,别直接拿英文的用。另外重排的候选数量不是越多越好,50 到 100 是个比较舒服的区间,再多收益递减还拖慢速度。
3.4 元数据过滤的妙用
很多检索效果差的问题,其实不是模型不行,而是没用好元数据。比如用户问"2024 年的销售政策",如果你在检索时能先用元数据把时间范围过滤到 2024 年,再在剩下的文档里做向量检索,精度会高很多。
我习惯在入库时给每个 chunk 打上丰富的元数据:来源文档、章节、时间、类型、权限标签等。检索时根据查询意图动态加过滤条件。这一步在工程上不难,但对效果的提升立竿见影,面试时能主动提到这点,说明你有实战经验。
4. 查询改写:让用户的问题更好被检索
4.1 为什么原始查询往往不是好查询
用户提问的方式和文档的表述方式经常对不上。用户问"这个功能咋用",文档里写的是"操作步骤说明",字面差异大,向量检索可能就召不回正确内容。这就是所谓的查询-文档语义鸿沟。
查询改写的目标就是把用户的口语化、模糊的提问,改写成更接近文档表述、更适合检索的形式。这一步做得好,召回率能提升一大截,而且它是在检索之前做的,成本相对低。
4.2 几种实用的改写手法
我常用的改写手法有这么几种,可以组合使用。
同义扩展:把查询里的关键词扩展成多个同义词。比如"报错"扩展成"报错、错误、异常、失败",这样无论文档用哪个词都能召回。
假设文档生成(HyDE):让大模型先根据查询"编"一个假想的答案文档,然后用这个假想文档去检索。原理是假想文档的表述更接近真实文档,检索效果更好。这个方法在语义鸿沟大的场景下特别有效。
查询分解:复杂问题拆成多个子问题分别检索。比如"对比 A 和 B 两个方案的优缺点",拆成"A 的优点""A 的缺点""B 的优点""B 的缺点"四个子查询,分别检索后再综合。
多查询生成:让模型从不同角度生成多个版本的查询,每个都去检索,结果合并。这能覆盖用户可能的不同表述方式。
# 多查询生成的提示词示例 prompt = """你是一个检索助手。请针对下面的用户问题, 从不同角度生成 3 个语义相同但表述不同的检索查询, 每行一个,不要编号,不要解释。 用户问题:{query} """4.3 改写不是越多越好
这里有个坑要提醒:查询改写会引入额外的大模型调用,增加延迟和成本。而且改写本身也可能出错,把原本正确的查询改歪了。所以我的建议是按需启用:简单直接的查询不用改写,复杂或模糊的查询才走改写流程。
另外改写后的多个查询检索结果合并时,要注意去重和排序,别让重复内容占满上下文窗口。我一般会用一个轻量的去重逻辑,按内容相似度过滤掉高度重复的片段。
5. 生成阶段:怎么让模型用好检索到的内容
5.1 上下文拼装的讲究
检索到相关片段后,怎么把它们拼进提示词里,也是有讲究的。我见过不少项目就是把片段一股脑塞进去,结果模型被无关信息干扰,答案质量反而下降。
我的做法是:按相关性排序,把最相关的放最前面和最后面。这是有心理学依据的,模型对上下文首尾的信息注意力更集中,中间容易忽略。所以别把最重要的内容埋在中间。
每个片段前面加上来源标记,比如"[来源1]""[来源2]",这样模型在生成时可以引用来源,也方便后续做溯源。如果片段之间有冲突,可以在提示词里明确告诉模型"如果信息冲突,以最新的为准"或者"请指出冲突"。
5.2 提示词模板的设计要点
一个好的 RAG 提示词模板,核心是约束模型只基于给定资料回答。我常用的模板结构是这样的:
你是一个严谨的问答助手。请严格根据下面提供的资料回答问题。 要求: 1. 只使用资料中的信息,不要编造资料之外的内容 2. 如果资料中没有相关信息,直接回答"根据现有资料无法回答" 3. 回答时标注信息来源编号 4. 保持简洁,直接回答问题 资料: {context} 问题:{question} 回答:这个模板的关键在于第 2 条,它给了模型一个"拒答"的出口。很多幻觉就是因为模型没有退路,只能硬编。给了退路之后,它在资料不足时会老实说不知道,幻觉率明显下降。
5.3 引用溯源怎么做
引用溯源是 RAG 相比纯生成的一大优势,也是很多企业场景的硬需求。实现方式是在拼装上下文时给每个片段编号,要求模型在回答里带上编号,然后前端根据编号映射回原文位置。
这里有个细节:模型有时候会引用错编号,或者引用了不存在的编号。所以后端要做校验,把无效引用过滤掉。更严谨的做法是让模型输出结构化的引用信息(比如 JSON),而不是让它自由发挥。
注意:别指望模型 100% 准确引用。我实测下来,即使提示词写得很清楚,引用准确率也就 85% 到 95% 之间。所以关键场景下,引用信息最好做二次校验,别直接展示给用户。
6. 评估与优化:怎么知道系统好不好
6.1 RAG 评估的两个层面
RAG 评估要分检索和生成两个层面来看,混在一起评估是新手常犯的错误。
检索层面看的是:该召回的内容召回了吗(召回率)、召回的内容相关吗(准确率)、排在前面的相关吗(MRR、NDCG)。生成层面看的是:答案忠实于资料吗(忠实度)、答案回答了问题吗(相关性)、有没有编造(幻觉率)。
分开评估的好处是,当效果不好时你能快速定位是检索的问题还是生成的问题。如果检索召回率就低,那再怎么优化生成也没用,得先修检索。
6.2 没有标注数据怎么评估
现实是很多项目一开始没有标注数据,这时候可以用大模型当裁判(LLM as a Judge)。让一个能力强的模型根据资料和问题,给生成的答案打分,评估忠实度和相关性。
这个方法的成本比人工标注低得多,而且可以批量跑。但要注意,裁判模型本身也可能有偏差,所以最好用多个模型交叉验证,或者人工抽检一部分校准。我一般会先用大模型跑一轮,挑出得分低和得分高的样本各一批人工复核,确认裁判的可靠性后再大规模用。
6.3 常见问题的排查思路
下面这张表是我整理的 RAG 常见问题速查表,基本覆盖了实际项目里 80% 的故障场景。
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 召回不到正确内容 | 切分不合理、embedding 不匹配 | 检查 chunk 质量、换 embedding 模型 |
| 召回了但答案不对 | 上下文噪声多、提示词约束弱 | 加重排、强化提示词约束 |
| 答案有幻觉 | 资料不足、模型硬编 | 加拒答机制、补充资料 |
| 响应太慢 | 重排候选太多、模型太大 | 减少候选数、换小模型 |
| 精确查询失效 | 纯向量检索不敏感 | 引入关键词混合检索 |
| 多轮对话答非所问 | 查询没结合历史 | 加查询改写,融入对话历史 |
排查的核心思路是分段定位:先看检索结果对不对,再看生成结果对不对。检索不对就修检索,检索对但生成不对就修提示词或换模型。别一上来就怀疑模型不行,大部分问题其实出在数据和检索环节。
6.4 持续优化的几个方向
RAG 系统上线不是终点,而是起点。我一般会从这几个方向持续优化。
建立反馈闭环:把用户的点赞点踩、追问、修正都收集起来,作为优化依据。被点踩的样本是金矿,分析它们能发现系统性的问题。
定期更新知识库:文档会过期,知识库要定期同步。我建议做成自动化的增量更新,别靠人工。
A/B 测试驱动:任何优化(换模型、调参数、改提示词)都别拍脑袋,用 A/B 测试验证。准备一批标准测试集,改动前后各跑一遍,用数据说话。
关注长尾查询:高频查询好优化,长尾查询才是难点。定期分析日志里的长尾查询,看看哪些召不回、答不好,针对性补充资料或优化改写。
7. 面试实战:六个高频问题的回答框架
前面讲了原理和工程,这一节我把面试里最常被问的六个问题整理成回答框架,你可以直接拿去用,但记得结合自己的项目经历填充细节,别背模板。
问题一:介绍一下 RAG 的原理。回答框架:先说它解决什么问题(知识截止、幻觉、私有知识),再说完整链路(离线:解析切分向量化;在线:改写检索重排生成),最后点出它和微调的区别。别只答"检索+生成",那样太浅。
问题二:chunk 怎么切?回答框架:先讲切分为什么重要(决定检索上限),再讲几种策略的取舍(固定、递归、语义、结构化),然后给出你的经验值(256-512 token,overlap 10%-20%),最后强调要 A/B 测试验证,别拍脑袋。
问题三:检索效果不好怎么办?回答框架:先分段定位(是检索问题还是生成问题),检索问题再细分(切分、embedding、检索方式),然后给出优化手段(混合检索、重排、元数据过滤、查询改写),最后强调用数据验证。
问题四:怎么减少幻觉?回答框架:从三个层面答。数据层面保证资料质量,提示词层面加约束和拒答机制,生成层面加引用溯源和校验。再补充一句"幻觉不可能完全消除,只能控制到可接受范围",显得你务实。
问题五:怎么评估 RAG 系统?回答框架:分检索和生成两个层面,检索看召回率准确率,生成看忠实度相关性。没有标注数据就用大模型当裁判,但要人工校准。最后强调评估要持续做,不是一次性的事。
问题六:RAG 有哪些优化方向?回答框架:按链路顺序讲,查询改写、混合检索、重排、上下文压缩、提示词优化、评估闭环。每个方向点一句核心思路,别展开太细,面试官感兴趣会追问。
提示:面试时最忌讳的是只背结论不讲逻辑。面试官问"为什么",你要能说出背后的权衡。比如问"为什么用重排",你要答"因为检索阶段的双塔模型精度有限,重排用交叉编码器精度高但慢,所以两阶段配合兼顾速度和精度",这样才显得你真懂。
8. 我踩过的那些坑
最后分享几个我在实际项目里踩过的坑,都是文档里不会写、但特别容易中招的。
坑一:PDF 解析出来的文本是乱的。很多 PDF 是扫描件或者排版复杂,直接解析出来顺序全乱。解决办法是先用 OCR 或者专门的 PDF 解析工具,解析完人工抽检一批,确认质量再入库。别偷懒,数据质量是地基。
坑二:embedding 模型换了但没重新入库。有次我们换了 embedding 模型,只改了查询端的编码,忘了重新编码文档库,结果检索效果暴跌。记住:换 embedding 模型必须全量重新入库,查询和文档必须用同一个模型。
坑三:重排候选数设太大拖慢响应。一开始为了追求效果,重排候选设了 200,结果响应时间翻倍,用户体验很差。后来降到 50,效果几乎没损失,速度回来了。参数不是越大越好,要找到平衡点。
坑四:提示词里没给拒答出口。早期提示词只写了"根据资料回答",没写"资料不足时怎么办",结果模型遇到资料没有的问题就硬编。加上拒答机制后,幻觉率明显下降。
坑五:忽略了多轮对话的查询改写。用户第二轮问"那它呢",如果直接把"那它呢"拿去检索,肯定召不回东西。必须结合对话历史把查询补全成完整问题再检索。这个坑在多轮场景里特别常见。
这些坑说到底都指向一个道理:RAG 不是把几个组件拼起来就完事,每个环节的细节都影响最终效果。面试官想考察的,正是你对这些细节的敏感度和处理经验。把原理吃透,把工程做扎实,再准备几个真实的踩坑故事,面试基本就稳了。