Rag常见面试题
为什么有RAG?
RAG 主要解决三个问题。
第一是知识时效性,LLM 训练完知识就固定了,训练截止日期之后发生的事它一无所知;
第二是私有知识覆盖,公司内部文档、行业专有数据根本没有机会进训练集,LLM 对这些内容是空白的;
第三是幻觉问题,没有知识依据时 LLM 容易「自己发挥」编出一个听起来合理但实际错误的答案,给了它参考资料之后幻觉就少很多。
这三个问题的根源都是同一件事,知识被固化在了模型参数里。RAG 的解法是把知识存到外部,用的时候实时检索注入,彻底绕开了参数里的知识限制。
什么是 RAG?RAG 的主要流程是什么?
RAG(Retrieval Augmented Generation,检索增强生成)是一种结合信息检索和生成式模型的技术方案,其主要流程包括两个核心环节:
- 检索(Retrieval): 基于用户的输入,从外部知识库中检索与查询相关的文本片段,通常使用向量化表示和向量数据库进行语义匹配
- 生成(Generation): 将用户查询与检索到的内容作为上下文输入给生成模型(如 GPT等),由模型输出最终回答。
即我们在本地检索到相关的内容,把它增强到提示词里,然后再去做结果生成简单来说就是利用外部知识动态补充模型生成能力,既能保证回答的准确性,又能在知识库更新时及时反映最新信息(还有一点就是部分业务是内部文档,网上没有,因此可以本地提供知识库来增强 AI 的知识)
整体流程如下:

