资讯动态

混合检索与RRF融合 - 核心知识点速记

发布时间:2026/8/21 20:27:52 来源:尧图企业网站定制
第十一章 混合检索与RRF融合 - 代码详解版本章核心向量检索 BM25检索 → RRF融合 → 更精准的召回一、为什么需要混合检索面试必问向量检索擅长语义理解BM25擅长关键词匹配两者互补。场景向量检索BM25用户问申请条件 vs 文档写需要满足哪些要求能匹配匹配不到用户问社保缴纳记录可能模糊精确匹配用户问第二条 支持范围忽略数字精确匹配二、代码详解分词函数 tokenize_chineseimport reimport jiebaSTOP_WORDS {“的”, “了”, “是”, “在”, “和”, “有”, “就”, “都”, “而”, “及”}valid_pat re.compile(r^([\u4e00-\u9fff]|[A-Za-z0-9])$)def tokenize_chinese(text: str) - list[str]:text_low text.lower()words jieba.lcut_for_search(text_low)tokens []for w in words:if valid_pat.fullmatch(w) and w not in STOP_WORDS and len(w) 1:tokens.append(w)return tokens【代码解释】转小写让英文不区分大小写AI和ai能匹配lcut_for_search搜索引擎模式切得更细提高召回去停用词“的”了这种词出现太频繁没有区分度去单字词“我”你这种词没有区分度BM25检索函数 keyword_searchfrom rank_bm25 import BM25Okapifrom sqlalchemy import selectasync def keyword_search(session: Session,query: str,top_k: int,filters: RetrievalFilters,) - list[dict[str, Any]]:# 第1步从MySQL查询所有子块 statement ( select(DocumentChunk, PolicyDocument) .join(PolicyDocument, DocumentChunk.document_id PolicyDocument.id) .where(DocumentChunk.is_parent.is_(False)) ) # 第2步应用过滤条件 if filters.document_ids: statement statement.where(DocumentChunk.document_id.in_(filters.document_ids)) if filters.topic: statement statement.where(PolicyDocument.topic filters.topic) if filters.effective_before: statement statement.where( PolicyDocument.effective_date filters.effective_before ) rows list(session.execute(statement).all()) if not rows: return [] # 第3步构建BM25模型 corpus [tokenize_chinese(chunk.content) for chunk, _ in rows] bm25_model BM25Okapi(corpus) # 第4步计算分数 scores bm25_model.get_scores(tokenize_chinese(query)) # 第5步排序取Top-K ranked sorted(enumerate(scores), keylambda item: item[1], reverseTrue)[:top_k] # 第6步构造返回结果 return [ { chunk_id: rows[index][0].id, content: rows[index][0].content, metadata: { document_id: rows[index][1].id, filename: rows[index][1].filename, section: rows[index][0].section or , page: rows[index][0].page or 0, parent_id: rows[index][0].parent_id or , }, score: float(score), source: bm25, } for index, score in ranked if score 0 ]【代码解释】第1步为什么只查子块向量检索也只用子块保持两路范围一致第2步过滤条件和向量检索一致确保在同一范围内比较第3步BM25Okapi统计每个词的IDF记录每个文档长度第4步get_scores返回查询和每个文档的分数越高越相关第6步返回结构和向量检索一致方便RRF融合RRF融合函数def reciprocal_rank_fusion(result_lists: list[list[dict[str, Any]]], k: int 60) - list[dict[str, Any]]:fused {}for results in result_lists: for rank, item in enumerate(results, start1): chunk_id item[chunk_id] if chunk_id not in fused: fused[chunk_id] { **item, rrf_score: 0.0, sources: [] } fused[chunk_id][rrf_score] 1 / (k rank) fused[chunk_id][sources].append(item.get(source, unknown)) return sorted(fused.values(), keylambda x: x[rrf_score], reverseTrue)【代码解释】为什么用字典用chunk_id作key把同一子块的多路结果合并为什么k60RRF标准参数起平滑作用sources记录什么记录来源如[‘vector’]或[‘bm25’]或[‘vector’,‘bm25’]两路都命中的结果rrf_score更高排得更靠前retrieve中的混合检索核心vector_task VectorStoreService().search(question, top_k * 3, filters.document_ids or None)vector_results, keyword_results await asyncio.gather(vector_task,keyword_search(session, question, top_k, filters),)vector_results apply_metadata_filters(vector_results, filters)vector_results vector_results[:top_k]fused reciprocal_rank_fusion([vector_results, keyword_results])candidates fused[:top_k]【代码解释】为什么向量检索取 top_k * 3RRF会重新排序只取top_k可能漏掉BM25高分结果为什么用 asyncio.gather两路检索独立同时执行节省时间为什么向量结果要截断召回阶段多取进入融合前截断减少排序开销三、重点难点总结重点必须掌握BM25原理词频 逆文档频率 长度归一化中文分词jieba分词 → 去停用词 → 过滤单字RRF核心不看分数看排名公式 1 / (k rank)sources字段[‘vector’,‘bm25’]表示两路都命中最可信混合检索流程两路并行 → RRF融合 → 截取候选难点理解原理即可BM25的数学公式RRF中k值的意义平滑参数asyncio.gather的并行原理常见坑点BM25返回空检查MySQL是否有子块检查分词是否过滤太狠ModuleNotFoundErrorpip install rank-bm25jieba效果差用lcut_for_search模式四、面试题预测Q1: 为什么需要混合检索参考答案向量检索擅长语义理解能处理同义表达。但在政策问答中用户经常问精确的日期、政策名称、条款编号这些在向量检索中容易被弱化。BM25基于词频匹配能精确命中这些关键词。两者互补。Q2: 什么是RRF为什么不用加权平均参考答案RRF全称倒数排名融合不看具体分数只看排名。BM25分数和向量分数量纲不同不能直接相加。RRF把排名转换成统一分数。同时RRF对异常值不敏感比加权平均更鲁棒。Q3: 为什么向量检索要取 top_k * 3参考答案向量检索先多取一些因为后面的RRF融合会重新排序。如果在召回阶段只取top_k可能会漏掉在BM25中排名高但在向量中排名靠后的结果。召回阶段多取排序阶段再截断。Q4: sources字段中同时包含vector和bm25意味着什么参考答案说明这个结果在语义上和关键词上都匹配是最可靠的证据。在后续RAG中这些结果应被优先使用。Q5: 中文分词的难点是什么参考答案中文没有空格必须依赖分词工具。项目用jieba的lcut_for_search模式切分更细。切分后要去停用词、过滤单字词减少噪声。五、自测清单能说出BM25是什么、解决什么问题能说出为什么需要分词能说出RRF的全称和核心思想能画出混合检索的完整流程能解释sources字段的作用知道asyncio.gather的作用能说出向量检索和BM25各自的优缺点能说出RRF和加权平均的区别知道top_k * 3的原因能说出RRF公式1/(krank)

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价