Skip to content

检索增强生成(RAG)

本页速览 从文档解析到重排生成的 RAG 全链路工程解剖:切分策略对比、混合检索与重排、向量数据库选型、Agentic RAG 的本质区别、RAGAS 评估与失败排查清单。

检索增强生成(RAG) ​

RAG(Retrieval-Augmented Generation)是 2020 年由 Meta(当时的 Facebook AI)的 Lewis 等人在论文中正式提出的范式:生成之前先从外部知识库检索相关片段,把片段塞进 prompt,让模型"开卷答题"。六年过去,它依然是企业落地 LLM 的第一块基石——也是新手最容易"三天搭起来、三个月调不好"的系统。本页不讲概念科普,直接解剖一条生产级 RAG 链路的每个环节,告诉你每一步的坑在哪。

一、RAG 解决什么问题 ​

RAG 存在的理由可以归结为三个硬约束:

  • 知识截止(knowledge cutoff)。模型的参数知识冻结在训练数据截止的那一刻。2026 年的模型不知道上周发布的产品文档,除非你在推理时喂给它。
  • 幻觉(hallucination)。LLM 对自己不知道的事不会说"不知道",它会以同样的置信度编造。RAG 把回答锚定在检索到的文本上,再配合引用(citation)机制,让答案可核查。
  • 私有数据。企业的内部文档、代码库、工单系统不可能进训练集。微调能把"语气"训进去,但把海量、高频更新的业务知识训进参数既不经济也不现实——RAG 是知识注入的正确路径,微调是行为校准的路径,两者别搞混。

什么时候不该用 RAG

如果你的知识库很小(几百页以内)且问题大多需要通读全文才能回答,直接把所有内容塞进长 context window 往往比 RAG 更简单更准。RAG 的价值在于从无法全部放入上下文的大规模语料中做选择。语料小于 context window 时上 RAG,是用检索错误换掉了一个本不存在的问题。

还要明确一点:RAG 不是 Agent 的对立面,而是 Agent 最重要的知识获取手段之一。在 什么是 AI Agent 的框架里,朴素 RAG 是一条固定 pipeline,而 Agentic RAG 则把检索变成 Agent 可调用的 工具。

二、全链路解剖 ​

一条完整的 RAG 链路分为离线索引(ingestion)和在线查询(query)两个阶段:

离线索引(文档变更时触发)
  文档 ──► 解析 ──► 切分 ──► 嵌入 ──► 写入索引
           │         │        │        │
           ▼         ▼        ▼        ▼
        PDF/表格    chunk    vector   向量库 +
        OCR/结构    元数据   (dense)  BM25 倒排

在线查询(每次请求)
  用户问题 ──► 查询改写 ──► 检索 ──► 重排 ──► 组装 prompt ──► 生成
                │           │        │                     │
                ▼           ▼        ▼                     ▼
             HyDE/       top-k    cross-encoder         LLM +
             多查询      召回     精排 top-n             引用

下面逐段说工程要点。

文档解析:垃圾进,垃圾出 ​

解析是整条链路里最脏也最被低估的环节。要点:

  • PDF 是重灾区。多栏排版、页眉页脚、表格、图表说明文字,用朴素的文本抽取会得到一锅粥。工程上常用专门的文档解析工具(如 Unstructured、Docling、Marker 等开源方案,或云厂商的文档智能服务)把 PDF 转成带结构标记的 Markdown/JSON,而不是直接抽纯文本。
  • 表格要特殊处理。把表格拍平成文本几乎必然丢失行列关系。常见做法是把表格转成 Markdown 表、HTML 表,或者为每张表生成一段自然语言摘要参与检索。
  • 保留元数据。来源文档、章节标题、页码、更新时间——这些既是后续过滤(filter)的依据,也是答案引用的来源。解析阶段丢掉元数据,后面补不回来。

切分:决定召回质量上限 ​

切分(chunking)的目标是:每个块语义自足,且粒度匹配检索场景。详细对比见第三节。

嵌入:语义空间的门票 ​