- 文本向量化(Embedding):使用语义模型 (如 OpenAl的 text-embedding-ada-002,或者 Sentence-BERT等)将文档和问题转为高维向量表示
- 向量数据库检索:使用如 Faiss、Milvus等向量数据库存储所有文档向量。用户提问后,对问题进行向量化,并在数据库中执行最近邻搜索,找出语义最相近的N 条内容
- 构建 Prompt:将用户原始问题和检索到的内容拼接成上下文输入,传入生成模型
- 生成回答:由大语言模型(如 GPT、LLaMA 等)综合已有上下文生成最终输出。
详细描述一个完整 RAG 系统的详细工作流程
一个完整的 RAG 系统分离线和在线两个阶段
离线阶段:文档加载 → 切割(Chunking)→ 向量化(Embedding)→ 入库,这一步只做一次;
在线阶段:Query 改写 → 向量检索(粗排)→ Rerank(精排)→ 拼接 prompt → LLM 生成。
两个阶段分工明确,离线负责建库,在线负责检索和生成。
RAG 最核心的价值体现在两点。一是知识可以随时热更新,往知识库里加新文档就行,不需要重新训练模型,成本极低。二是答案有溯源,每条回答都能追溯到来自哪个 chunk,可解释性比纯 LLM 生成强很多,遇到答错了也知道是哪条知识出了问题。
这也是为什么企业落地 AI 问答系统,RAG 是首选方案
相比直接微调 LLM,RAG 解决了什么问题?微调和 RAG 各自的优劣势是什么?
我的理解是这两个东西解决的不是同一层面的问题,不是谁替代谁的关系。
- 微调是把新知识直接烧进模型参数里,适合改变模型的行为风格或者培养深度的专业能力;
- RAG 是在推理的时候实时检索注入知识,适合知识需要频繁更新、或者需要有溯源的场景。
如果非要让我选一个,知识库类的问答系统我会首选 RAG,成本低而且可以随时更新。如果是要让模型学会特定的输出格式或者行业语气,那微调更合适。
实际上这两个方案也可以组合用,先微调再套 RAG。
RAG 的文档处理流程是怎样的?
RAG 文档处理流程的目的是把原始文档转化为 AI 可检索、可理解的格式,从而增强 Al 的回答质量,
这个流程主要包含以下四个核心步骤:
文档收集和切割
- 收集(Extract):从各种数据源收集原始文档资料。比如本地文件系统中的 PDF、Markdown 文档,或者网页、数据库等
- 预处理与切割(Transform):对收集到的原始文档进行清洗,去除无关信息(比如 HTML标签、广告内容)并统一格式,然后将篇幅较长的文档分割成更小、更易于处理的文本块。
向量转换和存储:
- 向量转换:使用专门的 Embedding 模型把上一步得到的每个文本块转换为高维的数字向量
- 向量存储(Load):把生成的向量及对应的原始文本块内容和元数据一起存入向量数据库中。
前两个步骤经历了完整的 ETL流程,也就是对文档进行抽取、转换和加载。
- 文档过滤和检索:
- 查询处理:当用户提出一个问题(查询)时,这个查询文本也会被同一个Embedding 模型转换为向量形式。
- 相似度搜索:系统使用用户查询的向量,在向量数据库中搜索与之最相似的文档向量。一般会检索出 Top-K个最相似的结果。
- 过滤机制:在检索过程中,可以根据文档的元数据进行过滤,例如只检索特定来源或特定时间范围内的文档,提高检索结果的精确性,。
- 查询增强和关联:
- 上下文组装:把上一步检索到的相关文档块提取出来,跟用户的原始查询组合在一起,形成一个增强的提示。
- 内容生成:这个增强的提示会被发送给大模型,大模型利用这些检索到的、作为额外上下文的知识来生成回答,因为有了相关的、可能更新的外部知识,生成的回答通常比仅依赖其内部预训练知识更为准确和具体。
- 源引用与后处理:生成的回答可以附带信息来源的引用,即说明哪些信息来自哪些检索到的文档。还可以进行一些后外理,如格式化输出、内容摘要等,优化最终呈现给用户的结果。
这个流程帮助 AI 在回答问题时,能够参考到最新的、特定的外部知识,有效缓解大模型的知识陈旧和“幻觉”问题
什么是 RAG 中的分块?为什么需要分块?
文档不能直接存进向量库,必须先切成小块也就是 chunk,每个 chunk 分别向量化之后存成一条记录。
分块就是把原始长文本(比如一本书、一篇论文)拆成若干个“小块”(通常几百字到上千字,比如500-1000字),每个小块包含相对完整的语义单元,比如一个段落、几个段落或一个小节。
为什么需要分块?
- 模型处理能力限制:大语言模型(如GPT)一次能处理的文本长度有限,太长的文本塞进去会“消化不良”,分块后每个小块能塞进模型的“肚子”里。
- 精准定位信息:用户提问通常针对局部内容(比如“第三章第二部分的案例是什么”),分块后每个小块像“信息卡片”,检索时能快速找到最相关的卡片,避免在整本大书里“大海捞针”
- 平衡上下文与效率:小块既能保留足够上下文(比如前后句子的逻辑),又能让计算机高效存储和检索(小块的向量计算更快)。
所以分块就是将输入文档或大段文本切分成多个较小的、可控粒度的“块”,以便后续的向量化检索和生成模块高效调用与组合。
切割粒度没有固定答案,通常 500 到 1000 个 token 是一个合理的起点,但更重要的是根据文档类型来选策略,普通文本用固定大小加重叠,有标题结构的文档按语义边界切,代码按函数切,如果既要检索精度又要上下文完整的话,我会用父子切割,也就是小块检索、大块返回。
在 RAG 中,常见的分块策略有哪些?分别有什么区别?
常见的分块策略包括自然结构分块、固定大小分块、滑动窗口分块、递归分块、语义分块及混合分块这六个。
固定大小分块:将文本按固定字符数、词数或 token 数等均匀切分,简单易实现。
重叠:在固定大小基础上为相邻 chunk 保留一定重叠,以减少上下文出现断裂
标题层级切割:先按段落/章节粗分,超长部分再递归细化(按句子或空行),适合复杂文档
语义分块:用 NLP 模型(如 BERT、GPT)判断文本语义边界,确保每个块是完整的语义单元(比如一个论点、一个案例)
父子切割(Parent-Child Chunking):存储时,同一段内容存两份。一份是细粒度的小 chunk(比如 200 token),专门用于向量检索,因为小 chunk 语义聚焦,围绕一个小话题,检索精度高。另一份是包含这个小 chunk 前后上下文的大 chunk(比如 1000 token),通过 ID 与对应的小 chunk 关联。检索时用小 chunk 找到精准的命中点,然后根据关联 ID 取出对应的大 chunk,把完整的上下文交给 LLM 阅读,生成质量更好。
Late Chunking:把整篇文档丢给 Embedding 模型编码,让模型看到完整的上下文,然后再从全文的 token 级向量中按边界划分出各个 chunk 的向量。这样每个 chunk 的向量实际上是在全文语境下生成的,保留了跨块的上下文信息。不过这个方法需要 Embedding 模型支持足够长的输入,对模型有一定要求。
把几种策略的适用场景梳理成表,实际选型时可以对照来看:
| 策略 | 适用文档类型 | 优点 | 缺点 |
|---|---|---|---|
| 固定大小 + 重叠 | 纯文本、无明显结构 | 实现简单、chunk 大小可控 | 可能在语义中间截断 |
| 语义边界切割 | 段落分明的文章 | 语义完整,召回质量好 | 实现稍复杂,chunk 大小不均 |
| 标题层级切割 | Markdown、HTML 文档 | 天然语义独立,带结构 metadata | 依赖文档有清晰的标题结构 |
| 代码按函数切割 | 代码文件 | 保留代码逻辑完整性 | 需要 AST 解析,限定语言 |
| 父子切割 | 各类文档(追求高质量) | 检索精准 + 上下文完整两全 | 存储量翻倍,索引构建复杂 |
| Late Chunking | 各类文档 | chunk 向量保留全文上下文 | 需要模型支持长输入 |
混合分块:同时结合固定、滑动、语义等策略,或依据标题、段落层级自适应切分,实现性能与精度平衡,比如在初始阶段使用固定长度快读分块,在后续阶段画通过语义分块进行更精细的分块。
怎么规避语义被切割掉的问题?
我的思路是从两个方向来规避这个问题。
第一个方向是切的时候就不要在语义中间截断,用重叠切割和语义边界切割来保证每个 chunk 内容是完整的,也就是按句子、段落这些自然的边界来切。
第二个方向是切完之后用检索策略把上下文补回来,核心方案是句子窗口检索,命中一个句子就把周围几句一起返回给 LLM;另外还有父子切割,小块检索命中、大块内容输出。
还有一个我觉得比较有价值的方案,是 Anthropic 提出的 Contextual Retrieval,在做 Embedding 之前先让大模型看着整篇文档为每个 chunk 生成一段背景说明,把这段背景和 chunk 拼在一起再向量化,从根本上解决孤立 chunk 没头没尾的问题。
在 RAG 中的 Embedding 嵌入是什么?
简单说,就是向量化。就是把文本内容、图像、音频、视频等形式的信息映射为高维空间中的密集向量(一串数字),这个过程叫“嵌入”(Embedding)。
向量就是空间中的坐标,捕捉对象之间的语义关系和隐含的意义、每个向量就像文本的“数字指纹”,包含了文本的语义信息,比加“猫”和“狗”的向量会很接近,“开心”和“悲伤”的向量会远离,即语义相近的对象在向量空间中彼此邻近,而语义相异的对象则相距较远。
然后通过在向量空间中进行数学计算(如余弦相似度),判断两段话是否相关(比如用户问题和文档块的匹配)
因此,分块后的文本块需要先生成Embeding,存入向量数据库(如 FAISS),用户提问时,系统通过计算提问的 Embeding 与文本块的 Embeding 相似度,找到最相关的内容,再交给大模型生成回答。
在 RAG 中,你知道有哪些 Embedding Model 嵌入模型?
在 RAG 中,常用的 Embedding Model(嵌入模型)主要分为以下几类:
第一类是 OpenAI 的 text-embedding 系列,
text-embedding-3-small是性价比最高的,1536 维,支持降维到 256 维来节省存储,调用方便,英文效果非常好;缺点是 API 调用有费用,而且数据要发到 OpenAI 服务器,有些企业有数据出境合规问题。第二类是 BGE 系列(北京智源研究院出品),
bge-large-zh-v1.5是经典的中文开源模型,1024 维,可以本地部署,数据不出境。不过要注意的是,BGE 虽然仍然是很好的选择,但已经不是中文场景的唯一首选了。bge-m3是 BGE 的多语言版本,同时支持中英日等多种语言,1024 维,而且支持三种检索模式(稠密向量、稀疏向量、ColBERT 式多向量),适合中英文混排的场景。第三类是新一代高性能模型,这两年涌现了一批在 MTEB 排行榜上超过 BGE 的模型。
Qwen3-Embedding(阿里通义出品)在多语言基准上表现突出,中文效果很强;Voyage-3-large在英文检索上精度超过 OpenAI 的模型;Cohere embed-v4支持 128K 超长上下文,适合长文档场景;Gemini Embedding(Google 出品)在多个评测中表现均衡。如果你在 2025-2026 年做新项目,建议关注这些新模型,在自己的数据上做测评后再选型。
如何选择 Embedding 模型
第一是中英文比例:知识库以中文为主,可以选
bge-large-zh-v1.5或者更新的Qwen3-Embedding;中英混合,选bge-m3;纯英文或追求省事,选text-embedding-3-small。第二是数据合规要求:数据不能出境,就必须用可以本地部署的开源模型,BGE 系列和 Qwen3-Embedding 都是很好的选择。
第三是向量维度对存储和检索速度的影响:维度越高精度越好,但存储空间和检索时间都会增加。百万量级的知识库,1024 维是个合理的平衡点;如果规模很小,1536 维也无所谓。有些新模型(如 text-embedding-3-small)支持 Matryoshka 降维,可以灵活调整维度来平衡精度和成本。
在 RAG 中,如何评估 Embedding Model 嵌入模型?
这里有一个常见的误区:很多人拿 MTEB 这类通用排行榜的分数来选模型,觉得分数高就一定好。MTEB 是一个权威的文本 Embedding 通用排行榜,用多种标准数据集评测模型的语义搜索能力,是好的参考。但它用的是通用数据集,你的业务场景(比如医疗问诊、法律文档、客服知识库)和通用数据分布差异很大,排行榜第一的模型不一定适合你
正确的评估方法是在自己的业务数据上测:准备几百条业务相关的「问题 + 正确答案 chunk」对,分别用候选模型做检索,看正确的 chunk 有没有出现在前 K 条结果里。这个指标叫 Hit@K,Hit@5 = 0.8 的意思就是,80% 的问题,它对应的答案都出现在了检索结果的前 5 条里。通常 Hit@5 低于 0.7 就要考虑换模型或者改进 Chunking 策略了。这种贴近真实场景的评估,比排行榜分数更有参考价值。
Embedding 有哪几种算法你了解过吗?
Embedding 算法大致经历了三代演进。
第一代是静态词向量,以 Word2Vec 和 GloVe 为代表,把每个词映射成固定向量,但同一个词不管上下文是什么,向量永远不变,处理不了多义词。
第二代是以 BERT 为代表的上下文相关向量,同一个词在不同语境下有不同的向量,表达能力大幅提升,但 BERT 本身输出的是 token 级别的向量,两个句子要比较相似度就必须拼在一起跑,百万条文档就要跑百万次,检索速度完全不可接受。
第三代是以 SBERT、SimCSE、BGE 为代表的句子级对比学习 Embedding,专门为「两段文本有多相似」这个任务优化,能提前把所有文档向量算好存起来,查询时只需算一次,是 RAG 场景的标配。
| 世代 | 代表模型 | 核心特点 | 主要局限 | RAG 适用性 |
|---|---|---|---|---|
| 第一代 | Word2Vec、GloVe、FastText | 词级静态向量 | 无法处理多义词,词级非句子级 | 不适用 |
| 第二代 | ELMo、BERT | 上下文动态向量 | 检索时需两两拼接,速度极慢 | 不适用于实时检索 |
| 第三代 | SBERT、SimCSE、BGE、E5 | 句子级 bi-encoder,对比学习 | 精度低于 cross-encoder | 标配,性能和精度平衡最优 |
RAG 场景下基本只考虑第三代模型,中文场景 BGE 和 Qwen3-Embedding 都是很好的开源选择,英文场景 E5 或 text-embedding-3-small 都不错。新趋势方面,指令感知 Embedding、Matryoshka 降维、多模态 Embedding 是值得关注的方向。
在 RAG 中,索引流程中的文档解析你们怎么做的?
在 RAG 的索引流程中,文档解析主要分 5大核心步骤,分别是“读、洗、拆、标、存”
- 读(文档加载):支持多格式解析 (PDF/Word/Markdow/HTML/图片等),用工具库(如 PyPDF2 读PDF、Docx2Text 读 Word、Unstructured 处理复杂格式)
- 洗(文本清洗):去除噪声(如页眉页脚、乱码、重复内容),标准化文本(统一大小写、替换特殊符号),处理多语言混合文本(如中英夹杂时保留语义完整性)。
- 拆(文本分块):按 语义单元拆分成合适长度的“知识块”(常见分块策略)。
- 标(元数据标注):给每个知识块附加 来源信息(文档标题/URL/作者/上传时间)、结构信息(章节标题/段落位置)、领域标签(如“产品手册”“用户协议”),方便后续检索时过滤和排序
- 存(结构化输出):将分块后的文本和元数据整合成 索引系统能识别的格式(如JSON/CSV),输出给向量数据库(如 FAISS/Easticsearch)或传统搜索引擎建立索引。
什么是向量数据库?在基于大模型的应用开发中,向量数据库主要解决什么问题?
向量数据库是一种专门设计用来存储和管理向量嵌入(vector embedding)的数据库系统,它可以将非结构化数据(如文本、图片、音频等)转换成高维向量的形式进行存储,并提供高效的相似性搜索功能。向量数据库支持的核心操作叫做 近似最近邻搜索(ANN,Approximate Nearest Neighbor)
普通关系型数据库的索引结构对高维向量基本上是失效的,所以需要专门的数据库来处理这个场景。
在基于大模型的应用开发中,向量数据库主要解决以下核心问题:
- 高效的相似性搜索:通过将用户查询转换为向量,可以快速找到语义相似的内容,这对于实现智能问答、推荐系统等功能至关重要。
- 海量数据处理:能够高效处理大模型生成的海量数据,传统数据库难以处理百万甚至数十亿的数据点,而向量数据库专门针对这种场景进行了优化。
- 实时交互支持:在需要实时用户交互的应用中(如聊天机器人),向量数据库可以确保快速检索相关上下文信息,提供实时响应,
向量数据库中的 ANN 是什么?为什么需要用它?
ANN 是"近似最近邻 (Approximate Nearest Neighbor)”的缩写,它不是某一种具体算法,而是一类通过牺牲一定精确性来换取搜索速度的算法框架或者技术
其核心是在海量高维向量中,快速找到与目标向量“近似相似”的结果,而非耗费大量时间寻找绝对精确的最近邻。
因为当数据量达到百万甚至十亿级时,暴力遍历所有向量计算距离(精确搜索)会慢到无法接受,而ANN通过智能索引和近似策略,实现毫秒级响应,满足实时搜索需求。
向量数据库原理是什么? 请简述下它的原理
向量数据库的核心原理是通过将高维数据(如图像、文本)转换为多维向量,并基于相似性度量(如余弦相以度、欧氏距离),利用高效的索引结内和近邻最近邻 (ANN) 算法,快速检索与目标最相以的向量结果。
- 向量化:将非结构化数据转化为数值向量,保留语义或特征信息
- 索引构建:通过HNSW图、IVF等结构预处理向量,加速搜索
- 近似搜索:允许一定误差,用ANN算法在速度与准确性之间平衡,返回Top-K相似结果
向量数据库中的 HNSW、IVF分别是什么意思?
它们是向量数据库中的核心索引与压缩技术,用于加速高维向量的相似性搜索
向量数据库之所以能做到毫秒级检索,秘密全在索引算法上。目前主流的索引算法有两种:
HNSW (Hierarchical Navigable Small World)图,这是目前精度最高的 ANN 算法之一:
- 在高维空间中,将所有向量组织成分层“小世界”图。
- 查询时,从上层稀疏图贪心跳转到最相似邻居,逐层下探到密集的底层图,快速找到近似最邻近点
- Qdrant、Milvus、Chroma 默认都用 HNSW。
- 优点是召回率高(通常能达到 95%+ 的精度)、查询速度快
- 缺点是建索引时内存消耗大,不适合内存极度受限的场景
IVDInverted File Index):
- 对向量做聚类,把相似的向量分进同一个「桶」里,查询时只搜最相关的几个桶
- IVF 的优点是内存占用小、适合超大规模;缺点是精度比 HNSW 略低,需要调参(聚类数量 nlist、搜索桶数量 nprobe)
- Milvus 在超大规模场景下会用 IVF 系列索引。
- 优点是内存占用小、适合超大规模;
- 缺点是精度比 HNSW 略低,需要调参(聚类数量 nlist、搜索桶数量 nprobe)
向量数据库在工程层面还需要具备哪些能力
光有 ANN 搜索还不够,生产级系统还要支持几个关键特性:
一个是 Metadata 过滤,也叫混合检索。实际业务里,知识库往往有多个部门、多个产品线的文档,用户查的时候只想搜「技术部的文档」或者「2024 年更新的内容」。向量数据库支持给每个向量挂上 metadata 字段,检索时加过滤条件,只在符合条件的子集里做 ANN 搜索。你可能会想,为什么不先 ANN 搜完再过滤?因为那样可能搜出来的 Top-K 结果大部分都不满足条件,白白浪费了检索名额。先过滤再 ANN 搜索,能保证召回的每一条都是真正想要的。
第二个是实时更新。RAG 的知识库经常需要新增、修改、删除文档,向量数据库需要支持增量更新。很多人以为向量索引建好就不能动了,其实主流的向量数据库都支持在线写入,新数据进来后增量构建索引,不需要停服重建。
第三个是与关键词检索融合。纯向量检索对一些精确词语(比如产品型号「GPT-4o」、专有名词)的效果不好,有些向量数据库同时支持向量检索 + BM25 关键词检索,做混合召回效果更好。
向量数据库的工作流程有哪些?请简述下
向量数据库的工作流程可拆解为五步,核心是将非结构化数据转化为可计算、可检索的向量形式
- 数据处理:清洗数据(去噪、归一化)、标注元数据(如标签、时间)
- 向量化:用AI模型(如BERT、ResNet)提取特征,生成高维向量
- 向量存储:将向量与原始数据关联,存入分布式存储(如分块存储)
- 索引构建:用HNSW、LSH等技术组织向量,建立高效检索结构
- 相似性检索:输入目标向量,通过索引快速返回Top-K近似结果

