---
layout: default
title: RAG 与检索系统：从 chunk 设计到多路召回
description: RAG 检索工程面试题拆解，覆盖 chunk 策略、查询改写、意图识别、召回精排
eyebrow: Agent 面试通关 / 09
---

# RAG 与检索系统：从 chunk 设计到多路召回

RAG 是 Agent 系统的“外部知识接口”。面试官考 RAG 时不想听“向量数据库”四个字——他想知道的是**离线怎么切片、在线怎么召回、召回后怎么排序**，以及面对复杂查询时怎么做改写和意图识别。

---


## 检索基础与原理

### Q：多维度的查询改写是什么？改写遇到需要用户补充信息时怎么设计？

> 来源：抖音基础架构 Agent 一面【淘天一面追问：改写为何提升精准度的底层原理】【[美团 Agent 开发一面](https://www.nowcoder.com/feed/main/detail/58159306df52463ab75d72daa80d66df)追问：短 Query 与长 Chunk 的非对称召回】【[阿里巴巴（淘天）- 大模型算法岗（搜推方向）](https://www.nowcoder.com/discuss/926272464059891712)追问：淘宝搜索中如何用大模型做 Query 理解和改写？】【[美团 - Agent 开发岗（场景设计方向）](https://www.nowcoder.com/discuss/926273749555376128)追问：RAG 召回不相关时 Query Rewrite 优化举例？】

**新手答**：“用大模型把用户的查询扩展一下。”

**高手答**：

多维度查询改写是指**同时从多个角度对原始 query 做变换**，提升召回的全面性：

```text
原始 query："北京周末带娃去哪玩"

维度1-意图展开：亲子活动 / 儿童乐园 / 博物馆 / 户外游
维度2-实体补全：北京市 → 朝阳区/海淀区/...
维度3-同义改写："带娃" → "亲子" / "儿童" / "适合小朋友"
维度4-约束提取：时间=周末，地点=北京，人群=有孩子的家庭
```

**需要用户补充信息时**，用**槽位填充 + 主动澄清**机制：
1. 意图识别后，检查必要槽位是否完整（比如“订酒店”需要日期、城市、人数）
2. 缺失槽位时，不是笼统地问“你能说得更清楚吗”，而是生成**具体的选择题**：“你想去哪个城市？”或“入住日期是什么时候？”
3. 多个槽位都缺时，按优先级排序，先问最关键的

技术实现上，查询改写用**模型生成 + 规则约束混合**：模型负责语义理解和改写，规则负责实体标准化和格式校验。槽位检测用一个轻量分类器判断缺失情况，避免每次都调大模型。

**差距在哪**：新手的“扩展一下”是单维度思考。高手的多维度改写覆盖了意图、实体、同义、约束四个角度，且有用户交互的槽位填充机制。面试官考的是你对查询理解链路的完整认知。

**追问：Query 改写为什么能提升召回精准度？底层原理是什么？**

改写不是“让 query 变长”，而是**解决 query 和文档之间的三类鸿沟**：

| 鸿沟类型 | 问题 | 改写如何解决 | 示例 |
|---------|------|------------|------|
| 词汇鸿沟 | 用户用词 ≠ 文档用词 | 同义扩展弥合词汇差异 | “手机卡” → “SIM 卡 / 流量卡 / 电话卡” |
| 意图鸿沟 | 短 query 意图模糊 | 意图展开消除歧义 | “苹果” → “Apple 公司 / 苹果水果”（结合上下文选择） |
| 粒度鸿沟 | query 太宽泛或太细碎 | 拆分/合并调整检索粒度 | “怎么部署” → “Docker 部署 + K8s 部署 + 物理机部署” |

**为什么多维度改写比单次改写效果好**：单次改写只能沿一个方向扩展，可能强化了某个意图但遗漏了其他。多维度改写用多条路径并行检索再合并，Recall 提升显著（实测 +15~25%），但 Precision 可能下降——所以改写后**必须接 Rerank 精排**过滤噪声。

**追问：Query 只有几个字，Chunk 却很长，直接比较 Embedding 会有什么问题？**

长度差异本身不是判错条件，真正的问题是**信息粒度不对称**：短 Query 意图不完整，长 Chunk 又可能混有多个主题，单个向量会把局部相关信号平均掉。先按线上 Query 长度、意图完整度和 Chunk 主题数切片评测，确认失败来自歧义、词汇鸿沟还是向量稀释，不能看到短 Query 就无条件扩写。

治理可以分三层：查询侧结合会话补全实体与约束，生成多条受控改写；文档侧用父子索引或多向量表示，让标题、摘要、子段分别参与召回；检索侧并行 BM25、稠密向量和 metadata filter，再用原始 Query 对候选精排。评测同时看短 Query 分桶的 Recall@K、最终答案正确率和改写引入的误召回，避免只把 Query 变长却改变用户意图。

---

### Q：RAG 的检索如何实现？

> 来源：阿里 AI Agent 开发一面；[钉钉一面](https://www.nowcoder.com/discuss/923765750446202880)

**新手答**：“用向量数据库做相似度搜索。”

**高手答**：

RAG 检索分**离线**和**在线**两部分。

离线侧负责文档清洗、切片、去重、embedding 计算和向量入库。在线侧的流程是：

1. 用 query 编码成向量，去向量库做召回
2. 结合 BM25 或关键词检索做**混合召回**
3. 用 rerank 模型重排
4. 把最相关的证据拼接给大模型生成答案

```python
# 简化版 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 一面

**新手答**：“用多线程跑意图分类。”

**高手答**：

并行化意图识别是指**同时执行多个意图维度的判断，而不是串行地先判大类再判小类**。

串行方案的问题是链路长、延迟高。比如先判“是不是搜索意图” → 再判“搜索什么品类” → 再判“有没有比较意图”，三级串行加起来可能 300ms+。而且前一级判断错了，后面全错——错误级联放大。

并行化把多个维度**同时发出去**：

```text
              ┌→ 意图大类分类器（搜索/导航/交易）
user query ──┼→ 品类识别器（美食/旅游/购物/...）
              ├→ 行为意图检测器（比较/推荐/查询/...）
              └→ 情感倾向检测器（正面/负面/中性）
```

每个分类器独立运行，结果汇总后做**融合决策**。实现关键点：
1. 各分类器用**异步并发**执行（`asyncio.gather` 或线程池），总延迟 = 最慢的那个
2. 每个分类器可以用不同规模的模型——简单维度用小模型，复杂维度用大模型
3. 设置**超时兜底**：某个分类器超时不影响其他维度，用默认值填充

**差距在哪**：新手的“多线程”只答了实现手段，没答为什么要并行化。高手先解释了串行方案的问题（延迟高 + 错误级联），再给出并行化的架构和融合策略。面试官考的是性能优化思维。

---

### Q：讲一下项目里召回的流程

> 来源：抖音基础架构 Agent 一面

**新手答**：“用向量搜索召回相关文档。”

**高手答**：

召回是一个**多路召回 → 合并去重 → 精排**的三阶段流程：

**第一阶段：多路召回**

```text
              ┌→ 向量召回（语义相似度，覆盖同义表达）
改写后 query ──┼→ 关键词召回（BM25，覆盖精确匹配）
              ├→ 标签召回（实体/品类标签匹配）
              └→ 图召回（知识图谱关联路径）
```

每路召回各取 top-K，各有优势：向量召回抓语义，关键词召回抓精确词，标签召回抓结构化属性，图召回抓实体关系。

**第二阶段：合并去重**

多路结果合并，同一文档被多路召回的加分。去重用文档 ID 做精确去重 + 内容指纹做近重复去重。

**第三阶段：精排**

用 Cross-Encoder 或精排模型对候选集做细粒度相关性打分。精排模型能看到 query 和文档的交互特征，精度远高于召回阶段的双塔模型。关键工程细节：每路召回的 K 值怎么定——K 太小漏结果，K 太大精排压力大，通常根据各路历史准确率动态调整。

**差距在哪**：新手只知道向量检索一路。高手的多路召回 + 精排是搜索系统的标准范式，面试官考的是你对召回-精排两阶段架构的完整理解。

---

### Q：如果 RAG 召回了很多相互矛盾的文档，Agent 应该怎么处理，而不是直接让模型自己总结？

> 来源：腾讯大模型应用开发二面

**新手答**：“让模型自己综合判断。”

**高手答**：

不能直接把矛盾文档一股脑丢给模型让它自己“综合”，那样很容易生成一个看起来圆滑但实际上没有依据的答案。

更合理的做法是**先做证据归一化和冲突检测**：

1. **分组**：按来源、时间、可信度对召回文档分组
2. **冲突检测**：抽取同一字段的不同取值，判断冲突类型——是时间差异导致的，还是来源本身互相打架
3. **冲突消解**：
   - 时间敏感信息 → 新版本优先
   - 来源权威性不同 → 官方文档优先
   - 无法判断 → 明确告诉用户存在冲突，说明目前最可信的依据

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。

**微调关键细节**：
1. **Hard Negative Mining**：negative 质量决定微调效果
2. **评估指标**：用 Recall@K、MRR、NDCG 在验证集上评估，不是看 loss 降了就行
3. **防止过拟合**：微调轮数不宜过多（1-3 epoch），否则通用检索能力退化

**差距在哪**：新手只说了“用数据训”。高手覆盖了完整链路——数据构造（三元组 + hard negative）、loss 选择、框架、评估、防过拟合。面试官考的是你有没有真正微调过检索模型。

---

### 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 为什么需要向量检索？和传统关键词检索有什么本质区别？

> 来源：蚂蚁集团智能体与大模型应用一面【[拼多多 复活赛 一面](https://www.nowcoder.com/feed/main/detail/2109cf8eb0254507911fbf86bcbf51e4)追问：关键词 / 倒排这种方式有明显局限，比如“苹果”和“apple”可能匹配不上，你们怎么看？】【[百度Agent一面](https://www.nowcoder.com/feed/main/detail/72858aade19d443facc870fea8bb134f)追问：BM25 和向量检索分别解决什么类型的问题？】

**新手答**：“向量检索更准。”

**高手答**：

不是“更准”——是**解决了不同的问题**。传统检索和向量检索的核心差异在于匹配方式：

| 维度 | 传统关键词检索（BM25） | 向量检索（Embedding） |
|------|---------------------|---------------------|
| 匹配方式 | 词级精确匹配 | 语义级相似度匹配 |
| 核心算法 | TF-IDF / BM25 | ANN（近似最近邻） |
| 能处理同义词 | ❌ “退货”搜不到“退款” | ✅ 语义相近就能匹配 |
| 能处理精确术语 | ✅ 搜“order_id=123”直接命中 | ❌ 向量化后精确性丢失 |
| 计算成本 | 低（倒排索引，O(1)级） | 高（向量距离计算 + ANN 索引） |
| 索引存储 | 小（倒排表） | 大（每条文档一个高维向量） |
| 对新词/专业术语 | 好（只要文档里有就能匹配） | 差（模型没见过的词 embedding 质量低） |

**RAG 为什么需要向量检索**：

用户提问的方式和文档的表述方式**几乎不可能完全一样**。用户问“服务器老是挂”，文档里写的是“服务可用性异常”——没有一个共同关键词，但语义完全匹配。传统检索在这种场景下召回率为零，向量检索能轻松解决。

**但向量检索不能完全替代关键词检索**：

查具体的错误码（`ERR_CONNECTION_REFUSED`）、精确的文件路径、特定的 API 名称——这些场景关键词检索比向量检索更快更准。向量化反而会把精确信息“模糊化”。

**生产环境的最佳实践是混合检索**：

```text
向量检索（语义匹配）─┐
                     ├→ 合并去重 → ReRank 精排 → Top-K
关键词检索（精确匹配）─┘
```

两路互补：向量路保证语义覆盖，关键词路保证精确命中。合并后用 ReRank 统一排序。

**差距在哪**：新手用“更准”一词概括——其实向量检索在精确匹配上反而不如关键词检索。高手明确了两者的优劣势互补关系，且指出生产环境用混合检索。面试官考的是你对检索系统的工程认知——不是“哪个更好”，而是“什么场景用什么”。

---

### Q：什么是余弦相似度？在 RAG 系统中用来做什么？

> 来源：携程 Agent 开发实习一面【[9.4 某小厂 AI Agent hr+技术面](https://www.nowcoder.com/feed/main/detail/10b2fcaf73d2401f8636bd0459e1cd08)追问：在向量检索召回阶段，度量 Query 与文档向量相似度的常用算法是什么？】

**新手答**：“衡量两个向量的相似程度。”

**高手答**：

余弦相似度衡量的是**两个向量方向的接近程度**，不关心长度，只关心角度：

```text
cos(A, B) = (A · B) / (|A| × |B|)

值域：[-1, 1]
  1  = 方向完全一致（语义最相似）
  0  = 正交（语义无关）
 -1  = 方向完全相反（语义相反）
```

**为什么用余弦而不是欧氏距离**：

Embedding 模型输出的向量，不同文本的向量长度（模）可能差异很大。如果用欧氏距离，一段长文本和一段短文本即使语义相同，距离也可能很远（因为向量模不同）。余弦相似度归一化了长度，只比较方向——语义相同的文本，无论长短，余弦相似度都接近 1。

**在 RAG 系统中的用途**：

1. **检索阶段**：用户 query 向量和文档库中所有文档向量计算余弦相似度，取 top-K 作为候选
2. **去重阶段**：两个 chunk 的余弦相似度 > 0.95，判定为近重复，去掉一个
3. **阈值过滤**：相似度低于阈值（如 0.6）的文档直接丢弃，不进入精排——避免把完全不相关的文档喂给模型

**补充**：实际向量数据库（Milvus / Qdrant）在 ANN 检索时用的是**近似最近邻算法**（HNSW / IVF），不是暴力遍历所有向量。精确的余弦计算只在小规模候选集上做。

**差距在哪**：新手只背了定义。高手从公式、为什么选余弦而非欧氏、在 RAG 中的三个具体用途（检索/去重/过滤）做了完整解释，且补充了工程实现细节。面试官考的是你理解了这个指标的设计动机，而不只是会算。

---

### Q：什么是嵌入（Embedding）？为什么 RAG 系统需要将文本转为向量？

> 来源：携程 Agent 开发实习一面

**新手答**：“把文本变成数字，方便计算。”

**高手答**：

嵌入（Embedding）是把文本映射到一个**高维向量空间**中的过程——每段文本变成一个固定长度的数字数组（如 768 维或 1536 维），这个数组就叫这段文本的“向量表示”。

**关键特性**：语义相近的文本，在向量空间中的位置也相近。

```text
"北京今天天气很好"  →  [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） |

**什么是分布式表示**：一个语义概念（如“食物”）不是由某一个维度单独表达，而是由多个维度的组合模式来表达。反过来，一个维度也参与了多个语义概念的编码。这就是为什么单看某一维的数值没有意义，但整体向量之间的距离能反映语义相似性。

**维度数量的工程权衡**：

```text
低维（256-384）：存储小、速度快，但语义区分度有限，适合简单场景
中维（768）：  最常见的平衡点，大多数场景够用
高维（1024-3072）：区分度更高，能捕获更细粒度的语义差异，但存储和计算成本线性增长
```

**面试加分点**：如果面试官继续追问“能不能让每个维度可解释”，可以提到 Sparse Embedding（如 SPLADE）——它在词表维度上做稀疏编码，每个维度对应一个 token，可解释性强但维度极高（30000+）。这也是为什么实际系统常用 Dense + Sparse 混合检索——Dense 捕获语义，Sparse 保留精确匹配能力。

---

### Q：双路召回的 TopK，K 是如何确定的？有没有试过一个多召回点、一个少召回点？

> 来源：腾讯 AI 应用开发

**新手答**：“K 就取 10 或 20。”

**高手答**：

K 值要**根据召回路的特性和下游精排的承载能力来定**。

```text
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 在回答问题之前，先去翻资料，而不是纯靠记忆回答。

**图书馆类比**（最直观）：

```mermaid
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
```

**核心价值**（三点）：
1. **知识实时性**：模型训练数据有截止日期，但外部知识库可以随时更新——今天新上线的产品文档，明天就能被检索到
2. **可追溯性**：回答附带引用来源，用户可以点击查看原文，建立信任
3. **降低幻觉**：有资料做依据，模型不容易“编”答案

**主要应用场景**：企业知识库问答（内部文档/FAQ）、客服系统（产品手册检索）、法律/医疗等需要精确引用的领域、代码文档助手。

**差距在哪**：新手的解释太技术化，非技术人员听不懂。高手用图书馆类比把 RAG 的三个角色（用户 → 检索员 → 专家）讲清楚了，再用三点核心价值说明“为什么需要”。面试官考的不只是你懂不懂 RAG，还考你能不能把复杂概念讲给不同背景的人听——这是工程师的沟通能力。

---

### Q：如何快速上手一个没接触过的技术（如向量数据库）？

> 来源：携程 Agent 开发实习一面

**新手答**：“看官方文档，跑个 Demo。”

**高手答**：

快速上手一个新技术，我的方法论是**“倒金字塔学习法”——从应用场景倒推，不从底层原理开始**：

**第一步：搞清楚“它解决什么问题”（30 分钟）**

不是先看原理，而是先理解：传统方案的痛点是什么？向量数据库比传统数据库多解决了什么？答案是——传统数据库只能精确匹配，而向量数据库能做**语义相似度检索**。这一步决定了你后面学的所有东西有没有方向感。

**第二步：跑通一个最小可用的 Demo（2-3 小时）**

选一个主流方案（如 Milvus / Qdrant / Chroma），跑通“文本 → Embedding → 入库 → 查询 → 返回结果”的最短路径。不要一开始就研究分布式部署、索引类型选择这些细节。

```python
# 最小 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 中如何提高文档召回率？

> 来源：蚂蚁集团智能体与大模型应用一面

**新手答**：“换更好的 Embedding 模型。”

**高手答**：

召回率低意味着**正确文档没有进入候选集**——模型连看到正确答案的机会都没有。提升召回率要从离线和在线两端同时入手：

**离线端——提升文档的“可被检索性”**：

1. **Chunk 策略优化**：
   - 块太大 → 一个 chunk 混了多个主题，语义被平均化，精准匹配变弱
   - 块太小 → 上下文断裂，语义不完整
   - 最佳实践：语义分块（按段落/标题/逻辑边界切割）+ overlap（相邻 chunk 重叠 10-20%）
2. **文档增强**：给每个 chunk 附加元信息——标题、所属章节、关键词标签、生成摘要。检索时不只匹配 chunk 正文，还匹配元信息
3. **Embedding 模型适配**：通用模型在专业领域（医疗/法律/代码）的表现可能大幅下降。用业务数据微调 Embedding 模型，Recall 通常能提升 10-20%

**在线端——提升查询的“被理解度”**：

4. **查询改写**：用大模型对用户 query 做多维度改写（同义替换、意图展开、实体补全），从不同角度匹配文档
5. **混合召回**：不只用向量检索，同时用 BM25 关键词检索 + 标签检索，多路结果合并。向量路抓语义，关键词路抓精确匹配，互补覆盖
6. **HyDE（Hypothetical Document Embeddings）**：让模型先生成一个“假设性答案”，用这个答案的 embedding 去检索——因为答案和文档的语义更接近，比直接用 query 检索效果好

```mermaid
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 的局限**：

标准 RAG 是 query → embedding → top-K 召回。但当文档量大且内容跨领域时，单次召回很难覆盖所有相关信息。用户可能需要多轮追问才能拿到完整答案——每轮交互本质上是在“逐步缩小检索范围”。

**更高效的方案**：

**1. 文档预处理——建立关联索引**：

不等用户查询时才找关联，离线阶段就把文档间的关联性预计算好：

- **文档聚类**：按主题对文档自动分组，查询时先定位到相关组，缩小搜索范围
- **知识图谱**：从文档中抽取实体和关系，建立文档间的语义关联图。查询时沿着图谱做多跳检索，找到间接关联的文档
- **摘要索引**：每份文档生成摘要，先用摘要做粗筛，再用原文做精排——两级检索比全量检索快很多

**2. Multi-hop RAG——一次查询找到链式关联**：

```mermaid
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 吗？RAG 的角色会怎么变？

> 来源：蚂蚁集团 Agent 开发二面

**新手答**：“模型上下文窗口越来越大，RAG 可能不需要了。”

**高手答**：

RAG 不会消失，但**角色会发生本质变化**——从“弥补模型知识不足”变成“渐进式披露架构的信息供给层”。

**为什么上下文窗口变大不能替代 RAG**：

即使窗口有 100 万 token，也不能把所有文档都塞进去：
1. **成本问题**：塞 100 万 token 的成本是塞 1 万 token 的 100 倍
2. **精度问题**：信息越多，模型在噪声中迷失的概率越大（Lost in the Middle）
3. **实时性问题**：长上下文解决不了“信息需要实时更新”的问题

所以不是“要不要 RAG”，而是“RAG 在新架构里扮演什么角色”。

**RAG 在渐进式披露架构中的新角色**：

```text
传统 RAG：用户提问 → 检索文档 → 塞进上下文 → 生成回答（一次性、静态）
新角色 RAG：Agent 执行到某阶段 → 按需检索该阶段需要的知识 → 精确注入 → 继续执行（多次、动态）
```

| 维度 | 传统 RAG | 渐进式架构下的 RAG |
|------|---------|-------------------|
| 触发时机 | 用户提问时一次性触发 | Agent 执行过程中多次按需触发 |
| 检索粒度 | 和用户 query 匹配 | 和当前执行阶段的子目标匹配 |
| 结果用途 | 直接喂给模型生成答案 | 作为当前阶段的决策依据 |
| 生命周期 | 检索一次用到结束 | 阶段切换后可能需要重新检索 |

**具体变化**：

1. **从“回答前检索”到“执行中检索”**：RAG 不只在开头检索一次，而是 Agent 在规划、执行、验证各阶段都可能触发检索，每次检索的 query 不同
2. **从“通用检索”到“阶段感知检索”**：同一个用户问题，规划阶段需要检索的是“方法论文档”，执行阶段需要的是“API 文档”，验证阶段需要的是“规范标准”——检索策略随阶段变化
3. **从“替代模型知识”到“精确注入运行时上下文”**：RAG 的价值从“给模型不知道的知识”转变为“在正确的时机给出正确的精确信息”

**差距在哪**：新手用“窗口大了不需要 RAG”的线性思维。高手看到了 RAG 在新架构下的角色转变——从一次性的知识注入变成多阶段的动态信息供给。面试官考的是你对 RAG 技术演进方向的理解，以及能不能把 RAG 放到更大的架构图景中思考。

---


### 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 重排就行。”

**高手答**：

三个控制点**都需要**，但投入产出比不同：

**召回层——控制“进来什么”**：

```text
问题：召回 50 条，30 条不相关
解法：① 优化 query（多维度改写）② 收紧召回阈值 ③ 加关键词路补充精确匹配
效果：减少噪声进入下游，降低精排压力
```

召回层的控制是**成本最低、效果最直接**的——垃圾在入口就拦住，比在后面处理便宜得多。

**重排层——控制“排在前面的是什么”**：

```text
问题：相关文档被召回了，但排在第 8 位，top-3 都是噪声
解法：用 Cross-Encoder 精排，把真正相关的推到前面
效果：精度提升显著，但增加延迟和计算成本
```

重排层解决的是**顺序问题**，前提是相关文档已经被召回了。如果召回阶段就漏了，重排也救不了。

**Agent 规划层——控制“怎么用检索结果”**：

```text
问题：top-3 文档质量还行，但信息分散，模型拼出了错误答案
解法：Agent 在使用检索结果前做质量判断——
      "这些证据是否足够回答问题？是否有矛盾？需不需要再查一次？"
效果：提升最终回答质量，但增加一轮模型调用
```

规划层是**最后一道防线**，也是唯一能做“再查一次”决策的地方。

**优先级排序**：

```text
投入优先级：召回优化（成本低效果大）> ReRank（精度提升明显）> 规划层判断（兜底）
```

先把召回质量做好，再加精排提升排序，最后在 Agent 层做质量兜底。很多团队跳过前两步直接在 Agent 层“让模型自己判断”，这是最贵且最不可靠的做法。

**差距在哪**：新手只想到 ReRank 一个点。高手从召回、重排、规划三层分别分析了控制方法和投入产出比，且给出了明确的优先级排序。面试官考的是你对 RAG 质量控制的全链路思维。

---

### Q：Agent 场景下，什么时候该做一次检索、多次使用；什么时候该边执行边检索？

> 来源：Agent 开发面试 30 题

**新手答**：“复杂问题多查几次。”

**高手答**：

判断标准是**信息需求在任务开始时是否完全可知**：

**一次检索、多次使用（Retrieve-once）**：

```text
适用条件：
- 任务开始时就能确定需要什么信息
- 信息需求不会随执行过程变化
- 检索结果在整个任务周期内有效

典型场景：
- 客服问答：用户问"退货政策是什么"→ 一次检索产品文档，回答完整问题
- 报告生成：给定主题，一次性检索所有相关数据，然后生成报告
- FAQ 类问答：一问一答，信息需求明确
```

优势是**延迟低、成本低**，只调用一次检索管线。

**边执行边检索（Iterative Retrieval）**：

```text
适用条件：
- 下一步需要查什么取决于上一步的结果
- 任务目标可能在过程中细化或变化
- 单次检索无法覆盖所有需要的信息

典型场景：
- 故障排查："服务挂了"→ 查架构文档 → 发现依赖 Redis → 查 Redis 变更记录 → 定位根因
- 竞品分析：查到竞品 A 的信息后，发现需要对比的维度，再查竞品 B
- 多跳推理：答案分布在多个文档中，需要逐步追溯
```

优势是**信息覆盖全**，但延迟和成本成倍增加。

**工程上的混合策略**：

先做一次广泛的初始检索，如果 Agent 判断信息不足再触发增量检索。设一个**检索预算上限**（如最多 3 次），避免无限检索。

```text
第一次：广泛检索，覆盖主要信息需求
执行中判断：信息够 → 继续执行。信息不够 → 生成更精确的子查询，再检索一次
最多追加 2 次，超过后用已有信息给出最优答案
```

**差距在哪**：新手用“复杂度”判断。高手用“信息需求是否可预知”做精确判断，且给出了一次检索和迭代检索的各自适用场景，以及“初始广泛 + 按需增量”的混合策略。面试官考的是你在 RAG 和 Agent 结合时的架构设计能力。

---

### Q：如果 RAG 返回了看似可信但实际过时的信息，你会怎么降低 Agent 被误导的概率？

> 来源：Agent 开发面试 30 题

**新手答**：“定期更新知识库。”

**高手答**：

定期更新是必要的，但**更新和检索之间一定有时间差**——今天更新的文档可能要到明天才入库，而在这个窗口期内返回的就是过时信息。更根本的问题是：**很多文档没有明确的“过期时间”，系统不知道它过时了**。

需要**多层防线**：

**第一层：离线端——给文档打时间标签和可信度标签**

- 每个 chunk 存储**文档创建时间、最后更新时间、来源权威等级**
- 有明确时效性的内容（价格、政策、版本号）标记为“时效敏感”
- 建立文档更新监控——源文档变更后自动触发重新入库

**第二层：检索端——时间感知的检索和排序**

- 检索时对结果按**时间新鲜度加权**——同等相关性下，新文档排在前面
- 对“时效敏感”标签的 chunk，如果超过 TTL（如 30 天）直接降权或过滤
- 返回结果时附带时间元信息，让 Agent 知道证据的时效性

**第三层：Agent 端——生成前做时效性判断**

在 Prompt 中明确要求模型：

```text
注意：以下检索结果附带了文档日期。如果信息可能已过时（如价格、政策、版本号），
请在回答中标注"此信息来源于 YYYY-MM-DD 的文档，建议确认是否仍然有效"。
```

模型看到日期后会主动判断时效性，并在回答中加上免责提示。

**第四层：用户端——可追溯的引用**

回答中附带引用来源和日期，让用户自行判断信息是否过时。这是最后一道防线——即使系统没发现过时，用户看到“来源：2024 年 3 月的文档”也会自己注意。

**差距在哪**：新手只想到“更新知识库”——这是必要但不充分的。高手从离线（时间标签）、检索（时间加权）、Agent（时效性判断）、用户（可追溯引用）四层构建了防过时信息的完整体系。面试官考的是你对 RAG 系统“证据可靠性”这个核心问题的工程化思考。

---

## 召回与排序优化

### Q：为什么在检索阶段引入BM25？它和向量检索怎样组合？

> 来源：快手 AI Agent 开发一面 【字节二面同题：多路检索 + 向量/关键词各解决什么】【淘天Agent开发同题：为什么加 BM25 + 具体解决了什么 bad case】【钉学科技 FDE 实习一面追问：双路短板、融合与分类 bad case 验证】【[9.4 某小厂 AI Agent hr+技术面](https://www.nowcoder.com/feed/main/detail/10b2fcaf73d2401f8636bd0459e1cd08)追问：在混合检索中，除了向量语义检索，常结合的基于关键词词频与文档相关性的检索算法是什么？】【[虾皮Agent一面](https://www.nowcoder.com/feed/main/detail/409dc8793a7b450eb51ee32c2b923d49)追问：BM25 检索结果和向量检索结果，两套数据如何做结果融合？】

**新手答**：“BM25 是传统检索方法，加上它可以互补。”

**高手答**：

**为什么引入 BM25**：向量检索擅长语义匹配，但在三类场景下表现很差：

1. **精确术语**：错误码 `ERR_OOM_KILL`、API 名称 `getUserProfile`——向量化后精确信息被稀释
2. **低频专业词**：Embedding 模型对训练数据中低频的词 embedding 质量差
3. **新词/缩写**：近期出现的产品名、术语，Embedding 模型没见过

BM25 基于词频和逆文档频率做精确匹配，恰好弥补这三个短板；但它对同义词、改写和没有词面重合的语义查询较弱。两路是互补关系，不应预设固定增益，是否有效必须由本业务评测集证明。

**组合方式**：主流有两种融合策略：

```text
方案一：分数融合（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：

```text
遍历 α = [0.5, 0.6, 0.7, 0.8, 0.9]
对每个 α，计算 Recall@10 和最终回答质量
选最优 α。不同业务场景最优值不同：
  技术文档 → BM25 权重可以更高（精确术语多）
  日常对话 → 向量权重更高（同义表达多）
```

**完整检索流程**（从 query 到最终上下文）：

```mermaid
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 生成回答"]
```

1. **Query 预处理**：意图识别、查询改写、关键词提取
2. **双路并行召回**：向量路取 Top-20~50，BM25 路取 Top-10~20，并行执行
3. **合并去重**：按文档 ID 去重，同一文档被多路命中的加分
4. **Rerank 精排**：用 Cross-Encoder（如 bge-reranker-v2、Cohere rerank）对 query-doc pair 做精细打分
5. **Top-K 截断**：取精排后 Top-3~5 作为最终上下文
6. **Prompt 组装**：把检索结果按相关度排序拼入 Prompt，交给 LLM 生成回答

评估不能只看一个聚合 Recall。要把 bad case 按查询类型分桶，例如：产品编码、版本号、错误码等精确词面查询；同义词、口语改写和近义表达等语义查询；同时包含精确约束与自然语言描述的混合查询。分别检查 BM25 单路、向量单路、合并后的候选集和 rerank 后结果，才能发现“总体指标上涨，但产品编码类反而退化”这类问题，并把失败样本持续沉淀为分类回归集。

**更广义的多路检索策略**：

BM25 + 向量检索只是多路检索的起点。生产级 RAG 系统的多路检索可以更丰富：

| 检索路径 | 原理 | 擅长 | 不擅长 |
|---------|------|------|--------|
| 向量检索 | 语义相似度 | 同义改写、模糊表述 | 精确关键词、专有名词 |
| BM25 | 词频-逆文档频率 | 精确关键词、专有名词 | 同义词、语义理解 |
| 元数据过滤 | 结构化字段匹配 | 时间范围、类别筛选 | 语义理解 |
| 知识图谱 | 实体关系遍历 | 多跳推理、关联查询 | 模糊意图 |

**融合策略**：

多路召回后需要融合排序，主流方案：

1. **RRF（Reciprocal Rank Fusion）**：`score = Σ 1/(k + rank_i)`，不需要归一化，简单有效
2. **加权分数融合**：`score = α × vector_score + β × bm25_score`，需要分数归一化到同一量纲
3. **级联融合**：第一路做粗召回（高召回率），第二路在结果集上做精排（高精确率）

实际生产中，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+） | 排名差异几乎被抹平，趋近于“被多少路命中”的计数 | 更看重文档被多路召回的次数，而非在单路的排名 |

**实际调参方法**：

```text
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 太短、太模糊、或用了非标准表述，导致检索系统无法精准匹配。

**分层应对策略**：

```mermaid
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：如何系统性提升 RAG 的检索相关度与生成效果？

> 来源：快手 AI Agent 开发一面【[抖音电商Agent全栈开发工程师一面](https://www.nowcoder.com/discuss/925066865183858688)追问：底层检索做了哪些提升？】

**新手答**：“换更好的 Embedding 模型。”

**高手答**：

优化要分**检索阶段**和**生成阶段**，两者瓶颈不同、手段不同：

**检索阶段优化（提升相关度）**：

| 优化方向 | 具体手段 | 预期效果 |
|---------|---------|---------|
| Query 侧 | 查询改写、意图识别、HyDE | 提升 query 和文档的匹配度 |
| 索引侧 | 父子索引、语义分块、元信息增强 | 提升文档的可检索性 |
| 模型侧 | Embedding 微调、ReRank 微调 | 提升语义匹配精度 |
| 召回策略 | 混合检索（BM25 + 向量）、多路召回 | 提升覆盖率 |

**生成阶段优化（提升回答效果）**：

| 优化方向 | 具体手段 | 预期效果 |
|---------|---------|---------|
| 上下文质量 | 精排后截断、去除低分块、冲突检测 | 减少噪声干扰 |
| Prompt 工程 | 引用约束、格式要求、拒答指令 | 提升回答的忠实度和格式规范 |
| 模型选择 | 复杂问题用强模型、简单问题用轻量模型 | 平衡质量和成本 |
| 后处理 | 答案事实性校验、引用来源标注 | 降低幻觉风险 |

**优先级排序**（投入产出比从高到低）：

```text
① 混合检索（加 BM25 路，成本几乎为零，Recall 提升 10-15%）
② Chunk 策略优化（父子索引、语义分块，一次性投入，长期受益）
③ 查询改写（显著提升长尾 query 的召回率）
④ ReRank 引入/微调（精排对最终质量影响大）
⑤ Embedding 微调（效果好但需要标注数据，成本较高）
⑥ Prompt 优化（持续迭代，每次小幅提升）
```

**如何验证优化是否有效**：

```text
离线评估：
  检索指标：Recall@K、MRR、NDCG
  生成指标：Faithfulness、Answer Relevancy、Completeness
  工具：RAGAS 框架自动化评估

在线验证：
  A/B 测试：新旧方案同时上线，对比用户满意度和任务完成率
  灰度发布：先 10% 流量验证，确认无回退再全量

关键原则：每次只改一个变量，否则无法归因
```

**差距在哪**：新手只想到换模型——这是最贵且不一定有效的做法。高手把优化拆成检索和生成两阶段，每个阶段有明确的手段和优先级排序，且有离线+在线的验证体系。面试官考的是你优化 RAG 系统时有没有系统性的方法论和效果验证闭环。

**追问：实际项目中召回不准，你做过哪些改进？效果如何？**

这种问题考的是**实战排查方法论**，而不是罗列技术。回答结构应该是“定位 → 归因 → 方案 → 效果”：

```text
典型排查流程：

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：Rerank 后一般返回几个块？TopK 截断策略怎么设计？

> 来源：快手 AI Agent 开发一面 【字节二面追问：Re-rank 的作用 + 为什么有了向量相似度还需要它】【淘天Agent开发追问：低分阈值提前过滤策略】

**新手答**：“返回 5 个左右。”

**高手答**：

**通常返回 3~5 个块**，这个范围是多个因素权衡的结果：

```text
太少（1-2 个）：信息可能不完整，复杂问题需要多个证据源佐证
太多（8-10 个）：
  ① 噪声增加——低相关度的块干扰模型判断
  ② Lost in the Middle——模型对中间位置的信息关注度下降
  ③ Token 成本线性增长
  ④ 生成延迟增加
```

**验证方法**：在评估集上做消融实验：

```text
固定其他参数，遍历 K = [1, 2, 3, 5, 8, 10]
对每个 K 计算：
  - 回答准确率（人工评判或 LLM-as-Judge）
  - Faithfulness（回答是否忠于检索结果）
  - Token 消耗和延迟
通常 K=3~5 是准确率和成本的甜点区间
```

**TopK 截断策略**：

不建议用固定 K 一刀切。更好的做法是**分数阈值 + 上限 K + 断崖检测**三条件组合：

```python
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]]
```

三个截断条件：

1. **硬上限**：最多取 max_k 个，防止上下文过长
2. **绝对阈值**：低于 min_score 的块直接丢弃——质量太差的证据不如没有
3. **分数断崖检测**：相邻块分数骤降超过阈值时截断——说明后面的块相关度显著下降

**上下文过长/过短的处理**：

| 情况 | 处理策略 |
|------|---------|
| 上下文过长 | ① 对每个块提取关键句而非全文 ② 截断低分块 ③ 只保留和 query 最相关的段落 |
| 上下文过短 | ① 增大召回 K 值，降低召回阈值 ② 启用父 chunk 扩展上下文 ③ 查询改写后重新检索 ④ 兜底：告知用户“知识库中未找到足够信息” |

**差距在哪**：新手随便说了个数。高手用消融实验确定 K 值，用三条件自适应截断代替固定 K，且对上下文过长/过短都有处理方案。面试官考的是你调参时有没有数据支撑的方法论，以及面对边界情况的工程化处理。

---

### Q：RAG 中为什么引入父子索引？

> 来源：快手 AI Agent 开发一面

**新手答**：“为了更精确地检索。”

**高手答**：

父子索引解决的是 chunk 粒度的两难问题——**检索精度和生成质量对 chunk 大小的需求是矛盾的**。

```text
小 chunk（100-200 字）：
  ✅ Embedding 语义集中，检索精度高
  ❌ 上下文断裂，模型拿到的信息不完整

大 chunk（500-1000 字）：
  ✅ 上下文完整，模型能理解前因后果
  ❌ Embedding 被多个主题稀释，检索精度下降
```

父子索引的设计思路是**解耦检索粒度和上下文粒度**：

```mermaid
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**（大粒度，上下文完整）。这样同时拿到了检索精度和生成上下文质量。

实现方式：

1. 离线阶段把文档切成大块（父 chunk），再把每个父 chunk 切成若干小块（子 chunk），子 chunk 记录 `parent_id`
2. 对子 chunk 做 embedding 入向量库
3. 检索时用 query 匹配子 chunk，拿到结果后通过 `parent_id` 取出对应的父 chunk
4. 父 chunk 去重后送入 rerank 和 LLM

进阶：还可以做**多级索引**——文档摘要作为最顶层，段落作为中间层，句子作为最底层。先粗筛文档，再定位段落，最后精确到句子，逐层缩小范围。

**差距在哪**：新手只说了“更精确”但不知道为什么需要两层。高手点出了核心矛盾——检索需要小 chunk 精度、生成需要大 chunk 上下文——父子索引用“小粒度检索、大粒度返回”同时满足两个需求。面试官考的是你对 chunk 策略的深入理解。

---

### Q：RAG 系统的端到端性能如何优化？

> 来源：快手 AI Agent 开发一面

**新手答**：“用更快的向量数据库。”

**高手答**：

RAG 的端到端延迟 = 检索延迟 + 排序延迟 + LLM 生成延迟。优化从三个层面切入：

**检索层优化**：

```text
① ANN 索引调优：HNSW 的 ef_construction 和 M 参数影响召回率和速度的平衡
② 预过滤：先用元数据（时间范围、文档类别）缩小候选集，再做向量检索
③ 并行召回：BM25 和向量检索并行执行，总延迟 = max(两路) 而非 sum(两路)
④ Embedding 缓存：高频 query 的 embedding 结果缓存，避免重复计算
```

**模型层优化**：

```text
① 流式输出（Streaming）：用户不用等完整回答生成完才看到内容
② 模型路由：简单问题用小模型快速回答，复杂问题用大模型保证质量
③ 上下文压缩：减少送入 LLM 的 token 数——token 越少生成越快、成本越低
④ KV Cache 复用：相同 system prompt 部分的 KV cache 可以跨请求复用
```

**架构层优化**：

```mermaid
flowchart LR
    Q["用户 Query"] --> C{"语义缓存\n命中？"}
    C -->|"命中"| R["直接返回缓存结果"]
    C -->|"未命中"| P["并行检索"]
    P --> RR["Rerank"]
    RR --> LLM["LLM 生成"]
    LLM --> W["写入缓存"]
    W --> R2["返回结果"]
```

```text
① 语义缓存：用 embedding 相似度判断——"RAG 是什么"和"什么是 RAG"命中同一缓存
   命中率高的场景能减少 40-60% 的 LLM 调用
② 异步预处理：文档入库时预计算 embedding、预生成摘要，查询时直接用
③ 请求合并：短时间内相同/相似 query 合并为一次检索
④ 分级 SLA：核心业务走低延迟链路，非核心走高质量链路
```

**各优化手段的延迟收益估算**：

| 优化手段 | 典型延迟收益 | 实现成本 |
|---------|-----------|---------|
| 并行召回 | -30~50ms | 低 |
| 语义缓存 | 命中时 -500ms+ | 中 |
| 流式输出 | 首 token 提前 200-500ms | 低 |
| Embedding 缓存 | -20~50ms | 低 |
| 模型路由 | 简单问题 -300ms+ | 中 |
| 上下文压缩 | -100~300ms | 中 |

**差距在哪**：新手只想到“换更快的数据库”——这只是检索层的一个点。高手从检索、模型、架构三层拆解了完整的性能优化方案，且给出了每个手段的延迟收益估算。面试官考的是你对 RAG 系统全链路延迟的分析和优化能力。

---


## 知识库与索引工程

### Q：分块策略怎么设计？不同策略的优缺点？

> 来源：高德 AI 应用开发实习一面【腾讯AI应用开发二面追问：chunk 边界修正 + 表格跨块修复】【字节AI一面追问：领域文档语义感知切片】【Shopee 一面追问：为什么不能只按固定 Token 数切分】【[字节数据平台 Agent 一面](https://www.nowcoder.com/feed/main/detail/f5f840632a19417b91b8987762427a6a)追问：跨物理页 Chunk 与页码引用】；[钉钉一面](https://www.nowcoder.com/discuss/923765750446202880)【[虾皮Agent一面](https://www.nowcoder.com/feed/main/detail/409dc8793a7b450eb51ee32c2b923d49)追问：文档分块具体采用什么分块策略？；除递归字符切分外，还有哪些文档分块方案？】【[百度Agent一面](https://www.nowcoder.com/feed/main/detail/72858aade19d443facc870fea8bb134f)追问：Chunk 太大或太小有什么影响？Chunk 大小怎么确定？】

**新手答**：“按 500 字切一段。”

**高手答**：

分块（Chunking）是 RAG 中**影响效果最大的单一设计决策**——切得不好，后面的检索和生成再怎么优化都救不回来。

**五种主流分块策略对比**：

| 策略 | 原理 | 优点 | 缺点 | 适用场景 |
|------|------|------|------|---------|
| 固定长度分块 | 按 token 数切分 | 实现简单、大小可控 | 可能切断语义 | 结构不清晰的纯文本 |
| 语义分块 | 按段落/章节/标题切分 | 保持语义完整 | 块大小不均匀 | 有清晰结构的文档 |
| 递归分块 | 先尝试大单元分割，不满足再细分 | 平衡语义和大小 | 实现复杂 | 通用场景首选 |
| Sliding Window | 固定大小 + 重叠区域 | 防止边界信息丢失 | 存储冗余 | 精度要求高的场景 |
| Agentic Chunking | 用 LLM 判断分块边界 | 最智能 | 成本高、速度慢 | 高价值文档 |

**关键参数设计**：

```text
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（用于父子索引）
```

**父子索引策略**——同时解决检索精度和上下文完整性：

```mermaid
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（上下文完整）——两全其美。

**递归分块的实现逻辑**：

```text
分割优先级：
  1. 先按 "\n\n"（段落）分割
  2. 段落仍然太大 → 按 "\n"（换行）分割
  3. 还是太大 → 按 "。"（句号）分割
  4. 再不行 → 按空格或字符数强制切分

每一级都尽可能保持语义完整，只有上一级切不够小时才降到下一级。
```

**实践建议**：从递归分块（LangChain 的 `RecursiveCharacterTextSplitter`）作为 baseline 开始，然后根据文档类型和评估结果调整。不同类型的文档用不同策略——技术文档用语义分块（按标题切），日志文件用固定长度分块，合同文档用 Agentic Chunking。

**差距在哪**：新手用固定长度一刀切，不考虑语义。高手对比了五种策略的优劣势，理解 chunk size 和语义完整性的 tradeoff，用父子索引同时满足检索精度和上下文质量，并且知道分块策略必须根据文档类型和评估结果来调整——不存在“万能的分块方案”。面试官考的是你对 RAG 管线中最关键的预处理环节有没有深入的工程理解。

**追问：chunk 切分的边界修正怎么做？比如表格被切成了两半怎么处理？**

> 来源：腾讯 AI 应用开发二面

边界修正是分块策略中最容易被忽视的工程细节。**切错边界比不切更糟**——一个被拦腰截断的表格，检索到任何一半都无法提供完整信息。

**主流边界修正策略**：

1. **结构感知切分**：先用文档解析器识别出结构元素（标题、段落、表格、代码块、列表），以完整结构元素为最小切分单位，绝不从结构内部切断
2. **Overlap（重叠窗口）**：相邻 chunk 之间保留 10-20% 的重叠区域。即使切断了，下一个 chunk 的开头会包含上一个的结尾，信息不会完全丢失
3. **表格特殊处理**：表格作为独立 chunk，不参与常规切分。如果表格超长，按行切分但**每个子 chunk 都保留表头**（表头是理解表格的关键）

**表格被切成两半的检测和修复**：

```text
检测：解析文档时标记所有表格的起止位置 → 切分后检查是否有 chunk 边界
     落在表格内部 → 有则标记为”边界异常”
修复：把跨越表格的两个 chunk 合并，或者把表格提取为独立 chunk
```

实际工程中，最稳的做法是**先提取后切分**：先用文档解析器把表格、图片、代码块等结构化元素单独提取出来，剩余的纯文本再做常规分块。这样结构化元素天然不会被切断。

**追问：领域文档（法律/医疗）的语义单元跨 chunk 被切散，怎么办？**

> 来源：字节 AI 一面（法律 RAG 项目深挖）

法律文档中，“第三条第二款”和“第三条之二”是完全不同的法律概念——前者是第三条下的第二个分款，后者是独立的第三条之二。如果切片把这类语义单元切散，模型会混淆引用关系，直接导致回答错误。

**领域感知切片的四层方案**：

| 层级 | 做什么 | 具体实现 |
|------|--------|---------|
| 结构解析 | 用领域规则识别语义边界 | 法律：条/款/项层级正则；医疗：诊断-处方-医嘱分段 |
| 边界保护 | 语义单元不可被切断 | 将“第X条”到“第X+1条”之间的内容视为原子块 |
| 层级 metadata | 每个 chunk 携带完整的上下文链 | `{law: "民法典", chapter: "第三编", article: "第三条", clause: "第二款"}` |
| 交叉引用 | 法条互相引用时双向关联 | “依据第五条”→ 在 chunk metadata 中标记依赖关系 |

**核心原则**：通用切片策略（按 token 数、按段落）是 baseline，领域文档必须叠加**领域感知的边界规则**。切片粒度不是工程参数，是业务决策——需要和领域专家一起定义“什么是不可切断的最小语义单元”。

**追问：句子或表格跨物理页时，Chunk 要不要跨页？怎么保证引用仍能定位？**

物理页是展示边界，不一定是语义边界。解析阶段先保存每个文本/表格单元的 `page_no + bbox/span`，再按段落、标题和表格结构拼语义单元；跨页句子可以合并，跨页表格要保留表头、续表关系和每一行的页码。Chunk metadata 不能只存一个 `page`，而应保存来源跨度，例如 `page_start/page_end` 和更细的证据 span。

检索返回 Chunk 后，引用层根据实际支持结论的 span 生成页码，而不是把整个跨页 Chunk 都标成同一页。验收要覆盖跨页句子、续表、脚注、页眉页脚和 OCR 错位，并检查“召回正确、引用页码也正确”两个指标。是否跨页最终由语义完整性决定，但证据定位必须保留到原始页面坐标。

---

### Q：GraphRAG 在处理 Agent 复杂关联查询时的优势在哪里？

> 来源：淘天 AI Agent 一面 【蚂蚁AI应用开发二面同题：GraphRAG 理解与应用】【字节二面追问：多跳推理/复杂逻辑查询场景下 RAG 架构优化】

**新手答**：“用知识图谱检索，比向量搜索更准。”

**高手答**：

先说清一个常见误解：GraphRAG ≠ 知识图谱检索。传统知识图谱检索是在预构建的三元组上做 SPARQL 查询，而 GraphRAG 是微软提出的一套完整架构——**从文档自动构建知识图谱 + 社区摘要 + 本地/全局双搜索**。

**传统 RAG 的瓶颈**：

传统 RAG 的检索单元是“文本块”（chunk），本质是扁平的。当用户的问题涉及**多跳推理**（A 和 B 的关系 → B 和 C 的关系 → 推导 A 和 C 的关系）或**跨文档关联**（信息分散在多个文档中）时，靠向量相似度只能找到局部相关的片段，无法把分散的信息串联起来。

```text
传统 RAG 的困境：
用户问：”项目 A 的技术负责人和项目 B 的技术负责人之间有什么合作关系？”

文档1 提到：张三是项目 A 的技术负责人
文档2 提到：李四是项目 B 的技术负责人
文档3 提到：张三和李四共同发表了论文 X

→ 向量检索可能只召回文档1和文档2，漏掉关键的文档3
→ 即使三个都召回了，模型也需要自己做推理串联
```

**GraphRAG 的架构与流程**：

```mermaid
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**：

```text
适合 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 的抽取结果**。

**抽取流程**：

```text
文档段落 → LLM 提取实体 + 关系 → 结构化三元组 → 合并去重 → 入图
```

**幻觉控制的四层防线**：

1. **Prompt 约束**：在抽取 Prompt 中明确要求“只提取文本中明确提到的实体和关系，不推断”。给出具体的输出格式和 Few-shot 示例，减少模型自由发挥的空间
2. **Schema 约束**：预定义允许的实体类型和关系类型（如医学领域只允许“疾病-症状-药物-基因”四类实体）。LLM 抽出的实体必须属于预定义类型，否则丢弃
3. **多轮验证**：同一段文本让 LLM 抽取两次，只保留两次结果一致的三元组（投票机制）。或者用一个 Critic 模型检查抽取结果是否有文本依据
4. **溯源校验**：每个三元组保留源文本引用。后续使用时可以回查原文验证

**实体抽取错误的排查方法**：

- **随机抽样检查**：从抽取结果中随机抽 100 条三元组，人工校验准确率
- **高频实体审查**：按实体出现频次排序，高频实体更值得校验（一个错误的高频实体会污染大量关系）
- **孤立节点检查**：只有一条边的实体往往是噪声

**追问：Community Summary 的设计思路是什么？**

> 来源：腾讯 AI 应用开发二面

Community Summary 是 GraphRAG 最核心的创新——把知识图谱从“查询时遍历”变成“预计算摘要”。

**设计思路**：

1. **社区检测**：用 Leiden 算法对知识图谱做社区划分，把紧密关联的实体聚成社区（类似“话题簇”）
2. **层次化摘要**：对每个社区调用 LLM 生成一段自然语言摘要，描述“这个社区里的实体之间有什么关系、核心主题是什么”
3. **多层级**：社区可以递归聚合——小社区合成大社区，大社区有更宏观的摘要。查询时根据问题的抽象程度选择合适的层级

**为什么这样设计**：传统知识图谱回答全局性问题（“这个领域的整体趋势是什么”）需要遍历大量节点，成本高且容易遗漏。Community Summary 把全局知识预压缩成摘要，查询时直接检索摘要即可——用离线计算换在线效率。

**追问：GraphRAG 的叶子节点在智能客服中如何触发？**

> 来源：币安 AI大模型实习一面

GraphRAG 的图结构是分层的：根节点（全局摘要）→ 社区节点（主题簇摘要）→ 实体节点 → 叶子节点（原始文档 chunk / 三元组细节）。不同粒度的问题触达不同层级。

**触发机制——Global Search vs Local Search**：

| 用户问题类型 | 搜索模式 | 是否触达叶子节点 | 示例 |
|-------------|---------|---------------|------|
| 全局性/概览性 | Global Search | 否，社区摘要足够 | “你们有什么售后服务” |
| 具体实体细节 | Local Search | 是，需要原始文档细节 | “iPhone 15 能不能 7 天无理由退” |
| 需要外部数据 | Local + API 调用 | 是，且需调用外部系统 | “我的订单 123456 什么时候到” |

**叶子节点的触发流程**：

```mermaid
flowchart LR
    Q[“用户 query”] --> NER[“实体识别\n提取产品名/订单号/功能名”]
    NER --> LOC[“图定位\n在知识图谱中找到目标实体节点”]
    LOC --> TRAV[“关系遍历\n沿实体的关系边向下到叶子节点”]
    TRAV --> AGG[“上下文聚合\n叶子内容 + 关系链 + 社区摘要”]
    AGG --> LLM[“LLM 生成回答”]
```

**工程实现要点**：用意图分类器判断问题粒度——粗粒度走 Global（快但粗），细粒度走 Local 到叶子节点（慢但准）。两者可以级联：先 Global 判断主题范围确认实体所属社区，再 Local 深入到叶子节点获取具体细节。

**智能客服中的典型场景**：用户问“我买的 XX 产品能退吗” → 实体识别提取产品名 → 图中定位到该产品节点 → 沿“退货政策”关系边遍历到叶子节点（具体退货条件、时间限制、操作步骤）→ 聚合上下文生成精确回答。

---

### Q：知识库整体怎么设计？

> 来源：高德 AI 应用开发实习一面【字节二面同题：RAG 完整流程从文档切块到生成】【阿里 Agent Infra 一面题库同题】；[百度 Agent 二面](https://www.nowcoder.com/feed/main/detail/bca7dc14bd654e91b89792608111b211)【[拼多多 复活赛 一面](https://www.nowcoder.com/feed/main/detail/2109cf8eb0254507911fbf86bcbf51e4)追问：聊聊你们知识库是怎么设计的？怎么检索的？】【[虾皮Agent一面](https://www.nowcoder.com/feed/main/detail/409dc8793a7b450eb51ee32c2b923d49)追问：讲下项目里 RAG 的整体实现流程。】

**新手答**：“把文档存到向量数据库。”

**高手答**：

知识库不是“把文档扔进向量数据库”——它是一个完整的系统，涵盖**文档处理、存储索引、检索召回、质量保障和运维**五个层面。

**整体架构**：

```mermaid
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：RAG 召回数据层应如何设计文档、Chunk、Embedding、版本和权限 Schema？

> 来源：[Newegg AI 软件工程实习一面](https://www.nowcoder.com/discuss/920719616005898240)【[拼多多 复活赛 一面](https://www.nowcoder.com/feed/main/detail/2109cf8eb0254507911fbf86bcbf51e4)追问：如果文档都做倒排或索引，怎么同时解决切分和权限问题？】

**新手答**：“建一张表存文档和向量，再加标题、内容、更新时间几个字段。”

**高手答**：

不要把逻辑文档、某次解析结果、Chunk 和 Embedding 塞进一张宽表。至少拆成五类实体：

```text
document       业务文档身份、数据源、owner、当前有效版本
document_ver   内容哈希、原始对象 URI、解析状态、创建时间、父版本
chunk          稳定 chunk_id、version_id、正文、章节路径、page/span、content_hash
embedding      chunk_id、model_id、维度、向量/向量库 point_id、构建状态
acl_binding    resource_id、subject/role/tenant、policy_version、有效期
```

离线任务另存 `ingestion_run`，记录解析器、Chunk 策略、Embedding 模型、输入/输出数量、失败项和幂等键。`document_id` 表示业务对象，`version_id` 表示不可变快照；回答绑定具体版本和 Chunk，不能只记“来自某文档”。Embedding 模型升级时新增 embedding 记录和索引版本，不覆盖旧向量；切流完成后再按保留策略回收。

关键不变量包括：Chunk 必须属于一个不可变文档版本；向量必须能反查原文 span；删除/权限撤销能沿数据血缘找到索引、缓存和派生摘要；同一 `content_hash + pipeline_version` 重跑幂等。关系库保存权威元数据和状态，原始大文件进对象存储，向量库负责近邻检索；三者通过稳定 ID 关联，不让向量库成为唯一事实源。

**差距在哪**：新手只有“内容加向量”，高手把版本、血缘、权限、模型迁移和可删除性落实成数据模型与不变量。

---

### Q：向量数据库怎么选型？不同规模下该用什么方案？

> 来源：阿里国际 AI 应用研发二面 【淘天Agent开发追问：为什么选 pgvector 而不是其他向量数据库】；[中兴软开一面](https://www.nowcoder.com/feed/main/detail/0b39815babfb47108464ffabdf929eba)；[钉钉一面](https://www.nowcoder.com/discuss/923765750446202880)

**新手答**：“用 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 应用开发实习一面

**新手答**：“用 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 | 中英文均衡 |

**实际选型决策流程**：

```mermaid
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定期评估效果"]
```

1. **先确定语言需求**：中文场景优先选中文优化模型，不要盲目选英文模型
2. **跑 domain-specific evaluation**：不只看 MTEB/C-MTEB 排名，必须在自己的数据上评估。公开 benchmark 上排名第一的模型，在你的业务数据上可能排第五
3. **对比部署成本**：API 调用模型（如 OpenAI）按量计费，自部署模型有 GPU 成本。数据量大时自部署更划算
4. **考虑是否需要微调**：领域术语多的场景（医疗、法律、金融），通用模型的 embedding 质量会明显下降，这时选开源模型做微调是更好的路径

**关键认知**：公开 benchmark 排名不等于你的场景效果。必须在自己的数据上做 A/B 评测——构建几百条 `(query, relevant_doc)` 的评估集，跑 Recall@K 和 MRR，用数据说话。

**差距在哪**：新手直接选最有名的模型。高手从六个维度系统评估，坚持在业务数据上做 domain-specific evaluation，而不是盲目跟 benchmark 排名。面试官考的是你选型时有没有工程化的方法论，以及对“通用模型 vs 领域适配”这个 tradeoff 的理解。

---

### 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 占优：

```text
自然语言查询："认证逻辑在哪里？"
  → grep 无法处理，因为代码中没有"认证逻辑"这个字符串
  → RAG 的语义检索能找到 auth_middleware.py、login_handler.py

概念级搜索："找到类似的实现"
  → grep 只能匹配精确字符串
  → 向量检索能找到语义相似的代码片段

跨模块依赖分析："哪些模块依赖了这个服务？"
  → grep 能做简单的 import 搜索，但知识图谱 / RAG 能发现间接依赖
```

**Claude Code 的实际策略——渐进式披露（Progressive Disclosure）**：

```mermaid
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：Coding Agent 应从代码反向理解领域知识，还是维护独立知识库/规则库？

> 来源：[国际业务 Agent 二面](https://www.nowcoder.com/feed/main/detail/b163baeb304e432d9b4c9c218ed467fa)

**新手答**：“把代码全部向量化放进知识库，Agent 检索到相关代码就能理解业务。”

**高手答**：

代码和知识库不是二选一，它们回答不同问题。代码、类型、接口和可执行测试是“系统现在怎么运行”的证据；产品规则、术语、流程、合规约束和设计决策解释“为什么这样运行、允许改成什么”。只读代码会把历史兼容逻辑误当业务规则，只读文档又可能使用已经过期的设计。

构建上下文时分层检索：先用符号搜索、依赖图和测试定位当前实现，再按模块、领域实体和版本检索规范/决策记录。每条外部知识保留 owner、适用服务、代码版本范围、更新时间和来源；代码索引绑定 commit，知识索引绑定文档版本。两者冲突时不让模型自行“取一个更合理的”，而是按权威策略处理：运行事实以当前代码/测试为准，目标行为以已批准 Spec 为准，无法判定则生成差异报告并请求 owner 澄清。

变更后同时验证代码和知识：接口/领域事件变化触发受影响文档检查，规范变更触发实现和测试差异检查。最终提交应说明用了哪些代码证据、哪些业务规则以及尚未解决的冲突，避免 Agent 把推断写成事实。

**差距在哪**：新手把领域知识等同于代码 Embedding，高手区分运行事实与规范意图，并用版本、权威层级和双向变更检查处理冲突。

---

### Q：升级 Embedding 模型后，怎么保证索引和检索向量的逻辑一致性？

> 来源：阿里国际 AI 应用研发二面

**新手答**：“重新跑一遍索引就行了。”

**高手答**：

**核心问题**：旧文档用 Model_v1 编码入库，新查询用 Model_v2 编码检索——两个模型的向量空间不对齐，导致检索质量断崖式下降。即使两个模型都很强，它们学到的向量空间是不同的，v1 空间里的“近邻”在 v2 空间里可能完全不是近邻。

“全量重建索引”是正确的方向，但**对于百万级文档，全量重建需要数小时甚至数天**，在此期间系统要么不可用，要么用旧模型继续服务（效果变差）。所以真正的挑战不是“能不能重建”，而是“怎么平滑过渡”。

**三种升级策略**：

**策略一：双索引并行**

```text
在旧索引继续服务的同时，后台用新模型构建新索引。
新索引构建完成后，做一次原子切换——流量瞬间从旧索引切到新索引。
```

优点：切换瞬间完成，用户无感知。缺点：过渡期需要双倍存储成本，且切换前无法验证新索引的线上效果。

**策略二：渐进式迁移**

```text
给每个文档向量打上 embedding_model_version 标签。
过渡期内，查询同时用 v1 和 v2 两个模型编码，分别检索对应版本的分区，合并结果。
后台逐步将旧文档用新模型重新编码，完成后删除旧分区。
```

优点：平滑过渡，任何时刻系统都可用。缺点：过渡期查询要编码两次、搜索两次，延迟翻倍。

**策略三：版本化索引 + 灰度切换**

```text
新索引构建完成后，不立即全量切换，而是先导流 5-10% 到新索引。
对比新旧索引的线上指标（召回率、相关性、用户点击率）。
指标达标 → 逐步扩大流量比例直到全量切换。
指标异常 → 立即回滚到旧索引。
```

优点：风险最低，有数据验证。缺点：验证周期长，需要流量分配基础设施。

**完整的双索引过渡流程**：

```mermaid
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释放存储"]
```

**工程保障措施**：

1. **始终存储原始文本**：向量可以重算，原始文本不能丢。每个 chunk 必须保留原始文本，这样任何时候都能用新模型重新编码
2. **版本标记每个向量**：`embedding_model_version` 字段必须持久化到向量记录中，方便区分哪些文档已迁移、哪些还没有
3. **自动化质量回归测试**：维护一组 `(query, expected_relevant_docs)` 的黄金测试集，每次模型升级后自动跑 Recall@K 和 MRR，低于阈值则阻断上线

**差距在哪**：新手只想到“重建”——这是正确的第一步但忽略了过渡期的可用性。高手给出了双索引并行、渐进式迁移、灰度切换三种策略，且强调了版本标记和自动化质量回归测试。面试官考的是你对向量检索系统升级的工程化思维——不是“能不能升级”，而是“怎么平滑升级”。

---


## 进阶检索架构

### Q：图检索、向量检索、混合检索有什么区别？怎么选？

> 来源：腾讯 AI 应用开发二面；[字节 AI 应用开发二面](https://www.nowcoder.com/feed/main/detail/7e8a821479a649fd914e449d312eeb95)【[百度Agent一面](https://www.nowcoder.com/feed/main/detail/72858aade19d443facc870fea8bb134f)追问：为什么选择混合召回？】【[作业帮秋招一面](https://www.nowcoder.com/feed/main/detail/c86c7591ba9d47b696774ddb48cdc9cb)追问：RAG 是多路检索吗，单路检索能不能做、能不能解决业务问题？】

**新手答**：“向量检索最常用，图检索更准。”

**高手答**：

三种检索方式解决的是**不同类型的查询问题**，不存在“哪个更准”：

| 维度 | 向量检索 | 混合检索 | 图检索（GraphRAG） |
|------|---------|---------|-------------------|
| **检索原理** | 语义相似度匹配 | 语义 + 关键词双路 | 实体关系遍历 + 社区摘要 |
| **擅长的查询** | “XX 是什么”“如何做 XX” | 既需要语义又需要精确匹配 | “XX 和 YY 有什么关系”“整体趋势” |
| **不擅长的查询** | 多跳推理、关系型问题 | 全局性总结类问题 | 简单事实问答 |
| **索引构建成本** | 低（只需 Embedding） | 中（Embedding + 倒排索引） | 高（LLM 抽取实体 + 图构建） |
| **查询延迟** | 毫秒级 | 毫秒级 | 秒级（需图遍历） |
| **维护成本** | 低（增量更新简单） | 中 | 高（实体变更需重建子图） |

**选型决策树**：

```text
查询类型主要是什么？
├── 事实问答（”XX 的创始人是谁”）→ 混合检索（关键词精确 + 语义兜底）
├── 概念解释（”什么是 RAG”）→ 向量检索（语义匹配足够）
├── 关系推理（”A 和 B 有什么关联”）→ 图检索
├── 全局总结（”这个领域的趋势”）→ 图检索（Community Summary）
└── 混合场景 → 多路检索 + ReRank 融合
```

**生产系统的常见做法**：不选一种，而是**多路并行、统一精排**。向量检索和关键词检索作为基础双路，图检索作为补充路（只在检测到关系型查询时启用）。最后用 ReRank 模型统一排序，取 Top-K 送给生成模型。

“为什么没用 Knowledge Graph”也要用证据回答：先按线上 Query 标注事实查找、精确术语、多跳关系和全局总结的占比，再用同一评测集比较向量/混合检索与图检索在目标切片上的增量。如果问题主要是单跳事实、实体关系数据不完整、知识更新频繁，图的抽取、消歧、冲突合并和增量维护成本可能高于收益，此时不使用 KG 是合理决策；只有关系型切片的端到端正确率、证据质量或可解释路径显著提升，且延迟与维护成本可接受，才逐步启用图检索。不能用“GraphRAG 更先进”或“项目没时间”代替量化取舍。

**差距在哪**：新手把三种检索当成“好/更好/最好”的关系。高手理解它们解决的是不同类型的查询问题——向量检索擅长语义匹配，混合检索兼顾精确和语义，图检索擅长关系推理和全局总结。选型依据不是“哪个更先进”，而是“你的查询类型分布是什么”。面试官考的是你对多种检索范式的系统性理解和选型能力。

---

### Q：RAG 架构与模型微调（Fine-tuning）相比，各自的适用场景和优缺点是什么？

> 来源：字节后端 Agent 开发二面 【淘天转正实习一面追问：预训练语料已包含相关知识为什么还要RAG】【[字节二面（Trae）](https://www.nowcoder.com/discuss/924821959647440896)追问：RAG 主要用来做什么？】

**新手答**：“RAG 不需要训练，Fine-tuning 效果更好。”

**高手答**：

这两种方案不是互斥的，而是解决**不同类型知识注入问题**的工具。核心判断框架：

**RAG 本质是「外挂知识」——检索时注入**

| 维度 | RAG |
|------|-----|
| 知识更新 | 改文档即生效，无需重新训练 |
| 可追溯性 | 每个回答都能追溯到来源文档 |
| 成本 | 无训练成本，主要是检索基础设施 |
| 知识容量 | 理论上无上限，取决于文档库规模 |
| 适用场景 | 知识频繁更新、需要引用来源、企业知识库 |

**Fine-tuning 本质是「内化知识」——训练时注入**

| 维度 | Fine-tuning |
|------|-------------|
| 知识更新 | 需重新训练和部署 |
| 可追溯性 | 无法追溯到具体来源 |
| 成本 | 训练 + 数据标注 + GPU 资源 |
| 知识容量 | 受模型参数量限制 |
| 适用场景 | 固化领域风格/格式、提升特定任务能力、降低推理成本 |

**关键决策点**：

```mermaid
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 开发二面【[阿里千问 AI 应用研发一面](https://www.nowcoder.com/feed/main/detail/da6d74a34ceb4e52b9b4fbcac25cfb3b)追问：文档/Chunk ACL、缓存与引用泄露】【[拼多多 复活赛 一面](https://www.nowcoder.com/feed/main/detail/2109cf8eb0254507911fbf86bcbf51e4)追问：你们把内部文档给 Agent 用，有没有考虑过泄露问题？】

**新手答**：“给每个用户建一个单独的知识库。”

**高手答**：

权限隔离和时效性是 RAG 上生产环境后**最先暴露的两个安全问题**。单独建库的方案在用户量大时不可行——需要系统性的工程方案：

**一、权限隔离：检索前过滤，而非物理隔离**

```text
用户请求 → 鉴权（提取用户权限标签）
         → 在向量检索时注入 metadata filter
         → 只召回用户有权访问的文档
```

实现方式：

| 方案 | 实现 | 适用场景 |
|------|------|---------|
| **元数据过滤** | 每个文档 chunk 存储权限标签（部门、角色、项目），检索时通过 pre-filter 过滤 | 权限维度明确、变更不频繁 |
| **多租户索引** | 按租户分索引（物理隔离），查询时路由到对应索引 | 租户间数据完全隔离（如 SaaS 多客户） |
| **行级安全** | 在关系型数据库层面做权限控制，向量检索结果再和权限表 join | 权限规则复杂、需要动态变更 |

关键工程细节：pre-filter（检索前过滤）vs post-filter（检索后过滤）的选择。pre-filter 在向量数据库层面直接过滤，效率高但可能影响召回质量（候选集变小）；post-filter 先全量召回再权限过滤，召回质量高但有信息泄露风险（模型可能已经「看到」了无权限内容的 embedding 相似度信号）。**生产中推荐 pre-filter**。

ACL 必须从文档继承到 Chunk、派生摘要、图片描述和索引点，所有向量/BM25/GraphRAG/补充检索路径使用同一授权条件。向量库的 metadata filter 只是执行能力，例如 [Qdrant 官方过滤文档](https://qdrant.tech/documentation/search/filtering/)支持 payload 条件；授权策略仍由可信身份系统决定。关系元数据可使用类似 [PostgreSQL Row-Level Security](https://www.postgresql.org/docs/current/ddl-rowsecurity.html)的默认拒绝策略，并审计高权限账号和策略绕过边界。

缓存键还要包含租户、权限摘要和策略版本；权限撤销后使相关缓存失效。Rerank、上下文组装和最终引用再次校验资源权限，避免无权 Chunk 通过缓存、日志或引用反查泄露。回归集覆盖跨租户、共享文档、权限撤销、ACL 继承和缓存串读。

**二、时效性：文档生命周期管理**

| 策略 | 做法 |
|------|------|
| **时间戳标记** | 每个 chunk 存储文档创建/更新时间，检索时做时间衰减加权 |
| **增量更新** | 文档变更时只重新嵌入受影响的 chunk，不重建全量索引 |
| **版本管理** | 同一文档的多个版本共存，检索时优先返回最新版本 |
| **TTL 机制** | 设置文档过期时间，过期后自动降权或剔除 |

**时效性的隐藏陷阱**：用户问「最新的政策是什么」，如果召回了旧版本文档，模型会很自信地给出过时的答案——这比「不知道」更危险。防御方案：在生成阶段注入时间上下文（「以下文档更新于 2024-03-01，请注意时效性」），让模型自己判断是否需要提示用户信息可能过时。

**差距在哪**：新手只想到物理隔离（按用户建库）。高手给出了三层权限方案（元数据过滤、多租户索引、行级安全）和四种时效性策略，且点出了 pre-filter vs post-filter 的安全 trade-off。面试官考的是你对 RAG 工程化落地中安全问题的实战认知。

---

### Q：向量数据库中 IVF_FLAT 和 HNSW 索引的区别是什么？各自适合什么场景？

> 来源：快手AI应用开发一面【[阿里巴巴（淘天）- 大模型算法岗（搜推方向）](https://www.nowcoder.com/discuss/926272464059891712)追问：向量检索中 IVF 与 HNSW 的选型依据是什么？】【[全栈实习一面，20分钟居然问这么细😂](https://www.nowcoder.com/feed/main/detail/4af1e257116e4e36970c6e0d8bf2f70e)追问：你的 RAG 向量数据库用的索引是什么？】

**新手答**：“都是加速向量搜索的索引，HNSW 更快。”

**高手答**：

IVF_FLAT 和 HNSW 是两种完全不同的索引范式——一个基于分区量化，一个基于图遍历，适用场景差异很大：

| 维度 | IVF_FLAT | HNSW |
|------|----------|------|
| 核心思想 | 先聚类分桶，查询时只搜少数桶 | 构建多层跳表图，贪心遍历近邻 |
| 构建时间 | 快（kmeans 聚类） | 慢（逐点插入建图） |
| 内存占用 | 低（只存聚类中心 + 原始向量） | 高（每个节点存多层邻居指针） |
| 查询速度 | 中等（受 nprobe 参数控制） | 极快（图遍历，路径短） |
| 召回率 | nprobe 小时召回低，大时接近精确 | 高（ef_search 调大后接近 100%） |
| 增量更新 | 容易（新向量分配到最近的桶） | 较难（插入需更新多层图结构） |
| 数据规模 | 适合千万级以上（配合 PQ 压缩） | 适合百万到千万级 |

**IVF_FLAT 工作原理**：

```text
建索引：对所有向量做 K-means 聚类 → 得到 nlist 个聚类中心
查询时：计算 query 与所有聚类中心的距离 → 选最近的 nprobe 个桶 → 在这些桶内暴力搜索
```

**HNSW 工作原理**：

```text
建索引：逐个插入向量，每个向量随机分配层级 → 在每层中与最近邻建立边
查询时：从顶层入口点开始 → 在每层贪心跳到最近邻 → 逐层下降直到底层 → 底层精搜 Top-K
```

**选型决策**：

| 场景 | 推荐索引 | 原因 |
|------|---------|------|
| 数据量大（>1000w）、内存有限 | IVF_PQ（IVF + 量化压缩） | HNSW 内存扛不住 |
| 数据量中等、要求低延迟 | HNSW | 查询速度最快，延迟稳定 |
| 数据频繁更新（实时写入） | IVF_FLAT | 增量更新成本低 |
| 离线批量检索、对延迟不敏感 | IVF_FLAT（大 nprobe） | 召回率高且节省内存 |
| RAG 在线服务 | HNSW | 用户感知延迟要低，内存可以加机器解决 |

**差距在哪**：新手只知道“HNSW 更快”——这是结论不是理解。高手从数据结构原理（分桶 vs 图遍历）出发，推导出各自的性能特征和适用场景，且能给出具体的选型建议。面试官考的是你对向量检索底层机制的理解深度——不是会用 API，而是知道为什么这个参数要这样调。

---

### Q：Deep Research 在代码层面是怎么实现的？和普通 RAG 有什么区别？

> 来源：bilibili AI研发实习一面【字节火山引擎 Managed Agent 一面追问：如何用 Agent 做 Auto Research】

**新手答**：“就是多搜几次，然后总结一下。”

**高手答**：

Deep Research 不是“多搜几次”——它是一种**Agent 驱动的迭代检索-合成架构**，核心区别在于检索策略是动态的、由 Agent 在运行时根据已有信息决定下一步查什么。

**和普通 RAG 的本质区别**：

| 维度 | 普通 RAG | Deep Research |
|------|---------|---------------|
| 检索次数 | 一次（query → 检索 → 生成） | 多次迭代（每轮检索结果影响下一轮查询） |
| 查询生成 | 用户原始 query 或简单改写 | Agent 根据已有信息动态生成子查询 |
| 知识综合 | 拼接检索结果给模型 | 逐步构建结构化的知识图，跨文档关联 |
| 覆盖判断 | 无——检索一次就结束 | Agent 评估“信息是否足够”，不够则继续检索 |
| 输出 | 短回答 | 长篇结构化报告 |

**代码层面的核心实现**：

用 LangGraph 实现 Deep Research 的典型架构：

```mermaid
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. 规划节点——把大问题拆成可检索的子问题**

```python
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. 检索节点——对每个子问题做多源并行检索**

```python
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 不只是检索，还要**评估检索结果的充分性**。如果发现知识缺口（某个子问题还没有足够的证据支撑），生成新的查询再检索。

```python
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. 迭代控制——防止无限检索**

```python
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：RAG 知识库的噪声剔除和文档去重怎么做？

> 来源：腾讯 AI 应用开发二面 【淘天Agent开发追问：防止 AI 批量生成虚假数据投毒】

**新手答**：“看着删。”

**高手答**：

知识库的质量直接决定 RAG 的效果上限。**垃圾进，垃圾出**——再好的检索和生成也无法弥补知识库本身的噪声。

**噪声剔除分三层**：

| 层次 | 噪声类型 | 剔除方法 |
|------|---------|---------|
| 文档级 | 空文档、格式损坏、非目标语言 | 规则过滤（长度/编码/语种检测） |
| 段落级 | 页眉页脚、版权声明、目录、广告 | 正则匹配 + 分类器（训练一个轻量模型区分正文和噪声） |
| 语义级 | 无信息量的废话、过时信息 | 信息密度评分（困惑度过低的段落往往是套话） |

**文档去重的三种策略**：

1. **精确去重（URL / 文件哈希）**：对文档的 URL 或内容 MD5 去重，处理完全相同的文档。最快，但无法处理“内容相同但格式不同”的情况
2. **近似去重（MinHash / SimHash）**：计算文档的指纹，相似度超过阈值（如 0.9）的文档只保留一篇。适合处理“同一篇文章的不同版本”
3. **语义去重（Embedding 聚类）**：对所有文档做 Embedding，用 DBSCAN 聚类，同一簇内保留质量最高的一篇。计算成本高，但能处理“不同表述的相同内容”

**重复片段识别**：

多篇文章中出现的重复段落（如“免责声明”“公司简介”）比整篇重复更隐蔽。处理方法：

```text
1. 对所有 chunk 计算 SimHash 指纹
2. 按指纹分桶，同桶内的 chunk 两两比较文本相似度
3. 相似度 > 0.85 的标记为重复片段
4. 保留出现次数最多的那一份，其余删除或标记为”低优先级”
```

**差距在哪**：新手要么不做清洗（让噪声进入检索），要么靠人工逐条检查（不可扩展）。高手从文档级/段落级/语义级三层做噪声剔除，用精确/近似/语义三种策略做去重，形成了可自动化、可扩展的知识库清洗管线。面试官考的是你对 RAG 数据质量的工程化管控能力。

---

### 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 的核心思路**：

```mermaid
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：多模态 Embedding 检索中，文本语义与图像视觉特征的权重怎么平衡？用户检索图纸参数却召回外观相似零件，根源是什么？

> 来源：阿里淘天 AI应用开发一面

**新手答**：“把文本和图片都转成向量，拼一起搜。”

**高手答**：

多模态检索的核心矛盾是**不同模态的语义空间天然不对齐**——文本描述的是抽象概念（尺寸、材质、公差），图像编码的是视觉特征（颜色、形状、纹理）。“拼一起搜”忽略了这个根本问题。

**两种融合方式**：

| 融合方式 | 原理 | 优点 | 缺点 |
|---------|------|------|------|
| Early Fusion（CLIP-style 联合空间） | 文本和图像在训练时就映射到同一个向量空间 | 端到端优化，跨模态匹配能力强 | 通用模型学到的是“外观相似性”，不理解工程参数 |
| Late Fusion（分别编码再加权） | 文本和图像各自编码，检索时加权合并分数 | 权重可灵活调整，各模态可独立优化 | 跨模态交互弱，依赖权重调参 |

**权重平衡方案**：

**静态权重**：`final_score = α × text_sim + (1-α) × image_sim`，α 是固定值（如 0.6）。简单但不灵活——参数查询和外观查询用同一个权重显然不合理。

**动态权重**（更优方案）：根据 query 类型自动调整权重——

```text
Query 类型判断：
  "M8×1.25 螺栓 材质 304" → 参数查询 → α=0.9（几乎全靠文本）
  "这个零件长什么样"      → 外观查询 → α=0.2（主要靠图像）
  "找类似这张图纸的零件"  → 混合查询 → α=0.5（均衡）
```

实现方式：用一个轻量分类器（或规则引擎）先判断 query 类型，再动态设置 α。

**图纸参数召回外观相似零件的根源分析**：

问题**不在检索算法，在 Embedding 模型**。通用视觉模型（如 CLIP）在预训练时学到的是外观级别的相似性——颜色、形状、轮廓。它不理解工程图纸中的参数语义（尺寸标注、公差范围、材质标签）。所以当用户搜“M8×1.25 不锈钢螺栓”时，模型返回的是外观最像螺栓的图片，而不是参数匹配的零件。

**三种解决方案对比**：

| 方案 | 做法 | 优点 | 缺点 | 适用场景 |
|------|------|------|------|---------|
| 通用视觉模型（CLIP） | 直接用预训练模型做多模态检索 | 开箱即用，零成本 | 不理解工程参数，召回靠外观 | PoC 阶段、外观检索 |
| 领域微调模型 | 用工程图纸 + 参数标注数据微调 Embedding | 理解领域语义，参数匹配准确 | 需要标注数据，训练成本高 | 参数检索精度要求高 |
| 混合检索方案 | 参数字段走精确匹配，外观走向量检索 | 各取所长，参数精确、外观覆盖 | 系统复杂度高，需维护两套索引 | 生产环境首选 |

**推荐的生产方案——混合检索**：

```text
结构化参数（尺寸、材质、公差）→ 标量过滤（精确匹配 / 范围查询）
外观特征（形状、颜色）        → 向量检索（视觉相似度）
两路结果取交集或加权合并
```

结构化参数**不应该走向量检索**——“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 的详细流程**：

```text
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）：

```python
# 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"  # 标量过滤
)
```

**最佳实践**：

1. **时间维度做 partition key**：按时间范围（如按月）对数据做分区，查询时直接跳过不相关的分区，连标量过滤都省了
2. **高基数标量字段建 scalar index**：对 timestamp、category 等字段建标量索引，加速过滤
3. **Pre-filter 优先**：除非过滤条件非常宽松（只筛掉 <10% 数据），否则 pre-filter 几乎总是优于 post-filter
4. **监控过滤率**：如果 post-filter 丢弃了超过 50% 的 ANN 结果，说明应该切换到 pre-filter

```mermaid
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 的一个可反复调用的工具。

**两者的核心架构差异**：

```mermaid
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 的三个关键能力**：

1. **自主判断检索时机**：不是每个 query 都需要检索——Agent 先评估自身知识是否足够，不足时才触发检索。避免了传统 RAG 对所有请求都检索的资源浪费
2. **检索结果自我评估**：检索完成后，Agent 判断结果是否能回答问题。如果召回的文档不相关或信息不完整，主动改写 query 或切换检索策略进行二次检索
3. **多源检索编排**：复杂问题拆成多个子问题，分别用不同的检索工具（向量库查概念、知识图谱查关系、SQL 查数据），最后综合推理

**工程落地的权衡**：

Agentic RAG 的能力更强，但**延迟和成本也更高**——多次检索意味着多次 LLM 推理。生产中通常采用**分级策略**：简单 query 走传统 RAG 快速响应，复杂 query 才进入 Agentic RAG 循环。

**差距在哪**：新手把 Agentic RAG 简单理解为「RAG + Agent」的拼凑。高手理解核心区别在于**检索从固定管线变成了 Agent 的可迭代工具**——Agent 能自主决定是否检索、检索什么、结果够不够、要不要换策略。面试官考的是你对 RAG 范式演进的认知——从被动管线到主动推理。

---

### Q：补充检索是如何评估数据质量并触发的？怎么保证二次检索能搜到之前没搜到的内容？

> 来源：淘天 Agent 开发

**新手答**：“检索结果不好就再搜一次。”

**高手答**：

「再搜一次」如果用同样的策略，结果不会有本质变化。补充检索的关键是**先评估质量，再换策略重搜**：

**一、什么时候触发补充检索？**

需要一个**质量评估门控**，而不是每次都二次检索：

| 评估维度 | 检测方法 | 触发条件 |
|---------|---------|---------|
| **召回数量** | 候选文档数 < 阈值 | top-K 返回不足 3 条 |
| **相关性得分** | Rerank 后最高分低于阈值 | 最高分 < 0.5（模型和数据相关） |
| **覆盖度** | query 中的关键实体/概念是否被召回文档覆盖 | 核心实体零命中 |
| **自洽性** | 让模型判断「基于这些文档能否回答问题」 | 模型输出「信息不足」信号 |

最实用的方案是**相关性得分 + 模型自判**组合：Rerank 分数兜底硬指标，模型自判捕捉语义层面的不足。

**二、怎么保证二次检索搜到不同的内容？**

核心是**换检索策略**，而不是重复同一策略：

```mermaid
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：RAG 检索到的 Chunk 不足以回答问题，后续怎么处理？

> 来源：字节春招大模型测开一面

**新手答**：“让模型直接用自己的知识回答，或者告诉用户找不到。”

**高手答**：

这是 RAG 系统最常见的退化场景之一。“Chunk 不足”有两种含义——检索到了但信息不够完整，或者根本没检索到相关内容。处理策略完全不同：

**第一层：判断“不足”的程度**

在生成前先做一步**充分性评估**（Sufficiency Check）：用 LLM 判断当前检索结果能否支撑回答。评估维度：

- 关键实体是否存在（问“A 和 B 的区别”，至少要有 A 和 B 的描述）
- 时间/数值是否覆盖（问“2024年营收”，2023年的数据不算充分）
- 逻辑链是否完整（问“为什么 X 导致 Y”，需要因果链条，不能只有结论）

**第二层：补充检索策略**

如果判定不足，触发 Agentic RAG 的补充检索：

```mermaid
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[“诚实告知+给出部分答案+标注不确定性”]
```

关键策略：

1. **查询改写** — 原始 query 可能太笼统或措辞偏离文档表达。拆成子问题、换同义词、补充上下文后重新检索
2. **扩大召回范围** — 降低相似度阈值、增加 Top-K、切换到 BM25 关键词匹配（语义检索漏掉的关键词匹配可能命中）
3. **跨库检索** — 如果本地知识库确实没有，可以调用外部搜索引擎、数据库查询、或调用其他系统的 API

**第三层：生成策略**

如果补充检索后仍不足：

- **部分回答 + 标注** — “根据现有信息，X 部分的答案是...，但关于 Y 的具体数据暂未找到”
- **不编造** — 绝对不能让模型用自身参数知识补充事实性信息（这是幻觉的主要来源）
- **引导用户** — “您可以提供更具体的文档/关键词吗？”或“建议查阅 XX 系统获取最新数据”

**差距在哪**：新手的处理是二元的——“有就答，没有就说不知道”。高手有一套分级应对策略：先评估不足程度，再用查询改写/扩大召回/跨库检索做补充，最后仍不足时给出部分答案并标注不确定性。面试官考的是你对 RAG 系统边界情况的处理能力——生产环境中“检索不足”比“检索精准”出现得更频繁。

---

### Q：向量数据库里两个同义词是什么关系？完全同义的词会在同一个点上吗？

> 来源：字节春招大模型测开一面

**新手答**：“同义词的向量应该一样，在同一个点上。”

**高手答**：

同义词在向量空间中**非常接近但不会完全重合**。这涉及 Embedding 模型的本质工作方式：

**为什么不在同一个点上**：

Embedding 模型编码的不仅是“词义”，还包括：
- **使用语境差异** — “开心”和“高兴”虽然同义，但“开心果”和“高兴果”不等价。模型会捕捉到这些细微的搭配差异
- **语域/正式度** — “逝世”和“死了”语义相同，但正式度不同，向量会有偏移
- **训练语料分布** — 两个词在训练数据中的上下文分布不完全相同，导致编码略有差异

**具体的空间关系**：

```text
完全同义词（如 “AI” 和 “人工智能”）：
  → 余弦相似度 > 0.95，几乎重合但有微小偏移

近义词（如 “开心” 和 “高兴”）：
  → 余弦相似度 0.85-0.95，同一区域的邻近点

上下位关系（如 “狗” 和 “动物”）：
  → 余弦相似度 0.6-0.8，有方向性偏移

反义词（如 “大” 和 “小”）：
  → 余弦相似度可能 0.3-0.6（不一定对称分布在原点两侧）
```

**对 RAG 的实际影响**：

同义词接近但不重合意味着：
1. 查询 “AI应用” 可以召回包含 “人工智能应用” 的文档（相似度高）
2. 但精确匹配度略低于完全相同的表述 → 这就是为什么需要**查询改写**（把 “AI” 扩展为 “AI/人工智能”）
3. 也是为什么**混合检索**（向量 + BM25）有效 — BM25 处理精确匹配，向量处理语义近似

**差距在哪**：新手认为同义词 = 同一个点。高手理解 Embedding 空间是连续的高维空间，同义词是“近邻”而非“重合”，且能解释为什么——因为模型编码的是上下文分布而非字典定义。面试官通过这个问题考察你对 Embedding 的理解是停留在“API 调用”还是真正理解其数学含义。

---

## Q：RAG 过程中如何处理文件里的图片？

> 来源：字节暑期agent实习二面【[PDD Agent三面](https://www.nowcoder.com/feed/main/detail/9908477cdd4041fabacbfbf02febb13c)追问：RAG如果存在图片类非文本内容，如何处理？】

**新手答**：“把图片转成文字再检索。”

**高手答**：

RAG 中图片处理是多模态 RAG 的核心问题，主流方案：
1. **图片→文本描述（OCR + Caption）**：用 OCR 提取图中文字，用多模态模型（如 GPT-4V/Qwen-VL）生成图片描述，将描述文本一起做 Embedding 索引
2. **多模态 Embedding 直接索引**：用 CLIP 等模型直接将图片编码为向量，和文本向量放同一向量空间，支持跨模态检索
3. **图文联合分块**：在 chunk 切分时保持图片与其上下文段落的关联——图片属于哪个段落，chunk 就包含“段落文本 + 图片描述/图片引用”
4. **Layout-Aware 解析**：用 PDF 解析工具（如 Unstructured、Marker）识别图片在文档中的位置和语义角色（图表？示意图？截图？），不同类型用不同处理策略
5. **延迟处理**：检索阶段只用文本索引，召回相关 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 不会消失的四个理由：**

1. **成本问题**：1M token 的推理成本远高于“检索 5 个 chunk + 8K 上下文”。对高 QPS 场景（如客服），全量塞入的经济性不可接受
2. **注意力稀释**：即使窗口能装下，模型对中间位置信息的关注度仍然不均匀（Lost in the Middle 问题未根本解决），检索后精选 Top-K 反而提高相关信息的权重
3. **实时性与权限**：知识库持续更新，不可能每次请求都重新拼装全量文档；且不同用户有不同的数据权限，RAG 的过滤层天然支持权限隔离
4. **可审计性**：RAG 能明确告诉你“这个回答基于哪个文档的哪个片段”，全量塞入后模型引用溯源变得极困难

**窗口扩容确实改变了什么：**

- **RAG 的角色从“必须”变成“优化”**：以前窗口不够必须检索，现在是为了提高质量和降本而检索
- **chunk 策略变粗**：以前切 512 token 的小块，现在可以检索整段甚至整篇文档
- **检索-生成边界模糊化**：可以先粗检索一批大文档塞进长窗口，再让模型自己做“精排 + 生成”
- **简单场景不再需要 RAG**：FAQ、单文档问答等简单场景，直接全文塞入更省事

**未来的融合形态：**

```mermaid
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开发实习一面【[百度Agent一面](https://www.nowcoder.com/feed/main/detail/72858aade19d443facc870fea8bb134f)追问：为了保证连续语义，文本具体怎么切分？有什么算法？】

**新手答**：“按 512 token 切就行了，父文档就是原始文档。”

**高手答**：

父子文档索引的核心思路是：**子文档用于精准召回，父文档用于提供完整上下文**。语义切分是让子文档在语义上自洽的关键。

**语义切分流程：**

```mermaid
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["聚合同一来源的子文档 → 父文档"]
```

**具体步骤：**
1. **句子级分割**：用标点/换行将文档拆成句子序列
2. **逐句 Embedding**：每句生成向量
3. **滑动窗口余弦相似度**：计算相邻句子之间的语义相似度，相似度骤降的位置即为语义断点
4. **按断点聚合**：断点之间的句子合并为一个语义段落（即子 chunk）
5. **token 限制兜底**：如果某个语义段超过 max_tokens（如 512），在段内再找次级断点拆分

**父文档的生成方式：**
- 最简单：父文档 = 原始文档（或原始文档的某一节）
- 进阶：多个相邻子 chunk 共享同一父文档，父文档 = 子 chunks 的上下文窗口扩展（向前向后各扩展 N 句）

**聚类后如何区分不同来源：**
- 每个 chunk 携带 metadata：`{doc_id, section_id, chunk_index, parent_id}`
- 检索命中子 chunk 后，通过 `parent_id` 回溯父文档，返回更完整的上下文
- 不同文档通过 `doc_id` 天然隔离，聚类只在单文档内部进行

**差距在哪**：面试官深挖切分细节是为了验证“你的 RAG 到底做了多深”。只说“按 token 切”说明你用了默认方案没调优。能说出语义断点检测 + 相似度骤降 + 父子回溯，说明你真的做过切分实验并理解效果差异。

---

### Q：RAG 项目里，MySQL 和 Elasticsearch 的数据一致性怎么保证？

> 来源：视频面经汇总

**新手答**：“写入 MySQL 后同步写 ES。”

**高手答**：

MySQL 和 ES 的数据一致性是经典的“双写一致性”问题，在 RAG 系统中尤为重要——MySQL 存文档元数据和权限，ES 存全文索引用于召回，两边不一致会导致“能搜到但无权限”或“有权限但搜不到”。

**常见方案对比**：

| 方案 | 一致性 | 延迟 | 复杂度 |
|------|--------|------|--------|
| 同步双写 | 强（但有失败风险） | 高 | 低 |
| 异步消息队列 | 最终一致 | 秒级 | 中 |
| CDC（binlog 监听） | 最终一致 | 秒级 | 高 |
| 定时全量同步 | 弱一致 | 分钟级 | 低 |

**我们的实践（CDC + 补偿）：**

```mermaid
flowchart LR
    A[应用写入 MySQL] --> B[binlog]
    B --> C[Canal/Debezium 监听]
    C --> D[消息队列 Kafka]
    D --> E[ES 同步消费者]
    E --> F[写入 ES 索引]
    D --> G[向量库同步消费者]
    G --> H[更新向量索引]
```

1. **主路径**：应用只写 MySQL，通过 Canal 监听 binlog 变更，推到 Kafka，下游消费者分别更新 ES 和向量库
2. **补偿机制**：定时 Job 对比 MySQL 和 ES 的文档 ID 集合，diff 出不一致的记录重新同步
3. **幂等保证**：ES 写入用文档 ID 作为 `_id`，重复消费不会产生重复数据
4. **顺序保证**：同一文档的变更按 partition key（doc_id）路由到同一 Kafka partition，保证顺序消费

**差距在哪**：新手的“同步双写”在生产中很脆弱——ES 写入失败怎么办？回滚 MySQL 吗？高手用 CDC 方案将写入解耦，配合补偿机制处理极端情况，这才是生产级的一致性方案。

---

### Q：手动干预切片是怎么做的？为什么需要这一步？

> 来源：视频面经汇总

**新手答**：“人工检查切片结果，把切错的手动改一下。”

**高手答**：

**为什么需要手动干预**：自动切片算法（按 token 数 / 按段落 / 按语义）在 80% 的文档上表现不错，但以下场景必须人工介入：

1. **表格跨页**：PDF 中的表格被切成上下两半，上半截没有表头，下半截缺少上下文
2. **图文混排**：图片说明被切到和图片不同的 chunk，检索到说明但没有图
3. **术语定义**：专业术语在第一次出现时有定义，但后续 chunk 中使用时失去了定义上下文
4. **层级结构**：技术文档的标题-子标题层级被打断，子内容脱离了父标题

**实现方式——三种粒度**：

| 方式 | 适用场景 | 实现 |
|------|---------|------|
| 标注切分规则 | 某类文档有固定结构 | 配置“遇到一级标题必须切分”等规则 |
| 手动标记断点 | 特殊文档（合同、法规） | 上传时人工在 UI 上标注分割点 |
| 切片后审核 | 线上 bad case 驱动 | 检索效果差的 query → 追溯到源 chunk → 人工修正 |

**工程落地的关键设计**：
- 切片结果存数据库时保留 `source_type` 字段标记（auto / manual_split / manual_merge）
- 人工修正后重新生成 embedding 并更新向量索引（增量更新，不重建全量）
- 建立“bad case → chunk 溯源 → 修正 → 效果验证”的闭环流程

**差距在哪**：面试官深挖切分细节是为了验证“你的 RAG 到底做了多深”。新手觉得切片是一次性的预处理工作。高手知道切片质量直接决定检索效果——自动切片解决大部分情况，但 bad case 驱动的人工干预是持续优化的关键环节。这体现了 RAG 系统的运营思维，而不是纯开发思维。

---

### Q：Text2SQL 的 RAG 架构里，DDL 层和规则层分别解决什么问题？业务表频繁变更时怎么保持可用？

> 来源：数据智能查询平台面试；[OPPO IT 开发一面](https://www.nowcoder.com/discuss/923561467092160512)

**新手答**：“DDL 层存表结构，规则层存一些 SQL 模板。”

**高手答**：

Text2SQL 的 RAG 架构不是简单的“查文档生成答案”，而是一个**分层知识注入系统**——每层解决不同类型的错误：

```mermaid
flowchart TD
    A[用户自然语言问题] --> B[意图识别 + 查询改写]
    B --> C[DDL 层检索]
    B --> D[规则层检索]
    B --> E[示例层检索]
    C --> F[Schema 上下文]
    D --> G[业务规则约束]
    E --> H[Few-shot 示例]
    F --> I[LLM 生成 SQL]
    G --> I
    H --> I
    I --> J[SQL 校验 + 执行]
```

**DDL 层解决的问题——Schema 对齐：**
- 存储表结构、字段含义、枚举值映射、表关联关系
- 解决“表不存在”“字段名错误”“join 关系写反”等**结构性错误**
- 关键设计：不是存原始 DDL 文本，而是存**语义化描述**（如 `status: 订单状态，1=待付款 2=已付款 3=已取消`）

**规则层解决的问题——业务逻辑对齐：**
- 沉淀内容：计算口径（“GMV = 实付金额，不含退款”）、过滤条件（“有效订单需排除测试账号”）、时间约定（“自然周从周一开始”）、特殊逻辑（“跨境订单金额需转为人民币”）
- 解决“SQL 能跑但结果不对”的**语义性错误**

业务“黑话”和同义词先进入受治理的术语表，例如把“成交额”“流水”“GMV”映射到明确的指标版本，而不是只靠 Query 改写。再往上一层可以建立本体/语义层：统一定义指标、实体、维度、实体关系与 Join，并把用户可见范围绑定到行列级权限。模型负责把自然语言解析成受约束的语义查询或候选计划；确定性编译器负责校验指标与关系、注入权限策略并生成目标数据源 SQL，最终 SQL 还要经过只读、成本和语法门禁。

[Cube 语义层文档](https://docs.cube.dev/docs/introduction)展示了一种实现边界：指标、实体、Join 和访问规则集中在共享数据模型中，查询经语义层校验并确定性应用访问策略后才到数据仓库。这不是说 Text-to-SQL 必须依赖 Cube，而是说明“模型理解问题”和“受治理地编译/执行 SQL”应分层。直接 Text-to-SQL 更灵活，适合 Schema 较小、探索性强的场景；语义层/本体方案前期建模成本更高，但更适合跨数据源、稳定业务口径和统一权限。两者可以组合：模型先选语义对象，语义层编译 SQL；无法覆盖的长尾查询再进入受限的直接生成路径。

**业务表频繁变更时的保鲜机制：**

| 策略 | 实现 | 适用场景 |
|------|------|---------|
| Schema 变更监听 | 监听数据库 DDL 变更事件（如 MySQL 的 schema_change 事件），自动触发知识库更新 | 字段增删改 |
| 定时全量同步 | CronJob 每天对比 information_schema 与知识库的 diff，补充新增表/字段 | 兜底保障 |
| 业务方标注 | 提供 Web UI，业务方修改字段含义/计算口径后自动更新 Embedding | 语义变更 |
| 版本化管理 | DDL 知识按版本存储，查询时带上时间戳匹配对应版本 | 历史数据查询 |

**规则层感知业务变化的方式：**
- 被动：用户反馈“SQL 结果不对” → 追溯到规则缺失/过期 → 补充规则
- 主动：监控上游数据表血缘变更（如字段口径调整的 MR/PR），自动创建规则审核任务
- 定期：每季度组织业务方 review 规则库，清理过期规则

**差距在哪**：新手把 Text2SQL 简化成“查表结构+生成SQL”。高手看到的是一个三层知识注入系统——DDL 解决结构错误、规则解决语义错误、示例解决风格对齐。面试官深挖“变更怎么感知”是在考你有没有运营过这套系统，而不只是搭建过。

---

### Q：笔试题：多路召回结果合并去重 + 加权排序 + TopK

> 来源：数据智能查询平台面试（笔试）【[作业帮秋招一面](https://www.nowcoder.com/feed/main/detail/c86c7591ba9d47b696774ddb48cdc9cb)追问：两路检索得到的召回结果如何做结果融合？】

**题目**：给定多路召回结果，每条包含 docId、score 和来源（source）。要求合并去重，按加权分数排序，返回 TopK，保证同一文档只出现一次。

**高手答**：

```python
from collections import defaultdict
from typing import List, Dict

def merge_recall_results(
    recall_results: List[Dict],  # [{"docId": "d1", "score": 0.8, "source": "vector"}, ...]
    source_weights: Dict[str, float],  # {"vector": 0.6, "bm25": 0.3, "graph": 0.1}
    top_k: int = 10
) -> List[Dict]:
    """多路召回合并去重 + 加权排序"""
    # 1. 按 docId 聚合，累加加权分数
    doc_scores = defaultdict(float)
    doc_sources = defaultdict(list)
    
    for item in recall_results:
        doc_id = item["docId"]
        source = item["source"]
        weight = source_weights.get(source, 0.0)
        doc_scores[doc_id] += item["score"] * weight
        doc_sources[doc_id].append(source)
    
    # 2. 按加权总分降序排序
    sorted_docs = sorted(
        doc_scores.items(),
        key=lambda x: x[1],
        reverse=True
    )
    
    # 3. 取 TopK
    results = []
    for doc_id, final_score in sorted_docs[:top_k]:
        results.append({
            "docId": doc_id,
            "score": round(final_score, 4),
            "sources": doc_sources[doc_id]
        })
    
    return results
```

**关键设计决策：**

1. **去重策略**：同一 docId 被多路召回时，分数是**累加**而非取最大值——被多路命中本身就是强相关信号
2. **权重设计**：向量召回侧重语义相关，BM25 侧重关键词精准匹配，权重根据业务场景调——FAQ 场景 BM25 权重高，开放问答场景向量权重高
3. **归一化**：如果不同来源的 score 量纲不同（如向量余弦 0-1，BM25 可能 0-30+），需要先做 Min-Max 或 Z-Score 归一化再加权
4. **时间复杂度**：O(N log N)，N 为总召回条数。如果 N 很大且只需 TopK，可用堆优化到 O(N log K)

**面试沟通要点**：先写出清晰正确的基础版本，再主动提出“归一化”和“堆优化”的扩展——展示工程思维的层次感。

**差距在哪**：这道笔试题不难，面试官看的是代码风格（清晰度、命名）和对“为什么这样设计”的思考。只写出 sort 是及格，能说出累加 vs 取最大值的权衡、归一化的必要性、以及大规模场景的堆优化，才是加分项。

---


## Q：RAG 文档切分中遇到代码块、表格、标题等特殊内容怎么处理？

> 来源：最有料 AI 实习生面经【[虾皮Agent一面](https://www.nowcoder.com/feed/main/detail/409dc8793a7b450eb51ee32c2b923d49)追问：PDF 电子表格如果很长、超出 chunk 长度，切分时会从表格中间截断吗？截断后如何避免表格行数据残缺带来的问题？】【[阿里边缘bu 秋招一面 （已过）](https://www.nowcoder.com/feed/main/detail/bdebbb6088b6405e9eb2bd2c345acb6e)追问：同一文档中的两个表格存在逻辑关联时，应该如何切分和维护这种关联？】

**新手答**：“按固定长度切就行，遇到什么都一样处理。”

**高手答**：

通用的固定长度切分对纯文本效果还行，但遇到代码块、表格、标题这类结构化内容，会导致**语义断裂和检索质量骤降**。每种特殊内容需要专门的处理策略。

**核心原则：结构化内容是不可切断的原子单元**

```mermaid
flowchart TD
    A["原始文档"] --> B["结构识别\n标记特殊内容边界"]
    B --> C["分类处理"]
    C --> D1["代码块 → 独立 chunk\n保持完整性"]
    C --> D2["表格 → 独立 chunk\n保留表头"]
    C --> D3["标题 → 作为 metadata\n注入子 chunk"]
    C --> D4["纯文本 → 语义切分\n正常处理"]
    D1 --> E["统一入库\n附带结构类型标签"]
    D2 --> E
    D3 --> E
    D4 --> E
```

**各类特殊内容的处理策略**：

| 内容类型 | 问题 | 处理方案 | 关键细节 |
|---------|------|---------|---------|
| **代码块** | 切断后语义完全丧失 | 整块作为独立 chunk | 附带前后文描述（“以下代码实现了XX功能”） |
| **表格** | 切断后失去表头，数据无意义 | 整表独立 chunk；超长表格按行切分但每段保留表头 | 额外生成表格的文字摘要用于语义检索 |
| **标题层级** | 切分后子内容脱离标题上下文 | 标题作为 metadata 注入所有子 chunk | 层级路径：“第三章 > 3.2 节 > 数据库设计” |
| **列表/枚举** | 列表项被拆到不同 chunk | 整个列表作为一个 chunk；超长列表按逻辑分组切 | 保留列表前的引导句 |
| **图片引用** | 图片说明和图片分离 | 图片说明 + 图片描述合成一个 chunk | 用多模态模型生成图片文字描述 |

**代码块的详细处理**：

```text
处理流程：
1. 用正则/AST 识别代码块边界（```...``` 或缩进块）
2. 判断代码块长度：
   - < 500 tokens → 整块作为独立 chunk
   - 500-2000 tokens → 整块 + 压缩前后文描述
   - > 2000 tokens → 按函数/类边界切分（需要语言感知）
3. 每个代码 chunk 附加 metadata：
   - 语言类型（Python/JavaScript/...）
   - 前文描述（代码块前的解释文字）
   - 所属标题路径
4. 额外为代码生成自然语言摘要（"这段代码实现了用户认证的 JWT 验证逻辑"）
   → 用摘要做语义检索，命中后返回原始代码
```

**表格的详细处理**：

```text
处理流程：
1. 识别表格边界（Markdown 表格语法 / HTML <table>）
2. 判断表格大小：
   - 行数 < 20 → 整表独立 chunk
   - 行数 > 20 → 按逻辑分组切分，每段保留表头
3. 为表格生成文字摘要：
   "该表格对比了 5 种向量数据库的性能指标，包括 QPS、延迟、内存占用"
4. 索引时同时存储：原始表格文本 + 文字摘要
   → 摘要用于语义检索，原始表格用于返回给用户
```

**标题层级的处理——上下文路径注入**：

标题不应该单独成为 chunk（信息量太少），而应该**作为 metadata 注入所有子内容**：

```text
原始文档结构：
  # 第一章 系统架构
  ## 1.1 数据库设计
  ### 1.1.1 表结构
  正文内容...

切分后的 chunk metadata：
{
  "content": "正文内容...",
  "title_path": "第一章 系统架构 > 1.1 数据库设计 > 1.1.1 表结构",
  "level": 3,
  "doc_id": "architecture.md"
}

检索时：title_path 参与 Embedding 计算和 BM25 索引
→ 用户搜"数据库表结构"时，即使正文中没有这几个字，
  标题路径也能帮助召回
```

**工程实现建议**：

1. **先识别后切分**：不要先切分再处理特殊内容。正确顺序是先标记所有特殊内容的边界，再对剩余的纯文本做常规语义切分
2. **类型标签**：每个 chunk 存储 `content_type` 字段（text/code/table/list），检索时可以按类型过滤
3. **双索引**：对代码和表格，同时索引原始内容和文字摘要——用摘要的语义 Embedding 做召回，用原始内容做返回
4. **评测覆盖**：评测集中必须包含针对代码/表格/标题层级的 query，验证特殊内容的召回率

**差距在哪**：新手用统一策略处理所有内容——结果是代码被切断无法理解、表格丢失表头、子内容脱离标题上下文。高手对每种结构化内容设计专门的保护策略，核心原则是“结构化内容是不可切断的原子单元”。面试官考的是你对 RAG 文档预处理的工程化深度——切分质量是 RAG 效果的上限。

---

## Q：处理一万个长文档构建 RAG 知识库，工程上怎么做？

> 来源：阿里 Agent 面经（场景题）

**新手答**：“循环处理每个文档，切分后存到向量数据库。”

**高手答**：

一万个长文档（假设每个 5000-50000 字）的规模，单机串行处理可能需要数天。工程上需要从**并行处理、管线设计、质量保障和增量更新**四个维度系统设计。

**整体处理管线**：

```mermaid
flowchart LR
    subgraph ingest["数据摄入层"]
        A["文档源\n（S3/本地/API）"] --> B["格式解析\n（PDF/Word/HTML→文本）"]
    end
    
    subgraph process["并行处理层"]
        B --> C["分片调度器\n（按文档分配 Worker）"]
        C --> D1["Worker 1\n清洗→切分→Embed"]
        C --> D2["Worker 2\n清洗→切分→Embed"]
        C --> D3["Worker N\n清洗→切分→Embed"]
    end
    
    subgraph store["存储层"]
        D1 --> E["向量数据库\n（批量写入）"]
        D2 --> E
        D3 --> E
        D1 --> F["元数据库\n（文档状态）"]
        D2 --> F
        D3 --> F
    end
    
    subgraph qa["质量保障"]
        E --> G["抽样检查\n切分质量/Embedding质量"]
        F --> H["进度监控\n失败重试/告警"]
    end
```

**第一步：格式解析——最容易卡住的环节**

一万个文档格式不会统一（PDF、Word、HTML、Markdown 混合），解析是最耗时且最容易出错的环节：

| 格式 | 解析工具 | 难点 |
|------|---------|------|
| PDF | MinerU / Unstructured / 商业 API | 表格、双栏、扫描件 OCR |
| Word | python-docx / Unstructured | 嵌入图片、复杂格式 |
| HTML | BeautifulSoup + 正则清洗 | 去除导航/广告/脚本 |
| Markdown | 原生解析 | 最友好，几乎无损 |

**关键决策**：对 PDF 等复杂格式，先用自动解析 + 后期抽样检查的方式，不要追求 100% 解析正确——80% 质量上线后再迭代优化 bad case。

**第二步：并行处理——从小时级降到分钟级**

```text
规模估算：
  10000 文档 × 平均 10000 字 = 1 亿字
  切分后约 50 万 chunk
  Embedding 计算：50 万 × 768 维 ≈ 1.5GB 向量数据

单机串行耗时：
  解析：~2s/文档 × 10000 = 5.5 小时
  切分：~0.5s/文档 = 1.4 小时
  Embedding：~0.1s/chunk × 50 万 = 14 小时
  总计：~21 小时

10 Worker 并行：
  总计降到 ~2 小时
```

**并行策略**：

1. **文档级并行**：每个 Worker 处理一批文档（如每批 100 个），互不依赖
2. **Embedding 批处理**：不逐条调 API，而是攒够 32/64 条后批量调用，减少 API 开销
3. **异步管线**：解析、切分、Embedding 三个阶段用生产者-消费者模式串联，不等全部解析完才开始切分

**第三步：容错与状态管理**

处理一万个文档必然有失败——某个 PDF 解析崩溃、Embedding API 超时、网络抖动。需要：

| 容错机制 | 实现 |
|---------|------|
| **Checkpoint** | 每个文档处理完记录状态（pending/processing/done/failed） |
| **重试策略** | 失败的文档指数退避重试 3 次，仍然失败标记为 manual_review |
| **幂等设计** | 重复处理同一文档不产生重复 chunk（用 doc_id + chunk_index 做去重） |
| **进度可视化** | 实时显示总进度、成功/失败/进行中的数量 |
| **断点续跑** | 中断后从 checkpoint 恢复，不重头开始 |

**第四步：入库策略——批量写入 vs 逐条写入**

```text
❌ 逐条写入向量数据库：
  50 万次网络请求，延迟巨大，可能触发限流

✅ 批量写入：
  每攒够 1000 条 chunk，批量 upsert 到向量数据库
  Milvus/Qdrant 都支持批量 insert，吞吐高 10-50 倍
```

**第五步：质量保障——不能只看“处理完了”**

1. **切分质量抽检**：随机抽 100 个 chunk，人工检查是否语义完整、有无切断代码/表格
2. **Embedding 质量验证**：构造 20 组已知相关的 (query, doc) pair，验证 Recall@10 是否达标
3. **去重检查**：对所有 chunk 做近似去重（SimHash），重复率超过 5% 说明切分策略有问题
4. **覆盖度分析**：统计各文档的 chunk 数量分布，异常值（某文档只有 1 个 chunk 或 1000 个 chunk）需要排查

**第六步：增量更新——不是一次性工程**

知识库建好后，文档会持续更新。增量更新策略：

```text
文档新增 → 走完整处理管线 → 新 chunk 入库
文档修改 → 删除旧 chunk → 重新处理修改后的文档 → 新 chunk 入库
文档删除 → 按 doc_id 删除所有关联 chunk

触发方式：
  - 文件系统监听（inotify / fswatch）
  - 定时扫描 diff（每天一次）
  - 手动触发（管理后台）
```

**差距在哪**：新手的“循环处理”忽略了规模化场景下的并行处理、容错设计、质量保障和增量更新。高手设计了完整的工程管线——并行 Worker + 批量写入 + Checkpoint 容错 + 质量抽检 + 增量更新——这才是生产级知识库构建方案。面试官考的是你能不能把一个“看起来简单”的任务（处理文档）做成一个可靠的、可运维的工程系统。

---

### Q：RAG 知识库更新怎么不停服？热更新方案怎么设计？

> 来源：腾讯AI应用开发（Agent后端）【[抖音电商Agent全栈开发工程师一面](https://www.nowcoder.com/discuss/925066865183858688)追问：更新是每天全量跑一遍吗？】

**新手答**：“重新跑一遍 Embedding 流水线，更新完重启服务。”

**高手答**：

生产级 RAG 知识库的更新必须做到对用户无感知——不能停服、不能出现检索空窗期。

**1. 双索引切换（Blue-Green）**：
- 维护两份向量索引：active（当前服务）和 standby（正在更新）
- 增量或全量更新只操作 standby 索引
- 更新+验证通过后，原子切换路由指向 → 零停服
- 切换后保留旧索引一段时间用于快速回滚

**2. 增量更新（适合高频变更）**：
- 新文档实时切块 → Embedding → 写入向量库（append）
- 删除/修改的文档通过 doc_id 标记 soft delete → 检索时过滤
- 定期做 compaction 清理已标记删除的向量

**3. 版本化管理**：
- 每次更新带版本号，检索请求携带版本参数
- 支持灰度：10% 流量走新索引，90% 走旧索引
- A/B 对比新旧索引的检索质量指标（召回率、相关性分数）

**4. 一致性保证**：
- 文档元数据（MySQL/ES）和向量索引的同步用事件驱动（CDC + 消息队列）
- 写入向量库成功但元数据未同步 → 检索到但渲染不出来 → 需要双写确认机制
- 设置“数据新鲜度”指标：从文档更新到可被检索到的延迟 < 5min

**差距在哪**：新手的“重新跑一遍+重启”在生产环境是不可接受的——停服意味着用户体验中断。高手用双索引切换做零停服、增量更新做低延迟、版本化做灰度验证，三层组合保证知识库永远在线且可回滚。

---

## Q：混合检索到底在哪个环节比单独用效果好？

> 来源：淘天/AI Agent一面【[百度Agent一面](https://www.nowcoder.com/feed/main/detail/72858aade19d443facc870fea8bb134f)追问：有没有通过实验分析向量检索及混合检索带来的提升？】

**新手答**：“混合检索综合了向量和关键词的优点，效果自然好。”

**高手答**：

混合检索不是“无条件好”——需要分场景分析：

**向量检索强的环节**：语义模糊/同义改写（“电脑卡顿” → “性能优化方案”）

**BM25强的环节**：精确术语/实体名/代码片段（“RuntimeError: CUDA out of memory”）

**混合检索真正发挥优势的场景**：

1. **长尾查询**：用户用口语化表达但目标文档是专业术语——向量桥接语义，BM25精确命中关键词
2. **多意图查询**：“Python的GIL锁对多线程性能的影响”——向量理解整体意图，BM25命中“GIL”这个精确关键词
3. **短查询消歧**：“苹果”——纯向量可能检索到手机和水果，BM25结合上下文关键词辅助消歧

**混合反而变差的场景**：

- 纯语义问答（“什么是幸福”）→ BM25引入噪声
- 纯关键词检索（错误码查询）→ 向量检索稀释精确结果

**融合策略的关键**：RRF（Reciprocal Rank Fusion）中的K参数决定了两路结果的权重平衡——需要根据实际查询分布调优。

```mermaid
flowchart TB
    Q["用户 Query"] --> D{"查询类型判断"}
    D -->|"语义模糊/同义改写"| V["向量检索主导"]
    D -->|"精确术语/实体名"| B["BM25 主导"]
    D -->|"长尾/多意图/消歧"| H["混合检索\nRRF 融合"]
    V --> R["结果"]
    B --> R
    H --> R
```

**差距在哪**：面试官考的不是“混合检索好不好”，而是“你知不知道它在什么条件下好”——要有反面案例。能说出混合反而变差的场景（纯语义问答、纯关键词检索），说明你对混合检索有深入的工程实践，而非人云亦云。

---

## Q：知识图谱如何从文档构建、增量维护，并处理实体与关系冲突？

> 来源：阿里云暑期 Agent 面经（2026-05-03）【[抖音电商Agent全栈开发工程师一面](https://www.nowcoder.com/discuss/925066865183858688)追问：知识构建与图谱抽取怎么做？；更新时实体抽取和关系关联怎么处理？】

**新手答**：“用大模型从文档抽取实体和三元组，去重后写入 Neo4j；有新文档就继续追加。”

**高手答**：

文档知识图谱不是一次三元组抽取，而是一条**带 Schema、溯源和变更语义的数据管线**：文档解析与分块 → 实体/关系候选抽取 → 类型与关系 Schema 校验 → 实体规范化与消歧 → 证据绑定和置信度计算 → 合并入图 → 质量评测。每个实体、关系或属性都要保留来源文档、版本、原文区间、抽取器版本和时间，不能只留下一个失去证据的 `(subject, predicate, object)`。

实体合并至少结合规范化名称、别名词典、唯一业务标识、上下文和邻居结构。例如两个“苹果”不能因字符串相同直接合并；同一家公司的中英文名也不能因字符串不同就创建两个节点。低置信候选先进入待确认区，高价值实体可用人工标注集持续评估 precision、recall 和错误合并率。

增量维护应以文档版本差异驱动，而不是只追加：

1. 新增文档只抽取受影响分块，并与现有实体做链接。
2. 修改文档先撤销旧版本贡献的证据，再重算变更分块及其邻域。
3. 删除文档删除的是证据边；只有当实体或关系不再有任何有效证据时才 tombstone，不能误删其他文档共同支持的知识。
4. Schema、消歧模型或抽取 Prompt 变更要记录版本，并支持按受影响类型回放，而不是全图无条件重建。

冲突也不能简单“新值覆盖旧值”。先区分实体重复、抽取错误和来源观点冲突：实体重复进入 merge/split 流程；违反 Schema 或原文不支持的关系拒绝入图；两个可信来源给出不同结论时，把“声明”建模为带来源、有效时间、适用范围和置信度的独立记录并保留并存。查询时再按时间、权威度、业务域和用户意图选择或同时展示，关键冲突升级人工裁决，裁决过程可回滚。

**差距在哪**：新手停在“LLM 抽三元组 + 图数据库”。高手覆盖了 Schema、实体消歧、证据溯源、按文档版本撤销贡献和多来源冲突建模，回答的是知识图谱如何长期可信地演化。

---

## Q：MMR 为什么还能提高效果？重排后为什么还要设置 MMR 截断？

> 来源：深势科技一面

**新手答**：“MMR去重，避免重复内容。”

**高手答**：

MMR（Maximal Marginal Relevance）的核心公式：

```text
MMR = argmax[λ × Sim(doc, query) - (1-λ) × max(Sim(doc, selected_docs))]
```

**为什么能提高效果（不只是去重）**：

1. **信息多样性**：相关但不重复的文档比多篇高度相似的文档提供更多有效信息
2. **减少上下文浪费**：如果Top-5里3篇说同一件事，相当于浪费了2个slot——MMR确保每个slot贡献增量信息
3. **缓解位置偏差**：Rerank可能因为表面相似度把多篇近义文档排在前面，MMR打散后模型能看到更全面的信息

**重排后还要MMR的原因**：

Rerank模型（Cross-encoder）只看query-doc相关性，不看doc-doc相似度——它可能给出5篇“都相关但信息高度重叠”的结果。MMR在Rerank之后引入文档间多样性约束，是“相关性+多样性”的二次平衡。

**MMR截断（λ阈值）**：当MMR分数低于阈值时停止取文档——宁可少给也不给低质量/低增量的内容。

```mermaid
flowchart LR
    Q["用户 Query"] --> RR["Rerank 精排\n只看 query-doc 相关性"]
    RR --> MMR["MMR 多样性筛选\n引入 doc-doc 相似度约束"]
    MMR --> CUT["MMR 截断\n分数低于阈值时停止"]
    CUT --> CTX["最终上下文\n每篇文档贡献增量信息"]
```

**实际效果数据**：Top-5中加MMR vs 不加MMR，生成答案的完整性提升约15-20%（因为覆盖了更多角度）。

**差距在哪**：面试官考的是对“检索后处理”环节的理解深度——不只是“去重”，而是信息论层面的最优信息子集选择。能说出Rerank和MMR各自解决什么问题（相关性 vs 多样性），说明你对检索管线有精细化的认知。

---

## Q：基于关键词的命令行代码搜索与基于 Embedding/RAG 的代码搜索，各有什么优缺点？

> 来源：某小厂FOSHO/AI应用开发二面

**新手答**：“关键词搜索快但不智能，RAG搜索智能但慢。”

**高手答**：

| 维度 | 关键词搜索（grep/ripgrep） | Embedding/RAG搜索 |
|------|--------------------------|-------------------|
| 速度 | 极快（ms级，直接扫文本） | 较慢（需要embedding+向量检索） |
| 精确度 | 完全精确匹配（函数名、变量名） | 可能有语义偏移 |
| 语义理解 | 零（只看字符串） | 强（理解“授权”=“authentication”） |
| 索引成本 | 零/极低 | 需要预计算embedding，增量更新成本高 |
| 跨语言能力 | 无 | 有（理解不同语言的同一概念） |
| 代码更新 | 实时（直接搜文件） | 有延迟（需要重新embedding） |
| 最佳场景 | 找函数定义、变量引用、精确错误信息 | 找“做XX功能的代码在哪”、理解性搜索 |

**为什么 Claude Code 选 grep 而非 RAG**：

1. 代码库变化频繁，embedding 索引维护成本高
2. 开发者搜索多数是精确搜索（函数名、类名、错误信息）
3. grep 结果确定性强——不会出现“检索到语义相似但不相关的代码”
4. 可以用 AST 结构化搜索补充 grep 的语义不足

**最佳实践**：混合方案——先 grep 精确匹配，无结果时再 fallback 到语义搜索。

```mermaid
flowchart TB
    Q["代码搜索需求"] --> D{"查询类型？"}
    D -->|"精确符号/函数名/错误码"| G["grep/ripgrep\n（ms级，100%精确）"]
    D -->|"概念级/功能描述"| R["Embedding/RAG\n（语义匹配）"]
    D -->|"不确定"| F["先 grep → 无结果 → fallback RAG"]
    G --> RES["结果"]
    R --> RES
    F --> RES
```

**差距在哪**：面试官考的是对“搜索”本质的理解——不同场景下最优解不同，不是新技术一定比老技术好。能说出 Claude Code 选择 grep 的四个工程理由，说明你对代码搜索有深入的实践经验。

---

## Q：知识库持续更新时，如何保证一次 RAG 回答读取同一逻辑快照？

> 来源：腾讯互娱全栈开发（AI）二面（2026-08-13）

**新手答**：“更新索引时用蓝绿切换，查询总是读当前索引。”

**高手答**：请求开始时取得快照 ID，Query Rewrite、稀疏/向量召回、补充检索、文档读取和引用校验都携带该 ID。新版本先完整写入正文、向量和元数据，校验后原子发布快照指针；旧快照保留到在线请求、评测和审计结束。跨存储要用共同版本清单，不能让正文、ACL 和向量各读“最新”。

**差距在哪**：新手保证单次检索可用，高手保证整条回答链的一致读视图。

---

## Q：如何设计支持版本过滤和时间旅行查询的向量索引？

> 来源：腾讯互娱全栈开发（AI）二面（2026-08-13）

**新手答**：“向量记录加 version 字段，查询时过滤即可。”

**高手答**：记录文档 ID、逻辑版本、生效区间、租户、ACL 和删除标记；更新时关闭旧版本区间并插入新版本，不覆盖历史。过滤选择性高时应预过滤或按快照分区，避免 Top-K 被不可见版本占满。异步构建失败时旧版本继续服务，只有正文、向量、元数据和权限全部就绪才发布新快照。

**差距在哪**：新手只有字段，高手考虑 ANN 过滤性能、发布原子性和历史保留。

---

## Q：RAG 如何防止引用漂移和跨版本证据拼接？

> 来源：腾讯互娱全栈开发（AI）二面（2026-08-13）

**新手答**：“让模型输出引用编号，生成后检查编号存在。”

**高手答**：证据块携带稳定 citation ID、文档/版本、章节区间、内容哈希和 ACL；模型只能选择本次允许的 ID。合并相邻块前验证版本、连续位置和权限一致，生成后逐 claim 检查引用是否真正支持结论。无法映射的断言删除、降级为不确定或触发补检索，不能只验证“编号存在”。

**差距在哪**：新手验证格式，高手验证证据身份、连续性和语义支撑。

---

## Q：RAG 前端如何展示长文档，并让引用稳定跳转到原文证据？

> 来源：商汤 AI Agent 开发面经（2026-03-05）

**新手答**：“答案旁边显示引用编号，点击后用页码跳到 PDF 对应页面并高亮关键词。”

**高手答**：

稳定跳转不能依赖模型生成的页码、标题文字或浏览器搜索。后端在解析文档时就要为证据建立稳定定位协议，例如：

```json
{
  "citation_id": "cit_01J...",
  "document_id": "doc_123",
  "version_id": "v7",
  "section_path": ["3", "3.2"],
  "page": 18,
  "block_id": "blk_8f2a",
  "char_range": [142, 286],
  "content_hash": "sha256:...",
  "quote": "用于校验和降级展示的短证据文本"
}
```

`block_id` 应在同一文档版本内绑定解析后的内容块；页码、字符区间或 PDF bounding box 作为渲染定位信息，`quote` 和内容哈希用于核验。不能只存字符偏移，因为 PDF 解析器升级、OCR 修正或空白归一化都会让偏移漂移。文档更新后生成新 `version_id`，历史答案继续打开旧快照；若旧版本已归档，则明确提示版本差异并展示引用快照，不能悄悄跳到新版本的近似段落。

前端适合采用“答案 + 证据阅读器”的双栏布局。长 HTML 文档使用虚拟列表按块渲染，PDF 按页懒加载；点击引用后先解析 `document/version/block`，再加载目标页或目标块、滚动到锚点并高亮精确范围。URL 保存 citation ID，支持刷新、复制深链和浏览器前进后退。多个引用可在侧栏形成证据列表，用户能在答案 claim 与原文之间来回切换。

还要处理降级路径：精确 range 校验失败时退到 block，再退到 page + quote 搜索，并显示“定位可能偏移”，不能无提示地高亮错误内容。服务端在返回正文前重新检查租户和 ACL，下载地址使用短期授权；前端不可因为持有 citation ID 就绕过文档权限。埋点应区分引用点击、定位成功、版本不匹配和用户反馈，用来持续发现解析漂移。

**差距在哪**：新手把引用当一个页码链接。高手把引用设计成带版本、内容身份和多级定位器的协议，再用虚拟化阅读器、深链、校验和降级机制保证长文档中的证据真正可达、可审计。

---

## 这类题的答题模式

RAG 与检索题的核心是**完整链路 + 工程细节**：

```text
1. RAG 不是"向量搜索"——是离线切片 + 在线召回 + 精排的完整管线
2. 查询改写不是"扩展"——是多维度变换 + 槽位填充 + 主动澄清
3. 召回不是"一路搜索"——是多路召回 + 融合 + 精排
4. 性能优化用并行化——串行意图识别会引入延迟和错误级联
```

下一篇建议继续看：

- [训练、数据与模型优化：从数据清洗到 LoRA](../10-training-and-data/index.html)
