这道题其实问的是,在[[RAG]]系统里,怎么在保证检索精度的同时,把延迟和成本压下来。本质是个工程平衡问题。我主要从检索、[[重排]]、查询改写、混合方案几个方向来说。
先说向量检索层。最常用的索引是 [[HNSW]],它本质是图索引,在召回率和速度之间做得比较均衡。但有两个关键参数要调:M控制每个节点的邻居数,ef控制搜索宽度。M太小图会不连通,召回会崩;ef太大延迟就上去了。一般我会先拿业务数据跑一下,看Recall@100在什么水平,再决定参数。另一个常见优化是量化压缩,像 [[Product Quantization]],可以把向量从32位压缩到8位,内存直接降到四分之一。但代价是精度损失,所以如果业务对召回要求很高,比如合同风控场景漏掉一条可能出法律风险,那量化就要慎用,或者只在粗排阶段用,精排再用原始向量。
工程上还有个常见套路是预过滤加向量搜索。比如电商场景,用户搜"苹果手机",但只想要黑色256G的,那就先用metadata过滤掉不匹配的SKU,再对剩下的做向量检索,候选集能小一个数量级。这里有个坑:如果过滤条件太强,候选集太小,向量检索的精度反而会下降,因为近邻不够了。所以我会设一个最小候选数,低于它就放宽过滤。
再一个重点是重排序。说白了,向量检索是粗筛,快速拿回Top-K,比如100条;重排是精排,用更复杂的模型从里面选出Top-5。精排模型我常用 [[Cross-Encoder]],精度高但延迟大,因为每对query-doc都要过一遍。所以线上一般只对Top-50或Top-100做重排。另一种方案是 [[ColBERT]],它用token级交互,比Cross-Encoder快,但精度稍低。具体选哪个要看业务SLA:如果要求100毫秒内返回,那ColBERT更现实;如果精度优先级高,比如医疗问答错一个可能误诊,那Cross-Encoder加缓存也能扛。
还有查询扩展。很多query是模糊的或口语化的,比如用户问"那个车",直接检索肯定不行。我会用LLM做query改写,把"那个车"扩成"特斯拉Model Y"或者根据上下文补全。另一个技巧是 HyDE,让LLM先生成一个假设答案,再用这个答案去检索。这能缓解query和文档的语义鸿沟。但前提是LLM生成的质量要够,如果LLM跑偏了,检索反而变差。所以上线前我会用一批bad case跑一下,看改写后的召回率有没有提升,没有就回滚。
最后说[[混合检索]]。纯向量检索对专有名词、ID号、精确匹配很弱,比如搜"订单号OD2024001",语义上它就是个字符串,向量可能找不到。所以我会把 [[BM25]] 和向量检索结合起来。融合策略用 RRF 最简单稳定,不用调权重,直接按排名倒序求和。但混合不是万能的,它会引入两套系统的维护成本,而且如果关键词和向量的结果差异很大,融合后可能两边都不讨好。所以我的做法是:先跑离线实验,看单独用向量和单独用关键词的Recall@K,再决定混合的权重,甚至做动态路由,比如检测到query里有数字或ID,就走关键词,否则走向量。
其实还有一个方向是自适应检索,根据query的难度动态决定召回多少条,或者决定要不要触发重排。比如简单问题只向量检索就够,复杂问题才走全链路。这个在线上能省不少算力,但实现起来要小心,因为判断query难易本身就不容易,搞错了反而降低体验。
所以整体上,我会把RAG检索看成一套组合拳,没有银弹。核心是理解业务场景的精度和延迟要求,然后选最合适的方案组合,上线前一定要用业务数据验证,特别关注bad case,因为往往就是那1%的case决定了用户对系统的信任度。