你都了解哪些向量数据库?如何选型?
常见的向量数据库有 Milvus、Pinecone、Weaviate、Qdrant、Chroma、Faiss、Annoy 等
选向量数据库主要看三个维度:数据规模、部署方式、是否需要混合检索。
中小到大规模、团队小、快速上线,首选 Qdrant,性能好、API 设计简洁、文档完善,Docker 一条命令就能部署,Rust 写的性能很稳。Qdrant 现在也支持分布式模式(分片 + 副本),实际已经有亿级规模的生产案例,覆盖面比较广。很多团队一开始用 Chroma 做原型,后来上生产就切到 Qdrant,这个路径很常见。
说到 Chroma,它确实是上手最快的选项,Python 直接 pip install,本地内存运行,零配置,配合 LangChain/LlamaIndex 原生集成非常方便。Chroma 现在也支持了 Client-Server 模式和 Chroma Cloud 托管服务,不再只是本地嵌入式了,而且在 2025 年还加入了 BM25/SPLADE 稀疏向量的混合检索支持。不过要注意的是,Chroma 的分布式能力还在成熟中,对于超大规模(千万级以上)的生产场景,它的稳定性和性能还不如 Milvus 和 Qdrant 这些专门为大规模设计的方案。总结来说,Chroma 适合快速原型验证和中小规模的生产使用,超大规模就要考虑其他选项了。
数据规模在千万到亿级,需要分布式,选 Milvus,国内大厂用得最多,支持多种索引类型,有完整的集群方案,但部署运维复杂度也高,团队需要有足够的人力来维护。
不想运维,数据在云上,用 Pinecone,全托管 SaaS,按用量付费,适合快速验证商业化产品。不过要注意数据出境的问题,如果业务数据有合规要求,Pinecone 就不合适了。
已经在用 PostgreSQL,数据量不是特别大,可以直接用 pgvector 插件,不用引入新组件,查询可以和业务数据做 SQL JOIN,运维成本为零。
把几个常用的选项对比一下,方便选型时做决策:
| 数据库 | 部署方式 | 适合规模 | 混合检索 | 主要优势 | 主要劣势 |
|---|---|---|---|---|---|
| Chroma | 本地/Client-Server/云 | 中小规模 | 是(支持 BM25/SPLADE) | 零配置上手极快,生态集成好 | 超大规模稳定性待验证 |
| Qdrant | 自托管/云(支持分布式) | 中大规模(亿级) | 是 | 性能好,API 简洁,Rust 高性能 | 超大规模需调优 |
| Milvus | 自托管(分布式) | 大规模(亿级) | 是 | 可水平扩展 | 部署运维复杂 |
| Pinecone | 全托管云服务 | 中大规模 | 是 | 无需运维 | 费用高,数据出境 |
| pgvector | PostgreSQL 插件 | 中小规模 | 是(配合全文检索) | 无需新组件,可 JOIN 业务数据 | 性能弱于专用向量库 |
讲讲你用的向量数据库?数据量级是多大?性能如何?遇到过性能瓶颈吗?
我们生产环境用的是 Milvus,数据量级在百万条向量左右,每条是 1024 维,用 HNSW 索引,单次查询的延迟在 20 到 50 毫秒。
选 Milvus 主要是因为它支持分布式部署和读写分离,适合数据量大、并发高的场景,方便后续扩展。我遇到过两个比较典型的瓶颈。
- 第一个是内存压力,百万级的 1024 维向量光原始数据就要好几个 GB,后来我们开启了标量量化 SQ8,把 float32 压成 int8,内存直接降到原来的四分之一。
我们知识库大概有 150 万条 chunk,每条用 BGE-large-zh 模型生成 1024 维的向量,索引用 HNSW(M=16,ef_construction=128)。
先算一下原始数据的内存占用:150 万 × 1024 维 × 4 字节(float32)≈ 6GB,这还不算索引结构本身的开销。实际上 Milvus 进程完整跑起来大概要 10~12GB 内存。你可能会想,12GB 内存而已,现在随便一台服务器都有 32GB,有什么好担心的?但别忘了这只是向量数据本身,同一台机器上还跑着应用服务、Redis、日志收集等各种组件,内存是要抢着用的。
最开始没有开量化,机器只有 8GB 内存,Milvus 把向量索引加载进内存之后,留给操作系统的空间已经很小了,稍微有点内存压力就开始频繁 swap(把内存数据换到磁盘),查询延迟直接从 20ms 飙到 2s+。很多人以为查询慢是索引算法的问题,其实根本不是,是内存不够被操作系统强行 swap 了。就像你开一个很大的 Excel 文件,内存不够就开始疯狂读硬盘,卡得怀疑人生。
开启 标量量化(SQ8) 之后,把每个 float32 压缩成 int8(用 1 字节代替 4 字节)。直觉上理解:就像把精确到小数点后 7 位的数字保留到小数点后 2 位,大部分语义信息其实在高位,截掉低位精度损失极小,但数据量直接缩到 1/4。内存从 10GB 降到约 3GB,精度损失不到 1%,是最划算的一个优化,几乎没有代价。彻底解决了 swap 问题。还有一个辅助手段:Milvus 支持把原始向量存在磁盘上(mmap),只把索引放内存,进一步节省内存。原始向量只在需要精排时才读,对查询延迟影响不大。
实测查询性能:单次 top-5 查询 P50 延迟约 20ms,P99 约 60ms,并发 100 QPS 时延迟基本稳定。这些数字才是面试官想听到的,不是「感觉挺快的」。
- 第二个是大批量写入的时候会触发后台 Segment 合并,影响查询延迟,我们的解法是把批量写入改到业务低峰期,分批小批次写入。
每天知识库有增量更新,一次性写入几十万条新数据时,Milvus 后台会触发 Segment 合并操作,把多个小的增量 Segment 合并成大的封存 Segment 并建好索引。 Segment 合并过程很耗 CPU 和磁盘 IO,期间查询的 P99 延迟会有明显抖动,从正常的 60ms 涨到 300ms+。
解法有两个思路。一是时间上错峰,把批量写入改到业务低峰期(比如凌晨),避开查询高峰,让 Segment 合并在用户不活跃时静默完成。二是量上化整为零,把每批写入量控制在 500~1000 条以内,分成多批写,每批之间间隔几秒。这样 Segment 合并的冲击变成多次小冲击,而不是一次大冲击,每次合并规模小、耗时短,查询服务基本感知不到抖动。
Milvus 的核心概念?
Collection(集合) 类似关系数据库里的表,存储一类向量数据。我们的知识库就是一个 Collection,每条记录包含:文本 chunk 的 ID、向量(embedding)、原文内容、来源文档等 metadata。
Segment(段) 是 Milvus 内部管理数据的基本单位,理解它对后面分析性能瓶颈很关键。新写入的数据先进增量段,像一个临时的接收缓冲区;积累到一定量后触发合并,变成封存段。封存段会建好索引,查询时走索引检索速度很快。但合并这个动作本身会消耗 CPU 和磁盘,就像磁盘碎片整理,把一堆小文件合并成大文件,期间会抢资源。记住这个 Segment 合并机制,后面讲性能瓶颈的时候会用到。
Index(索引) 是向量检索的关键加速结构。最常用的是 HNSW(Hierarchical Navigable Small World,分层可导航小世界图),一种图结构索引。它的核心思想是:把向量组织成多层图,查询时从稀疏的顶层开始,快速定位到大致区域,再逐层细化找到最近邻,整体复杂度接近 O(log N)。
HNSW 有两个关键参数,用社交网络来类比很好理解:M 是每个节点最多认识几个邻居,M 越大,图越密,找到最近邻的精度越高,但建索引的内存和时间都越多,通常设 16~32 就够了。ef_construction 是建图时每个节点考察多少候选,越大越精确,但建索引越慢,通常设 100~200。
查询时还有一个参数 ef(也叫 search_ef):查询时搜索的候选集大小,越大召回越准,延迟也越高,按实际需求在 50~200 之间调。可以理解为查询时多看几个候选再决定最终答案,ef 小查得快但可能漏掉真正最近的那个,ef 大查得慢但结果更准。
向量数据库中,常见的向量搜索方法:余弦相似度、欧几里得距离和曼哈顿距离分别是什么?有什么区别?
- 余弦相似度:衡量两个向量的“方向相似性”,不关心向量长度,取值范围[-1,1],值越大方向越接近(比如“猫”和“狗”的文本向量)
- 欧几里得距离(欧氏距离):计算两个向量在空间中的“直线距离”,取值20,距离越小向量越相似(比如两张图片的像素特征向量)。
- 曼哈顿距离:计算两个向量在各维度上的差值绝对值之和,类似“在城市街区中沿道路行走的距离”,取值≥0,适用于网格状数据(如地图坐标)
简单来看:余弦比“方向”,欧氏比“绝对距离”,曼哈顿比“线性累加距离”。因此:
- 文本、推荐系统常用余弦相似度(不关心文本长度,只看语义方向)
- 图像、视频检索常用欧氏距离(直接比较像素特征的空间差异)
- 网格数据(如城市坐标、表格数据)常用曼哈顿距离(符合实际移动逻辑,稀数据的处理)