嵌入模型(embedding model)把文本映射为向量。选型要点:

  • 领域匹配比排行榜分数重要。通用 MTEB 榜单高分的模型,在你的法律/医疗/代码语料上未必最好。最靠谱的方式是拿自己的查询-文档对做小规模评测。
  • 多语言场景选多语言模型。中英混合语料用纯英文优化的模型,效果会明显打折。BGE-M3 这类多语言模型同时输出 dense 和 sparse 向量,适合混合检索。
  • 维度与成本。更高维度通常带来略好的精度和成比例的存储/计算开销。不少模型支持 Matryoshka 截断(取前 N 维仍可用),是存储敏感场景的实用技巧。

嵌入向量一经写入,换模型意味着全量重建索引——这是索引阶段最贵的决策,动手前先想清楚。

检索与重排 ​

在线阶段的核心是"两阶段检索":先用便宜的方法(向量/BM25)粗召回 top-k(k 通常在 20-100),再用贵的模型(cross-encoder reranker)精排出 top-n(n 通常 3-10)。详见第四节。

生成:最后一步也可能前功尽弃 ​

  • 上下文组装。把检索结果按重排后的顺序拼接,附上来源元数据。注意 "lost in the middle" 现象——模型对上下文中间位置的利用率低于头尾,关键证据尽量放两端。
  • 显式约束。在 prompt 中要求模型"仅根据给定资料回答,资料不足时明确说明",并要求引用来源。这是压低幻觉率性价比最高的一步。
  • 上下文预算。检索结果不是塞得越多越好。无关 chunk 会稀释注意力、推高成本,这就是 context engineering 要管的账。

三、切分策略对比 ​

策略做法优点缺点适用场景
固定长度切分按 token 数硬切,配 overlap(如 512 token + 15% 重叠)实现最简单,行为可预测切断句子/段落,语义残缺快速原型、语料结构混乱时的兜底
递归/结构感知切分优先按标题、段落、句子等自然边界切,超长的再降级硬切保留文档结构,块语义较完整依赖文档本身结构良好大多数 Markdown/技术文档的默认选择
语义切分按相邻句子嵌入相似度的突变点切分边界贴合语义转换处计算开销大,边界不稳定,难调试无明显结构的连续长文本(转写稿等)
父子块(parent-child)小块做嵌入与检索,命中后返回所属的大块给 LLM检索精度与生成上下文兼得索引结构复杂,需维护两级映射答案分散在局部、理解需要上下文的场景
上下文增强块切分后用 LLM 为每个块生成一段定位说明,加在块前再嵌入显著缓解"块脱离全文后语义不明"索引阶段多一次 LLM 调用,成本上升高质量要求的企业知识库

几条来自实践的判断:

  1. 默认值用"结构感知 + overlap"。LangChain 的 RecursiveCharacterTextSplitter 代表的递归切分是大多数项目的正确起点,别一上来就搞语义切分。
  2. 块大小没有银弹。经验区间是 256-1024 token。块太小语义不完整,块太大稀释嵌入的区分度还浪费生成上下文。用评估数据说话,不要拍脑袋。
  3. 父子块(LlamaIndex 称 Parent Document / Sentence Window)是被低估的性价比方案:检索用小而准的块,生成用大而全的上下文,实现成本远低于语义切分。
  4. Anthropic 2024 年 9 月提出的 Contextual Retrieval 值得单独知道:用 LLM 为每个 chunk 生成 50-100 token 的"定位上下文"("本块来自 ACME 公司 2023 年 Q2 财报……"),前缀到块上再做嵌入和 BM25 索引。官方报告检索失败率降低 49%,叠加重排后降低 67%。代价是索引阶段每个块一次 LLM 调用(可用 prompt caching 摊薄)。
  5. Late Chunking 是另一条思路:先对整篇长文档过一遍长上下文嵌入模型,再按切分边界池化出每个块的向量,让每个块的向量天然携带全文信息。在长文档检索基准上相对朴素切分有约 3% 的平均提升——增益不大但实现侵入性低。

四、检索技术 ​

稠密向量检索:语义匹配 ​

把查询和文档嵌入同一向量空间,用余弦相似度/内积找最近邻。擅长同义改写、跨语言、模糊意图;不擅长精确匹配——产品型号、错误码、人名、ID 这类"一个字都不能错"的查询,向量检索经常翻车。

