语义搜索架构:召回、粗排、精排三阶段缺一不可 语义搜索架构召回、粗排、精排三阶段缺一不可一、为什么只靠向量相似度搜索不够向量检索Vector Search是语义搜索的基石。将查询和文档都编码为向量通过余弦相似度或欧氏距离找最相近的 Top K 文档。这套方案的简洁性使得它在 RAG 系统中被大量采用。但在实际使用中向量检索有三个明显缺陷。第一embedding 模型对语义的理解有限长文本的全局向量可能丢失关键细节。第二相似度最高的文档不一定是最相关的文档——向量空间中的近邻可能只是词汇层面相似。第三跨领域的零样本泛化能力不足在训练集未见过的领域检索质量断崖式下降。搜索系统的目标不是找到最像的而是找到最相关的。这两个目标在向量空间中不是完全重合的。弥补这一差距的方法就是多阶段检索架构用不同粒度、不同成本的方法逐级筛选候选集。见证奇迹的时刻是当召回阶段用向量检索抓海选粗排阶段用轻量模型筛候选精排阶段用重量级模型定最终排名——三阶段配合后检索准确率比单一向量搜索提升了 30%~50%。二、三阶段检索架构的设计原理graph TD A[用户查询] -- B[查询理解模块] B -- B1[查询改写与扩展] B -- B2[意图分类与实体抽取] B1 -- C[第一阶段: 召回] B2 -- C C -- C1[向量检索br/Top K1000] C -- C2[关键词检索br/BM25, Top K100] C -- C3[知识图谱检索br/实体链接] C1 -- D[第二阶段: 粗排] C2 -- D C3 -- D D -- D1[轻量 Cross-encoderbr/Top K50] D -- D2[规则特征: 时效性/热度/权威性] D1 -- E[第三阶段: 精排] D2 -- E E -- E1[重量级 Cross-encoderbr/逐条精排] E -- E2[多维度特征融合br/相关性时效权威多样性] E1 -- F[最终结果: Top K10] E2 -- F style C fill:#e1f5fe style D fill:#fff3e0 style E fill:#e8f5e9 style F fill:#f3e5f5第一阶段多路召回。不是单一的向量检索而是向量、关键词和知识图谱三条通路同时召回候选文档。三条通路互为补充向量检索捕捉语义相似性关键词检索保证字面匹配的精确性知识图谱检索利用结构化知识做实体关联。多路召回保证了高召回率Recall 95%但精确率很低候选集中 90% 以上是不相关的。第二阶段粗排Re-ranking Lite。对召回的 1000 候选文档用轻量级双塔模型Query-Document BERT快速打分筛到 Top 50。粗排模型的速度要求很高——每条文档的处理时间必须在 1~5 毫秒级别。精度不如精排但速度够快可以过滤掉明显不相关的文档。第三阶段精排Fine Ranking。对 Top 50 候选文档用重量级的 Cross-encoder如交互式 BERT逐条精排。精排模型将查询和文档拼接后送入模型做全注意力交互精度最高但速度最慢每条 20~50 毫秒。通过前两阶段的筛选精排阶段只需要处理 50 条文档总耗时在可接受范围内。三、三阶段检索引擎的实现以下代码展示了简化版的三阶段检索管道。import numpy as np from typing import List, Dict, Tuple, Any from dataclasses import dataclass dataclass class SearchResult: 搜索结果 doc_id: str content: str score: float source: str # 召回来源: vector/keyword/kg class MultiStageSearcher: 三阶段语义搜索引擎 设计原因将检索拆分为三个阶段用逐渐增加的模型精度 换取逐渐缩小的候选集规模。三个阶段的精度-速度关系 召回高召回低速度大集- 粗排中精度高速中集 - 精排高精度低速小集。 def __init__(self, embedding_model, bm25_index, light_reranker, heavy_reranker): self.embedding_model embedding_model self.bm25_index bm25_index self.light_reranker light_reranker self.heavy_reranker heavy_reranker def search(self, query: str, top_k: int 10) - List[SearchResult]: 执行三阶段搜索 # 阶段1: 多路召回 candidates self._multi_recall(query) # 阶段2: 粗排 reranked self._light_rerank(query, candidates) # 阶段3: 精排 final self._heavy_rerank(query, reranked[:50], top_k) return final def _multi_recall(self, query: str) - List[SearchResult]: 多路召回 设计原因单路召回容易漏掉语义词相关但字面不匹配的文档。 多路召回通过不同机制的互补大幅提升召回率。 不同召回源的结果使用统一的 SearchResult 格式归一化。 results [] # 通路1: 向量检索 # 设计原因对于语义模糊或抽象概念的查询 # 向量检索能捕捉到文本中不直接出现但语义相关的文档。 query_vector self.embedding_model.encode(query) vector_results self._vector_search(query_vector, top_k1000) for doc in vector_results: results.append(SearchResult( doc_iddoc[id], contentdoc[text], scoredoc[score], sourcevector )) # 通路2: 关键词检索 (BM25) # 设计原因对于包含专有名词或精确术语的查询 # 关键词匹配的精确率远高于向量检索。 keyword_results self.bm25_index.search(query, top_k100) for doc in keyword_results: results.append(SearchResult( doc_iddoc[id], contentdoc[text], scorefloat(doc[score]), sourcekeyword )) # 按 doc_id 去重保留最高分 seen {} for r in results: if r.doc_id not in seen or r.score seen[r.doc_id].score: seen[r.doc_id] r return list(seen.values()) def _light_rerank(self, query: str, candidates: List[SearchResult]) - List[SearchResult]: 轻量粗排 设计原因使用双塔模型Query 和 Document 分别编码后点积 速度快1~5ms/条精度中等NDCG10 ≈ 0.75。 目标是保留 Top 50 候选进入精排。 q_vec self.light_reranker.encode_query(query) for candidate in candidates: d_vec self.light_reranker.encode_document(candidate.content) # 将原始检索分数与粗排分数加权融合 # 设计原因不单纯依赖粗排分数 # 防止粗排模型的偏差覆盖掉召回阶段的正确排序。 candidate.score ( 0.3 * candidate.score 0.7 * np.dot(q_vec, d_vec) ) candidates.sort(keylambda x: x.score, reverseTrue) return candidates[:50] def _heavy_rerank(self, query: str, candidates: List[SearchResult], top_k: int) - List[SearchResult]: 精确重排 设计原因Cross-encoder 将 Query 和 Document 拼接后做全注意力 精度最高NDCG10 ≈ 0.85但速度最慢20~50ms/条。 这就是为什么必须在精排之前通过粗排大幅缩小候选集。 for candidate in candidates: score self.heavy_reranker.score(query, candidate.content) candidate.score score candidates.sort(keylambda x: x.score, reverseTrue) return candidates[:top_k]四、单阶段与多阶段检索的权衡分析多阶段检索的收益是明确的——用较低的总体计算成本获得较高的检索精度。但代价同样不容忽视。维度单阶段仅向量检索三阶段检索延迟50~100ms200~500ms准确率 (Recall10)0.850.92系统复杂度低高维护成本1 个模型3 个模型 2 个索引资源占用1 路3 路召回 2 路排序选择建议文档总量 10 万且召回质量要求不高单阶段向量检索即可。文档总量在 10 万~100 万两阶段召回 粗排足够。文档总量 100 万且对 Top 10 精度要求高必须三阶段。见证奇迹的时刻在于认识到检索系统的问题不是该用几阶段而是每阶段的定位——召回是保证不漏Recall 优先粗排是快速清理噪音精排是保证对Precision 优先。把一个阶段的职责分配给另一个阶段是检索系统设计的根本错误。五、总结语义搜索的三阶段架构将检索任务分解为召回高召回率、多通路、粗排中精度、高速度和精排高精度、低速度三个层次。每阶段处理的候选集规模依次缩小1000 → 50 → 10而精度依次提升。多路召回要求向量检索、关键词检索和知识图谱检索三通路互补共同保证召回覆盖率。粗排模型优先使用双塔架构以保证速度精排模型使用 Cross-encoder 以保证精度。三阶段架构的延迟代价约为 200500ms相比单阶段增加了 35 倍但检索准确率提升 30%~50%。文档规模越大多阶段架构的收益越明显。