Embedding / Reranker / 向量数据库:RAG 工程的三大基石

一句话:RAG 系统是 LLM 应用最常见的形态,而 RAG 工程能不能落地、效果好不好,绝大多数取决于三件事:Embedding 怎么选(把文本变向量)、向量数据库怎么用(存检索)、Reranker 配不配(精排提质)。本文是这三大基石的横向对比 + 选型决策 + 工程实战。

⚠️ 本篇为 Claude 原创补充笔记,非 LINUX DO @flymyd 原稿(详见末尾「作者声明」)。 📌 关系说明:RAG学习笔记.md 是 RAG 的入门笔记,聚焦”搜索 API 怎么选”;本文深入”自建 RAG 的三件套”,两者互补


目录


0. 三大基石各管什么

完整的 RAG 工作流:

  原始文档(PDF / Markdown / 网页)
        ↓ 切块(Chunking)
  小段文本(Chunk)
        ↓ Embedding 模型
  向量(768/1024/4096 维)
        ↓ 存入
  向量数据库(支持向量检索)
        ↓ 用户提问
  Query 文本
        ↓ 同一个 Embedding 模型
  Query 向量
        ↓ 向量数据库相似检索
  Top-K 候选 chunk(粗排)
        ↓ Reranker
  Top-N 精排结果(N < K)
        ↓ 塞进 prompt
  LLM 基于检索结果回答

三个基石各自的责任:

基石干什么不干什么
Embedding 模型把文本变成”能比较相似度的”向量不负责存储 / 检索
向量数据库存向量 + 提供”快速近似最近邻搜索”不理解语义
Reranker把”召回的 100 条”精排成”最相关的 5 条”不替代召回阶段

单凭任何一个都不够——Embedding 决定能不能召回,向量库决定能不能撑住量,Reranker 决定质量上限


1. Embedding 模型

1.1 Embedding 是什么 / 干什么用的

Embedding(嵌入):把文本(或图片、音频)变成固定长度的浮点数向量,使得”语义相近的文本→向量相近”

# 概念示意
embed("猫")          → [0.21, -0.05, 0.87, ...]   # 768 维
embed("小猫")        → [0.19, -0.03, 0.85, ...]   # 跟"猫"很接近
embed("打篮球")      → [-0.42, 0.61, 0.13, ...]   # 跟"猫"差很远
 
similarity(embed("猫"), embed("小猫"))     → 0.95
similarity(embed("猫"), embed("打篮球"))   → 0.12

相似度计算公式:几乎都用余弦相似度(Cosine Similarity):

sim(A, B) = (A · B) / (|A| × |B|)

值域:[-1, 1],越接近 1 越相似

1.2 主流 Embedding 模型选型

1.2.1 中文场景四大派系

模型家族出品方代表特点
BGE北京智源(BAAI)bge-large-zh-v1.5 / bge-m3中文 SOTA 之一,生态最广
M3EMokaAI(Moka)m3e-base / m3e-large早期中文标杆,目前不如 BGE
GTE阿里gte-large-zh / gte-Qwen2阿里推出,Qwen 系列友好
Conan腾讯conan-embedding-v1腾讯推出,微信场景验证

1.2.2 国际主流

模型出品方维度备注
text-embedding-3-largeOpenAI3072(可降维)商业 API,合规要求高时用
text-embedding-3-smallOpenAI1536性价比高
voyage-3Voyage AI1024Anthropic 投资,质量很好
Cohere embed v3Cohere1024多语言强
Jina Embeddings v3Jina AI1024(matryoshka)支持降维 + 多任务

1.2.3 多模态 Embedding

模型模态备注
CLIP(OpenAI 经典)文本 + 图像老牌,被广泛使用
SigLIP(Google)文本 + 图像CLIP 的改进版
BiomedCLIP / RAD-DINO医学影像宠物CT影像AI辅助诊断方案.md
bge-vl文本 + 图像BGE 家族多模态版

1.3 中文场景的关键选择

1.3.1 BGE-M3:2024 年起的”无脑选”

