第 10 · RAG · 9 min
读取你的文档
大语言模型如何在不记忆的情况下访问数千页内容。嵌入、语义搜索、注入上下文。
上下文窗口解决不了的问题
设想你想构建一个助手,能够回答关于公司全部内部文档的问题——五万页,每周更新。
即便有 100 万 Token 的上下文窗口,也装不下这些内容。退一步说,就算装得下,成本也将高不可攀,而且回复质量会急剧下降:模型在极长上下文中提取精确信息的能力是有限的。
你需要一种不同的方案。这就是 RAG——检索增强生成(Retrieval-Augmented Generation)。
核心思想
不是把整份文档塞给模型,而是只给它与当前问题相关的片段。为此,你需要两样东西:
- 一个索引:所有文档的向量表示,提前构建好。
- 一个语义搜索引擎:当问题到来时,找到语义上最接近的片段。
检索到的片段,随后与问题一起注入大语言模型的上下文。模型基于这些摘录来生成回答。
这正是我们在第 03 章已经了解的语义搜索——只不过应用到了真实文档上。
三步流水线
1. 索引(一次性完成)
文档被切割成片段(Chunk)——每段几百个 Token(相邻片段之间有少量重叠,以避免在边界处截断语义)。
每个片段被一个嵌入模型转换成一个向量,存入向量数据库(Pinecone、Chroma、pgvector……)。
这一步只需执行一次,或在文档更新时重新执行。
2. 检索(每次问答时)
问题到来时,用同一个嵌入模型将它转换成向量。
计算这个问题向量与数据库中片段向量的余弦相似度,检索出最近的 k 个片段——通常是 3 到 5 个。(实际上并不会和所有片段逐一比较:一旦超过几万条向量,向量数据库就会改用近似索引——HNSW、IVF——在不遍历全库的情况下找出最近邻。这恰恰是它们存在的意义。)
这就是语义搜索:"接近"并不意味着"包含相同的词",而是意味着"表达相似的意思"。
3. 生成
检索到的片段被拼装成一个提示词:
文档摘录:
[摘录 1 — ……]
[摘录 2 — ……]
[摘录 3 — ……]
问题:{用户的问题}
这个提示词被发送给大语言模型,模型基于摘录生成回答。模型没有记忆这份文档——它是在被提问的那一刻才读到它的。
流水线可视化
下面是这个系统在一个小型星球科学语料库上运行的样子。选择一个问题——观察哪些片段在向量空间中被检索,以及它们如何被组装进上下文。
选一个问题:它会被 embedded,与语料库的 chunks 比较,最相关的几个会在生成前被注入到 prompt 里。正是这一道弯路让 LLM 能回答它在训练时从未见过的文档相关的问题。
从这次操作里有两点要带走。第一,被检索出来的片段,并不是那些包含问题里的词的片段——问"哪颗行星最大?",回来的是气态巨行星的段落,而"大"这个字在那些摘录里根本没出现。决定权在向量空间中的距离,不在字面词汇。第二,模型从头到尾都没看到整个语料库:它只拿到那几段被选中的摘录。所以 RAG 的全部质量,都在生成之前就决定了。如果该被检索到的那一段没被检索到,再精妙的提示词也救不回来——这也正是本章余下篇幅几乎都在谈切割和检索的原因。
分块大小:一个真实的工程决策
将文档切割成片段并不简单。它往往是 RAG 系统中问题的首要来源。
片段太小:每个片段的信息量不足。我们检索到的碎片,脱离上下文后无法支撑模型给出正确回答。
片段太大:超出了嵌入模型的表达精度。一个代表 2,000 个 Token 的向量是一个模糊的平均——它会在某些问题上被检索到,但相关内容可能被淹没在大段文本中。
实践经验:200 到 500 个 Token 的片段,加上 10–15% 的重叠。合适的参数取决于你的文档结构。
该选哪个嵌入模型?
不是所有问题在同一个嵌入模型下都能命中正确的片段。一个在通用英文上训练的模型,检索 Python 代码时表现会很差;一个在代码上训练的模型,处理医疗对话时也会很差。
实践中常见的几种选择:
text-embedding-3-small/-large(OpenAI)—— 质量稳定,通用,多语言。曾经长期是入门的默认选项;如今在榜单上已被超越,但依然完全够用。bge-large/bge-m3(BAAI)—— 开源,多语言表现极佳。它们在 2024 年一度统治 MTEB 排行榜。- Gemini Embedding(Google)—— 英文 MTEB 榜首。
- Qwen3-Embedding(阿里)—— 开放权重模型中最强的一个,可以自己部署。
- Voyage 3.x、Cohere
embed-v4、Jina v4 —— 当下的商用标杆,其中最后一个是多模态的(文本和图像共用同一个向量空间)。 all-MiniLM-L6-v2(Sentence-Transformers)—— 体积小,速度快,可以本地部署。在简单场景下质量与成本的性价比很高。- 领域专用模型(代码、生物医学、法律)—— 在自己的领域里总是更强,但出了领域就明显变弱。
这份榜单每半年就会翻一次。看的时候还要注意:MTEB v2 的分数不能和 v1 的相比,两版之间任务已经换过了。
实践经验:拿两三个模型在你自己的数据和你自己的问题上跑一遍。MTEB 排行榜对你具体的使用场景说明不了太多。
Reranker:第二轮筛选
向量检索有一个缺陷:快,但是粗。一个几百维的嵌入是一段文本的一个模糊平均。许多"大致相关"的片段会浮上来,真正最好的那些有时反而被淹没。
正因如此,认真做的 RAG 系统里逐渐出现了一个标配的步骤:reranker(重排器)。
思路分两步:
- 第一轮(向量检索)—— 取回距离最近的 50 或 100 个片段。快。
- 第二轮(重排)—— 用一个小模型(通常是一个 cross-encoder)把每一对
(问题, 片段)一起送进去打分,输出一个更精确的得分,保留前 5–10 个。每个片段处理起来很慢,但只在第一轮挑出来的那 50 个候选上做。
reranker 同时看到问题和片段,这是嵌入做不到的。它能捕捉到向量检索漏掉的语义细节。Cohere、Jina AI、BAAI 都提供开箱即用的 reranker。
没有 reranker,你的 RAG 会很快遇到天花板。加上它,整体质量能上一个台阶,而流水线的其他部分都不用动。
为什么不用全文搜索
一个合理的疑问:为什么不直接用关键词搜索,就像传统搜索引擎那样?
向量搜索是对关键词搜索的补充,而不是替代。它能找到语义相似的段落,即便措辞完全不同。"哪颗行星自转最慢?"可以找到一个谈论"公转周期"的片段,即便那个词没有出现在问题里。
实践中,最好的系统将两者结合:混合搜索——同时计算向量相似度和关键词匹配(BM25)的综合得分。
RAG 的局限
RAG 不是魔法。以下是最常见的问题:
切割位置不当。 如果一个答案横跨两个片段,而切割恰好发生在关键位置,那两个片段单独来看都不足以支撑正确回答。
片段内部的"迷失在中间"。 如果片段过长,重要信息可能落在中间而被模型忽视。
问题措辞模糊。 如果问题含糊,问题向量与大多数相关片段的距离都会偏远。解决方案:改写问题,或生成多个变体。
对摘录的幻觉。 大语言模型可能在片段内容之外进行推断。RAG 减少了幻觉,但没有消除它。
RAG 在实践中意味着什么
RAG 是今天将大语言模型连接到私有数据或最新数据的参考技术。几乎所有企业聊天机器人、文档助手和知识库工具都依赖它。
但经典的流水线——嵌入、检索、生成——仍然是一种"被动"架构:模型接收问题,找到片段,给出回答。从 2025 年起,它在很多场景里遇上了对手:智能体式检索。做法是把一个搜索工具交给模型,让它自己循环地查、改写、深挖,直到拿到需要的东西为止。对于更复杂的任务——搜索,再根据结果行动,再搜索——你需要更进一步。
这正是下一章的主题。
更新于