BM25:关键词匹配老树常青 ​

BM25 是基于词频-逆文档频率(TF-IDF)思想的传统检索算法,不依赖任何神经网络。它在精确词匹配上极强,且零嵌入成本。2026 年的现实是:任何严肃的生产 RAG 都不应该只做向量检索。

混合检索 + RRF:生产标配 ​

行业标准做法是并行跑 BM25 和向量检索,用 Reciprocal Rank Fusion(RRF)融合两路排名:

python
def rrf_fuse(bm25_hits: list[str], vector_hits: list[str], k: int = 60) -> list[str]:
    """Reciprocal Rank Fusion:按两路结果的排名倒数融合,无需对齐分数尺度。"""
    scores: dict[str, float] = {}
    for rank, doc_id in enumerate(bm25_hits):
        scores[doc_id] = scores.get(doc_id, 0.0) + 1.0 / (k + rank + 1)
    for rank, doc_id in enumerate(vector_hits):
        scores[doc_id] = scores.get(doc_id, 0.0) + 1.0 / (k + rank + 1)
    return sorted(scores, key=scores.get, reverse=True)

RRF 的好处是只看排名不看分数——BM25 和向量相似度的分数尺度完全不同,加权求和需要调参,RRF 不需要。近年的检索基准反复验证:"混合检索 + 神经重排"的两段式 pipeline 稳定优于任何单阶段方法。

查询改写与扩展 ​

用户的原始问题往往不是好的检索 query。常见技术:

  • 查询改写(rewrite):用 LLM 把口语化、含代词的问题改写成自包含的检索查询。多轮对话场景几乎是必做项("它多少钱?"里的"它"必须解析出来)。
  • 多查询扩展(multi-query):让 LLM 生成 3-5 个语义等价但措辞不同的查询,并行检索后合并去重。用一点延迟换召回率。
  • HyDE(Hypothetical Document Embeddings,2022):让 LLM 先"假装回答"生成一段假设性答案,用这段答案(而不是原始问题)去做向量检索。思想是答案和答案在向量空间里比问题和答案更近。对领域跨度大的零样本检索有效,但如果 LLM 的假设答案错得离谱,会把检索带偏——它是召回增强手段,不是免费午餐。
  • Step-Back Prompting:把具体问题抽象成更高层的问题("某某在 2023 年 8 月上了哪所学校"→"某某的教育经历"),两路一起检索。适合需要背景知识的细节问题。

先做改写,再做扩展,最后才考虑 HyDE

查询改写解决的是"query 本身有歧义",几乎所有对话式 RAG 都该做;多查询扩展解决的是"召回不足",成本低收益稳;HyDE 解决的是"问题与文档语义距离远",收益因场景而异且有带偏风险。按这个顺序逐个加,每加一个用评估验证一次,别一次性全上。

多轮对话下的查询改写,实现上就是一次廉价的 LLM 调用(用小模型即可):

python
def contextualize_query(history: list[dict], question: str, llm) -> str:
    """把多轮对话中依赖上下文的追问,改写成自包含的检索查询。"""
    if not history:  # 首轮对话无需改写
        return question
    prompt = (
        "给定以下对话记录和用户的最新问题,把最新问题改写成一个"
        "不依赖对话记录也能被独立理解的检索查询。只输出改写后的问题。\n\n"
        + "\n".join(f"{m['role']}: {m['content']}" for m in history[-6:])  # 只带最近几轮
        + f"\n\n最新问题: {question}"
    )
    return llm.complete(prompt).strip()

注意只带最近几轮历史——全量历史既贵又会引入无关话题的干扰。改写这步用小参数模型完全够用,不必动用旗舰模型。

重排:精排是性价比最高的一次模型调用 ​

cross-encoder 重排模型(如 Cohere Rerank、BGE-Reranker 系列)把"查询+候选文档"成对输入,直接输出相关性分数,精度远高于向量点积——代价是无法预先计算、只能在线逐对打分,所以只用于对粗召回的 top-k 精排。

经验法则:向量粗排 top-50,重排精排 top-5,是把检索质量从"能用"拉到"好用"的标准动作。近期在文本+表格混合文档上的基准也显示,"混合检索 + Cohere Rerank"这类两段式组合以明显优势胜过所有单阶段方法。

