前言
ChatGPT 的爆火让"向量数据库"从一个冷门基础设施一跃成为 AI 时代最炙手可热的技术之一。从 RAG(检索增强生成)到语义搜索,从推荐系统到多模态检索,向量数据库几乎无处不在。但向量数据库到底是什么?它为什么能实现"语义级"的搜索?底层又是怎么做到在亿级向量中毫秒返回结果的?
这篇文章将从最底层的数学原理出发,带你彻底搞懂向量数据库。
一、为什么需要向量数据库?
1.1 传统搜索的困境
假设你搜索"如何缓解工作压力",传统关键词搜索会拆分为 ["如何", "缓解", "工作", "压力"],然后逐词匹配文档。但下面这些语义相关的文档它都搜不到:
- "职场减压的十个方法" ------ 没有"缓解"和"工作压力"这两个词
- "下班后如何放松身心" ------ 词汇完全不同,但语义高度相关
- "职业倦怠的自我调节" ------ 更是连一个关键词都对不上
根本原因:关键词搜索把文本当成"符号集合",无法理解语义。
1.2 向量搜索的思路
向量搜索的核心思想是:把一切数据映射到高维空间,用空间距离衡量语义相似度。
"如何缓解工作压力" → Embedding → [0.23, -0.67, 0.41, ...] (1536维)
"职场减压的十个方法" → Embedding → [0.21, -0.65, 0.43, ...] (1536维)
"今天天气不错" → Embedding → [-0.12, 0.89, -0.33, ...] (1536维)
前两个向量在空间中"挨得很近",第三个则"离得很远"------这就是语义搜索的本质。

二、从数学原理理解向量搜索
2.1 什么是向量嵌入(Embedding)?
Embedding 是将非结构化数据(文本、图片、音频)映射为固定维度浮点数向量的过程。这个映射不是随机的------它保留了数据的语义信息:语义相近的数据,向量在空间中也相近。
目前主流的 Embedding 模型:
| 模型 | 维度 | 提供方 | 特点 |
|---|---|---|---|
| text-embedding-3-small | 1536 | OpenAI | 通用性强 |
| text-embedding-3-large | 3072 | OpenAI | 精度更高 |
| bge-large-zh | 1024 | BAAI | 中文最优 |
| m3e-base | 768 | M3E | 中文开源 |
| Cohere embed-v3 | 1024 | Cohere | 多语言 |
2.2 相似度度量
有了向量,怎么衡量"远近"?有三种主流度量方式:
余弦相似度(Cosine Similarity)------ 最常用
衡量两个向量方向的相似性,忽略长度:
cos ( θ ) = A ⃗ ⋅ B ⃗ ∣ A ⃗ ∣ × ∣ B ⃗ ∣ \cos(\theta) = \frac{\vec{A} \cdot \vec{B}}{|\vec{A}| \times |\vec{B}|} cos(θ)=∣A ∣×∣B ∣A ⋅B
python
import numpy as np
def cosine_similarity(a, b):
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
# 语义相近 → 相似度高
cosine_similarity([0.23, -0.67, 0.41], [0.21, -0.65, 0.43]) # ≈ 0.998
# 语义无关 → 相似度低
cosine_similarity([0.23, -0.67, 0.41], [-0.12, 0.89, -0.33]) # ≈ -0.85
为什么用余弦? 因为 Embedding 模型输出的向量通常已经归一化,余弦相似度计算高效且效果稳定。
欧氏距离(Euclidean Distance)
空间中两点的直线距离:
d = ∑ i = 1 n ( a i − b i ) 2 d = \sqrt{\sum_{i=1}^{n}(a_i - b_i)^2} d=i=1∑n(ai−bi)2
内积(Inner Product)
直接计算向量点积,等价于归一化后的余弦相似度:
IP = A ⃗ ⋅ B ⃗ = ∑ i = 1 n a i × b i \text{IP} = \vec{A} \cdot \vec{B} = \sum_{i=1}^{n} a_i \times b_i IP=A ⋅B =i=1∑nai×bi
| 度量方式 | 值域 | 适用场景 |
|---|---|---|
| 余弦相似度 | -1, 1 | 文本语义搜索(最常用) |
| 欧氏距离 | [0, +∞) | 图像检索、聚类 |
| 内积 | (-∞, +∞) | 归一化向量时等价余弦 |
2.3 暴力搜索的问题
最朴素的搜索方式是线性扫描(Brute Force):把查询向量和数据库中每个向量逐一计算相似度,取 Top-K。
python
def brute_force_search(query, vectors, top_k=10):
scores = [(i, cosine_similarity(query, v)) for i, v in enumerate(vectors)]
scores.sort(key=lambda x: x[1], reverse=True)
return scores[:top_k]
问题:时间复杂度 O(N×D),N 是向量数,D 是维度。
| 数据规模 | 维度 | 线性扫描耗时 |
|---|---|---|
| 100万 | 1536 | ~500ms |
| 1亿 | 1536 | ~50s |
| 10亿 | 1536 | ~500s |
10亿向量要等8分钟?显然不可接受。这就是向量数据库要解决的核心问题:在海量向量中实现亚秒级近似最近邻搜索(ANN)。
三、ANN索引:向量数据库的灵魂
向量数据库的核心技术是 ANN(Approximate Nearest Neighbor)索引------用空间换时间,用精度换速度。
3.1 HNSW(Hierarchical Navigable Small World)------ 当前最主流
HNSW 是目前工业界最流行的 ANN 算法,Milvus、Qdrant、Weaviate 等都默认使用它。
核心思想
HNSW 的灵感来自"六度分隔理论":在社交网络中,任何两个人之间平均只需要6个中间人就能建立联系。HNSW 把向量空间也构建成一个"小世界网络":
-
多层图结构:底层包含所有向量,上层只保留少量"枢纽"向量
-
贪心搜索:从顶层开始,每层贪心地走向离目标最近的邻居
-
逐层下降:到达当前层最近点后,进入下一层继续搜索
Layer 2: ●─────────────────● (稀疏,长距离跳转)
│ │
Layer 1: ●─────●─────●─────● (中等密度)
│ │ │ │
Layer 0: ●─●─●─●─●─●─●─●─●─●─● (密集,精确搜索)

