☰
RAG 深度解析:让大模型学会“开卷考试“的知识外挂
2026/10/17 6:54:23 网站建设 项目流程

这是「近五年 AI 技术演进」系列的第二篇。第一篇《从 ChatGPT 到 Agent Harness》把全景图铺开了,这一篇单独深挖知识层的第一个关键角色——RAG(Retrieval-Augmented Generation,检索增强生成)。一句话定位:检索外挂知识,让模型开卷考试。它是后来 Memory(记忆)体系和 Agent 知识库能力的直系祖先。全文约 6000 字,建议收藏后按章节阅读。

一、RAG 到底要解决什么问题?

直接问大模型一个涉及私有数据的问题,你会遇到三堵墙:

第一堵墙:模型会编(幻觉,Hallucination)。幻觉指模型一本正经地输出看似合理、实则虚构的内容。你问它"我们公司的年假制度是什么",它不知道答案,但它不会说"我不知道"——训练目标决定了它永远倾向给出一个流畅的回答。流畅但不正确,比不回答更危险。

第二堵墙:知识有截止日期(Knowledge Cutoff)。模型的知识来自训练数据,训练数据有截止时间。今天的股价、上个月刚发布的产品、上周改的规章制度,模型统统不知道,而且不知道自己不知道。

第三堵墙:你的私有数据它不知道。这是最致命的一堵。企业内部的制度文档、产品手册、客服记录、代码仓库——这些数据从没进过任何模型的训练集。而把私有数据送去重新训练模型?成本高、周期长、数据安全还没法交代。

三堵墙的本质是同一个:模型的知识被"焊死"在参数里了。RAG 的思路不是去动参数,而是绕过去——把知识放在模型外面,用的时候检索进来。

二、核心类比:闭卷考试 vs 开卷考试

理解 RAG,一个类比就够了。

模型的预训练像闭卷考试:知识提前背进脑子,考场上全凭记忆。背过的题答得好,没背过的题——硬编也要编出来,因为闭卷考试不允许交白卷。上面说的幻觉、知识过期、私有数据缺失,全是闭卷的天然缺陷。

RAG 把考试改成了开卷:考前不要求背诵,但允许带资料进场。答题流程变成三步——先看题目,去资料里翻到相关的那几页,再照着资料作答。模型没有被"灌"进任何新知识,只是答题时眼前多了一份相关材料,准确率就完全不同了。

图书馆找书是另一个好用的类比:向量库就是图书馆,Embedding(向量化)就是给每本书生成索书号,检索就是按索书号找到最相关的那几本书,Rerank(重排序)就是图书管理员帮你把找出来的书按相关度再排个序,最后把翻开的页面递给你看。后面每一节,都会反复回到这两个类比。

对应到工程上,RAG 的工作分两个阶段:离线索引(整理资料、编好索书号、放上书架)和在线检索(考试时现场翻书)。看图:

下面把两个阶段逐环节拆开,每个环节都讲清楚"为什么需要它"和"参数怎么影响效果"。

三、离线索引阶段:把书编目上架

离线索引是一次性(或定期)执行的准备工作,三个环节:切分 → 向量化 → 入库。

3.1 文档切分(Chunking):书不能整本塞给模型

切分(Chunking)指把长文档切成一段一段的小块。为什么必须切?两个原因:

  1. 模型的上下文窗口有限,一整份 PDF 塞不进去,即使塞得进去,无关内容也会稀释注意力,让模型抓不住重点;
  2. 检索的粒度决定命中质量——你搜"年假天数",希望命中的是年假那一段,而不是整份员工手册。

切多大切少,直接决定 RAG 的效果上限。这是 RAG 调优中最容易被低估的一环:

  • 切太碎(chunk 太小):每个块只剩孤零零几句话,上下文丢了。"满三年 10 天"这句话如果和"年假制度"的标题不在同一个块里,检索到它也不知道它在说什么,模型自然答不对;
  • 切太大(chunk 过大):一块里混进了制度、财务、考勤好几个主题,检索时噪音多——你只要年假那段,它把整页都塞给你,无关内容既占 token 又干扰生成,还可能把不相干的"事假"规定带进来误导模型。

