RAG 向量数据库

一、简介

在RAG(检索增强生成)工作流中,向量数据库(Vector Database)是整个系统的记忆核心。它负责存储文档的向量表示,并提供高效的相似性检索能力。向量数据库的选择和使用直接影响RAG系统的性能、可扩展性和成本。

1.1 核心概念

向量数据库是一种专门设计用于存储、索引和查询高维向量的数据库系统。与传统的关系型数据库不同,向量数据库的核心操作是相似性搜索(Similarity Search),而不是精确匹配。

1.2 核心作用

在RAG工作流中:

  1. 文档被转换为向量:每个文档块通过嵌入模型变成高维向量
  2. 需要快速检索:用户查询时,需要在海量向量中找出最相似的几个
  3. 需要持久化存储:向量需要长期保存,而不是每次重新计算

传统数据库无法高效处理这种"找相似"的操作,而向量数据库专为此设计。

1.3 核心能力

能力 说明 重要性
向量存储 存储高维向量及其元数据 ⭐⭐⭐⭐⭐
相似性搜索 快速找到与查询向量最相似的向量 ⭐⭐⭐⭐⭐
混合搜索 结合向量相似度和标量过滤 ⭐⭐⭐⭐
索引构建 建立高效的数据结构加速搜索 ⭐⭐⭐⭐⭐
水平扩展 支持分布式部署和数据分片 ⭐⭐⭐
CRUD操作 增删改查向量数据 ⭐⭐⭐

二、向量数据库的核心技术

2.1 索引算法

RAG 中向量检索的核心矛盾是 "精度" vs "速度":

  • 精确检索:100%召回率,但计算量大,O(n)复杂度
  • 近似最近邻检索:牺牲少量精度换取数量级的速度提升,90-99%召回率

主流索引算法对比:

索引算法 核心原理 精度 速度 内存占用 适用场景(RAG)
暴力检索(BF) 遍历所有向量计算相似度 100% 极慢 <1 万条向量、极致精度场景
HNSW 多层导航图检索 95-99% 极快 中等 90% RAG 场景(首选)
IVF 分桶聚类 + 桶内暴力检索 90-95% 超大规模(1 亿 +)、低内存
IVF-HNSW IVF 分桶 + HNSW 桶内检索 95-98% 极快 千万 / 亿级向量、高并发
PQ 向量压缩 + 编码检索 85-90% 极快 极低 10 亿 + 向量、精度要求低

2.2 相似度度量

向量数据库支持的相似度度量:

度量 公式 特点 适用场景
余弦相似度 cos(θ) = A·B/(|A||B|) 关注方向,忽略长度 文本嵌入(最常用)
欧氏距离(L2) √Σ(A_i - B_i)² 关注绝对距离 图像、特征向量
内积(IP) Σ(A_i × B_i) 同时考虑方向和长度 向量已归一化时等同于余弦
曼哈顿距离(L1) Σ|A_i - B_i| 对异常值鲁棒 稀疏向量

2.3 元数据过滤

元数据是附加在向量上的结构化信息,例如文档的来源、作者、发布时间、类别、权限标签等。元数据过滤指的是在向量相似性搜索的过程中,根据这些属性条件缩小搜索范围,只返回满足特定业务约束的向量。

向量数据库通常采用以下三种方式之一或组合来实现元数据过滤:

  1. 预过滤(Pre-filtering)

    • 流程:先根据元数据条件从数据库中筛选出符合条件的向量 ID 列表,然后仅在这些 ID 对应的向量中进行近似最近邻搜索。
    • 优点:确保最终结果都满足元数据条件,不会漏掉符合条件的向量。
    • 缺点:如果元数据过滤后的候选集很小,向量索引可能无法充分利用,搜索精度可能下降(因为 ANN 索引通常依赖全局分布);同时两步操作可能增加延迟。
    • 适用场景:过滤条件能够将候选集缩小到可接受的范围,且对准确性要求高。
  2. 后过滤(Post-filtering)

    • 流程:先执行向量相似性搜索,返回 Top-K(通常 K 设置得比实际需要大一些),然后从这些结果中剔除不满足元数据条件的向量。
    • 优点:实现简单,向量搜索本身保持高效,无需修改索引结构。
    • 缺点:可能会丢失一些相似度高但被过滤掉的向量,尤其是当过滤条件比较严格时,Top-K 中可能包含大量不符合条件的向量,最终可用的结果不足 K 个。
    • 适用场景:元数据条件较为宽松,或可接受一定的召回损失。
  3. 混合过滤(或称"索引级过滤")

    • 流程:在向量索引构建阶段,就将元数据集成到索引结构中,使得搜索过程能同时考虑向量相似度和元数据条件。常见技术包括:
    • 带过滤的 IVF(Inverted File Index):在 IVF 的每个聚类(Voronoi cell)中,除了存储向量,还存储该向量所属的元数据标签。搜索时只扫描满足元数据条件的向量所在的聚类。
    • HNSW + 元数据位图:在 HNSW 图的节点上附加一个位图,标识节点是否满足某些元数据条件,搜索时动态跳过不满足条件的节点。
    • 倒排索引 + 向量索引:为每个元数据值建立倒排列表,搜索时先根据元数据条件合并倒排列表得到候选 ID 集,再在这些 ID 上执行向量搜索(可视为预过滤的优化版,但元数据索引与向量索引协同设计)。
    • 优点:搜索过程同时受元数据和向量相似度约束,既能保证结果满足条件,又能利用索引加速。
    • 缺点:实现复杂,不同数据库支持程度不同,索引构建和维护成本略高。
    • 适用场景:对准确性和性能都有较高要求,且元数据过滤是高频操作。

三、主流向量数据库对比

3.1 产品分类

类型 代表产品 特点
专用向量数据库 Pinecone、Milvus、Qdrant、Weaviate 专为向量设计,功能丰富
传统数据库扩展 pgvector(PostgreSQL)、Elasticsearch 集成现有生态
云服务 Azure AI Search、Vertex AI Vector Search 托管服务,开箱即用
轻量级/本地 Chroma、FAISS、LanceDB 开发友好,适合原型

3.2 详细对比表

特性 Chroma FAISS Milvus Pinecone pgvector Qdrant
类型 嵌入式 分布式 托管 扩展 专用
部署方式 本地 本地 自托管/云 本地 自托管/云
开发友好度 ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐
生产就绪 ⭐⭐ ⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐
水平扩展
混合搜索 ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐
内置UI ✅(Attu) ✅(控制台) ✅(Web UI)
开源
流行度 极高 极高

3.3 选择指南

场景 推荐选择 理由
快速原型、本地开发 Chroma 简单易用,无依赖,代码量少
算法研究、高性能计算 FAISS 最强大的向量索引库,灵活可定制
生产环境、大规模数据 Milvus / Qdrant 分布式、高可用、云原生
不想自建、快速上线 Pinecone 全托管,运维零成本
已有PostgreSQL pgvector 复用现有数据库,无需新组件
需要全文搜索+向量 Elasticsearch 强大的文本搜索+向量能力

四、LangChain中的向量数据库使用

4.1 统一接口

LangChain为所有向量数据库提供了统一的接口:

python 复制代码
from langchain_community.vectorstores import Chroma, FAISS, Milvus, Pinecone

# 所有向量存储都支持以下核心方法
vectorstore = SomeVectorStore.from_documents(
    documents=docs,           # 文档列表
    embedding=embeddings       # 嵌入模型
)

# 相似度搜索
docs = vectorstore.similarity_search(query, k=5)

# 带分数的相似度搜索
docs_with_scores = vectorstore.similarity_search_with_score(query, k=5)

# 根据向量搜索
docs = vectorstore.similarity_search_by_vector(embedding_vector, k=5)

# 添加文档
vectorstore.add_documents(new_docs)

# 删除文档
vectorstore.delete(ids)

# 作为检索器使用
retriever = vectorstore.as_retriever()

4.2 Chroma(本地开发首选)

Chroma是一个轻量级、嵌入式向量数据库,最适合本地开发和原型验证。

python 复制代码
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings

# 初始化
embeddings = OpenAIEmbeddings()

# 从文档创建
vectorstore = Chroma.from_documents(
    documents=docs,
    embedding=embeddings,
    persist_directory="./chroma_db",  # 持久化目录
    collection_name="my_collection"
)

# 或从现有持久化目录加载
vectorstore = Chroma(
    persist_directory="./chroma_db",
    embedding_function=embeddings
)

# 添加文档
vectorstore.add_documents(new_docs)

# 检索
results = vectorstore.similarity_search_with_score("查询", k=5)
for doc, score in results:
    print(f"相似度: {score:.4f}, 内容: {doc.page_content[:100]}...")

# 持久化
vectorstore.persist()

# 带元数据过滤
results = vectorstore.similarity_search(
    "查询",
    k=5,
    filter={"source": "handbook.pdf"}  # 只从特定来源检索
)

Chroma配置参数:

python 复制代码
# 创建集合时配置索引
vectorstore = Chroma.from_documents(
    documents=docs,
    embedding=embeddings,
    collection_metadata={
        "hnsw:space": "cosine",      # 相似度度量:cosine, l2, ip
        "hnsw:construction_ef": 200,  # 构建参数
        "hnsw:M": 16,                 # 图连接数
        "hnsw:search_ef": 64          # 搜索参数
    }
)

4.3 FAISS(高性能本地库)

FAISS(Facebook AI Similarity Search)是一个强大的向量索引库,适合需要精细控制索引的场景。

python 复制代码
from langchain_community.vectorstores import FAISS
import faiss

# 基本用法
vectorstore = FAISS.from_documents(docs, embeddings)

# 保存和加载
vectorstore.save_local("faiss_index")
vectorstore = FAISS.load_local("faiss_index", embeddings)

# 使用不同索引类型
index = faiss.IndexHNSWFlat(768, 32)  # 维度768,M=32
vectorstore = FAISS(
    embedding_function=embeddings,
    index=index,
    docstore=docstore,
    index_to_docstore_id=index_to_docstore_id
)

# 添加文档
vectorstore.add_documents(more_docs)

# 合并多个FAISS索引
another_store = FAISS.from_documents(other_docs, embeddings)
vectorstore.merge_from(another_store)

# 检索
docs = vectorstore.similarity_search_with_relevance_scores(
    "查询",
    k=5,
    score_threshold=0.7  # 只返回相似度>0.7的
)

FAISS索引类型选择:

python 复制代码
import faiss

dimension = 768

# 精确搜索(暴力计算)
index = faiss.IndexFlatL2(dimension)  # L2距离
# 或
index = faiss.IndexFlatIP(dimension)  # 内积(归一化后等于余弦)

# HNSW(推荐)
index = faiss.IndexHNSWFlat(dimension, 32)  # M=32

# IVF(内存友好)
nlist = 100  # 聚类中心数
quantizer = faiss.IndexFlatL2(dimension)
index = faiss.IndexIVFFlat(quantizer, dimension, nlist)
index.train(vectors)  # 需要训练

# PQ(极低内存)
m = 16  # 子量化器数
nbits = 8  # 每个子量化器的比特数
index = faiss.IndexPQ(dimension, m, nbits)
index.train(vectors)  # 需要训练

4.4 Milvus(生产级分布式)

Milvus是专为生产环境设计的分布式向量数据库,支持云原生部署。

bash 复制代码
from langchain_community.vectorstores import Milvus
from pymilvus import connections, utility

# 连接Milvus服务
connections.connect(
    alias="default",
    host="localhost",
    port="19530"
)

# 从文档创建
vectorstore = Milvus.from_documents(
    documents=docs,
    embedding=embeddings,
    collection_name="my_rag_collection",
    connection_args={
        "host": "localhost",
        "port": "19530"
    },
    index_params={
        "metric_type": "COSINE",      # 相似度度量
        "index_type": "HNSW",         # 索引类型
        "params": {"M": 16, "efConstruction": 200}
    },
    search_params={
        "params": {"ef": 64}
    }
)

# 检索
results = vectorstore.similarity_search_with_score(
    "查询",
    k=5,
    param={"search_params": {"ef": 100}}  # 动态调整搜索精度
)

# 带复杂过滤
results = vectorstore.similarity_search(
    "查询",
    k=5,
    expr="source == 'handbook.pdf' and year > 2020"  # 表达式过滤
)

# 删除集合
utility.drop_collection("my_rag_collection")

4.5 Pinecone(全托管云服务)

Pinecone是完全托管的向量数据库,无需运维,适合快速上线。

python 复制代码
from langchain_pinecone import PineconeVectorStore
import pinecone

# 初始化Pinecone
pinecone.init(
    api_key="your-api-key",
    environment="your-environment"
)

# 创建索引(如果不存在)
index_name = "my-rag-index"
if index_name not in pinecone.list_indexes():
    pinecone.create_index(
        name=index_name,
        dimension=1536,  # 向量维度
        metric="cosine",  # 相似度度量
        pods=1,           # Pod数量
        pod_type="p1.x1"  # Pod类型
    )

# 创建向量存储
vectorstore = PineconeVectorStore.from_documents(
    documents=docs,
    embedding=embeddings,
    index_name=index_name
)

# 检索
docs = vectorstore.similarity_search("查询", k=5)

# 带元数据过滤
docs = vectorstore.similarity_search(
    "查询",
    k=5,
    filter={"source": {"$eq": "handbook.pdf"}}
)

# 更新文档
vectorstore.add_documents(new_docs)

# 删除
vectorstore.delete(ids)

# 清理
pinecone.delete_index(index_name)

4.6 pgvector(PostgreSQL扩展)

如果已经在使用PostgreSQL,pgvector可以让你在现有数据库中增加向量能力。

python 复制代码
from langchain_community.vectorstores import PGVector

# 数据库连接URL
CONNECTION_STRING = "postgresql+psycopg2://user:pass@localhost:5432/dbname"

# 创建向量存储
vectorstore = PGVector.from_documents(
    documents=docs,
    embedding=embeddings,
    connection_string=CONNECTION_STRING,
    collection_name="documents",
    distance_strategy="cosine"  # 或 "l2"
)

# 检索
docs = vectorstore.similarity_search("查询", k=5)

# 使用SQL直接查询
from sqlalchemy import text

with vectorstore._connect() as conn:
    result = conn.execute(
        text("""
        SELECT content, 1 - (embedding <=> :query_embedding) as similarity
        FROM documents
        ORDER BY similarity DESC
        LIMIT 5
        """),
        {"query_embedding": query_vector}
    )

五、高级功能与优化

5.1 HNSW索引参数

python 复制代码
collection = client.create_collection(
    name="optimized_collection",
    metadata={
        "hnsw:space": "cosine",
        "hnsw:construction_ef": 200,  # 构建时的搜索范围(越大索引质量越高,但构建越慢)
        "hnsw:M": 32,  # 每个节点的最大连接数(越大召回率越高,但内存占用越大)
        "hnsw:search_ef": 100,  # 查询时的搜索范围(越大召回率越高,但查询越慢)
        "hnsw:num_threads": 4  # 构建时使用的线程数
    }
)

参数说明:

  • M:16-64之间,默认16。越大召回率越高,但内存占用越大
  • construction_ef:100-500之间,默认100。越大索引质量越高
  • search_ef:100-500之间,默认100。查询时可根据需要临时调整

5.2 IVF索引参数

python 复制代码
index_params = {
    "metric_type": "COSINE",
    "index_type": "IVF_FLAT",
    "params": {
        "nlist": 4096,  # 聚类中心数量(越大召回率越高,但查询越慢)
        "nprobe": 16    # 查询时搜索的聚类中心数量(越大召回率越高)
    }
}

5.3 PQ索引参数