BAAI/bge-m3 是当前中文 RAG 场景的默认推荐,因为它解决了多个痛点:

  • 多语言:100+ 语言通用,不用为每种语言换模型
  • 多功能:同一个模型支持 稠密向量 + 稀疏向量 + ColBERT 风格 三种检索
  • 多粒度:单文档最多支持 8192 token(过去的 bge 只支持 512)

用法:

from FlagEmbedding import BGEM3FlagModel
 
model = BGEM3FlagModel('BAAI/bge-m3', use_fp16=True)
 
embeddings = model.encode(
    ["猫是哺乳动物", "我喜欢吃苹果"],
    batch_size=12,
    max_length=8192
)
# embeddings['dense_vecs'].shape = (2, 1024)

1.3.2 其他场景的选择

场景推荐
通用中文 RAGBAAI/bge-m3(default)
极致中文表现BAAI/bge-large-zh-v1.5(精排时配 reranker)
多语言通用BAAI/bge-m3Jina-v3
必须用商业 API + 合规text-embedding-3-large(OpenAI)
想跟 Qwen 配套gte-Qwen2-7B-instruct(阿里出品)
完全离线 / 端侧bge-small-zh(33MB,CPU 也能跑)
超低延迟m3e-base(384 维,极快)

1.4 维度的工程权衡

维度内存占用(每 100 万向量,FP32)检索速度精度
384~1.5 GB极快一般
768~3 GB不错
1024~4 GB
1536(OpenAI small)~6 GB很好
3072(OpenAI large)~12 GB最好
4096(部分 LLM 派生)~16 GB视模型

💡 关键认知:维度越高 ≠ 越好——存储成本线性增,精度提升通常是亚线性的。

一些新模型支持 Matryoshka 维度(可在使用时降维):

  • OpenAI v3 系列:从 3072 砍到 256 都行
  • Jina v3:同样支持
  • 3072 维存储,使用时降到 768 维做粗排——存储不变、检索提速

2. 向量数据库

2.1 为什么需要向量数据库

传统数据库精确匹配(WHERE id = 1),但向量需要近似匹配(找跟我最像的 K 条)。

问题规模:

  • 文档数 100 万 × 每个 chunk 1024 维 = 存 10 亿 float
  • 每次查询要算这个查询向量和 所有 100 万条的相似度
  • 暴力计算每次 1024 × 100 万 = 10 亿次乘加 = 每次查 100ms-1s(慢!)

向量数据库的核心价值:用 ANN(Approximate Nearest Neighbor)算法,牺牲一点精度,换取 100-1000 倍的查询速度

2.2 ANN 索引算法速览

算法全称思路典型库
HNSWHierarchical Navigable Small World多层”近邻图”,从粗到细查Faiss / Milvus / Qdrant / pgvector
IVFInverted File Index先聚类成 N 个桶,只查最近的几个桶Faiss / Milvus
PQProduct Quantization把高维向量切段量化,大幅压缩Faiss(常和 IVF 组合)
DiskANN索引存盘,适合超大规模Milvus / 微软研究
ScaNNGoogleGoogle 内部优化的 IVF + 量化TensorFlow Serving

📌 HNSW 是当前最主流——召回率高、速度快、调参少。99% 的中小项目用 HNSW 就够

2.3 主流向量库横评

2.3.1 七大主流向量库

类型部署适合
Milvus独立向量库Docker / K8s企业级,主流首选
Qdrant独立向量库Docker / 二进制中小项目,Rust 实现性能好
Weaviate独立向量库 + 多模态Docker / 云多模态场景
Chroma独立向量库嵌入 / 服务端原型 / 个人项目
pgvectorPostgreSQL 扩展装到现有 PG已有 PG 的项目
Pinecone商业云服务SaaS无运维需求 + 预算够
Faiss库不是服务Python / C++学术 / 嵌入应用

2.3.2 横向对比

