Episode ini membahas embeddings dan embedding functions di ChromaDB: model bawaan ONNX MiniLM yang berjalan lokal tanpa API key, penggantian ke sentence-transformers atau OpenAI, penggunaan pre-computed embeddings, dan penulisan fungsi embedding custom.

Semua yang kalian lakukan di episode 5 berjalan di atas satu komponen yang belum kita bedah: embedding function. Embedding adalah jembatan antara bahasa manusia dan ruang vektor — dan kualitas jembatan ini menentukan segalanya. Model embedding yang buruk membuat dokumen yang relevan tampak jauh; model yang baik membuat semantic search terasa akurat.
Episode 6 membahas embedding function dari semua sisi: default berbasis ONNX MiniLM, penggantian ke sentence-transformers dan OpenAI, pemakaian pre-computed embeddings, hingga penulisan fungsi custom. Mari kita mulai dari yang paling sering dipakai.
ChromaDB datang dengan DefaultEmbeddingFunction berbasis model ONNX MiniLM. Model ini berjalan sepenuhnya lokal lewat onnxruntime — tanpa API key, tanpa koneksi internet, tanpa biaya per permintaan. Ini alasan utama ChromaDB begitu mudah dipakai sejak episode 0.
import chromadb
default_ef = chromadb.utils.embedding_functions.DefaultEmbeddingFunction()
vektor = default_ef(["Halo dunia"])
print(len(vektor[0]))default_ef(["Halo dunia"]) mengembalikan list vektor — satu per teks. Model MiniLM menghasilkan vektor 384 dimensi, cukup baik untuk sebagian besar use case dan sangat hemat resource.
Default cocok untuk: prototipe, aplikasi berbahasa campuran dengan kebutuhan normal, dan deployment yang ingin menghindari dependency eksternal. Untuk RAG produksi dengan tuntutan akurasi tinggi, model yang lebih besar biasanya lebih baik — dan di situlah penggantian embedding function berperan.
Info
Kunci yang harus diingat: embedding function harus konsisten antara saat data di-embed dan saat query di-embed. Mengganti embedding function di collection yang sudah berisi data akan membuat retrieval tidak akurat. Kumpulan itu tertanam di dalam collection — get_collection akan menolak embedding function berbeda.
sentence-transformers menyediakan model lokal yang jauh lebih kuat, misalnya all-MiniLM-L6-v2 atau model multilingual seperti paraphrase-multilingual-MiniLM-L12-v2. ChromaDB menyediakan adapter bawaan:
pip install sentence-transformersfrom 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") memakai model yang dioptimalkan untuk banyak bahasa — sangat relevan untuk konten berbahasa Indonesia. Model ini diunduh sekali saat pertama dipakai.
Untuk kualitas state-of-the-art dengan dimensi lebih besar, OpenAI embedding API adalah pilihan populer. ChromaDB menyediakan adapter yang membaca variabel environment OPENAI_API_KEY:
export OPENAI_API_KEY="sk-..."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") menghasilkan embedding 1536 dimensi. Perhatikan: setiap panggilan embed memakai API — ada biaya per token dan latensi jaringan. Pertimbangkan pre-computed embeddings (di bawah) untuk volume besar.
Untuk kontrol penuh dan biaya terkendali, kalian bisa men-embed di luar ChromaDB lalu menyimpan hasilnya sebagai embeddings:
import numpy as np
vectors = model.encode(teks_list)
collection.add(
ids=ids_list,
documents=teks_list,
embeddings=vectors.tolist(),
)Pola ini memisahkan pipeline embedding dari storage. collection.add(ids=ids_list, documents=teks_list, embeddings=vectors.tolist()) menghemat panggilan berulang dan memudahkan pemakaian model embedding apa pun — termasuk yang tidak punya adapter ChromaDB.
Jika model kalian tidak punya adapter, tulis kelas custom yang mengimplementasi EmbeddingFunction:
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)Kelas CustomEF(EmbeddingFunction) cukup mendefinisikan __call__ yang menerima Documents dan mengembalikan Embeddings. Kontraknya sederhana: teks masuk, list vektor keluar. Ini membuka ChromaDB untuk model dari mana saja — Cohere, Gemini, Ollama, hingga model lokal hasil fine-tuning.
Rangkuman dalam bentuk tabel:
| Embedding Function | Dimensi | Koneksi | Biaya | Cocok untuk |
|---|---|---|---|---|
| ONNX MiniLM (default) | 384 | Lokal | Gratis | Prototipe, hemat resource |
| Sentence-Transformers | 384-768 | Lokal | Gratis | Kualitas lebih baik, multilingual |
| OpenAI | 1536 | API | Per token | Kualitas tertinggi |
| Pre-computed custom | Bebas | Bebas | Bebas | Volume besar, model khusus |
Untuk konten berbahasa Indonesia dengan budget nol, kombinasi sentence-transformers multilingual adalah sweet spot. Jika kalian punya volume jutaan dokumen, serius pertimbangkan pre-computed embeddings dengan batch.
Episode 6 membuat kalian memegang kendali atas kualitas retrieval: dari default ONNX MiniLM yang berjalan lokal, sentence-transformers untuk kualitas dan multilingual, OpenAI untuk state-of-the-art, pre-computed embeddings untuk kontrol biaya, hingga fungsi custom untuk model apa pun. Embedding function bukan pelengkap — dia adalah penentu kualitas semantic search.
Inti yang harus dibawa pulang:
__call__(Documents) -> Embeddings.Di episode 7 selanjutnya kita akan membahas metadata filtering (where) — operator $eq, $ne, $in, $nin, $gt, $lt, $gte, $lte, logika $and dan $or, plus pencarian di dalam dokumen dengan $contains dan regex. Filter inilah yang membuat retrieval presisi.