1. 开篇:为什么同一个 RAG,有人做成玩具,有人做成生产力
RAG(检索增强生成)这两年确实火到不行,打开技术社区满屏都是“三天搭建企业知识库”“十分钟跑通本地RAG”的教程。但你只要照着这些教程真去跑一遍,大概率会发现一个尴尬的事实:demo 跑起来很顺,一问真实业务问题就露馅,答非所问、幻觉照旧、检索结果驴唇不对马嘴。于是很多人得出一个结论——RAG 这玩意儿水分太大,烂大街了。
我自己的看法是,RAG 本身不但没过气,反而是当前企业落地大模型最务实的一条路。烂大街的从来不是 RAG,而是那条谁都照着抄的流水线:加载文档、切块、灌向量库、接大模型、开个聊天窗口,完事。这套流程任何一个会调 API 的工程师一下午就能搭出来,但它离“能用”差着十万八千里。
真正检验一个 RAG 系统能不能打的,是流水线之外的六个分水岭:意图识别与路由、混合检索策略、重排序与上下文压缩、结构化知识与多模态内容的处理、查询改写和多轮记忆,以及评估体系。这六处每一处,都直接决定了你的 RAG 是一个只会复读机式拼凑的玩具,还是一个能让业务方真正放下人工客服、放下文档检索系统的生产力工具。
这篇内容是我做了多个真实 RAG 项目后踩坑总结出来的,不聊流水线怎么搭,专门聊这些分水岭上的细节。适合已经跑通过 demo、正在往生产环境推的工程师,也适合正准备选型的团队负责人。你会看到每一个关键点的具体做法、参数选择逻辑、常见坑,以及我在实际项目里验证过的方案。
2. 第一处分水岭:意图路由——你的 RAG 到底该有多聪明
2.1 不是所有问题都需要检索
我在第一版 RAG 系统里犯过一个大错误:不管用户问什么,一律给它先去向量库捞一遍文档、再做增强生成。结果就是用户问“你们公司的退货政策是什么”这种制度性问题时,系统答得还不错;但用户问“今天是星期几”“你们系统能不能导出 Excel”这种根本不涉及知识库内容的问题时,系统也煞有介事地去检索、去拼接,最后回答得又慢又蠢。
这个问题的本质,是没做意图路由。RAG 的前置条件应该是“这个问题需要知识库支持”,而不是“只要来了问题就检索”。生产环境里,用户的提问类型远比教程里丰富:
- 纯闲聊、问候类:不需要 RAG,直接走 LLM 常规对话。
- 知识库覆盖的领域问题:走完整 RAG 链路。
- 知识库覆盖不到的问题:应该明确告知“暂未收录”,而不是硬答。
- 对历史回答的追问和澄清:需要结合对话记忆,而不是重新检索。
2.2 轻量分类器是性价比最高的路由方案
我实测下来最稳的做法是双路由结构:先用一个极轻量的分类器(可以是正则规则、小模型分类,或直接用一个不带检索的 LLM 调用)判断问题类型,再决定走哪条链路。这种方案比想一口气上复杂 Agent 编排要稳妥得多。
举个例子,我之前做了一个工业设备维修知识库,用户大多是车间操作工,提问很口语化:“这个报警代码咋回事”“换油周期是多久”“能不能教我拆轴承”。这些明显都是知识库问题。但也会有用户输入“你好”“谢谢”“帮我查一下昨天那个故障”这种。我用的做法是让 LLM 先做一次零样本分类,输出 JSON:
{"intent": "knowledge_retrieval|chitchat|follow_up|out_of_scope", "rewritten_query": "..."}只有knowledge_retrieval和follow_up才走检索链路,其他两类直接走应答逻辑。这一步看起来简单,却让整体回答准确率提升了不止一个档次——因为系统不再被无关问题干扰,也避免了大量无效检索带来的延迟成本。
注意:路由模型本身也会出错。我见过有人在生产系统里用很弱的分类模型做路由,结果把知识库问题误判成闲聊,直接导致核心问答失效。建议路由阶段宁可多放行一些“疑似知识问题”的问题,也不要轻易拦截,拦截错了比检索错了更影响体验。
2.3 路由之后,还要想清楚检索时机的选择
意图路由只是第一步,第二步是“什么时候检索”。现在很多框架默认是“每轮对话都检索一次”,这在多数场景下是浪费的。我做过的一个客服系统,用户常常连续追问同一个订单的问题,第一轮已经把相关文档喂给模型了,第二轮的追问其实只需要延续上下文,根本不需要再检索。
我后来在路由结果里加了一个need_retrieval: true/false的字段,规则相对粗糙但有效:
- 用户说“继续”“然后呢”“还有吗”之类承接性话语,不检索。
- 用户问的问题和上一轮实体完全一致,不检索。
- 问题里出现新实体、新业务词,强制重新检索。
这一步优化直接把每轮对话的平均延迟从 2.8 秒降到了 1.5 秒左右,而且没有牺牲回答质量。分水岭的意义就在这里——它不显眼,但都是实打实的成本和质量收益。
3. 第二处分水岭:混合检索——Vector 检索不是万能的
3.1 关键词搜索被人遗忘的代价
向量检索(语义检索)被吹得神乎其神,但真实业务里它有个致命弱点:对精确信息不敏感。比如用户要查“型号 XYZ-200 的额定功率”,如果你的知识库里恰好有一句话是“XYZ-200 额定功率 2.5kW”,向量检索大概率能匹配到;但如果你问的是“订单号 20240918-001 的状态”,这种带精确编号的内容,向量检索的召回效果经常不如一个简单的match_phrase查询。
原因在于,向量模型是把文本映射成语义空间的坐标,它对语义相近的信息敏感,但对“字符级别的精确匹配”不敏感。像设备型号、订单号、故障码、标准条目编号这些场景,恰恰是知识库问答的高频需求。只靠向量检索等于让一个会说人话但记不住数字的人去查订单,结果自然惨不忍睹。
3.2 关键词检索 + 向量检索 + 重排的标准组合
我在生产项目里的标准做法是混合检索(Hybrid Search),具体来说:
- 用 BM25(或 Elasticsearch / OpenSearch 的全文检索)做关键词召回,负责精确匹配。
- 用向量检索做语义召回,负责同义表述、模糊表达的匹配。
- 两个通道的结果合并后,用重排序模型挑出最相关的片段。
这里的关键参数是两路召回的数量配置。我习惯把向量和 BM25 各取 top 20,合并后去重,再重排取 top 5 左右。为什么不直接各取 top 5?因为两个通道的命中结果重叠度往往只有 30% 左右,如果一开始就取太少,合并后很容易丢掉真正有用的内容。召回阶段宁多勿少,排序阶段再收窄。
之前有个医药企业知识库的项目,里面大量内容是药品说明书和临床指南,术语极其严格。纯向量检索的命中率(召回 top 5 含正确答案的比例)大概只有 60%,加上 BM25 的混合检索直接拉到 85%。这个提升没有任何模型调优成本,纯粹是检索策略换了一下。
3.3 实际配置 Elasticsearch 时的几个要点
如果你正在用 Elasticsearch 做混合检索,有几个必踩的坑。第一,别为了省事把向量字段和文本字段塞在同一个索引里不设置分离——不同的查询场景对字段权重需求完全不同,建议用multi_match对文本字段做加权,而不是一股脑用match_all。第二,ES 的向量检索(knn查询)在数据量小(几千条以内)时性能优势不明显,但到了百万级差距立刻拉开,所以索引参数的num_candidates要按数据量调,默认值往往不够。
我这里给一个简化可用的查询结构示意,不是完整代码,但思路可以参考:
ES 索引字段: - content_text: 文档切片原文,type=text,走 BM25 - content_vector: 向量,type=dense_vector,走 knn 第一步:BM25 查询,取 top 20 第二步:knn 查询,取 top 20 第三步:两路结果按相关度打分合并 第四步:再用交叉编码器重排,取 top 5踩坑提示:合并两路结果时,直接“向量分数 + BM25分数”的做法基本不可用,因为两路分数量纲完全不同。我建议直接跳过分数融合,把两路 top 20 的结果丢给 rerank 模型,让模型统一打分。简单有效,省掉一堆调权重的功夫。
4. 第三处分水岭:重排序与上下文压缩——决定回答质量的最后一公里
4.1 为什么 Top 5 命中还不够
混合检索帮你把候选文档从百万级缩到几十条,但这几十条里面真正有用的可能就三五条。直接把这几十条全部塞给 LLM,有两个问题:一是上下文窗口消耗快,成本高、延迟高;二是无关内容会干扰 LLM 的判断,导致幻觉和答非所问。
我做过一次对比实验,同样的知识库、同样的用户问题,直接把 10 个切片塞给模型,回答准确率只有不到 50%,错误主要来自模型被无关切片带偏。而做了重排之后,只取 top 3 相关切片喂给模型,准确率能到 80% 以上。重排这步不是你“有时间再优化”的加分项,而是必需品。
4.2 Cross-Encoder 重排的实操经验
重排模型的选型,我通常优先考虑 Cross-Encoder 架构。它把“用户问题 + 候选文档片段”拼在一起输入模型,打出一个相关度分数,理论上比 Bi-Encoder(就是向量检索用的那种双塔结构)精度高,但速度慢,所以只适合对几十条候选做精排,不适合全库范围检索。
常用的几个选择:
bge-reranker-base/bge-reranker-large:中文表现稳定,开源,部署成本可控。cohere-rerank:API 调用,质量高,适合不想自己维护模型的场景。cross-encoder/ms-marco-MiniLM-L-6-v2:英文效果好,中文一般。- 新出的
bge-reranker-v2-m3:多语言效果更好,参数量也更大,需要显存。
我在本地 CPU 环境部署bge-reranker-base时,单条排序耗时大约 50 到 100 毫秒,对 20 条候选排序完全可接受。如果你用的是 GPU,基本可以忽略耗时。注意一点:rerank 模型的输入长度有限,候选切片如果太长会被截断,所以控制好切片长度(后面会细说)是配合重排的重要前置条件。
4.3 上下文压缩:不是所有命中片段都值得进 Prompt
重排之后,很多人的做法是“取前 N 个片段,全塞进 Prompt”。这其实还是不够精细。因为即使排在前面的片段,也可能包含大量与问题无关的内容——比如切出来的片段里恰好转述了很多背景介绍,真正有用的就那一句。
上下文压缩的思路,是让一个轻量模型(或者直接让主干 LLM 自己)对命中的片段做提炼,只保留和当前问题相关的信息。我实测的一个例子:用户问“ZR-200 型设备润滑周期”,检索出来的片段是一段设备操作手册,整整 1000 字,里面真正相关的只有一句“润滑周期为每工作 500 小时”。如果不压缩,这 1000 字全进 Prompt;压缩之后只留那一句,效果非常明显。
实际操作上,我习惯把上下文压缩和 Prompt 组织合并处理,给 LLM 的指令大致是:
以下是检索到的资料。请仅提取与问题直接相关的信息,忽略无关内容。 若资料中不存在相关信息,请明确回答“未在资料中找到答案”,不得编造。这句话我试过很多变体,最关键是“不得编造”那半句。RAG 的幻觉来源很大一部分不是模型不会答,而是模型在资料不足的情况下“宁可编一个顺畅的说法也不肯承认查不到”。这属于 Prompt 层面的强约束,成本为零,效果立竿见影。
5. 第四处分水岭:文档切分与结构化知识——切片策略决定检索上限
5.1 固定长度切块为什么经常出错
市面上大多数教程会让你按固定字符数切分文档,比如每 500 字一个 chunk。这个方案简单,但坏处也明显:它完全无视了文本的语义边界。你可能把一个完整段落从中间割开,也可能把表格的字段和数值拆到两个 chunk 里,检索的时候永远差半句。
我之前做法律文书类知识库时裁过这个跟头。法律文书通常有严格的条款结构,一个条款是一个完整语义单位。按 500 字切分,最常见的后果是“第六条”被切到上一个 chunk 尾部,“内容”被切到下一个 chunk 头部,检索“第六条的责任”时干脆检索不到。后来我改成了按结构切分——用 Markdown 标题、条款序号、段落标记做边界,文本语义完整度立刻上来了。
5.2 结构化切分的正确姿势
合适的切分策略,和你的文档类型强相关。我总结了几个常用策略,按优先级排序:
- 有明确层级结构的文档(法律条文、规章制度、技术手册):按章节、条款切分,一个条款就是一块,不要硬切。
- Markdown 或 HTML 来源的文档:保留结构标记,按标题层级递归切分,这样每个 chunk 自带标题上下文。
- 表格类内容:尽量整行整表保存,不要丢行列信息。
- 长段落但是纯文本:用滑动窗口切分,但窗口之间保留重叠(overlap),一般重叠 10% 到 20%,避免切断关键词。
切片长度我建议根据 Embedding 模型的实际能力来定。很多模型对 512 token 以内的文本编码效果最好,超出后语义表示能力明显下降。你可以把 chunk 控制在模型有效长度内,留出重叠区间。我常用的配置是 chunk_size=400 token、overlap=60 token,是综合检索精度和存储成本的折中。如果你的 Embedding 模型支持更长(比如 8k 上下文的新模型),可以适当加大,但过大的 chunk 会让检索粒度变粗,不一定更好。
5.3 层级切片:给每个 chunk 挂上“户口本”
这是我在做企业知识库时最推荐的一个进阶做法:为每个 chunk 维护完整的父级结构信息。
举个例子,一份公司《差旅报销制度》,被切成了 40 个 chunk。如果每个 chunk 都携带“所属章节 = 第三章 报销标准”“所属制度 = 差旅报销制度 V3.2”“所属目录路径 = 财务部/报销相关”,那检索时就可以利用这些元数据做元数据过滤。用户如果问“2024 年新政策的差旅标准”,你可以在检索前用元数据先把制度版本锁定为 V3.2,而不是让向量模型从几百个版本里大海捞针。
这种层级信息还可以在上游做智能范围收窄:先检索一级目录级别,再在命中目录里检索具体条目,多级下钻。这个思路在项目里的名称一般叫“多层路由检索”,本质上是结构化知识层和语义检索的结合。
5.4 关于 Ontology RAG 的一点思考
热词里提到的 ontology RAG,本质就是这一层的制度化升级。普通 RAG 把文档看成“一堆文本切片”,ontology RAG 则是先建立一套领域概念关系图谱,再让检索系统按图谱路径去查找相关信息。
我目前对 ontology RAG 的态度是:如果知识库本身结构性强(比如医疗领域疾病、症状、药物、禁忌的关系非常明确),投入产出比很高;如果知识库就是一堆松散的文档,强行建模成 ontology 反而会引入不少额外维护成本。务实的做法是先用轻量化的元数据方案(上面说的“户口本”)把结构化信息管起来,等业务发展到知识库规模大到语义检索顶不住时,再考虑引入正式的 ontology 层。
6. 第五处分水岭:多模态内容与知识更新——图片表格不处理等于没建库
6.1 RAG 知识库到底能不能存图片
这是我在做知识库项目时被业务方问得最多的问题之一:“RAG 知识库能存储图片吗?”
直接回答:传统纯文本 RAG 不能,但多模态 RAG 能。如果你的知识库里大量内容以图片形式存在(设备图纸、产品照片、流程图、截图型说明书),而你的流水线只做“解析文字-向量化-检索-生成”,这些图片对系统来说是不存在的。你检索“这张图纸上的线性尺寸是多少”,系统根本找不到答案,因为你压根没把图片内容送进索引。
处理图片的正确路径有三条:
- 最简单:用 OCR 把图片里的文字提取出来,按文字内容入库。适用于截图型文档、扫描件、白板照片。
- 进阶:用图像描述模型(比如把图片喂给视觉语言模型,让它生成一段文字描述)把图片的“语义内容”转成文本,入库。适用于流程图、示意图、产品外观图。
- 高级:用多模态向量模型(比如 CLIP 类模型)把图片和文本映射到同一个语义空间,实现图文互相检索。适用于“用户描述一个外观特征,系统去匹配图片”的场景,技术难度最高,也是多模态 RAG 的完整形态。
我实际项目里 90% 的需求用 OCR + 图像描述就解决了。真正需要图文互相语义检索的场景占比很少,但一旦需要,普通文本 RAG 就无能为力,必须在索引层引入多模态模型。
6.2 图像入库的实操注意点
我做设备维修知识库时,接了不少现场拍的照片、设备铭牌图、参数表截图。当时踩了两个坑,说给你参考。
第一个坑是直接用 OCR 把图片转文字,可图片里的表格结构全丢了。参数表转出来变成一行行文字,数值和字段名失去对应关系。后来我改用带版面分析的 OCR 方案(比如 PaddleOCR 的表格识别能力),能把表格还原成结构化键值对,查询“这台设备的额定电压是多少”时检索质量立刻提升。
第二个坑是无视图片的分辨率和拍摄质量。现场拍的模糊照片、倾斜拍摄的铭牌,OCR 效果极差。后来我在处理流程里加了一个前置质检:图片清晰度低于阈值(比如分辨率过低、倾斜角度过大)就退回人工处理,而不是硬喂给 OCR 生成一堆错乱文本,污染知识库。
6.3 知识更新与增量入库的策略
多模态之外,知识更新也是一道分水岭。很多人建完知识库就再也不动了,结果业务文档更新了,RAG 的回答还停留在旧版本,这种情况下用户对系统的信任度会极速下降。
增量更新的核心原则是最小变更:文档更新时不要全量重灌,而是按“文档唯一 ID + 版本号”定位到变化内容,只删除旧切片、写入新切片。这需要你在入库时给每个 chunk 记录来源文档 ID 和版本号,否则后面根本没法定位要删哪条。
我见过有人偷懒,每次更新直接清空向量库重建,数据量小的时候没问题,但数据量到几十万条时每次重建都是十几个小时的事。生产环境必须走增量更新。具体实现可以参考这份更新流程:
- 文档入库前生成哈希指纹,文档没变就直接跳过。
- 文档变了,根据文档 ID 找到它的全部旧切片,删除。
- 重新解析、切分、向量化,写入新切片并更新版本号。
- 最后做一次一致性校验:确认新切片数量大于等于旧切片数量(防止删除后写入失败导致知识库残缺)。
7. 第六处分水岭:评估体系——没有评测指标的 RAG 都是玄学
7.1 不评估就上线,等于盲人骑瞎马
我见过太多团队把 RAG 系统调到“看起来还行”就上线了,上线后被业务方反馈的各类问题打回来,又不知道从哪里开始优化。原因很简单:没有建立一套评估体系,你根本不知道现在的 RAG 系统是好是坏、问题出在哪个环节。
RAG 评估的难点在于它不是一个单点模型,而是一条链路。问题可能出自检索召回,也可能出自重排,还可能出自 LLM 生成。没有分环节的评估指标,你永远只能笼统地说“效果不好”。所以要拆开测,重点看三个维度的指标:
- 检索质量:召回率(被检索出的文档中包含正确答案的比例)、命中率(top-k 中含正确答案的比例)、MRR(正确答案排在最前面的程度)。
- 生成质量:答案的忠实性(是否忠于检索到的资料,还是胡编)、答案完整性、答案相关性。忠实性可以说是 RAG 最重要的指标,直接对应幻觉程度。
- 端到端质量:一次问答从用户提问到返回答案,整体是否满意,通常需要人工标注或更强模型打分。
7.2 搭建一套低成本的评估基线
完整的 RAG 评估需要建测试集。我在项目里是这么做的:
- 从知识库里挑 100 到 200 个典型问题,覆盖业务高频场景。
- 每个问题人工标注“正确答案”和“答案出处文档”。
- 召回评估自动化跑:把检索环节单独拎出来,计算 top-k 命中率。
- 生成评估用强模型做裁判(比如用 GPT-4 或更强的开源模型逐条打分),主要打“忠实性”和“相关性”两个维度。
一开始没有测试集怎么办?我建议先让业务方提供 50 条高频真实用户问题,你可以不标注标准答案,先用“当前系统答一遍 + 让人工判断是否可接受”跑一条粗糙基线。有了粗糙基线,后面每次改动才有对比。
7.3 RAG 两个常见瓶颈的定位技巧
不少人问我:“RAG 做出来的东西为什么老胡编?”我不直接回答,而是建议他先分环节排查。我总结了两个高频瓶颈的排查顺序,你排查时直接照做就行:
- 回答内容来自无关资料,甚至自相矛盾:先查检索质量。跑一遍“给定标准问题,看召回 top 5 里的文档到底和问题相关吗”。如果召回就不相关,那重排和生成做得再好也白搭。这是最常见的瓶颈,大概率是切分策略有问题或 embedding 模型不适合当前领域。
- 召回相关但生成胡说:那问题在生成环节。检查是否直接把全部候选塞给了 LLM 导致上下文干扰,检查 Prompt 里是否有强约束“只能基于资料回答”的指令。很多时候就是缺了一句“资料中没找到就承认”。
记住一个原则:RAG 的调试顺序必须是“先检索后生成”,80% 的 RAG 烂项目,问题都出在检索环节。生成环节调一万次提示词,也救不了前面就抓错的检索结果。
8. 本地部署与工具链选型——零基础也能跑通的实践路径
8.1 Ollama 本地部署的起步配置
热词里提到“ollama + 简易本地 RAG 知识库”的零基础教程,这里我不重复流水线,但讲一下本地部署的关键决策和常见坑。
本地 RAG 的第一个决策是模型选型。Ollama 上可以直接拉模型,但我建议你把“生成模型”和“向量模型”分开选,不要图省事用一个模型包打天下。我常用的本地搭配是:
- 向量模型:
bge-m3(多语言效果好,对中文尤其友好,支持 8192 上下文)。 - 生成模型:
qwen2.5:7b或qwen2.5:14b(中文能力优于同体积的 llama 系,显存 8G 就上 7b,16G 可以考虑 14b)。 - 重排模型:
bge-reranker-base或本地部署的bge-reranker-v2-m3。
这套组合在纯 CPU 环境下可以跑,但体验明显卡顿。我的建议是最少配一块 8G 显存的 GPU,或者直接用云服务器带 T4 级别的卡。内存方面,qwen2.5:7b量化后约 5 到 6G,加上 embedding 和 rerank 模型,16G 内存是底线。
8.2 文本拆解工具的实操体验
热词里有人问“有没有本地的 RAG 文本拆解工具”,我推荐一个组合方案:PaddleOCR + LangChain 的自定义文本分割器 + Qdrant 向量库。这几个工具全部本地部署,不依赖任何外部 API,数据不出内网。
- PaddleOCR 负责图像和 PDF 扫描件的 OCR,对中文支持好,表格识别也够用。
- LangChain 或者直接自己写分割函数,按你文档的结构特性做切分(前面 5.2 节讲过的结构化切分)。
- Qdrant 做向量库,轻量、性能好,支持 metadata 过滤,比 FAISS 更适合做生产级本地知识库。
- 如果坚持用一个框架把整条链路串起来,LangChain4j(Java 生态)或者 LlamaIndex(Python 生态)都行。热词里那个 “langchain4j easy rag” 指的就是 Java 技术栈下用 LangChain4j 搭一个开箱即用的 RAG 骨架。
本地部署真正的坑不在选哪个框架,而在于依赖版本冲突。我因为 Python 包版本不一致,在torch和transformers之间折腾了整整一个下午。给两条血泪建议:一是用 Docker 把环境固定下来,换机器不重蹈覆辙;二是把依赖锁版本,不要图省事直接pip install -r requirements.txt,你永远不知道哪些包升级后行为会变。
8.3 框架怎么选:LangChain 还是 LlamaIndex,还是直接手写
框架的选择是很多团队纠结的问题,我给出的判断标准很简单:按团队技术储备和需求复杂度来。
- 如果你想快速验证一套 RAG 流程、跑 demo、参加比赛,LangChain 最合适,文档多、案例多、社区活跃,踩了坑也容易找到答案。
- 如果你要做生产级知识库,候选文档类型杂、检索策略复杂、需要精细控制每个环节,LlamaIndex 的数据接入和索引管理更顺手。
- 如果你们团队有足够的 NLP 工程经验,并且业务链路高度定制化,我建议手写核心链路——把 embedding、检索、rerank 各自封装好,组合逻辑自己控制。看着更“原始”,但排查问题、调优、扩展时自由度高了不止一个量级。
我之前做工业知识库就是半手写:底层用 Qdrant 做存储和检索,中间封装了一层自己的检索器负责混合检索和元数据过滤,上层接 LLM。依赖的框架很少,几乎没遇到框架升级带来的迁移问题。
9. 实操总结:从流水线到分水岭,我踩过的坑一次说清
做了这么多 RAG 项目,我体会最深的一点是:RAG 的难度曲线和大多数人预期的不一样。它不在“搭起来”这一步难,而是在“搭起来之后每个环节都要单独优化”这一步难。这也是为什么开篇说烂大街的是流水线——流水线几个小时就能搭完,但六个分水岭每一个都够你打磨几周甚至几个月。
最后分享一个我保留的习惯:每次调完 RAG 的任何一个环节,都固定跑一遍同一套测试集,看指标有没有恶化。因为很多调整是“局部看起来好了,整体却变差”的——比如你把 chunk 调大了,单条切片语义更完整了,但检索粒度变粗,精确问题的命中率反而降了。有了固定的测试集,这类问题一眼就能发现。
另外,如果你准备把 RAG 系统做到生产级别,我建议从一开始就加上链路追踪日志。把每轮对话的路由结果、检索出来哪些文档、每篇文档的相关度分数、最终喂给 LLM 的上下文片段全部记录下来。这样用户一旦反馈答错了,你能直接回放整个链路看问题出在哪个环节。没有这套日志,排障全靠猜,效率低到让人崩溃。
六处分水岭说完了,每一处都不是什么高深的理论,都是我拿真实业务磕出来的细节。RAG 从来没烂过,烂大街的只是那些只讲流水线不老讲细节的教程。把上面六处做扎实,你的 RAG 会和市面上的 demo 拉开一个肉眼可见的差距。