17.一个RAG系统的完整
文章目录
📚 从文档到答案:一个RAG系统的完整故事

第一章:准备工作——把文档”搬进”系统
想象你有一个大书架,上面堆满了各种资料。现在你要做一个智能助手,能让用户问问题,然后从这些资料里找到答案。
第一步:把书拆成”纸条”
你不可能把整本书直接塞给系统,因为:
- 系统一次处理不了太多文字
- 用户问的是具体问题,你需要找到具体段落
所以你要做的是:**把每本书拆成一张张”小纸条”**,每张纸条大概几百个字。
比如一本5000字的说明书,你会拆成10张纸条,每张500字左右。而且为了让上下文不丢失,相邻两张纸条之间会重叠几十个字——就像你撕纸条时,两张之间会有一部分连着。
第二步:给每张纸条做三份”档案”
这是最核心的一步。每张纸条产生后,你同时给它做三份不同的”档案”,分别放进三个不同的”柜子”里:
档案一:做一张”关键词地图”(倒排索引)
你拿一张纸条,比如内容是”苹果手机性能很强,拍照效果很好”。你用Jieba(中文分词工具)把它切成词:”苹果”、”手机”、”性能”、”很强”、”拍照”、”效果”、”很好”。
然后你在一本大笔记本上记下来:
- “苹果” → 这张纸条的编号
- “手机” → 这张纸条的编号
- “性能” → 这张纸条的编号
- …
这个笔记本就叫倒排索引。它的作用是:当用户搜索”手机”时,你能立刻翻到”手机”这一页,看到所有包含”手机”的纸条编号。
档案二:把纸条原封不动存起来(正排索引)
你拿一个盒子,把纸条原文”苹果手机性能很强,拍照效果很好”完整地放进去,在盒子外面贴上纸条编号。
这个盒子就叫正排索引。它的作用是:当你确定了要拿哪几张纸条时,能立刻根据编号找到原文内容。
档案三:给纸条拍一张”语义照片”(向量化)
这一步最神奇。你用一个叫Embedding模型的”翻译官”,把纸条的内容翻译成一组数字。这组数字大概有几百个,比如 [0.12, -0.56, 0.78, ...]。
这组数字不是乱码,它代表了这段话的”语义位置”。什么意思呢?你可以想象一个巨大的地图:
- “苹果手机”和”华为手机”的语义位置离得很近
- “苹果手机”和”西红柿炒蛋”的语义位置离得很远
然后你把这张”语义照片”连同纸条编号一起,存进一个专门的仓库——向量数据库。
第二章:用户提问——系统开始”破案”
现在,一个用户来了,他问:”苹果手机值得买吗?”
第三步:把问题也”处理”一遍
系统拿到问题后,做两件事:
第一件事:用Jieba切词
把”苹果手机值得买吗”切成:”苹果”、”手机”、”值得”、”买”。
第二件事:用Embedding翻译官翻译
把整个问题”苹果手机值得买吗”翻译成一组数字(向量),这组数字代表了问题的”语义位置”。
第四步:兵分两路,同时搜索
系统派出两路人马,同时去找答案:
一路人马:拿着”关键词地图”搜索
这路人拿着切好的词——“苹果”、”手机”、”值得”、”买”——去翻之前那本大笔记本(倒排索引)。
- 翻到”苹果”这一页,看到有纸条A、纸条C
- 翻到”手机”这一页,看到有纸条A、纸条B、纸条D
- 翻到”值得”这一页,发现没有
- 翻到”买”这一页,发现也没有
然后统计:纸条A出现了两次(苹果和手机),纸条B出现一次,纸条C一次,纸条D一次。按出现次数排序,纸条A排第一。
这路人找到的结果,是靠”关键词匹配”的。它的优点是精确——用户问”苹果”,它就能找到包含”苹果”的纸条。缺点是太死板——如果用户问”iPhone”,而文档里写的是”苹果手机”,它就找不到了。
另一路人马:拿着”语义照片”去向量仓库比对
这路人拿着问题的”语义照片”(向量),走进向量数据库仓库。仓库里存着所有纸条的”语义照片”。这路人说:”帮我找一下,哪些照片跟我的这张最像?”
仓库里的系统开始计算:问题照片跟纸条A的照片距离多远?跟纸条B呢?跟纸条C呢?……然后选出距离最近的10张。
这路人找到的结果,是靠”语义相似度”的。它的优点是聪明——用户问”iPhone”,它能把写有”苹果手机”的纸条也找出来,因为它俩的语义位置离得很近。缺点是可能漏掉一些精确的关键词。
第五步:把两路结果”融合”在一起
现在你有两份名单了:
- 关键词那路:纸条A(第1名)、纸条B(第2名)、纸条C(第3名)……
- 语义那路:纸条E(第1名)、纸条A(第2名)、纸条F(第3名)……
两份名单各有各的好,你怎么合并呢?
你用RRF(倒数排名融合)算法:给每张纸条算一个综合得分。大致逻辑是:
- 纸条A在两边都排得很靠前 → 得分非常高 → 排第一
- 纸条B只在关键词那路排第二 → 得分中等
- 纸条E只在语义那路排第一 → 得分也不低
最后你选出综合得分最高的5张纸条,作为”最佳候选”。
这就是混合搜索——关键词搜索和语义搜索都用了,取长补短,又准又全。
第三章:生成答案——把”候选”变成”回答”
第六步:取出纸条原文
你现在手里有5个纸条编号:A、B、E、G、H。
你走到正排索引那个盒子前,按照编号:
- 拿出纸条A,上面写着”苹果手机性能很强,拍照效果很好,电池续航也不错……”
- 拿出纸条B,上面写着”苹果手机价格较高,但保值率不错……”
- 拿出纸条E,上面写着”新款iPhone在处理器性能上领先安卓旗舰……”
- ……
你把这5张纸条的原文全部拿出来了。
第七步:把原文和问题一起”喂”给大模型
你写一段话(这叫Prompt),大概长这样:
1 | 你是我的助手。下面是一些参考资料,请根据这些资料回答用户的问题。 |
然后你把这段话发给大模型(比如GPT-4)。
第八步:大模型给出最终答案
大模型看完后,给出回答:
“根据资料,苹果手机在性能、拍照和保值率方面表现不错,但价格相对较高。如果你看重性能和保值,且预算充足,它是值得考虑的选择。不过建议结合自己的实际需求来判断。”
第九步:把答案和引用一起返回给用户
你不仅把答案还给用户,还贴心地附上”我根据以下资料回答的”:
1 | 答案:……(上面那段话) |
这样用户就知道答案是从哪儿来的,可信度更高。
🎯 整个流程的核心逻辑,用三句话总结
入库时:把文档切成小段 → 同时做三件事(①切词做关键词地图,②存原文,③拍照做语义照片)→ 分别放进三个仓库。
查询时:用户问题 → 同时走两条路(①关键词地图找,②语义照片比对)→ 两路结果融合 → 选出最佳候选。
出答案时:按编号取出原文 → 把原文和问题一起给大模型 → 大模型给出答案 → 答案+来源一起返回。
💡 几个容易混淆的地方,我再说清楚
一问:向量数据库里存的是原文还是分词?
答:存的是原文。分词的词元只用来做”关键词地图”(倒排索引)。向量化是对整段原文做的,不是对词元做的。就像你给一个人拍照,拍的是整个人,不是把他的名字拍进去。
二问:Jieba到底在哪儿用?
答:用两次。第一次在入库时,用来做”关键词地图”(倒排索引)。第二次在查询时,用来处理用户问题里的关键词。它的作用就是”切词”,把句子切成有意义的词。
三问:为什么不能只用一种搜索方式?
答:关键词搜索像”老实人”,你说”苹果”它就找”苹果”,但你说”iPhone”它就傻了。语义搜索像”聪明人”,你说”iPhone”它能找到”苹果手机”,但可能漏掉一些包含精确型号的文档。两个都用,互相补短板。
四问:最终给大模型的是向量还是原文?
答:给的是原文。向量只是用来”找位置”的,找到位置后你要把那里的”实物”(原文)拿出来。就像导航找到了一个地址,你得开车到那个地方,而不是只看地图上的点。