RAG面试篇6
10. 你使用 RAG 给大模型一个输入系统是怎样的工作流程当你把一个问题输入给 RAG 系统它不会直接丢给大模型而是先经历一套「检索 - 整理 - 生成」的流水线。具体来说系统先对问题做预处理改写成更适合检索的形式然后把问题向量化去向量库里找最相关的文档片段再经过精排筛掉噪音最后把筛选出来的片段和问题一起拼成 Prompt 交给大模型大模型基于这些「参考资料」生成最终答案。整个流程的核心目标只有一个让大模型在回答时有真实的知识作为依据而不是凭空发挥。为什么不能直接把问题扔给大模型先说为什么需要这套流程。大模型的上下文窗口是有限的不可能把整个知识库都塞进去而且没有外部知识依据时大模型只靠参数里的记忆回答容易出现幻觉。所以 RAG 的在线流程本质是一个「精准取件」的过程从海量知识里找到和这个问题最相关的那几段然后再让大模型在这个「小范围」里作答。你可能会想为什么不把用户问题直接拿去向量库里搜搜完就完了因为在实际业务中用户的提问往往是口语化的、带指代的、甚至有歧义的直接拿去检索效果极差。所以整个在线流程按顺序分为以下几步每一步都在为下一步准备更好的输入。第一步Query 预处理Query Rewrite为什么要把 Query 预处理放在第一步因为用户原始提问的质量决定了后续所有环节的天花板。如果输入就差后面再怎么精排、再怎么拼 Prompt 都是白搭。用户的提问往往是口语化的甚至带着指代比如「上次那个方案怎么样」这种问题离开对话上下文完全没法检索。另外就算问题表达清楚用词和知识库里文档的用词可能完全不一样直接拿去检索命中率会很低。这一步通常让一个小模型对原始问题做改写。常见技术有几种。一是简单改写把口语化问题改写成更正式、独立完整的检索句。二是 HyDEHypothetical Document Embeddings让 LLM 先「假设」一个可能的答案用这个假设答案的向量去检索。你可能会觉得奇怪不用问题去搜反而用假设答案去搜原因很简单问题和答案的用词往往差异很大但假设答案和真实答案的用词更接近它们的语义空间天然更匹配命中率往往更高。三是多角度扩写把同一个问题扩展成 3-5 种不同表述分别检索后合并结果覆盖面更广。第二步Query Embedding问题向量化Query 改写好了接下来要把它转换成向量才能去向量库里搜索。把处理后的问题用 Embedding 模型转成向量这一步本身很简单但有一个容易忽略的细节。很多人以为随便选一个 Embedding 模型就行了其实不然必须用和离线建库时完全相同的 Embedding 模型。为什么因为不同 Embedding 模型在训练时见过的数据、目标函数、输出维度、向量空间的形状都不一样模型 A 让「苹果手机」和「iPhone」的向量落在空间里的某个方向模型 B 完全有可能让它们落在另一个方向甚至连维度数都对不上A 是 1024 维B 是 1536 维根本没法算距离。用 A 模型建的库如果用 B 模型来检索两边的向量就像在不同坐标系里距离计算完全没有意义检索结果会一塌糊涂。这就像你用经纬度定位但一个系统用的是北京坐标系另一个用的是 WGS84 坐标系数值看起来差不多但实际位置差了十万八千里。这也是为什么一旦换 Embedding 模型整个知识库必须重建——老向量和新查询不在同一套坐标系里没法兼容。第三步向量检索ANN 搜索 多路召回拿着问题向量去向量数据库里做近似最近邻搜索ANN找出余弦相似度最高的 Top-K 个文档片段。这一步速度非常快即使百万量级的向量库通常几十毫秒就能返回结果。但工程实践中只用向量检索这一路往往不够。所以这一步通常同时进行多路召回向量检索负责捕获语义相似性BM25/全文检索负责捕获关键词精确匹配两路各有所长。很多人以为向量检索已经够用了为什么还要 BM25因为向量检索对精确词语比如产品型号、专有名词、错误拼写的识别能力比较弱而 BM25 对这些精确匹配反而更在行。比如用户问「LSTM 和 Transformer 的区别」向量检索能找到「序列模型对比」相关的语义内容BM25 能精准命中包含「LSTM」「Transformer」这两个词的文档。多路结果通过 RRF互倒排名融合算法合并最终召回的结果比单路覆盖面更广、质量更高。第四步Rerank 精排理解了第三步的粗排召回你会发现一个自然的问题Top-20 的结果里不可能条条都相关肯定混了一些干扰项进去。Rerank 就是为了解决这个问题的。向量检索是「粗排」召回的 Top-K比如 20 条里可能混入相关度不高的干扰片段。Rerank 模型Cross-Encoder 结构会把用户问题和每个候选片段拼在一起输入深度理解它们之间的语义匹配程度重新打分排序。最终只保留 Top-3 到 Top-5 的高质量片段把噪音过滤掉。你可能会问为什么不直接用 Rerank 模型来检索还要先粗排再精排因为 Rerank 是 Cross-Encoder 结构需要把查询和每个候选拼在一起过模型计算量比向量检索大得多。如果拿它对百万条数据逐一算分延迟完全不可接受。所以工程上采用「粗排筛到几十条精排再从几十条里挑最好的几条」这种两阶段策略兼顾速度和质量。Rerank 整体耗时通常在几百毫秒以内对用户体感影响不大但检索质量的提升非常明显。第五步Prompt 拼装精排后的高质量片段拿到了接下来要把它们和用户的原始问题组装成 Prompt 交给大模型。典型模板大概是这样prompt f 你是一个专业助手请根据以下参考资料回答用户的问题。 如果参考资料中没有相关信息请回答「根据现有资料无法回答」不要自行猜测。 参考资料 [1] {chunk_1} [2] {chunk_2} [3] {chunk_3} 用户问题{user_query} 你可能会觉得这个 Prompt 挺简单的没什么好讲的。但这里面每一条指令都有明确的工程意图。很多人以为只要把检索结果丢给大模型就行了其实 Prompt 拼得不好大模型一样会乱回答。「只根据资料回答」是为了抑制大模型凭记忆发挥的倾向大模型天生喜欢「帮忙」你问什么它都想着要回答哪怕资料里没有相关信息它也会自己编一个出来。「资料没有就说不知道」是防止它在信息不足时强行补全出幻觉内容。参考资料带编号是为了方便后续做引用溯源让用户知道每句话的依据是什么。第六步大模型生成 溯源大模型拿到 Prompt 之后基于参考资料生成答案。到这一步整个 RAG 在线流程的核心工作就完成了。工程实践中通常还会要求大模型在答案里标注每句话来自哪个片段比如「根据资料[1]...」这样用户可以追溯到原始文档验证答案是否准确可信度大幅提升。很多人以为 RAG 生成的答案就是最终答案不需要再验证了其实溯源这一步非常关键。大模型即使在有参考资料的情况下依然可能过度发挥或者误读资料溯源让用户有能力判断哪些内容是可靠的、哪些需要再确认。整个链路串起来在线阶段的耗时分布大概是Query 改写几十毫秒向量检索几十毫秒Rerank 几百毫秒Prompt 组装可忽略不计大模型生成根据输出长度在 1-10 秒不等。工程上常见的优化手段是缓存高频 Query 的检索结果以及把向量检索和 BM25 检索并行执行把两路召回的延迟从串行叠加变成取最大值。正确回答要按顺序把六个步骤讲清楚。第一步 Query 预处理改写、HyDE、多角度扩写把口语化的问题转成适合检索的形式第二步 Query Embedding注意必须用和建库时相同的模型第三步向量检索 多路召回向量检索和 BM25 各有所长通过 RRF 融合结果。第四步 Rerank 精排用 Cross-Encoder 深度理解语义把粗排的噪音过滤掉第五步 Prompt 拼装明确约束 LLM 只根据资料回答、不知道就说不知道第六步生成 溯源标注引用来源提升可信度。11. 请你介绍一下向量检索和关键词检索的区别关键词检索BM25 这类靠的是词频统计看查询词在文档里出现了多少次擅长精确命中向量检索靠的是语义空间里的距离能理解「换了种表达方式的同一个意思」擅长模糊语义匹配。两者各有盲区BM25 遇到同义词就没辙向量检索遇到专有名词、产品型号这类精确词就容易漏。所以 RAG 系统里通常两路都跑向量检索捕获语义相关的内容BM25 精准命中关键词再用 RRF 算法把两路结果合并这样覆盖面比单路宽很多。检索要解决的核心问题检索的本质是给你一段查询从海量文档里找出最相关的那几条。这个「相关」可以有两种理解方式一种是字面意义上的词汇重叠一种是语义层面的意思接近。很多人以为检索就是「搜关键词」其实那只是其中一种理解方式。关键词检索和向量检索分别对应这两种理解各有各的擅长和盲区。理解了这一点后面的内容就顺理成章了。关键词检索字面匹配靠统计先从最直观的方式说起。关键词检索的代表是 BM25Best Match 25这是 Elasticsearch、Lucene 等传统搜索引擎的核心算法。它的基本思路可以用一个类比来理解想象一个巨大的图书管理员他给每本书建了一张「词汇卡片」记录这本书里每个词出现了几次。用户来查「手机 截图」管理员就把包含「手机」的书挑出来再把包含「截图」的书挑出来然后看哪些书同时被挑中、被挑中的词出现频率高这些书就排前面。这个「词汇卡片」系统就是倒排索引它记录的不是「每篇文档里有哪些词」而是反过来记录「每个词出现在哪些文档里」。打分的时候核心看两个因素。第一个是词频TF这个词在这篇文档里出现了几次出现越多说明越相关。第二个是稀缺度IDF这个词在所有文档里有多罕见。你可能会想直接数词频不就行了为什么还要看稀缺度原因很简单「的」「是」「在」这些词在哪篇文章里都高频出现如果只看词频每篇文档都会因为包含这些常用词而拿到高分根本区分不出哪篇真正相关。所以 IDF 的作用就是给常见词降权、给罕见词加权让真正有区分力的词决定排序结果。BM25 在 TF-IDF 基础上又加了饱和度限制防止某个词重复出现太多次后权重无限叠加。# 用 rank_bm25 库做关键词检索的例子 from rank_bm25 import BM25Okapi # 文档库实际使用时是分词后的 token 列表 corpus [ [苹果, 手机, 截图, 方法], [iPhone, 截屏, 教程], [安卓, 手机, 拍照], ] bm25 BM25Okapi(corpus) # 查询也要分词 query [苹果, 手机, 截图] scores bm25.get_scores(query) # 每个文档的 BM25 分数BM25 的优势是对精确词汇命中率极高产品型号「iPhone 15 Pro Max」、专有名词「LSTM」、缩写「RAG」只要文档里有这个词BM25 就能精准找到。但它的劣势同样明显而且刚好是向量检索的优势所在遇到同义词就束手无策。用户查「手机截图」文档里写的是「iPhone 截屏教程」BM25 看到没有任何词汇重叠分数为零直接忽略这篇文档哪怕它正好是最相关的答案。这个致命的盲区直接催生了向量检索的出现。向量检索语义匹配靠 EmbeddingBM25 搞不定同义词那能不能让检索系统「理解意思」而不是「匹配字面」这就是向量检索要解决的问题。它的思路是这样的先用一个 Embedding 模型把每段文本转成一个高维数字向量比如 1024 维的浮点数列表这个向量可以理解成这段文本在「语义空间」里的坐标。语义相近的文本坐标就靠近语义不相关的坐标就离得远。好比给每段文字在地图上钉了个钉子意思越相近的钉子挨得越近。用户查询来了也转成向量然后去找最近的几个钉子。这样就好理解了「苹果手机怎么截图」和「iPhone 如何截屏」这两句话虽然一个字都不一样但经过 Embedding 之后向量的余弦相似度可以高达 0.95。向量检索通过近似最近邻算法ANN在向量库里找和查询向量最近的 Top-K 条速度极快百万量级的向量库通常几十毫秒就能返回结果。那向量检索是不是万能的很多人以为向量检索比关键词检索「高级」所以所有场景都该用向量检索其实不是。向量检索的优势是语义理解能力强能跨越同义词、近义词、不同表达方式的障碍但它的劣势是对精确词汇不敏感产品型号「Pro Max 256GB」、人名、版本号这类词在 Embedding 空间里可能彼此距离并不近向量检索容易把它们混淆在一起反而不如 BM25 直接命中准确。这就是为什么面试官会拿「M4 Pro 芯片参数」这个问题来追问向量检索对这类精确专有名词天然不擅长而 BM25 却能秒找到。两种方式的盲区恰好互补谁也不能替代谁。混合检索两路都跑合并取长补短既然两种方式各有盲区而且刚好互补那最自然的想法就是两路都跑。好比考试既考选择题精确匹配又考问答题语义理解两科综合评分比只考一科更全面。工程实践中的标准做法叫混合检索Hybrid Search同时跑向量检索和 BM25各自召回一批候选然后用 RRFReciprocal Rank Fusion互倒排名融合算法把两路结果合并排序。RRF 的思路很巧妙它不看各路的原始分数只看排名。你可能会问为什么不直接把两路的分数加权平均呢原因是向量相似度0 到 1 的余弦值和 BM25 分数可能是任意正数的量纲完全不同就像拿摄氏度和华氏度直接加在一起没有意义。RRF 用排名的倒数来打分排名越靠前倒数越大最终按总分降序排列。这样两路都认为相关的文档会排在最前面只有一路认为相关的文档也不会被完全丢掉。def reciprocal_rank_fusion(results_list, k60): results_list: 多路检索结果每路是一个 [doc_id, ...] 的有序列表 k: 平滑参数防止排名第 1 的文档权重过大通常取 60 scores {} for results in results_list: for rank, doc_id in enumerate(results): if doc_id not in scores: scores[doc_id] 0 # 排名越靠前rank 越小倒数越大 scores[doc_id] 1 / (rank k) # 按总分降序排列取 Top-K return sorted(scores.items(), keylambda x: x[1], reverseTrue) # 使用示例 vector_results [doc_a, doc_b, doc_c] # 向量检索的排序结果 bm25_results [doc_b, doc_d, doc_a] # BM25 的排序结果 merged reciprocal_rank_fusion([vector_results, bm25_results]) # doc_b 两路都高排doc_a 也两路命中会排在前面这个方案还有一个工程上的好处两路检索可以并行执行总延迟取两路中的最大值而不是两者相加几乎没有额外的时间代价但召回质量比单路明显更好。这也是为什么生产环境的 RAG 系统很少只用纯向量检索混合检索已经成为行业默认做法。