工程上没有万能参数,但有经验起点:纯文本类 300~500 字一块,块与块之间保留 10%~20% 的重叠(overlap)。重叠是为了对付"答案正好被切在边界上"的情况——关键句子被拦腰切断时,上一块的尾巴和下一块的开头都能保住完整语义。另外,按语义边界切(标题、段落、章节)永远优于按固定字数硬切,Recursive Character Text Splitter(递归字符切分器,先按段落切、超长再按句子切、再超长按字符切)之所以是各家框架的默认选项,就是因为它优先保住语义完整。

3.2 向量化(Embedding):把"意思"变成坐标

切块只是把书拆开了,还没法"按意思检索"——传统关键词搜索搜"年假"找不到写着"带薪休假"的段落。Embedding(嵌入/向量化)就是补上这一步的技术:用一个 Embedding 模型,把每段文本压缩成一个几百到几千维的向量(一串数字,可以理解为语义空间里的一个坐标点)。

它关键的特性是:意思相近的文本,向量在空间中的位置也相近。"年假有几天"和"带薪休假天数"用词完全不同,但它们的向量会落在语义空间的同一片区域。这就是为什么检索可以"按意思找"而不是"按字面找"。

用图书馆的类比:Embedding 模型就是一位特别懂行的编目员,它不看书名,而是读懂每本书讲了什么,然后给每本书编一个"语义索书号"——讲经济学的书索书号挨着,讲历史的书在另一个书架区,哪怕一本讲"经济史"的书,也会被放在两个书架的中间位置。

选型上有两点实用建议:中文场景优先选中文优化过的 Embedding 模型(如 BGE 系列、multilingual 系列,直接用英文模型处理中文,检索质量会明显打折);索引和查询必须用同一个模型——两个不同的"编目员"编出来的索书号体系互不兼容,用 A 模型建库、用 B 模型查询,等于拿错钥匙开锁。

3.3 存入向量库:书架与检索系统

向量库(Vector Database,专门存储和检索向量的数据库,如 Chroma、Milvus、Faiss)就是那个图书馆:每块文本带着自己的向量上架,同时保留原文——因为最后喂给模型的是原文,向量只是用来"找位置"的索引,就像索书号不是书本身。

四、在线检索阶段:考试现场怎么翻书

用户每次提问时执行,四个环节:问题向量化 → 相似度检索 → 重排序 → 拼进 Prompt 生成。

4.1 问题向量化与相似度检索

用户的问题先用同一个 Embedding 模型转成向量,然后去向量库里找"距离最近"的若干块。所谓相似度,通俗理解就是两个坐标点挨得近不近——挨得近,意思就相近。检索参数里最重要的一个叫Top-K:取最相近的 K 块。K 取小了可能漏掉关键材料(该在第 4 名的块没进候选),取大了噪音变多、token 消耗上涨,常见取值 3~10,宁可稍多、交给下一环节去筛。

4.2 向量检索的原理:不用背公式,记住一个画面

"几百万个向量里怎么瞬间找到最近的?"很多人以为要做海量的距离计算,实际上靠的是近似的艺术。

先想暴力做法:拿问题向量跟库里每个向量都算一次距离,取最近的 K 个。精确,但一百万条数据就是一百万次计算,太慢。工程上的答案是近似最近邻搜索(ANN,Approximate Nearest Neighbor):提前把语义空间划分成一片一片的区域,建好分区索引。查询时先判断"问题向量落在哪几片区域",只和这几片区域里的向量算距离。

还是图书馆的类比:找一本讲机器学习的书,管理员不会把全馆几百万册翻一遍——他先看分类索引,直接走向"计算机"那排书架,再在架内细找。牺牲了一点点精确性(最相关的书偶尔被分到了隔壁区),换来上千倍的检索提速,这笔账在工程上几乎永远是划算的。

4.3 Rerank:请图书管理员再排一次序

检索环节有一个结构性短板,理解它才能理解 Rerank(重排序)的价值。

Embedding 模型检索用的是双塔结构:问题和文档各自独立编码成向量,然后比距离。好处是文档向量可以提前算好,检索飞快;代价是问题和文档从头到尾没有"见面"——模型无法捕捉"这个问题的某个词正好对应那篇文档的某个细节"这种精细的语义咬合。所以 Top-K 检索的结果往往是"大致相关",排序不一定准,真正最相关的那块可能排在第 7 而不是第 1。

