Pencarian berbasis kata kunci (keyword search) sering gagal memahami maksud pengguna. Contohnya, saat kamu mengetik "cara menyimpan data tanpa server", mesin pencari biasa hanya mencocokkan kata demi kata dan bisa melewatkan artikel tentang SQLite meskipun maknanya persis sama. Di sinilah semantic search hadir: pencarian yang memahami makna, bukan sekadar teks. Dalam tutorial ini, kita akan membangun mesin pencari semantik sendiri menggunakan ChromaDB — vector database open-source yang ringan — dan Python, selangkah demi selangkah dari nol.

Apa Itu Semantic Search dan Vector Database?

Semantic search bekerja dengan mengubah teks menjadi embedding, yaitu vektor angka berdimensi tinggi yang merepresentasikan makna teks. Dua kalimat yang berbeda kata-katanya tetapi bermakna mirip akan menghasilkan vektor yang berdekatan, sedangkan kalimat yang berbeda makna akan berjauhan. Vector database adalah tempat menyimpan vektor-vektor ini dan menyediakan pencarian kemiripan (similarity search) yang sangat cepat.

Konsep ini adalah fondasi dari banyak aplikasi AI modern. Jika kamu pernah mendengar tentang RAG (Retrieval-Augmented Generation) pada chatbot, kamu bisa mempelajari penerapannya di artikel Membangun Chatbot RAG dari Nol dengan Python. Adapun model yang menghasilkan embedding biasanya berasal dari model transformer — dasar-dasarnya bisa kamu pelajari di Tutorial Hugging Face Transformers.

Persiapan: Instalasi

Kita membutuhkan dua library: chromadb sebagai vector database, dan sentence-transformers untuk menghasilkan embedding. Model yang kita pakai (paraphrase-multilingual-MiniLM-L12-v2) mendukung banyak bahasa termasuk bahasa Indonesia, sehingga query dalam bahasa Indonesia tetap akurat.

bash
pip install chromadb sentence-transformers

Langkah 1: Membuat Koleksi dan Menyimpan Data

Di ChromaDB, data disimpan dalam collection (koleksi). Pertama, kita inisialisasi client persisten agar data tersimpan permanen di disk (folder ./db), lalu buat koleksi dan masukkan beberapa dokumen contoh.

python
import chromadb
from sentence_transformers import SentenceTransformer

# 1. Muat model embedding (mendukung bahasa Indonesia)
model = SentenceTransformer("sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2")

# 2. Inisialisasi client ChromaDB (data tersimpan permanen di folder ./db)
client = chromadb.PersistentClient(path="./db")

# 3. Buat (atau ambil) koleksi artikel
collection = client.get_or_create_collection(
    name="artikel",
    metadata={"hnsw:space": "cosine"},  # gunakan cosine similarity
)

# 4. Data contoh
dokumen = [
    "Python adalah bahasa pemrograman populer untuk data science dan machine learning.",
    "JavaScript banyak digunakan untuk membangun antarmuka web yang interaktif.",
    "Docker memudahkan proses deployment aplikasi menggunakan container.",
    "Machine learning memungkinkan komputer belajar pola dari data.",
]
ids = ["doc1", "doc2", "doc3", "doc4"]

# 5. Ubah teks menjadi embedding, lalu simpan
embeddings = model.encode(dokumen).tolist()
collection.add(
    ids=ids,
    documents=dokumen,
    embeddings=embeddings,
)

print(f"Total dokumen tersimpan: {collection.count()}")

Langkah 2: Melakukan Pencarian Semantik

Setelah data tersimpan, kita bisa mencari menggunakan kalimat sehari-hari. Perhatikan bahwa query "bahasa pemrograman untuk analisis data" tidak mengandung kata "Python" maupun "data science" secara utuh, tetapi hasilnya tetap menemukan dokumen yang tepat karena pencarian berbasis makna.

python
# Query dengan bahasa sehari-hari
query = "bahasa pemrograman untuk analisis data"
query_embedding = model.encode([query]).tolist()

hasil = collection.query(
    query_embeddings=query_embedding,
    n_results=2,  # ambil 2 hasil teratas
)

for i, (doc, jarak) in enumerate(zip(hasil["documents"][0], hasil["distances"][0])):
    print(f"{i+1}. {doc}")
    print(f"   jarak cosine: {jarak:.4f}\n")

Langkah 3: Menambahkan Metadata dan Filtering

