用大白话把 RAG 这条管道讲透
约 3258 字大约 11 分钟
2026-09-07
上一篇说了要干什么,这一篇不写代码,专门干一件事:把 RAG 从头到尾的流程捋顺。捋到什么程度算合格?你能在纸上把整条管道画出来,并且能说出每一步可能怎么搞砸。做到这两点,后面十四篇你随时都知道自己在哪一层干活,出了问题也知道该去哪一层找。
先一句话说清 RAG 是个啥
RAG,全称 Retrieval-Augmented Generation,检索增强生成。名字唬人,原理用一句话就能说明白:
大模型不知道你博客里写了啥,那就在它回答之前,先把相关内容从库里翻出来,连同问题一起塞给它,让它「开卷考试」。
对,就这么朴素。大模型训练数据里没有你的博客,问它就是白问,还容易一本正经地编。RAG 做的事就是给它递小抄。所有后面要讲的解析、切分、向量、检索、重排,全都是在优化「递小抄」这件事——小抄递得准不准、全不全、干不干净。
整条管道长这样,先混个脸熟:
【离线:文章入库】
Markdown 文件 → 解析 → 清洗 → 切分成小段 → 每段算出向量 → 存进向量库
【在线:用户提问】
问题 → 改写 → 检索(向量 + 关键词)→ 融合 → 精排 → 拼进 Prompt → 大模型生成 → 带引用的回答下面一段一段过。每段我都按「它在干什么 → 为什么必须有 → 会怎么搞砸 → 在 relax-admin 里对应哪一页」来讲。
先把管道和 relax-admin 侧边栏的对应关系放这,这张表建议存下来,后面每一篇开头我都会点一下「本篇对应哪个页面」:
| 管道环节 | relax-admin 菜单 | 那一页是干嘛的 |
|---|---|---|
| 入库总流程 | 知识库 → 知识入库 | 拉取/上传的入口,看整条入库流水 |
| 解析 | 语料管理 → 文档上传 / 解析列表 | 上传文件、看每篇的解析状态和结果 |
| 增量更新 | 语料管理 → 增量同步 | Gitee 仓库 diff 检测,定时同步 |
| 切分 | 切片治理 → 入库策略 / 切片列表 | 配切分参数、看切出来的每一段 |
| 切分调优 | 切片治理 → 切片测试 / 质量分析 | 拿真问题试切分效果、看统计 |
| 向量化 / 引擎 | 知识库配置 → AI 引擎 | Embedding / Rerank / 生成模型配置 |
| 多库隔离 | 知识库配置 → 知识库列表 | 建库、配过滤、管来源 |
| 检索 | 会话洞察 → 检索评估 | 看每次检索的召回和排序细节 |
| 改写 | 会话洞察 → 多轮改写 | 改写前后的对照和命中分析 |
| 评测 | 评测中心 → Golden Set / 评测实验 | 基准集管理和 A/B 实验 |
| 安全 | 提示词安全 → 攻击记录 / 规则配置 | 注入检测和规则 |
| 管控 | 流量管控 → 限流策略 / 降级与容错 | 限额、熔断、降级开关 |
📷 图片位(待补充)
应放什么图:一张「管道 ↔ relax-admin 菜单」对照图。可以拿第 01 篇的全景截图做底,用箭头把「解析 → 解析列表页」「切分 → 切片列表页」这类对应关系画出来,一图讲清整个系列的结构。
离线半条线:把文章变成能被「语义搜索」的形态
解析:把文件变成干净的文本
博客文章是 Markdown 文件,解析听起来没难度?其实有几个不那么显眼的活儿:frontmatter(开头那段 title: xxx 的元信息)要摘出来存好,别混进正文;编码问题,早年的文件可能有 GBK 的;还有正文里的目录块、HTML 残渣这类杂物。
这一步的产出在 Java AI 里是一个统一的东西:Document 对象,正文加一包元数据。
记住这个类
Document 是整条管道的「通用货币」——解析产出它,切分消费它产出更多更小的它,向量库存的还是它。
为什么元数据这么重要,现在就能给你一个例子:用户问「你关于 Redis 那篇怎么说的」,检索的时候你总得能按分类过滤吧?答案要标注「出自哪篇文章」吧?文章改了要能找到旧版本删掉吧?全靠解析时写进元数据的那些字段。解析时偷懒没存的字段,后面全做不了,这是全管道最贵的一次后悔。
搞砸方式:文本抽出来了但标题层级丢了、代码块和正文混成一坨、乱码。后果是后面全链路陪葬。这些「搞砸」在 relax-admin 的「语料管理 → 解析列表」页都有观测位:每篇文档的解析状态、字符数、失败原因,出问题先去那一页看。
切分:把长文切成小段
为什么要切?因为检索的最小单位不能是一整篇文章。一篇三千字的文章讲五个主题,你问其中一个,拿「五个主题的混合体」去匹配,匹配不准;就算匹配到了,整篇塞给大模型也浪费上下文。
所以要把文章切成小段(行话叫 chunk),每段讲一件事,检索按段来。
这一步是全管道坑最多、手感最重的地方:
- 切太大,一段讲五件事,向量是五件事的平均值,哪件都检索不准
- 切太小,召回来的都是残句,大模型看了也答不了
- 切的时候句子被拦腰斩断、代码块被切成两半、「该功能」这种指代词离开标题后变成天书——全是日常事故
一句话记住它的分量:切分质量基本决定了 RAG 效果的一半,所以我专门用一整篇来讲它。
向量化:给每段文字发一个「身份证」
切好的段落是文字,文字没法算相似度。Embedding 模型干的事就是把一段文字变成一串数字(比如 1024 个小数),语义相近的文字,这串数字在空间里的位置就挨得近。「怎么防止重复下单」和「幂等性设计」字面完全不同,但它们的向量距离很近——这就是语义搜索能跨过「起标题的随机性」的原理。开篇那个「搜 mybatis 批量插入搜不到」的问题,到这一步就被解决了。
有一个认知必须现在就建立,它是很多诡异问题的根源:向量只有同一个模型算出来的才能互相比较。 每个模型都有自己的「坐标系」,A 模型给「退款政策」发的向量,拿到 B 模型的空间里,位置毫无意义。所以——换 Embedding 模型 = 全库里所有向量作废 = 必须全量重算重灌。这是架构级约束,不是「先随便选一个以后再说」能糊弄过去的。
搞砸方式:中文语料用了纯英文模型;新旧模型的向量混在一个库里,检索结果莫名其妙。
入库:存进 PGVector
最后把这些「文字 + 向量 + 元数据」的记录存进数据库。我们用 PGVector,就是给 Postgres 装个扩展,让它能存向量、能快速算「离某个向量最近的 K 条」。这里面有些索引原理(HNSW 是什么、为什么它是「近似」检索)值得搞明白,不然调参的时候全是玄学,第 07 篇细讲。
在线半条线:从一个问题到一个可信的回答
改写:把「人话」翻译成「能检索的话」
用户问「那它最快几天生效?」——「它」是谁?向量检索拿着这句去查,查个寂寞。得先用大模型把这句话结合上文改写成独立完整的问题:「域名的最长生效时间是多久?」,再去检索。
别小看这一步,多轮对话里翻车,十次有八次是它没做。用户明明在接着上文聊,你的系统却拿一句指代不明的话裸奔去查库。
检索:把相关的段落捞出来
拿着问题去向量库里找「语义最近」的若干段。但纯向量检索有盲区:错误码、专有名词、型号这类东西在语义空间里没有邻居,反而搜不准;「不支持 X 的方案」常给你召回「支持 X」的段落,就差一个「不」字。
所以成熟的做法是混合检索:向量通道管「意思像的」,关键词通道(BM25)管「字面对得上的」,两边各捞一批。捞回来怎么合并排序?两种分数量纲都不一样,直接加是错的——这里有个叫 RRF 的经典算法,只用排名不用分数,几十行代码,效果立竿见影。第 09 篇讲。
精排:最后一道筛子
检索回来的二三十段里,其实只有三五段真正能回答问题。Rerank 模型干的就是这个筛拣:它把「问题 + 段落」拼在一起仔细读一遍,判断这段话到底能不能回答这个问题,然后重新排序,砍到最精华的几条。
为什么检索阶段不直接用这么准的模型?因为贵和慢——它是逐条实时计算的,只能放在漏斗末端,对已经缩小的候选集下手。「粗召回捞 30 条,精排挑 5 条」,这个两级漏斗是搜索引擎验证了十几年的架构,我们只是把它搬进了 RAG。
生成:让模型开卷答题
最后,把筛好的几段证据编上号,连同问题和一堆纪律性的指令(「只准根据资料回答」「每个论断标注引用编号」「资料不够就说不知道」)一起发给大模型。
这里的核心矛盾是:大模型是个话痨,你让它只根据资料说话,它嘴上答应,手上还是会夹带私货——这就是幻觉。所以防幻觉不能只靠 Prompt 里一句「请勿编造」,得从检索质量、拒答闸门、输出校验、引用展示一层层围堵。第 12 篇专门讲。
把整条线串起来看一遍
拿本博客的真实场景走一遍:
我写过一篇《别让重复请求搞垮你的下单接口》。离线侧,这篇文章被解析成带元数据的 Document,按 Markdown 标题切成若干段(「为什么会出现重复下单」「方案一:数据库唯一索引」……),每段算出向量存进 PGVector。
在线侧,读者问「支付的时候怎么防止用户连点两次」:
- 改写环节发现这是个独立问题,不用改
- 向量检索捞出「方案一:数据库唯一索引」「方案二:Redis 分布式锁」等段落
- 精排后留下最相关的三段
- 生成模型照着这三段写出回答,末尾标注「来源:《别让重复请求搞垮你的下单接口》」
读者点出处,跳回原文。注意这个回答综合了文章里分散在几处的信息——这就是 RAG 相对「关键词搜索 + 跳转链接」的体验代差,它不是给你一个文档链接让你自己翻,是直接给你答案加出处。
每一步的失败会怎么传染
最后送你一张「病理图」,这是我认为这篇最有价值的部分。管道是串行的,上游的病会伪装成下游的症状:
| 症状(你在哪发现的) | 病根可能在 |
|---|---|
| 「检索不准」 | 解析丢了标题层级,切分位置乱,该召回的段落召回不了 |
| 「回答不全」 | 切分把关键句拦腰斩断,证据缺半句 |
| 「调参调不动」 | Embedding 模型烂,语义根本没对齐 |
| 「幻觉严重」 | 检索排错名次,模型看着错误证据侃侃而谈 |
排查第一课
RAG 排查问题,先分层定位:先看数据(原始文章和切分结果肉眼对比),再看检索(该召回的段落到底在不在、排第几),最后才看生成。拿着生成端的症状去改 Prompt,是新手最常见的南辕北辙。
这一篇就到这
我们把这根管道从头到尾走了一遍,每一步都留了一个「它会怎么坏」的钩子。后面的篇章就是挨个把这些钩子展开,配上真代码。
下一篇正式动手:把环境搭起来,做几个「一旦定了就不爱改」的决定——Java AI 版本、PGVector 的安装、Embedding 模型选谁。这几个决定里每个都藏着坑,我先把公认最容易踩的几个给你指出来。
有问题评论区见,尤其是「你这里讲得不对」的那种,欢迎来杠。