RAG(Retrieval-Augmented Generation,检索增强生成) 是目前大语言模型(LLM)落地最核心的技术之一。简单来说,它就像是给大模型配了一个“开卷考试”的搜索引擎,让模型在回答问题前,先去私有知识库里查阅相关资料,从而解决 LLM 知识冻结、无法覆盖私有数据和最新信息问题

整个 RAG 流程可以分为两个核心阶段:数据准备阶段(离线)检索与生成阶段(在线)

核心流程与技术全景图

1. 数据准备阶段(Data Preparation)

文档解析与清洗(Parsing & Cleaning)

将各种格式的原始数据读取进来,可以是 PDF、Word、Markdown、网页、数据库记录等。这一步通常用 LlamaIndex 或 LangChain 提供的 DocumentLoader 来做

  • 技术点:使用 PyPDFUnstructured 或基于深度学习的 LayoutLM 来解析复杂的 PDF 表格和布局。通过规则或大模型去除文档中的噪声(如广告、格式错乱字符)。

文本切片(Chunking)

一是向量模型有输入长度限制,整篇文档根本塞不进去。二是如果把一整篇文章压缩成一个向量,细节信息会被平均掉。所以要把文档切成小片段(chunk)实践中通常 500~1000 token 一个 chunk,同时做一定的重叠(比如前后各重叠 100 token)。每条chunk中包含向量,原文以及metadata,表示储存形式,对应文字,以及来源文件附加信息的映射。

  • 固定大小切割(Fixed Size Chunking)

    最简单的方式,按固定字符数或 token 数切割,不管语义边界在哪。优点是实现简单、 chunk 大小可控;缺点是可能在句子中间截断,破坏语义完整性。是简单保底的选择

  • 语义边界切割(Semantic/Structure Based Chunking)

    按文档的自然语义边界来切,比如段落、句子、标题层级。优先保留大粒度语义单位。

    在md文档里,按标题层级切是更优的选择:每个 chunk 对应整篇文档中的一个完整章节,metadata 里自动带上所属标题(比如「产品手册 > 退款政策 > 申请流程」),既语义独立,又方便过滤和溯源。

  • 特殊内容专项处理

    代码应该以函数或类为单位切割,表格则要整块保留,转成 Markdown 格式存储,不能按行截断。

  • 父子切割(Parent-Child Chunking)

    存储时,同一段内容存两份。一份是细粒度的小 chunk(比如 200 token),专门用于向 量检索,因为小 chunk 语义聚焦,围绕一个小话题,检索精度高。另一份是包含这个小 chunk 前后上下文的大 chunk(比如 1000 token),通过 ID 与对应的小 chunk 关联。检 索时用小 chunk 找到精准的命中点,然后根据关联 ID 取出对应的大 chunk,把完整的上 下文交给 LLM 阅读,生成质量更好。

    策略 适用文档类型 优点 缺点
    固定大小 + 重叠 (Fixed-size + Overlap) 纯文本、无明显结构 实现简单、chunk 大小可控 可能在语义中间截断
    语义边界切割 (Semantic Chunking) 段落分明的文章 语义完整,召回质量好 实现稍复杂,chunk 大小不均
    标题层级切割 (Markdown/HTML Splitter) Markdown、HTML 文档 天然语义独立,自带结构 metadata 依赖文档有清晰的标题结构
    代码按函数切割 (Code Chunking) 代码文件 保留代码逻辑完整性 需要 AST 解析,限定语言
    父子切割 (Parent-Child Chunking) 各类文档(追求高质量) 检索精准 + 上下文完整两全 存储量翻倍,索引构建复杂
    Late Chunking 各类文档 chunk 向量保留全文上下文 需要模型支持

