Building the ChromaDB index

Embeddings turn text into vectors that capture meaning. Chunks that mean similar things land near each other in vector space, even when they use completely different words. ChromaDB stores those vectors and lets us search them quickly.

Indexing flow

Chunks become vectors, and vectors live in ChromaDB alongside their source metadata.

rag_utils.py
python
class EmbeddingProvider:
    """Generates embeddings (vector representations) of text"""

    def __init__(self, model_name: str = "all-MiniLM-L6-v2"):
        self.model = SentenceTransformer(model_name)

    async def generate_embeddings(self, texts):
        if not texts:
            return []
        embeddings = self.model.encode(texts, convert_to_tensor=False)
        if len(embeddings.shape) == 1:
            return [embeddings.tolist()]
        return embeddings.tolist()

all-MiniLM-L6-v2 is a small, fast bi-encoder that produces 384-dimensional vectors. It runs comfortably on CPU and is strong enough for most retrieval workloads.

rag_utils.py
python
class VectorStore:
    """Manages document storage and retrieval using ChromaDB"""

    def __init__(self, collection_name: str = "website_rag"):
        self.client = chromadb.PersistentClient(path="./chroma_db")
        self.collection = self.client.get_or_create_collection(name=collection_name)

    def add_documents(self, documents, embeddings):
        ids = [f"{doc.url}_{doc.chunk_index}" for doc in documents]
        metadatas = [doc.metadata for doc in documents]
        contents = [doc.content for doc in documents]

        self.collection.add(
            ids=ids,
            embeddings=embeddings,
            metadatas=metadatas,
            documents=contents,
        )

PersistentClient keeps the collection on disk between runs. The deterministic ID scheme (url_chunkIndex) makes re-ingestion idempotent.

OpenAI embeddings are higher quality for general use, but they cost money per call and require a network round trip on every ingestion. Local models like all-MiniLM-L6-v2 are free, private, and fast enough for most workloads. Start local, measure retrieval quality, and upgrade to a paid embedding model only if the measurement tells you to.

Quiz: Quiz

Loading practice…