RAG的三次进化:从朴素检索到Agentic RAG
你有没有问过ChatGPT一个它不知道的问题,结果它编了一个看起来很真的答案?这就是"幻觉"。RAG(检索增强生成)是解决幻觉最主流的技术。这篇文章从向量检索的数学原理讲起,带你走完RAG从"朴素搜索"到"智能检索"的三次进化。
一、幻觉从何而来?从LLM的本质说起
Day1我们讲过,LLM的本质是"预测下一个字"。问它"2024年诺贝尔物理学奖得主是谁",如果训练数据截至2023年,它不知道答案。但它不会说"我不知道"——因为它的训练数据里很少有"不知道"对应的文本模式。它会从概率分布中挑一个"听起来很合理的答案"输出给你。这就是幻觉。
RAG的思路很朴素:别让LLM凭记忆,先帮它查资料。
普通LLM: 问题 → 回忆训练数据 → 回答(可能编造) RAG: 问题 → 查资料 → 把资料和问题一起给LLM → 基于资料回答二、Level 1:朴素RAG —— 用向量搜索替代关键词搜索
2.1 为什么不直接用关键词搜索?
传统搜索(比如Ctrl+F)的原理是:文档中有和搜索词相同的词,就认为匹配。这有一个根本问题:
用户搜索"怎么让系统更快"——但文档里用的是"性能优化方案"。
关键词完全不同,但实际上在讲同一件事。这就是"语义鸿沟"。
2.2 向量搜索的原理
向量搜索的基本思路是:把文字变成数字,用数字之间的距离来衡量"意思有多接近"。
- 准备文档:把所有文档切成小块(比如每块500字)
- 向量化:用一个"嵌入模型"(Embedding Model),把每块文字变成一串数字(向量)
- 存入数据库:把向量存到支持向量搜索的数据库(pgvector)
- 搜索时:用户问题 → 同样用嵌入模型变成向量 → 在数据库中找最接近的向量 → 返回对应的文档块
2.3 嵌入模型在做什么?
你可能会好奇:文字怎么能变成数字?而且"意思相近"的文字变成数字后自然就"距离接近"?
简单解释:嵌入模型是一个被训练了海量文本的神经网络。训练过程中,经常出现在同一上下文中的词会被"拉近",很少一起出现的词会被"推远"。训练完毕后,这个模型就学会了"词语的语义空间"——相似的词在这个空间中距离相近。
比如"国王 - 男人 + 女人 ≈ 女王"——这不是魔法,是向量空间的运算性质。
2.4 余弦相似度:衡量两个向量的"靠近程度"
用的是余弦相似度(Cosine Similarity),不是欧氏距离。为什么?
想象两个向量代表两篇文档。如果一篇文档很长(向量数值很大),一篇很短,用欧氏距离会认为它们"很远"——但这只是因为长度不同,不代表内容不同。
余弦相似度只看方向不看长度。两个文档讨论相似的话题 → 向量方向接近 → 余弦相似度高。和文档长度无关。
余弦相似度 = cos(θ) = (A·B) / (|A| × |B|) 取值范围: -1 到 1,1表示完全相同,0表示无关2.5 朴素RAG的问题
搜一次,拿到什么用什么。如果搜出来的东西不相关呢?LLM还是会基于这些不相关的材料编答案。这叫"垃圾进,垃圾出"。
而且LLM不知道检索结果是错的。它信任你给它的所有材料。
三、Level 2:Agentic RAG —— 让Agent判断检索质量
3.1 核心改进:加入质量评估
朴素RAG: 问题 → 检索 → 直接回答 Agentic RAG: 问题 → Agent判断是否需要检索? ├─ 不需要 → 直接回答 └─ 需要 → 检索 → Agent检查结果质量 ├─ 质量好(相似度>0.8) → 回答 ├─ 勉强(0.5-0.8) → 改写关键词重搜 └─ 差(<0.5) → 告知用户关键改进是第二处的"Agent判断"。Agent不再盲目接受检索结果——它会检查相似度评分,判断结果是否足够可靠。
3.2 为什么这个改进如此重要?
举个例子。用户问:“Spring AI和LangChain在MCP支持上有什么区别?”
第一次搜索:query="MCP 支持区别" → 5条结果,相似度分别是 0.45, 0.42, 0.38, 0.35, 0.31 → 平均相似度 0.38,远低于阈值 0.5 Agent判断:结果不可靠,改关键词重搜。 第二次搜索:query="Spring AI MCP集成 LangChain MCP集成 对比" → 5条结果,相似度 0.78, 0.75, 0.72, 0.68, 0.65 → 平均 0.72,超过阈值 Agent判断:结果可靠,基于此回答。Agent有了"质量意识",它不再机械地接受第一次结果。这就是Agentic RAG和朴素RAG的本质区别。
3.3 相似度阈值的经验
阈值不是固定值,需要根据嵌入模型调整。
| 相似度 | 含义 | 行动 |
|---|---|---|
| > 0.8 | 高度相关 | 直接回答 |
| 0.5-0.8 | 部分相关 | 考虑重搜 |
| < 0.5 | 基本无关 | 必须重搜或告知用户 |
BGE系列模型在中文上的相似度分布和OpenAI embedding不同,需要实测后调整阈值。
四、切片策略:RAG中最被低估的环节
向量检索的精度是"块"。一个块太大,检索不准;太小,语义不全。
4.1 为什么切片这么重要?
想象你有一个2000字的文档,切成:
- 4块,每块500字 → 粗粒度,搜索可能找不到具体信息
- 40块,每块50字 → 细粒度,但一个完整的段落被切成10块,语义被切断
4.2 我们的切片算法
每块500字符,相邻块重叠50字符。 优先在段落边界(\n\n)处切分 → 保持语义完整。 找不到段落边界 → 按行(\n)切分。 实在找不到 → 强行截断。重叠的作用是"保险"——防止关键信息恰好落在两个chunk的边界上被切断。
4.3 更高级的做法:语义切片
不是固定长度,而是根据语义相似度动态决定切分点。两个相邻句子的embedding向量突然变远了 → 说明话题变了 → 应该从这里切开。但计算成本比固定切片高一个数量级。
五、HNSW索引:为什么搜索这么快?
如果数据库里有100万个向量,每个搜索都要对100万个向量逐个算余弦相似度——那会很慢。
pgvector用HNSW(Hierarchical Navigable Small World)算法构建索引。简单解释:
5.1 通俗理解HNSW
想象你要在100万个陌生人中找和你最相似的人。你会怎么做?
HNSW的做法是:先建一个"社交网络"。每个人认识最相似的16个人(m=16)。搜索时,从上层(稀疏连接)快速定位到目标区域,然后在下层(密集连接)精确搜索。
这和跳表(Skip List)的原理很像:高层做"大方向"导航,底层做"精确查询"。
5.2 我们的配置
CREATEINDEXdocuments_embedding_idxONdocumentsUSINGhnsw(embedding vector_cosine_ops)WITH(m=16,ef_construction=200);m=16:每层每个节点最多16个邻居。越大越精确但索引越大。ef_construction=200:构建时的搜索宽度。越大越精确但构建越慢。
六、三点总结
- 语义检索不等于语义理解:Embedding只是把字符变成了数字。LLM需要判断检索结果是否真的回答了问题。
- Agentic RAG的核心是反馈循环:不是更好的检索算法,而是"检索→评估→修正"的闭环。
- 细节决定效果:切片策略、重叠大小、相似度阈值、索引参数——每个参数都影响最终效果。
项目链接
项目代码:day6-agent