从零理解 RAG:检索增强生成的工程实践
很多人对 RAG(Retrieval-Augmented Generation,检索增强生成)的第一印象是:把文档塞进向量库,问问题时检索几段拼进 prompt。能跑通是真的,但一旦上真实数据,效果往往让人失望——答非所问、引用错误、漏掉关键信息。问题几乎从来不在大模型本身,而在检索这一环。
这篇文章把一条能用于生产的 RAG 链路拆开,逐段说清每一步在解决什么问题。
为什么需要 RAG
大模型的知识冻结在训练截止日期,且无法访问你的私有数据。微调可以注入知识,但成本高、更新慢,还容易遗忘。RAG 的思路更轻:把「记忆」外置成可检索的知识库,回答时临时取用。知识更新只需要更新库,不用动模型。
一句话:微调改变模型「怎么说」,RAG 改变模型「知道什么」。
第一步:切分(Chunking)
检索的最小单位是 chunk。切得太大,单段里噪声多、嵌入向量被稀释;切得太小,又会把一个完整语义切断。经验值是 300~800 个 token,并保留 10%~15% 的重叠,避免句子在边界处被腰斩。
更重要的是按语义结构切,而不是按固定字符数硬切。Markdown 的标题层级、PDF 的章节、代码的函数边界,都是天然的切分点。带上「这段属于哪个标题」的元数据,召回后能显著提升模型的理解。
第二步:嵌入与召回
把每个 chunk 用嵌入模型转成向量存入向量库,查询时把问题也向量化,做相似度检索。这一步两个常见坑:
- 嵌入模型要匹配语言和领域。中文场景用在中文语料上训练过的模型,效果差距很大。
- 纯向量检索会漏掉精确匹配。比如产品型号、错误码这类关键词,语义相似度并不敏感。实践中常用混合检索:向量召回 + 关键词(BM25)召回,再合并。
第三步:重排(Rerank)
召回阶段为了不漏,通常会取回 top 20~50 段,但这么多段直接进 prompt 既贵又会干扰模型。这时用一个 cross-encoder 重排模型,对「问题—段落」逐对精算相关性,把最相关的 3~6 段排到前面。
重排是性价比极高的一步:召回负责「广」,重排负责「准」,两者配合才能既不漏又不吵。
第四步:组装 Prompt
把重排后的段落连同来源信息拼进 prompt,并明确要求模型只依据给定材料作答、标注引用、材料不足时直说不知道。这一句约束能大幅降低幻觉。
你是严谨的问答助手。请只依据下面提供的资料回答问题。
若资料中没有答案,请回答「资料中未提及」,不要编造。
回答时在句末用 [1][2] 标注引用来源。
【资料】
[1] {chunk_1}
[2] {chunk_2}
...
【问题】{question}
最容易被跳过、却最重要的一步:评估
没有评估,调参就是在碰运气。RAG 的评估要分两层看:
- 检索质量:召回的段落里到底有没有答案?用一批「问题 + 标准出处」的样本,算命中率与排序质量。检索不对,后面再好的模型也救不回来。
- 生成质量:答案是否忠于资料(faithfulness)、是否回答了问题(relevance)。可以用 LLM-as-judge 打分,配合少量人工抽检。
把这两层指标做成可重复运行的离线评测集,每次改动跑一遍,你才知道是真的变好了,还是只是「这次的例子刚好对」。
小结
一条靠谱的 RAG 链路是:语义切分 → 混合召回 → 重排 → 受约束生成 → 持续评估。其中 80% 的效果提升来自检索侧,而不是换更大的模型。当你的 RAG 效果不好时,先别怀疑模型,去看看召回的段落里到底有没有答案——大概率没有。