在什么场景下,你会选择使用图数据库来增强传统的向量检索?
当业务问题涉及多个实体之间的关联推理的时候,就需要考虑引入图数据库来增强。向量检索有一个根本的局限,它只能做单跳检索,找和问题直接相关的文档,没办法沿着实体之间的关系链做推理。
比如你问公司 A 的投资方和公司 B 有什么交集,单纯向量检索就很难处理了,因为答案不在某一段文档里,而是藏在多个节点之间的关系上。这时候图数据库就能发挥作用,沿着关系边一跳一跳地把关联信息收集回来。我接触过的典型场景有企业关系分析、医疗知识图谱、代码依赖关系查询、供应链溯源这些
向量检索 + 图数据库的组合用法
既然两者是互补的,那具体怎么搭配使用呢?两者组合起来的工作流是这样的:向量检索先作为「入口」,用户问「小米的竞争对手 CEO 是谁」,先用向量检索找到和「小米」相关的文档片段,从中识别出关键实体,定位到「小米」这个节点。接下来,图数据库接力做「关系遍历」,拿到入口实体之后,在图里沿着关系边一路走:「小米」-> 竞争关系 ->「OPPO、vivo、荣耀」-> CEO 关系 -> 对应人名,把沿途经过的节点信息都收集回来。最终,把向量检索结果和图遍历结果合并,一起塞给 LLM 生成回答。
打个比方,向量检索像是「导航定位」,帮你找到出发点在哪;图遍历像是「沿着路线一站一站走」,帮你把沿途经过的所有站点信息都收集起来。前者解决「在哪」的问题,后者解决「能到哪」的问题,合在一起才能给出完整答案。
这样,LLM 拿到的上下文既有语义相关的文档片段,也有沿关系链追出来的关联信息,两者互补,回答就完整了。
你使用 RAG 给大模型一个输入,系统是怎样的工作流程?
Query 改写 → 向量检索(粗排)→ Rerank(精排)→ 拼接 prompt → LLM 生成。
- Query 改写
用户的提问往往是口语化的,甚至带着指代,比如「上次那个方案怎么样」,这种问题离开对话上下文完全没法检索。另外,就算问题表达清楚,用词和知识库里文档的用词可能完全不一样,直接拿去检索命中率会很低。
这一步通常让一个小模型对原始问题做改写。常见技术有几种:一是简单改写,把口语化问题改写成更正式、独立完整的检索句;二是 HyDE(Hypothetical Document Embeddings),让 LLM 先「假设」一个可能的答案,用这个假设答案的向量去检索。你可能会觉得奇怪,不用问题去搜反而用假设答案去搜?原因很简单:问题和答案的用词往往差异很大,但假设答案和真实答案的用词更接近,它们的语义空间天然更匹配,命中率往往更高。三是多角度扩写,把同一个问题扩展成 3-5 种不同表述,分别检索后合并结果,覆盖面更广
- 向量检索(粗排)+ 多路召回
Query 改写好了,接下来要把它转换成向量才能去向量库里搜索。把处理后的问题用 Embedding 模型转成向量,这一步本身很简单,但有一个容易忽略的细节。
很多人以为随便选一个 Embedding 模型就行了,其实不然——必须用和离线建库时完全相同的 Embedding 模型。为什么?因为不同模型的向量空间不兼容,用 A 模型建的库,如果用 B 模型来检索,两边的向量就像在不同坐标系里,距离计算完全没有意义,检索结果会一塌糊涂。
拿着问题向量,去向量数据库里做近似最近邻搜索(ANN),找出相似度最高的 Top-K 个文档片段。这一步速度非常快,即使百万量级的向量库,通常几十毫秒就能返回结果。
但工程实践中,只用向量检索这一路往往不够。所以这一步通常同时进行多路召回:向量检索负责捕获语义相似性,BM25/全文检索负责捕获关键词精确匹配,两路各有所长。很多人以为向量检索已经够用了,为什么还要 BM25?因为向量检索对精确词语(比如产品型号、专有名词、错误拼写)的识别能力比较弱,而 BM25 对这些精确匹配反而更在行。
多路结果通过 RRF(互倒排名融合)算法合并,最终召回的结果比单路覆盖面更广、质量更高
- Rerank(精排)
Top-20 的结果里不可能条条都相关,肯定混了一些干扰项进去。Rerank 就是为了解决这个问题的:用 Cross-Encoder 深度理解语义,把粗排的噪音过滤掉
向量检索是「粗排」,召回的 Top-K(比如 20 条)里可能混入相关度不高的干扰片段。Rerank 模型(Cross-Encoder 结构)会把用户问题和每个候选片段拼在一起输入,深度理解它们之间的语义匹配程度,重新打分排序。最终只保留 Top-3 到 Top-5 的高质量片段,把噪音过滤掉。
你可能会问,为什么不直接用 Rerank 模型来检索,还要先粗排再精排?因为 Rerank 是 Cross-Encoder 结构,需要把查询和每个候选拼在一起过模型,计算量比向量检索大得多。如果拿它对百万条数据逐一算分,延迟完全不可接受。所以工程上采用「粗排筛到几十条,精排再从几十条里挑最好的几条」这种两阶段策略,兼顾速度和质量。Rerank 整体耗时通常在几百毫秒以内,对用户体感影响不大,但检索质量的提升非常明显
- 拼接 prompt
精排后的高质量片段拿到了,接下来要把它们和用户的原始问题组装成 Prompt 交给大模型。典型模板大概是这样:
prompt = f"""
你是一个专业助手,请根据以下参考资料回答用户的问题。
如果参考资料中没有相关信息,请回答「根据现有资料无法回答」,不要自行猜测。
参考资料:
[1] {chunk_1}
[2] {chunk_2}
[3] {chunk_3}
用户问题:{user_query}
"""你可能会觉得这个 Prompt 挺简单的,没什么好讲的。但这里面每一条指令都有明确的工程意图。很多人以为只要把检索结果丢给大模型就行了,其实 Prompt 拼得不好,大模型一样会乱回答。
只根据资料回答是为了抑制大模型凭记忆发挥的倾向——大模型天生喜欢帮忙,你问什么它都想着要回答,哪怕资料里没有相关信息它也会自己编一个出来。
资料没有就说不知道是防止它在信息不足时强行补全出幻觉内容。参考资料带编号是为了方便后续做引用溯源,让用户知道每句话的依据是什么。
- LLM 生成
大模型拿到 Prompt 之后,基于参考资料生成答案。到这一步,整个 RAG 在线流程的核心工作就完成了。
什么是查询改写?为什么在 RAG 应用中需要查询改写?
用 Query Rewrite 主要是为了弥补用户提问方式和知识库文档表述之间的语义鸿沟。用户的问题往往口语化、模糊、带缩写,而文档写的是正式书面语,向量相似度天然偏低,导致该召回的内容没被召回。
RAG(检索增强生成)的核心是“先检索、后生成”,如果原始查询不够准确或覆盖范围不足,会导致检索到的文档不相关或信息不全,最终生成的回答质量会受影响。查询改写能解决两个关键问题:
- 词汇匹配问题:用户用词和知识库中的术语可能不一致(如“新冠”vs“COVID-19”),扩展后能匹配更多相关内容。
- 语义补全问题:用户查询可能简短模糊(如“怎么理财”),扩展后能明确需求(如“新手理财入门 低风险投资 基金股票区别”),让检索更精准。
方法主要有四种:
- 直接改写,让 LLM 把口语化的问题转成更精准的表述;
表述鸿沟最简单的形态,就是口语化。把口语化、模糊的问题改写成更精准、更书面化的检索表述,这就是最基础的改写方式。改写的本质是消除口语化表达和书面知识库表述之间的向量距离
发给 LLM 的 Prompt 模板如下:
请将用户的问题改写成更适合知识库检索的表述。
要求:去掉口语化表达、补全缩写和指代、使用更正式的书面语、保留核心意图。
对话历史:{最近几轮对话}
用户问题:
只输出改写后的问题,不要解释:- Query 扩展,补充相关关键词
用 LLM 把用户原始问题改写成 3~5 个不同角度的版本,分别去检索,然后把所有结果合并去重。核心思路是:只要有一个改写版本和文档的表述对上了,就能把正确的内容召回来,就像拦截网越宽,捕到鱼的概率越高。适合用户提问风格多变、和文档表述差异大的场景,代价是多几次 LLM 调用。
- HyDE,让 LLM 先生成一个假设答案,然后用答案的向量去检索;
HyDE 的直觉可以这样理解:正常你用问题的影子去找答案的样子,HyDE 先描绘出答案可能长什么样子,再去找长得像它的文档。先让 LLM 根据用户问题生成一段假设性的答案,再用这段假设答案的向量去检索文档。假设答案不需要准确,只需要风格像文档就够了,它的作用是充当一个向量上更好的检索代理。
让 LLM 生成一个可能不准确的答案,再用这个答案去检索,这不是多此一举吗?其实不是。在向量空间里,答案到答案的距离,比问题到答案的距离要近得多。假设答案的作用不是「回答问题」,而是「扮演文档的角色」,用它当检索代理比用原始问题更高效。
生成假设答案用的 Prompt 模板:
请根据以下问题,生成一段可能的答案。
注意:不需要完全准确,只需要用于辅助检索,风格尽量像知识库文档。
问题:{用户问题}
假设答案:- Step-back Prompting,把具体问题往上抽象一层,检索更通用的背景知识。
对于涉及具体细节的问题,有时候知识库里没有直接对应的答案,但有背景原理。Step-back Prompting 把具体问题后退一步,提升到更抽象的层次,先检索背景知识,再结合背景知识回答具体问题。
类比一下就很直观:就像你做一道数学题,不是一上来就硬套公式,而是先想想这道题用的是哪种定理,再用定理解题。知识库里可能没有ef 参数设 100 是否合适的直接答案,但一定有HNSW 参数调优原则这样的背景文档,把具体问题后退成背景问题,才能找到真正有用的参考资料。
生成「后退版本」问题的 Prompt 模板:
请将以下具体问题转化为一个更通用的背景问题,用于检索相关背景知识。
具体问题:{原始问题}
背景问题:示例效果:具体问题Qdrant 的 HNSW ef 参数设多少合适-> 后退问题HNSW 索引的参数调优原则是什么。后退后的问题能命中更多通用背景文档,让 LLM 生成有原理支撑的答案。
把这四种方法放在一起对比一下,选型时就有了清晰的依据:
| 方法 | 解决的核心问题 | 额外开销 | 适合场景 |
|---|---|---|---|
| 直接改写 | 口语化、指代不清、上下文丢失 | 1 次 LLM 调用 | 多轮对话场景 |
| HyDE | 问题和文档文体风格差异大 | 1 次 LLM 调用 | 专业知识库、文体差异明显 |
| Step-back | 具体问题需要背景知识辅助 | 1 次 LLM 调用 | 技术文档、需要原理支撑的场景 |
| 多 Query 扩展 | 单一角度覆盖不全 | N 次 LLM 调用 | 答案涉及多个维度的复杂问题 |
什么是混合检索?在基于大模型的应用开发中,混合检索主要解决什么问题?
混合检索 是指在基于大模型的 RAG(检索增强生成)应用中,结合向量检索和关键词检索等检索技术的互补优势,提升检索结果的全面性和准确性。
它主要为了提升大模型的上下文理解和回答准确性,因为向量检索擅长语义理解(如“手机截图怎么截”与“iPhone 截屏教程”的关联),但准以精准匹配专有名词(“iphone 15”)或缩写(如“RAG”);关键词检索则反之。

所以在大模型 RAG 应用中,混合检索主要通过并行两种检索方式实现:
- 向量检索:将文本转化为高维向量,计算语义相似度
- 关键词检索:基于倒排索引、BM25等算法,精确匹配关键词(例如“GPT-5”必须完全命中)。
两种结果通过权重融合或重排序模型(如RRF)合并,最终输出最优答案。
在工程上例如 ElasticSearch 就同时支持关键词检索和向量检索,可以使用工具链例如 Llamaindex+ ElasticSearch 实现混合检索
核心区别对比:
| 维度 | 关键词检索(BM25) | 向量检索 |
|---|---|---|
| 匹配方式 | 词汇重叠统计 | 语义空间距离 |
| 索引结构 | 倒排索引(稀疏) | 向量库(稠密) |
| 同义词处理 | 无法处理 | 天然支持 |
| 精确词命中 | 极好 | 容易漏 |
| 计算方式 | 基于统计,可解释 | 黑盒,向量距离 |
| 适合场景 | 专有名词、代码、精确查询 | 语义问答、模糊表达 |
为什么需要 Rerank?如果没有 Rerank 会怎么样?
初步检索方法通常为了速度牺牲了一些精度,可能包含许多噪音文档。
而 Rerank 能够利用更高级的模型深入捕捉查询与文档之间的细微语义关系,从而筛除无关文档,确保生成模块获得高质量上下文信息。
如果没有 Rerank,生成模块可能基于噪音或不相关的文档生成回答,这会影响回答的准确性,甚至还可能引发误解。
为什么不直接用 Rerank 模型来检索,还要先粗排再精排?因为 Rerank 是 Cross-Encoder 结构,需要把查询和每个候选拼在一起过模型,计算量比向量检索大得多。如果拿它对百万条数据逐一算分,延迟完全不可接受。所以工程上采用「粗排筛到几十条,精排再从几十条里挑最好的几条」这种两阶段策略,兼顾速度和质量。Rerank 整体耗时通常在几百毫秒以内,对用户体感影响不大,但检索质量的提升非常明显
什么是自查询?为什么在 RAG 中需要自查询?
自查询(Self-Query)是指当用户输入 模糊表述或隐含需求时,RAG 系统通过内部处理让模型自动解析用户査询中的隐含条件(如时间、作者、标签等元数据),生成结构化查询语句的过程。
比如用户说“2025年SevenCoding的用户报告”,自查询会解析出两个条件:
- 语义匹配:用户报告
- 元数据过滤:作者=SevenCoding、时间=2025
为什么在RAG中需要自查询?因为用户提问往往包含模规表述或隐含需求,传统向量检索可能忽略元数据导致结果偏差。自查询通过解析+过滤两步走,让检索同时满足语义相关性和元数据条件,解决“检索不准”的核心问题。
如何优化 RAG 的检索效果?
我理解 RAG 的检索优化可以从四个层次来看:索引层决定知识怎么存,查询层决定问题怎么转换,召回层决定从哪些路径去找,重排序层决定最终哪些内容进入 prompt。每一层都有对应的优化手段,我的经验是单独优化一个层次往往效果有限,线上系统我会组合来用,先靠索引优化和多路召回来保证覆盖率,再用 Rerank 保证精度,如果用户提问质量比较差,再额外加上查询优化。
这个问题必然要从 RAG 的流程入手,看我们能干预的步骤有哪些,总结来说 RAG 检索效果的优化主要可以分为以下几个方面:
- 文档预处理与切片优化
- 高质量文档:原始文档内容不行一切都白搭,所以要保证知识库中的原始文档内容准确、结构清晰、格式规范,尽量减少噪音(水印、不相关图片)。
- 智能切片:采用合适的文档切片策略。切片过小可能导致语义不完整,过大则可能引入过多无关信息。可以考虑基于语义边界或智能分块算法,避免固定长度切分导致语义断裂,SpingAl 的 TokeTextspliter 就提供了基于 Token 的文本分割,也考虑了语义边界。
- 元数据标注:为文档切片添加合话的元数据(来源,日期、类别,标签),后续进行更精确的过滤和检索都能使用到。
- 索引优化:索引优化是 Chunking 策略的延伸,但它聚焦于一个更核心的矛盾:检索用的粒度和 LLM 读的粒度天然是矛盾的。
- Parent-Child Chunking 是最直接的方案。把文档切成两个版本:一份是细粒度的子 chunk(比如 150 token 一个),一份是粗粒度的父 chunk(比如 500 token 一个),每个子 chunk 通过 parent_id 关联到对应的父 chunk。入库时只给子 chunk 建向量索引;检索时用子 chunk 的向量来匹配,精度高;命中之后,根据 parent_id 取出对应的父 chunk,把父 chunk 塞给 LLM 阅读,上下文完整。这样就做到了「检索用小的,阅读用大的」,两全其美。
- 摘要索引(Summary Index)的思路稍有不同,它不是切割文档,而是让 LLM 为每一段内容生成一段摘要,用摘要来建向量索引。为什么这样做?因为文档原文有时候表述很散,而摘要是对核心意思的提炼,语义更聚焦,在向量空间里和用户的问题会更接近,命中率更高。检索时用摘要的向量匹配,命中后把原始段落塞给 LLM 阅读。
- 多粒度分层索引则更激进,同时建章节级、段落级、句子级三层索引。不同类型的问题适合不同粒度:「什么是 RAG」这种宽泛的概念性问题,用章节级就够了;「退款申请需要几个工作日」这种细节性问题,用句子级更精准。系统根据问题类型自动选择合适的粒度去检索,能覆盖更多类型的用户需求。
- 查询增强
- 查询重写:使用大模型把用户的原始查询改写的更清晰、更详细、更规范,提高后续检索的准确性。
- 查询扩展:将用户的单个查询扩展为多个语义相近的查询,然后合并检索结果,提高召回率。例如,将“Seven是谁”扩展为“程序员Seven介绍”、“Seven的技能”等。
- 查询翻译:如果知识库和用户查询的语言不一致,可以把查询翻译成知识库的语言。
- HyDE(Hypothetical Document Embeddings,假设文档嵌入)
- Step-back Prompting(后退提问)
- 检索器配置与策略
- 相似度调值:合理设置检索时返回文档的相似度阈值和数量,阈值过高可能漏掉相关文档,过低则可能引入噪音;返回文档过多会增加后续处理成本和模型幻觉风险,这些参数都需要根据具体场景进行调试。
- 元数据过滤:用预先标注的元数据对检索范围进行精确过滤,只在特定子集文档中搜索,提升检索的相关性和效率。
- 混合检索策略:结合不同检索方法的优势,像关键词检索、向量检索、知识图谱检索等。比如先用向量检索召回语义相关的文档,再用关键词过滤精确匹配,。
- 嵌入模型的选择与优化:选择高质量的嵌入模型对文本进行向量化,这会直接影响语义相似度计算的准确性。
- 召回优化:关键词检索(BM25) + 向量检索 + 查询扩展
- 重排:在召回多个文档后,可以用更复杂的排序模型对初步检索到的结果进行重新排序,把更相关、质量更高的文档排在前面。
文档预处理、检索器配置与策略这些优化属于前置步骤,其它几个优化解决的问题不同,实际落地时不需要全部都上,按业务场景和问题症状来选:
| 层次 | 解决的核心问题 | 推荐程度 |
|---|---|---|
| 索引优化(Parent-Child) | 检索粒度 vs 上下文完整性的矛盾 | 推荐,效果稳定 |
| 查询优化(Multi-Query / HyDE) | 用户提问和知识库表达不对齐 | 视场景,提问质量差时必做 |
| 多路召回(向量 + BM25) | 单路检索漏召 | 推荐,低成本高收益 |
| Rerank 精排 | 粗召精度不足 | 强烈推荐,提升精度最直接的手段 |
一个典型的生产级搭配:Parent-Child 索引 + 向量 BM25 多路召回 + Rerank 精排。这三层组合基本能覆盖大多数场景的检索质量问题。如果用户提问质量比较差(口语化、指代不清),再额外加上 Query 改写。
从另一个角度来记这四层:索引层保证「存进去的知识可以被找到」,查询层保证「搜索的姿势是对的」,召回层保证「不漏掉该找到的内容」,Rerank 层保证「送进 LLM 的是真正有用的内容」。每一层各司其职,组合起来才能把检索质量做到高水准
什么是提示压缩?为什么 RAG 中需要提示压缩?
在RAG(检索增强生成)中,提示压缩主要指对检索出的文档内容进行精简处理通过提取核心信息、过滤无关文本、压缩冗长内容、使最终输入大模型的"提示"(包含用户问题+文档片段)既完整保留关键信息,又符合模型输入长度限制。
例如:检索到一篇10页的技术白皮书,压缩后仅保留与用户问题相关的2个核心章节和关键数据,避免无关段落占用模型上下文空间。
为什么在RAG中需要提示压缩?RAG的生成效果高度依赖“输入给模型的文档质量”,而检索出的文档通常存在三大问题,必须通过压缩解决:
- 控制输入长度: 大语言模型都存在输入长度限制,直接拼接大量检索内容可能会超出模型的上下文窗口,通过压缩可以将关键信息浓缩进有限的 token 内。
- 提高知识相关性:检索出的文档中可能有大量无关内容(重复描述、背量知识、与问题无关的案例)会稀释关键信息,导致模型聚焦困难(例如用户问"如何优化代码"。文档中80%是算法原理,仅20%的优化技巧,未压缩时模型可能错误引用原理部分生成回答),甚至引发“幻觉”。
- 降低计算资源消耗::减少不必要的内容可以降低模型处理和推理的计算负担,如果调用的是商业大模型,还涉及到金额问题(都是通过 token 来计费的)。
在 RAG 应用的过程中,关于提示工程的设计有什么心得和技巧吗?
在 RAG 应用中,提示工程的核心设计技巧主要有以下几点:
- 明确角色和任务:提示中要清楚说明 AI 的身份、能力边界和目标任务
- 结构化提示:用明确的格式指导 AI 输出,比如:“背景 + 问题 + 输出格式”。
- 加上下文约束:明确告知 AI 只能基于检索到的资料回答,避免“幻觉”
- 模板化设计:将提示设计成模板,方便大规模复用和动态填充检索内容
- 加入冗余兜底机制:如没检索到相关内容,就让 AI 显式回复 “未找到相关资料”
- 给几个示例:给 1-2 个优质问答范例,让模型模仿输出风格和逻辑。
了解哪些更复杂的 RAG 范式?
RAG 的发展经历了三代,每一代都是在上一代的痛点上演进,但演进的方式越来越系统化。
最早的形态叫 Naive RAG,逻辑非常直白:用户提问 -> 向量检索 -> 拼 prompt -> LLM 生成。这套流程像搭积木的第一步,把各个环节拼在一起就能跑,两天就能上线一个 Demo。但它什么都没优化:检索召回什么就送什么,用户提问写得差就找得差,找到的内容有没有用也不管,全部一股脑塞给 LLM,幻觉和偏差就是这样来的。
意识到问题之后,Advanced RAG 出现了,核心思路是在检索前和检索后各加一道工序。检索前加 Query 改写和扩展,把用户口语化的提问打磨成更容易命中知识库的形式;检索后加 Rerank 精排和内容压缩,把召回内容里最相关的几条筛出来,不相关的过滤掉,减少喂给 LLM 的噪音。这套方案不用改框架,只需在原有流程上插入几个步骤,工程改动小,效果提升明显。目前大多数生产系统用的都是这个形态。
很多人以为 Advanced RAG 就是 RAG 的终极形态了,其实不是。Advanced RAG 有一个隐含的假设——流程是固定的「检索前优化 -> 检索 -> 检索后优化 -> 生成」,不管用户问什么,都走这套流程。但真实场景中,不同问题需要不同的处理策略,这就是 Modular RAG 要解决的。
再往后发展就是 Modular RAG,思路变了,不再是在固定流程上打补丁,而是把 RAG 的各个环节拆成可以独立替换的模块,像乐高一样按需组合:检索模块可以选向量检索、BM25 或图检索;改写模块可以选 HyDE、Step-back 或多 Query 扩展;生成模块可以是普通输出,也可以是带引用的结构化输出。不同业务场景挑不同模块组合,灵活性大幅提升。LlamaIndex 的 Workflow 和 LangGraph 都是这个设计思路的具体实现。
还了解其它更高级的范式吗?
最朴素的 RAG 范式是这样的:用户提问 -> 向量检索 -> 拼 prompt -> LLM 生成答案。这套流程一两天就能跑起来,大多数教程演示的都是这个形态。但一旦到了真实业务场景,问题就来了
第一个问题是检索质量参差不齐。用户的提问方式千变万化,有些问题一次检索就能找到正确答案,有些问题向量检索完全召不到相关内容,但系统不会区分,一律把检索结果喂给 LLM。然后呢?LLM 就会在低质量上下文上胡说八道,而用户完全不知道答案有没有依据。
第二个问题是所有问题都走同一套流程,太死板。有些问题根本不需要检索,比如「你好」「今天天气怎么样」,强行去知识库里找毫无意义;有些复杂问题需要多轮检索才能拼出完整答案,而朴素 RAG 一律只检索一次。然后呢?该省的地方没省,该深挖的地方浅尝辄止,结果既浪费又不准。
第三个问题是知识库覆盖有盲区。知识库里永远不可能有所有答案,当找不到相关内容时,朴素 RAG 要么让 LLM 编造答案,要么返回一个风马牛不相及的回答。然后呢?用户拿到的是一个自信满满的错误答案,体验很差。
Self-RAG:LLM 自己决定要不要检索
最朴素的 RAG 的第二个痛点是所有问题都走同一套流程,太死板。Self-RAG 就是专门解决这个问题的。
普通 RAG 不管用户问什么都去检索,但有些问题根本不需要检索(比如「1+1等于几」),有些检索结果根本不相关。Self-RAG 训练了一个特殊的 LLM,它会自主决定:
- 当前问题需不需要检索?(Retrieval token)
- 检索回来的内容和问题相不相关?(Relevance token)
- 生成的答案有没有幻觉?(Support token)
- 最终答案质量够不够好?(Utility token)
Self-RAG 的执行流程如下:
- 判断是否需要检索:LLM 先评估这个问题要不要查知识库。如果是常识性问题或者不需要外部信息,直接生成答案。
- 检索并逐条评估相关性:对每一个召回的 chunk,LLM 独立判断「这段内容和问题相关吗」,不相关的直接跳过。
- 基于相关 chunk 生成候选答案:每个相关 chunk 各自生成一个候选答案。
- 评估答案质量:对每个候选答案,LLM 打两个分,有没有文档支撑(防幻觉)、答案对用户有没有用。
- 选出最优答案返回:综合两个分数,选出最好的那个候选答案。
这套机制的代价是需要专门微调一个带有这些「自我评估」能力的 LLM,不能直接用通用模型套用。但好处也很明显:不需要检索的问题直接回答,省了检索开销;检索结果不相关时能识别出来,避免了基于错误上下文生成的幻觉。
CRAG(Corrective RAG):检索质量差时自动纠错
Self-RAG 解决的是要不要检索的问题,那如果检索了但结果质量很差怎么办?朴素 RAG 的第三个痛点——知识库覆盖有盲区——就会导致这种情况。CRAG 就是专门解决这个问题的。
CRAG 在检索完之后加了一个质量评估环节:如果检索到的内容质量高,正常走 RAG 流程;如果质量低,自动切换到网络搜索,用搜索结果代替知识库内容来回答问题;如果质量居中,把知识库结果和网络搜索结果都用上。
CRAG 的执行流程如下:
- 本地检索:先从知识库里召回 top-K 个 chunk。
- 质量评估:用一个轻量级的检索评估器(可以是专门训练的分类模型,也可以用 Rerank 模型的分数来近似)来判断检索结果和问题的相关程度。
- 三级路由决策:根据评估结果分成三档。评估为相关,直接用本地结果生成答案;评估为不相关,说明知识库没有覆盖这个问题,丢弃本地结果,降级走网络搜索;评估为模糊,把本地结果和网络搜索结果合并一起用,两者互补。具体的判断阈值需要根据业务场景来调,没有固定数值,核心思路是让系统能自动识别检索结果靠不靠谱并做出相应的降级决策。
- 生成答案:基于最终上下文生成回答。
这个方案的核心价值在于兜底:知识库覆盖不到的问题不会直接乱答,而是自动去网上找答案,大幅提升了系统的健壮性。很多人以为 RAG 系统只能用本地知识库回答问题,CRAG 打破了这个限制——它把知识库当主力,把网络搜索当备胎,两者配合使用。
GraphRAG:用知识图谱增强全局理解
Self-RAG 和 CRAG 解决的都是检索策略层面的问题,但还有一种瓶颈是检索方式本身的局限——普通向量检索只能召回和问题直接相关的文档片段,对于需要全局理解的问题(比如这批文档的核心主题是什么),单纯的向量检索只能找到局部信息,无法把散落在多处的知识串联起来。GraphRAG 就是微软 2024 年推出用来解决这个问题的方案。
GraphRAG 的核心做法分两步。预处理阶段,先用 LLM 从文档里抽取实体和关系,建成知识图谱,然后用 Leiden 等社区发现算法对图谱中的实体做聚类,把紧密关联的实体分成一个个「社区」,再对每个社区生成一份 LLM 摘要,描述这个社区里的实体之间是什么关系、整体在讲什么。这些社区摘要会形成多层的层次结构,从细粒度到粗粒度都有覆盖。
检索阶段,GraphRAG 支持两种查询模式。对于局部问题(比如「A 公司的 CEO 是谁」),可以直接在知识图谱中做实体查找和关系遍历;对于全局问题(比如「这些文档的主要主题有哪些」),则用 Map-Reduce 的方式,先让 LLM 分别阅读各社区的摘要提取相关信息,再汇总生成最终答案。这种「社区摘要 + Map-Reduce」的设计,是 GraphRAG 区别于普通知识图谱方案的核心创新。
你可能会问,为什么向量检索搞不定全局性问题?因为向量检索本质上是在做「一对一匹配」——用一个 query 向量去和一个 chunk 向量比较距离,它擅长找到和问题直接相关的局部片段,但无法理解整个知识库的宏观结构和跨文档的关联关系。GraphRAG 通过预先构建社区摘要,把全局信息提前「蒸馏」好了,查询时不需要遍历所有文档就能回答全局性问题。
适合知识之间关联性强、需要全局视角的场景,比如金融领域的企业关系分析、医疗领域的药物-疾病-症状关联查询、大规模文档集的主题归纳。代价是构建知识图谱和社区摘要需要大量 LLM 调用,预处理成本较高
Agentic RAG:把 RAG 做成 Agent
前面几种范式虽然各有创新,但都有一个共同点:流程的步骤是预先定义好的,最多是根据条件做分支选择。但有些问题复杂到连「需要检索几次」「每次检索什么」都无法预先定义,需要 LLM 根据中间结果动态决定下一步怎么做。这就引出了 Agentic RAG。
普通 RAG 是固定的「检索一次 -> 生成」的流程,但有些问题需要多轮检索:第一次检索发现信息不够,需要追加检索;检索结果互相矛盾,需要再检索来验证;问题本身是多步骤的,每一步都需要检索不同的内容。
Agentic RAG 把 RAG 嵌入 Agent 循环,LLM 可以自主决定:要不要再检索一次、用什么关键词检索、检索结果是否足够、什么时候可以生成最终答案。
Agentic RAG 的执行流程如下:
- 接收问题,进入循环:LLM 拿到用户问题,开始 Agent 决策循环。
- LLM 决定下一步动作:根据当前已收集到的上下文,LLM 判断,信息够了吗?如果不够,下一步该搜什么关键词?
- 执行检索,累积上下文:按 LLM 指定的关键词检索,把新召回的 chunk 追加到已有上下文里。
- 重复判断,直到信息充分:LLM 每轮都重新评估「现在能回答了吗」,不够就继续检索,够了就生成答案。
- 生成最终答案:信息充分后退出循环,基于所有累积的上下文生成回答。为防止死循环,设置最大迭代次数兜底。
这套机制特别适合需要多步骤推理的复杂问题,比如「帮我分析 A 公司最近三年的财报,找出营收增速放缓的根本原因」,这类问题需要多次、有针对性地检索不同维度的内容,一次检索完全不够。
和前面几种范式相比,Agentic RAG 最大的区别在于流程不是写死的,而是 LLM 在运行时自己决定」。
如果说 Self-RAG 是给 RAG 加了一个要不要检索的开关,CRAG 是加了一个检索不好怎么办的兜底,那 Agentic RAG 是把整个检索流程变成了一个可以不断循环、不断调整的智能体。
把几种高级范式的特点对比一下,选型时有个参考:
| 范式 | 核心创新 | 适合场景 | 工程复杂度 |
|---|---|---|---|
| Advanced RAG | Query 改写 + Rerank | 大多数生产场景 | 低 |
| Self-RAG | LLM 自主决策检索 | 问题类型多样、部分不需要检索 | 高(需特殊训练模型) |
| CRAG | 检索质量差时降级网络搜索 | 知识库覆盖不全的场景 | 中 |
| GraphRAG | 社区发现 + 层次摘要,支持全局查询 | 实体关系复杂、需要全局理解的场景 | 高(图构建 + 摘要成本大) |
| Agentic RAG | 多轮动态检索 | 复杂多步骤问题 | 中(Agent 框架) |
如何规避 RAG 系统中大模型的幻觉?
RAG 幻觉主要有两类:
- 检索没有召回到相关内容,LLM 没有可用的上下文,靠自身知识在编造答案。
- 检索内容召回了但 LLM 没有严格遵循,在文档内容的基础上加了自己的推断。
规避幻觉的策略主要有四个方向:
- Prompt 强约束,明确告知 LLM 只能根据提供的资料回答,资料里没有的就说不知道;
- 检索质量门控,Rerank 分数低于阈值就直接拒答,不让 LLM 在低质量上下文上硬撑;
- 生成后引用核查,每个关键的声明都要在 chunk 里找到来源依据;
- 结构化输出强制溯源,让 LLM 输出 JSON,每条结论必须附上来源编号。
其中 Prompt 强约束加检索质量门控是我觉得成本最低、收效最快的组合,生产系统上线前这两个必须做。
RAG 知识库如何实现动态与持续更新?
普通数据库更新一条记录,直接 UPDATE 就行,数据是独立的,改一条不影响别的。但 RAG 知识库的麻烦在于:原始文档和向量库之间不是一对一的关系,而是一对多的关系。
一篇文档会被切割成几十甚至上百个 chunk,每个 chunk 分别 Embedding 后存入向量库。当文档内容发生变化时,你不能简单地「更新一条记录」,因为文档结构变了,切割结果可能完全不同,chunk 的数量、边界、内容都会变。
所以 RAG 知识库的更新逻辑必须是先删掉旧文档对应的所有 chunk,再重新切割入库,即「先删后增」,而不是在原来的 chunk 上直接更新向量。
知识库更新的核心挑战是,文档变了,对应的 chunk 和向量都要跟着变,而且要做到增量处理,不能每次全量重建。
通用方案是给每个文档算一个内容 hash,每次文档入库时,计算文档内容的 MD5 或 SHA256 摘要,把这个 hash 值和文档 ID、对应的 chunk ID 列表一起存下来(存在 Redis、数据库都行)。下次检测到这篇文档时,重新计算 hash 和存储的值对比:相同说明内容没变,跳过;不同说明内容有更新,触发重处理流程。通过轮询或者监听数据源变更,检测到文档新增、修改、删除的时候,先清掉旧的向量,再重新切割入库。
对于实时性要求比较高的场景,我会用消息队列比如 Kafka 做变更事件驱动,实现秒级的入库
总结一下:生产环境推荐事件驱动 + hash 变更检测 + 先删后增的组合方案,兼顾实时性和数据一致性。新增和删除操作相对简单,修改操作记住一个原则,永远先删掉旧的所有 chunk,再重新入库,不要尝试局部更新,这是最可靠也最不容易出 bug 的做法。
如何进行 RAG 调优后的效果评估?请给出真实应用场景中采用的效果评估标准与方法
RAG调优后的效果评估可以围绕这三个维度来看:检索质量、生成质量、系统性能。
检索质量评估:需要准备 一批问题 + 对应的正确 chunk ID 的测评数据(可以从历史问答里标注,也可以让领域专家整理)
- 客观指标:Hit@K 是「找到没」,MRR 是「多快找到的」
- Hit@K(前k个结果的相关性比例):Top-K 的检索结果是否有想找的那个结果,一般 Hit@5 低于 0.7
- MRR(首个相关结果的排名倒数均值):要找的东西排在第几名,MRR 低于 0.5 通常说明 Rerank 效果不够好,正确内容召回了但没排到前面。
- 主观评测:通过人工审核检索结果是否满足业务需求。
生成的质量评估(RAGAs 框架)
- F可信度(Faithfulness)(评估生成的答案中是否存在幻觉):答案里说的每件事,在检索到的 chunk 里有没有出处?这个指标衡量的是幻觉程度。你可以把它理解为「LLM 裁判」在逐句问:「这句话你从哪条资料里找到的依据?」没有依据的句子越多,分越低。目标值是 > 0.8。
- AR 答案相关性(Answer Relevancy)(回答是否解决用户问题):这个指标和 Faithfulness 是两回事,很多人会把它们搞混。Faithfulness 是问「说的是不是真的」,Answer Relevancy 是问「说的是不是用户想要的」。目标值是 > 0.8。
- CR检索相关性(Context Relevancy)(答案是否基于检索内容):这个指标需要有「标准答案」作为参照,衡量的是检索层有没有「漏掉该找到的内容」。目标值是 > 0.7。
评测方法:
- 大模型打分(利用大模型评估答案质量)
- 人工打分(对CR、AR、F评分)。
通过指标定位问题
Context Recall 低,你可以把它理解为「检索结果里缺少了答好这道题必要的信息」,说明检索层没召回到正确内容,优化方向是换更强的 Embedding 模型、调整 Chunking 策略、或者加多路召回来补充覆盖面。
Context Precision 低,说明检索召回了太多噪音,相关内容是找到了,但不相关的内容也混进来了,把 LLM 的注意力稀释掉了,优化方向是加强 Rerank 模型、调低最终送给 LLM 的 chunk 数量。
Faithfulness 低,说明 LLM 在编造,幻觉问题多,你回答里说的东西在参考资料里找不到依据,优化方向是加强 Prompt 约束、引入引用核查、或者做检索质量门控,防止低质量上下文进入生成阶段。
Answer Relevancy 低,说明答案跑题了,没有聚焦在用户问的问题上,通常是 Prompt 的指令不够明确,告诉 LLM「请严格回答问题本身,不要展开无关内容」往往就能改善。
线上指标:最终衡量标准
- 踩率(thumbs_down_rate)是最直接的信号,用户主动点踩,说明这次回答让他不满意,是最真实的负反馈。
- 追问率(followup_rate)反映的是「答非所问」的程度,用户紧接着说「你没回答我的问题」或者追问同一个问题,通常意味着上一次回答没用。
- 转人工率(escalation_rate)衡量的是「RAG 放弃回答」的频率,这个比例太高说明知识库覆盖不足;但如果这个比例因为加了质量门控而上升,不一定是坏事,宁可转人工也不要给用户错误答案。
- 空回答率(answer_empty_rate)就是系统主动说「我不知道」的比例,过高说明知识库亟需扩充。
- 会话解决率(session_resolution_rate)是最综合的指标,衡量「一次对话能不能解决用户的问题」,是最贴近用户体验的衡量维度。
离线评估(Hit@K + RAGAs)用来快速迭代和定位问题,线上指标(踩率、转人工率)是最终验收标准。两者结合,形成「离线测评 -> 上线 -> 线上观测 -> 发现问题 -> 离线复现 -> 修复 -> 再上线」的完整评估闭环。
把几个核心指标和它们的含义整理成表,方便对照排查:
| 指标 | 属于哪层 | 衡量什么 | 低了说明什么 |
|---|---|---|---|
| Hit@K | 检索层 | 正确 chunk 是否被召回 | Embedding 或 Chunking 有问题 |
| MRR | 检索层 | 正确 chunk 的排名是否靠前 | Rerank 效果差 |
| Context Recall | 生成层输入 | 检索内容覆盖了多少正确信息 | 多路召回不足 |
| Context Precision | 生成层输入 | 检索内容里噪音多不多 | Rerank 没过滤掉无关内容 |
| Faithfulness | 生成层 | 答案有没有幻觉 | Prompt 约束不足或检索质量差 |
| Answer Relevancy | 生成层 | 答案和问题相不相关 | Prompt 写法问题 |
| 踩率 / 转人工率 | 线上 | 用户实际满意度 | 整体系统效果,综合反映 |
在实际落地中,你觉得 RAG 最难的地方是哪里?
我觉得 RAG 最难的不是把它跑起来,一个基础的 Demo 一两天就能搭起来,难的是把它调好。工程上最让我头疼的有三块。
第一是文档预处理,原始数据的格式五花八门,PDF 里面的表格、图片、嵌套的格式,处理不好就是一堆乱码进了知识库,进去的是垃圾出来的也是垃圾。
第二是检索质量的调优,向量召回不准是整个系统效果的天花板,但问题来源很多,Chunking、Embedding、Query 改写,任何一个环节出问题都会影响结果,排查起来很费劲。
第三是效果评估,答案对不对很难系统性地衡量,不知道是哪个环节出了问题,优化就变成了瞎猜。
