面试中,RAG 的考察早已不是"背定义"就能过关的了。面试官更看重的是你对检索、生成、评估 全链路的理解,以及遇到实际问题时的优化思路。
下面我按基础概念 → 检索优化 → 生成与评估 → 工程实践四个维度,梳理高频考点并配上可运行的代码案例。
一、基础概念篇(必考开场题)
Q1:RAG 和 SFT(微调)的本质区别是什么?
面试官追问:你们项目为什么选 RAG 而不是微调?
核心答题框架:
| 维度 | RAG | SFT(监督微调) |
|---|---|---|
| 知识存储 | 外挂向量库(非参数记忆) | 固化进模型参数(参数记忆) |
| 知识更新 | 换库即更新,秒级生效 | 需重新训练,成本高 |
| 幻觉控制 | 强制基于检索资料回答 | 依赖模型记忆,易幻觉 |
| 适用场景 | 知识高频变化、需溯源 | 固定风格/格式、低延迟 |
加分回答 :> "我们采用 RAG-First 策略,先跑通 RAG 验证想法;如果发现模型语感或输出格式不达标,再用 SFT 打补丁优化风格。一个是查资料,一个是背答案,现在业务需求变得比翻书还快,查资料更划算。"
二、检索优化篇(核心高频区)
Q2:如何解决"检索噪声"和"漏召"问题?
面试官最常问的就是 "检索结果不准怎么办" 。核心思路是混合检索 + 重排序。
代码案例:混合检索(BM25 + 向量检索 + RRF 融合)
python
import numpy as np
from sentence_transformers import SentenceTransformer
from rank_bm25 import BM25Okapi
import jieba
# 1. 准备文档库
documents = [
"RAG是检索增强生成,结合检索和生成解决幻觉问题",
"BM25是经典的关键词检索算法,适合精确匹配",
"向量检索通过Embedding实现语义搜索,适合模糊查询",
"混合检索结合BM25和向量检索,取长补短"
]
# 2. 中文分词(BM25需要)
tokenized_docs = [list(jieba.cut(doc)) for doc in documents]
bm25 = BM25Okapi(tokenized_docs)
# 3. 向量检索(用Embedding模型)
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2') # 支持中文
doc_embeddings = model.encode(documents)
query = "怎么解决RAG的检索不准问题"
query_embedding = model.encode([query])[0]
# 4. 分别执行两种检索
# BM25关键词检索
tokenized_query = list(jieba.cut(query))
bm25_scores = bm25.get_scores(tokenized_query) # 每个文档的BM25分数
# 向量语义检索(余弦相似度)
cosine_sims = np.dot(doc_embeddings, query_embedding) / (
np.linalg.norm(doc_embeddings, axis=1) * np.linalg.norm(query_embedding)
)
# 5. RRF(Reciprocal Rank Fusion)融合排序
def rrf_fusion(bm25_scores, vec_scores, k=60):
"""RRF融合:将排名转化为倒数分数求和"""
# 获取排名(分数越高排名越靠前)
bm25_rank = np.argsort(-bm25_scores)
vec_rank = np.argsort(-vec_scores)
rrf_scores = np.zeros(len(bm25_scores))
for rank, idx in enumerate(bm25_rank):
rrf_scores[idx] += 1 / (k + rank + 1)
for rank, idx in enumerate(vec_rank):
rrf_scores[idx] += 1 / (k + rank + 1)
return rrf_scores
final_scores = rrf_fusion(bm25_scores, cosine_sims)
best_idx = np.argmax(final_scores)
print(f"最佳匹配文档:{documents[best_idx]}")
为什么这样回答加分 :混合检索解决了纯向量检索对精确术语召回率不足的问题------某金融客服系统实践显示,混合检索使问题解决率从 68% 提升至 89%。
Q3:文档分块(Chunking)策略怎么选?
面试官追问:chunk_size 设多大?为什么?
核心答题框架:
| 策略 | 做法 | 优缺点 |
|---|---|---|
| 固定大小分块 | 按 token 数硬切(如 512) | 简单但可能截断语义 |
| 重叠分块 | 固定长度 + 20% 重叠 | 保留上下文连续性 |
| 递归分割 | 按 \n\n → \n → 。 层级切 |
LangChain 默认,语义较完整 |
| 语义分块 | 基于句子 Embedding 相似度合并 | 效果好但计算开销大 |
| 父子块 | 小块检索,返回父块上下文 | 兼顾精度和上下文长度 |
代码案例:LangChain 递归分割器
python
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 递归分割:按层级分隔符切割,保证语义完整性
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500, # 每块约500字符
chunk_overlap=50, # 重叠50字符,防止跨块信息丢失
separators=["\n\n", "\n", "。", "!", "?", ";", ",", " ", ""]
)
chunks = text_splitter.split_text(long_document)
print(f"切分为 {len(chunks)} 个片段")
加分回答 :> "chunk_size 没有万能值,我一般建一个离线评测集,在不同 chunk_size 下测 Recall@K,找到业务最优参数。过小(<256 tokens)会导致上下文断裂,过大(>2048 tokens)会使检索延迟增加 300%。"
Q4:查询改写(Query Rewriting)怎么做?
用户问得模糊时,检索效果会很差。HyDE(Hypothetical Document Embeddings) 是高频考点------让 LLM 先"想象"一份理想答案,再用这个答案去检索。
代码案例:HyDE 查询改写
python
from openai import OpenAI
client = OpenAI()
def hyde_rewrite(query: str) -> str:
"""HyDE:让LLM生成假设性答案,再用答案去检索"""
prompt = f"""请针对以下问题,写一段假设性的标准答案(不需要很長,100字左右):
问题:{query}
假设答案:"""
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}],
temperature=0.3
)
hypothetical_answer = response.choices[0].message.content
return hypothetical_answer
# 原始query:"RAG怎么优化"
rewritten = hyde_rewrite("RAG怎么优化")
# 用 rewritten 去做向量检索,而非直接用原始 query
三、评估篇(体现系统思维)
Q5:如何系统评估 RAG 系统的效果?
面试官追问:你不能只说"感觉还行",要量化。
核心答题框架 :分三层评估:
| 层级 | 评估什么 | 核心指标 |
|---|---|---|
| 检索层 | 该召回的有没有召回到 | Hit@K (命中率)、MRR(平均倒数排名) |
| 生成层 | 答案对不对、有没有幻觉 | Faithfulness (忠实度)、Answer Relevancy 、Context Recall |
| 端到端层 | 用户最终体验 | 任务完成率、点踩率、转人工率 |
代码案例:用 RAGAS 做自动化评估
python
from ragas import evaluate
from ragas.metrics import (
faithfulness, # 忠实度:答案是否基于检索上下文
answer_relevancy, # 答案相关性
context_recall, # 上下文召回率
)
from datasets import Dataset
# 准备评估数据(每条包含:question, answer, contexts, ground_truth)
test_data = Dataset.from_dict({
"question": ["RAG和微调有什么区别?"],
"answer": ["RAG是检索增强生成,通过外挂知识库让模型查资料回答..."],
"contexts": [["RAG全称Retrieval-Augmented Generation,是一种结合检索和生成的技术..."]],
"ground_truth": ["RAG检索外部知识,微调修改模型参数"]
})
# 运行评估
result = evaluate(
dataset=test_data,
metrics=[faithfulness, answer_relevancy, context_recall]
)
print(result)
加分回答 :> "我一般建立 离线+人工+在线 三层闭环:离线跑分做版本对比 → 小流量人工抽查 → 线上 A/B 验证 → 数据回流再优化。只靠人工看,没法规模化迭代。"
四、工程实践篇(拉开差距)
Q6:如何设计高并发 RAG 服务?
面试官追问:10万 QPS 怎么扛?
核心方案:
- 缓存层:Redis 缓存热门 query 结果,FAISS IVF_PQ 索引加速向量检索
- 异步处理:Kafka 消息队列削峰填谷
- 弹性扩展:Kubernetes 自动扩缩容
Q7:RAG 出现幻觉怎么办?
多层防御:
- 置信度过滤:设置答案置信度阈值(>0.85)
- 证据链验证:要求输出必须包含 ≥3 个文档引用
- Prompt 约束 :强制"必须基于提供的参考资料回答,若资料中没有相关信息,请直接说'不知道'"
五、面试避坑总结
| 常见扣分点 | 正确做法 |
|---|---|
| 只会背"加载→分块→嵌入→检索→生成" | 讲清楚每一步的取舍 和为什么这么做 |
| 说"我用 LangChain 搭的"就停了 | 补充向量数据库选型 (Milvus/Chroma/FAISS)和选型理由 |
| 评估只说"看用户反馈" | 分层讲 Hit@K、MRR、Faithfulness 等具体指标 |
| 不考虑工程落地 | 主动提缓存、异步、降级等高并发方案 |
掌握以上 7 个核心问题 + 配套代码 ,足以应对绝大多数 RAG 面试场景。面试时切记:不要背答案,要讲"取舍" ------面试官想听的是你在不同场景下为什么选 A 不选 B。