Chroma 向量数据库
Chroma 是一个专为 AI 应用设计的开源向量数据库,以简单易用为核心设计理念。它是 Python 生态中最友好的向量数据库,特别适合快速原型开发和小型项目。
概述
为什么选择 Chroma
| 特性 | 说明 |
|---|---|
| 极简 API | 几行代码即可启动,学习成本极低 |
| Python 原生 | 与 Python 生态无缝集成 |
| 本地优先 | 无需外部依赖,本地文件存储 |
| 嵌入式 | 可嵌入到应用程序中运行 |
| 自动嵌入 | 内置嵌入模型支持,无需手动编码 |
| 轻量级 | 资源占用少,适合边缘设备 |
适用场景
- 快速原型:验证想法,快速搭建 Demo
- 本地开发:无需配置服务器
- 小型应用:数据量在百万级以下
- 边缘部署:资源受限环境
- 学习实验:理解向量数据库概念
快速开始
1. 安装
pip install chromadb
# 如果需要内置嵌入模型
pip install chromadb[sentence-transformers]
2. 第一个示例
import chromadb
# 创建客户端(本地模式)
client = chromadb.Client()
# 创建集合(类似数据库表)
collection = client.create_collection(name="my_collection")
# 添加文档
collection.add(
documents=["这是第一篇文档", "这是第二篇文档", "这是第三篇文档"],
metadatas=[{"category": "tech"}, {"category": "life"}, {"category": "tech"}],
ids=["doc1", "doc2", "doc3"]
)
# 查询
results = collection.query(
query_texts=["技术相关文档"],
n_results=2
)
print(results)
3. 使用持久化存储
import chromadb
# 创建持久化客户端
client = chromadb.PersistentClient(path="./chroma_db")
# 后续操作与内存模式相同
collection = client.create_collection(name="my_collection")
核心概念
Client(客户端)
Chroma 提供三种客户端模式:
import chromadb
# 1. 内存客户端(数据不保存)
client = chromadb.Client()
# 2. 持久化客户端(数据保存到磁盘)
client = chromadb.PersistentClient(path="./chroma_data")
# 3. HTTP 客户端(连接远程服务器)
client = chromadb.HttpClient(host="localhost", port=8000)
Collection(集合)
集合是 Chroma 中的数据组织单位,包含文档、嵌入和元数据。
# 创建集合
collection = client.create_collection(
name="documents",
metadata={"description": "文档集合"}
)
# 获取或创建(如果不存在则创建)
collection = client.get_or_create_collection(name="documents")
# 获取现有集合
collection = client.get_collection(name="documents")
# 删除集合
client.delete_collection(name="documents")
# 列出所有集合
print(client.list_collections())
嵌入函数(Embedding Function)
Chroma 支持自动嵌入,无需手动调用嵌入模型。
from chromadb.utils import embedding_functions
# 默认嵌入(all-MiniLM-L6-v2)
default_ef = embedding_functions.DefaultEmbeddingFunction()
# Sentence Transformers
sentence_transformer_ef = embedding_functions.SentenceTransformerEmbeddingFunction(
model_name="all-MiniLM-L6-v2"
)
# OpenAI
openai_ef = embedding_functions.OpenAIEmbeddingFunction(
api_key="your-api-key",
model_name="text-embedding-3-small"
)
# 创建集合时指定嵌入函数
collection = client.create_collection(
name="my_collection",
embedding_function=sentence_transformer_ef
)
数据操作
添加数据(Add)
# 基本添加
collection.add(
documents=["文档1", "文档2", "文档3"],
ids=["id1", "id2", "id3"]
)
# 带元数据
collection.add(
documents=["iPhone 15 发布", "MacBook Pro 评测"],
metadatas=[
{"category": "electronics", "brand": "Apple", "price": 999},
{"category": "electronics", "brand": "Apple", "price": 1999}
],
ids=["product1", "product2"]
)
# 直接提供嵌入(跳过自动嵌入)
import numpy as np
embeddings = np.random.rand(2, 384).tolist()
collection.add(
embeddings=embeddings,
documents=["文档1", "文档2"],
ids=["id1", "id2"]
)
# 批量添加(推荐)
batch_size = 100
for i in range(0, len(documents), batch_size):
batch_docs = documents[i:i+batch_size]
batch_ids = [f"id_{j}" for j in range(i, i+len(batch_docs))]
collection.add(documents=batch_docs, ids=batch_ids)
查询(Query)
文本查询(自动嵌入)
# 基本查询
results = collection.query(
query_texts=["苹果手机"],
n_results=3
)
# 带过滤条件的查询
results = collection.query(
query_texts=["高端电子产品"],
n_results=5,
where={"price": {"$gt": 1000}}
)
# 指定返回字段
results = collection.query(
query_texts=["查询文本"],
n_results=3,
include=["documents", "metadatas", "distances"]
)
向量查询
# 使用向量直接查询
query_embedding = [0.1, 0.2, 0.3, ...] # 384维向量
results = collection.query(
query_embeddings=[query_embedding],
n_results=3
)
获取数据(Get)
# 根据 ID 获取
results = collection.get(ids=["id1", "id2"])
# 获取所有数据
results = collection.get()
# 带过滤条件获取
results = collection.get(
where={"category": "electronics"}
)
# 分页获取
results = collection.get(
limit=10,
offset=20
)
更新数据
# 使用相同的 ID 更新
collection.update(
ids=["id1"],
documents=["更新后的文档内容"],
metadatas=[{"updated": True}]
)
# upsert(存在则更新,不存在则插入)
collection.upsert(
ids=["id1", "id_new"],
documents=["更新内容", "新内容"],
metadatas=[{"status": "updated"}, {"status": "new"}]
)
删除数据
# 根据 ID 删除
collection.delete(ids=["id1", "id2"])
# 根据过滤条件删除
collection.delete(where={"category": "obsolete"})
# 删除所有数据
collection.delete()
过滤条件
支持的运算符
# 等于
{"category": "electronics"}
# 不等于
{"category": {"$ne": "clothing"}}
# 大于/大于等于
{"price": {"$gt": 100}}
{"price": {"$gte": 100}}
# 小于/小于等于
{"price": {"$lt": 1000}}
{"price": {"$lte": 1000}}
# 包含在列表中
{"category": {"$in": ["electronics", "computers"]}}
# 不包含在列表中
{"category": {"$nin": ["food", "book"]}}
组合条件
# AND 条件
{
"$and": [
{"category": "electronics"},
{"price": {"$lte": 1000}}
]
}
# OR 条件
{
"$or": [
{"brand": "Apple"},
{"brand": "Samsung"}
]
}
# 复杂组合
{
"$and": [
{"category": "electronics"},
{"$or": [
{"brand": "Apple"},
{"brand": "Samsung"}
]},
{"price": {"$gte": 500, "$lte": 2000}}
]
}
完整 RAG 示例
import chromadb
from openai import OpenAI
import os
class ChromaRAG:
def __init__(self, collection_name="knowledge_base"):
# 创建持久化客户端
self.client = chromadb.PersistentClient(path="./chroma_db")
# 获取或创建集合
self.collection = self.client.get_or_create_collection(
name=collection_name,
metadata={"description": "RAG 知识库"}
)
# OpenAI 客户端
self.openai = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
def add_documents(self, documents):
"""
添加文档到知识库
documents: [{"content": "...", "metadata": {...}}, ...]
"""
texts = [doc["content"] for doc in documents]
metadatas = [doc.get("metadata", {}) for doc in documents]
ids = [f"doc_{i}" for i in range(len(documents))]
self.collection.add(
documents=texts,
metadatas=metadatas,
ids=ids
)
print(f"已添加 {len(documents)} 篇文档")
def search(self, query, top_k=5, filters=None):
"""搜索相关文档"""
where_clause = filters if filters else {}
results = self.collection.query(
query_texts=[query],
n_results=top_k,
where=where_clause if where_clause else None
)
documents = []
for i in range(len(results["ids"][0])):
documents.append({
"id": results["ids"][0][i],
"content": results["documents"][0][i],
"metadata": results["metadatas"][0][i],
"distance": results["distances"][0][i]
})
return documents
def answer(self, question, top_k=3):
"""生成回答"""
# 检索相关文档
docs = self.search(question, top_k)
# 构建上下文
context = "\n\n".join([
f"[文档 {i+1}] {doc['content']}"
for i, doc in enumerate(docs)
])
# 调用 LLM
prompt = f"""基于以下文档回答问题。如果文档中没有相关信息,请说明。
文档:
{context}
问题:{question}
回答:"""
response = self.openai.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "你是一个基于文档回答问题的助手。"},
{"role": "user", "content": prompt}
]
)
return {
"answer": response.choices[0].message.content,
"sources": docs
}
# 使用示例
rag = ChromaRAG()
# 添加文档
docs = [
{"content": "Chroma 是一个开源的嵌入式向量数据库...", "metadata": {"source": "docs"}},
{"content": "向量数据库用于 AI 应用的语义搜索...", "metadata": {"source": "blog"}}
]
rag.add_documents(docs)
# 提问
result = rag.answer("什么是 Chroma?")
print(f"回答:{result['answer']}")
部署方式
嵌入式模式
直接嵌入到 Python 应用中:
import chromadb
# 内存模式
client = chromadb.Client()
# 持久化模式
client = chromadb.PersistentClient(path="./db")
服务器模式
# 安装 Chroma 服务器
pip install chromadb
# 启动服务器
chroma run --path ./chroma_data --port 8000
# 客户端连接
import chromadb
client = chromadb.HttpClient(host="localhost", port=8000)
collection = client.get_or_create_collection("my_collection")
Docker 部署
# docker-compose.yml
version: '3.9'
services:
chroma:
image: chromadb/chroma:latest
ports:
- "8000:8000"
volumes:
- ./chroma_data:/chroma/chroma
environment:
- IS_PERSISTENT=TRUE
docker-compose up -d
性能优化
批量操作
# 批量添加比逐条添加快得多
batch_size = 1000
for i in range(0, len(documents), batch_size):
batch = documents[i:i+batch_size]
collection.add(
documents=[d["text"] for d in batch],
metadatas=[d["metadata"] for d in batch],
ids=[f"id_{j}" for j in range(i, i+len(batch))]
)
索引配置
# Chroma 使用 HNSW 索引,可以通过参数调优
collection = client.create_collection(
name="optimized",
metadata={
"hnsw:space": "cosine", # 距离度量:cosine, l2, ip
"hnsw:construction_ef": 128, # 构建参数
"hnsw:search_ef": 64, # 搜索参数
"hnsw:M": 16 # 连接数
}
)
使用预计算嵌入
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('all-MiniLM-L6-v2')
# 预计算所有嵌入
embeddings = model.encode(documents, show_progress_bar=True).tolist()
# 直接添加嵌入(跳过 Chroma 的嵌入步骤)
collection.add(
embeddings=embeddings,
documents=documents,
ids=ids
)
与其他工具集成
LangChain 集成
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain.document_loaders import TextLoader
# 加载文档
loader = TextLoader("document.txt")
documents = loader.load()
# 切分文档
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_documents(documents)
# 创建 Chroma 向量存储
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(texts, embeddings, persist_directory="./chroma_db")
# 相似度搜索
docs = vectorstore.similarity_search("查询内容", k=3)
LlamaIndex 集成
from llama_index import VectorStoreIndex, SimpleDirectoryReader
from llama_index.vector_stores import ChromaVectorStore
from llama_index.storage.storage_context import StorageContext
import chromadb
# 创建 Chroma 客户端
chroma_client = chromadb.PersistentClient(path="./chroma_db")
chroma_collection = chroma_client.get_or_create_collection("llamaindex")
# 创建向量存储
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
# 加载文档
documents = SimpleDirectoryReader("./data").load_data()
# 创建索引
index = VectorStoreIndex.from_documents(documents, storage_context=storage_context)
# 查询
query_engine = index.as_query_engine()
response = query_engine.query("你的问题")
常见问题
Q: Chroma 适合生产环境吗?
Chroma 目前更适合:
- 原型开发和概念验证
- 小到中等规模的应用(百万级向量)
- 单节点部署
对于大规模生产环境,建议使用 Milvus 或 Pinecone。
Q: 如何备份数据?
import shutil
import json
# 持久化模式的备份
# 1. 直接复制数据目录
shutil.copytree("./chroma_db", "./chroma_backup")
# 2. 导出为 JSON
collection = client.get_collection("my_collection")
data = collection.get()
with open("backup.json", "w") as f:
json.dump(data, f)
Q: 如何处理大量数据?
# 1. 使用批量添加
# 2. 考虑使用服务器模式
# 3. 定期清理旧数据
# 清理示例:删除超过一定时间的数据
collection.delete(
where={"timestamp": {"$lt": cutoff_time}}
)
Q: 如何迁移到 Milvus?
# 1. 从 Chroma 导出
data = chroma_collection.get()
# 2. 导入到 Milvus
from pymilvus import Collection
milvus_collection = Collection("new_collection")
milvus_collection.insert([
data["documents"],
data["embeddings"]
])
下一步
- Weaviate 教程 - GraphQL 接口的向量数据库
- Qdrant 教程 - Rust 编写的高性能向量数据库
- 实践案例 - 完整项目示例