python 复制代码
index_params = {
    "m": 8,            # 子空间数,8-16
    "nbits": 8         # 每个子空间的比特数,通常8
}

5.4 混合搜索(向量 + 关键词)

许多向量数据库支持混合搜索,结合向量相似度和关键词匹配。

python 复制代码
# Qdrant 示例
from langchain_qdrant import QdrantVectorStore
from qdrant_client import QdrantClient
from qdrant_client.http.models import Filter, FieldCondition, MatchValue

vectorstore = QdrantVectorStore.from_documents(
    docs,
    embeddings,
    url="http://localhost:6333",
    collection_name="my_docs"
)

# 带关键词过滤
filter = Filter(
    must=[
        FieldCondition(
            key="source",
            match=MatchValue(value="handbook.pdf")
        )
    ]
)

docs = vectorstore.similarity_search(
    "查询",
    k=5,
    filter=filter
)

5.5 多向量检索

为每个文档存储多个向量(如摘要、关键句、全文),提高检索质量。

python 复制代码
from langchain.retrievers.multi_vector import MultiVectorRetriever
from langchain.storage import InMemoryStore
from langchain_community.vectorstores import Chroma

# 创建多向量检索器
vectorstore = Chroma(collection_name="embeddings", embedding_function=embeddings)
store = InMemoryStore()

retriever = MultiVectorRetriever(
    vectorstore=vectorstore,
    docstore=store,
    id_key="doc_id"
)

# 为每个文档生成多个向量
doc_ids = ["doc1", "doc2"]
summaries = ["文档1摘要", "文档2摘要"]
key_sentences = ["关键句1", "关键句2"]

summary_vecs = embeddings.embed_documents(summaries)
sentence_vecs = embeddings.embed_documents(key_sentences)

# 添加多个向量
retriever.vectorstore.add_vectors(doc_ids, summary_vecs)
retriever.vectorstore.add_vectors(doc_ids, sentence_vecs)

# 存储原始文档
retriever.docstore.mset(list(zip(doc_ids, docs)))

5.6 父子文档检索

将文档切分成小块用于检索,但返回时返回包含上下文的父文档。

python 复制代码
from langchain.retrievers import ParentDocumentRetriever
from langchain.storage import InMemoryStore

# 创建父子文档检索器
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=2000)
child_splitter = RecursiveCharacterTextSplitter(chunk_size=400)

vectorstore = Chroma(collection_name="child_docs", embedding_function=embeddings)
store = InMemoryStore()

retriever = ParentDocumentRetriever(
    vectorstore=vectorstore,
    docstore=store,
    child_splitter=child_splitter,
    parent_splitter=parent_splitter
)

# 添加文档
retriever.add_documents(docs)

# 检索(返回父文档)
parent_docs = retriever.get_relevant_documents("查询")

六、完整实战:构建生产级RAG向量存储

python 复制代码
import os
from langchain_community.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Milvus
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from pymilvus import connections, utility

# 1. 准备数据
loader = TextLoader("knowledge_base.txt")
documents = loader.load()

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50,
    separators=["\n\n", "\n", "。", "!", "?", ";", ",", " ", ""]
)
chunks = text_splitter.split_documents(documents)

print(f"加载了 {len(chunks)} 个文档块")

# 2. 初始化嵌入模型
embeddings = OpenAIEmbeddings(
    model="text-embedding-3-small",
    dimensions=512  # 降维节省存储
)

# 3. 连接Milvus
connections.connect(
    alias="default",
    host=os.getenv("MILVUS_HOST", "localhost"),
    port=os.getenv("MILVUS_PORT", "19530")
)

collection_name = "production_rag"

# 如果集合已存在,删除重建(仅示例)
if utility.has_collection(collection_name):
    utility.drop_collection(collection_name)

# 4. 创建向量存储(配置优化)
vectorstore = Milvus.from_documents(
    documents=chunks,
    embedding=embeddings,
    collection_name=collection_name,
    index_params={
        "metric_type": "COSINE",
        "index_type": "HNSW",
        "params": {
            "M": 16,
            "efConstruction": 200
        }
    },
    search_params={
        "params": {
            "ef": 64
        }
    },
    consistency_level="Eventually"  # 一致性级别
)

print(f"向量存储创建完成,集合名: {collection_name}")

# 5. 创建检索器(带混合搜索能力)
retriever = vectorstore.as_retriever(
    search_type="similarity",
    search_kwargs={
        "k": 5,
        "param": {"search_params": {"ef": 100}}  # 动态提高搜索精度
    }
)

# 6. 测试检索
test_queries = [
    "公司年假政策",
    "如何申请报销",
    "医疗保险覆盖范围"
]

for query in test_queries:
    docs_with_scores = vectorstore.similarity_search_with_score(query, k=3)
    print(f"\n查询: {query}")
    for doc, score in docs_with_scores:
        print(f"  相似度: {score:.4f} - {doc.page_content[:50]}...")

# 7. 构建RAG链
prompt = ChatPromptTemplate.from_template("""
基于以下上下文回答问题。如果无法从上下文中找到答案,请说"我不知道"。

上下文:
{context}

问题:{input}
回答:""")

model = ChatOpenAI(model="gpt-4o")
document_chain = create_stuff_documents_chain(model, prompt)
rag_chain = create_retrieval_chain(retriever, document_chain)

# 8. 问答
response = rag_chain.invoke({"input": "年假有多少天?"})
print(f"\n答案: {response['answer']}")

# 9. 性能统计
import time

latencies = []
for query in test_queries:
    start = time.time()
    docs = retriever.get_relevant_documents(query)
    end = time.time()
    latencies.append((end - start) * 1000)

print(f"\n检索性能统计:")
print(f"  平均延迟: {np.mean(latencies):.2f} ms")
print(f"  最大延迟: {np.max(latencies):.2f} ms")
print(f"  最小延迟: {np.min(latencies):.2f} ms")

七、常见问题与解决方案

Q1: 向量不归一化,检索乱掉

解决方案:

  • 必须开归一化。
python 复制代码
encode_kwargs={"normalize_embeddings": True}

Q2: FAISS 不能分布式,不能多线程并发写

解决方案:

  • 只适合开发,不适合高并发生产

Q3: 向量维度不匹配

解决方案:

  • 确保使用相同的模型
python 复制代码
assert len(vector) == collection_metadata["vector_dim"]

Q4: 不建索引,千万数据秒崩

解决方案:

  • 必须建索引:HNSW / IVF

Q5: 元数据丢失,无法溯源

解决方案:

  • 分块时一定要带:source、page、file_name、timestamp

Q6: 长文本直接嵌入

解决方案:

  • 超过模型最大长度,必须先分块

Q7: 用 L2 距离做文本检索,效果极差

解决方案:

  • RAG 只推荐:COSINE / IP
相关推荐
昵称只能一个月修改一次。。。2 小时前
SQLite
数据库
身如柳絮随风扬2 小时前
MySQL为什么使用B+树?
数据库·b树·mysql
小超同学你好2 小时前
LangGraph 9. Agent 背后:ReAct
人工智能·语言模型·langchain
怕浪猫2 小时前
第1章 初识 LangChain:大模型时代的开发脚手架
langchain·llm·ai编程
短剑重铸之日2 小时前
《ShardingSphere解读》04 配置驱动:ShardingSphere 中的配置体系是如何设计的?
java·数据库·后端·spring·shardingsphere
java1234_小锋2 小时前
基于LangChain的RAG与Agent智能体开发 - 阿里云百炼大模型平台接入
langchain·rag
胡斌附体2 小时前
MySQL 在 Docker 环境下连接变慢问题记录
数据库·mysql·docker·连接··本机·外部机器连接
·云扬·2 小时前
【MySQL实操】停服务方式新增从库:从架构到落地全指南
数据库·mysql·架构
真智AI2 小时前
FastAPI+SQLite任务API:从零到可测上线
数据库·sqlite·fastapi