语义保留

  • 重叠切割(Overlap)

    最基础的兜底方案,让相邻 chunk 之间有一段内容重叠,重叠量通常设为 chunk_size 的 10%~20%

  • 按语义边界切割

    找到文本中自然的语义边界再切,可以用 NLP 工具(比如 spacy 或 nltk )识别句子结束位置,然后以句子 为单位填充 chunk:把句子一条条往当前 chunk 里加,加满了就封存这个 chunk 开启新的,不会在句子中间截断。

  • 句子窗口检索(Sentence Window Retrieval)

    存储时把文档切成单个句子,每个句子单独做向量,用于精准检索;检索命中 一个句子后,并不只返回这一个句子,而是把这个句子前后各 N 个句子一起返回,形成一 个上下文窗口,交给 LLM 阅读。

  • 父子切割(Parent-Child Chunking)

    用小 chunk 检索,用大 chunk 生成

  • 命题化切割(Propositions-based Chunking)

    不按文本位置切割,而是用 LLM 把文档分解成一条条独立 的「命题」(Proposition)。每个命题是一个完整、自包含的陈述句,包含了表达这个事 实所需的全部上下文,单独拿出来就能看懂,不依赖上下文,只包含一个核心事实。

  • Contextual Retrieval(Anthropic)

    不改变 chunk 本身,而是在向量化之前把缺失的上下文补进去。

    • 生成 Context:让 LLM 看着整篇原始文档,为每一个切出来的 chunk 生成一段简短的 背景说明(通常 1~2 句话),说清楚这个 chunk 在整篇文档里处于什么位置、讲的是什么

    • 拼接再向量化:把生成的 Context 前置拼到 chunk 前面,然后把这个「Context + chunk」整体去做 Embedding 和 BM25 索引

面试总结:先说清问题:chunk 被单独拿出 来后失去语境,语义被拆散导致检索召回不到。 然后分两个方向讲方案:一是「切的时候别截断」,包括重叠切割、语义边界切割;二是 「切完之后补上下文」,包括句子窗口检索、父子切割、命题化切割、Contextual Retrieval。

实际工程中通常是组合使用:重叠切割做兜底,语义边界切割保证切割质量,对高质量要求 的场景再加父子切割或 Contextual Retrieval。

向量化(Embedding)

选模型主要依托三个维度:第一是场景支持,中文场景优先选 BGE 系列;第二是向量维度,维度越高精度越好,但存储成本也越大;第三是最大输入长度,这个决定了能处理多长的 chunk。

Embedding 模型做的事情本质上是「语义压缩」,把一段自然语言文本映射成一个固定长 度的浮点数向量。且语义相近的文本,向量的余弦相似度高。

Embedding 模型选型

  • OpenAI 的 text-embedding 系列, text-embedding-3-small 是性价比最高 的,1536 维,支持降维到 256 维来节省存储,调用方便,英文效果非常好;

  • BGE 系列(北京智源研究院出品), bge-large-zh-v1.5 是经典的中文开源模 型,1024 维,可以本地部署,数据不出境。

  • 新一代高性能模型,这两年涌现了一批在 MTEB 排行榜上超过 BGE 的模型。 Qwen3-Embedding (阿里通义出品)在多语言基准上表现突出,中文效果很强; Voyage3-large 在英文检索上精度超过 OpenAI 的模型; Cohere embed-v4 支持 128K 超长上 下文,适合长文档场景; Gemini Embedding (Google 出品)在多个评测中表现均衡。

模型评估方法

在自己的业务数据上测:准备几百条业务相关的「问题 + 正确答案 chunk」对,分别用候选模型做检索,看正确的 chunk 有没有出现在前 K 条结果里。这个 指标叫 Hit@K,Hit@5 = 0.8 的意思就是,80% 的问题,它对应的答案都出现在了检索结果的前 5 条里。通常 Hit@5 低于 0.7 就要考虑换模型或者改进 Chunking 策略了。这种贴近真实场景的评估,比排行榜分数更有参考价值。

