Episode ini membahas advanced query patterns ChromaDB: multi-vector dengan beberapa embedding per dokumen, multimodal untuk image dan text embedding, serta feedback loop dengan relevance scoring dan human feedback untuk tuning retrieval berkelanjutan.

Pola dasar retrieval sudah kalian kuasai di episode 18. Episode 19 naik satu level dengan advanced query patterns: cara-cara yang jarang dibahas tapi sering menentukan kualitas aplikasi RAG di dunia nyata — multi-vector, multimodal, dan feedback loop yang membuat retrieval terus membaik.
Tiga topik ini saling melengkapi: multi-vector memperkaya representasi satu dokumen, multimodal melebarkan jangkauan ke gambar, dan feedback loop menjadikan sistem kalian belajar dari pemakaian. Mari kita mulai.
Selama ini satu dokumen punya satu embedding. Multi-vector membalik asumsi itu: satu dokumen diwakili beberapa embedding — misalnya satu untuk judul, satu untuk isi, satu untuk kata kunci. Tiap embedding mencari di dimensi yang berbeda, dan hasilnya digabung.
Implementasinya di ChromaDB sederhana: karena ids unik, kalian menyimpan varian sebagai item terpisah dengan id berbeda namun document yang sama:
varian_teks = {
"judul": "Pengenalan Docker untuk Pemula",
"isi": "Docker mengemas aplikasi beserta dependency-nya",
"kunci": "container, image, deployment",
}
collection.upsert(
ids=[f"doc-1:{aspek}" for aspek in varian_teks],
documents=[teks for teks in varian_teks.values()],
metadatas=[{"aspek": aspek, "doc_id": "doc-1"} for aspek in varian_teks],
)ids=[f"doc-1:{aspek}" membuat tiga item untuk satu dokumen. Metadata doc_id menjaga keterkaitan — saat retrieval menemukan varian mana pun, kalian tahu dokumen aslinya.
Saat query, ChromaDB mengembalikan hasil dari berbagai varian. Langkah penting: deduplikasi berdasarkan doc_id:
hasil = collection.query(query_texts=["cara deploy container"], n_results=10)
terlihat = set()
final = []
for i, doc_id in enumerate(hasil["metadatas"][0]):
if doc_id["doc_id"] not in terlihat:
terlihat.add(doc_id["doc_id"])
final.append((doc_id["doc_id"], hasil["documents"][0][i]))Loop di atas mengambil dokumen unik pertama yang muncul. hasil["metadatas"][0] memberi doc_id untuk deduplikasi — pola ini wajib saat memakai multi-vector.
Info
Multi-vector meningkatkan recall karena beberapa sudut pandang dokumen bisa terpancing oleh query yang berbeda. Bonusnya: kalian bisa memberi bobot berbeda per aspek saat menggabung hasil.
ChromaDB tidak peduli data apa yang di-embed — vektor tetap vektor. Dengan multimodal embedding model seperti CLIP, gambar dan teks dipetakan ke ruang vektor yang sama, sehingga pencarian lintas media menjadi mungkin.
from chromadb.utils.embedding_functions import OpenCLIPEmbeddingFunction
ef = OpenCLIPEmbeddingFunction()
collection = client.create_collection(
name="katalog",
embedding_function=ef,
)
collection.add(
ids=["img-1", "img-2"],
embeddings=[ef([gambar1])[0], ef([gambar2])[0]],
metadatas=[{"tipe": "image", "judul": "Sepatu merah"}],
)OpenCLIPEmbeddingFunction() membangkitkan embedding yang setara untuk gambar dan teks. collection.add(ids=["img-1"], embeddings=[...]) menyimpan embedding gambar langsung — memakai pola pre-computed dari episode 6.
Keajaiban multimodal: query teks menemukan gambar yang cocok:
hasil = collection.query(
query_texts=["sepatu berwarna merah"],
n_results=3,
where={"tipe": "image"},
)collection.query(query_texts=["sepatu berwarna merah"], where={"tipe": "image"}) menemukan gambar sepatu merah meski query berupa teks. Karena CLIP menyatukan dua modalitas dalam satu ruang vektor, arah query-nya bisa bolak-balik.
Retrieval yang bagus tidak berhenti di deploy — ia belajar. Mulai dengan mencatat relevance score dari interaksi pengguna: apakah hasil yang diberikan ternyata membantu menjawab pertanyaan mereka?
collection_feedback.add(
ids=[f"fb-{next_id}"],
documents=[query_pengguna],
metadatas={
"doc_retrieved": doc_id,
"helpful": 1.0,
"timestamp": now,
},
)collection_feedback.add(ids=[...], metadatas={"helpful": 1.0, ...}) menyimpan sinyal kualitas: query, dokumen yang dipilih, dan apakah membantu. Kumpulan sinyal ini menjadi dataset latih untuk tuning retrieval.
Dengan feedback terkumpul, kalian bisa melakukan tuning berkelanjutan:
helpful.import statistics
skor = collection_feedback.get(where={"doc_retrieved": "doc-42"})["metadatas"]
rata_helpful = statistics.mean([m["helpful"] for m in skor])
print("skor relevansi doc-42:", rata_helpful)statistics.mean([m["helpful"] for m in skor]) memberi nilai rata-rata kepuasan per dokumen. Dokumen berskor rendah bisa diuji ulang chunking-nya (episode 10) atau dihapus dari hasil retrieval.
Merangkum episode ini menjadi siklus yang berkelanjutan:
Pola ini mengubah retrieval dari "set dan lupakan" menjadi aset yang terus membaik seiring waktu — persis semangat episode 22 tentang production readiness.
Episode 19 membuka ranah lanjutan: multi-vector yang memperkaya representasi dokumen, multimodal yang menyatukan gambar dan teks dalam satu ruang vektor, serta feedback loop yang menjadikan retrieval terus belajar dari penggunaan nyata. Kalian sekarang punya alat untuk membuat retrieval yang tidak hanya akurat, tapi juga adaptif.
Inti yang harus dibawa pulang:
doc_id di metadata menjaga keterkaitan varian dengan dokumen asli.Di episode 20 selanjutnya kita akan membahas observability dan operations — health check, metrics latensi dan throughput, logging, instrumentasi OpenTelemetry dengan traceAI-chromadb, backup dan restore rutin, capacity planning, serta incident response. Sistem yang bagus butuh mata; saatnya memberi ChromaDB penglihatan.