Cài đặt ChromaDB và chạy thử trong 5 phút
Nếu đang build AI Agent hay hệ thống RAG, ChromaDB là thứ cần biết ngay. Mình đi thẳng vào code trước, giải thích sau.
Cài đặt:
pip install chromadb
# Nếu cần embedding model local
pip install chromadb sentence-transformers
Khởi tạo và thêm dữ liệu ngay:
import chromadb
# In-memory client — test nhanh, không cần file
client = chromadb.Client()
# Tạo collection (tương đương table trong SQL)
collection = client.create_collection(name="my_knowledge_base")
# Thêm documents
collection.add(
documents=[
"Docker là nền tảng container hóa ứng dụng",
"Kubernetes quản lý container ở quy mô lớn",
"Redis là in-memory database tốc độ cao"
],
ids=["doc1", "doc2", "doc3"]
)
# Semantic search
results = collection.query(
query_texts=["container orchestration tool"],
n_results=2
)
print(results['documents'])
# Output: [['Kubernetes quản lý container ở quy mô lớn', 'Docker là nền tảng container hóa ứng dụng']]
Xong. Đó là ChromaDB cơ bản — semantic search không cần viết một dòng SQL nào.
ChromaDB là gì và tại sao cần nó?
ChromaDB là vector database mã nguồn mở, ban đầu được build để phục vụ AI applications. Khác database thông thường ở chỗ: thay vì tìm kiếm theo giá trị chính xác (exact match), nó tìm theo ý nghĩa ngữ nghĩa (semantic similarity) — query “container orchestration” trả về kết quả về Kubernetes dù không có từ nào khớp chính xác.
Hồi mình build tool internal để query documentation của team, thử dùng Elasticsearch full-text search — kết quả khá tệ. User gõ “container bị crash” thì không tìm ra bài viết “pod restart loop trong Kubernetes”. Chuyển sang ChromaDB với embedding, kết quả cải thiện rõ rệt.
ChromaDB hoạt động như thế nào?
- Text đầu vào → Embedding model chuyển thành vector (mảng số thực)
- Vector được lưu vào ChromaDB kèm metadata tùy chọn
- Khi query, text query cũng được embed thành vector
- ChromaDB tính cosine similarity, trả về documents gần nhất
Mặc định ChromaDB dùng all-MiniLM-L6-v2 từ sentence-transformers — nhỏ, nhanh, đủ tốt cho hầu hết use case. Muốn độ chính xác cao hơn thì swap sang OpenAI embeddings hay custom model cũng được — chỉ cần thay embedding function khi tạo collection.
Persistent Storage: Lưu dữ liệu ra file
In-memory client tiện để test nhanh, nhưng restart là mất sạch data. Production thì dùng PersistentClient:
import chromadb
# Lưu vào thư mục local
client = chromadb.PersistentClient(path="./chroma_db")
collection = client.get_or_create_collection(
name="devops_knowledge",
metadata={"hnsw:space": "cosine"} # cosine similarity
)
Data được lưu vào ./chroma_db ngay khi add documents. Restart process, data vẫn còn nguyên.
Xây dựng Knowledge Base cho AI Agent
RAG (Retrieval-Augmented Generation) là use case mình dùng ChromaDB thường xuyên nhất. Thay vì nhét toàn bộ documentation vào context của LLM (một file docs dài 50 trang đã ngốn ~40k tokens rồi), ta lưu vào ChromaDB và chỉ retrieve 3–5 đoạn liên quan khi cần.
Index tài liệu vào ChromaDB
import chromadb
from pathlib import Path
client = chromadb.PersistentClient(path="./knowledge_db")
collection = client.get_or_create_collection("docs")
def index_markdown_files(docs_dir: str):
docs_path = Path(docs_dir)
for md_file in docs_path.glob("**/*.md"):
content = md_file.read_text(encoding="utf-8")
# Chunk theo paragraph, bỏ qua đoạn quá ngắn
chunks = [c.strip() for c in content.split("\n\n") if len(c.strip()) > 50]
if not chunks:
continue
collection.add(
documents=chunks,
ids=[f"{md_file.stem}_{i}" for i in range(len(chunks))],
metadatas=[{"source": str(md_file), "chunk": i} for i in range(len(chunks))]
)
print(f"Indexed {md_file.name}: {len(chunks)} chunks")
index_markdown_files("./docs")
Query và retrieve context
def ask_knowledge_base(question: str, n_results: int = 3) -> str:
results = collection.query(
query_texts=[question],
n_results=n_results,
include=["documents", "metadatas", "distances"]
)
contexts = results["documents"][0]
sources = [m["source"] for m in results["metadatas"][0]]
distances = results["distances"][0]
# distance < 1.5 = còn liên quan (cosine distance: 0=giống nhau, 2=khác hoàn toàn)
relevant = [
(ctx, src)
for ctx, src, dist in zip(contexts, sources, distances)
if dist < 1.5
]
if not relevant:
return "Không tìm thấy thông tin liên quan."
return "\n---\n".join([ctx for ctx, _ in relevant])
context = ask_knowledge_base("Làm sao restart service trong systemd?")
print(context)
Metadata Filtering: Tìm kiếm có điều kiện
Collection chứa nhiều loại tài liệu? Metadata filter giúp thu hẹp phạm vi tìm kiếm — kết quả liên quan hơn, tốc độ cũng nhanh hơn:
# Chỉ tìm trong docs về Linux
results = collection.query(
query_texts=["cách mount disk"],
n_results=3,
where={"category": "linux"}
)
# Kết hợp nhiều điều kiện
results = collection.query(
query_texts=["backup database"],
n_results=5,
where={
"$and": [
{"category": {"$in": ["postgresql", "mysql"]}},
{"language": "vi"}
]
}
)
Chạy ChromaDB Server bằng Docker
Khi nhiều service hoặc nhiều người cần dùng chung, chạy ChromaDB riêng qua Docker là cách sạch nhất:
docker run -d \
--name chromadb \
-p 8000:8000 \
-v ./chroma_data:/chroma/chroma \
chromadb/chroma:latest
# Kiểm tra server đã chạy chưa
curl http://localhost:8000/api/v1/heartbeat
# Connect từ Python
import chromadb
client = chromadb.HttpClient(host="localhost", port=8000)
collection = client.get_or_create_collection("shared_knowledge")
Hoặc Docker Compose nếu muốn tích hợp cùng các service khác:
version: "3.8"
services:
chromadb:
image: chromadb/chroma:latest
ports:
- "8000:8000"
volumes:
- chroma_data:/chroma/chroma
environment:
- ALLOW_RESET=true # Chỉ bật khi development
volumes:
chroma_data:
Tips thực tế khi làm việc với ChromaDB
Chunk size ảnh hưởng nhiều đến chất lượng kết quả
Qua thử nghiệm, chunk khoảng 300–500 tokens cho kết quả tốt nhất. Quá nhỏ thì mất context, quá lớn thì embedding không capture được ý chính. Với tài liệu kỹ thuật dài, dùng sliding window — overlap 20% giữa các chunk liền kề để không bị mất thông tin ở ranh giới.
Luôn lưu source metadata đầy đủ
Retrieve xong mà không biết thông tin từ đâu thì vô dụng — không citation được, không debug được. Lưu ít nhất: source file, section title, ngày cập nhật. Mình từng quên lưu source và phải index lại toàn bộ 500 files — mất 2 tiếng chỉ vì bỏ qua bước này.
Update document dùng upsert
# ChromaDB không có UPDATE riêng — dùng upsert
collection.upsert(
documents=["Nội dung đã cập nhật"],
ids=["doc1"] # ID đã tồn tại → overwrite
)
Backup ChromaDB cực kỳ đơn giản
Data của PersistentClient được lưu dạng SQLite + binary files trong thư mục đã chỉ định. Backup đơn giản hơn nhiều so với database truyền thống — copy nguyên thư mục là xong:
# Backup thủ công
cp -r ./chroma_db ./chroma_db_backup_$(date +%Y%m%d)
# Nén để tiết kiệm dung lượng
tar -czf chroma_backup_$(date +%Y%m%d).tar.gz ./chroma_db
ChromaDB hay Qdrant — chọn cái nào?
Blog đã có bài riêng về Qdrant, nên mình chỉ note nhanh: ChromaDB dễ cài hơn, API Python-friendly hơn, phù hợp khi cần prototype nhanh hoặc team nhỏ. Qdrant mạnh hơn ở production scale lớn, filtering phức tạp, và hỗ trợ nhiều ngôn ngữ client. Đang làm side project hay internal tool? Bắt đầu với ChromaDB. Sau này scale lên thì migrate sang Qdrant vẫn kịp.
