RAG 准确率卡在 61%,我花了六周返工,换模型只贡献了 2 个点

🔑 关键词:RAG优化, 向量检索, 大模型应用开发, 评估集, 重排序

📖 摘要:一个内部知识库问答系统的完整返工记录。从 61% 到 89% 的六个改动,以及一个可能不太讨喜的结论:在你的业务场景里,检索质量的重要性远大于换模型。

一、先交代背景,免得看着像软文

图片

2023 年 11 月我们给客服团队做了个内部知识库问答,语料是 12000 多份文档,PDF 和飞书导出混着来。技术栈就是当时最流行的那套:LangChain(版本号记不清了,反正还没到 0.1,天天 breaking change)、Milvus 2.2.11、text-embedding-ada-002,生成用 GPT-4-0613。整个系统切出来 8 万 7 千个 chunk,慢是慢,但能跑。

我们自己标了个 150 条的测试集,跑下来准确率 61%。团队第一反应是「模型不行」——这个反应太自然了,因为换模型只需要改一行代码,而查检索问题得一行一行看日志。于是花了两周做了三件事:GPT-4 换成 GPT-4-turbo、top-k 从 3 调到 5、prompt 从英文改回中文。结果 61% 涨到 63%,其中还有一个点我怀疑是标注噪声。说难听点,那两周基本白干。

后来我把这叫「模型崇拜税」。出了问题先怀疑模型能力,是因为模型是个黑盒,你没法跟它讲道理;而检索和数据的问题很脏很碎,得蹲下来一条条看。人本能会选轻松的那条路。

图片

二、真正起作用的全是土办法

第一个改动是分块。原来用 RecursiveCharacterTextSplitter,chunk_size=512,overlap=50,按字符切。中文按字符切很要命,512 个字符大概 250 个 token,经常一句话被拦腰截断——「如果客户在收到商品后 7 天」/「内申请退款且商品未拆封」。句子都断了,embedding 出来的向量当然偏。

改成先按文档结构切:Markdown 的标题层级、PDF 里的章节号,然后在章节内部按 400 token 切,overlap 80,用 tiktoken 的 cl100k_base 数 token。切完 chunk 从 8.7 万降到 5.2 万,平均长度从 250 涨到 390 token。就这一下,准确率 63% → 71%。没加任何模型,没调任何参数,就是把文档切对了。

图片

第二个改动是混合检索加重排序。原来纯向量检索,对「SKU-88273」这种编号类问题基本瞎。加了 BM25 一路(用的 rank_bm25 库,没用 Milvus 的稀疏向量,因为那时候 sparse vector 还得自己训 SPLADE,太麻烦),两路各召回 top 20,用 RRF 融合(k=60),最后送 bge-reranker-v2-m3 重排取 top 5。

重排的延迟是实打实要付的:一开始用 PyTorch 在 CPU 上跑,20 条要 200ms 出头,p99 直接从 1.8s 干到 2.4s。后来导成 ONNX + INT8 量化,CPU 降到 60ms 左右,T4 上大概 15ms。代价是量化后准确率掉 0.4 个点,接受。这一步 71% → 84%。

三、最不该省的是评估集

原来那 150 条测试集是产品经理凭印象编的,问题都特别「标准」:「退款流程是什么」「发票怎么开」。但真实客服问的是「客户买了三天要退但拆封了能不能退」「订单被风控拦了但钱已经扣了怎么办」——又长又不规范,还带上下文。

图片

我们从线上日志里随机抽了 400 条真实 query,两个人花了三天人工标答案和引用来源。用真实测试集重跑,之前号称的 84% 其实只有 76%。测试集偏差 8 个点,如果你一直用它做迭代,那基本就是在拟合一个假目标。

这一步没有任何技术含量,但它是后面所有优化的地基。我现在的习惯是:任何 RAG 项目,评估集没到 300 条真实 query,你根本不知道自己在优化什么。

四、剩下的 5 个点,以及几个不太主流的看法

图片

最后的 76% → 89% 靠的是一堆零碎活:query 改写(把口语化问题补全成检索友好的一句话,用 GPT-3.5-turbo 跑,一次 300ms)、把「引用文档标题」这个动作从 prompt 里挪到代码里做(模型引用标题的准确率只有 70% 出头,代码里直接查元数据是 100%)、加了一个「检索结果相关性低于阈值就拒答」的分支。拒答这个事很反直觉——产品经理一开始不接受,觉得拒答就是没帮到用户,但错误答案的伤害比拒答大得多。

几个我自己的看法,可能不太主流:

第一,2024 年下半年之后,base model 的能力提升对具体业务场景的边际收益很小。我们做过 A/B:GPT-4o 和 GPT-4-turbo 在同一套检索结果上,准确率差 1.5 个点;但检索改一改能差 10 个点以上。预算花在哪儿其实很清楚。

图片

第二,eval 集比模型重要,比 prompt 重要,比向量库选型重要。但它不产出任何「进度」,所以在排期里最容易被砍。这是个组织问题,不是技术问题。

第三,RAG 不是搜索,是上下文组装。你用搜索引擎的思路做,就会天天纠结召回率;用组装思路做,你会开始关心「这个 chunk 塞进去会不会干扰模型判断」。我们最后把 top-5 砍到 top-3 反而涨了 1.2 个点,就是因为干扰少了。

最后一句实在话:这套东西没有任何技术壁垒,全是脏活。但如果你正在 61% 那个位置卡着,先别动模型,去把你那 8 万个 chunk 打开,随机抽 20 个看看切得对不对。

🏷️ 标签: