跳至主要内容

Chroma DB 教程:一步一步的指南

借助 Chroma DB,您可以轻松管理文本文档、将文本转换为嵌入,并进行相似度搜索。
· 10分钟 阅读

使用 AI 探索

随着大型语言模型(LLM)及其应用的兴起,集成工具、LLMOps 框架和向量数据库的热度也随之上升。这是因为使用 LLM 的工作方式与传统机器学习模型不同,需要采用另一套方法。

支持 LLM 的核心技术之一是向量嵌入。计算机无法直接理解文本,但嵌入可以将文本表示为数值。所有用户提供的文本都会被转换为嵌入,然后用于生成回复。

将文本转换为嵌入是一个耗时的过程。为避免重复计算,我们使用专门为高效存储与检索向量嵌入而设计的向量数据库。

在本教程中,我将带您了解向量存储与 Chroma DB——一个用于存储和管理嵌入的开源数据库。您将学习如何添加和删除文档、执行相似度搜索,以及将文本转换为嵌入。

作者供图

要点速览

  • ChromaDB 是一个开源向量数据库,用于存储和检索嵌入——使用 pip install chromadb 安装
  • 本地持久化使用 chromadb.PersistentClient(path="./chroma_db"),内存测试使用 chromadb.EphemeralClient()
  • 先创建集合(collection)(类似表),再用 add() 添加文档,可选附带元数据和 ID
  • 使用 collection.query(query_texts=["..."], n_results=5) 进行语义相似度搜索
  • 可接入任意嵌入模型——OpenAI、HuggingFace、Google Gemini,或自定义函数
  • ChromaDB 广泛用作 RAG(检索增强生成) 流水线中的向量存储

什么是向量存储?

向量存储是为高效存储与检索向量嵌入而专门设计的数据库。它们之所以必要,是因为像 SQL 这样的传统数据库并未针对大规模向量数据的存储和查询进行优化。

嵌入会在高维空间中以数值向量的形式表示数据(通常是文本等非结构化数据)。传统关系型数据库并不适合存储和搜索这些向量表示。

向量存储可以对向量进行索引,并使用相似度算法快速搜索相似向量,使应用能够在给定目标向量查询时找到相关向量。

例如,在个性化聊天机器人的场景中,用户向生成式 AI 模型输入提示。模型使用相似度搜索算法,在文档集合中查找相似文本。得到的信息将用于生成高度个性化且准确的回复。通过向量存储中的嵌入与向量索引,这种信息检索成为可能。

什么是 Chroma DB?

Chroma DB 是一个开源向量存储,用于存储和检索向量嵌入。其主要用途是保存嵌入及其元数据,供大型语言模型后续使用。此外,它也可用于文本数据上的语义搜索引擎。

Chroma DB 的特性

  • 简洁而强大:
    • 一条命令安装:pip install chromadb。
    • Python SDK 快速上手,便于无缝集成与快速搭建。
  • 功能完备:
    • 全面的检索功能:支持向量搜索、全文搜索、文档存储、元数据过滤和多模态检索。
    • 高可扩展性:本地持久化使用 SQLite,并支持客户端-服务器模式,便于多客户端与生产环境部署。
  • 多语言支持:
    • 提供多种主流语言 SDK,包括 Python、JavaScript/TypeScript、Ruby、PHP 和 Java。
  • 集成完善:
    • 与 HuggingFace、OpenAI、Google 等的嵌入模型原生集成。
    • 兼容 Langchain 与 LlamaIndex,更多工具集成即将推出。
  • 开源:
    • 采用 Apache 2.0 许可。
  • 速度与易用:
    • 专注简洁与高速,旨在在保证易用的同时,让分析与检索更高效。

Chroma DB 提供自托管服务器选项。如果您需要托管式或云原生的向量数据库,可参考我们的指南:Pinecone 向量数据库实战或 Weaviate 作为替代方案。

Image from  Chroma

图片来源:Chroma

Chroma DB 如何工作?

  1. 首先,创建一个集合(类似关系型数据库中的表)。默认情况下,Chroma 使用 all-MiniLM-L6-v2 将文本转换为嵌入,但您可以修改集合以使用其他嵌入模型。
  2. 将带有元数据和唯一 ID 的文本文档添加到新建集合中。当集合接收到文本时,会自动将其转换为嵌入。
  3. 通过文本或嵌入来查询集合,以获取相似文档。您也可以基于元数据过滤结果。

先决条件

要跟随本教程,您需要:

  • Python 3.8+(为获得最佳 ChromaDB 兼容性,推荐使用 Python 3.11)
  • pip 包管理器
  • OpenAI API 密钥(仅嵌入部分需要;ChromaDB 的核心部分无需密钥即可运行)
  • SQLite 3.35 或更高版本(内置于 Python 3.11;如果您使用更旧版本且遇到问题,请使用 pip install pysqlite3-binary)
  • 具备 Python 列表和字典的基础知识

开始使用 Chroma DB

本节中,我将创建一个向量数据库,添加集合,向其中加载文本,并运行一次相似度搜索查询。

首先安装 chromadb 和 openai。只有在“嵌入”一节您才需要OpenAI API 密钥——以下 ChromaDB 的核心示例不需要。

注意:Chroma 需要 SQLite 3.35 或更高版本。如果出现问题,请升级到 Python 3.11,或安装较旧版本的 chromadb。

!pip install chromadb openai 

选择客户端模式

ChromaDB 根据您的使用场景提供三种客户端模式:

ClientUse caseCode
EphemeralClient内存测试;退出即丢失数据chromadb.EphemeralClient()
PersistentClient本地文件存储;重启后数据仍在chromadb.PersistentClient(path="./chroma_db")
HttpClient生产环境;连接到正在运行的 ChromaDB 服务器chromadb.HttpClient(host="localhost", port=8000)

您可以使用 chromadb.EphemeralClient() 创建一个基于内存(临时)的测试数据库。它只在内存中存储数据,程序结束即重置——非常适合快速试验。

在本例中,我将创建一个持久化数据库,存储在 ./chroma_db 目录下。ChromaDB 在持久化模式下使用 SQLite 作为后端存储——DuckDB 后端已在 ChromaDB 0.4.0 中移除。

import chromadb

client = chromadb.PersistentClient(path="./chroma_db")

然后,我们将使用客户端创建一个 collection 对象。这类似于在传统数据库中创建表。

collection = client.create_collection(name="Students")

要向集合中添加文本,我们将生成关于学生、社团和大学的随机文本。您可以使用 ChatGPT 生成随机文本。

student_info = """
Alexandra Thompson, a 19-year-old computer science sophomore with a 3.7 GPA,
is a member of the programming and chess clubs who enjoys pizza, swimming, and hiking
in her free time in hopes of working at a tech company after graduating from the University of Washington.
"""

club_info = """
The university chess club provides an outlet for students to come together and enjoy playing
the classic strategy game of chess. Members of all skill levels are welcome, from beginners learning
the rules to experienced tournament players. The club typically meets a few times per week to play casual games,
participate in tournaments, analyze famous chess matches, and improve members' skills.
"""

university_info = """
The University of Washington, founded in 1861 in Seattle, is a public research university
with over 45,000 students across three campuses in Seattle, Tacoma, and Bothell.
As the flagship institution of the six public universities in Washington state,
UW encompasses over 500 buildings and 20 million square feet of space,
including one of the largest library systems in the world.
"""

现在,我们将使用 add() 函数添加带有元数据和唯一 ID 的文本数据。随后,Chroma 会自动下载 all-MiniLM-L6-v2 模型,将文本转换为嵌入并存储到 "Students" 集合中。

collection.add(
    documents = [student_info, club_info, university_info],
    metadatas = [{"source": "student info"},{"source": "club info"},{'source':'university info'}],
    ids = ["id1", "id2", "id3"]
)

要运行相似度搜索,您可以使用 query() 函数并用自然语言提问。它会将查询转换为嵌入,并使用相似度算法生成相似结果。在我们的示例中,它返回了两个相似结果。

results = collection.query(
    query_texts=["What is the student name?"],
    n_results=2
)

results

嵌入(Embeddings)

您可以使用嵌入模型列表中的任意高性能嵌入模型。您甚至可以创建自定义嵌入函数。若想深入了解 OpenAI 的现代引擎,请参阅我们的 text-embedding-3-large 指南。

本节中,我将使用 OpenAI 的 text-embedding-3-small 模型将文本转换为嵌入。这是 OpenAI 推荐用于替代旧版 text-embedding-ada-002 的模型——在基准上表现更好,成本降低 5 倍。

创建 OpenAI 嵌入函数后,您可以添加文本列表以生成嵌入。

了解如何使用 OpenAI 文本嵌入 API 创建文本分类器、信息检索系统和语义相似度检测器。

from chromadb.utils import embedding_functions

