RAG 检索面试十连问:召回率从 92% 掉到 61%,答案句被拦腰截断怎么排查

本文聚焦RAG检索中召回率异常下降(如92%→61%)的系统性排查方法,核心问题常源于文本切分不当导致答案句被截断。内容涵盖检索链路五段拆解、chunk切分策略(按结构而非字数、表格/代码完整性、overlap与元数据)、混合检索与重排机制、查询改写、评估指标(Recall@k/MRR/LLM-as-judge冷启动)、embedding模型升级与索引一致性、以及生产三大高频故障点。强调RAG本质是数据质量工程,排查应遵循“从便宜到昂贵”原则。

你好,我是利威尔xu。

这是「面试八股」系列的第二篇。上一篇我们拆了 Go GMP 调度器的 10 个高频追问;这一篇换赛道,聊 RAG 检索——站内热搜 TOP3 挂了一周的 RAG 词不是没道理的,这个方向现在面试出镜率极高,但绝大多数候选人的答案停在"向量数据库 + top-k"这一层,再往下问两句就露底。

点名一下受众:简历上写了"熟悉 RAG / 熟悉大模型应用开发",被问"召回率掉了怎么查"却只会答"换个 embedding 模型"的后端同学。这篇给你 10 个追问的标准答法,外加一次真实故障的完整排查复盘。

建议先收藏——第十一节附了一个能直接跑的 chunk 切分评估脚本,第十二节是一张「RAG 检索排查速查表」,面试前过一遍,比临时抱佛脚强。(新读者可以先看置顶的学习路线再决定从哪篇入坑。)

一、开场:一次真实的召回率跳水

先讲一个我身边真实发生过的故障(细节做了脱敏)。一个企业知识库问答服务,上线后前两周相安无事,某次运营往库里批量导入了一批新文档,召回率从 92% 掉到 61%——用户问的问题,知识库里明明有答案,就是检索不出来。

团队第一反应是" embedding 模型不行了",折腾两天换模型,召回率纹丝不动。最后定位到的真凶是:新文档的导出格式变了,切分逻辑把一个完整章节从表格中间拦腰截断,答案句横跨两个 chunk,向量语义稀碎,谁也匹配不上。

这个案例是本文的主线,十个追问里有一个算一个,都是这场故障里真实踩过的坑。它说明一件事:RAG 检索的问题,九成出在"文本怎么变成可检索单元"这一步,而不是向量模型本身。

先建一个直觉比喻:图书馆

后面全是术语,先用一个比喻把地图立起来。把 RAG 检索想象成去图书馆查资料:

  • 切分(chunk) = 怎么把一本书裁成一张张"资料卡"。裁太大,一张卡塞了三章内容,读者永远只翻到其中一页;裁太小,一句话半句话一片,上下文断了;
  • 向量检索 = 图书馆的检索系统,你说个大意,它按"意思像不像"给你一排卡片;
  • BM25 关键词检索 = 老式的书名/主题词卡片柜,按字面词匹配;
  • 重排(rerank) = 馆员把你挑出来的一摞卡片逐张翻一遍,把最相关的那几张放到最上面;
  • 生成 = 读者拿着最终几张卡片写摘要。

今天要讲的,就是这五步各自怎么失效、失效了怎么查。图书馆学得一句话:卡片裁歪了,后面检索排得再准也白搭。

二、追问 1:RAG 检索链路由哪几段组成?

标准答法:一条生产级 RAG 检索链路,从用户提问到拿到上下文,至少五段——

  1. 查询理解:指代消解、query 改写(多轮对话必须做);
  2. 检索:向量检索、BM25、或两者混合,各取 top-k;
  3. 融合(混合检索才有):把两路分数归一到同一把尺子上再合并;
  4. 重排:cross-encoder 精排,砍掉明显不相关的;
  5. 组装:按相关性排序拼进 prompt,控制总长度别超窗口。

面试追问 Q:这五段里,哪段最容易被忽视、又最常出问题?
A:查询理解和切分。切分在链路之前(索引期),查询理解在最前面,都不在"检索"这个动作里,但 90% 的"检索不准"最后都定位到这两段。很多人把 RAG 等同于"向量数据库检索",链路是残的。

面试追问 Q:为什么生成阶段也要算进检索质量?
A:因为检索的最终评价标准是"生成的答案对不对"。检索阶段 top-5 里有正确答案但排在第 5,生成模型可能根本没用它——检索排序的颗粒度要细到"前 1-2 条就必须可用"。

三、追问 2:chunk 切分大小怎么定?

标准答法:没有万能值,但有方法论——

  • 按内容结构切,别按字数硬切:优先保住标题层级、表格、代码块、列表的完整性。一个表格被切成两半,两张碎片都不知所云;
  • 常用区间:中文知识库 300-500 字/chunk 是常见起点;FAQ 类可以更短(一问一答正好一个 chunk);技术文档可以到 800-1000 字(要保住代码上下文);
  • 重叠(overlap)10-15%:给相邻 chunk 之间搭桥,缓解"答案恰好横跨边界";
  • 带元数据:每个 chunk 记下来源文档、章节路径、页码——排查时你能立刻知道"哪篇文档的哪一节出的问题",这是第一节那个故障两天才定位的直接原因(没有元数据,只能全文翻)。

面试追问 Q:切太小一定比切太大好吗?
A:不一定。切太小,单 chunk 语义不完整,向量表征质量下降;而且 chunk 数量暴涨,索引和检索开销都上去。切太大的核心问题是"语义被稀释"——一个 chunk 混了三个主题,用户问其中任何一个,相似度都被另外两个主题拉低。所以是"信噪比"问题,不是单纯的大小问题。

面试追问 Q:表格和代码怎么切?
A:表格尽量整张进一个 chunk,超长表格按行切时必须把表头复制到每个分片;代码按语法边界(函数/类)切,别按行数硬切。这两个是实战里最容易被"统一按 500 字切"逻辑毁掉的内容类型。

四、追问 3:top-k 之后为什么还要重排?

标准答法:向量检索(bi-encoder)是"双塔"结构—— query 和文档分别编码成向量再算相似度,两边没见过面,精度有上限;重排模型(cross-encoder)把 query 和文档拼在一起过模型,两者能逐词交互,精度显著更高,但慢,扛不住全库扫描。所以工业界的主流分工是:向量检索用速度换召回(从十万条里捞回一百条候选),cross-encoder 用精度换延迟(在一百条里排出前五)。

面试追问 Q:rerank 一定要上模型吗?
A:不一定。规则重排(关键词命中加权、时间衰减、文档权威度)在很多场景够用,且零成本、可解释。先上规则,发现"相关但排不上来"的 case 集中出现,再上模型。一上来就堆模型,是面试里的减分项——说明没有成本意识。

面试追问 Q:重排会把检索阶段正确的顺序打乱吗?
A:会,所以重排后要留一层"保底策略":比如原向量分特别高的(>0.9)不被重排降到阈值以下,避免精排模型在短文本上的抖动把好结果筛掉。

五、追问 4:混合检索为什么通常优于纯向量?

标准答法:因为两种检索的失效模式不同——

  • 向量检索怕:专业术语、型号、错误码、罕见专有名词("ERR_CONNECTION_REFUSED"这种,语义向量根本分不清它和别的错误码的区别);
  • BM25 怕:同义改写、口语化提问(用户说"怎么退钱",文档写"申请退款",字面零匹配)。

两者叠加,一方的盲区大概率是另一方的主场。生产环境默认上混合,除非你的场景极其特殊。

面试追问 Q:两路分数怎么融合?
A:常用两种。① 加权融合:score = α·norm(vector) + (1-α)·norm(bm25),α 靠小规模标注集调;② RRF(Reciprocal Rank Fusion):score = Σ 1/(k + rank_i),只用排名不用分数,免去归一化烦恼,效果稳,是懒人首选。面试答出 RRF 是加分项。

面试追问 Q:混合检索的成本代价是什么?
A:要维护两套索引,BM25 侧对分词质量敏感(中文要配好分词器),查询延迟是两路之和。流量小的场景可以先纯向量 + 关键词兜底,别过度设计。

六、追问 5:查询很短很口语,检索效果差,怎么办?

标准答法:三个层次的手段,按成本从低到高——

  1. 查询改写(Query Rewriting):用 LLM 把口语改写成"检索友好"的书面查询。“怎么退钱” → “退款流程 申请步骤”;
  2. 同义词/纠错扩展:挂一层业务同义词表(“尾款”=“余款”=“未付金额”),零成本,先做这个;
  3. HyDE(假设性文档嵌入):让 LLM 先生成一段"假想答案",用假想答案的向量去检索——答案和答案在向量空间里比问题和答案更近。适合答案风格统一的场景(如客服知识库)。

面试追问 Q:多轮对话里的指代消解怎么做?
A:送检索前,先用 LLM 把当前轮改写成"独立完整的问题"。“那第二个呢?” → “第二个方案的部署步骤是什么?”。不做这步,多轮 RAG 的检索质量从第二轮开始崩。注意改写要在检索链路之前,和会话历史管理是两回事。

面试追问 Q:查询改写引入 LLM 调用,延迟和成本怎么办?
A:改写用小模型/低温度,几百毫秒级;对首轮(没有历史)的查询跳过改写。真要对齐预算,同义词表 + 规则改写能覆盖七八成场景,LLM 改写只留给长尾。

七、追问 6:召回率突然掉了 30%,排查顺序是什么?

这是本文的复盘重点,标准答法是一条从后往前的链路(和第一节的故障一致):

  1. 先确认是不是检索的锅:抓一批失败 case,看知识库里到底有没有答案原文。没有——是文档缺失,跟检索无关;
  2. 看切分:抽 5-10 个失败 case 的答案句,检查它是否完整落在同一个 chunk 里。横跨边界 = 切分问题(第一节的真凶就在这);
  3. 看索引:新文档入库后索引刷新了吗?有没有入库失败但日志被淹没?embedding 批次任务挂没挂?
  4. 看模型/版本:embedding 模型或向量库版本是否动过?换模型后旧索引没有重建,新旧向量不在一个空间里,相似度全是噪声——这是隐形杀手;
  5. 看查询侧:是不是新上了一批口语化问法?指代消解有没有生效?

面试追问 Q:为什么"换模型"不该是第一步?
A:换模型成本最高(全量重算向量)、风险最大(新旧向量空间不兼容),而收益最不确定。排查要从"最可能且最便宜"的开始:文档在不在、切分全不全、索引新不新,这三个查完,一半的故障已经定位了。面试里体现"先便宜后昂贵"的排查顺序,是工程成熟度的信号。

面试追问 Q:没有失败 case 的日志,怎么回溯?
A:检索链路要把"query、召回的 chunk id 及分数、是否命中"全量落日志。没有这个,排查就是盲人摸象。这也是"可观测性要先于优化"的意思。

八、追问 7:RAG 的评估指标有哪些?没有标注集怎么起步?

标准答法:分两层——

  • 检索层:召回率(Recall@k:正确答案在不在前 k 条)、MRR(第一个正确结果排多前)、nDCG(带相关度分级的排序质量);
  • 生成层:答案忠实度(faithfulness:有没有编)、答案相关性(answer relevance:有没有答非所问)。

面试追问 Q:没有标注集,怎么冷启动评估?
A:三步走。① 先攒 50-100 条真实 query,人工标"标准答案所在文档/chunk",一两天能攒出最小集;② 没有标注时,用检索分数的分布做粗判——分数整体下移、top1 和 top5 分差变小,都是质量劣化的信号;③ 生成层先用 LLM-as-judge 跑批量打分,再人工抽检 10% 对齐口径。注意 LLM 判分有自己的偏好,口径要人工校准过再用。

面试追问 Q:Recall@k 的 k 取多少?
A:看链路后面接什么。直接 top-5 进 prompt,k 就是 5;接了重排,检索层可以放到 50-100,让重排去筛。k 不是越大越好——检得越多,混进来的噪声越多,重排和生成的压力越大。

九、追问 8:embedding 模型要换版本,旧索引怎么办?

标准答法:三个选项,按成本排——

  1. 全量重建(正路):新模型对全部文档重算向量。代价是停机或双写切换,适合数据量可控的场景;
  2. 双索引并行 + 灰度切流:新索引建好先影子流量验证,召回率不劣化再切主。大厂标配;
  3. 不换:先用规则/改写/重排把其他环节做到头,还不够再动模型。

面试追问 Q:为什么不能新旧向量混在一个索引里?
A:不同模型编码出的向量不在同一个语义空间,相似度分数没有可比性。混索引后,新文档的分数虚高/虚低全看运气,检索质量表现为"随机劣化",极难排查。这是第一节故障排查链里第 4 步专门查版本的原因。

面试追问 Q:文档更新了,索引怎么维护?
A:按文档 id 做增量:更新 = 删除旧 chunk 向量 + 插入新 chunk 向量,保证同一文档不会新旧两版同时在索引里。批量导入时要防"部分成功"——成功一半的索引比没建更糟。

十、追问 9:生产环境 RAG 最常翻车的三个点?

标准答法(按出现频率):

  1. 切分毁内容:表格断头、代码截断、章节拦腰切——修复便宜(改切分逻辑 + 全量重建),收益立竿见影;
  2. 多轮不消解:第二轮开始检索质量崩,用户体感"聊得越久越笨"——加一层 query 改写就好;
  3. 没有评估闭环:上线即失明,质量劣化只能靠用户投诉发现——先把日志和最小标注集建起来。

面试追问 Q:这三个点共同的根因是什么?
A:都把 RAG 当"接个向量数据库"的系统集成题在做,没当成"数据质量工程"。RAG 的效果上限在索引期就定了,检索期只是兑现它。

面试追问 Q:如果只允许改一处来提升召回率,你改哪?
A:切分策略 + 元数据。这是性价比最高的一刀,第一节的故障就是证据。

十一、追问 10:贴一段能直接跑的切分评估脚本

前面九问都是"怎么想",这一问给"怎么动手"。面试里如果被问"你怎么验证切分策略",下面这个脚本就是答案的雏形——它做一件事:用同一份文本、不同的 chunk 大小切分,统计"答案句被拦腰截断"的比例,用这个比例当切分质量的代理指标。

# chunk_cut_eval.py —— 评估切分策略把"答案句"拦腰截断的比例
# 用法:python chunk_cut_eval.py  (无需第三方库)
# 思路:把文档按句子切开,指定若干"答案句"(含关键信息的句子),
#       检查每个答案句是否完整落在同一个 chunk 内。断裂率越低,切分越稳。

def split_chunks(text, chunk_size, overlap):
    """按字数滑窗切分,overlap 为重叠字数"""
    step = max(1, chunk_size - overlap)
    return [text[i:i + chunk_size] for i in range(0, len(text), step)]

def cut_rate(text, answer_sents, chunk_size, overlap):
    chunks = split_chunks(text, chunk_size, overlap)
    broken = 0
    for s in answer_sents:
        # 答案句完整出现在某一个 chunk 里 => 未断裂
        if not any(s in c for c in chunks):
            broken += 1
    return broken / len(answer_sents), len(chunks)

if __name__ == "__main__":
    text = open("doc.txt", encoding="utf-8").read()  # 你的文档
    answer_sents = [
        "把这里换成你文档里的关键答案句",
        "每个答案句应该是能独立回答某个问题的完整句子",
    ]
    for size, ov in [(300, 30), (500, 50), (800, 80)]:
        rate, n = cut_rate(text, answer_sents, size, ov)
        print(f"chunk_size={size:4d} overlap={ov:3d} -> 断裂率={rate:.0%}  chunk数={n}")