Embedding算法

  • 第一代:静态词向量(Word2Vec / GloVe / FastText)

    Word2Vec 是这一代最有影响力的代表,2013 年 Google 提出。它有两种训练方式: CBOW(用周围词预测中心词)和 Skip-gram(用中心词预测周围词)。训练完之后,每 个词对应一个固定的向量,「国王 - 男人 + 女人 ≈ 女王」这个著名的类比就是用 Word2Vec 向量做到的,当时整个 NLP 圈都为之兴奋。

    GloVe(Global Vectors for Word Representation)是斯坦福提出的,思路和 Word2Vec 类似但更系统,直接对整个语料库的词共现矩阵做分解,对全局统计信息的利用更充分。实 际效果和 Word2Vec 差不多,很多场景两者可以互换。

    FastText 是 Facebook 提出的改进,解决了一个很实际的问题:Word2Vec 处理不了「未 登录词」,也就是训练集里从没见过的词。你可能会觉得这有什么大不了的,但在真实业务 里,新词、专有名词、网络流行语不断冒出来,处理不了新词就意味着检索直接断链。 FastText 的解法很巧妙——把词拆成字符级别的 n-gram 子词,比如「苹果」会被拆成 「苹」「果」「苹果」等子片段,每个子片段有自己的向量,一个词的向量是其子片段向量 的平均。这样遇到没见过的新词,只要子片段见过,就还能估算出一个合理的向量。

    这一代算法的共同局限性是两个字:静态。每个词只有一个固定向量,不管上下文如何。

  • 第二代:上下文相关向量(ELMo / BERT)

    ELMo(2018 年,Allen NLP 提出)用双向 LSTM 来建模,同时从左往右和从右往左扫描句子,把两个方向的隐藏状态拼起来作为词的上下文向量。

    BERT(2018 年,Google 提出)用 Transformer 替代 LSTM,引入了 Masked Language Model 预训练任务,效果全面超越 ELMo,成为 NLP 领域最重要的里程碑之 一。BERT 用 [CLS] token 来表示整个句子,同时看到前后文(双向),表达能力远超单向模型。

    要比较两个句子的相似度,必须把两个句子拼 在一起喂给 BERT,让 [CLS] 来做判断。这意味着每次检索都要把查询和每一个候选 chunk 拼在一起跑 BERT,百万条文档的知识库就要跑百万次。

    这一代算法的局限是计算量大,延迟过高

  • 第三代:句子级对比学习 Embedding(SBERT / SimCSE / BGE)

    SBERT(Sentence-BERT,2019 年)就是用这个思路解决了 BERT 在检索场景下用不了的 问题。它用 bi-encoder 结构:两个句子分别独立过 BERT,各自得到一个句子级向量,然 后只用余弦相似度来衡量两个向量的距离。

    SimCSE(2021 年,普林斯顿提出)进一步提升了句子 Embedding 的质量。核心思路是 对比学习:把同一句话做两次 dropout 得到两个不同的向量,把这两个向量作为正样本对,让模型学会把它们拉近;同时把同一个 batch 里其他句子的向量作为负样本,把它们推远。这个方法训练非常简单,不需要人工标注,但效果很好。

    BGE(BAAI General Embedding,北京智源研究院)是中文 RAG 场景非常流行的开源模 型,基于对比学习在大规模中英文数据上训练,同时支持 bi-encoder 和 reranker 两种形 态,专门为检索场景优化。E5(微软)是另一个常用的英文对比学习 Embedding,效果同样优秀。

  • 25-26出现的新趋势:

    指令感知 Embedding(比如 Qwen3-Embedding),模型能根据检索指令动态调整向量表示,同一段文本在「找相似问题」和「找答案」两种意图下会产生不同的向量。

    Matryoshka 表示学习(MRL),模型训练时让向量的前 N 维就能表达有意义的语义,这样你可以灵活地截断维度来平衡精度和存储成本。

    多模态 Embedding,同一个模型既能编码文本也能编码图片,让 RAG 可以做到文本和图片的跨模态检索。

向量存储(Vector Storage)

向量数据库支持的核心操作叫做 近似最近邻搜索(ANN,Approximate Nearest Neighbor):给你一个查询向量,在库里找出和它最相似的 K 个向量,返回对应的原始内容。

MySQL 擅长精确匹配( WHERE id = 123 ),向量数据库擅长语义相似 (「找和这个意思最接近的内容」)。两者解决的是完全不同的问题,不是互相替代的关 系。

核心索引算法:HNSW 和 IVF

  • HNSW(Hierarchical Navigable Small World),这是目前召回率最高的 ANN 算法之一。它构建的是一个多层图结构,查询时从最上层的稀疏图开始导航,逐层收窄范围,最终在底层找到最近邻。核心参数ef(ef_search),控制检索时遍历图的节点数量。HNSW 的优点是召回率高(通常在相同延迟下能到 95%+ 的召回率)、查询速度快;缺点是建索引时内存消耗大,不适合内存极度受限的场景。Qdrant、Milvus、 Chroma 默认都用 HNSW。

  • IVF(Inverted File Index) 是另一种思路,它先对向量做聚类,把相似的向量分进同一 个「桶」里,查询时只搜最相关的几个桶,而不是全量遍历。IVF 的优点是内存占用小、适合超大规模;缺点是精度比 HNSW 略低,需要调参(聚类数量 nlist、搜索桶数量 nprobe)。Milvus 在超大规模场景下会用 IVF 系列索引

  • 技术点:将向量及其对应的原文存储在专用的向量数据库中,以便后续进行快速的相似度检索。常见工具有 PineconeMilvusQdrantChroma 等。

通过加入关系型数据库的精确匹配能力,可以加入Metadata 过滤,支持给每个向量挂上 metadata 字段,检索时加过滤条件,只在符合条件的子集里做 ANN 搜索。

数据库 部署方式 适合规模 混合检索 主要优势 主要劣势
Chroma 本地 / Client-Server / 云 中小规模 是(支持 BM25 / SPLADE) 零配置上手极快,生态集成好 超大规模稳定性待验证
Qdrant 自托管 / 云(支持分布式) 中大规模(亿级) 性能好,API 简洁,Rust 高性能 超大规模需调优
Milvus 自托管(分布式) 大规模(亿级) 可水平扩展 部署运维复杂
Pinecone 全托管云服务 中大规模 无需运维 费用高,数据出境
pgvector PostgreSQL 插件 中小规模 是(配合全文检索) 无需新组件,可 JOIN 业务数据 性能弱于专用向量库

数据库查询性能

  • P50 ——第 50 百分位数延迟,在所有的查询中,有 50% 的请求可以在 xx 时间内完成

  • P99 ——第 99 百分位数延迟,在所有的查询中,有 99% 的请求可以在 xx 时间内完成

  • QPS——QPS(Queries Per Second,每秒查询率),系统在每秒钟需要同时处理 xx 个查询请求

2. 检索阶段(Retrieval)

当用户提出问题(Query)时,系统需要从向量数据库中精准找出与问题最相关的知识切片。

Query 处理

使用LLM将用户的问题改写成更适合检索的形式,S或从对话历史里补充必要的上下文。

  • 简单改写,把口语化问题改写成更正式、独立完整的检索句。

  • HyDE(Hypothetical Document Embeddings),让 LLM 先「假设」一个可能的答案,用这个假设答案的向量去检索。

  • Step-back Prompting(后退提问),对于涉及具体细节的问题,有时候知识库里没有直接对应的答案,但有背景原理。Stepback Prompting 把具体问题「后退一步」,提升到更抽象的层次,先检索背景知识,再结 合背景知识回答具体问题。

  • 多 Query 扩展,用 LLM 把用户原始问题改写成 3~5 个不同角度的版本,分别去检索,然后把所有结果合 并去重。

向量检索(粗排)+ 多路召回

把用户的问题也转成向量,然后去向量库里做相似度搜索,找出 向量距离最近的 Top-K 个 chunk。

混合检索(Hybrid Search):向量检索捕获语义相关的内容,BM25 精准命中关键词,再用 RRF 算法把两路结果合并

检索算法

  • BM25(Best Match 25)这是 Elasticsearch、Lucene 等传统搜索引擎的核心算法,该算法有两个核心指标

    第一个是词频(TF):这个词在这篇文档里出现了几次,出现越多说明越相关。

    第二个是稀缺度(IDF):这个词在所有文档里有多罕见。

    BM25 在 TF-IDF 基础上又加了饱和度限制,防止某个词重复出现太多次后权重无限叠加。

  • 向量检索

    使用是 Bi-encoder 结构:query 和 chunk 各自独立编码成向量,再算余弦相似度或内积(Dot Product)。这个结构的优点是速度极快,因为 chunk 的向量可以提前计算好存库,检索时只要算 一次 query 的向量然后做距离比较就行;缺点是 query 和 chunk 是分开编码的,模型没 办法看到两段文字之间的具体词语关联,相关性判断不够精准。

  • RRF(Reciprocal Rank Fusion,互倒排名融合)

    根据向量相似度和BM25排名的倒数来打分进行排序,赋予两路交集文档最高排序权重,同时对仅被任一单路覆盖的文档施以较高的保留分值,确保召回结果的多样性与稳定性

