Agent 面试通关 / 09
RAG 与检索系统:从 chunk 设计到多路召回
RAG 是 Agent 系统的“外部知识接口”。面试官考 RAG 时不想听“向量数据库”四个字——他想知道的是离线怎么切片、在线怎么召回、召回后怎么排序,以及面对复杂查询时怎么做改写和意图识别。
检索基础与原理
Q:RAG 的检索如何实现?
来源:阿里 AI Agent 开发一面
新手答:“用向量数据库做相似度搜索。”
高手答:
RAG 检索分离线和在线两部分。
离线侧负责文档清洗、切片、去重、embedding 计算和向量入库。在线侧的流程是:
- 用 query 编码成向量,去向量库做召回
- 结合 BM25 或关键词检索做混合召回
- 用 rerank 模型重排
- 把最相关的证据拼接给大模型生成答案
# 简化版 RAG 检索流程
query_vec = embed(query)
candidates = vector_db.search(query_vec)
bm25_docs = bm25.search(query)
merged = merge_and_dedup(candidates, bm25_docs)
reranked = reranker.rank(query, merged)
context = "\n".join(doc.text for doc in reranked[:top_k])
answer = llm.generate(query, context)
切片策略直接影响效果:块太大召回不准,块太小上下文断裂。通常用滑窗切割 + overlap,对长文档还会给每个 chunk 带上标题、章节路径、来源元信息,提升召回相关性。
差距在哪:新手只说了“向量数据库”——这是一个组件,不是方案。高手的回答覆盖了离线和在线两条链路,且点出了 chunk 设计这个影响效果的关键因素。面试官考的是你对 RAG 工程的完整认知。
Q:多维度的查询改写是什么?改写遇到需要用户补充信息时怎么设计?
来源:抖音基础架构 Agent 一面
新手答:“用大模型把用户的查询扩展一下。”
高手答:
多维度查询改写是指同时从多个角度对原始 query 做变换,提升召回的全面性:
原始 query:"北京周末带娃去哪玩"
维度1-意图展开:亲子活动 / 儿童乐园 / 博物馆 / 户外游
维度2-实体补全:北京市 → 朝阳区/海淀区/...
维度3-同义改写:"带娃" → "亲子" / "儿童" / "适合小朋友"
维度4-约束提取:时间=周末,地点=北京,人群=有孩子的家庭
需要用户补充信息时,用槽位填充 + 主动澄清机制:
- 意图识别后,检查必要槽位是否完整(比如“订酒店”需要日期、城市、人数)
- 缺失槽位时,不是笼统地问“你能说得更清楚吗”,而是生成具体的选择题:“你想去哪个城市?”或“入住日期是什么时候?”
- 多个槽位都缺时,按优先级排序,先问最关键的
技术实现上,查询改写用模型生成 + 规则约束混合:模型负责语义理解和改写,规则负责实体标准化和格式校验。槽位检测用一个轻量分类器判断缺失情况,避免每次都调大模型。
差距在哪:新手的”扩展一下”是单维度思考。高手的多维度改写覆盖了意图、实体、同义、约束四个角度,且有用户交互的槽位填充机制。面试官考的是你对查询理解链路的完整认知。
追问:Query 改写为什么能提升召回精准度?底层原理是什么?
改写不是”让 query 变长”,而是解决 query 和文档之间的三类鸿沟:
| 鸿沟类型 | 问题 | 改写如何解决 | 示例 |
|---|---|---|---|
| 词汇鸿沟 | 用户用词 ≠ 文档用词 | 同义扩展弥合词汇差异 | “手机卡” → “SIM 卡 / 流量卡 / 电话卡” |
| 意图鸿沟 | 短 query 意图模糊 | 意图展开消除歧义 | “苹果” → “Apple 公司 / 苹果水果”(结合上下文选择) |
| 粒度鸿沟 | query 太宽泛或太细碎 | 拆分/合并调整检索粒度 | “怎么部署” → “Docker 部署 + K8s 部署 + 物理机部署” |
为什么多维度改写比单次改写效果好:单次改写只能沿一个方向扩展,可能强化了某个意图但遗漏了其他。多维度改写用多条路径并行检索再合并,Recall 提升显著(实测 +15~25%),但 Precision 可能下降——所以改写后必须接 Rerank 精排过滤噪声。
Q:并行化意图识别是什么?为什么要并行化?如何实现的?
来源:抖音基础架构 Agent 一面
新手答:“用多线程跑意图分类。”
高手答:
并行化意图识别是指同时执行多个意图维度的判断,而不是串行地先判大类再判小类。
串行方案的问题是链路长、延迟高。比如先判“是不是搜索意图” → 再判“搜索什么品类” → 再判“有没有比较意图”,三级串行加起来可能 300ms+。而且前一级判断错了,后面全错——错误级联放大。
并行化把多个维度同时发出去:
┌→ 意图大类分类器(搜索/导航/交易)
user query ──┼→ 品类识别器(美食/旅游/购物/...)
├→ 行为意图检测器(比较/推荐/查询/...)
└→ 情感倾向检测器(正面/负面/中性)
每个分类器独立运行,结果汇总后做融合决策。实现关键点:
- 各分类器用异步并发执行(
asyncio.gather或线程池),总延迟 = 最慢的那个 - 每个分类器可以用不同规模的模型——简单维度用小模型,复杂维度用大模型
- 设置超时兜底:某个分类器超时不影响其他维度,用默认值填充
差距在哪:新手的“多线程”只答了实现手段,没答为什么要并行化。高手先解释了串行方案的问题(延迟高 + 错误级联),再给出并行化的架构和融合策略。面试官考的是性能优化思维。
Q:讲一下项目里召回的流程
来源:抖音基础架构 Agent 一面
新手答:“用向量搜索召回相关文档。”
高手答:
召回是一个多路召回 → 合并去重 → 精排的三阶段流程:
第一阶段:多路召回
┌→ 向量召回(语义相似度,覆盖同义表达)
改写后 query ──┼→ 关键词召回(BM25,覆盖精确匹配)
├→ 标签召回(实体/品类标签匹配)
└→ 图召回(知识图谱关联路径)
每路召回各取 top-K,各有优势:向量召回抓语义,关键词召回抓精确词,标签召回抓结构化属性,图召回抓实体关系。
第二阶段:合并去重
多路结果合并,同一文档被多路召回的加分。去重用文档 ID 做精确去重 + 内容指纹做近重复去重。
第三阶段:精排
用 Cross-Encoder 或精排模型对候选集做细粒度相关性打分。精排模型能看到 query 和文档的交互特征,精度远高于召回阶段的双塔模型。关键工程细节:每路召回的 K 值怎么定——K 太小漏结果,K 太大精排压力大,通常根据各路历史准确率动态调整。
差距在哪:新手只知道向量检索一路。高手的多路召回 + 精排是搜索系统的标准范式,面试官考的是你对召回-精排两阶段架构的完整理解。
Q:如果 RAG 召回了很多相互矛盾的文档,Agent 应该怎么处理,而不是直接让模型自己总结?
来源:腾讯大模型应用开发二面
新手答:“让模型自己综合判断。”
高手答:
不能直接把矛盾文档一股脑丢给模型让它自己“综合”,那样很容易生成一个看起来圆滑但实际上没有依据的答案。
更合理的做法是先做证据归一化和冲突检测:
- 分组:按来源、时间、可信度对召回文档分组
- 冲突检测:抽取同一字段的不同取值,判断冲突类型——是时间差异导致的,还是来源本身互相打架
- 冲突消解:
- 时间敏感信息 → 新版本优先
- 来源权威性不同 → 官方文档优先
- 无法判断 → 明确告诉用户存在冲突,说明目前最可信的依据
Agent 在这里更像证据调解器,而不是万能总结器。核心是在模型生成前就把冲突处理好,而不是让模型在矛盾信息中自己编一个“看起来合理”的答案。
差距在哪:新手直接把矛盾丢给模型——这会生成看似合理但无依据的答案。高手在模型生成前加了冲突检测和消解层,按时间、权威性、可判断性三个维度做处理。面试官考的是你有没有意识到 RAG 不只是“召回 + 生成”,中间还需要证据治理。
检索算法与微调
Q:Embedding 和 ReRank 模型具体怎么做的微调?
来源:腾讯 AI 应用开发 【腾讯AI应用开发一面追问:重排序完整实现流程】
新手答:“用自己的数据训练一下。”
高手答:
Embedding 模型微调:
目标是让模型在业务领域里,把语义相近的 query 和文档映射到向量空间中的相近位置。
训练数据格式:(query, positive_doc, negative_doc) 三元组。hard negative 越难越好——随机采样的 negative 太简单,模型学不到有区分度的表示。用 BM25 或当前模型 top-K 中的非相关文档做 hard negative。
常用 loss:
- MultipleNegativesRankingLoss(sentence-transformers 最常用):batch 内其他 query 的 positive 自动作为 negative,不需显式构造
- InfoNCE / Contrastive Loss:拉近 positive pair,推远 negative pair
- Triplet Loss:
max(0, d(q, pos) - d(q, neg) + margin)
框架:sentence-transformers 最成熟,支持 BGE、E5、GTE 等预训练模型的微调。
ReRank 模型微调:
ReRank 是 Cross-Encoder——输入是 (query, doc) 拼接后一起过模型,输出相关性分数。比 Embedding 双塔精度高,但计算量大,只用于精排。
训练数据:(query, doc, label),label 是相关性分数或 0/1 标签。Loss 用 BCE 或 MSE。
微调关键细节:
- Hard Negative Mining:negative 质量决定微调效果
- 评估指标:用 Recall@K、MRR、NDCG 在验证集上评估,不是看 loss 降了就行
- 防止过拟合:微调轮数不宜过多(1-3 epoch),否则通用检索能力退化
差距在哪:新手只说了“用数据训”。高手覆盖了完整链路——数据构造(三元组 + hard negative)、loss 选择、框架、评估、防过拟合。面试官考的是你有没有真正微调过检索模型。
Q:双路召回的 TopK,K 是如何确定的?有没有试过一个多召回点、一个少召回点?
来源:腾讯 AI 应用开发
新手答:“K 就取 10 或 20。”
高手答:
K 值要根据召回路的特性和下游精排的承载能力来定。
BM25 关键词召回:K = 10~20(精确匹配路,召回量不需要太大)
Embedding 向量召回:K = 20~50(语义路,覆盖面要大一些)
ReRank 精排后最终 TopK:K = 3~5(给模型的上下文不宜太多)
K 值调优方法:Grid Search 在评估集上搜索最优组合——固定一路 K,遍历另一路(5、10、20、30、50),合并去重后精排,用 Recall@K 和最终生成质量评判。
一多一少的策略:实际中确实会对不同路设不同 K:
- 向量路 K 大、BM25 路 K 小:向量覆盖面广但精度稍低,多召回交给 ReRank 筛;BM25 精确率高,少量就够
- 反过来也有场景:精确查询(订单号、型号)时 BM25 路 K 调大
关键权衡:K 越大覆盖率越高但噪声也多(ReRank 负担重、延迟增加);K 越小精度高但可能漏掉相关文档。生产环境通常还有延迟约束(“总检索时间不超过 200ms”),K 值要在 Recall 和延迟之间找平衡。
差距在哪:新手随便定一个 K。高手有系统的调优方法论——Grid Search + 不同路不同 K + 延迟约束。面试官考的是你调 RAG 参数时有没有数据驱动的方法。
Q:如何用通俗易懂的方式向非技术人员解释 RAG?有没有好的类比?
来源:携程 Agent 开发实习一面
新手答:“就是让模型先搜索再回答。”
高手答:
给非技术同事解释 RAG,我会这么说:
一句话版本:RAG 就是让 AI 在回答问题之前,先去翻资料,而不是纯靠记忆回答。
图书馆类比(最直观):
flowchart LR
subgraph no_rag["❌ 没有 RAG"]
direction LR
Q1["你提问"] --> E1["专家凭记忆回答"]
E1 --> R1["知识可能过时\n细节可能记错\n还可能自信地编答案"]
end
subgraph with_rag["✅ 有 RAG"]
direction LR
Q2["你提问"] --> S["检索员去书架\n找最相关的资料"]
S --> E2["专家翻阅资料后回答"]
E2 --> R2["答案有据可查\n知识随时可更新"]
end
核心价值(三点):
- 知识实时性:模型训练数据有截止日期,但外部知识库可以随时更新——今天新上线的产品文档,明天就能被检索到
- 可追溯性:回答附带引用来源,用户可以点击查看原文,建立信任
- 降低幻觉:有资料做依据,模型不容易“编”答案
主要应用场景:企业知识库问答(内部文档/FAQ)、客服系统(产品手册检索)、法律/医疗等需要精确引用的领域、代码文档助手。
差距在哪:新手的解释太技术化,非技术人员听不懂。高手用图书馆类比把 RAG 的三个角色(用户 → 检索员 → 专家)讲清楚了,再用三点核心价值说明“为什么需要”。面试官考的不只是你懂不懂 RAG,还考你能不能把复杂概念讲给不同背景的人听——这是工程师的沟通能力。
Q:如何快速上手一个没接触过的技术(如向量数据库)?
来源:携程 Agent 开发实习一面
新手答:“看官方文档,跑个 Demo。”
高手答:
快速上手一个新技术,我的方法论是“倒金字塔学习法”——从应用场景倒推,不从底层原理开始:
第一步:搞清楚“它解决什么问题”(30 分钟)
不是先看原理,而是先理解:传统方案的痛点是什么?向量数据库比传统数据库多解决了什么?答案是——传统数据库只能精确匹配,而向量数据库能做语义相似度检索。这一步决定了你后面学的所有东西有没有方向感。
第二步:跑通一个最小可用的 Demo(2-3 小时)
选一个主流方案(如 Milvus / Qdrant / Chroma),跑通“文本 → Embedding → 入库 → 查询 → 返回结果”的最短路径。不要一开始就研究分布式部署、索引类型选择这些细节。
# 最小 Demo:Chroma
import chromadb
client = chromadb.Client()
collection = client.create_collection("demo")
collection.add(documents=["北京今天晴天", "上海明天下雨"], ids=["1", "2"])
results = collection.query(query_texts=["天气怎么样"], n_results=1)
第三步:对标项目需求,补齐关键知识点(1-2 天)
Demo 跑通后,对照项目需求列一个清单:需要什么索引类型(HNSW / IVF)?数据量级多大?需不需要过滤?需不需要持久化?然后按需深入,不铺开学。
第四步:踩坑 → 查 Issue → 形成经验(持续)
真正的理解来自踩坑。遇到问题先查 GitHub Issue 和社区讨论,很多坑别人已经踩过了。
差距在哪:新手的“看文档跑 Demo”没有方法论,容易在原理细节里迷失方向。高手的倒金字塔方法有明确的四步节奏——先理解价值、再跑通最短路径、再按需深入、最后靠实践沉淀。面试官考的是你的学习效率和自驱能力。
Q:RAG 系统检索到的文档很多但回答质量差,怎么排查?
来源:携程 Agent 开发实习一面
新手答:“可能是模型不够好,换个更强的模型。”
高手答:
“检索多但回答差”说明问题大概率不在模型,而在检索质量和上下文组装。排查按链路从前到后逐段定位:
1. 检索相关性差(召回了但不准):
- 症状:返回的文档和用户问题表面相关但实际不对口
- 排查:抽样看 top-K 文档和 query 的匹配度,计算 Recall@K / Precision@K
- 常见原因 + 解法:
- Embedding 模型领域适配差 → 用业务数据微调 Embedding
- Chunk 切分不合理(太大导致噪声多,太小导致上下文断裂)→ 调整 chunk size 和 overlap
- 缺少多路召回 → 加 BM25 关键词路补充精确匹配
2. 排序失效(相关文档排在后面):
- 症状:相关文档确实被召回了,但排在第 8、9 位,top-3 都是噪声
- 排查:对比有无 ReRank 时的排序效果
- 解法:加 Cross-Encoder ReRank 模型做精排;或微调 ReRank 模型
3. 上下文组装问题(给模型的信息太杂):
- 症状:top-K 文档质量还行,但模型回答仍然差
- 排查:直接看拼给模型的 context,是不是信息太多、互相矛盾、或关键信息被淹没
- 解法:减少 top-K(从 10 降到 3-5);对召回文档做摘要提取再喂给模型;加冲突检测
4. Prompt 模板问题:
- 症状:换了更好的检索结果,回答质量还是没提升
- 排查:检查 Prompt 是否清晰指示模型“基于以下资料回答,如果资料不足请说明”
- 解法:优化 Prompt 模板,加引用约束
差距在哪:新手第一反应是“换模型”——这是最贵且通常无效的做法。高手按 RAG 链路逐段排查(检索 → 排序 → 组装 → Prompt),每段都有具体的症状、排查方法和解法。面试官考的是你排查问题时的系统性思维。
Q:什么是余弦相似度?在 RAG 系统中用来做什么?
来源:携程 Agent 开发实习一面
新手答:“衡量两个向量的相似程度。”
高手答:
余弦相似度衡量的是两个向量方向的接近程度,不关心长度,只关心角度:
cos(A, B) = (A · B) / (|A| × |B|)
值域:[-1, 1]
1 = 方向完全一致(语义最相似)
0 = 正交(语义无关)
-1 = 方向完全相反(语义相反)
为什么用余弦而不是欧氏距离:
Embedding 模型输出的向量,不同文本的向量长度(模)可能差异很大。如果用欧氏距离,一段长文本和一段短文本即使语义相同,距离也可能很远(因为向量模不同)。余弦相似度归一化了长度,只比较方向——语义相同的文本,无论长短,余弦相似度都接近 1。
在 RAG 系统中的用途:
- 检索阶段:用户 query 向量和文档库中所有文档向量计算余弦相似度,取 top-K 作为候选
- 去重阶段:两个 chunk 的余弦相似度 > 0.95,判定为近重复,去掉一个
- 阈值过滤:相似度低于阈值(如 0.6)的文档直接丢弃,不进入精排——避免把完全不相关的文档喂给模型
补充:实际向量数据库(Milvus / Qdrant)在 ANN 检索时用的是近似最近邻算法(HNSW / IVF),不是暴力遍历所有向量。精确的余弦计算只在小规模候选集上做。
差距在哪:新手只背了定义。高手从公式、为什么选余弦而非欧氏、在 RAG 中的三个具体用途(检索/去重/过滤)做了完整解释,且补充了工程实现细节。面试官考的是你理解了这个指标的设计动机,而不只是会算。
Q:什么是嵌入(Embedding)?为什么 RAG 系统需要将文本转为向量?
来源:携程 Agent 开发实习一面
新手答:“把文本变成数字,方便计算。”
高手答:
嵌入(Embedding)是把文本映射到一个高维向量空间中的过程——每段文本变成一个固定长度的数字数组(如 768 维或 1536 维),这个数组就叫这段文本的“向量表示”。
关键特性:语义相近的文本,在向量空间中的位置也相近。
"北京今天天气很好" → [0.12, -0.45, 0.78, ...] ─┐
"今日北京晴朗" → [0.11, -0.43, 0.76, ...] ─┘ 向量接近
"明天股市走势如何" → [-0.67, 0.23, -0.11, ...] ← 向量远离
为什么 RAG 需要向量化:
传统检索靠关键词匹配——用户搜“怎么退货”,系统只能找到包含“退货”两个字的文档。但用户可能问的是“买错了怎么办”“商品不满意能换吗”——意思一样,但没有一个共同关键词。
向量化解决的是语义匹配问题——“怎么退货”和“商品不满意能换吗”的 Embedding 向量很接近,即使没有共同关键词也能检索到。
Embedding 模型的选型考量:
- 通用模型:OpenAI text-embedding-3、BGE、E5、GTE——开箱即用,适合大部分场景
- 领域微调:如果业务术语多(医疗、法律、金融),通用模型可能把业务术语和日常用语混淆,需要用业务数据做微调
- 维度和性能的权衡:维度越高表达力越强,但存储和计算成本也越高。768 维是常见平衡点
差距在哪:新手的”变成数字”没有解释为什么要这么做。高手从语义匹配的角度解释了向量化的动机——解决关键词匹配的语义鸿沟问题,且覆盖了模型选型的实际考量。面试官考的是你理不理解 Embedding 在 RAG 管线中的核心作用。
追问:Embedding 的本质是什么?向量的每个维度代表什么含义?
来源:小红书 AI应用开发
Embedding 的本质是将离散符号映射到连续向量空间,使语义关系可以用几何距离衡量。但很多人会追问:向量的每个维度到底是什么意思?
每个维度不是一个可解释的特征——这是 Embedding 和传统特征工程的根本区别:
| 对比 | 传统特征向量 | Embedding 向量 |
|---|---|---|
| 每个维度 | 有明确含义(年龄、价格、频率) | 无单一可解释含义 |
| 表示方式 | 人工设计的特征 | 模型自动学到的潜在特征 |
| 信息分布 | 每维独立编码一种信息 | 一种语义分布在多个维度上 |
| 术语 | 特征工程 | 分布式表示(Distributed Representation) |
什么是分布式表示:一个语义概念(如”食物”)不是由某一个维度单独表达,而是由多个维度的组合模式来表达。反过来,一个维度也参与了多个语义概念的编码。这就是为什么单看某一维的数值没有意义,但整体向量之间的距离能反映语义相似性。
维度数量的工程权衡:
低维(256-384):存储小、速度快,但语义区分度有限,适合简单场景
中维(768): 最常见的平衡点,大多数场景够用
高维(1024-3072):区分度更高,能捕获更细粒度的语义差异,但存储和计算成本线性增长
面试加分点:如果面试官继续追问”能不能让每个维度可解释”,可以提到 Sparse Embedding(如 SPLADE)——它在词表维度上做稀疏编码,每个维度对应一个 token,可解释性强但维度极高(30000+)。这也是为什么实际系统常用 Dense + Sparse 混合检索——Dense 捕获语义,Sparse 保留精确匹配能力。
Q:RAG 中如何提高文档召回率?
来源:蚂蚁集团智能体与大模型应用一面
新手答:“换更好的 Embedding 模型。”
高手答:
召回率低意味着正确文档没有进入候选集——模型连看到正确答案的机会都没有。提升召回率要从离线和在线两端同时入手:
离线端——提升文档的“可被检索性”:
- Chunk 策略优化:
- 块太大 → 一个 chunk 混了多个主题,语义被平均化,精准匹配变弱
- 块太小 → 上下文断裂,语义不完整
- 最佳实践:语义分块(按段落/标题/逻辑边界切割)+ overlap(相邻 chunk 重叠 10-20%)
- 文档增强:给每个 chunk 附加元信息——标题、所属章节、关键词标签、生成摘要。检索时不只匹配 chunk 正文,还匹配元信息
- Embedding 模型适配:通用模型在专业领域(医疗/法律/代码)的表现可能大幅下降。用业务数据微调 Embedding 模型,Recall 通常能提升 10-20%
在线端——提升查询的“被理解度”:
- 查询改写:用大模型对用户 query 做多维度改写(同义替换、意图展开、实体补全),从不同角度匹配文档
- 混合召回:不只用向量检索,同时用 BM25 关键词检索 + 标签检索,多路结果合并。向量路抓语义,关键词路抓精确匹配,互补覆盖
- HyDE(Hypothetical Document Embeddings):让模型先生成一个“假设性答案”,用这个答案的 embedding 去检索——因为答案和文档的语义更接近,比直接用 query 检索效果好
flowchart TB
Q["用户 Query"] --> QR["查询改写\n(多维度)"]
Q --> HY["HyDE\n生成假设答案"]
QR --> VR["向量召回"]
QR --> KR["关键词召回"]
QR --> TR["标签召回"]
HY --> VR2["向量召回"]
VR --> M["合并去重"]
KR --> M
TR --> M
VR2 --> M
M --> RR["ReRank 精排"]
RR --> TOP["Top-K 结果"]
效果最大的三板斧:根据实践经验,混合召回 > chunk 优化 > Embedding 微调。很多团队一上来就微调模型,其实先加一路 BM25 召回就能显著提升 Recall。
差距在哪:新手只想到换模型。高手从离线端(chunk/文档增强/Embedding 微调)和在线端(查询改写/混合召回/HyDE)六个方向给出了完整方案,且点出了优先级排序。面试官考的不是你知不知道某个技术,而是你能不能系统性地优化一条 RAG 管线。
Q:RAG 为什么需要向量检索?和传统关键词检索有什么本质区别?
来源:蚂蚁集团智能体与大模型应用一面
新手答:“向量检索更准。”
高手答:
不是“更准”——是解决了不同的问题。传统检索和向量检索的核心差异在于匹配方式:
| 维度 | 传统关键词检索(BM25) | 向量检索(Embedding) |
|---|---|---|
| 匹配方式 | 词级精确匹配 | 语义级相似度匹配 |
| 核心算法 | TF-IDF / BM25 | ANN(近似最近邻) |
| 能处理同义词 | ❌ “退货”搜不到“退款” | ✅ 语义相近就能匹配 |
| 能处理精确术语 | ✅ 搜“order_id=123”直接命中 | ❌ 向量化后精确性丢失 |
| 计算成本 | 低(倒排索引,O(1)级) | 高(向量距离计算 + ANN 索引) |
| 索引存储 | 小(倒排表) | 大(每条文档一个高维向量) |
| 对新词/专业术语 | 好(只要文档里有就能匹配) | 差(模型没见过的词 embedding 质量低) |
RAG 为什么需要向量检索:
用户提问的方式和文档的表述方式几乎不可能完全一样。用户问“服务器老是挂”,文档里写的是“服务可用性异常”——没有一个共同关键词,但语义完全匹配。传统检索在这种场景下召回率为零,向量检索能轻松解决。
但向量检索不能完全替代关键词检索:
查具体的错误码(ERR_CONNECTION_REFUSED)、精确的文件路径、特定的 API 名称——这些场景关键词检索比向量检索更快更准。向量化反而会把精确信息“模糊化”。
生产环境的最佳实践是混合检索:
向量检索(语义匹配)─┐
├→ 合并去重 → ReRank 精排 → Top-K
关键词检索(精确匹配)─┘
两路互补:向量路保证语义覆盖,关键词路保证精确命中。合并后用 ReRank 统一排序。
差距在哪:新手用“更准”一词概括——其实向量检索在精确匹配上反而不如关键词检索。高手明确了两者的优劣势互补关系,且指出生产环境用混合检索。面试官考的是你对检索系统的工程认知——不是“哪个更好”,而是“什么场景用什么”。
Q:如果用全量生产文档做关联性检索,用户每个问题要交互多少轮?有没有更高效的方案?
来源:蚂蚁集团智能体与大模型应用二面
新手答:“文档多了就慢一点,多检索几次。”
高手答:
全量文档场景的核心挑战是文档量级和查询效率的矛盾——几万甚至几十万份文档,用户一个问题不可能遍历所有文档找关联。
传统 RAG 的局限:
标准 RAG 是 query → embedding → top-K 召回。但当文档量大且内容跨领域时,单次召回很难覆盖所有相关信息。用户可能需要多轮追问才能拿到完整答案——每轮交互本质上是在“逐步缩小检索范围”。
更高效的方案:
1. 文档预处理——建立关联索引:
不等用户查询时才找关联,离线阶段就把文档间的关联性预计算好:
- 文档聚类:按主题对文档自动分组,查询时先定位到相关组,缩小搜索范围
- 知识图谱:从文档中抽取实体和关系,建立文档间的语义关联图。查询时沿着图谱做多跳检索,找到间接关联的文档
- 摘要索引:每份文档生成摘要,先用摘要做粗筛,再用原文做精排——两级检索比全量检索快很多
2. Multi-hop RAG——一次查询找到链式关联:
flowchart LR
Q["用户提问:\n这个服务为什么延迟高?"]
Q --> R1["第一跳:找到\n服务架构文档"]
R1 --> R2["第二跳:从架构文档\n发现依赖 Redis"]
R2 --> R3["第三跳:找到\nRedis 配置变更记录"]
R3 --> A["综合三份文档回答"]
Multi-hop RAG 让 Agent 自动做多轮检索,每一跳基于上一跳的结果生成新的子查询,沿关联链逐步深入。用户只问一次,Agent 内部自动完成多轮检索。
3. GraphRAG——结构化关联检索:
微软提出的 GraphRAG 方案:先从全量文档构建知识图谱和社区摘要,查询时在图上检索而非在文档上检索。优势是能找到文档间的隐式关联——两份文档没有共同关键词,但通过实体关系链接在一起。
差距在哪:新手认为全量文档只能“多搜几次”。高手给出了三种更高效的方案——预计算关联索引、Multi-hop RAG 自动多跳检索、GraphRAG 结构化关联。面试官考的是你对 RAG 架构演进方向的认知——从单次检索到多跳检索到图检索,是检索系统面对大规模文档的自然演化路径。
RAG 在 Agent 中的角色
Q:RAG 在 Agent 体系里应该被看成工具、记忆,还是推理前置步骤?
来源:Agent 开发面试 30 题
新手答:“RAG 就是个工具,需要的时候调一下。”
高手答:
RAG 在 Agent 体系里的定位取决于使用方式,三种定位都成立,适用场景不同:
当工具用——按需调用:
Agent 在执行过程中判断“我需要查资料”,主动调用 RAG 检索。这时 RAG 和其他工具(搜索、计算器)是同级的。
适用场景:开放性任务,Agent 不一定每次都需要检索。比如用户问“1+1 等于几”不需要 RAG,问“公司休假制度”才需要。
当记忆用——上下文扩展:
每次对话开始时,自动检索和当前话题相关的历史文档注入上下文。RAG 充当的是“长期记忆的检索接口”。
适用场景:垂直领域问答,几乎每次都需要外部知识。比如客服机器人,每个问题都要查产品文档。
当推理前置步骤——先检索再思考:
RAG 不是可选步骤,而是必经环节——先检索证据,再基于证据推理。模型不允许在没有证据的情况下直接回答。
适用场景:高准确性要求的场景(法律、医疗),必须有据可依。
| 定位 | 触发方式 | 适用场景 | 典型架构 |
|---|---|---|---|
| 工具 | Agent 主动调用 | 开放性任务 | ReAct + RAG Tool |
| 记忆 | 每轮自动注入 | 垂直领域问答 | RAG-augmented Chat |
| 前置步骤 | 强制先检索 | 高准确性要求 | Retrieve-then-Read |
实际生产中这三种往往混合使用:基础知识自动注入(记忆模式),需要深入查找时主动调用(工具模式),关键结论必须有证据支撑(前置步骤模式)。
差距在哪:新手把 RAG 固定为“工具”一种定位。高手看到了 RAG 在 Agent 体系中的三种不同角色,且说清了各自的适用场景和触发方式。面试官考的是你对 RAG 和 Agent 系统集成方式的灵活理解。
Q:如果检索结果很多但质量参差不齐,你会把控制点放在召回、重排,还是 Agent 规划层?
来源:Agent 开发面试 30 题
新手答:“加 ReRank 重排就行。”
高手答:
三个控制点都需要,但投入产出比不同:
召回层——控制“进来什么”:
问题:召回 50 条,30 条不相关
解法:① 优化 query(多维度改写)② 收紧召回阈值 ③ 加关键词路补充精确匹配
效果:减少噪声进入下游,降低精排压力
召回层的控制是成本最低、效果最直接的——垃圾在入口就拦住,比在后面处理便宜得多。
重排层——控制“排在前面的是什么”:
问题:相关文档被召回了,但排在第 8 位,top-3 都是噪声
解法:用 Cross-Encoder 精排,把真正相关的推到前面
效果:精度提升显著,但增加延迟和计算成本
重排层解决的是顺序问题,前提是相关文档已经被召回了。如果召回阶段就漏了,重排也救不了。
Agent 规划层——控制“怎么用检索结果”:
问题:top-3 文档质量还行,但信息分散,模型拼出了错误答案
解法:Agent 在使用检索结果前做质量判断——
"这些证据是否足够回答问题?是否有矛盾?需不需要再查一次?"
效果:提升最终回答质量,但增加一轮模型调用
规划层是最后一道防线,也是唯一能做“再查一次”决策的地方。
优先级排序:
投入优先级:召回优化(成本低效果大)> ReRank(精度提升明显)> 规划层判断(兜底)
先把召回质量做好,再加精排提升排序,最后在 Agent 层做质量兜底。很多团队跳过前两步直接在 Agent 层“让模型自己判断”,这是最贵且最不可靠的做法。
差距在哪:新手只想到 ReRank 一个点。高手从召回、重排、规划三层分别分析了控制方法和投入产出比,且给出了明确的优先级排序。面试官考的是你对 RAG 质量控制的全链路思维。
Q:Agent 场景下,什么时候该做一次检索、多次使用;什么时候该边执行边检索?
来源:Agent 开发面试 30 题
新手答:“复杂问题多查几次。”
高手答:
判断标准是信息需求在任务开始时是否完全可知:
一次检索、多次使用(Retrieve-once):
适用条件:
- 任务开始时就能确定需要什么信息
- 信息需求不会随执行过程变化
- 检索结果在整个任务周期内有效
典型场景:
- 客服问答:用户问"退货政策是什么"→ 一次检索产品文档,回答完整问题
- 报告生成:给定主题,一次性检索所有相关数据,然后生成报告
- FAQ 类问答:一问一答,信息需求明确
优势是延迟低、成本低,只调用一次检索管线。
边执行边检索(Iterative Retrieval):
适用条件:
- 下一步需要查什么取决于上一步的结果
- 任务目标可能在过程中细化或变化
- 单次检索无法覆盖所有需要的信息
典型场景:
- 故障排查:"服务挂了"→ 查架构文档 → 发现依赖 Redis → 查 Redis 变更记录 → 定位根因
- 竞品分析:查到竞品 A 的信息后,发现需要对比的维度,再查竞品 B
- 多跳推理:答案分布在多个文档中,需要逐步追溯
优势是信息覆盖全,但延迟和成本成倍增加。
工程上的混合策略:
先做一次广泛的初始检索,如果 Agent 判断信息不足再触发增量检索。设一个检索预算上限(如最多 3 次),避免无限检索。
第一次:广泛检索,覆盖主要信息需求
执行中判断:信息够 → 继续执行。信息不够 → 生成更精确的子查询,再检索一次
最多追加 2 次,超过后用已有信息给出最优答案
差距在哪:新手用“复杂度”判断。高手用“信息需求是否可预知”做精确判断,且给出了一次检索和迭代检索的各自适用场景,以及“初始广泛 + 按需增量”的混合策略。面试官考的是你在 RAG 和 Agent 结合时的架构设计能力。
Q:如果 RAG 返回了看似可信但实际过时的信息,你会怎么降低 Agent 被误导的概率?
来源:Agent 开发面试 30 题
新手答:“定期更新知识库。”
高手答:
定期更新是必要的,但更新和检索之间一定有时间差——今天更新的文档可能要到明天才入库,而在这个窗口期内返回的就是过时信息。更根本的问题是:很多文档没有明确的“过期时间”,系统不知道它过时了。
需要多层防线:
第一层:离线端——给文档打时间标签和可信度标签
- 每个 chunk 存储文档创建时间、最后更新时间、来源权威等级
- 有明确时效性的内容(价格、政策、版本号)标记为“时效敏感”
- 建立文档更新监控——源文档变更后自动触发重新入库
第二层:检索端——时间感知的检索和排序
- 检索时对结果按时间新鲜度加权——同等相关性下,新文档排在前面
- 对“时效敏感”标签的 chunk,如果超过 TTL(如 30 天)直接降权或过滤
- 返回结果时附带时间元信息,让 Agent 知道证据的时效性
第三层:Agent 端——生成前做时效性判断
在 Prompt 中明确要求模型:
注意:以下检索结果附带了文档日期。如果信息可能已过时(如价格、政策、版本号),
请在回答中标注"此信息来源于 YYYY-MM-DD 的文档,建议确认是否仍然有效"。
模型看到日期后会主动判断时效性,并在回答中加上免责提示。
第四层:用户端——可追溯的引用
回答中附带引用来源和日期,让用户自行判断信息是否过时。这是最后一道防线——即使系统没发现过时,用户看到“来源:2024 年 3 月的文档”也会自己注意。
差距在哪:新手只想到“更新知识库”——这是必要但不充分的。高手从离线(时间标签)、检索(时间加权)、Agent(时效性判断)、用户(可追溯引用)四层构建了防过时信息的完整体系。面试官考的是你对 RAG 系统“证据可靠性”这个核心问题的工程化思考。
Q:在渐进式披露的架构下,还需要 RAG 吗?RAG 的角色会怎么变?
来源:蚂蚁集团 Agent 开发二面
新手答:“模型上下文窗口越来越大,RAG 可能不需要了。”
高手答:
RAG 不会消失,但角色会发生本质变化——从“弥补模型知识不足”变成“渐进式披露架构的信息供给层”。
为什么上下文窗口变大不能替代 RAG:
即使窗口有 100 万 token,也不能把所有文档都塞进去:
- 成本问题:塞 100 万 token 的成本是塞 1 万 token 的 100 倍
- 精度问题:信息越多,模型在噪声中迷失的概率越大(Lost in the Middle)
- 实时性问题:长上下文解决不了“信息需要实时更新”的问题
所以不是“要不要 RAG”,而是“RAG 在新架构里扮演什么角色”。
RAG 在渐进式披露架构中的新角色:
传统 RAG:用户提问 → 检索文档 → 塞进上下文 → 生成回答(一次性、静态)
新角色 RAG:Agent 执行到某阶段 → 按需检索该阶段需要的知识 → 精确注入 → 继续执行(多次、动态)
| 维度 | 传统 RAG | 渐进式架构下的 RAG |
|---|---|---|
| 触发时机 | 用户提问时一次性触发 | Agent 执行过程中多次按需触发 |
| 检索粒度 | 和用户 query 匹配 | 和当前执行阶段的子目标匹配 |
| 结果用途 | 直接喂给模型生成答案 | 作为当前阶段的决策依据 |
| 生命周期 | 检索一次用到结束 | 阶段切换后可能需要重新检索 |
具体变化:
- 从“回答前检索”到“执行中检索”:RAG 不只在开头检索一次,而是 Agent 在规划、执行、验证各阶段都可能触发检索,每次检索的 query 不同
- 从“通用检索”到“阶段感知检索”:同一个用户问题,规划阶段需要检索的是“方法论文档”,执行阶段需要的是“API 文档”,验证阶段需要的是“规范标准”——检索策略随阶段变化
- 从“替代模型知识”到“精确注入运行时上下文”:RAG 的价值从“给模型不知道的知识”转变为“在正确的时机给出正确的精确信息”
差距在哪:新手用“窗口大了不需要 RAG”的线性思维。高手看到了 RAG 在新架构下的角色转变——从一次性的知识注入变成多阶段的动态信息供给。面试官考的是你对 RAG 技术演进方向的理解,以及能不能把 RAG 放到更大的架构图景中思考。
召回与排序优化
Q:RAG 中为什么引入父子索引?
来源:快手 AI Agent 开发一面
新手答:“为了更精确地检索。”
高手答:
父子索引解决的是 chunk 粒度的两难问题——检索精度和生成质量对 chunk 大小的需求是矛盾的。
小 chunk(100-200 字):
✅ Embedding 语义集中,检索精度高
❌ 上下文断裂,模型拿到的信息不完整
大 chunk(500-1000 字):
✅ 上下文完整,模型能理解前因后果
❌ Embedding 被多个主题稀释,检索精度下降
父子索引的设计思路是解耦检索粒度和上下文粒度:
flowchart TB
D["原始文档"] --> P1["父 chunk 1(500字,完整段落)"]
D --> P2["父 chunk 2(500字)"]
P1 --> C1["子 chunk 1a(150字)"]
P1 --> C2["子 chunk 1b(150字)"]
P1 --> C3["子 chunk 1c(150字)"]
P2 --> C4["子 chunk 2a(150字)"]
P2 --> C5["子 chunk 2b(150字)"]
检索时用子 chunk(小粒度,精度高),返回时取父 chunk(大粒度,上下文完整)。这样同时拿到了检索精度和生成上下文质量。
实现方式:
- 离线阶段把文档切成大块(父 chunk),再把每个父 chunk 切成若干小块(子 chunk),子 chunk 记录
parent_id - 对子 chunk 做 embedding 入向量库
- 检索时用 query 匹配子 chunk,拿到结果后通过
parent_id取出对应的父 chunk - 父 chunk 去重后送入 rerank 和 LLM
进阶:还可以做多级索引——文档摘要作为最顶层,段落作为中间层,句子作为最底层。先粗筛文档,再定位段落,最后精确到句子,逐层缩小范围。
差距在哪:新手只说了“更精确”但不知道为什么需要两层。高手点出了核心矛盾——检索需要小 chunk 精度、生成需要大 chunk 上下文——父子索引用“小粒度检索、大粒度返回”同时满足两个需求。面试官考的是你对 chunk 策略的深入理解。
Q:为什么在检索阶段引入BM25?它和向量检索怎样组合?
来源:快手 AI Agent 开发一面 【字节二面同题:多路检索 + 向量/关键词各解决什么】【淘天Agent开发同题:为什么加 BM25 + 具体解决了什么 bad case】
新手答:“BM25 是传统检索方法,加上它可以互补。”
高手答:
为什么引入 BM25:向量检索擅长语义匹配,但在三类场景下表现很差:
- 精确术语:错误码
ERR_OOM_KILL、API 名称getUserProfile——向量化后精确信息被稀释 - 低频专业词:Embedding 模型对训练数据中低频的词 embedding 质量差
- 新词/缩写:近期出现的产品名、术语,Embedding 模型没见过
BM25 基于词频和逆文档频率做精确匹配,恰好弥补这三个短板。实际项目中,加一路 BM25 召回通常能把整体 Recall 提升 10-15%。
组合方式:主流有两种融合策略:
方案一:分数融合(Score Fusion)
final_score = α × normalize(vector_score) + (1-α) × normalize(bm25_score)
需要先对两路分数做归一化(min-max 或 z-score),否则量纲不同无法直接加权
方案二:倒数排名融合(Reciprocal Rank Fusion, RRF)
RRF_score(d) = Σ 1/(k + rank_i(d))
只看排名不看分数,对分数分布差异免疫,k 通常取 60
比例怎么设:不是拍脑袋定的。初始值用 0.7 向量 + 0.3 BM25,然后在评估集上做 grid search:
遍历 α = [0.5, 0.6, 0.7, 0.8, 0.9]
对每个 α,计算 Recall@10 和最终回答质量
选最优 α。不同业务场景最优值不同:
技术文档 → BM25 权重可以更高(精确术语多)
日常对话 → 向量权重更高(同义表达多)
完整检索流程(从 query 到最终上下文):
flowchart LR
Q["用户 Query"] --> QR["Query 预处理\n改写/扩展/意图识别"]
QR --> V["向量检索\nTop-K₁"]
QR --> B["BM25 检索\nTop-K₂"]
V --> M["合并去重\n分数融合"]
B --> M
M --> RR["Rerank 精排\nCross-Encoder"]
RR --> TK["Top-K 截断\n3~5 块"]
TK --> CTX["上下文组装\n拼接 Prompt"]
CTX --> LLM["LLM 生成回答"]
- Query 预处理:意图识别、查询改写、关键词提取
- 双路并行召回:向量路取 Top-20~50,BM25 路取 Top-10~20,并行执行
- 合并去重:按文档 ID 去重,同一文档被多路命中的加分
- Rerank 精排:用 Cross-Encoder(如 bge-reranker-v2、Cohere rerank)对 query-doc pair 做精细打分
- Top-K 截断:取精排后 Top-3~5 作为最终上下文
- Prompt 组装:把检索结果按相关度排序拼入 Prompt,交给 LLM 生成回答
更广义的多路检索策略:
BM25 + 向量检索只是多路检索的起点。生产级 RAG 系统的多路检索可以更丰富:
| 检索路径 | 原理 | 擅长 | 不擅长 |
|---|---|---|---|
| 向量检索 | 语义相似度 | 同义改写、模糊表述 | 精确关键词、专有名词 |
| BM25 | 词频-逆文档频率 | 精确关键词、专有名词 | 同义词、语义理解 |
| 元数据过滤 | 结构化字段匹配 | 时间范围、类别筛选 | 语义理解 |
| 知识图谱 | 实体关系遍历 | 多跳推理、关联查询 | 模糊意图 |
融合策略:
多路召回后需要融合排序,主流方案:
- RRF(Reciprocal Rank Fusion):
score = Σ 1/(k + rank_i),不需要归一化,简单有效 - 加权分数融合:
score = α × vector_score + β × bm25_score,需要分数归一化到同一量纲 - 级联融合:第一路做粗召回(高召回率),第二路在结果集上做精排(高精确率)
实际生产中,RRF 是默认首选——不需要调权重,对不同分布的分数天然鲁棒。
差距在哪:新手只说了”互补”两个字。高手说清了 BM25 解决的三类具体问题、两种融合方案的差异、比例调优方法论,以及完整的端到端检索流程。面试官考的是你对混合检索的工程化认知——不只是”加了 BM25”,而是知道怎么组合、怎么调参、怎么评估。
追问:RRF 融合中 K 参数一般取值多少?大一点、小一点对结果有什么影响?
RRF 公式:score(d) = Σ 1/(K + rank_i(d)),K 是一个平滑常数,决定了排名差异的敏感度。
| K 值 | 效果 | 适用场景 |
|---|---|---|
| K 很小(如 1~10) | Top-1 和 Top-2 的分数差异极大,”赢家通吃” | 某一路检索质量明显优于其他路,希望它主导结果 |
| K=60(默认值) | 排名差异被适度平滑,各路贡献相对均衡 | 多路检索质量相当,需要综合各路信号 |
| K 很大(如 200+) | 排名差异几乎被抹平,趋近于”被多少路命中”的计数 | 更看重文档被多路召回的次数,而非在单路的排名 |
实际调参方法:
1. 默认 K=60,大多数场景不需要调
2. 如果发现某一路检索远强于其他路 → 减小 K,让强路主导
3. 如果各路检索互补性强(向量找语义、BM25 找关键词) → 保持或增大 K
4. 在评测集上做 grid search:K ∈ {10, 30, 60, 100},看 Recall@K 和 MRR 变化
RRF 相比加权分数融合的核心优势:不需要归一化。不同检索路径的分数量纲和分布可能差异巨大(向量余弦 0~1,BM25 可能 0~50),RRF 只看排名不看分数,天然免疫分布差异。
追问:长尾或语义模糊的查询,怎么提升召回准确率?
长尾查询的核心问题是信息量不足——query 太短、太模糊、或用了非标准表述,导致检索系统无法精准匹配。
分层应对策略:
flowchart TB
Q[“长尾/模糊 Query”] --> D{“诊断 Query 类型”}
D -->|”太短(1-2词)”| E1[“Query 扩展\nLLM 生成多个子查询”]
D -->|”非标准表述”| E2[“同义改写\n术语标准化”]
D -->|”意图不明”| E3[“多意图并行检索\n各意图独立召回再合并”]
D -->|”领域专业词”| E4[“领域词典匹配\n+BM25 精确检索”]
E1 --> R[“合并多路结果\nRRF 融合”]
E2 --> R
E3 --> R
E4 --> R
R --> RK[“Rerank 精排\n过滤噪声”]
| 策略 | 原理 | 适用场景 |
|---|---|---|
| HyDE(假设性文档嵌入) | 让 LLM 先”编”一篇假想答案,用假想答案的 embedding 去检索 | query 极短但意图明确 |
| Query 扩展 | 一条 query 变 3~5 条子 query,多路召回合并 | query 模糊、可能有多种理解 |
| 领域词典 + BM25 | 对专业术语做标准化映射,用 BM25 精确匹配 | 医疗、法律、金融等术语密集领域 |
| 伪相关反馈(PRF) | 第一轮检索的 Top-K 结果作为反馈,扩展 query 做第二轮检索 | 初始召回有部分相关结果但不够 |
| 渐进式放宽 | 初始用严格条件检索,无结果则逐步放宽约束 | 用户需求精确但知识库覆盖不足 |
实测中,HyDE 对短 query 提升最大(Recall +20~30%),但会增加一次 LLM 调用的延迟。Query 扩展是性价比最高的通用方案。
追问:基于 Milvus,BM25 与向量检索的分数归一化具体怎么做?
来源:阿里淘天 AI应用开发一面
两路检索的分数量纲完全不同——BM25 分数是无界的(取决于文档长度和词频),向量余弦相似度在 [-1, 1] 区间。不归一化直接加权会导致一路完全压过另一路。
三种归一化方案:
| 方案 | 做法 | 优点 | 缺点 |
|---|---|---|---|
| Min-Max 归一化 | 每路取当前 batch 的 min/max 映射到 [0,1] | 简单直观 | 不同 query 的 min/max 波动大,不稳定 |
| Z-Score 归一化 | 减均值除标准差 | 统计稳定 | 需要维护全局统计量 |
| RRF(Reciprocal Rank Fusion) | 不看分数只看排名:score = Σ 1/(K+rank_i) | 无需归一化,对异常值鲁棒 | K 参数需调优 |
Milvus 实战:Milvus 2.4+ 原生支持 hybrid search,内置 RRF 和 WeightedRanker 两种融合策略。推荐先用 RRF(K=60 是常用起点),如果对精排效果不满意再切 WeightedRanker 手动调权重。
Q:Rerank 后一般返回几个块?TopK 截断策略怎么设计?
来源:快手 AI Agent 开发一面 【字节二面追问:Re-rank 的作用 + 为什么有了向量相似度还需要它】【淘天Agent开发追问:低分阈值提前过滤策略】
新手答:“返回 5 个左右。”
高手答:
通常返回 3~5 个块,这个范围是多个因素权衡的结果:
太少(1-2 个):信息可能不完整,复杂问题需要多个证据源佐证
太多(8-10 个):
① 噪声增加——低相关度的块干扰模型判断
② Lost in the Middle——模型对中间位置的信息关注度下降
③ Token 成本线性增长
④ 生成延迟增加
验证方法:在评估集上做消融实验:
固定其他参数,遍历 K = [1, 2, 3, 5, 8, 10]
对每个 K 计算:
- 回答准确率(人工评判或 LLM-as-Judge)
- Faithfulness(回答是否忠于检索结果)
- Token 消耗和延迟
通常 K=3~5 是准确率和成本的甜点区间
TopK 截断策略:
不建议用固定 K 一刀切。更好的做法是分数阈值 + 上限 K + 断崖检测三条件组合:
def adaptive_topk(reranked, max_k=5, min_score=0.6, score_drop=0.3):
selected = []
for i, result in enumerate(reranked[:max_k]):
if result.score < min_score:
break
if i > 0 and (selected[-1].score - result.score) > score_drop:
break
selected.append(result)
return selected if selected else [reranked[0]]
三个截断条件:
- 硬上限:最多取 max_k 个,防止上下文过长
- 绝对阈值:低于 min_score 的块直接丢弃——质量太差的证据不如没有
- 分数断崖检测:相邻块分数骤降超过阈值时截断——说明后面的块相关度显著下降
上下文过长/过短的处理:
| 情况 | 处理策略 |
|---|---|
| 上下文过长 | ① 对每个块提取关键句而非全文 ② 截断低分块 ③ 只保留和 query 最相关的段落 |
| 上下文过短 | ① 增大召回 K 值,降低召回阈值 ② 启用父 chunk 扩展上下文 ③ 查询改写后重新检索 ④ 兜底:告知用户“知识库中未找到足够信息” |
差距在哪:新手随便说了个数。高手用消融实验确定 K 值,用三条件自适应截断代替固定 K,且对上下文过长/过短都有处理方案。面试官考的是你调参时有没有数据支撑的方法论,以及面对边界情况的工程化处理。
Q:如何系统性提升 RAG 的检索相关度与生成效果?
来源:快手 AI Agent 开发一面
新手答:“换更好的 Embedding 模型。”
高手答:
优化要分检索阶段和生成阶段,两者瓶颈不同、手段不同:
检索阶段优化(提升相关度):
| 优化方向 | 具体手段 | 预期效果 |
|---|---|---|
| Query 侧 | 查询改写、意图识别、HyDE | 提升 query 和文档的匹配度 |
| 索引侧 | 父子索引、语义分块、元信息增强 | 提升文档的可检索性 |
| 模型侧 | Embedding 微调、ReRank 微调 | 提升语义匹配精度 |
| 召回策略 | 混合检索(BM25 + 向量)、多路召回 | 提升覆盖率 |
生成阶段优化(提升回答效果):
| 优化方向 | 具体手段 | 预期效果 |
|---|---|---|
| 上下文质量 | 精排后截断、去除低分块、冲突检测 | 减少噪声干扰 |
| Prompt 工程 | 引用约束、格式要求、拒答指令 | 提升回答的忠实度和格式规范 |
| 模型选择 | 复杂问题用强模型、简单问题用轻量模型 | 平衡质量和成本 |
| 后处理 | 答案事实性校验、引用来源标注 | 降低幻觉风险 |
优先级排序(投入产出比从高到低):
① 混合检索(加 BM25 路,成本几乎为零,Recall 提升 10-15%)
② Chunk 策略优化(父子索引、语义分块,一次性投入,长期受益)
③ 查询改写(显著提升长尾 query 的召回率)
④ ReRank 引入/微调(精排对最终质量影响大)
⑤ Embedding 微调(效果好但需要标注数据,成本较高)
⑥ Prompt 优化(持续迭代,每次小幅提升)
如何验证优化是否有效:
离线评估:
检索指标:Recall@K、MRR、NDCG
生成指标:Faithfulness、Answer Relevancy、Completeness
工具:RAGAS 框架自动化评估
在线验证:
A/B 测试:新旧方案同时上线,对比用户满意度和任务完成率
灰度发布:先 10% 流量验证,确认无回退再全量
关键原则:每次只改一个变量,否则无法归因
差距在哪:新手只想到换模型——这是最贵且不一定有效的做法。高手把优化拆成检索和生成两阶段,每个阶段有明确的手段和优先级排序,且有离线+在线的验证体系。面试官考的是你优化 RAG 系统时有没有系统性的方法论和效果验证闭环。
追问:实际项目中召回不准,你做过哪些改进?效果如何?
这种问题考的是实战排查方法论,而不是罗列技术。回答结构应该是“定位 → 归因 → 方案 → 效果”:
典型排查流程:
1. 定位:抽 100 条 bad case,标注失败原因
→ 发现 60% 是"召回了但排名靠后",30% 是"根本没召回",10% 是"召回正确但生成忽略"
2. 归因:
"召回了但排名靠后" → Rerank 模型对当前领域的区分度不够
"根本没召回" → query 用词和文档差异大(词汇鸿沟)
"召回正确但生成忽略" → 上下文中噪声 chunk 干扰了模型
3. 方案(按投入产出比排序):
① 加 BM25 路 → 解决词汇鸿沟问题,0.5 天
② Query 改写 → 用 LLM 做同义扩展,1 天
③ Rerank 模型换成 bge-reranker-v2 → 精排能力提升,0.5 天
④ 上下文截断策略从 Top-5 改为"分数阈值 + Top-3 兜底" → 减少噪声,0.5 天
4. 效果:Recall@10 从 72% → 85%,端到端回答准确率从 68% → 81%
关键认知:优化不是“把所有方法都堆上去”,而是先做归因分析,找到占比最大的失败模式,针对性解决。每次改一个变量,用评测集验证,确认有效后再上线。
Q:RAG 系统的端到端性能如何优化?
来源:快手 AI Agent 开发一面
新手答:“用更快的向量数据库。”
高手答:
RAG 的端到端延迟 = 检索延迟 + 排序延迟 + LLM 生成延迟。优化从三个层面切入:
检索层优化:
① ANN 索引调优:HNSW 的 ef_construction 和 M 参数影响召回率和速度的平衡
② 预过滤:先用元数据(时间范围、文档类别)缩小候选集,再做向量检索
③ 并行召回:BM25 和向量检索并行执行,总延迟 = max(两路) 而非 sum(两路)
④ Embedding 缓存:高频 query 的 embedding 结果缓存,避免重复计算
模型层优化:
① 流式输出(Streaming):用户不用等完整回答生成完才看到内容
② 模型路由:简单问题用小模型快速回答,复杂问题用大模型保证质量
③ 上下文压缩:减少送入 LLM 的 token 数——token 越少生成越快、成本越低
④ KV Cache 复用:相同 system prompt 部分的 KV cache 可以跨请求复用
架构层优化:
flowchart LR
Q["用户 Query"] --> C{"语义缓存\n命中?"}
C -->|"命中"| R["直接返回缓存结果"]
C -->|"未命中"| P["并行检索"]
P --> RR["Rerank"]
RR --> LLM["LLM 生成"]
LLM --> W["写入缓存"]
W --> R2["返回结果"]
① 语义缓存:用 embedding 相似度判断——"RAG 是什么"和"什么是 RAG"命中同一缓存
命中率高的场景能减少 40-60% 的 LLM 调用
② 异步预处理:文档入库时预计算 embedding、预生成摘要,查询时直接用
③ 请求合并:短时间内相同/相似 query 合并为一次检索
④ 分级 SLA:核心业务走低延迟链路,非核心走高质量链路
各优化手段的延迟收益估算:
| 优化手段 | 典型延迟收益 | 实现成本 |
|---|---|---|
| 并行召回 | -30~50ms | 低 |
| 语义缓存 | 命中时 -500ms+ | 中 |
| 流式输出 | 首 token 提前 200-500ms | 低 |
| Embedding 缓存 | -20~50ms | 低 |
| 模型路由 | 简单问题 -300ms+ | 中 |
| 上下文压缩 | -100~300ms | 中 |
差距在哪:新手只想到”换更快的数据库”——这只是检索层的一个点。高手从检索、模型、架构三层拆解了完整的性能优化方案,且给出了每个手段的延迟收益估算。面试官考的是你对 RAG 系统全链路延迟的分析和优化能力。
知识库与索引工程
Q:GraphRAG 在处理 Agent 复杂关联查询时的优势在哪里?
来源:淘天 AI Agent 一面 【蚂蚁AI应用开发二面同题:GraphRAG 理解与应用】【字节二面追问:多跳推理/复杂逻辑查询场景下 RAG 架构优化】
新手答:”用知识图谱检索,比向量搜索更准。”
高手答:
先说清一个常见误解:GraphRAG ≠ 知识图谱检索。传统知识图谱检索是在预构建的三元组上做 SPARQL 查询,而 GraphRAG 是微软提出的一套完整架构——从文档自动构建知识图谱 + 社区摘要 + 本地/全局双搜索。
传统 RAG 的瓶颈:
传统 RAG 的检索单元是”文本块”(chunk),本质是扁平的。当用户的问题涉及多跳推理(A 和 B 的关系 → B 和 C 的关系 → 推导 A 和 C 的关系)或跨文档关联(信息分散在多个文档中)时,靠向量相似度只能找到局部相关的片段,无法把分散的信息串联起来。
传统 RAG 的困境:
用户问:”项目 A 的技术负责人和项目 B 的技术负责人之间有什么合作关系?”
文档1 提到:张三是项目 A 的技术负责人
文档2 提到:李四是项目 B 的技术负责人
文档3 提到:张三和李四共同发表了论文 X
→ 向量检索可能只召回文档1和文档2,漏掉关键的文档3
→ 即使三个都召回了,模型也需要自己做推理串联
GraphRAG 的架构与流程:
flowchart TB
D[“源文档集合”] --> EE[“实体与关系抽取\n(LLM 逐段提取)”]
EE --> G[“构建知识图谱\n(实体节点 + 关系边)”]
G --> CD[“社区检测\n(Leiden 算法)”]
CD --> CS[“社区摘要生成\n(LLM 为每个社区\n生成主题摘要)”]
Q[“用户查询”] --> R{“查询路由”}
R -->|”具体实体/关系查询”| LS[“本地搜索\nLocal Search”]
R -->|”全局性/主题性查询”| GS[“全局搜索\nGlobal Search”]
LS --> G
LS --> D
LS -->|”从目标实体出发\n沿关系边遍历\n收集关联实体和原文”| A1[“聚合上下文”]
GS --> CS
GS -->|”遍历社区摘要\n提取相关主题\n逐层汇总”| A2[“Map-Reduce 聚合”]
A1 --> LLM[“LLM 生成最终回答”]
A2 --> LLM
GraphRAG 的三大核心优势:
1. 多跳推理能力
知识图谱天然支持关系链遍历。上面那个例子,在图中就是:项目A → 技术负责人 → 张三 → 合作论文 → 李四 → 技术负责人 → 项目B。沿着关系边走两三跳就能拿到完整的关联信息,不需要模型自己推理。
2. 全局理解能力
这是 GraphRAG 最大的创新——社区摘要。通过社区检测算法把图中紧密关联的实体聚成社区,再用 LLM 为每个社区生成主题摘要。当用户问”这个领域的整体趋势是什么”这类全局性问题时,传统 RAG 只能拼凑零散的片段,GraphRAG 可以直接基于社区摘要回答。
3. 结构化实体消歧
同一个实体在不同文档中可能有不同的名称(”OpenAI”、”openai”、”Open AI”),图构建过程中会做实体合并和消歧,保证检索的一致性。
不同查询类型下的对比:
| 查询类型 | 传统 RAG | GraphRAG |
|---|---|---|
| 单跳事实题(”X 是什么”) | 效果好,向量检索足够 | 效果好,但构建成本更高 |
| 多跳关系题(”A 和 C 什么关系”) | 容易漏召回,依赖模型推理 | 图遍历直接获取关系链 |
| 全局摘要题(”整体趋势是什么”) | 只能拼凑片段,质量差 | 社区摘要直接回答,质量显著更好 |
| 对比分析题(”X 和 Y 的异同”) | 需要分别检索再交叉,效果不稳定 | 两个实体的关联属性在图中直接可比 |
| 时序关系题(”先发生什么后发生什么”) | 缺乏时序建模 | 可在关系边上标注时间属性 |
什么时候该用 GraphRAG:
适合 GraphRAG 的场景:
✓ 文档集中有大量实体间的关系信息
✓ 用户经常问多跳推理或全局摘要类问题
✓ 实体消歧是痛点(同一实体多种写法)
✓ 需要可解释的推理路径
不适合 GraphRAG 的场景:
✗ 文档量小(<100篇),图太稀疏没有意义
✗ 查询以单跳事实题为主,传统 RAG 就够了
✗ 对实时性要求极高——图构建和社区摘要是离线批处理,延迟高
✗ 预算有限——图构建需要大量 LLM 调用(实体抽取 + 社区摘要)
成本方面,GraphRAG 的图构建阶段需要对每个文档段落调用 LLM 提取实体和关系,再对每个社区调用 LLM 生成摘要。对于 10 万篇文档的知识库,图构建的 LLM 调用成本可能是传统 RAG embedding 计算的 10-50 倍。但构建完成后,查询阶段的成本差异不大。
差距在哪:新手把 GraphRAG 等同于”知识图谱检索”——这忽略了 GraphRAG 最核心的创新:社区摘要和本地/全局双搜索架构。高手的回答从传统 RAG 的瓶颈讲起,解释了 GraphRAG 的完整管线(实体抽取 → 图构建 → 社区检测 → 摘要生成 → 双路搜索),并用对比表格说清了不同查询类型下的效果差异和适用边界。面试官考的不是”你知不知道 GraphRAG”,而是”你能不能说清楚它比传统 RAG 好在哪、什么时候该用、什么时候不该用”。
追问:GraphRAG 召回海量关联信息后,生成阶段如何用 Self-Reflection 或 CoT 策略过滤检索噪声?
来源:蚂蚁 AI应用开发 二面
GraphRAG 的 Community Summary + 三元组召回会返回大量关联信息,其中不少是”图上相关但问题无关”的噪声。生成阶段的过滤策略:
CoT 策略(生成前过滤):在生成 Prompt 中要求模型先做一步”证据筛选”——逐条判断每个召回片段与当前问题的相关性,只保留高相关的片段再生成回答。这相当于在生成阶段内置了一个轻量 Rerank。
Self-Reflection 策略(生成后校验):先基于全部召回信息生成初版回答,然后用反思 Prompt 检查:”回答中是否引用了与问题无关的信息?是否遗漏了关键关联?” 发现问题后定向修正。
两者结合:CoT 做前置过滤降低噪声,Self-Reflection 做后置校验捕获遗漏。前者减少”答非所问”,后者减少”漏答关键点”。
追问:GraphRAG 的三元组是用 LLM 抽取的吗?怎么保证不幻觉?
来源:腾讯 AI 应用开发二面
是的,三元组(实体-关系-实体)通常由 LLM 抽取,但不能无条件信任 LLM 的抽取结果。
抽取流程:
文档段落 → LLM 提取实体 + 关系 → 结构化三元组 → 合并去重 → 入图
幻觉控制的四层防线:
- Prompt 约束:在抽取 Prompt 中明确要求”只提取文本中明确提到的实体和关系,不推断”。给出具体的输出格式和 Few-shot 示例,减少模型自由发挥的空间
- Schema 约束:预定义允许的实体类型和关系类型(如医学领域只允许”疾病-症状-药物-基因”四类实体)。LLM 抽出的实体必须属于预定义类型,否则丢弃
- 多轮验证:同一段文本让 LLM 抽取两次,只保留两次结果一致的三元组(投票机制)。或者用一个 Critic 模型检查抽取结果是否有文本依据
- 溯源校验:每个三元组保留源文本引用。后续使用时可以回查原文验证
实体抽取错误的排查方法:
- 随机抽样检查:从抽取结果中随机抽 100 条三元组,人工校验准确率
- 高频实体审查:按实体出现频次排序,高频实体更值得校验(一个错误的高频实体会污染大量关系)
- 孤立节点检查:只有一条边的实体往往是噪声
追问:Community Summary 的设计思路是什么?
来源:腾讯 AI 应用开发二面
Community Summary 是 GraphRAG 最核心的创新——把知识图谱从”查询时遍历”变成”预计算摘要”。
设计思路:
- 社区检测:用 Leiden 算法对知识图谱做社区划分,把紧密关联的实体聚成社区(类似”话题簇”)
- 层次化摘要:对每个社区调用 LLM 生成一段自然语言摘要,描述”这个社区里的实体之间有什么关系、核心主题是什么”
- 多层级:社区可以递归聚合——小社区合成大社区,大社区有更宏观的摘要。查询时根据问题的抽象程度选择合适的层级
为什么这样设计:传统知识图谱回答全局性问题(”这个领域的整体趋势是什么”)需要遍历大量节点,成本高且容易遗漏。Community Summary 把全局知识预压缩成摘要,查询时直接检索摘要即可——用离线计算换在线效率。
追问:GraphRAG 的叶子节点在智能客服中如何触发?
来源:币安 AI大模型实习一面
GraphRAG 的图结构是分层的:根节点(全局摘要)→ 社区节点(主题簇摘要)→ 实体节点 → 叶子节点(原始文档 chunk / 三元组细节)。不同粒度的问题触达不同层级。
触发机制——Global Search vs Local Search:
| 用户问题类型 | 搜索模式 | 是否触达叶子节点 | 示例 |
|---|---|---|---|
| 全局性/概览性 | Global Search | 否,社区摘要足够 | “你们有什么售后服务” |
| 具体实体细节 | Local Search | 是,需要原始文档细节 | “iPhone 15 能不能 7 天无理由退” |
| 需要外部数据 | Local + API 调用 | 是,且需调用外部系统 | “我的订单 123456 什么时候到” |
叶子节点的触发流程:
flowchart LR
Q[“用户 query”] --> NER[“实体识别\n提取产品名/订单号/功能名”]
NER --> LOC[“图定位\n在知识图谱中找到目标实体节点”]
LOC --> TRAV[“关系遍历\n沿实体的关系边向下到叶子节点”]
TRAV --> AGG[“上下文聚合\n叶子内容 + 关系链 + 社区摘要”]
AGG --> LLM[“LLM 生成回答”]
工程实现要点:用意图分类器判断问题粒度——粗粒度走 Global(快但粗),细粒度走 Local 到叶子节点(慢但准)。两者可以级联:先 Global 判断主题范围确认实体所属社区,再 Local 深入到叶子节点获取具体细节。
智能客服中的典型场景:用户问”我买的 XX 产品能退吗” → 实体识别提取产品名 → 图中定位到该产品节点 → 沿”退货政策”关系边遍历到叶子节点(具体退货条件、时间限制、操作步骤)→ 聚合上下文生成精确回答。
Q:Embedding 模型怎么选?选型时考虑哪些因素?
来源:高德 AI 应用开发实习一面
新手答:“用 OpenAI 的就行。”
高手答:
Embedding 模型选型不是“哪个最有名用哪个”,而是要从六个维度系统评估:
| 维度 | 考虑因素 | 说明 |
|---|---|---|
| 语言支持 | 中文/英文/多语言 | 中文场景优先选中文优化模型 |
| 向量维度 | 768/1024/1536 | 维度越高精度越好但存储和检索成本越大 |
| 检索精度 | MTEB/C-MTEB 排名 | 用公开基准评估基础能力 |
| 推理速度 | tokens/sec | 影响索引构建和在线检索延迟 |
| 部署成本 | 模型大小、GPU 需求 | 小模型可 CPU 部署 |
| 最大长度 | 512/8192 tokens | 影响 chunk 大小设计 |
主流模型对比:
| 模型 | 来源 | 特点 |
|---|---|---|
| BGE 系列 | BAAI(智源) | 中文效果好,开源,多尺寸可选 |
| M3E | Moka | 中文优化,轻量 |
| text-embedding-3 | OpenAI | 效果好但需 API 调用,成本高 |
| jina-embeddings-v3 | Jina AI | 多语言,支持长文本 |
| GTE | Alibaba | 中英文均衡 |
实际选型决策流程:
flowchart TB
S["开始选型"] --> L["确定语言需求"]
L -->|"中文为主"| CN["优先 BGE / M3E / GTE"]
L -->|"英文为主"| EN["优先 E5 / GTE / OpenAI"]
L -->|"多语言"| ML["优先 jina-embeddings-v3"]
CN --> EV["在自己的数据上做评估"]
EN --> EV
ML --> EV
EV --> COST["对比部署成本"]
COST --> FT{"是否需要微调?"}
FT -->|"领域术语多"| YES["选开源模型\n用业务数据微调"]
FT -->|"通用场景"| NO["直接部署\n定期评估效果"]
- 先确定语言需求:中文场景优先选中文优化模型,不要盲目选英文模型
- 跑 domain-specific evaluation:不只看 MTEB/C-MTEB 排名,必须在自己的数据上评估。公开 benchmark 上排名第一的模型,在你的业务数据上可能排第五
- 对比部署成本:API 调用模型(如 OpenAI)按量计费,自部署模型有 GPU 成本。数据量大时自部署更划算
- 考虑是否需要微调:领域术语多的场景(医疗、法律、金融),通用模型的 embedding 质量会明显下降,这时选开源模型做微调是更好的路径
关键认知:公开 benchmark 排名不等于你的场景效果。必须在自己的数据上做 A/B 评测——构建几百条 (query, relevant_doc) 的评估集,跑 Recall@K 和 MRR,用数据说话。
差距在哪:新手直接选最有名的模型。高手从六个维度系统评估,坚持在业务数据上做 domain-specific evaluation,而不是盲目跟 benchmark 排名。面试官考的是你选型时有没有工程化的方法论,以及对“通用模型 vs 领域适配”这个 tradeoff 的理解。
Q:知识库整体怎么设计?
来源:高德 AI 应用开发实习一面 【字节二面同题:RAG 完整流程从文档切块到生成】
新手答:“把文档存到向量数据库。”
高手答:
知识库不是“把文档扔进向量数据库”——它是一个完整的系统,涵盖文档处理、存储索引、检索召回、质量保障和运维五个层面。
整体架构:
flowchart TB
subgraph offline["离线处理"]
direction TB
RAW["原始文档\nPDF / Word / HTML / Markdown"] --> PRE["预处理\n格式转换、清洗、去噪"]
PRE --> CHK["分块\n递归分块 / 语义分块"]
CHK --> EMB["Embedding 计算"]
CHK --> META["元数据提取\n来源、日期、类别"]
EMB --> VDB["向量数据库\nMilvus / Qdrant"]
META --> MDB["元数据库\nElasticsearch / PostgreSQL"]
CHK --> INV["倒排索引\nElasticsearch"]
end
subgraph online["在线检索"]
direction TB
Q["用户 Query"] --> QR["查询改写"]
QR --> MR["多路检索\n向量 + BM25 + 元数据过滤"]
MR --> RR["Rerank 精排"]
RR --> CA["上下文组装"]
CA --> LLM["LLM 生成回答"]
end
五层设计:
1. 文档处理层——把非结构化文档变成可检索的结构化数据:
- 格式解析:PDF / Word / HTML → 纯文本。不同格式的解析难度差异很大——PDF 的表格和多栏排版是重灾区
- 清洗去噪:去掉页眉页脚、广告、导航栏等无关内容
- 元数据提取:来源、创建日期、类别、作者——这些元信息在检索时用于过滤和排序
2. 索引层——三种索引各司其职:
- 向量索引(FAISS / Milvus):语义检索,处理同义改写和模糊表述
- 倒排索引(Elasticsearch):关键词检索,处理精确术语和专有名词
- 元数据索引(过滤字段):结构化筛选,处理时间范围、文档类别等约束
为什么需要三种?因为不同类型的查询需要不同的索引——“退货政策”需要语义检索,“ERR_OOM_KILL”需要关键词检索,“最近一周的变更记录”需要元数据过滤。
3. 检索层——查询理解 + 多路召回 + 精排:
- 查询改写(多维度扩展用户意图)
- 多路召回(向量 + BM25 + 元数据过滤并行)
- Rerank 精排(Cross-Encoder 细粒度打分)
- 上下文组装(Top-K 截断、信息去冗余)
4. 质量保障层——知识库不是建完就不管了:
- 文档去重:内容指纹检测近重复文档
- 新鲜度追踪:过期文档标记或降权
- 冲突检测:同一主题的矛盾信息标记提醒
- 覆盖分析:哪些主题的文档充足,哪些有缺口
5. 运维层——保证知识库持续可用:
- 增量更新管线:新文档自动入库,变更文档自动重新索引
- 版本管理:文档变更可追溯
- 访问控制:不同用户看到不同范围的文档
- 监控:检索命中率、用户反馈、知识库健康度
关键设计决策:
| 决策点 | 选项 | 权衡 |
|---|---|---|
| Chunk 大小 | 256 / 512 / 1024 tokens | 粒度越小检索越精确,但上下文越不完整 |
| 单索引 vs 多索引 | 只用向量 vs 向量 + BM25 + 元数据 | 多索引召回率高,但系统复杂度增加 |
| 实时更新 vs 批量更新 | 文档变更立即入库 vs 每天定时批量处理 | 实时性 vs 系统稳定性和计算成本 |
差距在哪:新手认为知识库就是向量数据库。高手设计了五层系统——文档处理、索引、检索、质量保障、运维——覆盖了从数据入库到持续运维的完整生命周期。面试官考的是你对知识库的认知是“一个组件”还是“一套系统”。
Q:分块策略怎么设计?不同策略的优缺点?
来源:高德 AI 应用开发实习一面
新手答:“按 500 字切一段。”
高手答:
分块(Chunking)是 RAG 中影响效果最大的单一设计决策——切得不好,后面的检索和生成再怎么优化都救不回来。
五种主流分块策略对比:
| 策略 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 固定长度分块 | 按 token 数切分 | 实现简单、大小可控 | 可能切断语义 | 结构不清晰的纯文本 |
| 语义分块 | 按段落/章节/标题切分 | 保持语义完整 | 块大小不均匀 | 有清晰结构的文档 |
| 递归分块 | 先尝试大单元分割,不满足再细分 | 平衡语义和大小 | 实现复杂 | 通用场景首选 |
| Sliding Window | 固定大小 + 重叠区域 | 防止边界信息丢失 | 存储冗余 | 精度要求高的场景 |
| Agentic Chunking | 用 LLM 判断分块边界 | 最智能 | 成本高、速度慢 | 高价值文档 |
关键参数设计:
Chunk Size(块大小):256 ~ 1024 tokens
太小(< 200 tokens)→ 语义不完整,一个 chunk 可能只有半句话
太大(> 1500 tokens)→ 噪声多,embedding 被多个主题稀释,召回精度低
Overlap(重叠区域):chunk size 的 10 ~ 20%
作用:防止切断句子或段落,保证边界处的信息在相邻 chunk 中都有
Metadata(元信息):每个 chunk 保留——
source:来源文档标识
page:所在页码
section_title:所属章节标题
parent_doc_id:父文档 ID(用于父子索引)
父子索引策略——同时解决检索精度和上下文完整性:
flowchart TB
DOC["原始文档"] --> PARENT["父 chunk(500-1000 tokens)\n完整段落,上下文丰富"]
PARENT --> CHILD1["子 chunk A(150-300 tokens)\n精确语义,检索用"]
PARENT --> CHILD2["子 chunk B(150-300 tokens)"]
PARENT --> CHILD3["子 chunk C(150-300 tokens)"]
Q["用户 Query"] --> SEARCH["用 Query 匹配子 chunk\n(小粒度,精度高)"]
SEARCH --> RETURN["返回对应的父 chunk\n(大粒度,上下文完整)"]
用小 chunk 做检索(精度高),返回大 chunk 给 LLM(上下文完整)——两全其美。
递归分块的实现逻辑:
分割优先级:
1. 先按 "\n\n"(段落)分割
2. 段落仍然太大 → 按 "\n"(换行)分割
3. 还是太大 → 按 "。"(句号)分割
4. 再不行 → 按空格或字符数强制切分
每一级都尽可能保持语义完整,只有上一级切不够小时才降到下一级。
实践建议:从递归分块(LangChain 的 RecursiveCharacterTextSplitter)作为 baseline 开始,然后根据文档类型和评估结果调整。不同类型的文档用不同策略——技术文档用语义分块(按标题切),日志文件用固定长度分块,合同文档用 Agentic Chunking。
差距在哪:新手用固定长度一刀切,不考虑语义。高手对比了五种策略的优劣势,理解 chunk size 和语义完整性的 tradeoff,用父子索引同时满足检索精度和上下文质量,并且知道分块策略必须根据文档类型和评估结果来调整——不存在”万能的分块方案”。面试官考的是你对 RAG 管线中最关键的预处理环节有没有深入的工程理解。
追问:chunk 切分的边界修正怎么做?比如表格被切成了两半怎么处理?
来源:腾讯 AI 应用开发二面
边界修正是分块策略中最容易被忽视的工程细节。切错边界比不切更糟——一个被拦腰截断的表格,检索到任何一半都无法提供完整信息。
主流边界修正策略:
- 结构感知切分:先用文档解析器识别出结构元素(标题、段落、表格、代码块、列表),以完整结构元素为最小切分单位,绝不从结构内部切断
- Overlap(重叠窗口):相邻 chunk 之间保留 10-20% 的重叠区域。即使切断了,下一个 chunk 的开头会包含上一个的结尾,信息不会完全丢失
- 表格特殊处理:表格作为独立 chunk,不参与常规切分。如果表格超长,按行切分但每个子 chunk 都保留表头(表头是理解表格的关键)
表格被切成两半的检测和修复:
检测:解析文档时标记所有表格的起止位置 → 切分后检查是否有 chunk 边界
落在表格内部 → 有则标记为”边界异常”
修复:把跨越表格的两个 chunk 合并,或者把表格提取为独立 chunk
实际工程中,最稳的做法是先提取后切分:先用文档解析器把表格、图片、代码块等结构化元素单独提取出来,剩余的纯文本再做常规分块。这样结构化元素天然不会被切断。
追问:领域文档(法律/医疗)的语义单元跨 chunk 被切散,怎么办?
来源:字节 AI 一面(法律 RAG 项目深挖)
法律文档中,“第三条第二款”和“第三条之二”是完全不同的法律概念——前者是第三条下的第二个分款,后者是独立的第三条之二。如果切片把这类语义单元切散,模型会混淆引用关系,直接导致回答错误。
领域感知切片的四层方案:
| 层级 | 做什么 | 具体实现 |
|---|---|---|
| 结构解析 | 用领域规则识别语义边界 | 法律:条/款/项层级正则;医疗:诊断-处方-医嘱分段 |
| 边界保护 | 语义单元不可被切断 | 将“第X条”到“第X+1条”之间的内容视为原子块 |
| 层级 metadata | 每个 chunk 携带完整的上下文链 | {law: "民法典", chapter: "第三编", article: "第三条", clause: "第二款"} |
| 交叉引用 | 法条互相引用时双向关联 | “依据第五条”→ 在 chunk metadata 中标记依赖关系 |
核心原则:通用切片策略(按 token 数、按段落)是 baseline,领域文档必须叠加领域感知的边界规则。切片粒度不是工程参数,是业务决策——需要和领域专家一起定义“什么是不可切断的最小语义单元”。
Q:为什么 Claude Code 不用 RAG 检索代码,而是直接用 grep?
来源:字节 Agent 开发实习一面
新手答:“可能是还没来得及实现 RAG。”
高手答:
这道题非常好,它直接挑战了“RAG 万能论”的假设。答案是:对于代码检索场景,grep 在多个维度上优于 RAG,这是一个深思熟虑的架构选择,而不是技术局限。
grep 胜过 RAG 的五个原因:
| 维度 | grep | RAG |
|---|---|---|
| 精确性 | grep "functionName" 精确匹配,100% 准确率 |
向量检索返回“语义相似”的结果,可能漏掉目标函数 |
| 实时性 | 直接操作当前文件系统,永远是最新的 | 需要索引,代码一改索引就可能过时(开发时代码频繁变动) |
| 成本 | 纯 CPU 计算,免费,耗时 <100ms | 需要 Embedding 计算、向量数据库,索引需随代码变更重建 |
| 可解释性 | 结果确定且可追溯——匹配到哪个文件第几行一清二楚 | 结果取决于 Embedding 质量,无法解释“为什么返回这个结果” |
| 代码结构利用 | 代码高度结构化(函数、类、import 有固定模式),grep + 正则能精准利用这种结构 | Embedding 会丢失代码的结构信息,把代码当成普通文本处理 |
什么时候 RAG 比 grep 更好:
grep 不是万能的,以下场景 RAG 占优:
自然语言查询:"认证逻辑在哪里?"
→ grep 无法处理,因为代码中没有"认证逻辑"这个字符串
→ RAG 的语义检索能找到 auth_middleware.py、login_handler.py
概念级搜索:"找到类似的实现"
→ grep 只能匹配精确字符串
→ 向量检索能找到语义相似的代码片段
跨模块依赖分析:"哪些模块依赖了这个服务?"
→ grep 能做简单的 import 搜索,但知识图谱 / RAG 能发现间接依赖
Claude Code 的实际策略——渐进式披露(Progressive Disclosure):
flowchart TB
Q["需要查找代码信息"] --> C1{"精确符号查找?"}
C1 -->|"是"| G["grep / find\n(免费、精确、实时)"]
C1 -->|"否"| C2{"需要目录结构?"}
C2 -->|"是"| LS["ls / directory listing\n(零成本)"]
C2 -->|"否"| C3{"需要语义理解?"}
C3 -->|"是"| R["Read 文件内容\n(按需加载)"]
C3 -->|"否"| G
核心原则是从最廉价、最精确的工具开始,按需逐步升级到更昂贵的工具。grep 和 find 是第一层(零成本、高精度),读文件是第二层(中等成本),只有在前两层无法满足需求时才考虑更重的检索手段。
这道题的 meta 启示:
最佳检索方法取决于查询类型。不要默认使用 RAG——有时候更简单的工具反而更好。这个原则不仅适用于代码检索,也适用于所有 Agent 的工具选择:先用廉价精确的工具,只在必要时才升级到昂贵复杂的工具。这就是渐进式披露在工具选择层面的体现。
差距在哪:新手假设 RAG 是万能的,认为不用 RAG 是技术缺陷。高手理解 grep 在精确性、实时性、成本、可解释性和代码结构利用五个维度上的优势,并且看到了 Claude Code 的渐进式披露策略是一个深思熟虑的架构选择——从廉价精确工具开始,按需升级。面试官考的是你能不能跳出“RAG 万能论”的思维定式,根据场景选择最合适的检索方案。
Q:向量数据库怎么选型?不同规模下该用什么方案?
来源:阿里国际 AI 应用研发二面 【淘天Agent开发追问:为什么选 pgvector 而不是其他向量数据库】
新手答:“用 Milvus 就行。”
高手答:
向量数据库的选型不是“挑一个最有名的”,而是按数据规模分层选择,再根据业务需求做筛选。
按数据规模分层推荐:
小规模(< 10 万条向量):pgvector / SQLite-VSS
直接在现有关系型数据库上加向量扩展。运维成本几乎为零,不需要额外部署服务。对于早期项目和 PoC 阶段完全够用。
中等规模(10 万 - 1000 万条向量):Milvus Lite / Qdrant / Weaviate
需要专用的向量数据库。这个量级对索引类型(HNSW / IVF)、过滤能力、持久化都有要求。这三个都支持元数据过滤,且社区活跃、文档完善。
大规模(1000 万条以上):Milvus 集群 / Pinecone / Zilliz Cloud
需要分布式架构和水平扩展能力。Milvus 集群支持多副本和分片;Pinecone 和 Zilliz Cloud 是全托管服务,运维成本低但按量计费。
选型维度对比:
| 选型维度 | 关键问题 | 影响选择 |
|---|---|---|
| 数据规模 | 当前多少条?预期增长到多少? | 决定单机 vs 分布式 |
| 过滤需求 | 是否需要 metadata 过滤(按时间、类别筛选)? | 排除不支持过滤的纯 ANN 方案 |
| 延迟要求 | P99 延迟要求多少 ms? | 影响索引类型和部署架构 |
| 运维复杂度 | 团队有没有专人运维? | 决定自建 vs 托管服务 |
| 成本 | 预算约束是什么? | 开源自建 vs 商业托管 |
| 混合检索 | 是否需要向量 + 关键词混合搜索? | 部分方案原生支持(Qdrant、Weaviate),部分需要外接 ES |
关键考量:大多数真实 Agent 场景都需要元数据过滤——比如“只检索最近 30 天的文档”“只查某个品类下的内容”。这个需求会直接淘汰一些纯 ANN 方案(如裸用 FAISS),因为它们不支持带条件的向量检索。
生产建议:先简单后迁移。项目初期用 pgvector 快速验证效果,等数据量或性能真正成为瓶颈时再迁移到专用向量数据库。过早引入分布式向量数据库是典型的过度工程——你花两周部署 Milvus 集群,结果数据才 5 万条,pgvector 10ms 就能搞定。
差距在哪:新手只给一个品牌名。高手按数据规模分层推荐,且给出了选型维度和“先简单后迁移”的工程建议。面试官考的是你对向量检索基础设施的全面认知。
Q:升级 Embedding 模型后,怎么保证索引和检索向量的逻辑一致性?
来源:阿里国际 AI 应用研发二面
新手答:“重新跑一遍索引就行了。”
高手答:
核心问题:旧文档用 Model_v1 编码入库,新查询用 Model_v2 编码检索——两个模型的向量空间不对齐,导致检索质量断崖式下降。即使两个模型都很强,它们学到的向量空间是不同的,v1 空间里的“近邻”在 v2 空间里可能完全不是近邻。
“全量重建索引”是正确的方向,但对于百万级文档,全量重建需要数小时甚至数天,在此期间系统要么不可用,要么用旧模型继续服务(效果变差)。所以真正的挑战不是“能不能重建”,而是“怎么平滑过渡”。
三种升级策略:
策略一:双索引并行
在旧索引继续服务的同时,后台用新模型构建新索引。
新索引构建完成后,做一次原子切换——流量瞬间从旧索引切到新索引。
优点:切换瞬间完成,用户无感知。缺点:过渡期需要双倍存储成本,且切换前无法验证新索引的线上效果。
策略二:渐进式迁移
给每个文档向量打上 embedding_model_version 标签。
过渡期内,查询同时用 v1 和 v2 两个模型编码,分别检索对应版本的分区,合并结果。
后台逐步将旧文档用新模型重新编码,完成后删除旧分区。
优点:平滑过渡,任何时刻系统都可用。缺点:过渡期查询要编码两次、搜索两次,延迟翻倍。
策略三:版本化索引 + 灰度切换
新索引构建完成后,不立即全量切换,而是先导流 5-10% 到新索引。
对比新旧索引的线上指标(召回率、相关性、用户点击率)。
指标达标 → 逐步扩大流量比例直到全量切换。
指标异常 → 立即回滚到旧索引。
优点:风险最低,有数据验证。缺点:验证周期长,需要流量分配基础设施。
完整的双索引过渡流程:
flowchart TB
START["触发模型升级"] --> BUILD["后台构建新索引\n(Model_v2 编码全量文档)"]
BUILD --> OLD["旧索引继续服务\n(Model_v1)"]
BUILD --> DONE{"新索引\n构建完成?"}
DONE -->|"否"| OLD
DONE -->|"是"| CANARY["灰度切换\n5% 流量到新索引"]
CANARY --> CHECK{"质量指标\n达标?"}
CHECK -->|"是"| SCALE["逐步扩大流量\n10% → 50% → 100%"]
CHECK -->|"否"| ROLLBACK["回滚到旧索引\n排查问题"]
SCALE --> FULL["全量切换完成"]
FULL --> CLEAN["清理旧索引\n释放存储"]
工程保障措施:
- 始终存储原始文本:向量可以重算,原始文本不能丢。每个 chunk 必须保留原始文本,这样任何时候都能用新模型重新编码
- 版本标记每个向量:
embedding_model_version字段必须持久化到向量记录中,方便区分哪些文档已迁移、哪些还没有 - 自动化质量回归测试:维护一组
(query, expected_relevant_docs)的黄金测试集,每次模型升级后自动跑 Recall@K 和 MRR,低于阈值则阻断上线
差距在哪:新手只想到“重建”——这是正确的第一步但忽略了过渡期的可用性。高手给出了双索引并行、渐进式迁移、灰度切换三种策略,且强调了版本标记和自动化质量回归测试。面试官考的是你对向量检索系统升级的工程化思维——不是“能不能升级”,而是“怎么平滑升级”。
进阶检索架构
Q:Deep Research 在代码层面是怎么实现的?和普通 RAG 有什么区别?
来源:bilibili AI研发实习一面
新手答:“就是多搜几次,然后总结一下。”
高手答:
Deep Research 不是“多搜几次”——它是一种Agent 驱动的迭代检索-合成架构,核心区别在于检索策略是动态的、由 Agent 在运行时根据已有信息决定下一步查什么。
和普通 RAG 的本质区别:
| 维度 | 普通 RAG | Deep Research |
|---|---|---|
| 检索次数 | 一次(query → 检索 → 生成) | 多次迭代(每轮检索结果影响下一轮查询) |
| 查询生成 | 用户原始 query 或简单改写 | Agent 根据已有信息动态生成子查询 |
| 知识综合 | 拼接检索结果给模型 | 逐步构建结构化的知识图,跨文档关联 |
| 覆盖判断 | 无——检索一次就结束 | Agent 评估“信息是否足够”,不够则继续检索 |
| 输出 | 短回答 | 长篇结构化报告 |
代码层面的核心实现:
用 LangGraph 实现 Deep Research 的典型架构:
flowchart TB
Q["用户研究问题"] --> PLAN["规划节点\n生成研究大纲 + 子问题列表"]
PLAN --> SEARCH["检索节点\n对当前子问题做多源检索"]
SEARCH --> ANALYZE["分析节点\n提取关键信息 + 评估覆盖率"]
ANALYZE --> CHECK{"覆盖率\n足够?"}
CHECK -->|"否:发现知识缺口"| REFINE["改写节点\n生成新的子查询"]
REFINE --> SEARCH
CHECK -->|"是"| SYNTH["综合节点\n跨文档知识整合"]
SYNTH --> REPORT["报告生成节点\n结构化输出 + 引用标注"]
关键实现细节:
1. 规划节点——把大问题拆成可检索的子问题
def plan_node(state):
prompt = f"""将以下研究问题拆解成 3-5 个具体的子问题,
每个子问题应该能通过一次文献检索回答:
研究问题:{state["research_question"]}
"""
sub_questions = llm.invoke(prompt)
return {"sub_questions": sub_questions, "current_index": 0}
2. 检索节点——对每个子问题做多源并行检索
async def search_node(state):
query = state["sub_questions"][state["current_index"]]
results = await asyncio.gather(
search_semantic_scholar(query),
search_arxiv(query),
search_web(query),
)
merged = deduplicate(flatten(results))
return {"search_results": merged}
和普通 RAG 只查一个向量库不同,Deep Research 通常并行查询多个数据源(学术数据库、网页、专利库),然后合并去重。
3. 分析节点——提取信息并评估覆盖率
这一步是 Deep Research 的核心——Agent 不只是检索,还要评估检索结果的充分性。如果发现知识缺口(某个子问题还没有足够的证据支撑),生成新的查询再检索。
def analyze_node(state):
extracted = llm.invoke(f"""从以下检索结果中提取关键信息:
{state["search_results"]}
已有知识:{state.get("accumulated_knowledge", "")}
当前子问题:{state["sub_questions"][state["current_index"]]}
输出:1. 关键发现 2. 置信度评估 3. 知识缺口
""")
return {
"accumulated_knowledge": state.get("accumulated_knowledge", "") + extracted.findings,
"gaps": extracted.gaps,
"coverage_sufficient": extracted.confidence > 0.8
}
4. 迭代控制——防止无限检索
def should_continue(state):
if state["coverage_sufficient"]:
return "synthesize"
if state["iteration_count"] >= MAX_ITERATIONS: # 通常 3-5 轮
return "synthesize"
return "refine_query"
必须设置最大迭代次数,否则 Agent 可能陷入“总觉得信息不够”的无限循环。
5. 综合节点——跨文档知识整合
普通 RAG 直接把检索结果拼给模型生成答案。Deep Research 需要先做跨文档的知识整合——对比不同来源的观点、识别共识和争议、构建时间线和因果关系——然后基于整合后的结构化知识生成报告。
工程上的核心难点:
| 难点 | 解决方案 |
|---|---|
| 迭代多轮导致上下文膨胀 | 每轮只保留结构化摘要,原始检索结果不进入下一轮上下文 |
| 多源检索结果质量参差不齐 | 来源权威性评分 + ReRank 精排 |
| 幻觉引用风险高 | 所有事实必须关联到具体检索结果的 ID,生成后做引用校验 |
| 检索成本高 | 设最大迭代次数 + 子问题优先级排序(先查最关键的) |
差距在哪:新手把 Deep Research 理解为”多搜几次加总结”。高手在代码层面展示了完整的迭代检索-分析-改写循环,核心是 Agent 驱动的动态查询生成和覆盖率评估——每轮检索的 query 不是预设的,而是根据已有信息实时生成的。面试官考的是你能不能把 Deep Research 从产品概念转化为可实现的 Agent 管线。
Q:PDF 解析用什么工具?Layout-aware Parsing 是怎么做的?
来源:腾讯 AI 应用开发二面
新手答:”用 PyPDF 读文本就行。”
高手答:
PyPDF 只能提取纯文本流,完全丢失版面布局信息——表格变成散乱的文字、双栏论文的左右栏被混在一起、图片标注和正文混淆。RAG 的质量上限取决于解析质量,解析错了后面全链路都救不回来。
主流 PDF 解析工具对比:
| 工具 | 原理 | 优势 | 劣势 | 适合场景 |
|---|---|---|---|---|
| PyPDF / pdfplumber | 文本流提取 | 快、零依赖 | 丢失布局,表格乱 | 纯文本 PDF |
| Unstructured | 规则 + ML 混合 | 支持多格式,社区活跃 | 表格准确率一般 | 通用文档 |
| Marker | 视觉模型 | 版面还原度高 | 速度慢 | 学术论文 |
| MinerU (PDF-Extract-Kit) | 版面检测 + OCR | 表格还原强,开源 | 资源消耗大 | 复杂排版 PDF |
| 商业方案(Azure Document Intelligence) | 云端 ML | 准确率最高 | 收费、数据出境 | 企业级生产 |
Layout-aware Parsing 的核心思路:
flowchart LR
A[“PDF 页面图像”] --> B[“版面检测\n(识别文本/表格/图片区域)”]
B --> C[“区域分类\n(标题/正文/表格/脚注)”]
C --> D[“分区提取\n(文本用OCR,表格用专门解析器)”]
D --> E[“阅读顺序重建\n(双栏→单栏序列)”]
E --> F[“结构化输出\n(带标签的文本块)”]
关键是先”看”再”读”——不是直接提取文本流,而是先用视觉模型识别页面上每个区域的类型和位置,然后按阅读顺序依次提取。
选型建议:学术论文用 Marker 或 MinerU(版面复杂但格式相对规范),企业文档用 Unstructured + 自定义后处理,对准确率要求极高的场景用商业方案。没有万能工具,必须横向对比——用同一批标注好的文档测试不同工具的准确率,选最适合自己文档类型的。
差距在哪:新手用 PyPDF 读文本,遇到表格和复杂排版就束手无策。高手理解 Layout-aware Parsing 的核心是”先视觉理解版面结构,再分区提取内容”,并能对比不同工具的适用场景做选型。面试官考的是你对 RAG 管线最上游(文档解析)的工程理解——这一步的质量决定了后续所有环节的上限。
Q:RAG 知识库的噪声剔除和文档去重怎么做?
来源:腾讯 AI 应用开发二面 【淘天Agent开发追问:防止 AI 批量生成虚假数据投毒】
新手答:”看着删。”
高手答:
知识库的质量直接决定 RAG 的效果上限。垃圾进,垃圾出——再好的检索和生成也无法弥补知识库本身的噪声。
噪声剔除分三层:
| 层次 | 噪声类型 | 剔除方法 |
|---|---|---|
| 文档级 | 空文档、格式损坏、非目标语言 | 规则过滤(长度/编码/语种检测) |
| 段落级 | 页眉页脚、版权声明、目录、广告 | 正则匹配 + 分类器(训练一个轻量模型区分正文和噪声) |
| 语义级 | 无信息量的废话、过时信息 | 信息密度评分(困惑度过低的段落往往是套话) |
文档去重的三种策略:
- 精确去重(URL / 文件哈希):对文档的 URL 或内容 MD5 去重,处理完全相同的文档。最快,但无法处理”内容相同但格式不同”的情况
- 近似去重(MinHash / SimHash):计算文档的指纹,相似度超过阈值(如 0.9)的文档只保留一篇。适合处理”同一篇文章的不同版本”
- 语义去重(Embedding 聚类):对所有文档做 Embedding,用 DBSCAN 聚类,同一簇内保留质量最高的一篇。计算成本高,但能处理”不同表述的相同内容”
重复片段识别:
多篇文章中出现的重复段落(如”免责声明””公司简介”)比整篇重复更隐蔽。处理方法:
1. 对所有 chunk 计算 SimHash 指纹
2. 按指纹分桶,同桶内的 chunk 两两比较文本相似度
3. 相似度 > 0.85 的标记为重复片段
4. 保留出现次数最多的那一份,其余删除或标记为”低优先级”
差距在哪:新手要么不做清洗(让噪声进入检索),要么靠人工逐条检查(不可扩展)。高手从文档级/段落级/语义级三层做噪声剔除,用精确/近似/语义三种策略做去重,形成了可自动化、可扩展的知识库清洗管线。面试官考的是你对 RAG 数据质量的工程化管控能力。
Q:图检索、向量检索、混合检索有什么区别?怎么选?
来源:腾讯 AI 应用开发二面
新手答:”向量检索最常用,图检索更准。”
高手答:
三种检索方式解决的是不同类型的查询问题,不存在”哪个更准”:
| 维度 | 向量检索 | 混合检索 | 图检索(GraphRAG) |
|---|---|---|---|
| 检索原理 | 语义相似度匹配 | 语义 + 关键词双路 | 实体关系遍历 + 社区摘要 |
| 擅长的查询 | “XX 是什么””如何做 XX” | 既需要语义又需要精确匹配 | “XX 和 YY 有什么关系””整体趋势” |
| 不擅长的查询 | 多跳推理、关系型问题 | 全局性总结类问题 | 简单事实问答 |
| 索引构建成本 | 低(只需 Embedding) | 中(Embedding + 倒排索引) | 高(LLM 抽取实体 + 图构建) |
| 查询延迟 | 毫秒级 | 毫秒级 | 秒级(需图遍历) |
| 维护成本 | 低(增量更新简单) | 中 | 高(实体变更需重建子图) |
选型决策树:
查询类型主要是什么?
├── 事实问答(”XX 的创始人是谁”)→ 混合检索(关键词精确 + 语义兜底)
├── 概念解释(”什么是 RAG”)→ 向量检索(语义匹配足够)
├── 关系推理(”A 和 B 有什么关联”)→ 图检索
├── 全局总结(”这个领域的趋势”)→ 图检索(Community Summary)
└── 混合场景 → 多路检索 + ReRank 融合
生产系统的常见做法:不选一种,而是多路并行、统一精排。向量检索和关键词检索作为基础双路,图检索作为补充路(只在检测到关系型查询时启用)。最后用 ReRank 模型统一排序,取 Top-K 送给生成模型。
差距在哪:新手把三种检索当成”好/更好/最好”的关系。高手理解它们解决的是不同类型的查询问题——向量检索擅长语义匹配,混合检索兼顾精确和语义,图检索擅长关系推理和全局总结。选型依据不是”哪个更先进”,而是”你的查询类型分布是什么”。面试官考的是你对多种检索范式的系统性理解和选型能力。
Q:多模态 Embedding 检索中,文本语义与图像视觉特征的权重怎么平衡?用户检索图纸参数却召回外观相似零件,根源是什么?
来源:阿里淘天 AI应用开发一面
新手答:“把文本和图片都转成向量,拼一起搜。”
高手答:
多模态检索的核心矛盾是不同模态的语义空间天然不对齐——文本描述的是抽象概念(尺寸、材质、公差),图像编码的是视觉特征(颜色、形状、纹理)。“拼一起搜”忽略了这个根本问题。
两种融合方式:
| 融合方式 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| Early Fusion(CLIP-style 联合空间) | 文本和图像在训练时就映射到同一个向量空间 | 端到端优化,跨模态匹配能力强 | 通用模型学到的是“外观相似性”,不理解工程参数 |
| Late Fusion(分别编码再加权) | 文本和图像各自编码,检索时加权合并分数 | 权重可灵活调整,各模态可独立优化 | 跨模态交互弱,依赖权重调参 |
权重平衡方案:
静态权重:final_score = α × text_sim + (1-α) × image_sim,α 是固定值(如 0.6)。简单但不灵活——参数查询和外观查询用同一个权重显然不合理。
动态权重(更优方案):根据 query 类型自动调整权重——
Query 类型判断:
"M8×1.25 螺栓 材质 304" → 参数查询 → α=0.9(几乎全靠文本)
"这个零件长什么样" → 外观查询 → α=0.2(主要靠图像)
"找类似这张图纸的零件" → 混合查询 → α=0.5(均衡)
实现方式:用一个轻量分类器(或规则引擎)先判断 query 类型,再动态设置 α。
图纸参数召回外观相似零件的根源分析:
问题不在检索算法,在 Embedding 模型。通用视觉模型(如 CLIP)在预训练时学到的是外观级别的相似性——颜色、形状、轮廓。它不理解工程图纸中的参数语义(尺寸标注、公差范围、材质标签)。所以当用户搜“M8×1.25 不锈钢螺栓”时,模型返回的是外观最像螺栓的图片,而不是参数匹配的零件。
三种解决方案对比:
| 方案 | 做法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 通用视觉模型(CLIP) | 直接用预训练模型做多模态检索 | 开箱即用,零成本 | 不理解工程参数,召回靠外观 | PoC 阶段、外观检索 |
| 领域微调模型 | 用工程图纸 + 参数标注数据微调 Embedding | 理解领域语义,参数匹配准确 | 需要标注数据,训练成本高 | 参数检索精度要求高 |
| 混合检索方案 | 参数字段走精确匹配,外观走向量检索 | 各取所长,参数精确、外观覆盖 | 系统复杂度高,需维护两套索引 | 生产环境首选 |
推荐的生产方案——混合检索:
结构化参数(尺寸、材质、公差)→ 标量过滤(精确匹配 / 范围查询)
外观特征(形状、颜色) → 向量检索(视觉相似度)
两路结果取交集或加权合并
结构化参数不应该走向量检索——“M8×1.25”这种精确规格用 Embedding 编码后会丢失精确性,直接用结构化字段过滤才是正确做法。向量检索只负责外观维度的相似度匹配。
差距在哪:新手把多模态当成“拼向量”。高手理解多模态检索的核心矛盾——不同模态的语义空间不对齐,权重平衡不是调参数,是选架构。面试官考的是你能不能从一个检索失败案例反推出系统设计缺陷。
Q:向量数据库中需要限定时间范围检索时,标量条件过滤怎么高效实现?
来源:阿里淘天 AI应用开发一面
新手答:“先向量检索再过滤掉不符合时间的。”
高手答:
“先搜后滤”(Post-filter)是最直觉但最低效的方案——放大 K 倍检索再过滤,过滤率高时大量计算浪费,且可能凑不够 K 条结果。
三种策略对比:
| 策略 | 做法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| Pre-filter | 先用标量索引(B-tree / bitmap)筛出时间范围内的文档 ID 集合,再在该子集上做 ANN 搜索 | 结果精确,不浪费向量计算 | 时间范围内文档太少时 ANN 效果退化(候选集不够大) | 过滤条件选择性高(筛掉大部分数据) |
| Post-filter | 先做 ANN 搜索取 top-K×M(放大 K),再用标量条件过滤 | 实现简单,不影响 ANN 索引结构 | 过滤率高时计算浪费严重,可能凑不够 K 条结果 | 过滤条件选择性低(只筛掉少量数据) |
| Hybrid | 在 ANN 搜索过程中同时检查标量条件,边搜边滤 | 平衡精度和效率 | 实现复杂,需要索引层支持 | 通用场景 |
Pre-filter 的详细流程:
1. 用标量索引快速筛选:SELECT doc_id FROM docs WHERE timestamp > 1700000000
→ 得到候选 ID 集合(毫秒级,B-tree 范围查询)
2. 在候选 ID 子集上做 ANN 搜索:只在这些向量上计算距离
→ 得到 top-K 结果(精确且无浪费)
Milvus 具体实现:
Milvus 支持 boolean expression 做 pre-filter,底层在 segment 级做分区裁剪(partition pruning):
# Milvus pre-filter 示例
results = collection.search(
data=[query_vector],
anns_field="embedding",
param={"metric_type": "COSINE", "params": {"nprobe": 10}},
limit=10,
expr="timestamp > 1700000000 and timestamp < 1710000000" # 标量过滤
)
最佳实践:
- 时间维度做 partition key:按时间范围(如按月)对数据做分区,查询时直接跳过不相关的分区,连标量过滤都省了
- 高基数标量字段建 scalar index:对 timestamp、category 等字段建标量索引,加速过滤
- Pre-filter 优先:除非过滤条件非常宽松(只筛掉 <10% 数据),否则 pre-filter 几乎总是优于 post-filter
- 监控过滤率:如果 post-filter 丢弃了超过 50% 的 ANN 结果,说明应该切换到 pre-filter
flowchart TB
Q["用户 Query + 时间范围"] --> D{"过滤选择性?"}
D -->|"高(筛掉 >50% 数据)"| PRE["Pre-filter\n先标量筛选再 ANN"]
D -->|"低(筛掉 <10% 数据)"| POST["Post-filter\n先 ANN 再标量过滤"]
D -->|"中等"| HYB["Hybrid\n边搜边滤"]
PRE --> R["Top-K 结果"]
POST --> R
HYB --> R
差距在哪:新手用”先搜后滤”的暴力方案。高手对比了 pre/post/hybrid 三种策略,理解 Milvus partition pruning 机制。面试官考的是你对向量数据库工程实现的理解深度。
Q:Agentic RAG 是什么?和传统 RAG 的核心区别?
来源:美团Keeta Agent开发一面
新手答:「就是把 RAG 和 Agent 结合起来吧。」
高手答:
传统 RAG 是单轮管线:query → 检索 → 生成,一次检索定胜负。Agentic RAG 的本质变化是把 Agent 的推理循环引入检索过程——检索不再是单次操作,而是 Agent 的一个可反复调用的工具。
两者的核心架构差异:
flowchart LR
subgraph trad[“传统 RAG”]
Q1[“Query”] --> R1[“检索”] --> G1[“生成”]
end
subgraph agentic[“Agentic RAG”]
Q2[“Query”] --> A[“Agent 推理”]
A -->|”判断需要什么信息”| R2[“检索工具”]
R2 -->|”结果不够/不对”| A
A -->|”需要补充”| R3[“二次检索/换策略”]
R3 --> A
A -->|”信息充足”| G2[“生成”]
end
| 维度 | 传统 RAG | Agentic RAG |
|---|---|---|
| 检索决策 | 固定管线,query 进来就检索 | Agent 判断是否需要检索、检索什么 |
| 检索次数 | 单次 | 多次迭代,根据结果质量决定是否继续 |
| Query 改写 | 预定义规则改写 | Agent 根据上下文动态改写,甚至拆分子问题 |
| 结果评估 | 无(检索到什么就用什么) | Agent 评估检索质量,不满意则换策略重检 |
| 工具组合 | 只有向量检索 | 可组合多种检索工具(向量、图谱、SQL、API) |
| 适用场景 | 简单事实问答 | 复杂多跳推理、需要整合多源信息的任务 |
Agentic RAG 的三个关键能力:
- 自主判断检索时机:不是每个 query 都需要检索——Agent 先评估自身知识是否足够,不足时才触发检索。避免了传统 RAG 对所有请求都检索的资源浪费
- 检索结果自我评估:检索完成后,Agent 判断结果是否能回答问题。如果召回的文档不相关或信息不完整,主动改写 query 或切换检索策略进行二次检索
- 多源检索编排:复杂问题拆成多个子问题,分别用不同的检索工具(向量库查概念、知识图谱查关系、SQL 查数据),最后综合推理
工程落地的权衡:
Agentic RAG 的能力更强,但延迟和成本也更高——多次检索意味着多次 LLM 推理。生产中通常采用分级策略:简单 query 走传统 RAG 快速响应,复杂 query 才进入 Agentic RAG 循环。
差距在哪:新手把 Agentic RAG 简单理解为「RAG + Agent」的拼凑。高手理解核心区别在于检索从固定管线变成了 Agent 的可迭代工具——Agent 能自主决定是否检索、检索什么、结果够不够、要不要换策略。面试官考的是你对 RAG 范式演进的认知——从被动管线到主动推理。
Q:RAG 架构与模型微调(Fine-tuning)相比,各自的适用场景和优缺点是什么?
来源:字节后端 Agent 开发二面 【淘天转正实习一面追问:预训练语料已包含相关知识为什么还要RAG】
新手答:“RAG 不需要训练,Fine-tuning 效果更好。”
高手答:
这两种方案不是互斥的,而是解决不同类型知识注入问题的工具。核心判断框架:
RAG 本质是「外挂知识」——检索时注入
| 维度 | RAG |
|---|---|
| 知识更新 | 改文档即生效,无需重新训练 |
| 可追溯性 | 每个回答都能追溯到来源文档 |
| 成本 | 无训练成本,主要是检索基础设施 |
| 知识容量 | 理论上无上限,取决于文档库规模 |
| 适用场景 | 知识频繁更新、需要引用来源、企业知识库 |
Fine-tuning 本质是「内化知识」——训练时注入
| 维度 | Fine-tuning |
|---|---|
| 知识更新 | 需重新训练和部署 |
| 可追溯性 | 无法追溯到具体来源 |
| 成本 | 训练 + 数据标注 + GPU 资源 |
| 知识容量 | 受模型参数量限制 |
| 适用场景 | 固化领域风格/格式、提升特定任务能力、降低推理成本 |
关键决策点:
flowchart TD
A["知识会频繁变化吗?"] -->|"是"| B["RAG"]
A -->|"否"| C["需要引用来源吗?"]
C -->|"是"| B
C -->|"否"| D["是格式/风格问题\n还是知识问题?"]
D -->|"格式/风格"| E["Fine-tuning"]
D -->|"知识"| F["数据量够吗?"]
F -->|"充足"| G["Fine-tuning + RAG 混合"]
F -->|"不足"| B
最优解往往是混合方案:用 Fine-tuning 让模型学会领域的「语言」和「格式」(怎么说),用 RAG 注入具体的「事实」和「知识」(说什么)。例如医疗场景——微调让模型学会医学术语和问诊风格,RAG 提供最新的诊疗指南和药品信息。
差距在哪:新手把 RAG 和 Fine-tuning 当成二选一。高手理解两者解决不同问题——RAG 解决「知道什么」,Fine-tuning 解决「怎么表达」——且给出了决策框架和混合方案。面试官考的是你对知识注入手段的全局认知。
Q:如何处理 RAG 过程中的权限隔离和时效性问题?
来源:字节后端 Agent 开发二面
新手答:“给每个用户建一个单独的知识库。”
高手答:
权限隔离和时效性是 RAG 上生产环境后最先暴露的两个安全问题。单独建库的方案在用户量大时不可行——需要系统性的工程方案:
一、权限隔离:检索前过滤,而非物理隔离
用户请求 → 鉴权(提取用户权限标签)
→ 在向量检索时注入 metadata filter
→ 只召回用户有权访问的文档
实现方式:
| 方案 | 实现 | 适用场景 |
|---|---|---|
| 元数据过滤 | 每个文档 chunk 存储权限标签(部门、角色、项目),检索时通过 pre-filter 过滤 | 权限维度明确、变更不频繁 |
| 多租户索引 | 按租户分索引(物理隔离),查询时路由到对应索引 | 租户间数据完全隔离(如 SaaS 多客户) |
| 行级安全 | 在关系型数据库层面做权限控制,向量检索结果再和权限表 join | 权限规则复杂、需要动态变更 |
关键工程细节:pre-filter(检索前过滤)vs post-filter(检索后过滤)的选择。pre-filter 在向量数据库层面直接过滤,效率高但可能影响召回质量(候选集变小);post-filter 先全量召回再权限过滤,召回质量高但有信息泄露风险(模型可能已经「看到」了无权限内容的 embedding 相似度信号)。生产中推荐 pre-filter。
二、时效性:文档生命周期管理
| 策略 | 做法 |
|---|---|
| 时间戳标记 | 每个 chunk 存储文档创建/更新时间,检索时做时间衰减加权 |
| 增量更新 | 文档变更时只重新嵌入受影响的 chunk,不重建全量索引 |
| 版本管理 | 同一文档的多个版本共存,检索时优先返回最新版本 |
| TTL 机制 | 设置文档过期时间,过期后自动降权或剔除 |
时效性的隐藏陷阱:用户问「最新的政策是什么」,如果召回了旧版本文档,模型会很自信地给出过时的答案——这比「不知道」更危险。防御方案:在生成阶段注入时间上下文(「以下文档更新于 2024-03-01,请注意时效性」),让模型自己判断是否需要提示用户信息可能过时。
差距在哪:新手只想到物理隔离(按用户建库)。高手给出了三层权限方案(元数据过滤、多租户索引、行级安全)和四种时效性策略,且点出了 pre-filter vs post-filter 的安全 trade-off。面试官考的是你对 RAG 工程化落地中安全问题的实战认知。
Q:补充检索是如何评估数据质量并触发的?怎么保证二次检索能搜到之前没搜到的内容?
来源:淘天 Agent 开发
新手答:“检索结果不好就再搜一次。”
高手答:
「再搜一次」如果用同样的策略,结果不会有本质变化。补充检索的关键是先评估质量,再换策略重搜:
一、什么时候触发补充检索?
需要一个质量评估门控,而不是每次都二次检索:
| 评估维度 | 检测方法 | 触发条件 |
|---|---|---|
| 召回数量 | 候选文档数 < 阈值 | top-K 返回不足 3 条 |
| 相关性得分 | Rerank 后最高分低于阈值 | 最高分 < 0.5(模型和数据相关) |
| 覆盖度 | query 中的关键实体/概念是否被召回文档覆盖 | 核心实体零命中 |
| 自洽性 | 让模型判断「基于这些文档能否回答问题」 | 模型输出「信息不足」信号 |
最实用的方案是相关性得分 + 模型自判组合:Rerank 分数兜底硬指标,模型自判捕捉语义层面的不足。
二、怎么保证二次检索搜到不同的内容?
核心是换检索策略,而不是重复同一策略:
flowchart LR
A["首次检索失败"] --> B["策略切换"]
B --> C["查询改写\n同义扩展/拆分子问题"]
B --> D["换检索路\n向量→BM25\n或反过来"]
B --> E["放宽约束\n扩大时间范围\n降低阈值"]
B --> F["升级索引\n从chunk级→文档级\n或引入父子索引"]
C --> G["二次检索"]
D --> G
E --> G
F --> G
| 策略 | 做法 | 为什么能搜到新内容 |
|---|---|---|
| 查询改写 | 用 LLM 将原 query 改写为 2-3 个不同表述 | 弥补词汇鸿沟,触达不同文档 |
| 切换检索路 | 首次向量召回不好 → 换 BM25 精确匹配 | 两种检索的盲区不同 |
| 放宽约束 | 去掉时间/来源等过滤条件 | 扩大候选集 |
| 拆分子问题 | 复杂 query 拆成多个简单子 query 分别检索 | 每个子 query 更精准 |
三、防止无限循环
补充检索必须设上限(最多 2-3 轮)。如果多轮检索后质量仍不达标,应该诚实告知用户信息不足,而不是用低质量结果硬凑答案。
差距在哪:新手的「再搜一次」没有质量评估也没有策略切换——大概率搜出同样的结果。高手先用质量门控判断是否需要补充检索,再用策略切换保证二次检索有实质性差异。面试官考的是你对 RAG 检索链路的精细化控制能力。
Q:向量数据库中 IVF_FLAT 和 HNSW 索引的区别是什么?各自适合什么场景?
来源:快手AI应用开发一面
新手答:“都是加速向量搜索的索引,HNSW 更快。”
高手答:
IVF_FLAT 和 HNSW 是两种完全不同的索引范式——一个基于分区量化,一个基于图遍历,适用场景差异很大:
| 维度 | IVF_FLAT | HNSW |
|---|---|---|
| 核心思想 | 先聚类分桶,查询时只搜少数桶 | 构建多层跳表图,贪心遍历近邻 |
| 构建时间 | 快(kmeans 聚类) | 慢(逐点插入建图) |
| 内存占用 | 低(只存聚类中心 + 原始向量) | 高(每个节点存多层邻居指针) |
| 查询速度 | 中等(受 nprobe 参数控制) | 极快(图遍历,路径短) |
| 召回率 | nprobe 小时召回低,大时接近精确 | 高(ef_search 调大后接近 100%) |
| 增量更新 | 容易(新向量分配到最近的桶) | 较难(插入需更新多层图结构) |
| 数据规模 | 适合千万级以上(配合 PQ 压缩) | 适合百万到千万级 |
IVF_FLAT 工作原理:
建索引:对所有向量做 K-means 聚类 → 得到 nlist 个聚类中心
查询时:计算 query 与所有聚类中心的距离 → 选最近的 nprobe 个桶 → 在这些桶内暴力搜索
HNSW 工作原理:
建索引:逐个插入向量,每个向量随机分配层级 → 在每层中与最近邻建立边
查询时:从顶层入口点开始 → 在每层贪心跳到最近邻 → 逐层下降直到底层 → 底层精搜 Top-K
选型决策:
| 场景 | 推荐索引 | 原因 |
|---|---|---|
| 数据量大(>1000w)、内存有限 | IVF_PQ(IVF + 量化压缩) | HNSW 内存扛不住 |
| 数据量中等、要求低延迟 | HNSW | 查询速度最快,延迟稳定 |
| 数据频繁更新(实时写入) | IVF_FLAT | 增量更新成本低 |
| 离线批量检索、对延迟不敏感 | IVF_FLAT(大 nprobe) | 召回率高且节省内存 |
| RAG 在线服务 | HNSW | 用户感知延迟要低,内存可以加机器解决 |
差距在哪:新手只知道”HNSW 更快”——这是结论不是理解。高手从数据结构原理(分桶 vs 图遍历)出发,推导出各自的性能特征和适用场景,且能给出具体的选型建议。面试官考的是你对向量检索底层机制的理解深度——不是会用 API,而是知道为什么这个参数要这样调。
Q:RAG 检索到的 Chunk 不足以回答问题,后续怎么处理?
来源:字节春招大模型测开一面
新手答:”让模型直接用自己的知识回答,或者告诉用户找不到。”
高手答:
这是 RAG 系统最常见的退化场景之一。”Chunk 不足”有两种含义——检索到了但信息不够完整,或者根本没检索到相关内容。处理策略完全不同:
第一层:判断”不足”的程度
在生成前先做一步充分性评估(Sufficiency Check):用 LLM 判断当前检索结果能否支撑回答。评估维度:
- 关键实体是否存在(问”A 和 B 的区别”,至少要有 A 和 B 的描述)
- 时间/数值是否覆盖(问”2024年营收”,2023年的数据不算充分)
- 逻辑链是否完整(问”为什么 X 导致 Y”,需要因果链条,不能只有结论)
第二层:补充检索策略
如果判定不足,触发 Agentic RAG 的补充检索:
flowchart TD
A[“初次检索结果”] --> B{“充分性评估”}
B -->|”充分”| C[“直接生成回答”]
B -->|”不充分”| D[“策略切换”]
D --> E[“查询改写\n换角度/拆子问题”]
D --> F[“扩大召回\n放宽阈值/切换索引”]
D --> G[“跨库检索\n换知识源/调外部API”]
E & F & G --> H{“二次充分性评估”}
H -->|”充分”| C
H -->|”仍不足”| I[“诚实告知+给出部分答案+标注不确定性”]
关键策略:
- 查询改写 — 原始 query 可能太笼统或措辞偏离文档表达。拆成子问题、换同义词、补充上下文后重新检索
- 扩大召回范围 — 降低相似度阈值、增加 Top-K、切换到 BM25 关键词匹配(语义检索漏掉的关键词匹配可能命中)
- 跨库检索 — 如果本地知识库确实没有,可以调用外部搜索引擎、数据库查询、或调用其他系统的 API
第三层:生成策略
如果补充检索后仍不足:
- 部分回答 + 标注 — “根据现有信息,X 部分的答案是…,但关于 Y 的具体数据暂未找到”
- 不编造 — 绝对不能让模型用自身参数知识补充事实性信息(这是幻觉的主要来源)
- 引导用户 — “您可以提供更具体的文档/关键词吗?”或”建议查阅 XX 系统获取最新数据”
差距在哪:新手的处理是二元的——“有就答,没有就说不知道”。高手有一套分级应对策略:先评估不足程度,再用查询改写/扩大召回/跨库检索做补充,最后仍不足时给出部分答案并标注不确定性。面试官考的是你对 RAG 系统边界情况的处理能力——生产环境中”检索不足”比”检索精准”出现得更频繁。
Q:向量数据库里两个同义词是什么关系?完全同义的词会在同一个点上吗?
来源:字节春招大模型测开一面
新手答:”同义词的向量应该一样,在同一个点上。”
高手答:
同义词在向量空间中非常接近但不会完全重合。这涉及 Embedding 模型的本质工作方式:
为什么不在同一个点上:
Embedding 模型编码的不仅是”词义”,还包括:
- 使用语境差异 — “开心”和”高兴”虽然同义,但”开心果”和”高兴果”不等价。模型会捕捉到这些细微的搭配差异
- 语域/正式度 — “逝世”和”死了”语义相同,但正式度不同,向量会有偏移
- 训练语料分布 — 两个词在训练数据中的上下文分布不完全相同,导致编码略有差异
具体的空间关系:
完全同义词(如 “AI” 和 “人工智能”):
→ 余弦相似度 > 0.95,几乎重合但有微小偏移
近义词(如 “开心” 和 “高兴”):
→ 余弦相似度 0.85-0.95,同一区域的邻近点
上下位关系(如 “狗” 和 “动物”):
→ 余弦相似度 0.6-0.8,有方向性偏移
反义词(如 “大” 和 “小”):
→ 余弦相似度可能 0.3-0.6(不一定对称分布在原点两侧)
对 RAG 的实际影响:
同义词接近但不重合意味着:
- 查询 “AI应用” 可以召回包含 “人工智能应用” 的文档(相似度高)
- 但精确匹配度略低于完全相同的表述 → 这就是为什么需要查询改写(把 “AI” 扩展为 “AI/人工智能”)
- 也是为什么混合检索(向量 + BM25)有效 — BM25 处理精确匹配,向量处理语义近似
差距在哪:新手认为同义词 = 同一个点。高手理解 Embedding 空间是连续的高维空间,同义词是”近邻”而非”重合”,且能解释为什么——因为模型编码的是上下文分布而非字典定义。面试官通过这个问题考察你对 Embedding 的理解是停留在”API 调用”还是真正理解其数学含义。
Q:RAG 过程中如何处理文件里的图片?
来源:字节暑期agent实习二面
新手答:”把图片转成文字再检索。”
高手答:
RAG 中图片处理是多模态 RAG 的核心问题,主流方案:
- 图片→文本描述(OCR + Caption):用 OCR 提取图中文字,用多模态模型(如 GPT-4V/Qwen-VL)生成图片描述,将描述文本一起做 Embedding 索引
- 多模态 Embedding 直接索引:用 CLIP 等模型直接将图片编码为向量,和文本向量放同一向量空间,支持跨模态检索
- 图文联合分块:在 chunk 切分时保持图片与其上下文段落的关联——图片属于哪个段落,chunk 就包含”段落文本 + 图片描述/图片引用”
- Layout-Aware 解析:用 PDF 解析工具(如 Unstructured、Marker)识别图片在文档中的位置和语义角色(图表?示意图?截图?),不同类型用不同处理策略
- 延迟处理:检索阶段只用文本索引,召回相关 chunk 后发现有关联图片时,再把图片和文本一起送多模态模型做最终回答
差距在哪:面试官关注你对”多模态信息如何融入检索链路”的工程思考——不是有 OCR 就够了,要考虑检索效果和成本的平衡。
Q:如何避免模型回复过度依赖检索到的外部知识,导致回答生硬、缺乏共情能力和自然度?
来源:阿里淘天 AI Agent应用开发二面
新手答:“在 Prompt 里加一句‘请用自然语言回答,不要直接复制文档内容’就行了。”
高手答:
这个问题的本质是检索内容“绑架”了模型的生成自由度。模型把 RAG 返回的文档当成唯一信息源,逐句改写而非理解后重新组织,导致回答像“念文档”而非“对话”。
解法分三层:
1. 检索层——控制注入量和粒度
- 不是检索到的东西全塞进 Prompt,而是只注入与当前轮次直接相关的片段
- 设置相关性阈值:低于阈值的 chunk 不注入,让模型用自身知识补全
- 对长段落做摘要后注入,而不是原文照搬——摘要后模型有更大的表达空间
2. Prompt 层——分离“知识”和“表达风格”
- 系统 Prompt 中明确区分角色:你是一个友好的助手,参考资料只是辅助,回答要自然
- 使用“参考但不照抄”的约束:
以下资料供参考,请结合你的理解用口语化方式回答,不必逐条复述 - 给模型留“自由发挥区”:允许在事实准确的前提下加入过渡语、共情表达、追问
3. 生成层——后处理与反馈闭环
- 自然度评分:对生成结果用小模型打分(流畅性、重复率、文档复制比例),低分回退重生成
- A/B 测试:对比“全量注入 vs 摘要注入 vs 无 RAG 回退”三种策略的用户满意度
- 温度调节:对事实性回答用低温度保准确,对情感/共情类回答适当提高温度增加自然度
实际工程中常见的 Pattern:
- 双通道生成:先让模型不看 RAG 结果生成一版“自然回答”,再与 RAG 检索结果做事实校验合并
- 引用标记 + 自由组织:要求模型标注哪些内容来自文档、哪些是自己推理的,便于后续审计
差距在哪:面试官考的是你对“RAG 不只是拼 Prompt”的理解。检索是为了保证事实准确,但不能牺牲对话体验。高手会从注入策略、Prompt 设计、后处理三层分别控制“依赖度”,而不是只靠一句 Prompt 指令。
Q:随着大模型上下文窗口持续扩容(100K→1M+),传统 RAG 技术是否会被完全替代?
来源:阿里淘天 AI Agent应用开发二面
新手答:“窗口够大了就不需要 RAG 了,直接把所有文档塞进去就行。”
高手答:
短期内不会被完全替代,长期会深度融合而非简单替代。核心论点:
RAG 不会消失的四个理由:
- 成本问题:1M token 的推理成本远高于“检索 5 个 chunk + 8K 上下文”。对高 QPS 场景(如客服),全量塞入的经济性不可接受
- 注意力稀释:即使窗口能装下,模型对中间位置信息的关注度仍然不均匀(Lost in the Middle 问题未根本解决),检索后精选 Top-K 反而提高相关信息的权重
- 实时性与权限:知识库持续更新,不可能每次请求都重新拼装全量文档;且不同用户有不同的数据权限,RAG 的过滤层天然支持权限隔离
- 可审计性:RAG 能明确告诉你“这个回答基于哪个文档的哪个片段”,全量塞入后模型引用溯源变得极困难
窗口扩容确实改变了什么:
- RAG 的角色从“必须”变成“优化”:以前窗口不够必须检索,现在是为了提高质量和降本而检索
- chunk 策略变粗:以前切 512 token 的小块,现在可以检索整段甚至整篇文档
- 检索-生成边界模糊化:可以先粗检索一批大文档塞进长窗口,再让模型自己做“精排 + 生成”
- 简单场景不再需要 RAG:FAQ、单文档问答等简单场景,直接全文塞入更省事
未来的融合形态:
graph TD
A[用户 Query] --> B{复杂度判断}
B -->|简单/单文档| C[直接全文塞入长窗口]
B -->|复杂/多文档/高QPS| D[RAG 检索 + 精选注入]
B -->|实时数据/权限隔离| E[必须经 RAG 过滤层]
C --> F[生成回答]
D --> F
E --> F
差距在哪:这是开放观点题,面试官考的是技术判断力。新手非此即彼(要么 RAG 必须,要么窗口万能),高手能分场景讨论成本、注意力、实时性、可审计性四个维度,给出“不是替代而是融合”的结论,并能描述融合后的具体形态。
ES 切换向量检索的能力变化
Q:如果从 ElasticSearch 切换到向量检索,哪些能力会下降,哪些能力会提升?
来源:字节跳动 Agent开发实习生一面
新手答:“向量检索语义能力更强,ES 适合关键词。”
高手答:
这不是简单的“好 vs 坏”,而是两种检索范式的能力互补。切换时要清楚你会失去什么、得到什么、以及怎么补回来。
切换后提升的能力:
| 能力 | 原因 |
|---|---|
| 语义理解 | “如何降低延迟”能匹配“性能优化方案”,ES 做不到 |
| 同义/改写鲁棒性 | 用户换种说法不影响召回质量 |
| 跨语言检索 | 多语言 Embedding 天然支持中英混搜 |
| 模糊意图处理 | 用户表达不精确时,语义相似度比关键词匹配更有效 |
切换后下降的能力:
| 能力 | 原因 |
|---|---|
| 精确匹配 | 搜“错误码 E4012”,向量检索可能返回语义相关但不是这个编码的文档 |
| 结构化过滤 | ES 天然支持 range/term/bool 组合过滤,向量库需要额外的 metadata filter |
| 实时索引 | ES 近实时(1s),很多向量库批量索引有延迟 |
| 高亮/聚合 | ES 内建 highlight、aggregation,向量库通常没有 |
| 可解释性 | BM25 分数有明确含义(TF-IDF),向量相似度的 0.82 vs 0.79 难以解释 |
最佳实践——混合检索:
不要“切换”,而是“叠加”。用 ES 做精确匹配 + 结构化过滤,用向量库做语义召回,然后 RRF 或 Rerank 融合。把 ES 降级为精确匹配通道,而不是完全替换。
差距在哪:面试官考的是你对检索系统的完整理解。只说“向量比 ES 好”说明你没在生产环境用过 ES。能说出精确匹配下降、结构化过滤缺失、实时性差异,并给出混合方案,说明你做过检索系统的技术选型。
语义切分与文档聚类
Q:父文档是怎么得到的?语义切分具体是怎么做的?聚类后怎么区分不同文档?
来源:同程Agent开发实习一面
新手答:“按 512 token 切就行了,父文档就是原始文档。”
高手答:
父子文档索引的核心思路是:子文档用于精准召回,父文档用于提供完整上下文。语义切分是让子文档在语义上自洽的关键。
语义切分流程:
flowchart TD
A[原始文档] --> B[句子级分割]
B --> C[逐句 Embedding]
C --> D[相邻句子相似度计算]
D --> E{相似度低于阈值?}
E -->|是| F[标记为语义断点]
E -->|否| G[继续]
F --> H[按断点聚合为语义段落]
H --> I[检查 token 上限]
I -->|超限| J[段落内再按相似度二分]
I -->|合格| K[生成子文档 chunk]
K --> L[聚合同一来源的子文档 → 父文档]
具体步骤:
- 句子级分割:用标点/换行将文档拆成句子序列
- 逐句 Embedding:每句生成向量
- 滑动窗口余弦相似度:计算相邻句子之间的语义相似度,相似度骤降的位置即为语义断点
- 按断点聚合:断点之间的句子合并为一个语义段落(即子 chunk)
- token 限制兜底:如果某个语义段超过 max_tokens(如 512),在段内再找次级断点拆分
父文档的生成方式:
- 最简单:父文档 = 原始文档(或原始文档的某一节)
- 进阶:多个相邻子 chunk 共享同一父文档,父文档 = 子 chunks 的上下文窗口扩展(向前向后各扩展 N 句)
聚类后如何区分不同来源:
- 每个 chunk 携带 metadata:
{doc_id, section_id, chunk_index, parent_id} - 检索命中子 chunk 后,通过
parent_id回溯父文档,返回更完整的上下文 - 不同文档通过
doc_id天然隔离,聚类只在单文档内部进行
差距在哪:面试官深挖切分细节是为了验证“你的 RAG 到底做了多深”。只说“按 token 切”说明你用了默认方案没调优。能说出语义断点检测 + 相似度骤降 + 父子回溯,说明你真的做过切分实验并理解效果差异。
这类题的答题模式
RAG 与检索题的核心是完整链路 + 工程细节:
1. RAG 不是"向量搜索"——是离线切片 + 在线召回 + 精排的完整管线
2. 查询改写不是"扩展"——是多维度变换 + 槽位填充 + 主动澄清
3. 召回不是"一路搜索"——是多路召回 + 融合 + 精排
4. 性能优化用并行化——串行意图识别会引入延迟和错误级联
下一篇建议继续看: