LESSON 04 / 05
Membangun Pipeline RAG Sederhana
20 menit bacaRAG dari Nol
Pipeline minimal yang jujur
// 1. INGEST (sekali / saat dokumen berubah)
for (const doc of docs) {
for (const chunk of chunkDocument(doc)) {
await vectors.insert({
embedding: await embed(chunk.text),
metadata: { docId: doc.id, title: doc.title, heading: chunk.heading },
});
}
}
// 2. RETRIEVE (tiap pertanyaan)
const qvec = await embed(question);
const hits = await vectors.search({ vector: qvec, topK: 5, filter: allowedDocs });
// 3. GENERATE dengan sitasi
const answer = await llm.chat([
{ role: "system", content: `Jawab HANYA berdasarkan konteks berikut. Sertakan sitasi [n] untuk tiap klaim. Jika konteks tidak cukup, katakan begitu.` },
{ role: "user", content: formatContext(hits) + "\n\n" + question },
]);
Keputusan desain yang menentukan
- Top-k: mulai dari 4–6. Lebih banyak bukan lebih baik — noise ikut masuk.
- Reranker: model kecil yang mengurutkan ulang 50 kandidat → ambil 5 teratas. Gain besar dengan biaya kecil.
- Sitasi wajib: memaksa model menunjuk sumber membuat halusinasi mudah terdeteksi dan jawaban mudah diaudit.
- Refusal path: desain jawaban untuk "konteks tidak cukup" — RAG yang tidak bisa bilang "tidak tahu" akan mengarang.
Error yang pasti kamu temui
- Potongan terpotong di tengah tabel → perbaiki chunking struktural.
- Jawaban benar tapi sitasi salah urut → kunci format dengan few-shot.
- Pertanyaan berbahasa Indonesia, dokumen Inggris (atau sebaliknya) → pastikan embedding multibahasa + query translation bila perlu.