五、向量数据库选型 ​

2026 年的共识是:没有"最好"的向量数据库,只有和你规模/运维能力匹配的。先给一个粗略的决策罗盘:

方案类型适用规模特点注意事项
pgvectorPostgres 扩展< 约千万级向量零新增基础设施,向量与业务数据同事务、同 SQL 过滤/join超大规模下延迟和吞吐不如专用引擎
Qdrant开源(Rust)+ 云服务千万到亿级延迟低、过滤能力强、部署简单生态比 Milvus 小
Milvus开源分布式 + 云服务亿级以上面向超大规模设计,GitHub star 数在四家中领先(约 4.4 万)运维复杂度最高,小规模是杀鸡用牛刀
Weaviate开源 + 云服务千万到亿级内建混合搜索和模块化向量化,schema 灵活资源消耗偏大
Pinecone全托管 SaaS不限零运维、上手最快生产规模下月费可达数百至上万美元,数据驻留受约束
Chroma开源嵌入式原型/小规模进程内运行,最适合本地实验别拿它上生产

务实的选型路径是行业里反复被验证的那条:已经在跑 Postgres、向量量又不大,先用 pgvector——少一个组件就少一类故障、少一份运维。等延迟或规模真的顶到了,再迁 Qdrant(自建)或 Pinecone/Weaviate Cloud(托管)。一开始就上 Milvus 集群的团队,大多是在为不存在的问题付运维税。

索引算法比数据库品牌更值得懂一点

主流向量库底层都是 HNSW(分层可导航小世界图)及其变体。你只需要记住两个旋钮:ef(搜索时探索的候选数)调召回/延迟的权衡,M(图的连接度)调内存/精度的权衡。换数据库解决不了的问题,调这两个参数常常能解决。

六、Agentic RAG ​

朴素 RAG 是一条固定 pipeline:检索一次,生成一次,无论检索结果好坏都得硬着头皮回答。Agentic RAG 把检索变成 Agent 的 工具,由模型在 Agent Loop 里自主决策:

                 ┌─────────────────────────────────┐
                 │            Agent Loop           │
                 │                                 │
  用户问题 ────► │  思考:这个问题需要检索吗?      │
                 │    ├─ 不需要 → 直接回答          │
                 │    └─ 需要 → 调用 search 工具    │
                 │          ↓ 观察结果              │
                 │       结果够好吗?               │
                 │    ├─ 不够 → 改写 query 再检索   │
                 │    ├─ 不够 → 换工具(SQL/API)   │
                 │    └─ 够了 → 生成带引用的答案    │
                 └─────────────────────────────────┘

本质区别在于三点:

  1. 检索从"必经步骤"变成"可选动作"。模型可以判断"这个问题不需要查库",省去无谓的检索延迟和噪声。
  2. 多轮检索与自我纠正。检索结果不理想时,Agent 可以改写查询、换关键词、换数据源,而不是一次性把烂结果交给生成端。grading 检索结果相关性再决定是否重试,是 LangGraph 官方 Agentic RAG 教程里的经典结构(见 LangGraph 页面)。
  3. 检索只是工具箱里的一件。Agent 可以在向量库、SQL、Web 搜索、内部 API 之间路由和组合,这是多跳问题("先查 A 的部门,再查该部门的预算")的正确解法。

代价同样真实:每多一轮"思考-检索"就多一次 LLM 调用。2026 年 1 月一篇系统对比 RAG 范式的实验论文测得,Agentic RAG 的成本可达朴素方案的 3.6 倍,来自额外的推理步骤和重复工具调用。结论很直接:简单事实型 QA 用朴素 RAG,多跳、需要查证、需要组合多数据源的复杂问题才值得上 Agentic RAG。这也是 成本优化 里"按问题复杂度路由"策略的典型应用。

七、评估 ​

RAG 系统不评估等于裸奔。评估分两层,缺一不可:

检索层指标:机器可自动算 ​

  • Hit Rate@k:top-k 结果里是否包含正确答案所在的文档。最直观的质量门禁。
  • MRR(Mean Reciprocal Rank):第一个相关结果排名的倒数的均值。命中但排在第 10 位,和命中且排在第 1 位,对下游生成的影响天差地别——MRR 捕捉的就是这个。
  • nDCG:考虑 graded relevance(相关程度有高低)的更精细指标,标注成本高,多数团队用前两个就够。

前提是你有一个带标注的测试集:一组"问题 → 应命中文档"的对。哪怕只有 50-100 条,也足够暴露大问题。没有测试集时,可以先用 LLM 对已有文档反向生成问题来冷启动。

端到端指标:LLM-as-a-judge ​

  • Faithfulness(忠实度):答案中的每个论断是否都能被检索到的上下文支持。这是对幻觉的直接度量。
  • Answer Relevancy(答案相关性):答案是否真的在回答用户的问题,而非答非所问。
  • Context Precision / Recall:检索上下文中相关内容的占比 / 应检索到的内容是否都检到了。

RAGAS 是这一层事实上的标准框架:它用 LLM 做裁判自动计算上述指标,不需要人工标注答案,评分范围 0-1,并且可以和 LangChain、LlamaIndex 等框架直接集成。当前版本的 RAGAS 已从"RAG 专项评估库"演进为通用的 LLM 应用评估框架,支持自定义指标和实验管理。同类还有 DeepEval、TruLens,选型差异不大,先跑起来比选哪个重要。

评估分数不是免死金牌

RAGAS 的 faithfulness 由 LLM 裁判给出,裁判本身会犯错,尤其在专业领域。正确姿势:自动化指标用于回归测试和趋势监控(防退化),上线前和重大改版时仍要抽样人工审查。把评估接进 CI 和 可观测性 平台,让每次 prompt、chunk 参数、模型的改动都有分数对照——这套方法论的通用部分见 评估。

八、常见失败案例与排查清单 ​

Barnett 等人 2024 年的论文《Seven Failure Points When Engineering a RAG System》(基于三个真实案例研究)总结的经典失败点,至今仍是排查的主线:

  1. 缺失内容(missing content):答案根本不在知识库里,模型却强行作答。→ 补语料 + prompt 中允许"我不知道"。
  2. 未命中(missed top-ranked):答案在库里,但没被检索进 top-k。→ 调 chunk 大小/overlap、上混合检索、加查询改写。
  3. 未进入上下文(not in context):检索到了但被重排或截断策略丢出了最终 prompt。→ 检查 top-k 到 top-n 的裁剪逻辑和 context 预算。
  4. 未被提取(not extracted):答案就在上下文里,模型却没用上——上下文太长、噪声太多时的典型症状。→ 减少无关 chunk、压缩上下文、关键证据放头尾。
  5. 格式错误(wrong format):要求输出表格/JSON 却给了散文。→ 强化指令、给 few-shot 示例、加输出校验重试。
  6. 特异度错误(incorrect specificity):回答太笼统或太啰嗦,粒度与问题不匹配。→ prompt 中明确期望的详略程度。
  7. 答案不完整(incomplete):答案分布在多个 chunk 里,只拼出了一半。→ 父子块策略、增大召回面、Agentic 多轮检索。

动手排查时按这个顺序过一遍,别急着换模型:

  1. 先隔离故障域:拿失败 case 单独看检索结果——问题在检索还是生成?faithfulness 高但答案错,是检索的锅;上下文里有答案却答错,是生成的锅。
  2. 检查解析产物:把出问题文档的解析结果和切分结果打出来人工看。相当比例的"检索不好"其实是"解析把表格/多栏切烂了"。
  3. 单变量迭代:一次只改一个变量(chunk 大小、top-k、是否重排、改写策略),用评估集对照分数。同时改三个变量等于没改。
  4. 看分布不看个案:单个 case 的失败可能是噪声,攒 20 个失败 case 分类统计后,修复优先级自己会浮出来。

最后一句实话:RAG 的难点从来不在"搭起来",任何框架五十行代码都能跑通——难点在于把 hit rate 从 60% 磨到 90%+ 的那些脏活:解析、切分、改写、评估。这也是它能成为面试高频题的原因,相关岗位考察点见 求职知识地图。想亲手搭一遍,从 从零构建你的第一个 Agent 开始。

参考资料 ​