🍊一篇面向工程师的技术深度文章,带你从原理到实践,彻底搞懂检索增强生成(RAG)的技术栈。
目录
- [什么是 RAG?](#什么是 RAG?)
- [Embedding 模型与语义张量](#Embedding 模型与语义张量)
- 向量数据库全景
- 主流向量数据库工具一览
- [Milvus vs FAISS:深度对比](#Milvus vs FAISS:深度对比)
- 选型建议与实践
- 总结
1. 什么是 RAG?
1.1 背景:大语言模型的局限
大语言模型(LLM)虽然强大,但存在三个核心短板:
- 知识截止日期:模型训练数据有固定的时间窗口,无法感知训练之后发生的事件。
- 幻觉问题:当模型被问到它不掌握的事实,它可能"编造"一个听起来合理但完全错误的答案。
- 私有知识盲区:企业内部文档、专有数据、个人知识库,模型从未见过。
1.2 RAG 的核心思想
RAG(Retrieval-Augmented Generation,检索增强生成) 正是为了解决上述问题而生的架构范式。它的工作流程可以概括为三步:
用户提问 → 检索相关知识 → 将知识注入 Prompt → LLM 生成回答
具体来说:
-
离线阶段(索引构建):将你的文档库切分成小块(Chunk),通过 Embedding 模型将每个 Chunk 转换为高维向量,存入向量数据库。
-
在线阶段(检索 + 生成):用户提问后,同样用 Embedding 模型将问题转为向量,在向量数据库中做近似最近邻(ANN)搜索,召回最相关的 Top-K 个文档片段。
-
增强生成:将这些片段作为上下文拼接到 Prompt 中,让 LLM 基于"外部检索到的知识"来回答问题,而非仅凭模型参数中记忆的知识。
┌─────────────────────────────────────────────────────────────────┐
│ RAG 架构全景 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ 文档库 │───▶│ Chunk 拆分 │───▶│ Embedding 模型编码 │ │
│ └──────────┘ └──────────────┘ └────────┬─────────────┘ │
│ │ │
│ ┌───────▼──────────┐ │
│ │ 向量数据库 │ │
│ │ (Milvus/FAISS) │ │
│ └───────┬──────────┘ │
│ │ │
│ 用户提问 ──▶ Embedding 编码 ──▶ ANN 检索 ───┘ │
│ │ │
│ ┌───────▼──────────┐ │
│ │ Top-K 文档片段 │ │
│ └───────┬──────────┘ │
│ │ │
│ ┌─────────────────────────┘ │
│ ▼ │
│ ┌────────────────┐ │
│ │ 拼接 Prompt │ │
│ │ + LLM 生成回答 │ │
│ └────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
1.3 RAG 相比微调的优势
| 维度 | RAG | 微调(Fine-tuning) |
|---|---|---|
| 知识更新 | 实时,更新数据库即可 | 需要重新训练 |
| 可解释性 | 可追溯引用来源 | 黑盒,难以溯源 |
| 幻觉控制 | 显著降低 | 可能学会"更好的编造" |
| 实施成本 | 低,无需 GPU 训练 | 高,需要大量标注数据和算力 |
| 适用场景 | 知识库问答、文档检索 | 风格迁移、特定格式输出 |
2. Embedding 模型与语义张量
2.1 什么是 Embedding?
Embedding 是将非结构化数据(文本、图片、音频)映射到一个固定维度稠密向量空间的技术。这个向量空间的魔力在于:语义相近的内容,在向量空间中的距离也近。
比如:
- "苹果很好吃" →
[0.12, -0.34, 0.87, ..., 0.05](768 维) - "这个水果很甜" →
[0.11, -0.32, 0.85, ..., 0.07](768 维)
两句含义相近的话,它们的向量在高维空间中彼此靠近(余弦相似度接近 1)。
2.2 语义张量的本质
"语义张量"(Semantic Tensor)这个表述强调的是 Embedding 的数学本质------它是一个 N 维实数张量,每一维都没有可解释的具体含义,但整体编码了输入内容的语义特征。
文本: "What is vector search?"
Embedding 模型: text-embedding-3-large (OpenAI)
输出张量: [0.014, -0.023, 0.041, ..., -0.008] ← 3072 维
↑
每一维都是 float32,整体构成一个语义"指纹"
常见的 Embedding 维度与模型:
| 模型 | 维度 | 提供商 |
|---|---|---|
| text-embedding-3-small | 512 / 1536 | OpenAI |
| text-embedding-3-large | 256 / 1024 / 3072 | OpenAI |
| bge-large-zh-v1.5 | 1024 | BAAI(开源) |
| all-MiniLM-L6-v2 | 384 | Sentence-Transformers |
| multilingual-e5-large | 1024 | Microsoft |
2.3 相似度度量
向量检索的核心是计算向量之间的"距离"或"相似度"。三种主流度量方式:
- 余弦相似度(Cosine Similarity) :
cos(θ) = (A·B) / (||A|| × ||B||),值域 -1, 1,越接近 1 越相似。最常用的文本相似度度量。 - 欧氏距离(Euclidean Distance / L2) :
||A - B||₂,值域 [0, ∞),越小越相似。适合低维且归一化的向量。 - 内积(Inner Product / IP) :
A·B,值越大越相似。在推荐系统中广泛使用。
3. 向量数据库全景
3.1 为什么需要专门的向量数据库?
你可能会问:传统数据库不能存向量吗?可以------但无法高效检索。
向量检索的核心操作是 ANN(Approximate Nearest Neighbor,近似最近邻搜索) :在百万、亿级向量中,以毫秒级找到与查询向量最相似的 K 个向量。这是在传统数据库中用 SQL ORDER BY distance LIMIT 10 无法做到的------对百万行做全表扫描计算余弦相似度,延迟会高到不可接受。
向量数据库通过 索引算法(如 HNSW、IVF、PQ)在精度与速度之间取得平衡,实现亚秒级检索。
3.2 核心索引算法
| 算法 | 原理 | 特点 |
|---|---|---|
| Flat(暴力搜索) | 遍历所有向量逐一计算距离 | 100% 精度,速度最慢 |
| IVF(倒排文件) | K-Means 聚类后只在最近几个簇中搜索 | 速度快,精度可调 |
| HNSW(分层可导航小世界图) | 多层图结构,逐层跳跃逼近目标 | 速度快、精度高,内存占用大 |
| PQ(乘积量化) | 将高维向量压缩为短编码 | 极大节省内存,精度略降 |
| DiskANN | 基于图的磁盘索引 | 适合超大规模、内存受限场景 |
4. 主流向量数据库工具一览
4.1 专用向量数据库
| 工具 | 定位 | 核心特点 | 适用规模 |
|---|---|---|---|
| Milvus | 云原生分布式向量数据库 | 存算分离、水平扩展、多索引支持 | 亿级~百亿级 |
| Pinecone | 全托管云服务 | 零运维、Serverless | 任意规模 |
| Weaviate | 开源向量搜索引擎 | GraphQL 接口、内置模块化 Pipeline | 百万~亿级 |
| Qdrant | 高性能 Rust 向量数据库 | 丰富的过滤、分组、推荐 API | 百万~十亿级 |
| Chroma | 轻量级 AI 原生数据库 | 极简 API、适合原型开发 | 十万~百万级 |
4.2 向量检索库
| 工具 | 定位 | 核心特点 |
|---|---|---|
| FAISS | Meta 开源的向量相似度搜索库 | C++ 核心、GPU 加速、极致性能、纯库非数据库 |
| Annoy | Spotify 开源的 ANN 库 | 内存映射、只读索引、轻量 |
| ScaNN | Google 开源的 ANN 库 | 针对内积搜索优化、高召回率 |
| HNSWlib | 纯 HNSW 算法实现 | 极简标头库、速度极快 |
4.3 传统数据库的向量扩展
| 工具 | 向量能力 |
|---|---|
| PostgreSQL + pgvector | 在 SQL 中直接做向量检索,适合已有 PG 技术栈的团队 |
| Elasticsearch | 8.x 起支持向量检索,适合混合搜索(关键词 + 语义) |
| Redis | Redis Stack 提供向量搜索模块 |
| MongoDB Atlas | Atlas Vector Search,托管方案 |
5. Milvus vs FAISS:深度对比
这是很多工程师在做技术选型时最关心的问题。先说结论:Milvus 是完整的数据库系统,FAISS 是高性能向量检索库------它们不是同一层面的竞品,而是解决不同层次的问题。
5.1 本质差异
FAISS: 一个 C++ 库,提供向量索引构建和检索算法
└── 类比:一个超高性能的"向量搜索引擎内核"
Milvus: 一个分布式云原生数据库系统
└── 类比:一个完整的"向量数据管理平台"
| 对比维度 | FAISS | Milvus |
|---|---|---|
| 类型 | 向量检索 库(Library) | 向量 数据库(Database) |
| 开发者 | Meta(Facebook AI Research) | Zilliz(开源社区驱动) |
| 首次发布 | 2017 | 2019 |
| 语言 | C++(Python/Java 绑定) | Go(核心)+ C++(索引引擎) |
| 开源协议 | MIT | Apache 2.0 |
5.2 架构对比
FAISS 架构
FAISS 是典型的 库级架构------你在自己的进程里加载它,索引数据存于内存或本地磁盘。没有服务端、没有网络协议、没有持久化机制(需自己实现)。
┌──────────────────────────────────┐
│ 你的 Python 进程 │
│ ┌────────────────────────────┐ │
│ │ FAISS 库 │ │
│ │ ┌──────────────────────┐ │ │
│ │ │ Index (HNSW/IVF/...) │ │ │
│ │ │ 全部在进程内存中 │ │ │
│ │ └──────────────────────┘ │ │
│ └────────────────────────────┘ │
└──────────────────────────────────┘
优点 :零网络开销,极致低延迟,GPU 加速直接可用。
缺点:单机内存瓶颈,索引需手动序列化/反序列化,无数据管理能力。
Milvus 架构
Milvus 是 存算分离的云原生架构,由多个微服务组成:
┌─────────────────────────────────────────────────────────────────┐
│ Milvus 架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Proxy │ │ Query │ │ Data │ │ Index │ │
│ │ (接入层) │ │ Node │ │ Node │ │ Node │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │ │
│ ┌────┴──────────────┴──────────────┴──────────────┴────┐ │
│ │ Message Queue (Pulsar/Kafka) │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │ │ │ │
│ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ │
│ │ Etcd │ │ MinIO │ │ MinIO │ │ MinIO │ │
│ │ (元数据) │ │ (日志) │ │ (数据) │ │ (索引) │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────────────────────────┘
优点 :水平扩展、高可用、数据持久化、多租户、SDK 丰富。
缺点:部署复杂度高,资源开销大,延迟比 FAISS 略高(网络 + 序列化开销)。
5.3 功能对比
| 功能 | FAISS | Milvus |
|---|---|---|
| 向量索引 | ✅ 极其丰富(30+ 种索引) | ✅ 丰富(支持 HNSW、IVF、DiskANN 等) |
| GPU 加速 | ✅ 原生支持,CUDA 实现 | ⚠️ 有限支持,依赖底层 FAISS/Knowhere |
| 数据持久化 | ❌ 需手动实现 | ✅ 自动持久化到 MinIO/S3 |
| 水平扩展 | ❌ 单机 | ✅ 分布式,支持动态扩缩容 |
| CRUD 操作 | ❌ 需重建索引 | ✅ 支持插入、删除、更新 |
| 标量过滤 | ⚠️ 需自行实现(如结合 IDSelector) | ✅ 原生支持,可结合向量 + 标量条件 |
| 多租户 | ❌ | ✅ Partition Key / RBAC |
| 数据一致性 | ❌ 无保证 | ✅ 可配置一致性级别 |
| 监控与运维 | ❌ 无 | ✅ Prometheus + Grafana 集成 |
| SDK 语言 | Python, C++ | Python, Java, Go, Node.js, C# |
| 云服务 | ❌ | ✅ Zilliz Cloud 全托管 |
| 社区生态 | 学术界 + 工业界广泛使用 | 快速增长,CNCF 毕业项目 |
5.4 性能对比
这里给出典型场景下的性能参考数据(基于公开 Benchmark):
| 场景 | FAISS (HNSW) | Milvus (HNSW) |
|---|---|---|
| Top-10 检索延迟 | ~0.5 ms | ~2-5 ms |
| QPS(单机) | 10,000+ | 5,000-8,000 |
| QPS(分布式) | N/A(单机) | 100,000+(水平扩展) |
| 最大向量数 | 受单机内存限制(通常 <1 亿) | 百亿级(存算分离) |
| 索引构建速度 | 快(纯内存) | 中等(涉及 I/O) |
| 召回率@10 | 95-99%(可调) | 95-99%(可调) |
关键洞察:FAISS 在单机场景下的原始性能无敌;但当你需要管理超过单机内存容量的数据、需要高可用、需要多用户访问时,Milvus 的优势就显现出来了。
5.5 代码示例对比
FAISS:构建索引与检索
python
import faiss
import numpy as np
# 准备数据
dim = 768
data = np.random.random((100000, dim)).astype('float32')
# 构建 HNSW 索引
index = faiss.IndexHNSWFlat(dim, 32) # 32 是 M 参数(连接数)
index.add(data)
# 检索
query = np.random.random((1, dim)).astype('float32')
distances, indices = index.search(query, k=10)
# 持久化(手动)
faiss.write_index(index, "index.faiss")
index = faiss.read_index("index.faiss") # 加载
Milvus:构建索引与检索
python
from pymilvus import MilvusClient, DataType
# 连接(支持集群)
client = MilvusClient(uri="http://localhost:19530")
# 创建 Collection(相当于表)
client.create_collection(
collection_name="my_docs",
dimension=768,
metric_type="COSINE",
auto_id=True,
)
# 插入数据
client.insert(
collection_name="my_docs",
data=[{"vector": vec.tolist(), "text": "..."} for vec in vectors],
)
# 创建索引
client.create_index(
collection_name="my_docs",
index_type="HNSW",
metric_type="COSINE",
params={"M": 16, "efConstruction": 200},
)
# 检索(支持标量过滤)
results = client.search(
collection_name="my_docs",
data=[query.tolist()],
limit=10,
filter='category == "tech"', # Milvus 独有:标量过滤
output_fields=["text", "url"],
)
5.6 何时选择 FAISS?
- ✅ 研究、实验、原型阶段
- ✅ 数据量在单机内存容量以内(百万~数千万级)
- ✅ 对检索延迟要求极高(亚毫秒级)
- ✅ 需要在 GPU 上做大规模向量检索
- ✅ 你的应用是单进程、无需多租户
- ✅ 你愿意自己实现持久化、CRUD、服务化等周边逻辑
5.7 何时选择 Milvus?
- ✅ 生产环境的 RAG 系统
- ✅ 数据量超单机内存(亿级以上)
- ✅ 需要多用户、多团队共享
- ✅ 需要数据持久化、高可用、灾难恢复
- ✅ 需要混合搜索(向量相似度 + 标量过滤)
- ✅ 不想自己造轮子,需要开箱即用的数据库功能
- ✅ 团队更习惯 SQL/NoSQL 数据库的使用体验
6. 选型建议与实践
6.1 决策树
数据量 < 500 万?
├── 是 → 直接用 FAISS 或 Chroma,简单高效
└── 否 → 是否需要持久化/多用户/高可用?
├── 否 → FAISS + 自定义服务化,或 Qdrant(轻量数据库)
└── 是 → 需要分布式?
├── 否 → Milvus Standalone 或 Qdrant
└── 是 → Milvus Cluster 或 Pinecone(全托管)
6.2 混合方案:Milvus + FAISS
有趣的是,Milvus 底层依赖 Knowhere------一个对 FAISS、HNSWlib、Annoy 等库的统一抽象层。也就是说,Milvus 在索引执行引擎层面实际上可以用 FAISS 的算法。所以你可以把 Milvus 理解为"加了数据库皮肤的 FAISS 生态"。
在某些极致性能要求的场景,还有一种混合架构:
写入路径:Milvus(数据管理、持久化、高可用)
检索路径:FAISS(将热数据加载到 GPU FAISS 索引,亚毫秒检索)
6.3 从 RAG 原型到生产的最佳实践
- 原型阶段:Chroma + LangChain/LlamaIndex,快速跑通 RAG 链路。
- 小规模生产(<100 万向量):PostgreSQL + pgvector,复用现有数据库基础设施。
- 中大规模生产(100 万 ~ 10 亿向量):Milvus Standalone / Qdrant。
- 超大规模生产(10 亿+):Milvus Cluster / Zilliz Cloud / Pinecone。
- 极致性能场景:FAISS GPU + 自定义服务化封装。
7. 总结
RAG 正在成为大模型落地的标准范式,而向量数据库是这个范式中的关键基础设施。理解 Embedding 的语义张量本质、掌握 ANN 索引算法的取舍、选对向量数据库工具,是每个 AI 工程师的必修课。
最后用一句话总结 FAISS 与 Milvus 的关系:
FAISS 是你造车时的引擎,而 Milvus 是一辆可以直接上路的汽车。 如果你只需要一个高性能引擎嵌入到自己的系统中,选 FAISS;如果你需要一辆能载客、能上高速、有安全气囊的整车,选 Milvus。
如果这篇文章对你有帮助,欢迎分享给更多做 AI 应用的同学。也欢迎在评论区交流你的技术选型经验和踩坑记录!