第 10 课 | 向量数据库 ChromaDB 实战:存储与检索

第 9 课的嵌入向量存在内存里,重启就没了。这节课,我们把它们存入向量数据库 ChromaDB——持久化、高效检索、元数据过滤,一条龙搞定。


一、为什么需要向量数据库

1.1 第 9 课的遗留问题

在第 9 课,我们学会了用 BGE 模型把文本变成向量。但有个问题:这些向量存在 Python 的内存里,程序一关就没了。

想象一下你的竞品监控 Agent 处理 100 万条商品数据:每次重启都要重新生成 100 万个嵌入向量?那得等几个小时。而且 100 万个 1024 维向量存在内存里,需要 100 万 × 1024 × 4 字节 ≈ 4GB 内存。这还只是向量,不算原始文本。

向量数据库就是来解决这个问题的。

1.2 向量数据库的三大核心能力

能力说明对应传统数据库
持久化存储向量存磁盘,重启不丢失数据持久化
高效检索(ANN)近似最近邻搜索,毫秒级返回 Top-K索引查询
元数据过滤按类别、时间、价格等条件过滤WHERE 子句

ANN(Approximate Nearest Neighbor)是向量数据库的核心技术。精确的最近邻搜索需要遍历所有向量(O(n)),100 万条数据就是 100 万次比较。ANN 通过索引结构(如 HNSW)将复杂度降到 O(log n),毫秒级返回结果。

1.3 向量数据库选型

数据库特点适用场景
ChromaDB轻量,Python 原生,零配置开发原型、中小规模(<100 万)
Milvus分布式,高性能,GPU 加速大规模生产(>100 万)
pgvectorPostgreSQL 插件已有 PG 基础设施
Weaviate全功能,GraphQL API企业级应用
QdrantRust 实现,高性能对延迟敏感的场景

对于我们的教程,ChromaDB 是最佳选择——Python 原生,安装简单,功能够用,和 sentence-transformers 无缝集成。


二、ChromaDB 简介与安装

2.1 安装

uv pip install chromadb

ChromaDB 的依赖很少,安装很快。它自带 SQLite 作为底层存储引擎,不需要额外安装数据库。

2.2 核心概念

ChromaDB Client
客户端

Collection
集合(类似表)

Document
文档(原始文本)

Embedding
嵌入向量(1024维)

Metadata
元数据(类别、价格等)

图 1:ChromaDB 核心概念

  • Client:客户端,管理连接和 Collection
  • Collection:集合,类似关系数据库的"表",存储一组相关文档
  • Document:原始文本,即你要检索的内容
  • Embedding:文档的向量表示,由嵌入模型自动生成
  • Metadata:文档的附加属性,用于过滤

2.3 两种模式

ChromaDB 支持两种模式:

# 内存模式(数据在内存中,重启丢失)
client = chromadb.Client()

# 持久化模式(数据存磁盘,重启不丢失)★ 推荐
client = chromadb.PersistentClient(path="./chroma_db")

生产环境一定要用持久化模式。


三、创建 Collection 并导入数据

3.1 完整导入流程

code/chroma_store.py 实现了从第 9 课的 products.json 到 ChromaDB 的完整导入流程:

import chromadb
from chromadb.utils import embedding_functions

# 1. 创建嵌入函数
embedding_fn = embedding_functions.SentenceTransformerEmbeddingFunction(
    model_name="BAAI/bge-large-zh-v1.5",
)

# 2. 创建持久化客户端
client = chromadb.PersistentClient(path="./chroma_db")

# 3. 创建 Collection
collection = client.create_collection(
    name="product_knowledge_base",
    embedding_function=embedding_fn,
    metadata={"description": "商品知识库"},
)

# 4. 批量导入
collection.add(
    ids=["1", "2", "3", ...],
    documents=["商品描述1", "商品描述2", ...],
    metadatas=[{"name": "耳机", "category": "数码"}, ...],
)

关键点:

  • embedding_function 指定了嵌入模型。ChromaDB 会在导入时自动调用 BGE 生成向量,不需要手动调用 model.encode()
  • ids 必须唯一,用于后续的更新和删除
  • 导入是增量的,多次调用 add() 会追加数据

3.2 持久化验证

导入完成后,检查 ./chroma_db 目录:

chroma_db/
├── chroma.sqlite3          ← 元数据和向量索引
└── 索引文件...

数据已经在磁盘上了。重启 Python 后,只需 get_collection() 就能恢复所有数据,不需要重新导入。


四、语义检索

4.1 基础检索

collection = client.get_collection(
    name="product_knowledge_base",
    embedding_function=embedding_fn,
)

results = collection.query(
    query_texts=["适合办公的椅子"],
    n_results=3,
)

返回结果结构:

{
    "ids": [["4", "7", "16"]],
    "documents": [["人体工学办公椅...", "升降桌 E5...", "双肩电脑包..."]],
    "metadatas": [[{"name": "人体工学办公椅", "category": "家居办公"}, ...]],
    "distances": [[0.12, 0.35, 0.72]],
}
  • distances 是向量距离(越小越相似)。对于归一化向量,距离 ≈ 2 × (1 - 余弦相似度)
  • 转换公式:similarity = 1 - distance / 2

4.2 与第 9 课手动检索的对比

维度第 9 课手动检索ChromaDB
代码量~30 行~5 行
持久化不支持自动持久化
检索速度(1000 条)~50ms~5ms
检索速度(100 万条)~30s~50ms
元数据过滤手动实现内置支持

五、元数据过滤

5.1 基础过滤

元数据过滤让你在语义相似的基础上,按业务条件筛选:

# 只看数码电子类
results = collection.query(
    query_texts=["音质好的设备"],
    n_results=3,
    where={"category": "数码电子"},
)

效果对比:

过滤条件结果
无过滤智能蓝牙耳机 Pro → 高清降噪耳机 X3 → 便携蓝牙音箱
category=数码电子智能蓝牙耳机 Pro → 高清降噪耳机 X3 → 智能手表 Ultra

加了过滤后,第三条从"便携蓝牙音箱"(也是数码电子)变成了"智能手表 Ultra"——因为只返回数码电子类,不相关的被排除了。

5.2 复合过滤条件

ChromaDB 支持丰富的过滤操作符:

# 多条件
where={"category": "数码电子", "price": {"$gt": 100}}

# 范围
where={"price": {"$gte": 50, "$lte": 200}}

# 列表
where={"category": {"$in": ["数码电子", "家用电器"]}}

这些过滤条件在向量检索之前执行(Pre-filtering),确保只在与过滤条件匹配的文档中搜索。


六、实战:构建商品知识库

6.1 完整流程

数据准备 → 定义嵌入函数 → 创建 Collection → 批量导入 → 语义检索

整个流程在 code/chroma_store.py 和 code/chroma_search.py 中完整实现。

6.2 性能测试

在 20 条商品数据上的检索性能:

操作耗时说明
导入 20 条~1.5s含嵌入生成 + 写入
单次检索~5ms含嵌入生成 + ANN 搜索
带过滤检索~3ms过滤减少了搜索空间
50 次检索~250ms平均 5ms/次

对于 20 条数据,ChromaDB 的 ANN 索引优势还不明显。但当数据量达到 10 万条以上时,ANN 索引的优势就体现出来了——检索速度几乎不随数据量线性增长。

6.3 扩展到大规模数据

当数据量达到 10 万级别时,需要注意:

  1. 分批导入:每次 add() 不超过 1000 条,避免内存溢出
  2. 索引构建:大量数据导入后,ChromaDB 会自动构建 HNSW 索引
  3. 显存管理:嵌入函数在 GPU 上运行,注意 batch_size 控制

七、小结与预告

这节课我们完成了向量数据库 ChromaDB 的部署和使用——从数据导入到语义检索,再到元数据过滤。

核心收获:

  1. ChromaDB 解决持久化问题:向量存磁盘,重启不丢失
  2. 嵌入函数自动调用:不需要手动 model.encode(),ChromaDB 自动处理
  3. 元数据过滤:语义相似 + 业务条件,双重筛选
  4. ANN 索引:毫秒级检索,随数据量增长几乎不减速

下一节课,我们将引入 Rerank 模型——在向量检索的粗排结果上,做一次精排,进一步提升检索准确率。这是 RAG 系统中"检索质量"的关键一环。

我们下一课见。


系列教程导航

上一篇:第 09 课 | 嵌入模型 BGE 本地部署:让机器理解语义

下一篇:第 11 课 | Rerank 模型部署:粗排 + 精排,双重保险

本系列共 50 课,持续更新中。关注我不迷路。

Logo

聚焦RDK与具身智能,汇集机器人开发教程、开源案例和技术交流

更多推荐