这道题问的是[[RAG]]检索模块,其实本质是问怎么在召回率和精度之间做工程取舍,而不是单纯讲流程。我按自己的理解来拆解一下。
先说检索模块怎么工作。核心就三步:第一步,离线把文档切碎,然后用 [[Embedding]] 模型编码成向量,存到 [[Vector Database]] 里。第二步,用户来了一个查询,同样编码成向量,然后做 [[ANN]] 近似最近邻搜索,召回 TopK 候选。第三步,把结果和元数据拼起来,送到生成模块。这里面有个容易忽略的细节:切分策略直接影响检索质量。比如固定大小切分,简单但容易切断语义,导致召回片段不完整;而语义切分虽然好,但依赖模型质量,而且对长文档可能切出太多碎片。所以实际落地时,我倾向于用分层切分:先按段落切,再对过长段落做[[滑动窗口]],同时保留父子文档的映射关系,这样检索时可以用子块匹配,返回时带出父块上下文。
接下来重点讲三个提升相关性的方法,也是我实际中经常组合用的。
先看[[混合检索]]。说白了就是向量语义检索加关键词匹配,比如 [[BM25]]。向量检索擅长语义相似,但遇到专有名词、订单号这种精确匹配就抓瞎。举个例子,电商客服场景,用户问“退款到哪了”,向量检索可能召回各种退款流程的文档,但关键词“退款订单号”能直接命中具体记录。我一般用 RRF 倒数排序融合,简单有效。这里有个前提:你得有好的[[分词器]]和倒排索引,不然关键词检索本身就不准。
接着说[[重排序]]。向量检索用的是 [[Bi-Encoder]],查询和文档分开编码,效率高但精度有限。重排阶段我会换成 [[Cross-Encoder]],把查询和文档拼在一起做交互注意力,相关性打分更准。但代价是慢,所以一般走两阶段:先用向量检索召回 Top 100,再用轻量重排模型筛出 Top 5。这样延迟增加不大,但精度提升明显。
再补充查询改写。用户的问题经常表述模糊,比如“怎么省钱”,文档里写的可能是“降低成本的策略”。直接检索可能漏掉。我会用 LLM 做同义扩展,或者用 HyDE 方法:让模型先根据问题写一个理想答案,然后用答案的向量去检索。这样隐式地把问题转成了文档侧的表达。但有个风险:如果 LLM 生成的假答案质量差,反而会带偏检索。所以 HyDE 更适合问题清晰但措辞不匹配的场景,如果问题本身就很模糊,不如先做多轮对话澄清。
其实还有一个方向是上下文压缩,检索回来的长文档里经常夹杂噪声,用 LLM 提取相关片段能提升生成质量。但这里有个取舍:压缩本身增加了延迟和成本,而且压缩模型如果不够强,可能丢掉关键信息。所以 我更倾向把压缩作为可选模块,只在文档特别长、噪声特别大时才启用。
最后总结一下我的判断:没有银弹。混合检索是基础,几乎必上;重排序是精度提升的利器,但延迟敏感场景要慎用;查询改写适合问题模糊的交互场景。上线前我会特别关注检索延迟和召回率之间的平衡,用一组离线 query 做回归测试,确保改动不导致 Recall 掉点。如果只让我选一个方向深耕,我会选 重排序,因为它直接提升最终生成质量,而且随着 Cross-Encoder 模型越来越轻量,延迟问题在逐步缓解。