Dalam aplikasi nyata, kita sering perlu memfilter hasil berdasarkan atribut tertentu, misalnya kategori atau level. ChromaDB mendukung metadata dan filter where yang mirip sintaks MongoDB.

python
# Menyimpan data dengan metadata
collection.add(
    ids=["doc5", "doc6", "doc7"],
    documents=[
        "Cara membuat REST API dengan FastAPI.",
        "Panduan deployment aplikasi dengan Docker Compose.",
        "Mengoptimalkan query database PostgreSQL.",
    ],
    embeddings=model.encode([
        "Cara membuat REST API dengan FastAPI.",
        "Panduan deployment aplikasi dengan Docker Compose.",
        "Mengoptimalkan query database PostgreSQL.",
    ]).tolist(),
    metadatas=[
        {"kategori": "web", "level": "menengah"},
        {"kategori": "devops", "level": "pemula"},
        {"kategori": "database", "level": "mahir"},
    ],
)

# Pencarian dengan filter metadata
query = "cara deploy aplikasi"
query_embedding = model.encode([query]).tolist()

hasil = collection.query(
    query_embeddings=query_embedding,
    n_results=5,
    where={"kategori": "devops"},  # hanya dokumen kategori devops
)

for doc in hasil["documents"][0]:
    print(f"- {doc}")

Studi Kasus: Mesin Pencari Dokumentasi

Mari kita rangkai semuanya menjadi mesin pencari dokumentasi kecil yang benar-benar berfungsi. Skrip di bawah menyimpan beberapa baris dokumentasi, lalu menjawab pertanyaan dengan bahasa yang tidak persis sama dengan teks sumbernya.

python
import chromadb
from sentence_transformers import SentenceTransformer

model = SentenceTransformer("sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2")
client = chromadb.PersistentClient(path="./db_dokumentasi")
col = client.get_or_create_collection("dokumentasi", metadata={"hnsw:space": "cosine"})

dokumentasi = [
    ("apa-itu-venv", "Virtual environment mengisolasi dependensi antar proyek Python."),
    ("install-package", "Gunakan pip install untuk menambahkan package baru ke proyek."),
    ("deploy-server", "Deploy aplikasi web ke server produksi dengan Nginx dan Gunicorn."),
    ("koneksi-db", "Menghubungkan aplikasi ke database PostgreSQL menggunakan connection string."),
    ("buat-fungsi", "Mendefinisikan fungsi di Python dengan kata kunci def."),
]

col.add(
    ids=[x[0] for x in dokumentasi],
    documents=[x[1] for x in dokumentasi],
    embeddings=model.encode([x[1] for x in dokumentasi]).tolist(),
)

def cari(teks, n=2):
    emb = model.encode([teks]).tolist()
    hasil = col.query(query_embeddings=emb, n_results=n)
    for doc, jarak in zip(hasil["documents"][0], hasil["distances"][0]):
        print(f"[{jarak:.3f}] {doc}")

print("Query: 'cara pasang library'")
cari("cara pasang library")

print("\nQuery: 'menaruh aplikasi ke server'")
cari("menaruh aplikasi ke server")

Jalankan skrip di atas dan perhatikan hasilnya: query "cara pasang library" menemukan dokumentasi tentang pip install, sedangkan "menaruh aplikasi ke server" menemukan dokumentasi deploy — padahal kata-katanya tidak cocok secara literal. Inilah kekuatan semantic search.

Tips memilih model embedding: gunakan model multilingual (seperti paraphrase-multilingual-MiniLM-L12-v2) jika dokumenmu berbahasa Indonesia. Untuk dokumen berbahasa Inggris saja, model all-MiniLM-L6-v2 lebih ringan dan cepat. Semakin besar dimensi vektor, semakin kaya representasi maknanya, tetapi semakin lambat dan besar penyimpanannya.

ChromaDB mendukung dua mode: PersistentClient (menyimpan data ke disk, cocok untuk produksi kecil) dan EphemeralClient (data hanya di memori, cocok untuk eksperimen cepat). Ada juga mode client-server untuk deployment terpisah.

Kesimpulan

Kamu baru saja membangun mesin pencari semantik yang berfungsi penuh dengan ChromaDB dan Python. Dari sini, kamu bisa mengembangkannya menjadi basis RAG untuk chatbot, sistem rekomendasi, atau pencarian produk di aplikasi e-commerce. Vector database adalah keterampilan yang semakin penting di era AI — dan sekarang kamu sudah memegang fondasinya.