Rerank 用的则是交叉编码器(Cross-Encoder):把"问题 + 每一块候选文档"拼在一起送进模型,让它们深度交互后直接输出相关度分数。相当于双塔是"两人各自填表后比对资料",交叉编码器是"两人坐下来当面详谈"。后者准得多,但它必须逐对实时计算,太慢——所以不能拿它扫全库,只能给 Top-K 的少量候选做精排。

这就是 RAG 的漏斗架构:向量检索用便宜快的方式从百万级里捞出几十条粗候选,Rerank 用贵而准的方式把几十条精排,最后只把排名最靠前的几条喂给模型。加不加 Rerank,检索质量常常有肉眼可见的差距,是 RAG 调优中性价比最高的单点改进——尤其是知识库规模大、文档之间话题相近的场景。

4.4 拼 Prompt 与生成:开卷作答

最后把精选出来的资料块和问题组装成一个 Prompt,常见模板长这样:

请仅根据以下资料回答问题。如果资料中没有相关信息,直接说"资料中未提及",不要编造。 【资料】 <检索到的块 1> <检索到的块 2> ... 【问题】 用户的原始问题

"仅根据资料"和"没有就说没有"这两句不是客套——它们把开卷考试的规矩讲给了模型,能显著压低幻觉率。资料里没有的信息,模型宁可说不知道,也不闭卷硬编。

五、RAG vs 微调:什么时候该用哪个?

RAG 不是唯一的"注入知识"手段,它的老对手是微调(Fine-tuning)——用领域数据继续训练,把知识直接改写进模型参数里。还是考试类比:RAG 是允许带资料进场,微调是考前请家教把知识背进脑子。

两者最本质的区别在于知识放在哪、更新多贵:

维度RAG微调(Fine-tuning)
知识存放位置模型外部的知识库烧进模型参数里
知识更新重新索引文档即可,分钟级重新训练,天级起步 + 算力成本
数据要求原始文档即可,无需标注需要高质量的训练数据集
一次性成本低(向量库 + Embedding 模型)高(GPU 训练 + 数据工程)
持续成本每次查询多几次检索调用推理成本不变
数据安全数据不出本地知识库训练数据进入模型,存在泄露面
可解释性答案能附引用来源,可追溯知识黑盒,说不清从哪学的
擅长注入事实性知识(文档、规范、FAQ)能力与风格(格式、口吻、领域技能)
典型场景企业知识库问答、客服、文档检索固定格式输出、行业话术、垂直领域能力

什么时候该用哪个?画一张决策图:

经验法则可以压缩成三句话:知识经常变、需要溯源、是事实性内容 → RAG;输出格式和风格固化、知识极少变化、且 RAG 调不出来 → 微调;两者都要 → 先 RAG 后微调,也可以同时用。大多数团队的最佳起点是 RAG——便宜、快、可解释、随时可改,先跑通再谈微调。

六、RAG 的三种典型死法

RAG 架构简单,生产环境里翻车的方式却高度重复。拆解三种典型失败,排障时按图索骥:

死法一:检索不到。用户问了,库里其实有答案,但检索没命中。常见原因:切分把答案切得七零八落;用户的问题和文档的表述方式差太远(文档写"差旅费用报销流程",用户问"出差花的钱怎么要回来");或者干脆是索引和查询用了不同的 Embedding 模型。解法按顺序排查:检查切分参数 → 换更强的 Embedding 模型 → 在检索前加一步"问题改写"(让模型先把用户的口语问题改写成文档风格再检索)。

死法二:检索到错的。命中了一堆"看起来相关"实则无关的内容。典型场景是知识库里文档话题彼此相近(比如都是制度文档),纯向量检索分不清"年假"和"事假"的细微差别。解法:上 Rerank 做精排;给检索加元数据过滤(先按部门、文档类型等字段缩小范围再向量检索);切分时把标题等上下文信息一并带进块里。

死法三:检索到了但模型没用。最冤的一种——资料明明在 Prompt 里,答案也明明在资料里,模型却答非所问,甚至继续编。常见原因:候选太多噪音淹没关键信息(Top-K 太大)、Prompt 里没有"仅根据资料回答"的约束、或者资料块之间互相矛盾而模型随机站队。解法:减小 Top-K、把最相关的资料放在 Prompt 最前面(模型对开头的注意力最强)、强化指令约束,必要时让模型先逐条引用资料再下结论。

一个朴素的排障心法:RAG 出问题,先看检索层再怪模型。把检索到的原始块打印出来人工看一眼,八成的问题在这一步就现形了。

七、动手:100 行以内的最小 RAG