openai_ef = embedding_functions.OpenAIEmbeddingFunction(
    api_key="YOUR_OPENAI_API_KEY",
    model_name="text-embedding-3-small"
)
students_embeddings = openai_ef([student_info, club_info, university_info])
print(students_embeddings)
[[-0.01015068031847477, 0.0070903063751757145, 0.010579396970570087, -0.04118313640356064, 0.0011583581799641252, 0.026857420802116394,....],]

与其使用默认嵌入模型,我将把已生成的嵌入直接加载到一个新集合中。

  1. 我们将使用 get_or_create_collection() 函数创建一个名为 "Students2" 的新集合。该函数不同于 create_collection(),如果集合已存在则获取,否则创建。
  2. 现在我们将把嵌入、文本文档、元数据和 ID 添加到新集合中。
collection2 = client.get_or_create_collection(name="Students2")

collection2.add(
    embeddings = students_embeddings,
    documents = [student_info, club_info, university_info],
    metadatas = [{"source": "student info"},{"source": "club info"},{'source':'university info'}],
    ids = ["id1", "id2", "id3"]
)

还有一种更直接的方法。您可以在创建或访问集合时添加 OpenAI 嵌入函数。除了 OpenAI,您还可以使用 Cohere、Google Gemini、HuggingFace 和 Instructor 模型。

在我们的场景中,添加新文本文档时会调用 OpenAI 嵌入函数,而非默认模型,将文本转换为嵌入。

collection2 = client.get_or_create_collection(name="Students2",embedding_function=openai_ef)

collection2.add(
    documents = [student_info, club_info, university_info],
    metadatas = [{"source": "student info"},{"source": "club info"},{'source':'university info'}],
    ids = ["id1", "id2", "id3"]
)

让我们在新集合上运行相似查询,看看差异。

results = collection2.query(
    query_texts=["What is the student name?"],
    n_results=2
)

results

我们的结果有所改善。现在相似度搜索返回的是关于大学的信息,而不是社团。此外,向量之间的距离比默认嵌入模型更低,这是好事。

image2.png

过滤查询结果

ChromaDB 支持通过元数据过滤来缩小相似度搜索结果范围。在 query() 中使用带操作符的 where 参数:

results = collection.query(
    query_texts=["What is the student name?"],
    n_results=2,
    where={"source": "student info"}  # only return documents with this metadata
)

# Combine multiple filters with $and / $or
results = collection.query(
    query_texts=["university"],
    n_results=5,
    where={
        "$or": [
            {"source": "student info"},
            {"source": "university info"}
        ]
    }
)

支持的操作符:$eq、$ne、$gt、$gte、$lt、$lte、$in、$nin、$and、$or。您也可以使用 where_document={"$contains": "chess"} 对文档内容进行过滤。

更新与删除数据

与关系型数据库类似,您可以更新或删除集合中的值。要更新文本与元数据,我们将提供记录的特定 ID 和新文本。

collection2.update(
    ids=["id1"],
    documents=["Kristiane Carina, a 19-year-old computer science sophomore with a 3.7 GPA"],
    metadatas=[{"source": "student info"}],
)

运行一个简单查询,检查更改是否已生效。

results = collection2.query(
    query_texts=["What is the student name?"],
    n_results=2
)

results

可以看到,我们得到的是 Kristiane,而不是 Alexandra。

image3.png

要从集合中删除记录,我们将使用 delete() 并指定唯一 ID。

collection2.delete(ids = ['id1'])


results = collection2.query(
    query_texts=["What is the student name?"],
    n_results=2
)

results

学生信息文本已被删除;取而代之的是返回下一个最优结果。

image7.png

集合管理

本节我将介绍集合工具函数,用于统计、列出、重命名与删除集合。

我们将创建一个名为 "vectordb" 的新集合,并添加关于 Chroma DB 速查表、文档和 JS API 的信息及其元数据。

vector_collections = client.create_collection("vectordb")


vector_collections.add(
    documents=["This is Chroma DB CheatSheet",
               "This is Chroma DB Documentation",
               "This document Chroma JS API Docs"],
    metadatas=[{"source": "Chroma Cheatsheet"},
    {"source": "Chroma Doc"},
    {'source':'JS API Doc'}],
    ids=["id1", "id2", "id3"]
)

我们将使用 count() 函数查看集合有多少条记录。

vector_collections.count()
3

要查看集合中的所有记录,使用 get() 函数。

vector_collections.get()
{'ids': ['id1', 'id2', 'id3'],
 'embeddings': None,
 'documents': ['This is Chroma DB CheatSheet',
  'This is Chroma DB Documentation',
  'This document Chroma JS API Docs'],
 'metadatas': [{'source': 'Chroma Cheatsheet'},
  {'source': 'Chroma Doc'},
  {'source': 'JS API Doc'}]}

