1. 那条流水线确实烂大街了,但分水岭从来不在流水线上
如果你最近半年逛过技术社区,大概率会有一种错觉:RAG 已经被讲烂了。随便打开一篇文章,都是“文档切块 → 向量化 → 存向量库 → 检索 Top-K → 拼进 Prompt → 调 LLM 生成”这六步。教程铺天盖地,框架层出不穷,Demo 一跑就通,于是很多人得出一个结论:RAG 没有门槛了。
我一开始也这么想,直到我把同一套流水线分别用在“公司内部制度问答”和“跨部门技术方案检索”两个场景上,前者效果还行,后者答得一塌糊涂。同样的切块大小、同样的嵌入模型、同样的 Top-K,为什么差距这么大?后来我花了不少时间复盘,才意识到一个被大多数人忽略的事实:烂大街的从来不是 RAG 本身,而是那条最朴素的 Naive RAG 流水线。真正决定一个 RAG 系统能不能用的,是流水线之外的六个分水岭。
这篇文章不打算再教你搭一遍最基础的 RAG,那种内容你随便搜都有。我想聊的是:当你已经跑通了 Demo,准备把它做成一个真正能用的东西时,会在哪六个地方卡住,以及每个地方背后的取舍逻辑是什么。关键词里提到的 Contextual Retrieval、Agentic RAG、GraphRAG、DeepWiki、ontology rag 这些概念,本质上都是在这六个分水岭上做文章。适合已经写过至少一个 RAG Demo、但发现效果不稳定、想搞清楚“下一步该往哪优化”的开发者。
先说结论,这六处分水岭分别是:切块策略、检索的上下文补全、检索结果的融合与重排、知识的结构化表达、检索与推理的编排方式、以及评估与迭代闭环。下面逐个拆。
2. 第一道分水岭:切块不是切菜,切错了后面全白搭
2.1 固定长度切块为什么在真实文档上必然翻车
几乎所有入门教程都教你按 500 或 1000 个字符切块,加个重叠窗口。这个做法在结构规整的文档上勉强能用,但真实世界的文档是什么样?是带层级标题的 PDF、是嵌套表格的 Word、是代码和说明混排的 Markdown、是问答对形式的客服记录。你按固定长度一刀切下去,最直接的后果就是语义被拦腰截断。
我踩过一个很典型的坑:一份技术规范文档里,某个参数的定义在上一段末尾,它的取值范围和约束条件在下一段开头。固定切块正好从中间切开,检索时命中了“定义”那一块,但“取值范围”那块没被召回,模型就凭定义硬编了一个范围出来。这种错误最可怕的地方在于,它答得非常自信,你不去核对原文根本发现不了。
固定长度切块的另一个问题是块内信息密度极不均匀。有的块全是废话铺垫,有的块塞了三个关键结论。向量化之后,废话块的嵌入被稀释,关键块的嵌入被平均,检索质量自然上不去。
2.2 结构感知切块:先读懂文档再动手
正确的做法是先解析文档结构,再决定切点。对于 Markdown 和带标题的文档,按标题层级切是最自然的;对于 PDF,得先做版面分析,识别出标题、正文、表格、图注;对于代码文档,按函数或类切。这一步的投入产出比极高,因为切块质量直接决定了检索的上限——检索永远不可能召回一个根本没被正确切出来的块。
具体操作上,我一般会保留每个块的“面包屑路径”,也就是它从属的章节标题链。比如一个块属于“第三章 → 3.2 节 → 参数配置”,这个路径会作为元数据存下来,检索时一起带进 Prompt。这样即使块本身很短,模型也能知道它在文档里的位置。
2.3 块大小到底怎么定:一个被问烂但没有标准答案的问题
“块多大合适”这个问题我被问过无数次。我的回答永远是:取决于你的查询粒度和文档粒度,没有万能值。但有一个可操作的判断方法——看你的典型问题需要多少上下文才能回答。
如果用户问的是“XX 功能的开关叫什么名字”,答案就在一个短语里,块可以小到 200 字。如果用户问的是“XX 方案为什么选 A 不选 B”,答案需要一整段论证,块就得到 800 到 1500 字。我通常的做法是准备两套粒度:小块用于精准命中,大块(或小块加相邻块)用于提供完整上下文。这就是所谓的“父子块”或“小块检索、大块喂给模型”策略。
提示:不要迷信任何博客里给的“最佳块大小”。先拿你真实的 20 个问题去测,看命中率,再调。脱离数据谈参数都是耍流氓。
2.4 表格、图片、代码这些“非文本”怎么处理
热词里有人问“rag 知识库能存储图片嘛”,这其实是个好问题。图片本身不能直接进向量库,但可以走多模态嵌入,或者用视觉模型生成图片描述再嵌入。表格更麻烦,直接转文本会丢失行列对应关系,我的做法是把表格转成 Markdown 或 JSON 保留结构,同时生成一段自然语言摘要一起存。
代码块则要单独处理,因为代码的语义和自然语言差别很大,用同一个嵌入模型效果往往不好。有条件的话,代码用专门的代码嵌入模型,或者至少在切块时保证函数完整。
3. 第二道分水岭:检索到的块,缺了它赖以成立的上下文
3.1 一个块的“孤立感”是怎么毁掉答案的
这是我认为最被低估的一道分水岭。你检索到一个块,它写着“该值默认为 30,超过会导致超时”。问题来了:“该值”是什么值?“超时”是哪个环节的超时?这个块单独拿出来,信息是不完整的,因为它的指代对象在上一段。
Naive RAG 直接把这样的块拼进 Prompt,模型只能靠猜。猜对了是运气,猜错了就是幻觉。这就是为什么很多 RAG 系统在 Demo 上表现不错,一到真实文档就露馅——Demo 文档往往自包含,真实文档充满了跨段指代。
3.2 Contextual Retrieval:给每个块补一句“它讲的是什么”
Anthropic 提出的 Contextual Retrieval 思路很值得借鉴:在嵌入和存储每个块之前,先用 LLM 给这个块生成一段简短的上下文说明,把它的指代对象、所属主题补全,然后把这段说明拼在块前面一起嵌入。
比如上面那个块,补全后变成:“在‘请求超时配置’章节中,关于 timeout 参数的说明:该值默认为 30 秒,超过会导致请求超时。”这样一来,即使块被单独召回,语义也是完整的。代价是预处理阶段要为每个块调一次 LLM,成本不低,但换来的是检索准确率的明显提升。我的实测经验是,在指代密集的文档上,这个改动能把命中率拉高一大截。
3.3 元数据不是装饰品,是检索的第二条腿
很多人存块的时候只存文本和向量,元数据随便填。这是浪费。元数据至少应该包含:来源文档、章节路径、块类型(正文/表格/代码)、时间戳、权限标签。检索时可以用元数据做过滤,比如“只在最新版本的文档里搜”“只搜有权限看的块”。
更进阶的用法是混合检索:向量检索负责语义相似,关键词检索(BM25)负责精确匹配。两者结果融合,能覆盖单一方法的盲区。比如用户搜一个具体的错误码,向量检索可能召回一堆语义相近但不含该错误码的块,而 BM25 能精准命中。融合策略我一般用 RRF(倒数排名融合),简单且稳。
3.4 重排:把“差不多相关”和“真的相关”分开
检索回来的 Top-K,排序往往不准。向量相似度高不代表真的能回答问题。这时候需要一个重排模型(Reranker),对候选块做一次精排。重排模型通常是交叉编码器,把查询和块一起编码,判断相关性,比单纯的向量点积准得多。
我的标准流程是:向量检索召回 20 到 50 个候选,重排后取前 3 到 5 个喂给模型。召回阶段要宽,重排阶段要严。这个组合在大多数场景下比直接取 Top-3 效果好很多。重排模型可以本地部署,也可以用 API,看你的延迟和成本预算。
4. 第三道分水岭:知识是散的还是连的,决定了它能答多深
4.1 向量库擅长“找相似”,不擅长“找关系”
向量检索的本质是语义相似度匹配。它能很好地回答“有没有讲过类似的东西”,但回答不了“A 和 B 之间是什么关系”“从 A 能不能推导到 C”。当你的问题需要跨多个文档、多个实体做关联推理时,纯向量检索就力不从心了。
举个例子:用户问“我们用的那个消息队列,和订单服务之间的依赖关系是怎样的”。答案可能散在架构文档、部署文档、故障复盘三个地方,每个地方都提到了一部分。向量检索可能只召回其中一个,因为它不知道这三块讲的是同一个关系。
4.2 GraphRAG 和 ontology rag 到底在解决什么
GraphRAG 的思路是把文档里的实体和关系抽出来,构建一个知识图谱,检索时既走向量也走图。ontology rag 更进一步,先定义好领域本体(有哪些实体类型、哪些关系类型),再按本体去抽取,保证图谱结构的一致性。
这俩概念听起来高大上,但核心动机很朴素:把“散落的知识”变成“连起来的知识”。当知识以图的形式组织,检索就可以做多跳推理——从“消息队列”跳到“订单服务”,再跳到“依赖的配置项”。
不过我要泼盆冷水:GraphRAG 不是银弹。构建图谱的成本很高,抽取质量依赖 LLM,而且不是所有场景都需要。如果你的问题大多是单点事实查询,向量检索加好的切块就够了。只有当你的问题明显需要跨实体、跨文档推理时,图谱的投入才划算。
4.3 结构化知识库和向量知识库,不是替代是互补
热词里有人问“kg 知识库、rag 知识库和结构知识库区分以及应用场景”。我的理解是:结构化知识库(比如数据库、知识图谱)擅长精确查询和关系推理,向量知识库擅长模糊语义匹配。真实系统里两者往往并存。
我的做法是:能用结构化查询解决的,优先走结构化;剩下的模糊问题走向量。比如“上个月销售额是多少”直接查数据库,别绕 RAG;“为什么上个月销售额下降”才需要 RAG 去检索分析报告。把两者混在一起用,反而会让系统变复杂。
4.4 DeepWiki 这类做法给我们的启发
DeepWiki 这类工具的思路是把代码仓库变成一个可对话的知识库,它做的其实就是结构化和向量化的结合:代码的调用关系是结构化的,代码的注释和文档是文本的。它把两者关联起来,所以能回答“这个函数被谁调用了”这种关系型问题,也能回答“这个模块是干嘛的”这种语义型问题。
这个思路可以迁移到任何领域:先想清楚你的知识里哪些是结构化的、哪些是文本的,然后分别处理,最后在检索层融合。
5. 第四道分水岭:一次检索不够,那就让 Agent 来决定怎么查
5.1 Naive RAG 的“一锤子买卖”问题
Naive RAG 的流程是线性的:检索一次,生成一次。但真实问题往往需要多步。比如“对比 A 方案和 B 方案在成本上的差异”,这需要先分别检索 A 和 B 的成本信息,再对比。一次检索很难同时召回两边且保证质量。
更麻烦的是,有些问题检索一次发现信息不够,需要根据已有信息追问。线性流程做不到这一点,它没有“反思”和“再行动”的能力。
5.2 Agentic RAG:把检索变成一个有策略的动作
Agentic RAG 的核心是让 LLM 自己决定:要不要检索、检索什么、检索几次、结果够不够、要不要换个关键词再查。它把检索从一个固定步骤变成了一个可编排的工具。
实现上,通常是一个 ReAct 风格的循环:模型思考当前缺什么信息,决定调用检索工具,看结果,判断是否足够,不够就调整查询再查。这个模式在处理复杂查询时优势明显,但代价是延迟和成本上升,因为可能触发多次 LLM 调用和检索。
我的经验是:简单事实查询走 Naive RAG,复杂多跳查询走 Agentic RAG,用路由先分类。不要所有问题都上 Agent,那样又慢又贵。
5.3 查询改写和分解:让检索更懂用户
用户的问题往往口语化、含糊、带指代。直接拿去做检索,效果打折。查询改写就是先把用户问题改写成更适合检索的形式。比如“那个超时的东西怎么调”改写成“请求超时参数如何配置”。
查询分解则是把复杂问题拆成几个子问题,分别检索再综合。比如“A 和 B 哪个更适合我们”拆成“A 的特点”“B 的特点”“我们的需求”,分别查完再对比。
这两个技巧实现成本不高,但效果立竿见影,我建议优先做。
5.4 多路召回与融合:别把鸡蛋放一个篮子
单一检索策略总有盲区。多路召回就是同时用多种方式检索:向量、关键词、图谱、甚至不同粒度的块。然后把结果融合。融合可以用 RRF,也可以用 LLM 做一次筛选。
多路召回的关键是各路之间要有差异性,如果两路召回结果高度重叠,那就白做了。我一般会确保至少一路是语义的、一路是关键词的,必要时加一路基于元数据过滤的。
6. 第五道分水岭:没有评估,你根本不知道自己在优化什么
6.1 “感觉变好了”是最危险的信号
我见过太多团队优化 RAG 全靠感觉:改了个切块大小,试了几个问题,觉得“好像好点了”,就上线了。结果用户一用,该错的还是错。没有量化评估,所有优化都是盲人摸象。
评估的第一步是建一个测试集:一批真实问题,加上标准答案或标准来源块。不用很多,50 到 100 个就能看出趋势。然后定义指标:检索阶段看召回率、命中率,生成阶段看答案正确性、忠实度(有没有编造)、相关性。
6.2 检索和生成要分开评估
这是很多人忽略的。一个 RAG 系统答错了,可能是检索没召回对的块,也可能是召回了但模型没用好。不分开评估,你根本不知道该修哪。
我的做法是:先固定生成模型,单独评估检索的召回率和命中率;再固定检索结果,单独评估生成的忠实度和正确性。两个环节各自优化到位,整体才稳。
6.3 用 LLM 做评估:便宜但有坑
人工评估准但贵,用 LLM 做评估(LLM-as-a-judge)便宜但要注意偏差。LLM 评估常见的坑包括:偏爱长答案、偏爱自己生成的答案、对位置敏感。缓解办法是:用多个模型交叉评估、随机打乱候选顺序、给出明确的评分标准。
我一般用 LLM 做初筛,把明显有问题的挑出来,人工再复核边界案例。这样成本和质量的平衡比较好。
6.4 线上反馈闭环:真实用户才是最好的评估者
测试集再全,也覆盖不了真实用户的问法。上线后要埋点收集:哪些问题被问了、哪些答案被点赞/点踩、哪些触发了“重新生成”。这些数据回流到测试集,系统才能持续进化。
我特别看重“点踩”数据,因为每一个点踩背后都是一个具体的失败模式。定期分析这些失败案例,比盲目调参有用得多。
7. 第六道分水岭:框架选型背后的取舍逻辑
7.1 框架不是越全越好,是越贴合越好
热词里“rag 框架”被搜了很多次。市面上的框架大致分两类:一类是大而全的编排框架,把切块、检索、生成、Agent 都封装好;另一类是轻量的库,只解决某一个环节。
我的建议是:先用轻量库把每个环节跑通,理解每一步在干什么,再决定要不要上重框架。一上来就用重框架,出了问题你都不知道是哪一层的问题。而且重框架的抽象往往带来调试困难,改一个行为要翻好几层源码。
7.2 本地部署还是调 API:算一笔账
“怎么在 mac 上搭建 rag 知识库”这类问题背后,其实是本地部署的需求。本地部署的好处是数据不出门、成本可控、延迟稳定;坏处是要自己维护模型、显存/内存吃紧、效果可能不如大模型 API。
我的判断标准是:数据敏感度高、查询量大、有运维能力,就本地;否则先用 API 跑通,验证价值再考虑迁移。本地嵌入模型现在选择很多,小模型在中文上的效果也够用,不一定非要上最大的。
7.3 本地文本拆解工具怎么选
“有没有本地的 rag 文本拆解工具”,这个需求很实际。PDF 解析推荐看版面分析能力,Markdown 解析相对简单,代码解析要看语言支持。我的经验是:不要指望一个工具通吃所有格式,按格式分别选工具,最后统一成一种中间格式(比如带元数据的 Markdown)再进切块流程。
7.4 从 Demo 到生产,中间隔着什么
Demo 到生产,隔的不是代码量,是工程化。包括:并发和限流、缓存、失败重试、监控告警、权限控制、版本管理。这些在 Demo 阶段都可以不管,但生产环境一个都不能少。我见过太多 Demo 惊艳、上线崩盘的案例,问题几乎都出在工程化上,而不是算法上。
8. 我在实际项目里踩过的几个具体坑
第一个坑是过度依赖单一嵌入模型。我早期所有场景都用同一个模型,后来发现中文长文本和代码用同一个模型效果差异很大。换成分场景选模型后,检索质量明显改善。
第二个坑是忽略了文档更新。知识库里的文档会更新,但向量库不会自动同步。结果用户问的是新版本的问题,检索到的却是旧版本的块。后来我加了文档版本管理和增量更新机制,才解决这个问题。
第三个坑是Prompt 里塞太多块。一开始我觉得多给点上下文总没错,结果模型被无关信息干扰,反而答偏了。后来严格控制块的数量和质量,宁可少而精。
第四个坑是没有处理“检索不到”的情况。当检索结果相关性都很低时,系统应该明确说“我没找到相关信息”,而不是硬编一个答案。这个兜底逻辑非常重要,能大幅降低幻觉率。
9. 给准备深入 RAG 的人几条实在建议
如果你现在还在纠结“RAG 是不是烂大街了”,我的建议是别纠结这个,去动手做一个真实场景的系统,你会立刻发现那六个分水岭一个都绕不过去。切块、上下文补全、融合重排、结构化、Agent 编排、评估闭环,每一个都值得单独花时间研究。
优先级上,我建议先做切块优化和上下文补全,这两个投入产出比最高,能解决大部分“检索不准”的问题。然后做评估闭环,没有它你后面所有优化都是瞎猜。再往后根据场景需要,考虑重排、多路召回、Agent 编排。GraphRAG 和 ontology 这类,等你的问题确实需要关系推理时再上,别为了用而用。
最后说一句,RAG 的难点从来不在“搭起来”,而在“调得好”。那条流水线谁都会搭,但把它调到一个真实业务能用的水平,需要的是对每个环节的深入理解和大量实测。这六处分水岭,就是区分“跑通 Demo”和“做成产品”的地方。