跑出来的输出长这样:

chunk_size= 300 overlap= 30 -> 断裂率=40%  chunk数=17
chunk_size= 500 overlap= 50 -> 断裂率=20%  chunk数=10
chunk_size= 800 overlap= 80 -> 断裂率=10%  chunk数=7

解读就一条:别信任何"最佳 chunk 大小"的江湖传说,用自己的文档和答案句跑一遍。上面这组数说明 800 字对这文档更友好,但 chunk 少了、单 chunk 主题可能变杂——所以真实决策是"断裂率"和"语义纯度"一起看,这就是第一节故障本可以避免的方式。

面试追问 Q:这个脚本和生产级评估差在哪?
A:差三层。① 答案句靠人工列,生产用标注集;② 只查"截断"不查"语义稀释",生产还要看检索分数分布;③ 单文档,生产要全库跑。但作为 30 分钟上手的最小评估,它比"凭感觉调 chunk"强一个数量级。

面试追问 Q:表格和代码块怎么进这个评估框架?
A:把它们当成"必须完整"的特殊答案句——整张表/整个函数是一个 answer_sent,断裂率直接反映它们有没有被切碎。第一节的故障用这个框架,当天就能定位。

十二、RAG 检索排查速查表

这张表建议收藏,线上召回率出问题按顺序过:

现象头号嫌疑定位手段处理方向
库里有的答案检不出来切分把答案句截断答案句是否完整在同一 chunk按结构切 + overlap + 表格整张保留
新文档入库后质量崩索引没刷新/部分失败查入库批次日志增量删除旧向量 + 重插
换模型后随机劣化新旧向量混索引查模型版本变更记录全量重建或双索引灰度
术语/错误码检不准纯向量检索盲区专有名词 query 单独测上 BM25 混合检索
口语提问效果差查询未改写对比改写前后 top5query rewriting / 同义词表
多轮聊久了变笨指代未消解看第二轮起的 query 日志检索前做指代消解
top-k 里有答案但排后面排序粒度不够看正确答案的 rank上 cross-encoder 重排
不知道质量有没有劣化没有评估闭环检索日志 + 最小标注集先建日志和 50 条标注

💡 本章面试要点

  1. 检索链路五段:查询理解 → 检索 → 融合 → 重排 → 组装;RAG ≠ 向量数据库检索。
  2. 切分按结构不按字数;表格整张进 chunk、代码按语法边界;overlap 10-15%;必须带元数据。
  3. bi-encoder 换召回、cross-encoder 换精度;rerank 先用规则再上模型;RRF 是混合检索的懒人标配。
  4. 召回率暴跌的排查顺序:文档在不在 → 切分全不全 → 索引新不新 → 模型换没换;先便宜后昂贵。
  5. 换 embedding 模型必须重建索引,新旧向量不混库;文档更新走"删旧插新"增量。
  6. 评估两层:检索层 Recall@k/MRR/nDCG,生成层忠实度/相关性;冷启动靠 50 条人工标注 + LLM-as-judge。
  7. 生产三大翻车点:切分毁内容、多轮不消解、没有评估闭环——根子都是把 RAG 当集成题不当数据质量工程。

写在最后

留一个具体问题:你的 RAG 服务,现在能说出"正确答案排在第几名"吗?如果没量过,按第十一节那个脚本的思路,拿 20 条真实 query 跑一次 top-k 命中率——欢迎评论区贴你的数字,我挑几个有代表性的拆解。

如果这篇帮你把"RAG 检索为什么不准、不准了怎么查"理顺了,建议收藏——第十二节那张排查速查表和建议直接抄的评估脚本,下次线上召回率跳水,按表过一遍,比临时换模型快得多。

下篇预告:「面试八股」系列第三篇——Go channel 与 select 底层 12 问:从"两个 goroutine 互发 100 万条消息"讲起,拆环形队列、发送/接收等待队列、select 的随机公平,以及为什么关闭的 channel 会 panic。

作者:利威尔xu | CSDN 专栏《面试八股》

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值