要更改集合名称,使用 modify()。要查看所有集合名称,使用 list_collections()。

vector_collections.modify(name="chroma_info")

# list all collections
client.list_collections()

看起来我们已将 "vectordb" 成功重命名为 "chroma_info"。

[Collection(name=Students),
 Collection(name=Students2),
 Collection(name=chroma_info)]

要访问任意新集合,您可以使用集合名称调用 get_collection()。

vector_collections_new = client.get_collection(name="chroma_info")

我们可以使用客户端的 delete_collection() 并指定集合名称来删除集合。

client.delete_collection(name="chroma_info")
client.list_collections()
[Collection(name=Students), Collection(name=Students2)]

我们可以使用 client.reset() 删除整个数据库中的集合。但并不推荐这样做,因为删除后无法恢复数据。

client.reset()
client.list_collections()
[]

将 ChromaDB 与 LangChain 结合:一个 RAG 示例

ChromaDB 最常见的集成之一是与 LangChain 一起构建 RAG 应用。以下是一个将 ChromaDB 用作向量存储的最小示例:

from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain_core.documents import Document

# Initialize embedding model and vector store
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vector_store = Chroma(
    collection_name="rag_docs",
    embedding_function=embeddings,
    persist_directory="./chroma_db"
)

# Add documents
docs = [
    Document(page_content="ChromaDB stores vector embeddings", metadata={"source": "doc1"}),
    Document(page_content="LangChain simplifies LLM application development", metadata={"source": "doc2"}),
]
vector_store.add_documents(docs)

# Similarity search
results = vector_store.similarity_search("vector database", k=2)
for doc in results:
    print(doc.page_content)

如需一套可用于生产的 FastAPI RAG 流水线,请参阅我们的教程:使用 LangChain 和 FastAPI 构建 RAG 系统。基于 DeepSeek 的示例请参阅:使用 Chroma 的 DeepSeek R1 RAG 聊天机器人。

结语

像 Chroma DB 这样的向量存储正在成为大型语言模型系统的关键组件。通过提供针对向量嵌入的专用存储和高效检索,它们为 LLM 提供快速获取相关语义信息的能力。

在本篇 Chroma DB 教程中,我们介绍了创建集合、添加文档、将文本转换为嵌入、进行语义相似查询以及管理集合的基本用法。

下一步的自然方向是构建以 ChromaDB 为向量存储的检索增强生成(RAG)应用。您可以从我们的 使用 LangChain 和 FastAPI 构建 RAG 系统 教程开始,获得一套可用于生产的流水线;或探索更高级的代理驱动检索流程 Agentic RAG。您还可以使用 LlamaIndex 将私有数据引入 LLM,或参考 LangChain LLM 教程进行完整应用开发。

FAQs

Chroma DB 是否能处理非文本数据(如图像或音频)的嵌入存储与检索?

可以。ChromaDB 不仅能存储文本的嵌入,还能存储任意数据类型的嵌入。如果您使用多模态模型从图像、音频或其他非结构化数据生成嵌入,您可以像处理文本嵌入一样进行存储与查询。将预先计算好的嵌入向量通过 collection.add() 的 embeddings 参数直接传入即可。无论嵌入来源如何,检索与过滤逻辑完全一致。

是否可以仅更新文档的元数据,而不修改其嵌入?

可以。调用 collection.update() 时,您可以只传入 ids 和 metadatas 参数,而省略 documents 与 embeddings。ChromaDB 将就地更新元数据,而不会重新生成嵌入。您也可以使用 collection.upsert(),它会在文档存在时更新,不存在时插入。

在应对大规模部署时,Chroma DB 与 Pinecone 有何差异?

ChromaDB 是开源且自托管的,您可以完全掌控数据与基础设施。对于大规模本地或云端部署,您可以以服务器模式(HttpClient)运行 ChromaDB,并独立扩展。相比之下,Pinecone 是完全托管的云服务,具备自动扩展和无服务器层,操作更简单,但会带来供应商锁定和按用量计费的成本。

如果您希望掌控并规避 SaaS 成本,ChromaDB 是不错的选择;如果您更看重零运维,Pinecone 或许更合适。参阅我们的 Pinecone 教程与 Weaviate 教程进行对比。

我可以在 Chroma DB 中使用由其他模型生成的自定义嵌入吗?