下面是一个能直接跑的最小实现,用 Chroma(一个开箱即用的本地向量库)+ 任意 OpenAI 兼容接口的模型,不到 40 行核心代码:

# pip install chromadb openai import chromadb from openai import OpenAI llm = OpenAI() # 读环境变量 OPENAI_API_KEY,也可指向兼容接口 db = chromadb.PersistentClient(path="./rag_db") # 向量库(本地持久化) col = db.get_or_create_collection("company_kb") # ===== 阶段一:离线索引(真实项目里这一步读 PDF/网页后按块入库)===== docs = [ "年假制度:入职满一年享 5 天年假,满三年 10 天,满五年 15 天。", "报销制度:差旅费用需在行程结束后 15 天内提交报销单,逾期不予受理。", "考勤制度:工作日 9:30 上班,18:30 下班,午休一小时,弹性 30 分钟。", ] col.add(documents=docs, ids=[f"doc_{i}" for i in range(len(docs))]) # add 时 Chroma 会用内置 Embedding 模型自动向量化——生产中建议换中文优化的模型 # ===== 阶段二:在线检索 + 生成 ===== def rag_ask(question: str, top_k: int = 2) -> str: # 1. 问题向量化并检索最相关的 top_k 块 hits = col.query(query_texts=[question], n_results=top_k)["documents"][0] # 2. 拼进 Prompt,明确"照着资料答" context = "\n".join(f"[资料{i+1}] {t}" for i, t in enumerate(hits)) prompt = ( "请仅根据以下资料回答问题。资料中没有的信息,直接回答" f"'资料中未提及',不要编造。\n\n{context}\n\n问题:{question}" ) # 3. 开卷作答 resp = llm.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": prompt}], ) return resp.choices[0].message.content if __name__ == "__main__": print(rag_ask("工作满三年有几天年假?")) print(rag_ask("上个月的火车票还能报销吗?"))

跑一下第二个问题:"上个月的火车票还能报销吗?"——模型会基于检索到的报销制度回答"15 天内可以提交",而不是闭卷瞎编一个天数。把docs换成你自己的文档、加上文件读取和切分逻辑,就是一个可用的知识库问答原型。

八、从 RAG 到 Memory:为什么说 RAG 是 Agent 记忆的祖先

回到系列的主线。第一篇里讲过,Agent 的 Memory 体系分短期、工作、长期三层,而长期记忆的标准动作是:把历史经验存进外部存储,需要时检索相关片段注入上下文。

把这个动作和 RAG 的在线检索阶段对照一下:一模一样。唯一的区别是检索的对象——RAG 检索的是公司文档,Memory 检索的是 Agent 自己的经历(之前的对话、用户的偏好、上次任务的结论)。今天主流的记忆框架(Mem0、Letta 等),底层检索机制就是向量检索 + 重排序这一套。

所以脉络是清楚的:RAG 先证明了"外部存储 + 语义检索 + 按需注入"这条路走得通;Memory 把检索对象从"静态文档"扩展成"动态经历";再往后,Agent 的知识库能力、Skills 的渐进式披露(按需加载知识,而不是全量塞进上下文),骨子里都是同一个思想的不同变体——上下文窗口是最宝贵的资源,知识要存在外面,用的时候才取进来。

RAG 诞生的初衷只是解决"模型不知道我的私有数据",但它沉淀下来的这个模式,成了整个 Agent 技术栈知识层的第一块基石。这也是它在这条演进链上的位置:Prompt 之后、Memory 之前——先让模型学会查资料,才谈得上让它记住事情。


总结一下这篇的要点:RAG 用"开卷考试"化解幻觉、知识过期、私有数据三堵墙;索引阶段切分决定上限(太碎丢上下文、太大噪音多),Embedding 让"按意思检索"成为可能;检索阶段靠向量近邻搜索粗筛、Rerank 精排,性价比最高的改进往往就在 Rerank;RAG 与微调不是对手而是分工——事实知识用 RAG,能力风格用微调;排障先查检索层;而它"外部存储 + 按需检索"的模式,正是 Agent 记忆能力的起点。

下一篇预告:Knowledge 之后是什么?我们会沿着这条线继续往 Agent 的能力层走。参考资料:Lewis et al. 2020(RAG 原始论文)、Chroma / LlamaIndex 官方文档、各 Embedding 与 Rerank 模型公开评测。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询