向量存储是RAG解决方案的核心,目前市面上由很多向量存储产品,由免费开源的,也有商业闭源的;有本地部署的,也有完全云托管的;有传统数据库产品推出的针对向量存储的扩展,也有新势力专门针对向量存储设计的新产品。基于所有的VectorStore都提供了对应的检索器,后者可以以一个Runnable对象的形式作为LCEL链上的一环。

1. InMemoryVectorStore

InMemoryVectorStore是一个轻量级、完全基于内存的向量存储实现。它主要用于快速原型开发、测试环境或处理不需要持久化且数据量较小的临时检索任务。它的核心特性包括:

  • 非持久化:数据存储在当前运行进程的内存中。一旦程序停止或重启,所有存储的向量和文档都会丢失;
  • 无需配置:不像Pinecone或Milvus需要设置API密钥或启动Docker容器,它是开箱即用的,依赖极少;
  • 速度极快:由于不涉及网络请求和磁盘I/O,对于小规模数据集(如几百到几千个文档)的检索速度非常快;
  • 易于调试:是学习RAG (检索增强生成) 流程的最理想工具;

适合如下的典型场景:

  • 快速原型设计:在决定使用哪种大型向量数据库之前,先验证RAG管道的逻辑;
  • 单元测试:在自动化测试中模拟向量搜索行为,无需搭建复杂的基础设施;
  • 临时上下文检索:例如处理单个长文档或用户上传的临时文件,任务完成后即可释放内存;

InMemoryVectorStore的创建很方便,只需要指定Embeddings调用构造函数就行。InMemoryVectorStore采用余弦相似度算法,而且similarity_search_with_score/asimilarity_search_with_score方法返回的不是两个向量的余弦距离,而是归一化的余弦相似度。由于_select_relevance_score_fn方法并未重写,所以similarity_search_with_relevance_scores/asimilarity_search_with_relevance_scores方法在被调用时会抛出异常。

class InMemoryVectorStore(VectorStore):
    def __init__(self, embedding: Embeddings) -> None:

如下的代码演示了针对InMemoryVectorStore的简单使用:

from dotenv import load_dotenv
from langchain_openai import OpenAIEmbeddings
from langchain_core.vectorstores import InMemoryVectorStore

load_dotenv()

embedding=OpenAIEmbeddings(model="text-embedding-3-small")
texts=[
        "I love eating spicy Sichuan food.",
        "My favorite sport is swimming."]
query = "What are my food preferences?"

store = InMemoryVectorStore.from_texts(
    texts=texts,
    embedding=embedding,
    metadatas=[{"category": "food"}, {"category": "sport"}],
    similarity = "euclidean"
)
assert store.similarity_search(query=query,k=1)[0].page_content == texts[0]

2. 本地轻量级方案(适合中小型项目)

如果您希望数据能持久化到本地文件,而不仅仅存在于内存中,以下是首选:

  • Chroma:

    • 特点:目前最受LangChain开发者欢迎的本地数据库。它开源且对 AI 极其友好,支持多模态数据;
    • 优势:安装简单(pip install chromadb),支持将数据持久化到本地磁盘,且能够方便地切换到分布式云端模式;
  • FAISS (Facebook AI Similarity Search):

    • 特点:由Meta开发的高性能相似度搜索库,严格来说它是一个库而非数据库;
    • 优势:搜索速度极快,特别是在处理数千万级向量时表现卓越。它支持多种索引类型(如HNSW,IVF),并可利用GPU加速;

3. 全托管云端方案(适合生产环境/免运维)

对于不想管理服务器或需要跨地域扩展的团队:

  • Pinecone:

    • 特点:目前市场上最成熟的商业全托管向量数据库。
    • 优势:Serverless 架构,完全不需要配置基础设施。其毫秒级的检索性能和高度的稳定性使其成为企业级 RAG 应用的首选。
  • Zilliz Cloud (Milvus 托管版):

    • 特点:基于开源 Milvus 提供的云服务,针对大规模向量检索做了极致优化。

4. 开源企业级/分布式方案(适合大规模自研)

如果您需要处理十亿级数据或有严格的私有化部署需求:

  • Milvus:

    • 特点:国产开源之光,专为云原生设计的分布式向量数据库。
    • 优势:支持百亿级向量规模,具备极强的水平扩展能力,并提供丰富的索引策略和混合搜索(向量 + 标量过滤)功能。
  • Qdrant:

    • 特点:使用 Rust 编写,以高性能和低延迟著称。
    • 优势:在元数据过滤(Metadata Filtering)方面表现极其出色,适合需要复杂业务逻辑筛选的检索场景。
  • Weaviate:

    • 特点:支持混合搜索(Dense + Sparse)和 GraphQL 查询,能很好地处理结构化与非结构化数据的关联。

5. 传统数据库扩展

如果您现有的架构中已经在使用关系型数据库:

  • pgvector (PostgreSQL): 这是目前开发者最常用的方案,让最强大的开源关系型数据库具备了向量检索能力

    • 特点:通过插件让PostgreSQL具备向量存储能力。支持精确搜索(L2、余弦、点积)和模糊搜索(IVFFlat、HNSW 索引);
    • 优势:无需引入新的组件,可以直接在现有的SQL环境中进行向量检索,极大地降低了系统复杂度;
  • RediSearch(Redis):Redis通过Stack 模块支持向量存储和检索

    • 特点:数据存储在内存中,检索延迟极低;
    • 优势:速度极快,支持 HNSW 算法。适合需要高并发、低延迟的场景(如实时推荐、毫秒级语义匹配);
  • OpenSearch (Elasticsearch):作为传统的全文搜索引擎,它们很早就引入了dense_vector类型

    • 特点:将传统关键词搜索 (BM25) 与向量搜索 (k-NN) 完美融合(即“混合搜索”);
    • 优势:处理大规模文本数据的经验丰富,支持复杂的过滤逻辑和分布式集群扩展;
  • Atlas Vector Search(MongoDB):MongoDB在其云服务Atlas中原生集成了向量搜索

    • 特点:基于 Lucene 分级索引实现;
    • 优势:适合 JSON 文档型数据结构。如果你的数据本身就在 MongoDB 里,不需要为了向量检索再迁移数据;
  • ClickHouse:作为高性能的列式数据库(OLAP),ClickHouse 引入了向量索引支持

    • 特点:极其擅长大规模数据的批量插入和高吞吐查询;
    • 优势:适合在处理海量日志或分析型数据时,顺便进行向量比对;

6. 检索器

被定义成Runnable的检索器使数据检索也成为了LCEL链上的一环。绝大部分VectorStore类型都提供了as_rereiver方法生分成基于自身的检索器对象。

6.1 BaseRetriever

所有的检索器类型都继承自如下这个BaseRetriever抽象类,它继承自RunnableSerializable,其输入和输出类型为RetrieverInputRetrieverOutput,分别表示作为输入的查询文本和作为输出的Document列表。BaseRetriever将具体的检索操作利用抽象方法_get_relevant_documents下放给子类,_aget_relevant_documents默认会以Crorutine的形式调用此方法。invokeainvoke方法最终会调用这两个方法返回检索结果。

class BaseRetriever(RunnableSerializable[RetrieverInput, RetrieverOutput], ABC):
    tags: list[str] | None = None
    metadata: dict[str, Any] | None = None

    @override
    def invoke(
        self, input: str, config: RunnableConfig | None = None, **kwargs: Any
    ) -> list[Document]

    @override
    async def ainvoke(
        self,
        input: str,
        config: RunnableConfig | None = None,
        **kwargs: Any,
    ) -> list[Document]

    @abstractmethod
    def _get_relevant_documents(
        self, query: str, *, run_manager: CallbackManagerForRetrieverRun
    ) -> list[Document]

    async def _aget_relevant_documents(
        self, query: str, *, run_manager: AsyncCallbackManagerForRetrieverRun
    ) -> list[Document]