多路召回

向量检索 + BM25 关键词检索 + 多 Query 扩展

Rerank(精排)

Rerank 模型(通常是 Cross-Encoder 结构)会把用户问题和每个候选 chunk 拼在一起,深度理解它们之间的相 关性,然后重新排序,把不相关的结果过滤掉。常只对粗排返回的 Top-20 结果做精排,最终留下 Top-3 到 Top-5

双编码器(Embedding)是把问题和文档分开计算的,而重排模型(如 BGE-RerankerCohere Rerank)是将“问题 + 文档”拼接在一起同时输入模型,进行全自注意力机制(Full Self-Attention)计算。

虽然计算速度慢,但准确度极高,能精准给文档的相关性重新打分,筛选出最优质的 Top-3 或 Top-5。

检索优化

  • 索引优化:父子分块,摘要索引(Summary Index),多粒度分层索引

  • 查询优化:详见Query 处理

  • 召回优化:多路召回

  • 重排序:

3. 提示词拼接与上下文压缩(Prompt Engineering & Compression)

把用户的问题和重排后筛选出的最优知识片段,组装成一段大模型听得懂的话。

  • 上下文压缩(Context Compression)

    • 技术点:大模型的上下文窗口是有限的,且存在“迷失在中间(Lost in the Middle)”的现象(即模型容易忽略长文本中间的信息)。可以使用 LLMLingua 等技术,去掉检索结果中的废话和停用词,只保留核心语义。
  • 提示词模板(Prompt Template)

    • 技术点:通过 LangChain 或 LlamaIndex 构建结构化 Prompt。
    • 示例:“请严格基于以下参考内容回答用户的问题。如果内容中没有提到,请回答‘不知道’,不要编造。参考内容:[检索到的知识片段];用户问题:[Question]”。

4. 生成阶段(Generation)

最后一步,将拼接好的 Prompt 送入大语言模型,生成最终回答。并要求大模型在答案里标注每句话来自哪个片段

幻觉

  • 检索层没找到,LLM 靠「猜」回答。

  • 检索到了,但 LLM 没有严格照着说。

规避幻觉

  • 用 Prompt 告诉 LLM「只准照着资料说」

  • 检索质量差时,直接拒绝回答(阈值门控)

  • 答案生成后,(LLM)逐条核查有没有「凭空捏造」

  • 强制 LLM 输出时带上来源编号

量化效果

指标 层级 量化指标 较低原因
Hit@K 检索层 核心的目标 chunk 是否成功包含在召回的 Top-K 个结果中。 低于 0.7 就说明Embedding 模型质量差,或 Chunking(切片)颗粒度/语义不合理;高于 0.8 说明 检索层 OK,如果答案还不好,问题在生成层。
MRR (Mean Reciprocal Rank) 检索层 正确(最相关)的 chunk 在检索结果中的排名是否足够靠前 低于 0.5说明Rerank(重排)模型效果差,没有把最有用的内容顶到最前面。
Context Recall 生成层(输入端) 检索到的上下文内容,覆盖了多少回答问题所需的正确信息。目标值 > 0.7 多路召回能力不足,或者底层知识库本身存在信息缺失。
Context Precision 生成层(输入端) 检索到的上下文里噪音多不多(是否有太多无关的内容混了进来)。 Rerank 阶段的阈值设得太低,没有有效过滤掉无关内容。
Faithfulness (忠实度/幻觉度) 生成层(输出端) 模型生成的答案是否完全基于上下文,有没有瞎编(幻觉)。目标值 > 0.8 Prompt(提示词)约束不足,或者输入的检索内容本身存在互相矛盾。
Answer Relevancy 生成层(输出端) 最终答案和用户提的问题相不相关、有没有答非所问。 Prompt 的引导写法有问题,或者基座大模型的指令遵循(Alignment)能力较弱。
踩率 / 转人工率 线上业务层 用户在实际业务场景中的真实满意度 整个 RAG 系统效果的综合反映(需结合上述各层指标进行排查)。