搜索过程详解
假设要查找离查询向量 Q 最近的 Top-K 个点:
Step 1: Layer 2 - 从入口点EP开始,贪心走向Q的最近邻 → 找到A
Step 2: Layer 1 - 从A开始,贪心搜索 → 找到B
Step 3: Layer 0 - 从B开始,扩展邻居搜索 → 找到C, D, E (Top-K结果)
关键参数:
| 参数 | 含义 | 影响 |
|---|---|---|
| M | 每个节点的最大连接数 | 越大→图越稠密→召回率高→内存大 |
| efConstruction | 构建图时的搜索宽度 | 越大→图质量高→构建慢 |
| efSearch | 查询时的搜索宽度 | 越大→召回率高→查询慢 |
python
# HNSW参数调优经验值
params = {
"M": 16, # 通用场景
"efConstruction": 256, # 构建质量优先
"efSearch": 64, # 查询时平衡精度和速度
}
# 高精度场景
params_high_recall = {
"M": 32,
"efConstruction": 512,
"efSearch": 256,
}
HNSW的优缺点
- ✅ 查询延迟极低(1-5ms)
- ✅ 召回率高(95%+ with proper params)
- ✅ 支持动态插入删除
- ❌ 内存占用大(需要存整个图)
- ❌ 构建速度较慢
3.2 IVF(Inverted File Index)------ 大规模数据首选
IVF 的思路是"先粗筛,再细搜":把向量空间划分为多个聚类(Voronoi单元),查询时只搜索最近的几个聚类。
核心步骤
构建阶段:
- 对所有向量做 K-Means 聚类,得到 nlist 个聚类中心
- 每个向量归入最近的聚类中心,形成倒排索引
查询阶段:
-
计算查询向量到所有聚类中心的距离
-
选取最近的 nprobe 个聚类
-
在这些聚类内做暴力搜索,返回 Top-K
┌───────────┐ │ 聚类中心 │ │ ● ● ● │ │ / \ | / \ │ │● ●● ●│ nlist=9个聚类 │ \ / | \ / │ │ ● ● ● │ └───────────┘查询Q → 找到最近的nprobe=3个聚类 → 在3个聚类内暴力搜索

