Membangun RAG (Retrieval-Augmented Generation) dari nol: chunking, indexing, retrieval, dan generation yang grounded pada dokumen. Episode ini juga memandu pipeline PDF/website ke Q&A serta mengevaluasi kualitas retrieval (recall) dan kualitas jawaban terhadap dokumen sumber.

Setelah di episode 6 kalian menguasai embeddings dan vector databases, episode ini merangkai semuanya menjadi satu pipeline yang mengubah cara aplikasi AI bekerja: RAG (Retrieval-Augmented Generation). RAG adalah alasan mengapa aplikasi AI bisa menjawab pertanyaan tentang data internal perusahaan tanpa fine-tuning — dan pada 2026 ia sudah menjadi standar produksi, bukan sekadar teknik eksperimental.
Mengapa ini penting? Karena LLM polos punya dua masalah fatal di produksi: ia tidak tahu data privat kita, dan ia berhalusinasi. RAG menyelesaikan keduanya sekaligus: kita memberi model konteks yang relevan (dari dokumen kita), sehingga jawabannya grounded — bisa diverifikasi terhadap sumber.
Empat tahapnya:
konteks + pertanyaan.Tahap 1 dilakukan sekali (offline); tahap 2-4 terjadi setiap query.
Chunking adalah langkah yang paling sering diremehkan, padahal menentukan segalanya. Chunk terlalu besar → top-K memuat banyak teks tidak relevan; chunk terlalu kecil → konteks terpotong.
def chunk_text(text, size=500, overlap=50):
chunks = []
start = 0
while start < len(text):
end = min(start + size, len(text))
chunks.append(text[start:end])
start = end - overlap
return chunks
paragraphs = chunk_text(open("manual.txt").read())
print(len(paragraphs), "chunk", "contoh:", paragraphs[0][:80])Kombinasi yang biasa dipakai di produksi:
Aturan praktis: mulailah dengan 300-800 karakter + overlap 10-20%. Ukur hasilnya, jangan menebak.
Gunakan vector DB dari episode 6 — simpan chunk, embedding, dan metadata (sumber dokumen, halaman, judul bagian) sekaligus:
import chromadb
from openai import OpenAI
client = OpenAI()
vdb = chromadb.PersistentClient(path="./rag-db")
col = vdb.get_or_create_collection("manual")
ids, vectors, metas, texts = [], [], [], []
for i, chunk in enumerate(chunks):
ids.append(f"chunk-{i}")
vectors.append(client.embeddings.create(
model="text-embedding-3-small", input=chunk).data[0].embedding)
metas.append({"source": "manual.txt", "chunk": i})
texts.append(chunk)
col.add(ids=ids, embeddings=vectors, metadatas=metas, documents=texts)Metadata source dan chunk ini penting: nanti dipakai untuk citation — pengguna harus bisa melihat dari dokumen mana jawaban berasal.
Saat query masuk, kita ambil K chunk paling relevan lalu susun prompt yang memaksa model menjawab dari konteks:
def ask_rag(question, k=4):
qv = client.embeddings.create(
model="text-embedding-3-small", input=question).data[0].embedding
hits = col.query(query_embeddings=[qv], n_results=k)
context = "\n\n---\n\n".join(
f"[{m['source']} #{m['chunk']}]\n{d}"
for d, m in zip(hits["documents"][0], hits["metadatas"][0])
)
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system",
"content": "Jawab HANYA berdasarkan konteks berikut. "
"Jika jawaban tidak ada di konteks, katakan "
"'Tidak ditemukan di dokumen'. Kutip sumber: [source #chunk]."},
{"role": "user", "content": f"Konteks:\n{context}\n\nPertanyaan: {question}"},
],
)
return resp.choices[0].message.content
print(ask_rag("Bagaimana cara reset password?"))Perhatikan dua elemen kunci di system prompt: batasan ("hanya berdasarkan konteks") dan citation ("kutip sumber"). Ini fondasi grounded answer — dan pencegah hallucination utama.
Warning
Jangan pernah mengabaikan evaluasi retrieval hanya karena jawaban "terlihat benar". Retrieval yang jelek menghasilkan jawaban yang terlihat meyakinkan tapi salah sumber. Selalu cek: apakah chunk yang diambil benar-benar memuat jawabannya? Evaluasinya di bagian berikut.
Seberapa baik kita menemukan dokumen yang relevan? Dua metrik standar:
Apakah jawaban setia pada konteks? Dua istilah yang wajib dikenal:
Cara praktis mengukurnya tanpa framework: minta LLM judge menilai (pola dari episode 4), atau cek manual dengan golden set kecil. Framework otomatis (RAGAS, DeepEval) kita pakai di episode 15.
judge = client.chat.completions.create(
model="gpt-4o-mini",
response_format={"type": "json_object"},
messages=[{"role": "system",
"content": "Nilai faithfulness jawaban terhadap konteks. "
"Skor 1-5 + daftar klaim yang tidak didukung konteks."},
{"role": "user",
"content": f"Konteks: {context}\nJawaban: {answer}"}],
)
print(judge.choices[0].message.content)Inti yang harus dibawa pulang:
Di episode 8 selanjutnya kita akan mendalami RAG Lanjutan — strategi chunking tingkat lanjut, reranking, GraphRAG, parent-child document, plus multimodal RAG: image/table extraction, vision RAG, dan integrasi OCR. Sampai jumpa di episode 8!