10 · RAG · 9 min

读取你的文档

大语言模型如何在不记忆的情况下访问数千页内容。嵌入、语义搜索、注入上下文。

上下文窗口解决不了的问题

设想你想构建一个助手,能够回答关于公司全部内部文档的问题——五万页,每周更新。

即便有 100 万 Token 的上下文窗口,也装不下这些内容。退一步说,就算装得下,成本也将高不可攀,而且回复质量会急剧下降:模型在极长上下文中提取精确信息的能力是有限的。

你需要一种不同的方案。这就是 RAG——检索增强生成(Retrieval-Augmented Generation)。

核心思想

不是把整份文档塞给模型,而是只给它与当前问题相关的片段。为此,你需要两样东西:

  1. 一个索引:所有文档的向量表示,提前构建好。
  2. 一个语义搜索引擎:当问题到来时,找到语义上最接近的片段。

检索到的片段,随后与问题一起注入大语言模型的上下文。模型基于这些摘录来生成回答。

这正是我们在第 03 章已经了解的语义搜索——只不过应用到了真实文档上。

三步流水线

1. 索引(一次性完成)

文档被切割成片段(Chunk)——每段几百个 Token(相邻片段之间有少量重叠,以避免在边界处截断语义)。

每个片段被一个嵌入模型转换成一个向量,存入向量数据库(Pinecone、Chroma、pgvector……)。

这一步只需执行一次,或在文档更新时重新执行。

2. 检索(每次问答时)

问题到来时,用同一个嵌入模型将它转换成向量。

计算这个问题向量与数据库中所有片段向量的余弦相似度,检索出最近的 k 个片段——通常是 3 到 5 个。

这是语义搜索:"接近"并不意味着"包含相同的词",而是意味着"表达相似的意思"。

3. 生成

检索到的片段被拼装成一个提示词:

文档摘录:
[摘录 1 — ……]
[摘录 2 — ……]
[摘录 3 — ……]

问题:{用户的问题}

这个提示词被发送给大语言模型,模型基于摘录生成回答。模型没有记忆这份文档——它是在被提问的那一刻才读到它的。

流水线可视化

下面是这个系统在一个小型星球科学语料库上运行的样子。选择一个问题——观察哪些片段在向量空间中被检索,以及它们如何被组装进上下文。

选一个问题:它会被 embedded,与语料库的 chunks 比较,最相关的几个会在生成前被注入到 prompt 里。正是这一道弯路让 LLM 能回答它在训练时从未见过的文档相关的问题。

分块大小:一个真实的工程决策

将文档切割成片段并不简单。它往往是 RAG 系统中问题的首要来源。

片段太小:每个片段的信息量不足。我们检索到的碎片,脱离上下文后无法支撑模型给出正确回答。

片段太大:超出了嵌入模型的表达精度。一个代表 2,000 个 Token 的向量是一个模糊的平均——它会在某些问题上被检索到,但相关内容可能被淹没在大段文本中。

实践经验:200 到 500 个 Token 的片段,加上 10–15% 的重叠。合适的参数取决于你的文档结构。

该选哪个嵌入模型?

不是所有问题在同一个嵌入模型下都能命中正确的片段。一个在通用英文上训练的模型,检索 Python 代码时表现会很差;一个在代码上训练的模型,处理医疗对话时也会很差。

实践中常见的几种选择:

  • text-embedding-3-small / -large(OpenAI)—— 质量稳定,通用,多语言。刚开始时的默认选项。
  • bge-large / bge-m3(BAAI)—— 开源,多语言表现极佳,2024 年 MTEB 排行榜的头部。
  • all-MiniLM-L6-v2(Sentence-Transformers)—— 体积小,速度快,可以本地部署。在简单场景下质量与成本的性价比很高。
  • 领域专用模型(代码、生物医学、法律)—— 在自己的领域里总是更强,但出了领域就明显变弱。

实践经验:拿两三个模型在你自己的数据和你自己的问题上跑一遍。MTEB 排行榜对你具体的使用场景说明不了太多。

Reranker:第二轮筛选

向量检索有一个缺陷:快,但是粗。一个几百维的嵌入是一段文本的一个模糊平均。许多"大致相关"的片段会浮上来,真正最好的那些有时反而被淹没。

正因如此,认真做的 RAG 系统里逐渐出现了一个标配的步骤:reranker(重排器)。

思路分两步:

  1. 第一轮(向量检索)—— 取回距离最近的 50 或 100 个片段。快。
  2. 第二轮(重排)—— 用一个小模型(通常是一个 cross-encoder)把每一对 (问题, 片段) 一起送进去打分,输出一个更精确的得分,保留前 5–10 个。每个片段处理起来很慢,但只在第一轮挑出来的那 50 个候选上做。

reranker 同时看到问题片段,这是嵌入做不到的。它能捕捉到向量检索漏掉的语义细节。Cohere、Jina AI、BAAI 都提供开箱即用的 reranker。

没有 reranker,你的 RAG 会很快遇到天花板。加上它,整体质量能上一个台阶,而流水线的其他部分都不用动。

为什么不用全文搜索

一个合理的疑问:为什么不直接用关键词搜索,就像传统搜索引擎那样?

向量搜索是对关键词搜索的补充,而不是替代。它能找到语义相似的段落,即便措辞完全不同。"哪颗行星自转最慢?"可以找到一个谈论"公转周期"的片段,即便那个词没有出现在问题里。

实践中,最好的系统将两者结合:混合搜索——同时计算向量相似度和关键词匹配(BM25)的综合得分。

RAG 的局限

RAG 不是魔法。以下是最常见的问题:

切割位置不当。 如果一个答案横跨两个片段,而切割恰好发生在关键位置,那两个片段单独来看都不足以支撑正确回答。

片段内部的"迷失在中间"。 如果片段过长,重要信息可能落在中间而被模型忽视。

问题措辞模糊。 如果问题含糊,问题向量与大多数相关片段的距离都会偏远。解决方案:改写问题,或生成多个变体。

对摘录的幻觉。 大语言模型可能在片段内容之外进行推断。RAG 减少了幻觉,但没有消除它。

RAG 在实践中意味着什么

RAG 是今天将大语言模型连接到私有数据或最新数据的参考技术。几乎所有企业聊天机器人、文档助手和知识库工具都依赖它。

但这仍然是一种"被动"架构:模型接收问题,找到片段,给出回答。对于更复杂的任务——搜索,再根据结果行动,再搜索——你需要更进一步。这就是我们在前一章已经见过的智能体架构。

两者结合,才是当今最强大的应用系统的基础。

更新于

RAG 详解:让 LLM 读懂你的文档 · Step by Token