摘要
随着大语言模型(LLM)应用的快速发展,向量数据库作为AI时代的关键基础设施,正在成为RAG(检索增强生成)、语义搜索、智能推荐等场景的核心组件。本文将从向量嵌入的原理出发,深入讲解向量相似度搜索与近似最近邻(ANN)算法,横向对比Chroma、FAISS、Milvus、Pinecone、Qdrant、Weaviate等主流向量数据库的架构特点与适用场景,并配以完整的Python代码示例,帮助开发者快速掌握向量数据库的实战技能。
关键词: 向量数据库、向量嵌入、ANN算法、相似度搜索、RAG、Chroma、FAISS、Milvus
一、向量数据库基础
1.1 什么是向量嵌入
向量嵌入(Vector Embedding)是将高维稀疏数据(如文本、图像、音频)映射为低维稠密向量的技术手段。这些向量本质上是一串浮点数构成的数组,每个维度编码了原始数据在语义空间中的某种特征。
以文本为例,"苹果"和"香蕉"在向量空间中距离较近,因为它们同属水果;而"苹果"和"手机"虽然字面有重叠,但在语义空间中距离较远。通过这种映射方式,AI系统能够"理解"数据之间的语义关系。
# 使用 OpenAI 的 text-embedding-3-small 模型生成文本向量
from openai import OpenAI
client = OpenAI()
def get_embedding(text: str) -> list[float]:
"""
生成文本的向量嵌入表示
"""
response = client.embeddings.create(
model="text-embedding-3-small",
input=text
)
# 返回向量数组
return response.data[0].embedding
# 示例:生成三个水果名称的向量
texts = ["苹果", "香蕉", "手机"]
embeddings = [get_embedding(t) for t in texts]
print(f"向量维度: {len(embeddings[0])}")
print(f"'苹果'向量前5维: {embeddings[0][:5]}")
1.2 向量相似度搜索原理
向量相似度搜索的核心目标是:从海量向量中快速找到与查询向量最相似的Top-K个结果。相似度的衡量通常有以下几种度量方式:
| 度量方式 | 公式 | 适用场景 |
|---|---|---|
| 余弦相似度 | cos(θ) = (A·B) / (|A||B|) | 文本语义相似度 |
| 欧氏距离 | L2 = √Σ(aᵢ - bᵢ)² | 图像特征、推荐系统 |
| 点积 | A·B = Σaᵢbᵢ | 排序打分、推荐系统 |
import numpy as np
def cosine_similarity(vec_a: list[float], vec_b: list[float]) -> float:
"""
计算两个向量的余弦相似度
"""
a = np.array(vec_a)
b = np.array(vec_b)
# 余弦相似度 = 点积 / (模长乘积)
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
def euclidean_distance(vec_a: list[float], vec_b: list[float]) -> float:
"""
计算两个向量的欧氏距离
"""
a = np.array(vec_a)
b = np.array(vec_b)
return float(np.linalg.norm(a - b))
# 示例
vec1 = get_embedding("人工智能")
vec2 = get_embedding("机器学习")
vec3 = get_embedding("水果")
print(f"'人工智能' vs '机器学习' 余弦相似度: {cosine_similarity(vec1, vec2):.4f}")
print(f"'人工智能' vs '水果' 余弦相似度: {cosine_similarity(vec1, vec3):.4f}")
1.3 近似最近邻(ANN)算法
精确的最近邻搜索(KNN)在数据量达到百万、千万级别时,时间复杂度 O(N) 成为性能瓶颈。近似最近邻(ANN) 算法通过牺牲少量精度,换取数量级的性能提升,是向量数据库的底层核心技术。
主流ANN算法包括:
-
IVF(倒排文件索引):将向量空间聚类成多个桶,查询时只搜索最近的几个桶
-
HNSW(分层导航小世界图):构建多层图结构,从上往下逐步逼近目标
-
PQ(乘积量化):将高维向量压缩成短编码,降低内存占用
-
ANNOY(随机投影树):使用多棵随机投影树划分子空间
二、主流向量数据库横向对比
| 数据库 | 开发语言 | 部署方式 | 索引算法 | 适用场景 | 特点 |
|---|---|---|---|---|---|
| Chroma | Python | 本地/客户端 | HNSW | 开发测试 | 轻量级、上手快 |
| FAISS | C++/Python | 本地 | IVF/PQ/HNSW | 研究、离线分析 | Facebook开源、性能强 |
| Milvus | Go | 分布式集群 | HNSW/IVF/PQ | 生产环境 | 分布式、云原生 |
| Pinecone | - | 云服务 | HNSW | 云原生应用 | 免运维、SaaS |
| Qdrant | Rust | 本地/容器 | HNSW | 高性能需求 | 内存安全、高并发 |
| Weaviate | Go | 分布式 | HNSW/BM25 | 混合搜索 | 图结构+向量混合 |
2.1 Chroma --- 轻量级开发首选
Chroma是目前最流行的本地向量数据库,专为LLM应用原型开发设计。它将数据存储在本地SQLite文件中,零配置即可使用,非常适合快速验证RAG流程。
# 安装:pip install chromadb
import chromadb
# 初始化Chroma客户端(本地持久化存储)
client = chromadb.PersistentClient(path="./chroma_db")
# 创建集合(类似于表)
collection = client.create_collection(
name="articles",
metadata={"description": "技术文章向量库"}
)
# 添加向量数据
collection.add(
documents=[
"大语言模型(LLM)是当前AI领域的核心技术",
"向量数据库用于存储和检索高维向量数据",
"RAG技术结合了检索和生成两种能力"
],
ids=["doc1", "doc2", "doc3"],
metadatas=[
{"category": "AI", "author": "张三"},
{"category": "数据库", "author": "李四"},
{"category": "AI", "author": "王五"}
]
)
# 相似度查询
results = collection.query(
query_texts=["什么是大语言模型?"],
n_results=2 # 返回最相似的2条
)
print("查询结果:")
for i, (doc, distance) in enumerate(zip(results["documents"][0], results["distances"][0])):
print(f" [{i+1}] 距离={distance:.4f} | {doc}")
# 删除文档
collection.delete(ids=["doc3"])
2.2 FAISS --- Facebook开源本地库
FAISS(Facebook AI Similarity Search)是Facebook开源的高效相似度搜索库,专注于在CPU/GPU上实现高速向量检索。它支持批量化索引构建和搜索,是生产环境做离线分析或嵌入式部署的首选。
# 安装:pip install faiss-cpu(或 faiss-gpu)
import numpy as np
import faiss
# 1. 准备数据:生成10000条128维的随机向量(模拟嵌入向量)
dimension = 128 # 向量维度
num_vectors = 10000 # 向量数量
np.random.seed(42)
# 生成随机向量并转为float32
vectors = np.random.rand(num_vectors, dimension).astype('float32')
# 2. 使用IVF索引加速查询
nlist = 100 # 聚类中心数量
# 训练:使用IVF索引需要先对数据进行聚类
quantizer = faiss.IndexFlatIP(dimension) # IP = 内积(余弦相似度需先归一化)
index = faiss.IndexIVFFlat(quantizer, dimension, nlist, faiss.METRIC_INNER_PRODUCT)
index.train(vectors) # 训练索引
index.add(vectors) # 添加向量
# 设置搜索范围(搜索的聚类中心数,越大越精确但越慢)
index.nprobe = 10
# 3. 相似度搜索:找最相似的5个
query_vector = np.random.rand(1, dimension).astype('float32')
k = 5 # Top-K
# 执行搜索
distances, indices = index.search(query_vector, k)
print(f"查询向量维度: {query_vector.shape}")
print(f"找到的{k}个最近邻:")
for i, (idx, dist) in enumerate(zip(indices[0], distances[0])):
print(f" 第{i+1}个: 索引={idx}, 距离={dist:.4f}")
# 4. 使用HNSW索引(更精确但内存占用更大)
print("\n--- 使用HNSW索引 ---")
hnsw_index = faiss.IndexHNSWFlat(dimension, 32) # 32为每层连接数
hnsw_index.hnsw.efConstruction = 40
hnsw_index.add(vectors)
hnsw_index.hnsw.efSearch = 64 # 搜索时动态列表大小
distances_hnsw, indices_hnsw = hnsw_index.search(query_vector, k)
print(f"HNSW搜索结果 (Top-{k}):")
for i, (idx, dist) in enumerate(zip(indices_hnsw[0], distances_hnsw[0])):
print(f" 第{i+1}个: 索引={idx}, 距离={dist:.4f}")
2.3 Milvus --- 分布式生产级方案
Milvus是面向生产环境设计的分布式向量数据库,支持PB级数据存储,提供丰富的索引类型和水平扩展能力,适合企业级AI应用。
# 安装:pip install pymilvus
from pymilvus import MilvusClient, DataType
# 连接本地Milvus服务(或远程地址)
client = MilvusClient(uri="./milvus_demo.db")
# 创建集合(schema定义)
schema = MilvusClient.create_schema(
auto_id=True,
enable_dynamic_field=True,
description="RAG知识库集合"
)
# 添加字段
schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True)
schema.add_field(field_name="content", datatype=DataType.VARCHAR, max_length=512)
schema.add_field(field_name="embedding", datatype=DataType.FLOAT_VECTOR, dim=1536)
schema.add_field(field_name="category", datatype=DataType.VARCHAR, max_length=64)
# 创建索引参数
index_params = client.prepare_index_params()
index_params.add_index(
field_name="embedding",
index_type="HNSW", # HNSW索引
metric_type="COSINE", # 余弦相似度
params={"M": 16, "efConstruction": 200}
)
# 创建集合
if client.has_collection("knowledge_base"):
client.drop_collection("knowledge_base")
client.create_collection(
collection_name="knowledge_base",
schema=schema,
index_params=index_params
)
# 插入数据
documents = [
"Transformer是现代NLP的基石架构",
"向量数据库解决大模型记忆问题",
"LangChain简化LLM应用开发流程"
]
# 模拟嵌入向量(实际使用时替换为真实embedding)
embeddings = [np.random.rand(1536).tolist() for _ in documents]
data = [
{"content": doc, "embedding": emb, "category": "AI"}
for doc, emb in zip(documents, embeddings)
]
result = client.insert(collection_name="knowledge_base", data=data)
print(f"插入成功,共 {result.insert_count} 条记录")
# 搜索
query_embedding = np.random.rand(1536).tolist()
search_params = {"ef": 128}
results = client.search(
collection_name="knowledge_base",
data=[query_embedding],
limit=3,
search_params=search_params,
output_fields=["content", "category"]
)
print("\n搜索结果:")
for hits in results:
for hit in hits:
print(f" 内容: {hit['entity']['content']} | 距离: {hit['distance']:.4f}")
# 删除集合
client.drop_collection("knowledge_base")
client.close()
2.4 Pinecone --- 云原生SaaS
Pinecone是完全托管的云向量数据库,用户无需关心基础设施运维,支持快速弹性扩缩容,适合快速上线的产品级应用。
# 安装:pip install pinecone-client
from pinecone import Pinecone
# 初始化(需要API Key,可在 pinecone.io 免费注册)
pc = Pinecone(api_key="your-api-key")
# 连接到索引
index = pc.Index("llm-knowledge-base")
# 插入向量(Upsert)
vectors = [
("vec1", [0.1] * 1536, {"text": "RAG是检索增强生成技术", "source": "AI论文"}),
("vec2", [0.2] * 1536, {"text": "向量数据库支持高维向量检索", "source": "技术文档"}),
("vec3", [0.3] * 1536, {"text": "大模型需要外部知识库补充", "source": "博客"}),
]
index.upsert(vectors=vectors)
print("向量插入成功")
# 相似度查询
query_result = index.query(
vector=[0.15] * 1536,
top_k=2,
include_metadata=True
)
print("\nTop-2 相似结果:")
for match in query_result.matches:
print(f" ID: {match.id} | 分数: {match.score:.4f} | 文本: {match.metadata['text']}")
# 删除向量
index.delete(ids=["vec3"])
2.5 Qdrant --- Rust实现高性能
Qdrant由Rust语言实现,以内存安全和高并发著称。其滤波搜索能力强大,支持带条件过滤的向量查询,在同等硬件条件下性能表现优异。
# 安装:pip install qdrant-client
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct, Filter, FieldCondition, MatchValue
# 连接Qdrant服务
client = QdrantClient(url="http://localhost:6333")
# 创建集合
collection_name = "products"
vector_size = 128
if not client.collection_exists(collection_name):
client.create_collection(
collection_name=collection_name,
vectors_config=VectorParams(size=vector_size, distance=Distance.COSINE)
)
# 批量插入向量
points = [
PointStruct(
id=1,
vector=list(np.random.rand(vector_size)),
payload={"name": "iPhone 15", "category": "手机", "price": 6999}
),
PointStruct(
id=2,
vector=list(np.random.rand(vector_size)),
payload={"name": "MacBook Pro", "category": "电脑", "price": 14999}
),
PointStruct(
id=3,
vector=list(np.random.rand(vector_size)),
payload={"name": "AirPods Pro", "category": "耳机", "price": 1899}
),
]
client.upsert(collection_name=collection_name, points=points)
print("插入成功")
# 带过滤条件的搜索:只查询"手机"类别
filter_condition = Filter(
must=[FieldCondition(key="category", match=MatchValue(value="手机"))]
)
search_results = client.search(
collection_name=collection_name,
query_vector=list(np.random.rand(vector_size)),
query_filter=filter_condition,
limit=1
)
print("\n过滤搜索结果(category=手机):")
for result in search_results:
print(f" {result.payload['name']} | 分数: {result.score:.4f}")
# 删除点
client.delete(collection_name=collection_name, points=[1, 2, 3])
2.6 Weaviate --- 图结构+向量混合
Weaviate是原生支持GraphQL风格的向量数据库,能够将向量搜索与传统BM25关键词搜索结合,实现"语义+关键词"的混合检索,特别适合需要同时利用结构化数据过滤的场景。
# 安装:pip install weaviate-client
import weaviate
# 连接本地Weaviate服务
client = weaviate.Client("http://localhost:8080")
# 创建类(Collection)
schema = {
"class": "Article",
"description": "技术文章",
"vectorizer": "text2vec-transformers", # 使用本地transformer模型生成向量
"moduleConfig": {
"text2vec-transformers": {
"vectorizeClassName": False # 类名不参与向量化
}
},
"properties": [
{"name": "title", "dataType": ["text"]},
{"name": "content", "dataType": ["text"]},
{"name": "category", "dataType": ["text"]}
]
}
if client.schema.exists("Article"):
client.schema.delete_class("Article")
client.schema.create_class(schema)
print("Article 类创建成功")
# 添加对象(Weaviate自动生成向量)
client.data_object.create(
class_name="Article",
data_object={
"title": "向量数据库详解",
"content": "本文介绍向量数据库的核心概念和使用方法",
"category": "数据库"
}
)
# 语义搜索
response = client.query.get(
"Article",
["title", "content", "category"]
).with_near_text({
"concepts": ["AI检索技术"]
}).with_limit(2).do()
print("\n语义搜索结果:")
for obj in response["data"]["Get"]["Article"]:
print(f" 标题: {obj['title']} | 类别: {obj['category']}")
# 混合搜索(向量 + BM25关键词)
hybrid_response = client.query.get(
"Article",
["title", "content"]
).with_hybrid(
query="数据库",
alpha=0.5 # 0.5表示向量搜索和关键词搜索各占50%
).with_limit(2).do()
print("\n混合搜索结果:")
for obj in hybrid_response["data"]["Get"]["Article"]:
print(f" 标题: {obj['title']}")
三、索引算法详解
3.1 IVF(倒排文件索引)
IVF(Inverted File Index)的核心思想是"聚类+倒排"。先将所有向量通过K-Means聚类成N个桶(Voroni单元),查询时先找到查询向量所属的桶,再在该桶内做精确搜索。这将搜索范围从N缩小到N/nlist。
import faiss
import numpy as np
# 生成100000条256维向量
dimension = 256
n_vectors = 100_000
vectors = np.random.rand(n_vectors, dimension).astype('float32')
# 构建IVF索引
nlist = 1024 # 聚类中心数量
quantizer = faiss.IndexFlatL2(dimension)
index = faiss.IndexIVFFlat(quantizer, dimension, nlist, faiss.METRIC_L2)
index.train(vectors)
index.add(vectors)
# 搜索性能对比
import time
query = np.random.rand(1, dimension).astype('float32')
# nprobe=1(只搜索1个桶)
index.nprobe = 1
start = time.time()
_, _ = index.search(query, 10)
t1 = time.time() - start
# nprobe=32(搜索32个桶)
index.nprobe = 32
start = time.time()
_, _ = index.search(query, 10)
t2 = time.time() - start
print(f"IVF nprobe=1: {t1*1000:.2f}ms")
print(f"IVF nprobe=32: {t2*1000:.2f}ms")
print(f"精度提升约 {(t2/t1):.1f}x 时间换取更高召回率")
3.2 HNSW(分层导航小世界图)
HNSW(Hierarchical Navigable Small World)是一种基于图的近似最近邻算法。它构建一个多层图结构,上层连接稀疏(快速定位),下层连接稠密(精确结果)。查询时从最上层开始,通过贪心遍历逐步向下逼近。
import faiss
import numpy as np
dimension = 128
n_vectors = 50_000
vectors = np.random.rand(n_vectors, dimension).astype('float32')
# HNSW参数说明:
# M: 每个节点的最大连接数(越大越精确,内存越高)
# efConstruction: 构建时动态列表大小(越大构建越慢,索引质量越高)
hnsw_index = faiss.IndexHNSWFlat(dimension, 32)
hnsw_index.hnsw.efConstruction = 40
hnsw_index.add(vectors)
# 查询时 efSearch 参数:值越大搜索越精确但越慢
for ef in [16, 64, 256]:
hnsw_index.hnsw.efSearch = ef
_, indices = hnsw_index.search(np.random.rand(1, dimension).astype('float32'), 10)
print(f"efSearch={ef}: Top-10结果索引前3个={indices[0][:3]}")
3.3 PQ(乘积量化)
PQ(Product Quantization)将高维向量分割成多个子空间,对每个子空间分别做K-Means量化,从而将原始向量压缩成短编码。这使得在有限内存中存储数十亿向量成为可能。
import faiss
import numpy as np
dimension = 64
n_vectors = 20_000
vectors = np.random.rand(n_vectors, dimension).astype('float32')
# PQ参数:m为子空间数量,nbits为每个子空间的比特数
# 原始向量 64 dim * 4 bytes = 256 bytes
# 压缩后: m * nbits bits
m = 8 # 子空间数量
nbits = 8 # 每个子空间用8bit编码(256个聚类中心)
pq_index = faiss.IndexPQ(dimension, m, nbits)
pq_index.train(vectors)
pq_index.add(vectors)
# 注意:PQ索引的搜索结果是有损的
query = np.random.rand(1, dimension).astype('float32')
distances, indices = pq_index.search(query, 5)
print(f"PQ压缩后,每个向量仅需 {m * nbits / 8:.1f} bytes")
print(f"压缩比: {256 / (m * nbits / 8):.1f}x")
3.4 ANNOY(随机投影树)
ANNOY(Approximate Nearest Neighbors Oh Yeah)使用多棵随机投影二叉树来划分子空间,查询时并行搜索所有树,取最优结果。Google开发,主要用于音乐推荐(Audioscrobbler数据集)。
# 安装:pip install annoy
from annoy import AnnoyIndex
import random
dimension = 40
n_vectors = 10000
n_trees = 50
# 构建索引
annoy_index = AnnoyIndex(dimension, 'angular') # angular等价于余弦距离
for i in range(n_vectors):
vec = [random.gauss(0, 1) for _ in range(dimension)]
annoy_index.add_item(i, vec)
# 构建50棵树(越多越精确,越慢)
annoy_index.build(n_trees)
# 查询最近邻
query = [random.gauss(0, 1) for _ in range(dimension)]
results = annoy_index.get_nns_by_vector(query, 5, search_k=-1)
print(f"ANNOY查询结果 (Top-5): {results}")
# 指定搜索预算(越大越精确)
results_exact = annoy_index.get_nns_by_vector(query, 5, search_k=500)
print(f"ANNOY精确查询 (search_k=500): {results_exact}")
四、向量数据库核心操作
4.1 插入向量
import chromadb
client = chromadb.PersistentClient(path="./demo_db")
collection = client.get_or_create_collection("demo")
# 单条插入
collection.add(
ids="single_doc",
documents=["这是一段技术文档"],
metadatas=[{"author": "工程师"}]
)
# 批量插入(推荐,性能更好)
batch_data = {
"ids": [f"doc_{i}" for i in range(1000)],
"documents": [f"文档内容 {i}" for i in range(1000)],
"metadatas": [{"index": i} for i in range(1000)]
}
collection.add(**batch_data)
print(f"批量插入完成,共 {collection.count()} 条")
4.2 相似度查询
# 基于文本的语义搜索
results = collection.query(
query_texts=["查找AI相关的技术文档"],
n_results=5,
where={"author": "工程师"}, # 元数据过滤条件
include=["documents", "distances", "metadatas"] # 指定返回字段
)
# 基于向量的搜索
import numpy as np
query_vector = np.random.rand(1536).tolist()
results = collection.query(
query_embeddings=[query_vector],
n_results=3
)
4.3 范围查询(Range Search)
范围查询返回与查询向量距离在指定阈值内的所有向量,适用于"附近"类应用。
from qdrant_client import QdrantClient
client = QdrantClient(url="http://localhost:6333")
# 在Qdrant中执行范围查询(搜索距离阈值内的所有向量)
search_results = client.search(
collection_name="demo_collection",
query_vector=list(np.random.rand(128)),
score_threshold=0.7, # 相似度分数阈值(0~1)
limit=100 # 最大返回数量
)
print(f"范围内找到 {len(search_results)} 条记录")
4.4 删除与更新
# Chroma - 删除
collection.delete(ids=["doc_1", "doc_2"]) # 删除指定ID
collection.delete(where={"category": "过时"}) # 按条件删除
# Chroma - 更新(先删后加)
collection.update(
ids="doc_1",
documents=["更新后的内容"],
metadatas=[{"status": "updated"}]
)
# Milvus - 删除
client.delete(
collection_name="knowledge_base",
filter="id in [1, 2, 3]"
)
# Milvus - 更新(通过upsert实现)
client.upsert(
collection_name="knowledge_base",
data=[{"id": 1, "content": "更新内容", "embedding": [0.1]*1536}]
)
五、使用场景
5.1 RAG知识库
RAG(Retrieval-Augmented Generation)是向量数据库最经典的应用场景。通过将企业知识库文档预先向量化并存储,LLM在回答用户问题时,先检索最相关的文档片段,再结合上下文生成答案,有效缓解大模型的"幻觉"问题。
# 完整的RAG流程示例
import chromadb
from openai import OpenAI
client_chroma = chromadb.PersistentClient(path="./rag_db")
collection = client_chroma.get_or_create_collection("rag_knowledge_base")
openai_client = OpenAI()
# 第一步:文档预处理与入库
def process_document(text: str, doc_id: str):
"""将文档文本向量化并存储"""
embedding = openai_client.embeddings.create(
model="text-embedding-3-small",
input=text
).data[0].embedding
collection.add(
ids=doc_id,
documents=[text],
embeddings=[embedding]
)
# 第二步:RAG检索
def rag_query(user_question: str, top_k: int = 3) -> str:
"""
完整的RAG查询流程:
1. 将用户问题向量化
2. 检索最相关的文档片段
3. 构建Prompt并调用LLM生成答案
"""
# 1. 检索相关文档
query_embedding = openai_client.embeddings.create(
model="text-embedding-3-small",
input=user_question
).data[0].embedding
results = collection.query(
query_embeddings=[query_embedding],
n_results=top_k
)
# 2. 组合上下文
context = "\n\n".join(results["documents"][0]) if results["documents"] else "无相关背景信息"
# 3. 构建Prompt(Few-shot)
prompt = f"""基于以下背景信息回答用户问题。如果背景信息不足以回答,请如实说明。
背景信息:
{context}
用户问题:{user_question}
回答:"""
# 4. 调用LLM生成
response = openai_client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt}],
temperature=0.3
)
return response.choices[0].message.content
# 示例查询
answer = rag_query("向量数据库有哪些应用场景?")
print(f"RAG回答:{answer}")
5.2 以图搜图
将图片通过视觉模型(如CLIP、ResNet)提取特征向量,存入向量数据库,实现基于内容的图像检索。
import numpy as np
import chromadb
client = chromadb.PersistentClient(path="./image_search_db")
collection = client.get_or_create_collection("image_features")
# 模拟图片向量(实际使用CLIP等模型提取)
def simulate_image_embedding(image_path: str) -> list[float]:
"""模拟图片向量提取(实际应使用clip.encode_image())"""
np.random.seed(hash(image_path) % 2**32)
return np.random.rand(512).tolist()
# 添加图片到索引
images = [
{"id": "img_001", "path": "/photos/cat.jpg", "desc": "可爱的猫咪"},
{"id": "img_002", "path": "/photos/dog.jpg", "desc": "活泼的狗狗"},
{"id": "img_003", "path": "/photos/car.jpg", "desc": "红色跑车"},
]
for img in images:
emb = simulate_image_embedding(img["path"])
collection.add(
ids=img["id"],
documents=[img["desc"]],
embeddings=[emb]
)
# 搜索"可爱的动物"对应的图片
query_embedding = simulate_image_embedding("cute animal")
results = collection.query(
query_embeddings=[query_embedding],
n_results=2
)
print("以图搜图结果:")
for doc, dist in zip(results["documents"][0], results["distances"][0]):
print(f" {doc} | 距离: {dist:.4f}")
5.3 语义搜索
超越关键词匹配,在语义层面理解用户查询意图,返回真正相关的结果。适用于搜索引擎、企业知识管理、内容推荐等场景。
# 语义搜索示例(使用Chroma)
import chromadb
client = chromadb.PersistentClient(path="./semantic_search_db")
collection = client.get_or_create_collection("news_articles")
# 批量添加新闻数据
articles = [
"美联储宣布降息以应对经济下行压力",
"人工智能芯片需求爆发式增长",
"某地发生5.0级地震暂无人员伤亡",
"新能源车销量突破历史新高",
"科学家发现新的量子计算算法"
]
collection.add(
ids=[f"news_{i}" for i in range(len(articles))],
documents=articles
)
# 语义查询:"金融政策"应该匹配到美联储降息的新闻
query = "货币政策和经济调控"
results = collection.query(
query_texts=[query],
n_results=2
)
print(f"语义查询: '{query}'")
print("返回结果:")
for i, (doc, dist) in enumerate(zip(results["documents"][0], results["distances"][0])):
print(f" {i+1}. {doc}")
5.4 推荐系统
基于用户行为和物品特征构建向量表示,通过向量相似度计算实现个性化推荐。
import numpy as np
import chromadb
client = chromadb.PersistentClient(path="./recommendation_db")
user_collection = client.get_or_create_collection("user_profiles")
item_collection = client.get_or_create_collection("items")
# 模拟:用户兴趣向量和商品特征向量
def cosine_sim(a, b):
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
# 用户画像向量(由历史行为生成)
user_vector = np.random.rand(128).tolist()
# 商品向量
item_vectors = {
"item_101": np.random.rand(128).tolist(),
"item_102": np.random.rand(128).tolist(),
"item_103": np.random.rand(128).tolist(),
"item_104": np.random.rand(128).tolist(),
}
# 计算相似度并排序推荐
similarities = [
(item_id, cosine_sim(user_vector, vec))
for item_id, vec in item_vectors.items()
]
ranked = sorted(similarities, key=lambda x: x[1], reverse=True)
print("个性化推荐结果(Top-3):")
for item_id, score in ranked[:3]:
print(f" {item_id}: 相似度={score:.4f}")
六、综合代码示例:构建本地RAG系统
以下代码整合了本文的核心内容,展示如何使用Chroma构建一个完整的本地RAG知识库问答系统:
"""
完整的本地RAG系统示例
功能:从知识库检索相关片段 + LLM生成回答
依赖:chromadb, openai
"""
import chromadb
from openai import OpenAI
from typing import Optional
class LocalRAGSystem:
"""本地RAG知识库系统"""
def __init__(self, db_path: str, openai_api_key: str):
# 初始化Chroma持久化存储
self.client = chromadb.PersistentClient(path=db_path)
self.collection = self.client.get_or_create_collection(
name="knowledge_base",
metadata={"description": "本地RAG知识库"}
)
# 初始化OpenAI客户端
self.llm = OpenAI(api_key=openai_api_key)
self.embedding_model = "text-embedding-3-small"
def add_documents(self, documents: list[str], ids: Optional[list[str]] = None):
"""批量添加文档到知识库"""
if ids is None:
ids = [f"doc_{i}" for i in range(len(documents))]
# 批量生成嵌入向量
embeddings = []
batch_size = 100
for i in range(0, len(documents), batch_size):
batch = documents[i:i+batch_size]
response = self.llm.embeddings.create(
model=self.embedding_model,
input=batch
)
embeddings.extend([r.embedding for r in response.data])
self.collection.add(
ids=ids,
documents=documents,
embeddings=embeddings
)
print(f"✅ 成功添加 {len(documents)} 篇文档,当前共 {self.collection.count()} 篇")
def retrieve(self, query: str, top_k: int = 3) -> list[dict]:
"""从知识库检索最相关的文档"""
# 生成查询向量
response = self.llm.embeddings.create(
model=self.embedding_model,
input=query
)
query_embedding = response.data[0].embedding
# 检索
results = self.collection.query(
query_embeddings=[query_embedding],
n_results=top_k,
include=["documents", "distances", "metadatas"]
)
# 格式化返回
retrieved = []
for i in range(len(results["ids"][0])):
retrieved.append({
"id": results["ids"][0][i],
"document": results["documents"][0][i],
"distance": results["distances"][0][i],
"metadata": results["metadatas"][0][i]
})
return retrieved
def generate(self, query: str, context: list[dict]) -> str:
"""使用LLM结合上下文生成回答"""
# 组合上下文
context_text = "\n".join([
f"[{i+1}] {ctx['document']}" for i, ctx in enumerate(context)
])
prompt = f"""你是一个专业的问答助手。请基于以下参考信息回答用户问题。
如果参考信息不足以回答,请如实说明,不要编造答案。
参考信息:
{context_text}
用户问题:{query}
回答:"""
response = self.llm.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt}],
temperature=0.3,
max_tokens=500
)
return response.choices[0].message.content
def query(self, question: str, top_k: int = 3) -> dict:
"""完整的RAG查询:检索 + 生成"""
# 1. 检索
retrieved = self.retrieve(question, top_k)
# 2. 生成
answer = self.generate(question, retrieved)
return {
"question": question,
"answer": answer,
"references": [ctx["document"] for ctx in retrieved]
}
# 使用示例
if __name__ == "__main__":
# 注意:实际使用时替换为真实API Key
# rag = LocalRAGSystem("./my_rag_db", api_key="sk-...")
# 添加知识库文档
docs = [
"Transformer架构由Google在2017年提出,是现代NLP的核心模型",
"BERT是基于Transformer的双向编码器表示,在多项NLP任务上取得最优成绩",
"GPT系列是OpenAI开发的大型语言模型,采用自回归生成方式",
"向量数据库通过近似最近邻算法实现高速相似度检索",
"RAG(检索增强生成)结合了检索系统和生成模型的优势"
]
# rag.add_documents(docs)
# 结果 = rag.query("什么是RAG技术?")
# print(f"问题: {结果['question']}\n回答: {结果['answer']}")
七、总结与选型建议
开发测试阶段 推荐使用 Chroma,零配置、纯Python、SQLite后端,5分钟即可跑通RAG流程。
离线分析/研究阶段 推荐 FAISS,Facebook出品,性能极强,支持GPU加速,适合处理海量数据。
生产环境根据规模选择:
-
中小规模(百万级):Qdrant (Rust高性能)或 Milvus(功能丰富)
-
大规模/云原生:Milvus (分布式集群)或 Pinecone(SaaS免运维)
-
需要混合检索:Weaviate(向量+关键词融合)
选型核心考量因素:
-
数据规模:本地/Chroma适合万级;FAISS可达亿级;Milvus/Pinecone支持十亿级以上
-
查询延迟:Qdrant和FAISS在同硬件下最快;Pinecone延迟取决于云端配置
-
运维成本:Pinecone最低(完全托管);Chroma次之;自建Milvus需要Kubernetes经验
-
功能需求:需要过滤查询优先Qdrant;需要混合搜索优先Weaviate;需要强一致性选Milvus