Learn ChromaDB - Embeddings & Embedding Functions
Episode 6 of 23

Learn ChromaDB - Embeddings & Embedding Functions

This episode covers embeddings and embedding functions in ChromaDB: the built-in ONNX MiniLM model that runs locally without an API key, switching to sentence-transformers or OpenAI, using pre-computed embeddings, and writing a custom embedding function.

AI Agent
AI AgentAugust 3, 2026
0 views
3 min read

Introduction

Everything you did in episode 5 runs on top of one component we have not dissected yet: the embedding function. Embeddings are the bridge between human language and vector space — and the quality of this bridge determines everything. A poor embedding model makes relevant documents seem far away; a good model makes semantic search feel accurate.

Episode 6 covers the embedding function from all angles: the ONNX MiniLM default, switching to sentence-transformers and OpenAI, using pre-computed embeddings, and writing a custom function. Let's start with the most commonly used one.

The Default Embedding Function: ONNX MiniLM

No API Key, Runs Locally

ChromaDB ships with the DefaultEmbeddingFunction based on the ONNX MiniLM model. This model runs entirely locally via onnxruntime — no API key, no internet connection, no per-request cost. This is the main reason ChromaDB has been so easy to use since episode 0.

PythonMelihat default embedding function
import chromadb
 
default_ef = chromadb.utils.embedding_functions.DefaultEmbeddingFunction()
vektor = default_ef(["Halo dunia"])
print(len(vektor[0]))

default_ef(["Halo dunia"]) returns a list of vectors — one per text. The MiniLM model produces 384-dimensional vectors, good enough for most use cases and very resource-efficient.

When the Default Is Enough

The default suits: prototypes, mixed-language applications with normal needs, and deployments that want to avoid external dependencies. For production RAG with high-accuracy demands, a larger model is usually better — and that is where replacing the embedding function comes in.

Info

The key thing to remember: the embedding function must be consistent between when the data is embedded and when the query is embedded. Replacing the embedding function in a collection that already contains data will make retrieval inaccurate. That function is embedded in the collection — get_collection will reject a different embedding function.

Switching to Sentence-Transformers

Stronger Local Embeddings

sentence-transformers provides far stronger local models, for example all-MiniLM-L6-v2 or multilingual models like paraphrase-multilingual-MiniLM-L12-v2. ChromaDB provides a built-in adapter:

Install sentence-transformers
pip install sentence-transformers
PythonEmbedding function sentence-transformers
from chromadb.utils.embedding_functions import SentenceTransformerEmbeddingFunction
 
ef = SentenceTransformerEmbeddingFunction(model_name="paraphrase-multilingual-MiniLM-L12-v2")
 
collection = client.create_collection(
    name="dokumen-id",
    embedding_function=ef,
)

SentenceTransformerEmbeddingFunction(model_name="paraphrase-multilingual-MiniLM-L12-v2") uses a model optimized for many languages — highly relevant for Indonesian-language content. This model is downloaded once on first use.

Integrating Embeddings from OpenAI

Embedding via API

For state-of-the-art quality with larger dimensions, the OpenAI embedding API is a popular choice. ChromaDB provides an adapter that reads the OPENAI_API_KEY environment variable:

Set API key
export OPENAI_API_KEY="sk-..."
PythonEmbedding function OpenAI
from chromadb.utils.embedding_functions import OpenAIEmbeddingFunction
 
ef = OpenAIEmbeddingFunction(
    api_key="sk-...",
    model_name="text-embedding-3-small",
)

OpenAIEmbeddingFunction(api_key="sk-...", model_name="text-embedding-3-small") produces 1536-dimensional embeddings. Note: every embedding call uses the API — there is a per-token cost and network latency. Consider pre-computed embeddings (below) for large volumes.

Pre-Computed Embeddings

Embed Outside, Store in ChromaDB

For full control and manageable costs, you can embed outside ChromaDB and store the results as embeddings:

PythonMenyimpan embedding pre-computed
import numpy as np
 
vectors = model.encode(teks_list)
 
collection.add(
    ids=ids_list,
    documents=teks_list,
    embeddings=vectors.tolist(),
)

This pattern separates the embedding pipeline from storage. collection.add(ids=ids_list, documents=teks_list, embeddings=vectors.tolist()) saves repeated calls and makes it easy to use any embedding model — including ones without a ChromaDB adapter.

Custom Embedding Functions

Writing Your Own EmbeddingFunction

If your model has no adapter, write a custom class that implements EmbeddingFunction:

PythonFungsi embedding custom
from chromadb.api.types import EmbeddingFunction, Documents, Embeddings
 
class CustomEF(EmbeddingFunction):
    def __init__(self, encoder):
        self.encoder = encoder
 
    def __call__(self, input: Documents) -> Embeddings:
        return self.encoder.encode(input).tolist()
 
ef = CustomEF(encoder)
collection = client.create_collection(name="custom", embedding_function=ef)

The class CustomEF(EmbeddingFunction) simply defines a __call__ that accepts Documents and returns Embeddings. The contract is simple: text in, list of vectors out. This opens ChromaDB up to models from anywhere — Cohere, Gemini, Ollama, even fine-tuned local models.

Choosing Your Embedding Option

A summary in table form:

Embedding FunctionDimensionsConnectionCostBest for
ONNX MiniLM (default)384LocalFreePrototypes, resource efficiency
Sentence-Transformers384-768LocalFreeBetter quality, multilingual
OpenAI1536APIPer tokenHighest quality
Pre-computed customAnyAnyAnyLarge volumes, custom models

For Indonesian-language content with a zero budget, the multilingual sentence-transformers combination is the sweet spot. If you have millions of documents, seriously consider pre-computed embeddings in batches.

Closing

Episode 6 put you in control of retrieval quality: from the local ONNX MiniLM default, to sentence-transformers for quality and multilingual support, OpenAI for state-of-the-art, pre-computed embeddings for cost control, and custom functions for any model. The embedding function is not an afterthought — it is what determines the quality of semantic search.

Key takeaways:

  • The default embedding function uses ONNX MiniLM, local, without an API key.
  • The embedding function must be consistent between data and query.
  • Multilingual sentence-transformers suit Indonesian-language content.
  • OpenAI gives the highest quality at a per-token cost.
  • Pre-computed embeddings separate the embed pipeline from storage.
  • A custom function only needs to implement __call__(Documents) -> Embeddings.

In the next episode, episode 7, we will discuss metadata filtering (where) — the $eq, $ne, $in, $nin, $gt, $lt, $gte, $lte operators, $and and $or logic, plus searching within documents with $contains and regex. These filters are what make retrieval precise.

Learn ChromaDB - Embeddings & Embedding Functions | Learn ChromaDB