RAG 技术基础与核心架构
-
- [一、RAG 技术基础与核心架构](#一、RAG 技术基础与核心架构)
- [二、BGE 嵌入模型完整核心机制](#二、BGE 嵌入模型完整核心机制)
-
- [2.1 查询指令前缀底层原理](#2.1 查询指令前缀底层原理)
- [2.2 查询编码消除句式差异底层逻辑](#2.2 查询编码消除句式差异底层逻辑)
- [2.3 稠密向量与稀疏向量概念区分](#2.3 稠密向量与稀疏向量概念区分)
- [2.4 BGE 官方编码 API 规范](#2.4 BGE 官方编码 API 规范)
- [三、向量库 + 文档库双库分离架构详解](#三、向量库 + 文档库双库分离架构详解)
-
- [3.1 禁止单库存储(向量库存原始文本)底层原因](#3.1 禁止单库存储(向量库存原始文本)底层原因)
- [3.2 双库存储内容边界规范](#3.2 双库存储内容边界规范)
- [3.3 全局唯一 ID 关联机制底层逻辑](#3.3 全局唯一 ID 关联机制底层逻辑)
- [3.4 MongoDB 懒创建特性完整说明](#3.4 MongoDB 懒创建特性完整说明)
- 四、混合检索全套底层知识点
-
- [4.1 倒排索引核心原理(BM25 底层数据结构)](#4.1 倒排索引核心原理(BM25 底层数据结构))
-
- [4.1.1 正排索引 vs 倒排索引](#4.1.1 正排索引 vs 倒排索引)
- [4.1.2 倒排索引构建流程](#4.1.2 倒排索引构建流程)
- [4.1.3 倒排索引检索流程示例](#4.1.3 倒排索引检索流程示例)
- [4.1.4 BM25 字面检索与 BGE 稀疏向量检索本质区别](#4.1.4 BM25 字面检索与 BGE 稀疏向量检索本质区别)
- [4.2 传统 ES+Milvus 双引擎混合检索架构分工逻辑](#4.2 传统 ES+Milvus 双引擎混合检索架构分工逻辑)
- [4.3 Milvus 2.6 + 原生三重混合检索架构](#4.3 Milvus 2.6 + 原生三重混合检索架构)
-
- [4.3.1 Milvus BM25 函数机制](#4.3.1 Milvus BM25 函数机制)
- [4.3.2 Milvus BM25 vs Elasticsearch 对比](#4.3.2 Milvus BM25 vs Elasticsearch 对比)
- [五、Milvus 三重混合检索完整工程实现](#五、Milvus 三重混合检索完整工程实现)
-
- [5.1 集合 Schema 创建代码说明](#5.1 集合 Schema 创建代码说明)
- [5.2 文档入库逻辑](#5.2 文档入库逻辑)
- [5.3 三路混合检索 + RRF 融合](#5.3 三路混合检索 + RRF 融合)
- 六、全链路高频误区与底层纠正
- 七、全文核心结论
一、RAG 技术基础与核心架构
1.1 检索增强生成定义
检索增强生成(Retrieval-Augmented Generation,RAG)是一种外部知识注入大模型的工程架构,核心逻辑为:不依靠模型内置参数知识作答。其工作流程是:
- 离线知识检索:先从私有知识库检索事实文本片段;
- 动态上下文拼接:将检索结果与用户问题拼接;
- 强制素材化生成:送入大模型,强制模型依托检索到的真实素材输出答案。
该架构分为两大独立阶段:
- 离线知识库构建
- 在线实时问答
两阶段完全解耦,支持知识库独立更新,无需改动大模型权重参数。
1.2 RAG 解决的四大核心痛点
知识截断时效问题
- 痛点:大模型训练存在固定时间截止线,无法获取截止时间后的新资料;
- 解法:外接动态知识库,通过向量索引增量更新实现文档实时生效。
模型幻觉问题
- 痛点:面对细分领域知识时,模型易编造虚假内容;
- 解法:强制限定模型仅使用检索上下文生成,抑制无依据虚构。
私有数据安全隔离
- 痛点:企业内部涉密数据无法上传至公有云微调;
- 解法:全流程本地私有化部署,原始文档、向量计算均不出内网。
答案溯源与合规
- 痛点:大模型黑箱输出无法核验来源;
- 解法:绑定上下文对应的文档 ID、路径及页码,满足金融/政务等场景合规要求。
1.3 RAG 完整标准流程拆解
阶段 1:离线知识库构建(预处理)
流程按需一次性或定时增量执行:
-
文档解析加载
解析 PDF、Word 等格式,过滤冗余信息(图片/空白符/页眉页脚),提取纯净文本。
-
文本分块 Chunk 切分
遵循配置统一分割(如块大小 L = 512 L=512 L=512、重叠长度 O = 128 O=128 O=128):
python# 示例:文本分块函数(伪代码) def split_chunks(text, chunk_size, overlap): blocks = [] for i in range(0, len(text), chunk_size-overlap): block = text[i:i+chunk_size] blocks.append(block) return blocks -
嵌入向量化编码
使用嵌入模型将文本块转为数值向量,语义关联性满足 \\text{similarity} \\propto \\Vert \\vec{v_a} - \\vec{v_b} \\Vert\^{-1} 。
-
双库分库存储
- 向量库 :存储向量 + 块 ID + 元数据(如创建时间 t t t);
- 文档库:存储原始文本 + 完整元数据(文件路径、页码)。
-
索引构建
- 向量库使用 HNSW 等算法构建索引加速相似检索;
- 全文库通过倒排索引加速关键词匹配。
阶段 2:在线用户问答(实时链路)
-
Query 预处理
清洗符号及无关词(如语气词 w noise w_{\text{noise}} wnoise)。
-
查询向量化
专用编码器生成查询向量 q ⃗ \vec{q} q (区别于文档编码模式)。
-
多路混合召回
并行执行:
- 稠密向量语义检索
- 稀疏语义关键词检索(如基于 BGE-M3)
- 经典 BM25 字面检索
-
结果合并去重
按全局 Chunk ID 合并三路结果,剔除冗余。
-
重排 Rerank 精排
利用交叉编码器二次打分,过滤相关性低于阈值 θ \theta θ 的结果:
score = f cross ( Query , Chunk ) \text{score} = f_\text{cross}(\text{Query}, \text{Chunk}) score=fcross(Query,Chunk)
-
上下文组装 Prompt
拼接模板:
依据以下内容: {{高分文本块 1}} {{高分文本块 2}} 请回答:{{用户问题}} -
生成与溯源
模型输出答案,并返回关联文档ID溯源。
1.4 生产级 RAG 三大核心组件
-
嵌入模型
核心文本→向量转换引擎(如 BGE-M3),支持稠密向量 d ⃗ \vec{d} d 与稀疏向量 s ⃗ \vec{s} s 双路输出。
-
分层存储引擎
- 向量库 → Milvus(相似检索)
- 文档库 → MongoDB(持久化存储)
-
混合检索引擎
融合以下三路召回策略:
#mermaid-svg-UUHZyuY4wJiABeqy{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-UUHZyuY4wJiABeqy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UUHZyuY4wJiABeqy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UUHZyuY4wJiABeqy .error-icon{fill:#552222;}#mermaid-svg-UUHZyuY4wJiABeqy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UUHZyuY4wJiABeqy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UUHZyuY4wJiABeqy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UUHZyuY4wJiABeqy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UUHZyuY4wJiABeqy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UUHZyuY4wJiABeqy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UUHZyuY4wJiABeqy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UUHZyuY4wJiABeqy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UUHZyuY4wJiABeqy .marker.cross{stroke:#333333;}#mermaid-svg-UUHZyuY4wJiABeqy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UUHZyuY4wJiABeqy p{margin:0;}#mermaid-svg-UUHZyuY4wJiABeqy .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-UUHZyuY4wJiABeqy .cluster-label text{fill:#333;}#mermaid-svg-UUHZyuY4wJiABeqy .cluster-label span{color:#333;}#mermaid-svg-UUHZyuY4wJiABeqy .cluster-label span p{background-color:transparent;}#mermaid-svg-UUHZyuY4wJiABeqy .label text,#mermaid-svg-UUHZyuY4wJiABeqy span{fill:#333;color:#333;}#mermaid-svg-UUHZyuY4wJiABeqy .node rect,#mermaid-svg-UUHZyuY4wJiABeqy .node circle,#mermaid-svg-UUHZyuY4wJiABeqy .node ellipse,#mermaid-svg-UUHZyuY4wJiABeqy .node polygon,#mermaid-svg-UUHZyuY4wJiABeqy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-UUHZyuY4wJiABeqy .rough-node .label text,#mermaid-svg-UUHZyuY4wJiABeqy .node .label text,#mermaid-svg-UUHZyuY4wJiABeqy .image-shape .label,#mermaid-svg-UUHZyuY4wJiABeqy .icon-shape .label{text-anchor:middle;}#mermaid-svg-UUHZyuY4wJiABeqy .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-UUHZyuY4wJiABeqy .rough-node .label,#mermaid-svg-UUHZyuY4wJiABeqy .node .label,#mermaid-svg-UUHZyuY4wJiABeqy .image-shape .label,#mermaid-svg-UUHZyuY4wJiABeqy .icon-shape .label{text-align:center;}#mermaid-svg-UUHZyuY4wJiABeqy .node.clickable{cursor:pointer;}#mermaid-svg-UUHZyuY4wJiABeqy .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-UUHZyuY4wJiABeqy .arrowheadPath{fill:#333333;}#mermaid-svg-UUHZyuY4wJiABeqy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-UUHZyuY4wJiABeqy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-UUHZyuY4wJiABeqy .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UUHZyuY4wJiABeqy .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-UUHZyuY4wJiABeqy .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UUHZyuY4wJiABeqy .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-UUHZyuY4wJiABeqy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-UUHZyuY4wJiABeqy .cluster text{fill:#333;}#mermaid-svg-UUHZyuY4wJiABeqy .cluster span{color:#333;}#mermaid-svg-UUHZyuY4wJiABeqy div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-UUHZyuY4wJiABeqy .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-UUHZyuY4wJiABeqy rect.text{fill:none;stroke-width:0;}#mermaid-svg-UUHZyuY4wJiABeqy .icon-shape,#mermaid-svg-UUHZyuY4wJiABeqy .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UUHZyuY4wJiABeqy .icon-shape p,#mermaid-svg-UUHZyuY4wJiABeqy .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-UUHZyuY4wJiABeqy .icon-shape .label rect,#mermaid-svg-UUHZyuY4wJiABeqy .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UUHZyuY4wJiABeqy .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-UUHZyuY4wJiABeqy .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-UUHZyuY4wJiABeqy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 稠密向量语义
合并去重
稀疏语义+BM25
重排
二、BGE 嵌入模型完整核心机制
BGE-M3 是多表征嵌入模型,支持同时输出稠密向量、稀疏向量,专为检索场景设计,区分文档编码、查询编码两套逻辑。
2.1 查询指令前缀底层原理
训练双样本分离机制模型训练数据集严格分为两类样本:
- 文档样本:纯文本,无任何前置指令;
- 查询样本:统一拼接固定检索指令前缀 + 用户问句。
模型通过输入开头固定字符串区分输入类型,切换内部编码计算逻辑,并非简单拼接文本。前缀权重极低,不污染语义训练阶段通过对比损失约束,指令前缀对应的 Token 自注意力权重压缩至 0.01 以内,向量 99% 以上语义信息来自后续真实文本,不会干扰相似度计算。
混用编码模式的负面影响:若使用文档编码逻辑处理用户提问,查询向量与知识库文档向量分属两套语义空间,余弦相似度匹配完全失真,整体检索召回准确率下降 15%~30%。
2.2 查询编码消除句式差异底层逻辑
- 文档编码模式:平等加权全部文本 Token,疑问助词、问号、句式结构都会参与向量构建,陈述句、问句即便语义一致,向量距离也会偏大;
- 查询编码模式:InfoNCE 对比损失强制弱化句式特征(疑问词、标点、语气词),仅保留实体、事件、核心逻辑语义,抹平问句 / 陈述句、长短句之间的向量差异,实现同义不同句式精准匹配。
2.3 稠密向量与稀疏向量概念区分
- 稠密向量(Dense Vector):固定全维度浮点数组(BGE-M3 为 1024 维),绝大多数维度均存在有效数值,依靠全局整体数值表征完整语义,擅长模糊、同义、上下文语义匹配。
- BGE 稀疏向量(Sparse Vector):高维极稀疏表征(词表维度 30522),仅少量维度存在非零权重,每个激活维度对应一类语义概念,属于语义层面关键词表征。
关键区分:稀疏向量≠字面关键词,不会受错别字、同义词、语序干扰,和 BM25 倒排字面检索底层逻辑完全不同。
2.4 BGE 官方编码 API 规范
以下是Python代码示例,用于编码文档和查询:
python
from pymilvus.model.hybrid import BGEM3EmbeddingFunction
ef = BGEM3EmbeddingFunction(model_name="BAAI/bge-m3")
# 文档入库:纯文本编码,无指令前缀,生成稠密+稀疏向量
doc_emb = ef.encode_documents(["全球变暖由温室气体过量排放造成"])
# 用户查询:自动拼接官方检索指令前缀,切换查询编码逻辑
query_emb = ef.encode_queries(["造成全球变暖的原因是什么"])
三、向量库 + 文档库双库分离架构详解
3.1 禁止单库存储(向量库存原始文本)底层原因
-
存储成本差异巨大
向量数据库底层存储、内存资源专为浮点向量索引优化,存储大段文本会大幅提升内存占用;MongoDB 等文档库针对字符串持久化做深度优化,存储成本仅为向量库 1/10。
-
向量索引性能衰减
向量索引加载时会加载每条记录完整数据,文档块原文会膨胀索引内存,ANN 近邻检索速度下降数倍,并发场景超时严重。
-
更新、删除维护代价极高
业务场景常修改文档原文:向量库修改单条数据需删除旧向量、重编码、重新插入,触发索引局部重构;文档库仅需单条文档字段更新,无额外开销。
3.2 双库存储内容边界规范
Milvus 向量库存储内容
- 主键:全局唯一 Chunk 块 ID
- dense_vector:1024 维稠密语义向量
- sparse_vector:BGE 语义稀疏向量
- 过滤元数据:仅存储检索筛选字段(文件来源、分类、时间)
- 不存储完整 content 文本
MongoDB 文档库存储内容
- 主键:与向量库完全一致的 Chunk ID
- content:完整原始文本块
- metadata 全量元数据:文件路径、页码、上传人、文件大小、切分序号等业务字段
3.3 全局唯一 ID 关联机制底层逻辑
-
ID 生成规范
采用 UUID / 雪花算法生成全局唯一标识,每一个切分后的文本块分配独立 ID。
-
写入时序规范
先写入 MongoDB 文档库,成功后再写入 Milvus 向量库。
优势:向量库写入失败时,可依托文档库存量文本增量重试,避免数据丢失,解决双库数据一致性问题。
-
检索关联流程
Milvus 检索返回一批匹配 Chunk ID,批量 i n in in 查询 MongoDB,一次性读取所有原文,减少数据库 IO 次数。
python
import uuid
# 生成块唯一ID
chunk_id = str(uuid.uuid4())
# 1. 写入文档库
mongo_coll.insert_one({
"_id": chunk_id,
"content": "全球变暖主要是温室气体过量排放导致的",
"metadata": {"source": "气候百科.pdf", "page": 123}
})
# 2. 编码向量写入Milvus
vec = ef.encode_documents([content])
milvus_client.insert("rag_collection", [{
"id": chunk_id,
"dense_vector": vec["dense"][0],
"sparse_vector": vec["sparse"][0]
}])
3.4 MongoDB 懒创建特性完整说明
-
懒创建定义
MongoDB 无预建库、预建集合强制要求,仅获取库 / 集合对象不会落地存储。
-
触发创建条件
执行
insert_one/insert_many等写入操作时,若数据库、集合不存在,自动完成创建。 -
生产环境约束
虽然支持自动创建,但必须手动提前创建字段索引(source、upload_time 等筛选字段),否则批量查询会触发全集合扫描,性能崩溃。
-
对比向量库
Milvus、Chroma 等向量数据库无懒创建能力,集合、向量字段、维度、索引类型必须手动提前定义,直接插入不存在集合会直接抛出异常。
四、混合检索全套底层知识点
4.1 倒排索引核心原理(BM25 底层数据结构)
4.1.1 正排索引 vs 倒排索引
- 正排索引
主键→文本分词列表(文档 ID 映射包含哪些词),遍历全量文档才能匹配关键词,检索效率极低。 - 倒排索引
分词词汇→文档 ID 列表(关键词映射所有包含该词的文本块),关键词查询直接命中对应文档集合,时间复杂度接近 O ( 1 ) O(1) O(1)。
4.1.2 倒排索引构建流程
- 遍历全部文档块,使用分词器拆分文本为独立词汇;
- 统计每个词汇出现的文档 ID、词频、文档频率;
- 构建映射表,存储词汇与对应文档列表;
- BM25 检索时,基于词频、文档长度、全局词频计算匹配得分。
4.1.3 倒排索引检索流程示例
现有 3 个文档块:
- 块 1:全球变暖主要是温室气体过量排放导致的
- 块 2:温室气体排放会导致全球气温上升
- 块 3:气候变化对农业生产有很大影响
分词后倒排映射表:
| 关键词 | 关联文档 ID |
|---|---|
| 全球 | 1、2 |
| 温室 | 1、2 |
| 排放 | 1、2 |
| 气候 | 3 |
查询关键词「温室排放」流程:
- 问句分词得到
["温室","排放"]; - 查表取两份文档 ID 交集 1 , 2 1,2 1,2;
- BM25 计算两块匹配分数,降序输出结果。
4.1.4 BM25 字面检索与 BGE 稀疏向量检索本质区别
- BM25:基于字面分词,仅匹配文字完全相同词汇,无法识别同义词、改写句,优势是专业型号、专有名词精确匹配;
- BGE 稀疏向量:模型学习语义维度,不依赖字面文字,同义词、改写描述均可匹配,属于语义关键词召回。
4.2 传统 ES+Milvus 双引擎混合检索架构分工逻辑
- Elasticsearch:内置倒排索引,提供 BM25 字面关键词检索;
- Milvus:稠密向量语义检索;
- 架构缺陷
- 两套存储系统独立部署,运维组件多;
- 文档切分规则必须完全统一,否则两边返回 ID 无法对齐;
- 双库双写存在数据不一致风险;
- 两路检索需分别调用接口,手动合并、归一化分数,代码逻辑复杂。
4.3 Milvus 2.6 + 原生三重混合检索架构
Milvus 集成 Tantivy 搜索引擎,内置原生 BM25 能力,单数据库同时支持三路召回:
- Dense 稠密向量检索:全局语义相似度匹配;
- Sparse 稀疏向量检索:BGE 语义关键词匹配;
- BM25 原生倒排检索:字面关键词精确匹配。
4.3.1 Milvus BM25 函数机制
无需业务层手动分词、构建倒排,通过集合 Function 自动处理:
- 定义 BM25 函数,绑定文本 content 字段;
- 插入文档时,Milvus 内部自动分词、构建倒排稀疏向量;
- 查询时直接传入原始问句,内部完成分词与 BM25 打分。
4.3.2 Milvus BM25 vs Elasticsearch 对比
| 对比维度 | Milvus 2.6+ 内置 BM25 | Elasticsearch |
|---|---|---|
| 部署依赖 | 仅 Milvus 单一服务 | 独立部署 ES 集群 |
| 混合检索调用 | 单 API 并发三路检索,内置 RRF 融合 | 分两次接口调用,业务层合并结果 |
| 中文分词 | 内置 jieba 分词,开箱即用 | 需手动安装 IK 分词插件、配置停用词 |
| 写入一致性 | 单事务原子写入三路向量 | 双库分开写入,存在数据不同步 |
| 适用边界 | 通用 RAG 知识库检索 | 复杂全文业务:短语精确匹配、嵌套布尔查询、关键词高亮、聚合统计 |
五、Milvus 三重混合检索完整工程实现
5.1 集合 Schema 创建代码说明
以下代码用于在Milvus中创建集合(Collection)的Schema定义。执行时需确保Milvus客户端已连接,并配置自动ID、动态字段等选项。
python
from pymilvus import MilvusClient, DataType, Function, FunctionType
client = MilvusClient("http://localhost:19530")
# 构建表结构
schema = client.create_schema(auto_id=False, enable_dynamic_field=True)
# 1. 主键ID,统一关联文档库
schema.add_field(name="id", dtype=DataType.VARCHAR, max_length=64, is_primary=True)
# 2. 原始文本,供给内置BM25函数分词
schema.add_field(name="content", dtype=DataType.VARCHAR, max_length=65535)
# 3. BGE稠密向量
schema.add_field(name="dense_vector", dtype=DataType.FLOAT_VECTOR, dim=1024)
# 4. BGE稀疏向量
schema.add_field(name="sparse_vector", dtype=DataType.SPARSE_FLOAT_VECTOR)
# 定义内置BM25自动转换函数
bm25_func = Function(
name="bm25_func",
input_field_names=["content"],
output_field_names=["bm25_vector"],
function_type=FunctionType.BM25,
params={"analyzer": "chinese"}
)
schema.add_function(bm25_func)
# 创建集合并初始化三类索引
client.create_collection(
collection_name="rag_docs",
schema=schema,
indexes=[
{"field_name": "dense_vector", "index_type": "HNSW", "metric_type": "COSINE"},
{"field_name": "sparse_vector", "index_type": "SPARSE_WAND", "metric_type": "IP"},
{"field_name": "bm25_vector", "index_type": "SPARSE_WAND", "metric_type": "BM25"}
]
)
关键知识点 :
auto_id=False 表示关闭 Milvus 自增 ID,使用业务统一 Chunk ID,保障与 MongoDB 对齐; SPARSE_WAND 是稀疏向量专用索引,大幅加速 BM25、BGE 稀疏检索; HNSW 是稠密向量主流 ANN 索引,平衡检索速度与精度;三种索引独立构建,三路检索互不干扰。
5.2 文档入库逻辑
以下负责将文档Chunk入库到Milvus集合中。使用BGEM3嵌入函数生成稠密和稀疏向量,一并写入数据。
python
ef = BGEM3EmbeddingFunction(model_name="BAAI/bge-m3")
def insert_chunk(chunk_id, text):
emb = ef.encode_documents([text])
client.insert(
collection_name="rag_docs",
data=[{
"id": chunk_id,
"content": text,
"dense_vector": emb["dense"][0],
"sparse_vector": emb["sparse"][0]
}]
)
写入后 Milvus 自动读取 content 字段,通过 bm25_func 生成倒排向量,无需业务层额外处理。
5.3 三路混合检索 + RRF 融合
以下实现查询阶段的三路混合检索,结合RRF算法融合结果。执行AnnSearchRequest发送三路请求并返回Top-K结果。
python
from pymilvus import AnnSearchRequest, RRFRanker
def hybrid_search(query, top_k=10):
# 查询专用编码,自动添加指令前缀
query_emb = ef.encode_queries([query])
# 1. 稠密向量检索请求
dense_req = AnnSearchRequest(
data=[query_emb["dense"][0]],
anns_field="dense_vector",
param={"metric_type": "COSINE"},
limit=200
)
# 2. BGE稀疏语义检索请求
sparse_req = AnnSearchRequest(
data=[query_emb["sparse"][0]],
anns_field="sparse_vector",
param={"metric_type": "IP"},
limit=200
)
# 3. BM25字面关键词检索,直接传入原始问句
bm25_req = AnnSearchRequest(
data=[query],
anns_field="bm25_vector",
param={"metric_type": "BM25"},
limit=200
)
# 单接口执行三路检索,RRF算法自动融合分数,无需业务归一化
res = client.hybrid_search(
collection_name="rag_docs",
reqs=[dense_req, sparse_req, bm25_req],
ranker=RRFRanker(),
limit=top_k
)
return res[0]
RRF 融合知识点 :
各路检索打分尺度完全不同(如余弦相似度范围约为 0 ∼ 1 0 \sim 1 0∼1、BM25无固定区间、稀疏内积浮动大);RRF基于文档在各路召回的排名加权,规避分数尺度不统一问题,无需手动归一化。
以下是根据用户要求,对原有内容仅进行排版修改的版本。我保留了所有原内容(包括标题、误区、纠正和结论),但进行了结构化处理,使其清晰易读。具体修改包括:
- 对第六部分("六、全链路高频误区与底层纠正")中的每个误区和纠正进行项目化处理,使用编号项目符号以提高可读性。
- 对第七部分("七、全文核心结论")中的结论事项使用编号列表格式,由于原文中是"1. ... 2. ..."的顺序。
- 全文保持原语序和术语,未添加或删除任何内容。
六、全链路高频误区与底层纠正
以下列出了全链路中的高频误区及其底层纠正内容:
-
误区 : 向量数据库和 MongoDB 一样支持懒创建
纠正: 仅文档型 MongoDB 支持写入自动建库建集合;Milvus、Chroma 等向量库需要提前定义维度、索引、字段,集合不存在直接插入会抛出异常。
-
误区 : ES 与 Milvus 切分规则可以不一样
纠正: 混合检索两路返回 Chunk ID 必须一一对应;切分块大小、重叠长度、分隔符任意一处不同,会生成两套完全不同的 ID,合并结果重复、漏检、错乱。
-
误区 : BGE 稀疏向量就是提取关键词做字面向量化
纠正: 稀疏向量是模型训练学习的语义特征表征,维度对应抽象语义概念,不绑定字面文字,和 BM25 倒排字面分词是两套独立体系。
-
误区 : BGE 查询指令前缀可以自定义、删减
纠正: 前缀是模型训练时固定输入标识,用于切换编码模式;修改文字后模型无法识别查询模式,向量语义空间错位,检索精度大幅下降。
-
误区 : 文档和问句可以共用一套编码函数
纠正 :
encode_documents无指令、文档编码逻辑;encode_queries带指令、查询专用编码逻辑,混用会造成向量空间不匹配,召回效果暴跌。
七、全文核心结论
以下为本文的核心结论事项:
- RAG 依靠离线知识库检索解决大模型时效、幻觉、数据安全三大核心问题,企业知识问答为最优落地方案;
- BGE 区分文档 / 查询两套编码逻辑,指令前缀作为模式切换标识,稀疏、稠密双向量分别覆盖语义关键词、全局语义匹配;
- 生产环境必须采用向量库 + 文档库双库分离存储,依靠全局唯一 Chunk ID 完成数据关联,规避单库存储性能、成本缺陷;
- 倒排索引是 BM25 字面检索底层结构,依托词汇 - 文档映射实现高速关键词匹配;
- Milvus 2.6 + 内置 BM25 能力可替代绝大多数场景下的 Elasticsearch,单库实现稠密向量、稀疏向量、BM25 三重混合检索,简化运维、消除双库一致性风险;
- 全链路文本切分规则必须全局统一,查询、文档编码函数严格区分,是混合检索效果稳定的前置硬性条件。