维度MilvusQdrantWeaviateChromapgvectorPineconeFaiss
部署模式独立服务独立服务独立服务嵌入 / 独立装在 PGSaaS
数据持久化✅(走 PG)❌ 自己写
元数据过滤✅⭐✅⭐✅⭐✅(SQL!)
稀疏向量(BM25)部分✅(pg_trgm)需自实现
多模态✅⭐部分自实现
水平扩展✅⭐需 sharding
生产 SLA⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐(依 PG)⭐⭐⭐⭐⭐⭐⭐
学习曲线极低极低(SQL)极低
国内可用部分
LicenseApache 2.0Apache 2.0BSDApache 2.0PostgreSQL商业MIT

2.4 选型决策树

你需要存的向量数量?
  │
  ├─ < 10 万(原型 / 个人) ──── Chroma 或 pgvector(够用就别折腾)
  │
  ├─ 10 万 - 1000 万(中小项目)
  │     ├─ 已有 PostgreSQL? ──── pgvector(零额外运维)
  │     ├─ 想要"简单 + 自部署"? ─ Qdrant(单二进制就跑)
  │     └─ 多模态需求? ────────── Weaviate
  │
  ├─ 1000 万 - 10 亿(企业级)
  │     ├─ 自建团队 ──────────── Milvus(集群版)
  │     └─ 想完全外包 ────────── Pinecone(贵)
  │
  └─ 嵌入到 Python 应用 ──────── Faiss(直接 import,无服务)

2.5 极简代码:Qdrant 30 行入门

from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct
from FlagEmbedding import BGEM3FlagModel
 
# 1. 准备模型 + 客户端
embedder = BGEM3FlagModel('BAAI/bge-m3', use_fp16=True)
client = QdrantClient(":memory:")    # 内存模式;生产用 url="http://qdrant:6333"
 
# 2. 建 collection
client.create_collection(
    collection_name="docs",
    vectors_config=VectorParams(size=1024, distance=Distance.COSINE),
)
 
# 3. 灌数据
docs = ["猫是哺乳动物", "我喜欢吃苹果", "Python 是编程语言"]
vectors = embedder.encode(docs)['dense_vecs']
 
client.upsert(
    collection_name="docs",
    points=[
        PointStruct(id=i, vector=v.tolist(), payload={"text": doc})
        for i, (doc, v) in enumerate(zip(docs, vectors))
    ]
)
 
# 4. 查询
query_vec = embedder.encode(["小猫"])['dense_vecs'][0]
results = client.search(
    collection_name="docs",
    query_vector=query_vec.tolist(),
    limit=3
)
for r in results:
    print(r.score, r.payload["text"])
# 0.91 猫是哺乳动物
# 0.18 我喜欢吃苹果
# 0.05 Python 是编程语言

3. Reranker

3.1 为什么需要 Reranker

Embedding 检索的根本局限:

阶段Embedding 检索Reranker
算什么Query 向量 vs Document 向量(各算各的)Query + Document 拼起来 一起算
架构Bi-Encoder(双塔)Cross-Encoder(单塔交叉编码)
速度极快(候选向量提前算好)慢(每对都要重新过模型)
精度中(隐藏了交互信息)高(能看到 Query × Document 的细节)
Bi-Encoder(Embedding 检索):
  query  → [BERT] → [v_q]
                              cos_sim
  doc    → [BERT] → [v_d]   ←─────────  打分

Cross-Encoder(Reranker):
  [CLS] query [SEP] doc [SEP] → [BERT] → [score]
                                          ↑
                            能看到 query 和 doc 的所有 token 交互

典型 RAG 工作流:

Query → Embedding 召回 Top-100(快)
                ↓
       Reranker 精排到 Top-5(慢但准)
                ↓
            塞进 LLM Prompt

3.2 主流 Reranker 模型

模型出品方备注
bge-reranker-v2-m3BAAI中文 RAG 默认选,与 bge-m3 配套
bge-reranker-largeBAAI老一代,bge-reranker-v2-m3 不可用时降级
Cohere Rerank 3Cohere商业 API,多语言
Jina Reranker v2Jina AI多语言,支持 8192 token
mxbai-rerank-large-v1Mixedbread英文场景强
ms-marco-MiniLM-L-12-v2Microsoft老牌轻量,英文

