最近在做一个企业内部知识库的问答系统,用了Milvus+OpenAI的embedding模型(text-embedding-3-small),数据是几千篇技术文档。按说向量检索应该比关键词搜索更智能,但实际测试发现,很多用户问“打印机卡纸怎么办”,向量检索出来的结果反而没有Elasticsearch直接搜“卡纸”来得准。我怀疑是不是我分块策略有问题,目前是按段落切分,每个块512 token,有没有大佬指点一下?或者是不是embedding模型对中文长尾问题支持不够好?
向量数据库做RAG时,为什么检索准确率还不如直接用关键词搜索?
全部回复
共 112 条你这情况我太熟了,之前做客服文档检索也踩过这坑。问题八成出在分块上,512token对技术文档来说太长了,一个段落里可能混了好几个知识点,向量一平均就把关键信息稀释了。建议试试按语义边界切,比如句子或小标题,200-300token左右,检索时再配合关键词做重排,效果会明显不一样。另外中文长尾词确实吃embedding模型,如果预算允许,换个中文优化过的模型比如bge-m3,或者干脆用混合检索,别全押在向量上。
你这情况我太熟了,之前做设备维修知识库也栽过同样的坑。问题大概率不在embedding模型,而是分块策略加查询意图的错配——512 token对技术文档来说太碎了,一段话里往往藏着操作步骤和故障现象,向量化后语义被平均掉,反而不如关键词精准命中名词。我后来改成按标题和章节层级切分,每个块控制在800-1000 token,并且把“故障现象”和“解决方案”拆成不同字段存,检索时用混合查询(向量召回Top50,再用BM25重排),准确率直接翻倍。另外text-embedding-3-small对中文长尾的确偏弱,你可以试试用bge-large-zh或者m3e做替换,或者干脆对用户query做实体抽取,把“卡纸”这类关键动作词提出来加权。还有个细节,你Elasticsearch搜“卡纸”准,很可能是因为文档标题或首段就包含了这个词,向量检索却把整段语义都揉进去了,试试给段落开头和标题更高权重,或者用Reranker模型(比如bge-reranker)二次过滤,效果会立竿见影。
说实话你这个现象挺典型的,我这边之前做客服工单检索也踩过类似的坑。分块策略只是表面问题,核心在于embedding模型对“短查询+长文档”的匹配天然不占优,尤其你用的是text-embedding-3-small,它对中文里那种“卡纸”这种强实体词的表征,反而不如BM25的精确词频匹配来得干脆。我试过把分块改成按句子+小标题组合,然后对每个块做关键词加权(比如把文档标题和首句重复拼进向量里),准确率能上来一点,但代价是索引体积翻倍。另外你可以考虑混合检索,别纯靠向量,用ES的布尔查询先粗筛一遍,再对候选集做向量重排,这样“卡纸”这种硬词不会被向量空间里的语义漂移带偏。还有个坑是OpenAI那个模型对中文长尾词(比如“出纸槽卡顿”)的token切分很碎,你试试用BGE或者bge-m3的中文微调模型,效果可能直接提升一截。最后问下,你query进来有没有做同义词扩展?比如“卡纸”和“卡住”在向量空间里可能离得挺远的,但关键词搜索天然就能命中。
分块确实挺影响结果的,512个token对技术文档来说可能太碎了,尤其打印机卡纸这种操作步骤往往横跨好几个段落,语义被切散了。另外embedding模型对中文里“卡纸”这种强关键词的场景确实不占优,向量检索擅长的是语义匹配,但你这需求其实关键词就能精准命中,不如试试混合检索,把ES和Milvus结果做个加权融合,或者调整分块策略,按章节或者语义边界来切,应该会好很多。
你这问题多半出在分块上,512 token对技术文档太碎了,试试按章节分块加上重叠,效果会明显不一样。
说实话我这边也踩过类似的坑,后来发现问题往往不是embedding本身,而是分块粒度跟query的匹配度。512token对技术文档来说有点太粗了,很多操作步骤里的关键动作词会被上下文稀释掉,反而丢了“卡纸”这种强信号词。建议试试先粗切块再按句子边界二次召回,或者干脆对标题和首段单独建一个加权索引。另外中文长尾确实是痛点,可以对比一下bge-m3或者m3e这类国产模型,有时候小模型在垂直领域反而更稳。
试试把512token改成256或者按标题+首段切,卡纸这种词向量真不一定比得上倒排索引。
我之前也踩过类似的坑,后来发现问题多半出在分块粒度上。512 token对中文技术文档来说太碎了,一个完整的操作步骤被拦腰截断,向量语义自然就散了。你试试改成按标题或章节层级切分,或者用递归字符分割器保留段落上下文,效果可能立刻不一样。
另外别太迷信embedding模型,text-embedding-3-small对中文长尾词确实一般,尤其是“卡纸”这种偏口语化的词,向量空间里可能跟“纸张堵塞”离得远,反而被无关内容干扰。你可以对比下换成bge-m3或者国产的text2vec,说不定有惊喜。
还有个容易被忽略的点,就是检索策略本身。向量召回后最好加个重排环节,比如用cross-encoder对top20结果再打分,不然单纯靠余弦相似度排序,很容易把语义相近但答案错误的段落顶上来。我之前用Rerank模型后准确率直接涨了十几个点。
最后想说,关键词和向量不是二选一,很多生产系统都是混合检索,ES先粗筛再喂给向量精排,或者反过来。你这个场景如果用户问题都特别具体,甚至可以考虑给向量检索加个关键词加权,别让纯语义主导结果。
分块太粗了,512token对技术文档来说语义容易跑偏,试试按小节切+重叠窗口,召回率会明显提升。
我觉得你这个问题挺典型的,其实不是向量检索不行,而是它跟关键词搜索解决的压根不是同一类问题。像“打印机卡纸”这种高频、意图明确的故障词,ES的倒排索引天然擅长,因为用户问的就是文档里反复出现的核心词,而embedding模型反而会把“卡纸”和“搓纸轮”“传感器”这些相关但非直接答案的词混在一起,拉低了排序。你的分块策略确实值得怀疑,512 token对技术文档来说太长了,一个段落可能包含多个操作步骤或故障场景,向量被平均后语义就模糊了,我建议切成256甚至128,并且尽量按标题或步骤边界切,而不是硬切段落。另外,text-embedding-3-small对中文长尾支持确实一般,尤其在专业领域术语上,你可以试试用bge-large-zh或者m3e这类中文专用模型做对比,成本不高但效果可能差很多。还有个思路是混合检索,先用ES把“卡纸”这类精确词捞出来,再用向量做语义召回,最后用rerank模型合并排序,这样互补性很强。我自己的经验是,向量数据库在“模糊描述但意思明确”的查询上优势明显,比如“电脑开不了机但风扇在转”,但在“直接说故障名”的场景下真拼不过关键词。你可以先拿二十个真实用户query跑一下,看看坏结果到底是召回漏了还是排序靠后,别急着换方案,定位问题比盲目调参重要。
分块策略确实值得再调调,512个token对技术文档来说可能太长了,尤其“卡纸”这种关键信息埋在段落中间,embedding会被其他内容稀释。我之前试过按标题和章节切,再配合小窗口重叠,效果提升很明显。另外中文长尾词确实容易吃亏,text-embedding-3-small对专业术语的语义理解有限,建议你拿几个典型问题对比一下,看看是不是某些词压根没被正确向量化。
这个现象其实挺典型的,不是embedding模型不行,而是你的场景刚好踩在了向量检索的短板上。像“打印机卡纸”这种高频、意图明确的操作类问题,关键词匹配天然有优势,因为用户问的就是文档里反复出现的术语本身,而embedding反而会把语义相近但字面不同的表述混在一起,导致相关性被稀释。另外512token的段落切分对技术文档来说可能太大了,一段里往往包含多个操作步骤或概念,向量化之后特征会被平均掉,检索时很难精准命中局部关键信息。我建议你先试下把分块缩小到128-256token,或者按标题/小标题切,同时把原始段落和切分后的块都存下来,召回后做重排序。还有个思路是混合检索,Elasticsearch跑关键词召回,Milvus跑语义召回,最后用cross-encoder或者简单的规则融合排序,效果通常会好很多。至于中文长尾问题,text-embedding-3-small对常见技术词没问题,但遇到你们内部的黑话或缩写肯定不行,有条件的话可以微调一个领域embedding,或者至少把高频术语扩充进分词词典。你现在的困惑我特别理解,因为很多人默认向量比关键词高级,但实际工程里它们互补才是常态。