最近在做企业知识库问答,用的RAG方案是bge-large向量检索+monoT5精排,前期测试准确率还行,结果一上线真实用户提问就翻车。发现几个问题:1)用户口语化严重,比如“合同里违约金咋算的”检索到的段落根本不在点子上,召回Top20里有用的就2-3条;2)我试着把重排权重调高,相关性是上来了,但回答里开始出现原文没有的细节,比如把A合同的条款硬安到B合同上;3)还试过加query改写,但效果不稳定,有的改写后反而更偏。想请教下大家,这种生产环境下的检索退化,是应该先优化embedding微调,还是重点搞重排序策略?或者干脆换混合检索(BM25+向量)?有没有踩过坑的同学分享下排查思路?
RAG项目上线后召回率暴跌,重排救了回来但幻觉反而变多了?
全部回复
共 49 条之前我们上线也遇到过类似情况,后来发现根因不在重排,而是召回阶段就丢了关键信息,重排只是矮子里拔将军。建议你先别急着微调embedding,把BM25和向量检索的结果做融合,很多口语化query靠关键词反而更稳。另外你提到的幻觉问题,我猜是重排分数虚高把错误片段顶到前面了,可以试试在生成前加一道事实校验,或者直接对比原文看片段是否真的支持回答。
混合检索得加上,口语化query靠BM25兜底,向量召回太吃语义精确度了。重排调太狠容易让模型脑补,先卡个相关性阈值。
说到这个我太有同感了,我们之前上线一个法律咨询的RAG也是这德行,用户问“试用期被辞退有赔偿吗”,召回的全是“试用期约定”和“辞退程序”的条款,压根拼不到一块去。后来排查发现,bge-large对口语化短query的语义理解真的弱,尤其缺行业术语泛化,你那个“违约金咋算”其实跟“违约金的计算方式”在向量空间里距离挺远的。
我建议你先别急着调重排,monoT5本质上是在给定候选里挑相对好的,如果Top20里就没几条靠谱的,重排再强也是矮子里拔将军,反而会把那些语义近但事实错的段落排到前面,幻觉自然就多了。混合检索确实值得试,BM25对实体词(合同、违约金)很敏感,能先把硬匹配的段落捞回来,再跟向量结果做RRF融合,我们当时加了之后召回Top10的有效率从20%提到50%左右。
另外你说的query改写,我试过用LLM做,但得加约束,比如只拆解关键词、不扩展同义词,否则它容易把“咋算”改写成“怎么计算方式”这种画蛇添足的,反而更偏。还有个坑是embedding微调,别一上来就全量微调,先用你线上真实用户query去挖掘难负样本,做领域适配,不然容易过拟合到测试集。排查顺序建议是:先看召回源头,统计Top20里有效段落占比,如果低于30%,那问题在检索不在重排,再考虑是否切分粒度太大,比如长合同按章节切而不是固定512字。
混合检索先加上吧,BM25兜底口语化查询很有效,重排调太猛确实容易幻觉。
重排权重别拉太高,先试试召回阶段用BM25+向量融合,比单调精排稳得多。
混合检索真得赶紧加上,BM25对口语化query的容错比向量好太多,我之前也是纯向量召回惨不忍睹,加了关键词权重立马稳了。重排调太高确实会引入幻觉,建议你检查下monoT5是不是把不相关段落也硬排上来了,可以试试给重排结果加个相关性阈值过滤。query改写这玩意儿我也试过,小模型容易矫枉过正,不如直接拿用户原query和改写后的各跑一遍召回再合并。
混合检索先加上吧,你这明显是召回源头就偏了,重排救不回来还容易引入幻觉。
加个BM25兜底,再对重排阈值卡严点,比瞎调embedding快。
我遇到过几乎一样的情况,上线前测试集太干净了,用户一开口全是口语和指代,召回崩是必然的。你提到的重排权重调高反而带来幻觉,这个我太有同感了——monoT5这类精排模型本质是在“矮子里拔高个”,Top20里本来就没啥靠谱段落,它硬挑一个相对相关的出来,LLM再一生成,细节就自己脑补了,A合同条款安到B合同上我这边也出过。
我觉得你先别急着微调embedding,那个成本高且见效慢,更关键的是先解决“根本召不回”的问题。混合检索(BM25+向量)几乎是生产环境的标配了,BM25对关键词和专有名词(比如“违约金”、“合同编号”)特别敏感,能补上向量检索对精确匹配的短板。我当时的做法是双路召回,BM25和向量各取Top30,然后合并去重再进重排,召回率立刻稳了不少。
至于query改写,我试过LLM改写和规则改写,感觉它更适合“口语转书面语”这种明确转换,如果用户问题本身信息量就少(比如只说“那个条款咋写的”),改写反而会放大歧义。你可以先做轻量预处理,把明显的口语词映射到知识库里的标准术语,别全盘依赖模型重写。
还有个排查点你可能忽略了:检查一下用户提问里的实体是不是在知识库里确实存在。如果检索结果里压根没有B合同的内容,那模型把A合同内容安过去,本质是它在硬凑答案,这时候该考虑的是加一个“无答案兜底”逻辑,而不是继续调重排。我建议你按这个顺序试:先上混合检索,再调重排阈值(宁可拒答也别给幻觉),最后看badcase分布决定要不要碰embedding。
我们之前也遇到过类似的坑,口语化query直接把向量检索带偏了,后来发现单纯调重排权重治标不治本,反而把噪声放大了。建议先上混合检索,BM25做关键词兜底基本能保住“违约金”这类实体词,至少召回不会那么飘。至于幻觉,我怀疑是重排把高相关的片段硬塞给LLM,但片段本身跨文档切碎了,信息冲突没被识别出来,可以试试在生成前加一道事实校验,或者按文档来源做分组再送prompt。另外query改写对口语真不一定友好,不如直接做同义词扩展,成本低见效快。
混合检索先上,BM25兜口语化漏召,重排权重别拉太高,不然模型容易自己编。