最近在做一个内部知识库问答,用的LangChain+OpenAI,文档主要是PDF和Word,大概几百份。目前问题:用户问“报销流程”,召回的全是“差旅费标准”这种泛泛的内容,精准条款反而排很后面。我试过按固定字符(500字)分块,也试过按段落分,embedding用的bge-large,效果都不理想。是不是我分块粒度有问题?还是说应该先做文档结构解析(比如标题层级)再决定切哪里?另外,向量检索之外是不是还得加一层关键词匹配来兜底?求有经验的朋友指点一下,卡了好几天了,挺迷茫的。
RAG召回结果太差,是不是我分块方式有问题?
全部回复
共 12 条结构解析确实该做,标题层级切分比固定字符靠谱多了,再加层BM25关键词兜底,效果能明显改善。
说实话我之前也遇到过一模一样的情况,分块方式折腾半天,最后发现瓶颈不在字符数,而在文档结构本身。PDF和Word里那些标题层级、表格、条款编号,如果直接按段落切,语义信息就碎了,尤其报销流程这种内容,前置条件和操作步骤往往跨段关联,单纯靠向量相似度很难把它们拉回来。我后来改用基于标题和列表的递归切分,先识别文档大纲,把每个章节下的内容整体作为一个块,再对超长的块按语义断点二次切,召回明显稳了。另外你提到关键词兜底,这个真的很必要,尤其内部知识库有大量专有名词和条款编号,向量检索对精确匹配天生弱势,我习惯用BM25或者ES的match query做一层召回,然后跟向量结果做RAG fusion,分数加权合并,效果比单靠向量好很多。还有个小细节,bge-large对中文长文本的区分度其实一般,可以试试把query也做一下改写,比如把“报销流程”扩展成“报销申请步骤”“报销审批流程”再检索,召回会准一些。最后想问下,你那些文档里有没有扫描版的PDF?如果有,OCR质量对分块的影响其实比embedding模型还大,这点也得排查下。
分块方式确实是个大坑,但我觉得你现在的核心问题可能不在分块粒度上,而是压根没把文档结构利用起来。几百份PDF和Word,如果直接按字符或者段落硬切,那些藏在表格里、附录里的精准条款很容易被割裂成碎片,跟泛泛的概述混在一起,向量相似度自然会被“差旅费标准”这种高频词带偏。我个人建议你先跑一遍文档解析,把标题层级、章节编号、甚至表格结构提取出来,然后按语义块切,比如一个完整的条款或小节作为一个chunk,这样embedding时上下文才完整。另外,bge-large对中文长文本的效果其实一般,你可以试试换成bge-m3或者干脆用OpenAI的text-embedding-3-large做对比,有时候模型切换比调参数见效快。至于关键词匹配兜底,我强烈建议加,尤其是内部知识库这种术语密集的场景,做个简单的BM25或者Elasticsearch召回,跟向量结果做RRF融合,能直接救回那些被向量模型忽略的精确条款。最后想问下,你预处理时有没有做OCR或者格式清洗?有些扫描版PDF如果不转成文本,后面所有步骤都是白搭。
标题层级切块是真关键,光按字数分肯定把条款拆碎了,先整结构再定粒度试试。
结构解析那步真别省,几百份文档里肯定有大量条款藏在三级标题下面,无脑切分等于把答案打散。我建议先抽一下PDF里的目录和标题层级,按最小语义单元(比如条款)去切,再给每个块打上父标题的metadata,检索时候能加权。另外关键词兜底必须有,bge对专有名词和短查询有时候就是会飘,整个BM25混合召回,分数简单归一化再融合,效果立竿见影。你可以先拿报销流程那几页手工调一下分块规则,看看召回排序变不变,再决定要不要动embedding。
结构解析很关键,先按标题层级切块再考虑混合检索,关键词兜底能救急但别依赖。
试试把精准条款单独切出来,再配个重排模型,效果能好不少。
关键词兜底必须加,混合检索比单向量靠谱得多。另外分块前把标题层级带上,效果立刻不一样。
你这个问题我也踩过坑,bge-large对长文档确实没那么友好,尤其固定500字切分容易把条款上下文切断。建议先用pdfplumber或者marker把文档结构抽出来,按标题层级分块,小标题下的内容单独切,这样语义更聚合。另外关键词检索兜底真的有必要,我现在都是BM25和向量混合召回,用RRF融合一下,效果立竿见影。你试试把标题文本单独拼进块内容里,权重高一点,精准条款排名会明显靠前。
试试按标题层级切块再加bm25关键词召回,混合检索一般能救回来不少。
试试按标题层级切块+段落合并,再叠加BM25关键词召回混合排序,应该能救回来不少。
说实话我觉得你这个问题不全在分块粒度上,bge-large对长文档的语义捕捉本来就容易偏向主题词,像“报销流程”这种动作型query,跟“差旅费标准”这种名词型内容天然就更近,因为语义空间里它们更“像”。你不如先试试把文档结构拆出来,按标题和章节层级切,每个块带上父级标题作为上下文,这样模型至少知道这块属于哪个环节。另外固定500字和按段落都有个毛病,就是会把条款和解释混在一起,导致向量被稀释,我建议你按“条款”为单位切,比如每个编号条目单独一块,这样召回精度会好很多。关键词兜底我觉得必须有,特别是内部知识库这种术语固定的场景,向量召回漏掉精确匹配太常见了,可以先用BM25或者ES的match跑一遍,然后把结果和向量召回合并去重,或者用RRF重排一下。我之前做过类似的项目,就是“结构解析+条款级切块+混合检索”一起上,效果比单纯调embedding或分块参数提升明显得多,你可以先拿几个高频问题做个小批量测试,别一次全量跑。还有个小坑,PDF和Word的表格内容如果直接文本化会乱,最好单独抽出来做表格摘要块,不然检索时全是乱序数字。
结构化解析真得加上,几百份PDF里标题层级往往比正文信息密度高,直接按段落切会把条款的从属关系打散。我试过先用pypdfium2抽文本,再按标题和字号定位分块,召回率能好不少。另外你提到关键词兜底,这个我强烈建议做,尤其报销流程这种强术语场景,用bm25或者es做混合检索,比单靠向量稳很多,bge对长尾词确实容易飘。最后补一句,可以试试把常见问题抽象成几个模板问题,用few-shot微调一下embedding,有时候不是分块的问题,是向量空间没对齐你的业务语义。