Hướng dẫn cài đặt và sử dụng ChromaDB: Vector Database mã nguồn mở để xây dựng hệ thống lưu trữ tri thức cho AI Agent

Database tutorial - IT technology blog
Database tutorial - IT technology blog

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?

  1. Text đầu vào → Embedding model chuyển thành vector (mảng số thực)
  2. Vector được lưu vào ChromaDB kèm metadata tùy chọn
  3. Khi query, text query cũng được embed thành vector
  4. 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.

Share: