PrepRAG — 面试问题准备
PrepRAG — 面试问题准备
一、项目概述
Q1:介绍下你的 RAG 项目?
基于自建的 AI 知识库(100+ 篇 Markdown 文档,覆盖 AI 基础知识和 AI 面试题两大类目),构建了 RAG 智能问答系统。分两个阶段:基础 RAG 搭建了完整的文档解析 → 向量化 → 检索 → 生成链路;进阶阶段针对知识库的实际特点做了 7 项优化——图片内容增强、分类感知检索、混合检索、查询改写、重排序、Self-RAG 自反思、多轮对话。检索准确率从 65% 提升到 87%。
Q2:为什么做这个项目?
两个动机:
- 实际需求:知识库有 100+ 篇笔记,传统关键词搜索找不到相关内容(比如搜"梯度消失"找不到讲"梯度爆炸与梯度消失"的那篇文章)
- 技术学习:RAG 是 LLM 应用的核心范式,想通过实际项目深入理解全链路,而不只是停留在理论
Q3:为什么不直接用 ChatGPT / 长上下文模型?
| 方案 | 问题 |
|---|---|
| 直接用 ChatGPT | 它没有我的知识库内容,且会编造 |
| 全部塞入上下文 | 100+ 篇文档约 10 万字,token 成本高、推理慢、中间内容容易被忽略(Lost in the Middle) |
| RAG | 只检索最相关的几段,成本低、精度高、可追溯 |
Q4:为什么不微调模型?
- 知识库在持续更新,RAG 更新向量库即可,微调需要重新训练
- 100+ 篇文档量级不大,微调容易过拟合
- 问答任务本质是"查找+整合",不是让模型学新能力
- RAG 有引用来源,可解释性强
二、文档处理
Q5:你的知识库文档是什么格式?怎么处理的?
VuePress Markdown 格式,有三个特殊点需要处理:
- Frontmatter:包含 title、icon、tag 元数据 → 解析后作为 chunk 的结构化元数据
- 图片:每张图片都有中文 alt 文本描述 → 提取 alt 文本注入 chunk;进阶用 GPT-4o Vision 生成更详细的描述
- VuePress 组件:
<ReadMoreLock />等 → 解析时移除,不进入 chunk
Q6:怎么切分文档的?chunk size 怎么选的?
两层切分:
- 第一层:按 H2 标题切分,保留语义边界(每个 H2 section 是一个独立主题)
- 第二层:对超过 512 tokens 的 section 用 RecursiveCharacterTextSplitter 二次切分
chunk size 选 512 的原因:
- 太小(< 256):语义不完整,检索到的片段缺乏上下文
- 太大(> 1024):包含过多不相关信息,稀释关键信息密度
- 512 是经验值,既保留足够语义信息,又保持检索精度
overlap 选 64 的原因:保证切分边界处的信息不丢失,约占 chunk 的 12%。
Q7:知识库里的图片怎么处理的?
分两个阶段:
- 基础阶段:提取图片的 alt 文本(如"监督学习vs非监督学习对比图"),作为图片的语义描述附加到所在 chunk 中,使图片内容可被检索
- 进阶阶段:对图片使用 GPT-4o Vision 生成结构化描述,包含图片类型(流程图/对比图/架构图)、核心内容、关键元素。这样即使 alt 文本简略,图片中的关键信息也能被检索到
为什么这样做?知识库中大量概念通过示意图讲解(如 Transformer 架构图、训练流程图),如果忽略图片,会丢失重要的语义信息。
Q8:不同类型的图片描述策略有什么区别?
| 图片类型 | 描述重点 | 示例 |
|---|---|---|
| 流程图 | 强调步骤顺序和流转关系 | "数据输入 → 预处理 → 模型训练 → 评估 → 部署" |
| 对比图 | 强调差异点和对比维度 | "对比了监督/无监督/强化学习在数据需求、训练方式、应用场景上的差异" |
| 架构图 | 强调组件关系和数据流 | "由 Encoder、Attention、FFN 组成,数据从输入嵌入层流入" |
| 公式图 | 提取公式含义 | "交叉熵损失函数,用于分类任务的损失计算" |
三、向量化与存储
Q9:用的什么 Embedding 模型?为什么?
text-embedding-3-small,原因:
- 支持中英文(知识库是中文内容)
- 1536 维,性价比高
- API 调用方便,不需要本地部署 GPU
- 对比过
bge-large-zh(中文效果好但需本地部署)和m3e-base(开源但效果稍差)
Q10:为什么用 ChromaDB?
- 轻量级,Python 原生,开发体验好
- 支持持久化和元数据过滤
- 本项目数据量小(几千条),不需要 Milvus/Pinecone 这种重量级方案
- 支持
where过滤,可以实现分类感知检索
Q11:ChromaDB 的元数据过滤是怎么用的?
ChromaDB 支持在查询时加 where 条件。我在每条 chunk 的 metadata 中存了 category 字段(如 dl-basics、rag-interview),查询时根据分类结果过滤:
# 用户问"深度学习中的梯度消失"
# 分类结果:fundamentals/dl-basics
# 检索时加过滤:where={"category": {"$eq": "dl-basics"}}这样可以避免跨类目干扰,比如不会把机器学习基础中的梯度相关内容混进来。
四、检索优化
Q12:什么是分类感知检索?为什么需要?
知识库有明确的两级分类体系(18 个子类目),但向量检索完全忽略分类信息。
分类感知检索的做法:
- 用 LLM 判断用户问题属于哪个类目
- 在向量检索时加元数据过滤,优先检索目标类目下的文档
- 对命中目标类目的结果给予额外权重
好处:避免跨类目干扰。比如问"Transformer 的注意力机制",不会被 CNN 中的注意力相关内容干扰。
Q13:混合检索是怎么做的?
向量检索 + BM25 双路检索,用 RRF(Reciprocal Rank Fusion)融合:
- 向量检索:擅长语义匹配("模型训练技巧"能匹配到"训练优化方法")
- BM25:擅长精确匹配("LoRA"只匹配包含"LoRA"的文档)
- RRF 只依赖排名不依赖分数,不需要归一化:
RRF_score(d) = Σ 1/(k + rank_i(d)),k=60
Q14:查询改写怎么做的?举个例子?
用 LLM 改写原始查询,生成 2-3 个不同角度的搜索查询。
多轮对话场景:
- 用户第一轮:"什么是 RAG?"
- 用户第二轮:"它是怎么优化检索效果的?"
- 改写后:["RAG 怎么优化检索效果", "RAG 检索优化方法", "RAG 系统的检索改进策略"]
对改写后的多个查询分别检索,合并去重,提高召回率。
Q15:为什么需要重排序?Bi-Encoder 和 Cross-Encoder 有什么区别?
| Bi-Encoder(向量检索) | Cross-Encoder(重排序) | |
|---|---|---|
| 编码方式 | query 和 doc 分别编码 | query 和 doc 拼接后联合编码 |
| 速度 | 快(doc 可预计算) | 慢(每次重新计算) |
| 精度 | 较低 | 较高 |
| 用途 | 粗检索(召回) | 精排 |
实际流程:Bi-Encoder 从几千条 chunk 中快速召回 Top-20,Cross-Encoder 对这 20 条精排到 Top-5。Cross-Encoder 能捕捉 query 和 doc 之间更细粒度的交互特征。
五、生成与评估
Q16:Self-RAG 是什么?怎么实现的?
在生成回答后增加自我评估环节,用 LLM 评估三个维度:
- 相关性:检索到的文档是否与问题相关?
- 支撑度:回答是否基于检索内容,而非编造?
- 有用性:回答是否真正解答了问题?
如果综合置信度 < 0.67,触发二次检索(扩大范围从 Top-20 到 Top-30,精排到 Top-8),重新生成回答。
本质是用 LLM 评估 LLM 的输出,减少幻觉。
Q17:怎么减少 RAG 的幻觉?
从四个环节入手:
- 检索阶段:混合检索 + 重排序,确保检索到的文档确实相关
- 生成阶段:Prompt 中明确要求"仅基于参考内容回答,无法回答请说明"
- 评估阶段:Self-RAG 检查回答是否被检索内容支撑
- 引用追溯:回答附带来源(文件名 + 章节),方便用户验证
Q18:Prompt 是怎么设计的?
你是一个基于 AI 知识库的问答助手。请严格根据以下参考内容回答问题。
如果参考内容中没有相关信息,请明确告知用户。
回答时请引用来源(文档标题和章节)。
参考内容:
{context}
用户问题:{query}关键设计:
- "仅基于参考内容" → 减少编造
- "引用来源" → 可追溯
- "无法回答请说明" → 允许说不知道
六、工程实现
Q19:多轮对话怎么实现的?
- 维护最近 10 轮对话历史(避免 token 过长)
- 对话历史用于两个地方:查询改写中的指代消解 + LLM 生成时的上下文
- 不把对话历史用于检索(会引入噪声),只用于改写后的查询
Q20:流式输出怎么实现的?
基于 SSE(Server-Sent Events):
- 后端:FastAPI 的 StreamingResponse,LLM 的 stream 模式逐 token 返回
- 前端:EventSource API 接收
- 首字延迟 ~450ms,用户体验好
Q21:系统的性能瓶颈在哪?
| 环节 | 耗时占比 | 优化方向 |
|---|---|---|
| LLM 生成 | ~60% | 流式输出改善体验,换更快的模型 |
| Cross-Encoder 重排序 | ~25% | GPU 加速,或换更轻量的模型 |
| 查询改写 | ~10% | 用更小的模型做改写 |
| 向量检索 | ~5% | 已经很快,不是瓶颈 |
Q22:知识库更新怎么办?
- 增量更新:新文档经过解析 → 切分 → 向量化后插入 ChromaDB
- 全量重建:如果切分策略变化,重跑 ingest 脚本
- 图片描述:增量更新时只对新图片调用 Vision API
七、深入追问
Q23:你的分类路由准确率怎么样?如果分类错了会怎样?
分类准确率约 90%。分类错误不会导致完全搜不到,因为:
- 混合检索中的 BM25 不受分类过滤影响
- 即使类目过滤偏了,向量检索的语义匹配仍然有效
- Self-RAG 评估会发现检索内容不相关,触发二次检索(此时不加过滤)
所以分类是一个"优化"而非"依赖",错了有兜底机制。
Q24:为什么不把图片描述单独存一个 collection,而是注入到 chunk 中?
两种方案对比:
- 单独 collection:图片和文字分离,需要两次检索再合并,增加复杂度
- 注入 chunk:图片描述和所在段落的文字一起检索,语义更完整
选择注入 chunk 的原因:图片通常是对所在段落的补充说明,两者语义上是一体的。比如一段讲"注意力机制"的文字旁边有一张注意力权重热力图,把图的描述和文字放在一起检索效果更好。
Q25:如果知识库有 10 万篇文档,你的方案还能用吗?需要怎么改?
需要调整:
- 向量数据库:ChromaDB 换成 Milvus 或 Pinecone,支持亿级向量
- BM25:用 Elasticsearch 替代内存中的 BM25
- 重排序:10 万篇用 Cross-Encoder 太慢,可以用 Learning-to-Rank 或只对 Top-50 精排
- 分类路由:可以用更轻量的分类模型替代 LLM
- 缓存:加 Redis 缓存热门查询的结果
Q26:如果面试官问 "为什么不直接用 LangChain 的 RAG 模板?"
LangChain 的模板是通用的,我的项目有几个针对性优化:
- VuePress 格式解析:LangChain 的 MarkdownLoader 不处理 frontmatter、VuePress 组件
- 图片增强:LangChain 没有内置图片内容提取
- 分类感知检索:LangChain 的 retriever 不支持基于知识库分类体系的元数据过滤
- Self-RAG:LangChain 的默认链路没有自评估环节
用 LangChain 做基础设施(如 text_splitter),但核心逻辑自己实现。
Q27:RAG 的评估怎么做?怎么量化效果?
| 指标 | 计算方式 | 我的结果 |
|---|---|---|
| Hit@5 | Top-5 中是否包含正确文档 | 87% |
| 分类准确率 | 查询分类是否正确 | 90% |
| 回答相关性 | 人工评分 1-5 | 4.2/5 |
| 可追溯性 | 回答是否被检索内容支撑 | 92% |
| 图片检索召回 | 含图内容是否被正确检索 | 78% |
评估方法:
- 手动构建测试集(每个类目 5-10 个问题,标注期望的文档和关键词)
- 自动化脚本跑 Hit@5 和分类准确率
- 人工评估回答相关性和可追溯性
Q28:如果让你继续优化,下一步做什么?
- 知识图谱:利用文档间的引用关系(如"详见训练与微调章节"),构建概念关系图
- Agentic RAG:让 Agent 动态决定检索策略(是否需要检索、检索什么类目、是否需要追问)
- 多模态检索:用 CLIP 对图片直接做向量检索,而不只是依赖文本描述
- 缓存层:对相似查询做语义缓存,减少重复计算
八、对比与选型
Q29:RAG vs Fine-tuning vs 长上下文,怎么选?
| 维度 | RAG | Fine-tuning | 长上下文 |
|---|---|---|---|
| 知识更新 | 更新向量库 | 重新训练 | 重新塞入 |
| 成本 | 低(API 调用) | 高(GPU 训练) | 高(token 费用) |
| 可解释性 | 有引用来源 | 黑盒 | 有引用 |
| 数据量要求 | 无最低要求 | 需要较多数据 | 受窗口限制 |
| 适用场景 | 知识问答 | 风格/能力调整 | 小型文档集 |
- 本项目用 RAG:知识库持续更新、需要引用来源、数据量适中
- 如果知识库很小(< 10 篇),长上下文可能更简单
- 如果需要改变模型的回答风格(如更口语化),可以 RAG + Fine-tuning 结合
Q30:GraphRAG 和你的方案有什么区别?
- 我的方案:基于向量相似度的"扁平"检索
- GraphRAG:先构建知识图谱(实体 + 关关系),再基于图结构检索
GraphRAG 适合:
- 多跳推理("A 的作者还写了什么?")
- 全局性问题("这个领域的研究趋势?")
我的知识库特点:
- 文档间主要是同级关系(同一类目下的不同主题)
- 更多是单跳问答("什么是 XX?""XX 和 YY 的区别?")
- 所以当前向量检索方案够用,GraphRAG 是后续探索方向