💡 2026 年默认推荐:中文场景用 BAAI/bge-reranker-v2-m3,英文场景用 Cohere Rerank 3Jina Reranker v2

3.3 召回-精排两阶段架构

3.3.1 为什么不直接用 Reranker?

单用 Reranker召回 + 精排
100 万文档每条都过 Cross-Encoder100 万→Top-100(Embedding,毫秒级)→Top-5(Reranker,百毫秒)
每次查 30 分钟每次查 < 1 秒
显存 / GPU 占用爆炸可接受

两阶段是工程必然

3.3.2 配置参数:K 怎么选

K召回数精排数 N适用
K=10, N=3103极快,但漏召概率高
K=20, N=5205平衡
K=50, N=5505常见默认值
K=100, N=51005高质量,慢一些
K=200, N=1020010极致质量,Reranker 慢

调优经验:

  • 召回 Recall@K 不够时(目标答案没被召回):加大 K
  • 精排后还不准(LLM 上下文有相关但效果差):换 Reranker 或加 N
  • 延迟接受范围内,先加 K 后加 N

3.3.3 Reranker 调用示例

from FlagEmbedding import FlagReranker
 
# 1. 加载
reranker = FlagReranker('BAAI/bge-reranker-v2-m3', use_fp16=True)
 
# 2. 输入 query + 候选文档对
query = "什么是 RAG?"
docs = [
    "RAG 是 Retrieval-Augmented Generation,检索增强生成",
    "Python 是一种高级编程语言",
    "RAG 通过外挂知识库扩展 LLM 的能力",
    "今天天气真好",
]
 
# 3. 算分(每对 query-doc 一个分数)
scores = reranker.compute_score(
    [[query, d] for d in docs],
    normalize=True
)
# scores = [0.95, 0.02, 0.92, 0.01]
 
# 4. 排序取 Top-N
ranked = sorted(zip(docs, scores), key=lambda x: -x[1])
top_2 = ranked[:2]
print(top_2)
# [("RAG 是 Retrieval-Augmented Generation...", 0.95),
#  ("RAG 通过外挂知识库扩展 LLM 的能力", 0.92)]

4. 完整 RAG Pipeline 示例

把三者拼起来,一个最小可工作的 RAG:

# 完整 RAG 示例(伪生产代码)
from FlagEmbedding import BGEM3FlagModel, FlagReranker
from qdrant_client import QdrantClient
from qdrant_client.models import VectorParams, Distance, PointStruct
from openai import OpenAI
 
# 1. 初始化
embedder = BGEM3FlagModel('BAAI/bge-m3', use_fp16=True)
reranker = FlagReranker('BAAI/bge-reranker-v2-m3', use_fp16=True)
qdrant = QdrantClient(url="http://localhost:6333")
llm = OpenAI(base_url="http://localhost:8000/v1", api_key="...")
 
# 2. 灌库阶段(离线)
def index_documents(docs: list[str]):
    """切块 + 向量化 + 存库"""
    qdrant.recreate_collection(
        collection_name="kb",
        vectors_config=VectorParams(size=1024, distance=Distance.COSINE)
    )
    vectors = embedder.encode(docs, batch_size=32)['dense_vecs']
    qdrant.upsert(
        collection_name="kb",
        points=[
            PointStruct(id=i, vector=v.tolist(), payload={"text": d})
            for i, (d, v) in enumerate(zip(docs, vectors))
        ]
    )
 
# 3. 查询阶段(在线)
def rag_query(query: str, k: int = 50, n: int = 5) -> str:
    # Step A:Embedding 检索 Top-K
    qvec = embedder.encode([query])['dense_vecs'][0]
    candidates = qdrant.search(
        collection_name="kb",
        query_vector=qvec.tolist(),
        limit=k
    )
    candidate_docs = [c.payload["text"] for c in candidates]
    
    # Step B:Reranker 精排 Top-N
    rerank_scores = reranker.compute_score(
        [[query, d] for d in candidate_docs],
        normalize=True
    )
    top_docs = [d for d, _ in sorted(
        zip(candidate_docs, rerank_scores),
        key=lambda x: -x[1]
    )[:n]]
    
    # Step C:塞进 LLM
    context = "\n\n".join(f"[{i+1}] {d}" for i, d in enumerate(top_docs))
    prompt = f"""请基于以下知识回答问题。如果知识里没有相关信息,直接说"我不知道"。
 
知识:
{context}
 
问题:{query}
 
回答:"""
    
    response = llm.chat.completions.create(
        model="qwen-flagship",
        messages=[{"role": "user", "content": prompt}]
    )
    return response.choices[0].message.content
 