当然可以。ChromaDB 允许您通过 collection.add() 的 embeddings 参数直接传入预计算的嵌入。您也可以在创建集合时通过 embedding_function=your_function 传入自定义嵌入函数——之后在添加或查询文档时,ChromaDB 会自动调用该函数。

内置嵌入函数包括 OpenAI(text-embedding-3-small、text-embedding-3-large)、HuggingFace Sentence Transformers、Cohere 和 Google Gemini 等。

如果误删了集合会怎样?还能恢复吗?

遗憾的是,在 ChromaDB 中删除集合是不可恢复的。delete_collection() 与 reset() 会立即移除数据,且没有内置撤销功能。对于 PersistentClient,底层的 SQLite 文件也会从磁盘删除。

最佳实践:在执行破坏性操作前备份您的 ChromaDB 数据目录(./chroma_db/)。您也可以在删除前将集合数据导出为 JSON 或 DataFrame,或先在 EphemeralClient() 上测试破坏性操作。

ChromaDB 的 EphemeralClient、PersistentClient 与 HttpClient 有何区别?

ChromaDB 提供三种客户端模式:EphemeralClient() 仅在内存中存储数据,适合测试——脚本结束后数据即丢失。PersistentClient(path='./chroma_db') 将数据写入磁盘,重启后仍在,适合本地开发与小规模部署。HttpClient(host='...', port=8000) 连接到独立运行的 ChromaDB 服务器进程,支持多客户端访问与生产部署。

旧的 chromadb.Client(Settings(chroma_db_impl='duckdb+parquet')) 用法已在 ChromaDB 0.4.0 中移除,切勿再使用。

如何在 RAG(检索增强生成)应用中使用 ChromaDB?

在 RAG 流水线中,ChromaDB 充当保存文档嵌入的向量存储。流程为:(1)将文档切分为更小的段落;(2)使用如 text-embedding-3-small 的模型为每个片段生成嵌入;(3)将嵌入连同元数据存入 ChromaDB 集合;(4)在查询时,将用户问题进行嵌入,并调用 collection.query() 检索最语义相近的片段。随后将这些片段作为上下文传给 LLM,以生成有依据的回答。

ChromaDB 与 LangChain(langchain_community.vectorstores.Chroma)和 LlamaIndex 原生集成,便于接入现有 RAG 框架。完整实现请参阅我们的 使用 LangChain 和 FastAPI 构建 RAG 系统 教程。

ChromaDB 的 EphemeralClient、PersistentClient 与 HttpClient 有何区别?

ChromaDB 提供三种客户端模式:EphemeralClient() 仅在内存中存储数据,适合测试——脚本结束后数据即丢失。PersistentClient(path='./chroma_db') 将数据写入磁盘,重启后仍在,适合本地开发与小规模部署。HttpClient(host='...', port=8000) 连接到独立运行的 ChromaDB 服务器进程,支持多客户端访问与生产部署。

旧的 chromadb.Client(Settings(chroma_db_impl='duckdb+parquet')) 用法已在 ChromaDB 0.4.0 中移除,切勿再使用。

如何在 RAG(检索增强生成)应用中使用 ChromaDB?

在 RAG 流水线中,ChromaDB 充当保存文档嵌入的向量存储。流程为:(1)将文档切分为更小的段落;(2)使用如 text-embedding-3-small 的模型为每个片段生成嵌入;(3)将嵌入连同元数据存入 ChromaDB 集合;(4)在查询时,将用户问题进行嵌入,并调用 collection.query() 检索最语义相近的片段。随后将这些片段作为上下文传给 LLM,以生成有依据的回答。

ChromaDB 与 LangChain(langchain_community.vectorstores.Chroma)和 LlamaIndex 原生集成,便于接入现有 RAG 框架。完整实现请参阅我们的 使用 LangChain 和 FastAPI 构建 RAG 系统 教程。

Abid Ali Awan

作者

Abid Ali Awan

作为一名持证的数据科学家,我热衷于利用前沿技术打造创新的机器学习应用。凭借在语音识别、数据分析与报告、MLOps、对话式人工智能以及自然语言处理方面的扎实背景,我不断打磨构建智能系统的能力,力求带来切实影响。除技术专长外,我也擅长沟通,能够将复杂概念提炼为清晰、简明的表述。因此,我成为数据科学领域备受关注的博主,与不断壮大的数据专业人士社区分享洞见与实践经验。目前,我专注于内容创作与编辑,借助大语言模型打造有力且吸引人的内容,帮助企业与个人更好地发挥数据价值。

主题