Mengubah teks menjadi vektor: OpenAIEmbeddings dan HuggingFaceEmbeddings, dimensi serta normalisasi, lalu menyimpan index ke Chroma, FAISS, pgvector, dan Qdrant dengan add_documents dan persist index.

Di episode 9 kalian berhasil mengubah PDF, web, CSV, dan JSON menjadi chunks yang rapi. Tapi chunks itu masih berupa teks — mesin pencari harus bisa menemukannya berdasarkan makna, bukan sekadar kata kunci. Di sinilah embeddings dan vector stores berperan.
Episode ini membangun lapisan penyimpanan pipeline RAG. Pertama, embeddings: bagaimana OpenAIEmbeddings dan HuggingFaceEmbeddings mengubah teks menjadi vektor angka, plus konsep dimensi dan normalisasi. Kedua, vector stores: menyimpan dan mencari vektor tersebut di Chroma, FAISS, pgvector, dan Qdrant, lengkap dengan add_documents dan persist index.
Embedding adalah representasi teks sebagai vektor bilangan riil. Kalimat yang bermakna mirip akan menghasilkan vektor yang berdekatan dalam ruang berdimensi tinggi, sehingga kemiripan makna bisa diukur dengan kemiripan vektor — biasanya lewat cosine similarity.
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vektor = embeddings.embed_query("Apa itu retrieval augmented generation?")
print(len(vektor)) # dimensi vektor
print(vektor[:5]) # sebagian isi vektorembeddings.embed_query(...) menghasilkan satu vektor untuk satu teks. Ada juga embed_documents untuk batch. Panjang list itulah yang disebut dimensi — semakin besar, semakin banyak nuansa yang bisa ditangkap, tapi semakin mahal biaya penyimpanan dan komputasinya.
Dua keluarga embedder paling umum: yang ter-hosting dan yang berjalan lokal.
from langchain_huggingface import HuggingFaceEmbeddings
# Lokal, gratis, tanpa API key
huggingface = HuggingFaceEmbeddings(
model_name="sentence-transformers/all-MiniLM-L6-v2"
)
vektor_lokal = huggingface.embed_query("Halo dunia")
print(len(vektor_lokal))Pilihan embedder memengaruhi segalanya: dimensi vektor, kualitas retrieval, biaya, dan latensi. Aturan praktis: prototipe cepat dengan embedder lokal berukuran kecil, lalu ukur kualitas retrieval sebelum memutuskan naik ke embedder berbayar yang lebih besar.
Dimensi vektor ditentukan oleh model embedder. text-embedding-3-small menghasilkan 1536 dimensi, all-MiniLM-L6-v2 menghasilkan 384 dimensi. Dimensi besar menangkap lebih banyak konteks tetapi memakan lebih banyak ruang penyimpanan — pertimbangan penting saat data mencapai jutaan chunk.
Normalisasi berarti mengubah vektor agar panjangnya satu satuan. Vektor ternormalisasi membuat perbandingan via dot product setara dengan cosine similarity, dan beberapa vector store memanfaatkannya untuk pencarian lebih cepat.
import numpy as np
vektor = np.array([0.2, 0.5, -0.3])
normalized = vektor / np.linalg.norm(vektor)
print(normalized)Banyak provider mengembalikan vektor yang sudah ternormalisasi secara default. Yang penting dipahami: jika kalian menormalisasi manual dan vector store juga menormalkan, hasilnya bisa berubah dua kali — pastikan konsisten antara sisi embedding dan sisi index.
Vector store menyimpan vektor dan menyediakan pencarian berdasarkan kemiripan. Chroma adalah pilihan paling mudah untuk memulai: berjalan lokal, persisten ke direktori, dan tanpa server terpisah.
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db",
)
print(vectorstore._collection.count())from_documents menerima list Document dari episode 9, meng-embed teksnya, lalu menyimpannya di ./chroma_db. Satu langkah menggantikan dua operasi manual: embedding dan insert.
Setiap vector store punya kelebihan berbeda. FAISS adalah pustaka pencarian vektor dari Meta — cepat, ringan, dan bisa disimpan sebagai satu file index lokal.
from langchain_community.vectorstores import FAISS
faiss_store = FAISS.from_documents(chunks, embeddings)
faiss_store.save_local("faiss_index")
faiss_store = FAISS.load_local(
"faiss_index",
embeddings,
allow_dangerous_deserialization=True,
)save_local menulis index ke folder, load_local memuatnya kembali — praktis untuk aplikasi tanpa server vector terpisah. pgvector menyimpan vektor sebagai kolom di PostgreSQL, menyatu dengan data relasional kalian; Qdrant menawarkan server terdedikasi dengan filtering dan scaling yang kuat untuk produksi besar.
Warning
FAISS menggunakan pickle saat menyimpan index — selalu set allow_dangerous_deserialization=True hanya jika index berasal dari sumber tepercaya. Jangan memuat index yang diunduh dari internet.
Dalam produksi, data bertambah terus-menerus. add_documents memungkinkan penyisipan inkremental tanpa membangun ulang seluruh index.
dokumen_baru = [{"page_content": "Bab 2: RAG di produksi", "metadata": {"sumber": "buku.md"}}]
vectorstore.add_documents(dokumen_baru)
vectorstore.persist() # untuk Chroma, simpan perubahan ke diskPola umum: bangun index awal lewat from_documents saat pertama kali, lalu gunakan add_documents untuk pembaruan harian atau penambahan batch baru. Untuk Chroma, persist() menulis perubahan ke direktori; FAISS cukup menyimpan ulang lewat save_local bila perlu.
Lapisan penyimpanan pipeline RAG sudah berdiri. Kalian bisa mengubah chunks dari episode 9 menjadi vektor dengan OpenAIEmbeddings atau HuggingFaceEmbeddings, memahami dampak dimensi dan normalisasi, lalu menyimpan index ke Chroma, FAISS, pgvector, atau Qdrant dengan add_documents dan persist. Yang tersisa hanyalah mencari — dan itulah tema episode berikutnya.
Inti yang harus dibawa pulang:
OpenAIEmbeddings untuk produksi ter-hosting, HuggingFaceEmbeddings untuk lokal dan gratis.add_documents untuk pertambahan inkremental; persist index secara berkala agar data tidak hilang.Di episode 11 kalian memakai semua yang sudah dibangun: Retrieval & RAG Dasar — VectorStoreRetriever, similarity_search dengan top-k dan filter metadata, merangkai pipeline retrieve-prompt-generate dengan LCEL, serta mengevaluasi kualitas jawaban. Sampai jumpa!