# 4. 跑起来
index_documents([
    "RAG 全称 Retrieval-Augmented Generation,检索增强生成",
    "RAG 由三部分组成:Embedding 检索、向量数据库、LLM 生成",
    "Reranker 用于二阶段精排,提升 RAG 质量",
    "Python 是一种解释型编程语言",
])
 
print(rag_query("RAG 有几个部分?"))
# → RAG 由三部分组成:Embedding 检索、向量数据库、LLM 生成。

5. 常见坑与最佳实践

5.1 切块(Chunking)策略

切块质量决定召回质量,但常被忽视。

策略适用优点缺点
固定字符数(如 500 字)简单文档极简单可能切断语义
固定 token 数LLM 友好准确控制成本同上
按段落 / 句子结构化文档保语义完整长度不均
递归切分(LangChain 默认)复杂文档兼顾结构和长度实现稍复杂
语义切分(用 LLM 判断切点)高质量需求质量最好成本高
滑动窗口 + overlap上下文连贯敏感不漏边界信息存储翻倍

经验默认值:

  • chunk_size: 500-1000 字符200-500 token
  • overlap: chunk_size 的 10-20%

5.2 多语言混合时

场景应对
全中文bge-m3bge-large-zh-v1.5
全英文bge-en-iclvoyage-3
中英混合bge-m3(多语言原生支持)
代码混合voyage-code-3codesage 等代码 embedding

5.3 Hybrid Search(混合检索)

纯向量检索的弱点:命名实体(人名 / 产品名 / 错别字)检索效果差,因为 Embedding 模型对精确字符不敏感。

解决方案:Hybrid Search = BM25 关键词检索 + 向量检索

# 伪代码
keyword_results = bm25_search(query, k=20)
vector_results  = vector_search(query, k=20)
merged = reciprocal_rank_fusion(keyword_results, vector_results)
final  = reranker.rerank(query, merged, top_n=5)

何时必须用 Hybrid:

  • 客服 / 售后场景(产品型号、订单号等命名实体)
  • 学术 / 法律(精确术语)
  • 代码搜索(关键字、API 名)

支持 Hybrid 的向量库:Milvus / Qdrant / Weaviate / Elasticsearch + 向量。

5.4 元数据过滤(Metadata Filtering)

生产环境必备:

# 只在某个用户的文档里搜
results = qdrant.search(
    collection_name="docs",
    query_vector=qvec,
    query_filter=Filter(
        must=[
            FieldCondition(key="user_id", match=MatchValue(value="user_123")),
            FieldCondition(key="date", range=Range(gte="2026-01-01")),
        ]
    ),
    limit=50
)

典型过滤维度:

  • user_id / tenant_id(多租户)
  • date / created_at(时间窗口)
  • doc_type(只搜文档 / 邮件 / Slack)
  • permission_level(权限隔离)
  • language(语言过滤)

⚠️ 重要:元数据过滤要在向量库层做,不要”先取 100 个再过滤”——这会导致过滤后剩 5 个甚至 0 个,严重影响召回。

5.5 评测 RAG 的 4 个核心指标

指标含义算法
Recall@KTop-K 召回里包含正确答案的比例标注数据集 + 自动算
MRR@K平均倒数排名(正确答案越靠前越好)同上
NDCG@K考虑相关度的排名质量同上
FaithfulnessLLM 回答是否忠于检索内容LLM-as-Judge / 人工

💡 冷启动经验:先做 Recall@10——如果你的 RAG Recall@10 不到 80%,所有”Reranker 调优 / Prompt 优化”都白费,先把 Embedding 和切块改对

