如何能够高效提升 RAG 知识库的质量?
如何能够高效提升 RAG 知识库的质量?
题主说的"按常规默认流程搭建,命中测试效果不佳",这个情况太常见了。默认流程一般就是:文档扔进去,按固定长度切块,用个 embedding 模型向量化,查询的时候算余弦相似度取 top-k。这套流程能跑通,但效果好不好完全看运气。
我自己做过几个 RAG 项目,走了不少弯路,说说哪些事情投入产出比高,哪些是在浪费时间。
先搞清楚效果差在哪
很多人一看命中率不行,就开始到处调参数——改 chunk size、换 embedding 模型、加 rerank。但你连问题出在哪都没搞清楚,调什么都是碰运气。
拿出你命中测试里效果差的 case,一条一条看,分清楚它到底差在哪:
是根本没检索到相关的块?还是检索到了但排名太靠后被截断了?还是检索到的块里确实包含答案但块太长答案被淹没了?还是检索到的内容跟问题在语义上确实不相关,是用户问法和文档表述差异太大?
这几种情况的解法完全不同。你不分清楚就动手,大概率是在做无用功。
我自己会建一个简单的评估表格,每条 bad case 标注三个信息:用户问了什么、正确答案在哪个文档的哪个位置、检索结果里有没有这个块以及排在第几。跑个五六十条 case 分完类,问题的分布就很清楚了。
分块策略:大部分人在这一步就输了
默认流程里最坑人的就是固定长度切块。按 500 字或者 1000 个 token 一刀切,完全不管内容的语义边界。一个完整的概念可能被切成两半分到了两个块里,检索的时候哪一半都匹配不上。
我实际测下来,分块策略对最终效果的影响大概占到 40%-50%。很多人花大量时间换 embedding 模型或者调 rerank,其实分块没做好的话,后面怎么调都是在垃圾上面做优化。
如果你的文档有明确的标题层级(比如 Markdown 的 # ## ###),直接按标题来切分,一个二级标题下的内容作为一个块,太长再按段落拆。这比按字数切的效果好太多,因为标题本身就是内容的语义边界。
还有一个细节很多人忽略:块被切出来之后,它丢失了"我属于哪个章节、前面讲了什么"这个信息。我的做法是在每个块的开头拼上它的层级标题路径。比如原文是"第三章 > 3.2 退款政策 > 3.2.1 七天无理由退款"下面的一段话,存进向量库的时候前面加上"第三章 - 退款政策 - 七天无理由退款:"再接正文。这样 embedding 的时候模型就知道这段话在讲什么主题。我在一个产品手册的项目里测过,就这一个改动,召回率从 68% 到了 79%。
另外相邻两个块之间保留 10%-20% 的重叠内容。这样一个概念正好落在切分边界上的时候,两个块里都能包含完整的表述。大部分分块工具都支持 overlap 参数,设一下就行。
文档本身的质量比你想的重要得多
我遇到过不止一个团队,花大量时间调 RAG 管线的各种参数,最后发现效果差的根因是文档本身就有问题。
最典型的就是术语不统一。同一个概念在 A 文档里叫"退货流程",B 文档叫"退款申请步骤",C 文档叫"退换货规则"。用户问"怎么退货"的时候,只有 A 能被检索到,B 和 C 因为措辞差异匹配不上。还有文档里大量的"参见第 X 条""详见附件 3"这种交叉引用,切块之后引用指向的内容根本不在同一个块里,模型拿到的是残缺信息。表格类的内容也是重灾区,一个价格对照表被切成"标准版 999 元"一行、"专业版 1999 元"另一行,分到了不同的块里,检索的时候怎么都匹配不全。
解决办法不需要什么高级技术:统一术语、把交叉引用展开成实际内容、表格转成自然语言描述。枯燥,但回报很高。
在 Query 端做文章,往往比在文档端死磕更高效
很多人只盯着文档端——切块、embedding、索引,但用户输入的 query 质量其实也是大问题。用户的提问经常是模糊的、口语化的、甚至不完整。"那个七天的政策是什么来着"——这个 query 直接拿去检索,效果能好才怪。
最直接的做法是在检索之前用 LLM 做一次 Query 改写,把口语化的提问转成更规范的表述。比如"那个七天的政策"改写成"七天无理由退款政策的具体规定"。成本就是多调一次 LLM,但检索质量的提升非常明显。我做过一个内部知识库的项目,用户问法特别口语化,文档却是正式的技术文档,加了 Query 改写之后召回率从 71% 到了 82%,比换 embedding 模型的提升还大。
如果用户的问题比较复杂,还可以做 Query 拆分。"标准版和专业版有什么区别,价格分别多少"——这其实是两个检索意图,拆成两个 query 分别检索再合并结果,比一个大而全的 query 效果好很多。
还有一个方法叫 HyDE,思路比较巧妙:让 LLM 先根据用户的问题生成一个假设性的回答,然后用这个假设回答去做 embedding 检索,而不是用原始问题。因为假设回答和真实文档在措辞风格上更接近,匹配效果往往更好。用户问法和文档表述差异大的场景下特别好使。
关于 Rerank
很多人把 rerank 当成最后一根救命稻草,觉得效果不好加个 rerank 就能上去。有用,但没很多人想的那么神。
Rerank 干的事情是对检索结果做精排:先用 embedding 粗召回 top-20 或 top-50,再用交叉编码器对这些候选逐条打分,重新排序取 top-k。它能解决的是 embedding 语义理解不够精细、把不太相关的块排到前面的问题。但前提是正确答案已经在粗召回的候选集里了——如果分块有问题导致正确答案根本不在 top-50 里,rerank 排来排去也排不出来。
简单说就是:召回率已经 80% 以上但正确答案排名靠后(在 top-20 里但不在 top-3),加 rerank 很有效。召回率本身只有 50%-60%,先去解决召回,rerank 不是你现在该操心的事。
关于"花很多精力调整文本分段和关联问题"
题主觉得这个过程很费精力,我理解,但说实话这就是 RAG 的核心工作。
很多人以为 RAG 是一个"搭建好就能跑"的系统。不是的。RAG 本质上是一个信息检索系统,信息检索系统的效果永远取决于数据质量和检索策略的调优。Google 搜索做了二十多年,到现在还在不停优化排序算法和索引策略。
但你可以让这个过程更高效:
建一个评估集。哪怕只有 100 条"问题-期望答案-答案出处"的三元组,就够你快速验证每次改动的效果了。没有评估集你就是在盲调,改了也不知道是变好了还是变差了。
先做投入产出比高的改动。按我的经验排序:文档清洗和术语统一 > 分块策略优化 > Query 改写 > 加 rerank > 换 embedding 模型。很多人一上来就去换 embedding 模型或者研究最新的检索架构,但前面的基础工作没做好,换什么模型效果都上不去。
接受"够用就行"。RAG 的召回率从 70% 提到 85% 可能只需要几天功夫,但从 85% 提到 95% 可能要几周。想清楚你的业务对准确率的要求到底是多少,达到了就停手,不要追求完美。
一个不太常见但很有效的做法
如果你的文档量不是特别大(几百到几千页),可以试试让 LLM 预先为每个文档块生成几个"可能被问到的问题"。把这些生成的问题和原始块一起存进向量库。
用户提问的时候,不只是跟文档块做匹配,还跟这些预生成的问题做匹配。因为 LLM 生成的问题在措辞上更接近用户的提问方式,匹配效果往往比直接用文档块做匹配更好。
这个方法的缺点是前期要跑一遍 LLM 生成问题,有一定的成本。但只要跑一次,后续检索质量的提升是持续的。我在一个法律知识库的项目里用过这个方法,对那种"用户用大白话问、文档是法律条文"的场景效果非常好,召回率提了将近 15 个百分点。
