跳至主要内容
向量库解决不了的问题

向量库解决不了的问题

第一次做 RAG 的时候,我花了三周时间换 embedding 模型,从一个小模型换到更大的,再换到多语言版本。评测分数涨了不到两个点。

真正的问题是,七成的 bad case 跟向量库没有关系。

大部分错误发生在检索之前

我把失败的案例逐条打印出来看,问题集中在三个地方。

第一是切分。文档按固定长度切开,一个条件的说明和它的例外被分到了两个块里。检索命中前半段,模型就用前半段回答,语气还很确定。这种错误最难发现,因为答案读起来通顺,只是不完整。

第二是问题本身。用户问“能不能退”,而文档里写的是“退货条件”。语义相近,但意图差得很远。用户的问法里还常常省略主语和场景,这些信息根本不在文档里。

第三是上下文拼装。召回的片段是对的,拼进去的时候顺序错了,模型把补充说明当成了主条款。后来我在每个片段前面加了来源和章节标题,这类错误明显少了。

向量检索解决的是“找得到”,不是“答得对”。

候选越多,噪声越多

我做过一个很朴素的实验:把 top-k 从 5 调到 20。

结果准确率反而降了。多出来的十五个片段里,有相关的,也有只是“词面相似”的。模型的注意力被稀释,回答开始变得含糊。

有效的做法是加一层重排,而不是扩大召回:

1
2
3
chunks = split(doc, by="section", max_tokens=400, overlap=50)
hits = index.search(question, top_k=20)
hits = rerank(question, hits)[:5]

代价是延迟增加,一次查询多了几百毫秒。对一个内部知识库来说可以接受,对实时对话就未必。所以不是所有场景都值得上重排,要先看用户能等多久。

我现在会先做的三件事

顺序变了。

先建一个五十条左右的评测集,问题全部来自真实用户,不自己编。编出来的问题往往太规整,覆盖不到真实的模糊表达。

再把每次召回的原文打印出来,不看分数,只看内容对不对。检索分数高但内容是错的,这种情况比想象中常见。

最后才决定要不要上向量库。很多场景里,关键词检索加结构化过滤已经够用,而且结果可解释。等这三步做完,向量库到底该用哪一款、索引怎么建,反而变成了一个很小的问题。

还没解决的部分

多轮对话里的指代,我一直没处理好。用户说“那这个呢”,检索层拿不到足够的信息,只能靠上一轮的上下文硬猜。这个问题看上去是检索问题,实际上是会话状态管理,和向量库没有关系。

评测指标也有坑。召回率和答案正确率是两件事,前者可以通过放宽阈值刷上去,后者不会跟着动。我现在的评测集里,每条问题都标了标准答案和必须出现的关键条件,只看最终回答,不看中间分数。

我现在对 RAG 的理解是:它首先是一个数据工程问题,其次才是检索问题,最后才是模型问题。顺序反过来做,也能跑起来,只是会在某个地方卡住,然后回头重来。

关于本文

由 bl8ds 撰写,采用 CC BY-NC 4.0 许可协议。

#RAG #检索 #工程实践