这道题问的是[[RAG]]里语义匹配怎么做,本质上就是两件事:[[嵌入]]模型负责把文本变成向量空间里的点,[[向量数据库]]负责在这些点里快速找邻居。分开看都不复杂,但配合好了才能从海量文档里精准召回。
离线阶段先处理文档。文档解析完要[[分块]],这是第一个容易出问题的地方。固定长度分块简单,但照搬一个固定值容易把一句话拦腰切断,丢失上下文。语义分块按段落或句子切,保语义完整,但计算开销大。我的做法是把固定长度、递归切分、语义分块,以及无重叠、短重叠、较长重叠都作为候选,在同一评测集比较边界召回、答案正确率、索引体积和延迟,再确定线上参数。分块大小直接影响检索效果:块太小上下文不够,模型匹配不准;块太大噪声多,相似度被稀释。
分块完了用嵌入模型编码成向量,然后入库建索引。嵌入模型的选择很关键,通用场景我倾向用BGE或E5系列,它们在中文和英文上表现均衡。如果是垂直领域,比如金融或者医疗,最好用领域数据微调一下,用对比学习让模型理解专业术语。向量数据库我用[[Faiss]]或者Milvus,索引类型选[[HNSW]],参数ef_construction设200,ef_search设100到200,这样召回率高,建索引速度也能接受。距离度量用余弦相似度,最直观。
在线阶段,用户提一个问题,同样用嵌入模型编码成向量,然后去向量数据库里做[[近似最近邻]]搜索,召回Top-K个块。K值一般设10到50,具体看下游任务。如果后面有[[重排]]环节,K可以取大一点,比如50;如果直接拼上下文,K取10就够。这里有个坑:不是召回来的都能用。相似度低于某个阈值的块,比如0.6以下,直接丢掉,不然会引入噪声。阈值怎么定?上线前用一批标注数据跑一遍,画PR曲线,选一个平衡点。
说到边界划分,单纯靠向量检索有它的盲区。比如用户问“订单号12345”,这种精确匹配场景,向量检索往往不如[[BM25]]关键词检索准。反过来,用户问“退款流程有哪些”,语义泛化能力强,向量检索就占优。所以真正落地我倾向**[[混合检索]]**,把向量检索和关键词检索的结果按权重融合,比如用倒数排名融合,互补覆盖。
再举个例子,比如电商客服场景,用户问“满减和优惠券能叠加吗”。如果文档里只有“满减规则”和“优惠券规则”两个独立块,向量检索可能只召回其中一个。这时候分块策略就要优化,比如用[[Parent Document]]方式,先分大块再分小块,检索用小块,上下文用大块,保证信息完整。
上线前我还会特别关注几个风险。一个是嵌入模型的质量,如果模型在领域数据上表现差,整个检索效果都会崩,所以一定要先做离线评测,用Recall@K和MRR指标卡一下。另一个是索引的实时更新,知识库频繁增删时,HNSW索引的增量更新容易导致召回率下降,我一般用软删除加定时重建的策略。
还有一个方向,就是单向量检索有时候不够,比如用户问题里有否定词或条件限定,向量空间里可能区分不开。这时候可以用[[ColBERT]]这种交互式模型做后期交互,或者用[[Self-RAG]]让模型自己判断召回的内容是否相关。
所以整体上,我会把语义匹配看成嵌入模型定上限,向量数据库保下限。嵌入模型决定了能理解到什么程度,向量数据库决定了能不能快速找到。选型时我更看重嵌入模型在业务数据上的表现,而不是一味追求更快的索引。