1. 那条流水线为什么谁都能搭,但谁都用不好
RAG 这个词现在确实被说烂了。随便打开一个技术社区,满屏都是"十分钟搭建你的专属知识库""零基础 RAG 实战教程"。我去年帮三个团队做过 RAG 系统的调优,发现一个特别有意思的现象:Demo 跑通的时间越来越短,从半天缩短到半小时;但真正上线后能用的,一个都没有。
问题出在哪?出在绝大多数人搭的那条流水线,本质上是一个"文档切块 → 向量化 → 存库 → 检索 Top-K → 塞进 Prompt"的固定管道。这条管道本身没有任何问题,它是 RAG 的基础骨架。但骨架不等于血肉。你把同样的骨架给一百个人,九十九个人搭出来的东西检索准确率都惨不忍睹,剩下那一个能用的,是因为他在骨架的六个关键节点上做了完全不同的选择。
这六个节点,才是 RAG 真正的分水岭。它们分别是:切块策略的语义完整性、检索阶段的召回质量、上下文组装的信息密度、知识图谱的引入时机、Agent 化的工作流编排、以及评估体系的闭环设计。前三个决定了你的 RAG 能不能"查得准",后三个决定了它能不能"用得久"。
这篇文章不打算再给你一条流水线。流水线你已经有了,网上到处都是。我要做的是把这六个分水岭逐一拆开,告诉你每个节点上"大多数人怎么做"和"真正能用的系统怎么做"之间的差距在哪里,以及为什么会有这个差距。如果你正在做 RAG 项目,或者做完了发现效果不行想推倒重来,这篇内容应该能帮你省下至少两个月的试错时间。
2. 切块不是切菜:语义完整性决定了检索的天花板
2.1 固定长度切块为什么是灾难的开始
几乎所有入门教程教你的第一件事就是:把文档按 500 字或者 1000 字切一块,重叠 50 到 100 字。这个做法在 Demo 阶段看起来没问题,因为你的测试文档可能只有几页,内容主题集中,怎么切都能检索到。但一旦文档量上到几百上千页,问题就暴露了。
我拿一个真实的案例来说明。有个团队做的是企业内部制度问答,文档是员工手册、报销规范、考勤制度这类。他们用 500 字固定切块,结果员工问"出差住宿标准是多少",检索出来的块是这样的:
……员工出差期间产生的交通费用,按照实际发生金额凭票报销。住宿费用标准按照职级划分,具体标准见下表。出差补贴按天计算……
然后下一块是:
……(表格内容)职级 A:一线城市 600 元/晚,二线城市 400 元/晚……
问题来了:检索系统只召回了第一块,因为"出差住宿标准"这几个字在第一块里出现了。但真正的答案在第二块的表格里。大模型拿到第一块,只能回答"住宿费用标准按照职级划分,具体标准见下表"——它看不到那个表。
这就是固定长度切块最致命的问题:它假设"语义单元"和"字符长度"是对齐的,但现实中这两者几乎从来不对齐。一个完整的制度条款可能只有 200 字,也可能有 1500 字。你按 500 字切,要么把一个完整语义切碎,要么把多个不相关的语义混在一起。
2.2 按语义边界切块的三种可落地方法
那怎么切才对?核心原则只有一个:让每一个块都是一个自包含的语义单元。具体落地有三种方法,复杂度从低到高。
第一种是基于文档结构的切块。如果你的文档是 Markdown、HTML 或者有明确标题层级的格式,直接按标题层级切。一个三级标题下的内容就是一个块,如果太长再按段落二次切分。这个方法实现成本最低,效果提升却最明显。我试过在一个技术文档库上做对比,同样的检索模型,按标题切块比固定长度切块的召回准确率高了将近 30 个百分点。
第二种是基于语义相似度的切块。把文档按句子拆开,然后计算相邻句子的 embedding 相似度,在相似度骤降的地方切一刀。这个方法的好处是不依赖文档格式,纯文本也能用。但要注意一个坑:相似度阈值不能设死,不同文档的最佳阈值差异很大。我的经验是先用 0.6 试,然后人工看 20 个切块结果,根据实际情况调整。
第三种是基于大模型判断的切块。直接把文档给大模型,让它判断在哪里切分最合理。这个方法效果最好,但成本也最高。适合文档量不大但对精度要求极高的场景,比如法律合同、医疗指南这类。
2.3 切块时最容易忽略的元数据附加
切完块之后,大多数人直接把文本丢进向量库就完事了。但真正好用的系统,会在每个块上附加丰富的元数据。这些元数据在检索阶段能发挥的作用,远超你的想象。
我通常会附加这几类元数据:
| 元数据类型 | 具体内容 | 检索时的作用 |
|---|---|---|
| 来源信息 | 文件名、章节路径、页码 | 支持按来源过滤,回答时可引用出处 |
| 结构信息 | 块在文档中的位置、前后块 ID | 支持上下文扩展,检索到一块可拉出相邻块 |
| 语义标签 | 主题分类、实体列表 | 支持按主题预过滤,减少无关召回 |
| 时间信息 | 文档创建/更新时间 | 支持时效性排序,旧文档降权 |
尤其是"前后块 ID"这个设计,解决了一个非常实际的问题:当检索到的块信息不完整时,系统可以自动把它的前后块也拉出来,拼成一个更大的上下文。这比单纯增大切块尺寸要精准得多,因为它只在需要的时候才扩展,不会污染所有检索结果。
3. 检索阶段:为什么你的 Top-K 里全是垃圾
3.1 纯向量检索的盲区在哪里
向量检索的本质是语义相似度匹配。你问"如何申请年假",它能找到"年假申请流程"相关的块,这没问题。但它有两个盲区。
第一个盲区是精确匹配失效。用户问"报销单编号规则是什么",向量检索可能会召回一堆"报销流程""报销标准"的块,因为它们在语义上都很接近。但真正包含"编号规则"的块,可能因为表述是"单据编码格式为……"而排在后面。向量模型对这类精确术语的匹配能力很弱。
第二个盲区是否定和条件检索失效。用户问"哪些费用不能报销",向量检索会召回大量"可以报销的费用"的块,因为"报销"这个核心词太强了,否定的语义在向量空间里几乎被淹没。
3.2 混合检索的权重调参实战
解决这两个盲区的标准方案是混合检索:向量检索 + 关键词检索(通常是 BM25),然后做融合排序。但这里有一个关键问题:两者的权重怎么定?
我见过很多团队直接设 0.5 比 0.5,然后就不管了。这是典型的"配置了但没调优"。实际上,权重应该根据你的查询类型动态调整。我的做法是引入一个轻量的查询分类器,判断用户问题属于哪种类型:
- 概念解释型("什么是 XX"):向量检索权重 0.7,BM25 权重 0.3
- 精确查找型("XX 的编号是多少"):向量检索权重 0.3,BM25 权重 0.7
- 条件筛选型("哪些情况不能 XX"):向量检索权重 0.4,BM25 权重 0.6,同时启用否定词增强
这个分类器不需要很复杂,用几个关键词规则就能覆盖 80% 的情况。我实测下来,动态权重比固定权重在混合检索上的准确率提升了 15% 到 20%。
融合排序的算法也有讲究。最常见的是 RRF(Reciprocal Rank Fusion),它只看排名不看分数,对分数尺度不敏感,比较稳。但如果你能确保两路检索的分数都做了归一化,加权求和的效果会更好,因为它保留了分数差异的信息。
3.3 重排序模型:小成本大收益的典范
混合检索之后,你通常会拿到 20 到 50 个候选块。这时候直接取 Top-5 塞给大模型,是对候选池的极大浪费。重排序模型(Reranker)是 RAG 流水线里性价比最高的一环,没有之一。
它的原理很简单:用一个交叉编码器(Cross-Encoder)把查询和每个候选块拼在一起打分,而不是像向量检索那样分别编码再算距离。交叉编码器能看到查询和文档之间的细粒度交互,所以排序精度远高于向量检索。代价是它不能预计算,必须在线推理,所以只适合对少量候选做精排。
我通常的配置是:混合检索召回 30 个候选,重排序后取前 5 个。这个配置下,重排序带来的准确率提升通常在 20% 到 40% 之间,而增加的延迟只有 100 到 300 毫秒。相比它带来的效果提升,这个延迟完全可以接受。
注意:重排序模型的选择要和你的语言匹配。中文场景下,用多语言模型或者专门的中文重排序模型,效果会比纯英文模型好很多。我试过用英文模型做中文重排序,效果还不如不做。
4. 上下文组装:把正确的信息放在正确的位置
4.1 检索到了不等于用上了
这是一个非常容易被忽略的环节。很多人以为只要检索到了正确的块,大模型就一定能用上。但实际情况是:大模型对上下文的利用效率,高度依赖于信息的排列方式和呈现形式。
我做过一个对比实验。同样的 5 个检索块,同样的查询,只是改变块在 Prompt 中的排列顺序,回答准确率相差了将近 25%。大模型存在明显的"位置偏好"——它更容易关注上下文开头和结尾的信息,中间部分容易被忽略。这就是所谓的"迷失在中间"现象。
所以上下文组装的第一条原则是:把最相关的块放在最前面和最后面,次相关的放中间。具体操作上,重排序后的结果不要按顺序平铺,而是做一个"三明治"排列:第 1 名放开头,第 2 名放结尾,第 3 到 5 名放中间。
4.2 上下文压缩:少即是多的信息密度优化
另一个常见问题是上下文太长。你检索了 10 个块,每个块 500 字,加起来 5000 字,全塞进 Prompt。结果大模型被大量无关信息干扰,反而抓不住重点。
上下文压缩的思路是:对每个检索块,只保留和查询相关的部分。实现方式有两种。一种是抽取式压缩,用一个小模型判断块中哪些句子和查询相关,只保留相关句子。另一种是生成式压缩,让大模型把块压缩成和查询相关的摘要。
抽取式压缩更安全,不会引入幻觉,但可能丢失一些隐含信息。生成式压缩信息密度更高,但有引入错误的风险。我的建议是:对事实性要求高的场景用抽取式,对理解性要求高的场景用生成式。如果拿不准,就先用抽取式,稳定之后再考虑混合方案。
4.3 引用标注:让回答可追溯的工程实现
一个真正能用的 RAG 系统,回答必须可追溯。用户看到答案后,应该能知道这个信息来自哪个文档的哪个部分。这不仅是信任问题,也是排错的基础——当回答错误时,你需要快速定位是检索错了还是生成错了。
实现引用标注的关键是:在组装上下文时,给每个块打上明确的编号标记,并在 Prompt 中要求大模型在回答时引用编号。比如:
[1] 员工出差住宿标准:一线城市 600 元/晚…… [2] 出差补贴按天计算,标准为……然后在 Prompt 里明确要求:"回答时请在相关句子末尾标注信息来源编号,如 [1][2]。"
这里有个坑:大模型有时候会编造编号,引用一个不存在的来源。解决办法是在后处理阶段做校验,检查回答中出现的所有编号是否都在实际提供的编号范围内。如果有越界的,要么重新生成,要么把越界的引用标记去掉。
5. 知识图谱:什么时候该上 GraphRAG,什么时候不该
5.1 GraphRAG 解决的是什么问题
GraphRAG 这两年被讨论得很多,但很多人对它有一个误解,以为它是 RAG 的升级版,上了它效果就会更好。GraphRAG 不是万能药,它解决的是一个非常具体的问题:多跳推理和全局性查询。
什么叫多跳推理?举个例子。你问"张三负责的项目里,有哪些用到了李四团队开发的组件?"这个问题需要先找到"张三负责的项目",再找到这些项目用到的组件,再找到"李四团队开发的组件",最后求交集。纯向量检索很难处理这种需要串联多个实体关系的查询,因为它每次只能召回和查询语义相似的块,无法沿着关系链跳转。
GraphRAG 的做法是:先从文档中抽取实体和关系,构建知识图谱,然后在检索时沿着图谱的边做多跳遍历。这样就能把分散在不同文档块中的关联信息串联起来。
5.2 构建知识图谱的三个现实成本
但 GraphRAG 的代价也很明显,主要体现在三个方面。
第一是抽取成本。从文档中抽取实体和关系,目前最可靠的方法还是用大模型。这意味着你需要对全量文档跑一遍大模型抽取,成本可能是普通 RAG 的几十倍。而且抽取质量高度依赖 Prompt 设计,抽出来的图谱经常有大量噪声。
第二是维护成本。文档更新时,图谱也要同步更新。新增一个实体,可能要建立十几条边;删除一个实体,要清理所有相关边。这个维护逻辑比向量库的增删改查复杂得多。
第三是查询成本。图谱查询需要专门的图数据库和查询语言,多跳遍历的延迟也远高于向量检索。如果你的场景对响应时间敏感,GraphRAG 可能会成为瓶颈。
5.3 我的判断标准:什么场景值得上图谱
基于这几个成本,我通常用三个标准来判断一个场景是否值得上 GraphRAG:
- 查询中多跳推理的占比是否超过 30%:如果大部分查询都是单跳的事实查找,GraphRAG 的收益覆盖不了成本。
- 实体关系是否密集且明确:法律、医疗、金融这类领域,实体和关系定义清晰,图谱构建质量高。而散文、新闻这类文本,实体关系模糊,图谱噪声大。
- 是否有持续的维护投入:图谱不是建完就完的,需要持续维护。如果没有专人负责,图谱会很快腐化,效果反而不如纯向量检索。
如果这三个条件都满足,GraphRAG 值得上。如果只满足一两个,我建议先用混合检索 + 重排序把基础打牢,等基础 RAG 的效果确实遇到瓶颈了,再考虑图谱。
6. Agentic RAG:让检索从一次性动作变成迭代过程
6.1 传统 RAG 的单次检索为什么不够
传统 RAG 的检索是一次性的:用户提问 → 检索 → 生成 → 结束。这个流程假设"一次检索就能拿到所有需要的信息",但现实中这个假设经常不成立。
比如用户问"我们公司和竞争对手在产品定价上的差异是什么?"这个问题需要先检索自己公司的定价,再检索竞争对手的定价,然后做对比。单次检索只能召回和整个问题语义相似的块,很可能只召回了自己公司的定价信息,竞争对手的信息因为表述差异没被召回。
Agentic RAG 的核心思想是:把检索变成一个由 Agent 控制的迭代过程。Agent 可以先分析问题,拆解成子问题,逐个检索,然后判断信息是否足够,不够就换个查询再检索,直到信息充分了再生成回答。
6.2 用 LangGraph 编排检索工作流的实操结构
LangGraph 是目前做 Agentic RAG 比较顺手的框架,它的核心概念是"状态图"——你定义一个状态结构,然后定义节点和边,节点之间通过状态传递信息。
一个典型的 Agentic RAG 工作流包含这几个节点:
- 查询分析节点:判断问题类型,决定是否需要拆解
- 查询改写节点:把原始问题改写成更适合检索的形式
- 检索节点:执行检索,返回候选块
- 充分性判断节点:判断检索结果是否足够回答问题
- 补充检索节点:如果不够,生成新的查询再次检索
- 生成节点:信息充分后生成最终回答
这个工作流的关键在于"充分性判断"节点。它决定了什么时候停止迭代。判断逻辑可以用大模型来做,给它原始问题和已检索到的信息,让它判断"这些信息是否足以回答该问题"。如果不够,还要让它指出"缺少什么信息",这样补充检索节点才能生成有针对性的新查询。
6.3 迭代终止条件与成本控制的平衡
Agentic RAG 最大的风险是无限迭代。如果充分性判断一直说"不够",Agent 就会一直检索下去,成本和延迟都会失控。
我的做法是设置三重终止条件:
- 最大迭代次数:通常设 3 到 5 次,超过就强制生成
- 信息增量阈值:如果新一轮检索没有带来新的有效信息,就停止
- 置信度阈值:如果充分性判断的置信度超过某个值,就认为够了
这三重条件同时生效,任何一个满足就终止。实测下来,大部分查询在 2 到 3 轮内就能收敛,只有极少数复杂查询会触发最大迭代限制。
提示:Agentic RAG 的延迟通常是传统 RAG 的 3 到 5 倍。如果你的场景对响应时间要求很高(比如实时客服),要慎重使用。它更适合那些对准确性要求高、对延迟容忍度也高的场景,比如研究报告生成、复杂问题分析。
7. 评估闭环:没有度量就没有优化
7.1 RAG 评估的三个核心指标
RAG 系统的评估和传统模型评估不一样,它要同时评估检索质量和生成质量。我通常用三个核心指标:
检索命中率(Hit Rate):正确答案所在的块是否出现在检索结果中。这个指标衡量的是检索的召回能力。如果命中率低,后面所有环节都是白搭。
检索精确率(Precision):检索结果中有多少是真正相关的。这个指标衡量的是检索的噪声水平。精确率低会导致上下文被无关信息污染。
回答忠实度(Faithfulness):生成的回答是否完全基于检索到的内容,没有编造。这个指标衡量的是幻觉程度。忠实度低意味着系统在"胡说八道"。
这三个指标要分开测,不能混在一起。因为它们的优化方向不同:命中率低要改切块和检索策略,精确率低要加重排序和过滤,忠实度低要改 Prompt 和生成策略。
7.2 构建评估集的低成本方法
评估 RAG 系统需要评估集,但人工标注评估集成本很高。我的做法是半自动构建:
先从真实用户查询中采样 100 到 200 条,覆盖各种查询类型。然后对每条查询,人工标注它对应的正确文档块。这一步是必须人工的,但工作量可控。最后用大模型对每条查询生成一个参考答案,人工审核修正。
这个评估集建好之后,每次系统改动都跑一遍,看三个指标的变化。没有评估集的 RAG 优化就是盲人摸象,你永远不知道改动是变好了还是变差了。
7.3 持续监控:上线后的效果衰减与应对
RAG 系统上线后,效果会随着时间衰减。原因有两个:一是文档在更新,旧的切块和索引可能过时了;二是用户的查询分布在变化,新的查询类型可能没有被覆盖到。
所以上线后的持续监控很重要。我通常会监控这几个信号:
- 检索命中率的周环比变化:如果持续下降,说明文档或查询分布发生了变化
- 用户反馈的负样本率:用户点"踩"的比例,是最直接的信号
- 无答案率:系统回答"根据现有信息无法回答"的比例,如果升高说明检索覆盖不足
当这些信号出现异常时,就要触发重新索引或者调整检索策略。我一般建议每个月做一次全量评估,每周看一次监控指标。
8. 六个分水岭的优先级排序与落地建议
8.1 如果只能改一处,先改哪里
如果你现在的 RAG 系统效果不好,但资源有限只能改一处,我的建议是:先改切块策略。切块是检索的天花板,切块不对,后面所有优化都是在错误的基础上修修补补。而且切块优化的成本最低,不需要额外模型,不需要额外算力,只需要重新设计切分逻辑。
第二优先的是重排序。它的投入产出比最高,加一个重排序模型,准确率通常能提升 20% 以上,而工程改动量很小。
第三优先的是混合检索。它解决的是向量检索的盲区问题,对精确查找和条件筛选类查询提升明显。
8.2 不同规模团队的差异化路线
小团队(1 到 2 人)建议走"切块优化 + 混合检索 + 重排序"的路线,把基础打牢,先不要碰图谱和 Agent。这三个环节做好,大部分场景的效果已经够用了。
中等团队(3 到 5 人)可以在基础之上加 Agentic RAG,用 LangGraph 做迭代检索。同时开始建评估集,用数据驱动优化。
大团队(5 人以上)可以考虑 GraphRAG,但前提是有专人负责图谱的构建和维护。同时要建立完整的评估和监控体系,保证系统长期稳定。
8.3 我踩过的三个印象最深的坑
第一个坑是过度依赖向量检索。早期我做 RAG 只用向量检索,觉得 embedding 模型够强就行了。结果在一个法律文档场景上,用户问"合同法第 52 条规定的无效情形有哪些",向量检索完全找不到正确的块,因为"第 52 条"这种精确引用在向量空间里没有区分度。后来加了 BM25 才解决。
第二个坑是忽略上下文顺序。有段时间我发现检索明明是对的,但回答总是漏掉关键信息。排查了很久才发现,关键信息被放在了上下文的中间位置,大模型没注意到。调整排列顺序后问题就解决了。
第三个坑是没有评估集就盲目调参。我曾经花了两周时间调各种参数,感觉效果在变好,但上线后用户反馈还是很差。后来建了评估集一测,发现我调的参数在评估集上根本没有提升,之前的"感觉变好"完全是错觉。
这三个坑的共同点是:它们都不是技术难题,而是认知盲区。你不知道有这个问题,就永远不会去解决它。希望这篇内容能帮你把这些盲区补上。
最后分享一个我一直在用的小技巧:每次改动 RAG 系统之前,先跑一遍评估集,记录当前指标。改完之后再跑一遍,对比指标变化。如果指标没提升,不管你觉得改动多合理,都回滚。这个习惯帮我避免了很多次"自以为在优化,实际在退步"的情况。