最近在做一个人事制度问答的RAG系统,用的bge-m3做embedding,存到milvus里。现在有个很头疼的问题:用户问“年假怎么休”,检索出来的片段有一半是考勤、绩效甚至招聘的,相关性排序一团糟。我试过调topk从5降到3,效果不明显,召回的片段本身就不准。chunk_size也试过256和512,感觉变化不大。想问下大家,除了换embedding模型或者微调reranker,有没有什么工程上比较实用的优化思路?比如混合检索(BM25+向量)或者query改写之类的?有没有人实际在项目里用过,效果提升明显吗?
RAG检索老召回一堆不相关内容,除了调topk还能怎么优化?
全部回复
共 4 条混合检索值得试,BM25对人事制度这种术语匹配挺友好,能兜住向量漏掉的精确词。另外query改写别小看,“年假怎么休”改写成“年假申请条件+天数规定”再检索,命中率会高不少。你chunk_size调了但有没有考虑过把制度条文按条款粒度切?之前我做类似场景,把“休假”“考勤”这类强分类标签直接作为metadata过滤,比单靠相关性排序干净多了。
混合检索值得试,bm25能把那些带“年假”关键词但语义偏的片段拉回来,向量负责语义扩展,两边结果去重合并再排序,效果会比单路强不少。另外你可以看看召回片段里是不是有大量重复的固定话术,比如制度总则里那些套话,这种用MMR或者按位置权重降一下能压掉不少噪音。query改写对问法太口语的情况有用,但你这问题更可能是chunk切分时把不同制度条款混在一个片段里了,建议试试按条款标题做结构化切分,让每个chunk只讲一个规则。
混合检索在我们项目里提升挺明显的,BM25对“年假”这种关键词的精确匹配帮了大忙,向量那路负责语义兜底。另外可以试试在检索前先做一轮query意图分类,人事制度这种场景类目清晰,把考勤、招聘的chunk在metadata里打上标签,检索时直接过滤掉,比调topk管用多了。reranker如果暂时不想微调,bge-reranker-base先拿来用效果也不差。
年假这种问法确实容易翻车,因为“年假怎么休”本身太短了,bge-m3再强也扛不住query信息量太少,向量空间里跟考勤、绩效的embedding距离拉不开很正常。你调topk和chunk_size没用,是因为问题出在召回源头,不是数量问题。我建议先把query改写加上,用LLM把“年假怎么休”扩成“员工年假天数、申请流程、休假条件”这种带具体意图的句子再去做检索,光这一步在人事场景里提升就很明显。混合检索也值得上,BM25对“年假”这种关键词的精确匹配比纯向量靠谱,milvus现在也支持sparse向量,bge-m3本身就能出稀疏表示,直接做dense+sparse混合不用再引一套ES。另外你chunk切的时候可以带上制度标题和章节路径做metadata,检索时先按制度类型过滤一层,比如限定在“假期管理”类目下再算相似度,能砍掉一大半无关片段。reranker如果不想微调,先用bge-reranker-v2-m3做粗排后的精排,成本不高但效果立竿见影。这几招叠一起,比单换embedding模型性价比高多了。