5.6 何时 RAG 比微调更好

维度RAGFine-tuning
知识更新频率高频(每天)✅低频(月度)
数据量(几百 KB 起)大(GB 级)
部署成本高(GPU 训练)
可解释性(看 source)
工程难度
模型行为改变❌ 不能✅ 能(风格 / 格式)

最佳实践:先 RAG,实在搞不定再 Fine-tuning(或两者结合)。


6. 与本知识库其他章节的关联


7. 术语速查

术语全称 / 含义
Embedding把文本 / 图像 / 音频变成定长向量的过程 / 模型
Dense Vector稠密向量(浮点数数组,每位都有值)
Sparse Vector稀疏向量(大部分为 0,如 BM25 关键词向量)
Cosine Similarity余弦相似度,向量相似度的标准算法
Bi-Encoder双塔结构,Query / Doc 分开编码
Cross-Encoder单塔结构,Query+Doc 拼起来一起编码
ANNApproximate Nearest Neighbor,近似最近邻
HNSWHierarchical Navigable Small World,主流 ANN 算法
IVF / PQInverted File Index / Product Quantization,经典 ANN 算法组合
Reranker精排模型(Cross-Encoder)
Recall@KTop-K 内召回率
NDCG / MRR排名质量指标
Chunking切块,把长文档切成检索单元
Hybrid Search混合检索 = 关键词 + 向量
BM25经典关键词检索算法,常和向量检索互补
RRFReciprocal Rank Fusion,合并多个检索结果的算法
Matryoshka套娃维度,同一个 embedding 可在不同维度使用
ColBERT后期交互检索范式,bge-m3 支持
FaithfulnessRAG 评测维度:回答是否忠于检索内容
LLM-as-Judge用强 LLM 给生成结果打分的评测方法
Vector DB / 向量数据库专为向量检索优化的存储
Multi-tenancy多租户,生产环境必备
Embedding 服务化用 Infinity / TEI 把 Embedding 模型变成 OpenAI 兼容 API
TEItext-embeddings-inference,HuggingFace 出品的 Embedding 推理 server

8. 作者声明、来源与局限性

8.1 作者声明

⚠️ 本篇是 Claude 基于公开技术资料整理的科普综述,不是 LINUX DO @flymyd 的实战经验帖

原作者在「简单易懂的 LLM 相关知识梳理」目录中标记 ep.7「Embedding、Reranker 与向量数据库」为待填坑,本笔记为补位之作。

8.2 主要来源

8.3 本笔记的局限性

维度局限性
个人 benchmark没有亲自跑过 7 大向量库的 100 万级压测,性能数据来自各官方 benchmark + MTEB 榜
超大规模(10 亿+)本文止步于亿级,Twitter / Google 级别的 RAG 架构未涉及
法律 / 医疗等垂直领域不同领域的 best practice 差异大(医学要 BiomedCLIP / 法律要 lex-emb),没逐一展开
GraphRAG / 知识图谱 RAG微软 GraphRAG 等”图 + RAG” 新范式未涉及
Agentic RAG”Agent 决定检索什么 / 什么时候检索” 的高级 RAG 未涉及
多模态 RAG文本 RAG 是主体,图像 / 视频 / 音频 RAG 的特殊处理未展开
评测方法RAGAS、TruLens 等专业评测框架未深入

8.4 给读者的建议

  • 想 30 分钟跑起来 RAG:Chroma + bge-m3,本文 § 4 的代码改改就能用
  • 想给公司搭中型 RAG:Qdrant + bge-m3 + bge-reranker-v2-m3,这是当前最实用的组合
  • 想要企业级 RAG:Milvus 集群 + bge-m3 + bge-reranker + Hybrid Search,配 LangChain / LlamaIndex
  • 想做研究 / 评测:RAGAS 是当前主流的 RAG 评测框架
  • 关注前沿:微软 GraphRAG、Anthropic Contextual Retrieval、CRAG(Corrective RAG)是 2025 年值得关注的新方向