IVF + PQ(Product Quantization)------ 内存杀手锏
当向量数达到亿级,内存成为瓶颈。PQ(乘积量化)通过向量压缩解决这一问题:
-
将 D 维向量切分为 M 个子向量(如 1536维 → 12个128维子向量)
-
每个子向量独立做 K-Means 聚类(通常 256 个聚类中心)
-
每个子向量用 1 字节(聚类中心ID)表示
原始向量: [0.23, -0.67, 0.41, ...] (1536维 × 4字节 = 6144字节)
↓ PQ压缩
编码结果: [12, 187, 43, ...] (12个子码 × 1字节 = 12字节)压缩比: 6144 / 12 = 512倍!
距离计算:使用查表法,预计算查询子向量到每个聚类中心的距离表,然后查表求和:
python
# PQ距离计算 - 查表法
# 预计算距离表: dist_table[m][k] = 查询子向量m 到 聚类中心k 的距离
dist_table = precompute_distance_table(query_subvectors, codebooks)
# 查表求和得到近似距离
for vector_id in candidate_ids:
codes = pq_codes[vector_id] # [c1, c2, ..., cM]
distance = sum(dist_table[m][codes[m]] for m in range(M))
| 方案 | 内存占用 | 查询速度 | 召回率 |
|---|---|---|---|
| 暴力搜索 | 1x | 最慢 | 100% |
| IVF | 1x | 快 | 95%+ |
| IVF+PQ | 1/512x | 快 | 90%+ |
| IVF+PQ+Rerank | 1/512x + 1x(部分) | 中 | 98%+ |
3.3 其他索引类型
ANNOY(Approximate Nearest Neighbors Oh Yeah)
Spotify 开源,用随机投影树实现:
┌───── 分裂超平面1 ─────┐
│ │
┌────┐ ┌────┐
│左子树│ │右子树│
└────┘ └────┘
- 构建多棵随机投影树,查询时合并结果
- ✅ 构建快,支持磁盘存储
- ❌ 不支持动态插入,召回率一般
ScaNN(Scalable Nearest Neighbors)
Google 开源,在各向量化量化(AQ)上做了创新:
- 使用各向异性量化(Anisotropic Quantization),比PQ损失更小
- 在相同压缩比下,召回率比PQ高3-5%
- 目前 ANN-Benchmarks 上的性能冠军之一
3.4 索引选型指南
数据规模
│
┌───────┼───────┐
│ │ │
<100万 100万-1亿 >1亿
│ │ │
HNSW HNSW IVF+PQ
(内存够) (内存够) (内存不够时)
│
需要实时更新?
│ │
是 否
│ │
HNSW IVF_FLAT
| 场景 | 推荐索引 | 理由 |
|---|---|---|
| <100万向量,内存充足 | HNSW | 延迟最低,召回率最高 |
| 100万-1亿,内存充足 | HNSW | 仍然可行,注意M参数 |
| 亿级向量,内存有限 | IVF+PQ | 压缩比高,内存友好 |
| 需要实时增删 | HNSW | 原生支持动态更新 |
| 离线构建,只读查询 | IVF+ScaNN | 离线构建质量更高 |
| 磁盘存储场景 | DiskANN | 专为磁盘优化 |
四、主流向量数据库深度对比
4.1 全景图
┌─────────────────────────────────────────────────────┐
│ 向量数据库生态 │
├─────────────┬─────────────┬─────────────────────────┤
│ 专用向量DB │ 向量插件 │ 云托管服务 │
├─────────────┼─────────────┼─────────────────────────┤
│ Milvus │ pgvector │ Zilliz Cloud(Milvus) │
│ Qdrant │ Elasticsearch│ Pinecone │
│ Weaviate │ OpenSearch │ Weaviate Cloud │
│ Chroma │ Redis │ Qdrant Cloud │
│ Faiss(库) │ MongoDB │ Azure AI Search │
│ ScaNN(库) │ │ │
└─────────────┴─────────────┴─────────────────────────┘
4.2 核心对比
| 维度 | Milvus | Qdrant | Weaviate | Chroma | pgvector |
|---|---|---|---|---|---|
| 语言 | Go+C++ | Rust | Go | Python | C |
| 索引类型 | HNSW, IVF, IVF_PQ, DiskANN | HNSW | HNSW | HNSW | HNSW, IVFFlat |
| 分布式 | ✅ 原生分布式 | ✅ 分片 | ✅ 分片 | ❌ 单机 | ❌ 跟随PG |
| 过滤搜索 | ✅ 强 | ✅ 强 | ✅ 强 | ⚠️ 基础 | ✅ SQL |
| 数据持久化 | ✅ | ✅ WAL | ✅ | ⚠️ 简单 | ✅ PG |
| 多向量查询 | ✅ | ✅ | ✅ | ❌ | ❌ |
| 标量过滤+向量 | ✅ | ✅ | ✅ | ⚠️ | ✅ |
| 动态增删 | ✅ | ✅ | ✅ | ✅ | ✅ |
| 云原生 | ✅ K8s | ✅ | ✅ | ❌ | ❌ |
| 最大规模 | 100亿+ | 亿级 | 亿级 | 百万级 | 千万级 |
| 开源协议 | Apache 2.0 | Apache 2.0 | BSD-3 | Apache 2.0 | PostgreSQL |
| 成熟度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ |
4.3 各数据库详解
Milvus ------ 企业级首选
Milvus 是目前最成熟的向量数据库,由 Zilliz 开发,Linux Foundation 孵化。
核心优势:
- 原生分布式架构,支持水平扩展
- 索引类型最丰富(HNSW、IVF、IVF_PQ、IVF_SQ8、DiskANN)
- 支持标量字段过滤 + 向量搜索的混合查询
- 100亿+向量规模的生产验证
架构:
┌──────────────────────────────────────┐
│ Proxy (无状态) │ ← 接入层
├──────────────────────────────────────┤
│ Root Coord │ Query Coord │ Data Coord│ ← 协调层
├──────────────────────────────────────┤
│ Query Node │ Data Node │ Index Node│ ← 执行层
├──────────────────────────────────────┤
│ MinIO / S3 │ Pulsar / Kafka │ ← 存储层
└──────────────────────────────────────┘
适用场景:大规模生产环境、需要分布式、对可靠性要求高
快速上手:
python
from pymilvus import connections, Collection, FieldSchema, CollectionSchema, DataType
connections.connect("default", host="localhost", port="19530")
# 创建集合
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=1536),
FieldSchema(name="title", dtype=DataType.VARCHAR, max_length=256),
]
schema = CollectionSchema(fields, description="文章向量")
collection = Collection("articles", schema)
# 创建HNSW索引
index_params = {
"metric_type": "COSINE",
"index_type": "HNSW",
"params": {"M": 16, "efConstruction": 256}
}
collection.create_index("embedding", index_params)
# 插入数据
import numpy as np
data = [
[1, 2, 3], # id
[np.random.rand(1536).tolist() for _ in range(3)], # embedding
["文章1", "文章2", "文章3"], # title
]
collection.insert(data)
# 搜索
collection.load()
results = collection.search(
data=[np.random.rand(1536).tolist()],
anns_field="embedding",
param={"metric_type": "COSINE", "params": {"ef": 64}},
limit=10,
expr='title like "%技术%"' # 标量过滤
)
Qdrant ------ 性能黑马
Qdrant 用 Rust 编写,性能极佳,API 设计优雅。
核心优势:
- Rust 编写,内存安全 + 高性能
- 单机性能极强(10万+ QPS)
- WAL 持久化,数据安全
- 优秀的过滤搜索支持(Payload Filters)
- 支持 Cosine、Euclidean、Dot Product
适用场景:中等规模、对性能敏感、偏好轻量部署
python
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct
client = QdrantClient(host="localhost", port=6333)
# 创建集合
client.create_collection(
collection_name="articles",
vectors_config=VectorParams(size=1536, distance=Distance.COSINE),
)
# 插入数据
client.upsert(
collection_name="articles",
points=[
PointStruct(id=1, vector=[0.1, 0.2, ...], payload={"title": "文章1", "category": "tech"}),
PointStruct(id=2, vector=[0.3, 0.4, ...], payload={"title": "文章2", "category": "life"}),
]
)
# 搜索(带过滤)
results = client.search(
collection_name="articles",
query_vector=[0.15, 0.25, ...],
query_filter={"must": [{"key": "category", "match": {"value": "tech"}}]},
limit=10,
)
Weaviate ------ 语义优先
Weaviate 的特色是深度集成各种 Embedding 模型,可以"自动向量化"。
核心优势:
- 内置多种 Embedding 模块(OpenAI、Cohere、HuggingFace等)
- 数据写入时自动调用模型生成向量
- GraphQL API,查询灵活
- 支持多模态(文本+图片)
适用场景:不想自己管Embedding、需要快速原型验证
python
import weaviate
client = weaviate.Client("http://localhost:8080")
# 创建类(自动向量化)
schema = {
"class": "Article",
"vectorizer": "text2vec-openai",
"properties": [
{"name": "title", "dataType": ["text"]},
{"name": "content", "dataType": ["text"]},
]
}
client.schema.create_class(schema)
# 写入数据(自动调用OpenAI生成向量)
client.data_object.create(
class_name="Article",
data_object={"title": "向量数据库入门", "content": "本文介绍向量数据库..."},
# 不需要手动提供向量!
)
# 语义搜索
results = (
client.query
.get("Article", ["title", "content"])
.with_near_text({"concepts": ["如何使用向量数据库"]})
.with_limit(10)
.do()
)
Chroma ------ 轻量原型利器
Chroma 是最简单的向量数据库,专为 AI 应用原型设计。
核心优势:
- 3行代码即可使用
- 内置 Embedding 函数
- 纯 Python,pip install 即用
适用场景:快速原型、Jupyter实验、小型项目
python
import chromadb
client = chromadb.Client() # 内存模式,无需启动服务
collection = client.create_collection("articles")
# 添加数据(自动Embedding)
collection.add(
documents=["向量数据库入门", "深度学习实战", "Python编程技巧"],
metadatas=[{"source": "blog"}, {"source": "book"}, {"source": "tutorial"}],
ids=["1", "2", "3"]
)
# 查询
results = collection.query(
query_texts=["什么是向量搜索"],
n_results=2,
)
# 自动返回语义最相关的结果
pgvector ------ 不引入新组件的选择
pgvector 是 PostgreSQL 的向量扩展,适合已有 PG 的团队。
核心优势:
- 复用 PG 的所有能力(事务、备份、权限等)
- SQL 查询,学习成本为零
- 与现有业务表无缝 JOIN
适用场景:已有PG基础设施、向量规模不大、不想引入新组件
sql
-- 安装扩展
CREATE EXTENSION vector;
-- 创建表
CREATE TABLE articles (
id SERIAL PRIMARY KEY,
title TEXT,
content TEXT,
embedding vector(1536)
);
-- 创建HNSW索引
CREATE INDEX ON articles USING hnsw (embedding vector_cosine_ops);
-- 插入数据
INSERT INTO articles (title, content, embedding)
VALUES ('向量数据库入门', '...', '[0.1, 0.2, 0.3, ...]');
-- 语义搜索 + 标量过滤
SELECT title, 1 - (embedding <=> '[0.15, 0.25, ...]') AS similarity
FROM articles
WHERE title LIKE '%数据库%'
ORDER BY embedding <=> '[0.15, 0.25, ...]'
LIMIT 10;
4.4 性能基准测试
基于 ANN-Benchmarks(glove-100-angular, 100万向量):
| 数据库 | 索引 | QPS | Recall@10 | 延迟P99 |
|---|---|---|---|---|
| Milvus | HNSW | ~8000 | 99.2% | ~3ms |
| Qdrant | HNSW | ~12000 | 99.0% | ~2ms |
| Weaviate | HNSW | ~6000 | 98.8% | ~5ms |
| pgvector | HNSW | ~3000 | 98.5% | ~8ms |
| Chroma | HNSW | ~2000 | 98.0% | ~12ms |
注:Qdrant 的 QPS 最高得益于 Rust 的零成本抽象和优化的内存布局。Milvus 在分布式场景下 QPS 可线性扩展。
五、RAG实战:构建知识库问答系统
这是向量数据库目前最火的应用场景。完整架构如下:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 知识文档 │────→│ 文档切片 │────→│ Embedding │
│ (PDF/MD/..) │ │ (Chunking) │ │ 模型 │
└──────────────┘ └──────────────┘ └──────┬───────┘
│
┌──────▼───────┐
│ 向量数据库 │
│ (Milvus等) │
└──────┬───────┘
│
┌──────────────┐ ┌──────────────┐ ┌──────▼───────┐
│ 用户提问 │────→│ 问题向量 │────→│ 相似度检索 │
│ │ │ Embedding │ │ Top-K文档 │
└──────────────┘ └──────────────┘ └──────┬───────┘
│
┌──────▼───────┐
│ LLM (GPT等) │
│ 上下文+问题 │
└──────┬───────┘
│
┌──────▼───────┐
│ 生成回答 │
└──────────────┘
5.1 完整代码实现
python
"""
RAG知识库问答系统 - 基于Milvus + OpenAI
"""
import openai
from pymilvus import connections, Collection, FieldSchema, CollectionSchema, DataType
import numpy as np
from typing import List
# ============ 1. Embedding 封装 ============
class EmbeddingService:
def __init__(self, model="text-embedding-3-small"):
self.model = model
self.dim = 1536
def encode(self, texts: List[str]) -> List[List[float]]:
resp = openai.embeddings.create(input=texts, model=self.model)
return [item.embedding for item in resp.data]
# ============ 2. 文档切片 ============
def chunk_text(text: str, chunk_size=500, overlap=50) -> List[str]:
"""按字符数切片,带重叠"""
chunks = []
start = 0
while start < len(text):
end = start + chunk_size
chunks.append(text[start:end])
start += chunk_size - overlap
return chunks
# ============ 3. 向量数据库操作 ============
class KnowledgeBase:
def __init__(self, collection_name="knowledge_base"):
connections.connect("default", host="localhost", port="19530")
self.collection_name = collection_name
self.embedder = EmbeddingService()
self._init_collection()
def _init_collection(self):
fields = [
FieldSchema("id", DataType.INT64, is_primary=True, auto_id=True),
FieldSchema("embedding", DataType.FLOAT_VECTOR, dim=self.embedder.dim),
FieldSchema("content", DataType.VARCHAR, max_length=2048),
FieldSchema("source", DataType.VARCHAR, max_length=256),
]
schema = CollectionSchema(fields, description="知识库")
self.collection = Collection(self.collection_name, schema)
# 创建HNSW索引
self.collection.create_index("embedding", {
"metric_type": "COSINE",
"index_type": "HNSW",
"params": {"M": 16, "efConstruction": 256}
})
def add_documents(self, documents: List[dict]):
"""添加文档到知识库"""
all_contents = []
all_sources = []
all_embeddings = []
for doc in documents:
chunks = chunk_text(doc["content"])
embeddings = self.embedder.encode(chunks)
all_contents.extend(chunks)
all_sources.extend([doc["source"]] * len(chunks))
all_embeddings.extend(embeddings)
self.collection.insert([all_embeddings, all_contents, all_sources])
self.collection.load()
print(f"Added {len(all_contents)} chunks from {len(documents)} documents")
def search(self, query: str, top_k: int = 5, filter_expr: str = None) -> List[dict]:
"""语义搜索"""
query_embedding = self.embedder.encode([query])[0]
results = self.collection.search(
data=[query_embedding],
anns_field="embedding",
param={"metric_type": "COSINE", "params": {"ef": 64}},
limit=top_k,
expr=filter_expr,
output_fields=["content", "source"],
)
return [
{"content": hit.entity.get("content"), "source": hit.entity.get("source"), "score": hit.score}
for hit in results[0]
]
# ============ 4. RAG问答 ============
class RAGChat:
def __init__(self):
self.kb = KnowledgeBase()
self.system_prompt = """你是一个专业的知识库助手。请根据以下参考资料回答用户问题。
如果参考资料中没有相关信息,请诚实地说"根据现有资料无法回答"。
参考资料:
{context}"""
def chat(self, question: str) -> str:
# 1. 检索相关文档
results = self.kb.search(question, top_k=5)
context = "\n\n".join([f"[来源: {r['source']}]\n{r['content']}" for r in results])
# 2. 构造Prompt
messages = [
{"role": "system", "content": self.system_prompt.format(context=context)},
{"role": "user", "content": question},
]
# 3. 调用LLM生成回答
response = openai.chat.completions.create(
model="gpt-4o",
messages=messages,
temperature=0.3,
)
return response.choices[0].message.content
# ============ 5. 使用示例 ============
if __name__ == "__main__":
rag = RAGChat()
# 导入知识文档
rag.kb.add_documents([
{"content": "StarRocks是一款高性能MPP分析型数据库...", "source": "starrocks_intro.md"},
{"content": "向量数据库使用ANN索引实现毫秒级搜索...", "source": "vector_db.md"},
])
# 问答
answer = rag.chat("StarRocks和ClickHouse哪个更适合多表Join?")
print(answer)
六、选型决策树
开始选型
│
├─ 已有PostgreSQL且不想引入新组件?
│ └─ 是 → pgvector
│
├─ 只做快速原型/Jupyter实验?
│ └─ 是 → Chroma
│
├─ 需要自动Embedding(不想自己调模型)?
│ └─ 是 → Weaviate
│
├─ 数据量 > 10亿,需要分布式?
│ └─ 是 → Milvus / Zilliz Cloud
│
├─ 追求极致单机性能?
│ └─ 是 → Qdrant
│
├─ 需要云托管(免运维)?
│ └─ 是 → Pinecone / Zilliz Cloud
│
└─ 通用场景,中等规模
└─ Qdrant 或 Milvus
七、常见误区与最佳实践
误区1:向量数据库能替代传统数据库
❌ 向量数据库不是 传统数据库的替代品。它擅长的是相似度搜索,而非精确查询、事务处理、复杂JOIN。正确的做法是:向量数据库 + 传统数据库配合使用。
误区2:维度越高越好
❌ 维度越高,信息保留越完整,但:
- 存储和计算成本线性增长
- 高维空间中距离的区分度下降(维度灾难)
- 实际上 768-1536 维已经覆盖绝大多数场景
误区3:召回率必须100%
❌ ANN的本质是用精度换速度。95%的召回率在大多数业务场景下已经足够------用户感知不到"少了一个不太相关的结果",但能明显感知到"搜索变快了"。
最佳实践
- Chunk策略比向量数据库更重要:文档切分质量直接影响RAG效果,建议 300-500字/块,重叠50字
- 混合搜索:向量搜索 + 关键词搜索(BM25)融合,效果优于单一搜索
- Rerank:用Cross-Encoder对Top-K结果重排序,精度提升显著
- 监控召回率:定期用标注数据集评估召回率,调整ef/nprobe参数
- 数据版本管理:向量数据也需要版本管理,方便回滚
八、总结
向量数据库的核心价值链:
数据 → Embedding → 向量存储 → ANN索引 → 相似度检索 → 业务价值
(模型层) (存储层) (算法层) (查询层) (应用层)
选型的核心考量:
| 考量维度 | 关键问题 |
|---|---|
| 数据规模 | 百万级?亿级?是否需要分布式? |
| 查询模式 | 纯向量?向量+标量过滤?多向量? |
| 延迟要求 | 毫秒级?秒级可接受? |
| 运维能力 | 能自建集群?需要云托管? |
| 生态集成 | 与现有技术栈的兼容性? |
向量数据库不是银弹,但在 AI 应用爆发的今天,它已经是不可或缺的基础设施。理解底层原理,才能做出正确的技术选型。
参考资料:
- ANN-Benchmarks - 向量搜索算法基准测试
- HNSW论文 - Efficient and robust approximate nearest neighbor search
- Milvus官方文档
- Qdrant官方文档
- Weaviate官方文档
- pgvector GitHub
- Faiss GitHub
- ScaNN GitHub
- Faiss Wiki
- ScaNN论文 - Accelerating Large-Scale Inference with Anisotropic Vector Quantization