RetrieverInput = str
RetrieverOutput = list[Document]

_get_relevant_documents/_aget_relevant_documents方法的run_manager返回一个CallbackManagerForRetrieverRun/AsyncCallbackManagerForRetrieverRun, 这两个对象是LangChain框架中专门用于追踪、监控)和调试的“事件调度器”。它们是LangChain可观测性的基石。有了它们,我们才能在LangSmith仪表盘上清晰地看到每一条检索到的Document及其背后的分数和耗时。

6.2 VectorStoreRetriever

VectorStore一般会利用as_retriever方法返回一个VectorStoreRetriever对象。VectorStoreRetriever是对一个VectorStore对象的封装,并针对此向量存储实施检索。

class VectorStoreRetriever(BaseRetriever):
    vectorstore: VectorStore
    search_type: str = "similarity"
    search_kwargs: dict = Field(default_factory=dict)

    allowed_search_types: ClassVar[Collection[str]] = (
        "similarity",
        "similarity_score_threshold",
        "mmr",
    )

    @override
    def _get_relevant_documents(
        self, query: str, *, run_manager: CallbackManagerForRetrieverRun, **kwargs: Any
    ) -> list[Document]:
        kwargs_ = self.search_kwargs | kwargs
        if self.search_type == "similarity":
            docs = self.vectorstore.similarity_search(query, **kwargs_)
        elif self.search_type == "similarity_score_threshold":
            docs_and_similarities = (
                self.vectorstore.similarity_search_with_relevance_scores(
                    query, **kwargs_
                )
            )
            docs = [doc for doc, _ in docs_and_similarities]
        elif self.search_type == "mmr":
            docs = self.vectorstore.max_marginal_relevance_search(query, **kwargs_)
        else:
            msg = f"search_type of {self.search_type} not allowed."
            raise ValueError(msg)
        return docs

    @override
    async def _aget_relevant_documents(
        self,
        query: str,
        *,
        run_manager: AsyncCallbackManagerForRetrieverRun,
        **kwargs: Any,
    ) -> list[Document]

    def add_documents(self, documents: list[Document], **kwargs: Any) -> list[str]:
        return self.vectorstore.add_documents(documents, **kwargs)

    async def aadd_documents(
        self, documents: list[Document], **kwargs: Any
    ) -> list[str]:
        return await self.vectorstore.aadd_documents(documents, **kwargs)

VectorStoreRetriever提供了三个实例字段和一个静态字段:

  • vectorstore:实施检索任务的向量存储;
  • search_type:采用的相似度算法;
  • search_kwargs:为具体查询方法提供的关键字参数;
  • allowed_search_types:可用相似度算法白名单,作为一个Pydantic模型,会验证search_type字段的值在此名单中;

VectorStoreRetriever实现了_get_relevant_documents方法,并根据search_type字段针对相似度算法的设置调用对应的方法返回检索结果的Document列表。除此之外,它还提供了add_documents/aadd_documents方法向存储中添加数据。

在如下的程序中, 我们调用InMemoryVectorStore对象的as_retriever方法,并利用它返回的VectorStoreRetriever对象以Runnable对象的形式实施检索。

from dotenv import load_dotenv
from langchain_openai import OpenAIEmbeddings
from langchain_core.vectorstores import InMemoryVectorStore

load_dotenv()

embedding=OpenAIEmbeddings(model="text-embedding-3-small")
texts=[
        "I love eating spicy Sichuan food.",
        "My favorite sport is swimming."]
query = "What are my food preferences?"

store = InMemoryVectorStore.from_texts(
    texts=texts,
    embedding=embedding,
    metadatas=[{"category": "food"}, {"category": "sport"}],
    similarity = "euclidean"
)

retriever = store.as_retriever()
documents = retriever.invoke(query,k=2)
assert len(documents) == 2
assert documents[0].page_content == texts[0]
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