【AI核心概念讲解】一口气搞懂 RAG 检索增强生成:AI 的外部专业知识库大脑

0 阅读10分钟

深入理解 RAG:让大模型拥有专业知识库的核心技术

前言:从 LLM 的局限到 RAG 的诞生

Context:LLM 的“记忆”系统

在之前的文章中,我们详细探讨了大语言模型(LLM)。我们知道了它其实就是**“循环预测 “下一个字是什么”**。

所以,LLM 本质上并没有 “记忆” 系统。这就意味着,它无法自动学习新的知识,也无法回忆起你刚刚跟它聊过的内容。

LLM失忆示意图

那么,在对话系统中,我们通常的做法是把历史对话记录作为 Context(上下文) 拼接到当前的提问前面,一起发给 LLM。这样,模型就能 “看到” 之前的对话,从而维持对话的连贯性。

Context 示意图

Context 记忆大量知识的限制

这个思路启发了我们:如果我想让模型掌握私有知识库的大量内容,比如公司的产品手册、内部的规章制度,或者扮演某角色所需的大量知识,我能不能也把这些知识全部作为 Context,在用户提问的时候一起发给模型呢?

知识库全文传输示意图

理论上是可以,但实际上行不通

原因很简单:

  1. Context Window 限制:每一个 LLM 都有一个固定的上下文窗口大小(例如 4k, 8k, 32k, 128k tokens)。这意味着它一次性可以 “看” 到的文字是有限的。你的知识库可能有几十万甚至上百万字,远远超出了这个限制。 Context Window 限制示意图
  2. 成本过高:LLM 的推理成本是与输入的 Token 数量成正比的。如果你每次提问都把整个几十万字的知识库发过去,哪怕模型能处理,那单次调用的费用也会高到离谱,完全无法承受。

RAG:大量“记忆”的回忆方案

那么,有没有一种办法,既能让模型利用上庞大的知识库,又不用每次都把所有知识都塞给它?

答案就是: RAG (Retrieval-Augmented Generation,检索增强生成)

简单来说,RAG 的核心思想就是:我不把所有知识都给你,我只把跟你当前问题最相关的那一小部分知识找出来给你。

RAG 简述示意图

这样一来,输入给 LLM 的 Context 就永远只有寥寥几百上千字,既不会超出上下文窗口,也极大地降低了计算成本

整个 RAG 的工作流程可以清晰地划分为两个阶段:提问前的准备提问后的回答


一、提问前的准备部分

在用户还没有提问之前,我们需要先对庞大的知识库进行预处理。这个过程是一次性的(或者定期更新),目的是把非结构化的文档,转化为一种可以被快速检索的结构。

这个阶段主要包含两步:文本分片(Chunking)向量索引(Indexing)

1. 文本分片:把长文档切成 “小积木”

原始的知识库文档往往非常长,可能是一本几百页的电子书,或者一个上万行的数据库备份。直接对整个文档进行处理是不现实的。我们需要先把它切分成一个个小的、独立的文本片段,我们称之为 Chunk(块)

分片示意图

根据文档的类型和结构,分片的策略也有很多种:

  • 按字数 / Token 切分:这是最通用、最简单的保底方案。不管文档结构如何,直接按固定的长度(比如 512 个 Token)一刀切。优点是简单粗暴,处理速度快,适合处理无结构的纯文本。缺点是可能会把一句话从中间切断,破坏语义完整性。

  • 按段落切分:利用文档中的换行符,天然地按段落进行切分。这能很好地保证语义的完整性,因为段落本身就是作者用来表达完整意思的单位。

  • 按章节切分:对于有清晰标题结构的文档(如 Markdown 文档、PDF 电子书),我们可以按一级标题、二级标题来切分。比如把 “第一章 产品介绍” 作为一个块,“第二章 安装指南” 作为另一个块。这种方式能最大程度地保留文档的逻辑结构。

  • 按页码切分:对于扫描版 PDF 或者分页的文档,有时候我们会直接按物理页码来切分,虽然不一定完全符合语义边界,但能方便用户定位到原文的具体位置。

在实际应用中,我们通常会结合多种策略,比如先按章节切,如果章节太长,再按段落切,如果段落还长,最后按 Token 限制切。

2. 向量索引:把文本变成可搜索的向量

分完片之后,我们得到了一堆文本块。接下来,我们要把这些文本块 “入库”,但不是直接存文本,而是先把它们 Embedding 为 向量(Vector)然后再将文本片段和文本向量存入向量数据库中。

Embedding:文本片段的向量化

这个文本片段转向量的过程,就叫做 Embedding(嵌入)

我们使用一个训练好的 Embedding 模型,把一段文本输入进去,它就能输出一个长长的数字数组。这个数组就是这段文本的 “语义指纹”。

这个模型有一个神奇的特性:语义相近的文本,转化出来的向量在空间中的距离也很近。

比如,“咪的Coding喜欢阅读” 和 “咪的Coding爱阅读”,这两句话用词不同,但意思差不多,它们的向量在高维空间里就离得很近。而 “今天天气真好” 的向量,就会离它们很远。

Embedding 文本转向量示意图

这就解决了传统关键词搜索的痛点。传统搜索只能匹配 “阅读”、“喜欢” 这些词,而向量搜索能直接匹配 “语义”。

目前,业界有一个权威的 MTEB 排行榜,用来评测不同 Embedding 模型的好坏。像 harrier、Qwen、gemini 等模型在榜单上表现优异,是目前构建 RAG 系统的首选。 huggingface.co/spaces/mteb…

MTEB 模型排行榜

向量数据库:用于高效存储和查询向量的数据库

得到了文本块对应的向量之后,我们需要把它们存起来。这时候,向量数据库(Vector Database) 就登场了。

向量数据库中同时存储了文本片段和文本向量。并且它内置了特殊的索引算法(如 ANN 近似最近邻搜索),能让你在百万甚至上亿条向量中,毫秒级地找出与目标向量最近的那几个。

向量数据库示意图

常见的向量数据库有 Elasticsearch、Milvus、Chroma等。它们就像是 RAG 系统的 “大脑记忆库”,负责存储所有的知识向量,并随时准备响应检索请求。


二、提问后的回答部分

好了,离线的准备工作都做完了。现在,用户终于来提问了。

这个阶段,也分为三步:问题召回 -> 重排 -> LLM 生成

1. 问题召回:粗筛候选片段

首先,我们把用户的提问,也用同样的 Embedding 模型,转化为一个查询向量。

然后,我们拿着这个查询向量,去向量数据库里进行向量相似度计算。数据库会立刻返回 Top N 个(比如 Top 10)与查询向量最相似的文本块。

这一步,我们称之为 召回(Retrieval)

这一步的核心,就是 向量相似度计算

召回示意图

目前流行的算法方案有 余弦相似度欧氏距离点积

  • 余弦相似度:计算两个向量之间的夹角θ的余弦值。夹角越小,相似度越高。

余弦相似度计算示意图

  • 欧氏距离:计算两个向量之间的距离,即图中红线的距离。距离越小,相似度越高

欧氏距离计算示意图

  • 点积:综合两个向量之间的夹角和和向量的长度。由图中绿色向量向黄色向量引黑色垂线,点积结果为 红线长度 *(红线长度 + 黄线长度)。结果越大,相似度越高。

点积计算示意图

通过这个计算,我们就能快速地从海量数据中,把跟用户问题语义最相关的那十几个候选片段给 “捞” 出来。这一步速度极快,因为向量数据库做了优化,哪怕是亿级数据也能毫秒返回。

2. 重排:精排精准度

召回阶段虽然快,但它其实是一个 “粗筛” 的过程。它使用的是 Bi-Encoder(双编码器)模式,即把问题和文档分开编码,然后比向量距离。这种方式虽然快,但丢失了问题和文档之间的细粒度交互信息。

简单来说,它可能会把一些看起来相关、但实际上答非所问的文档也捞上来。

为了解决这个问题,我们引入了 重排(Reranking) 步骤。

这一步,我们使用 Cross-Encoder(交叉编码器) 模型。

重排示意图

Cross-Encoder 的工作方式完全不同:

  • 它不是把问题和文档分开编码。
  • 它把问题和每一个候选文档拼接在一起,作为一个整体输入给模型。
  • 模型会通读这对 “问题 + 文档”,然后输出一个 0 到 1 之间的分数,直接告诉你这篇文档到底跟这个问题有多相关。

这种方式能捕捉到非常细微的语义匹配关系,比如 “iPhone 充电线” 和 “安卓充电线” 的区别,从而把最相关的文档精准地排到最前面。

当然,Cross-Encoder 比较慢,所以我们不能用它来遍历全库。我们只让它对召回阶段捞出来的那 10 个候选进行打分排序就够了。这就像是:先用简历初筛出符合招聘要求的一批人,然后再通过面试精选出最符合要求的几个人。

3. 生成:LLM 的输出

经过了召回和重排,我们终于拿到了最相关的那几篇文档片段。

现在,我们要做的,就是把这些片段拼起来,作为 Context,和用户的原始问题,一起拼接成一个 Prompt,发给 LLM。

Prompt 通常长这样:

请根据以下背景知识来回答用户的问题。如果背景知识里没有答案,就说不知道。

背景知识:
{我们检索到的相关文档片段}

用户的问题:
{用户的提问}

这时候,LLM 就拿到了它需要的专业知识。它不再需要依赖自己过时的训练数据,而是严格根据我们给它的最新、最准确的上下文来生成回答。

这样,我们就得到了一个既准确、又有来源、还减少幻觉产生的专业回答。


总结

至此,整个 RAG 的流程就走完了。我们来回顾一下:

  1. 准备:把长文档切成小块 -> 把小块通过 Embedding 转成向量 -> 把向量存入向量数据库。

准备全流程示意图

  1. 回答:用户提问 -> 把问题转成向量 -> 去向量数据库召回相似的块 -> 用 Cross-Encoder 对候选块重排 -> 把最相关的块作为 Context 发给 LLM -> LLM 生成回答。

回答全流程示意图

通过这套机制,我们完美地解决了 LLM 上下文窗口有限、私有知识注入困难、推理成本高昂的问题。这也是为什么 RAG 成为了当前大模型落地企业应用最主流、最成熟的技术方案。

感谢你看到这里,如果喜欢的话可以点个关注支持一下吧!也欢迎各位在评论区留言!