「AI Agent 全栈开发 50 讲」——从本地模型部署到多智能体系统,一年省 87 万 第10节
第 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 万) |
| pgvector | PostgreSQL 插件 | 已有 PG 基础设施 |
| Weaviate | 全功能,GraphQL API | 企业级应用 |
| Qdrant | Rust 实现,高性能 | 对延迟敏感的场景 |
对于我们的教程,ChromaDB 是最佳选择——Python 原生,安装简单,功能够用,和 sentence-transformers 无缝集成。
二、ChromaDB 简介与安装
2.1 安装
uv pip install chromadb
ChromaDB 的依赖很少,安装很快。它自带 SQLite 作为底层存储引擎,不需要额外安装数据库。
2.2 核心概念
图 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 万级别时,需要注意:
- 分批导入:每次
add()不超过 1000 条,避免内存溢出 - 索引构建:大量数据导入后,ChromaDB 会自动构建 HNSW 索引
- 显存管理:嵌入函数在 GPU 上运行,注意 batch_size 控制
七、小结与预告
这节课我们完成了向量数据库 ChromaDB 的部署和使用——从数据导入到语义检索,再到元数据过滤。
核心收获:
- ChromaDB 解决持久化问题:向量存磁盘,重启不丢失
- 嵌入函数自动调用:不需要手动
model.encode(),ChromaDB 自动处理 - 元数据过滤:语义相似 + 业务条件,双重筛选
- ANN 索引:毫秒级检索,随数据量增长几乎不减速
下一节课,我们将引入 Rerank 模型——在向量检索的粗排结果上,做一次精排,进一步提升检索准确率。这是 RAG 系统中"检索质量"的关键一环。
我们下一课见。
系列教程导航
上一篇:第 09 课 | 嵌入模型 BGE 本地部署:让机器理解语义
下一篇:第 11 课 | Rerank 模型部署:粗排 + 精排,双重保险
本系列共 50 课,持续更新中。关注我不迷路。
更多推荐
所有评论(0)