Pernah excited bangun AI chatbot untuk bisnis, eh malah kasih info salah ke customer? Artikel ini bahas RAG (Retrieval Augmented Generation) — teknik yang bikin AI kamu jawab berdasarkan DATA NYATA, bukan halusinasi. Dari konsep dasar sampai setup knowledge base yang efektif.
Bagian 1: Masalah Besar AI yang Jarang Dibahas
Cerita yang Mungkin Familiar
Bayangkan skenario ini:
Kamu baru launch AI chatbot untuk customer support. Excited banget karena akhirnya bisa automate jawab pertanyaan customer 24/7. Hari pertama berjalan lancar. Hari kedua, ada customer yang komplain:
"Chatbot kalian bilang garansi produk 2 tahun. Pas saya klaim, CS bilang cuma 1 tahun. Ini gimana?"
Kamu cek. Ternyata AI-nya ngarang. Di dokumen official jelas tertulis garansi 1 tahun. Tapi AI dengan confident-nya bilang 2 tahun.
Ini bukan bug. Ini bukan error. Ini hallucination — fenomena di mana AI mengarang informasi yang kedengarannya benar, tapi sebenarnya salah.
Dan ini terjadi lebih sering dari yang kamu kira.
Hallucination: Masalah Serius yang Sering Di-ignore
CONTOH HALLUCINATION DI PRODUCTION:
❌ Customer support bot kasih info harga yang salah
→ Customer marah, trust hilang
❌ Legal assistant cite pasal hukum yang tidak exist
→ Keputusan hukum berdasarkan info palsu
❌ Medical chatbot kasih saran kesehatan ngawur
→ Potentially dangerous
❌ Internal assistant kasih policy perusahaan yang outdated
→ Keputusan bisnis salah
❌ Coding assistant suggest library yang tidak ada
→ Developer buang waktu debug "hallucinated code"
Hallucination bukan cuma masalah akurasi. Ini masalah trust. Sekali user tau AI kamu bisa ngawur, mereka akan selalu doubt semua jawaban — bahkan yang benar sekalipun.
Kenapa AI Bisa Hallucinate?
Untuk understand solusinya, kita perlu tau dulu kenapa masalah ini terjadi.
LLM (Large Language Model) seperti ChatGPT, Claude, atau Gemini itu pada dasarnya adalah pattern matching machine yang sangat canggih. Mereka di-train dengan triliunan text dari internet, buku, artikel, dan berbagai sumber lain.
Tapi ada beberapa limitasi fundamental:
LIMITASI LLM:
1. KNOWLEDGE CUTOFF
└── Training data punya batas waktu
└── GPT-4: Data sampai April 2023
└── Claude: Data sampai awal 2024
└── Info setelah itu? AI gak tau
2. TIDAK PUNYA AKSES KE DATA SPESIFIK
└── AI gak tau dokumen internal perusahaan kamu
└── AI gak tau FAQ produk kamu
└── AI gak tau policy terbaru kamu
└── AI gak tau database customer kamu
3. "CONFIDENT IGNORANCE"
└── Kalau AI gak tau, dia GAK BILANG gak tau
└── Instead, dia "generate" jawaban yang plausible
└── Jawaban kedengarannya masuk akal
└── Tapi sebenarnya NGARANG
4. NO SOURCE OF TRUTH
└── AI gak punya referensi untuk cross-check
└── Dia jawab berdasarkan "feeling" pattern
└── Gak ada citation, gak ada verification
Ini ibarat punya karyawan yang sangat pintar dan percaya diri, tapi:
- Dia gak punya akses ke file-file penting perusahaan
- Pengetahuannya berhenti di tahun lalu
- Kalau ditanya sesuatu yang gak tau, instead of bilang "saya gak tau", dia akan ngarang dengan sangat meyakinkan
Nightmare, kan?
Root Cause: AI Jawab dari "Ingatan", Bukan dari Data
Mari kita lihat bagaimana LLM biasa menjawab pertanyaan:
FLOW LLM BIASA:
User: "Berapa harga paket premium BuildWithAngga?"
AI Process:
├── Check "ingatan" (training data)
├── Cari pattern yang relevan tentang "harga" dan "BuildWithAngga"
├── Kalau ada di training data → jawab
├── Kalau TIDAK ada → generate jawaban yang "masuk akal"
└── Output dengan confidence tinggi regardless
Result:
├── Mungkin benar (kalau ada di training data)
├── Mungkin outdated (kalau data lama)
└── Mungkin NGARANG (kalau gak tau)
Masalahnya: kamu gak tau which one kamu dapat.
AI gak bilang "ini dari training data" atau "ini saya ngarang". Semua dijawab dengan confidence yang sama.
Solusinya: Gimana Kalau AI Bisa "Baca" Dulu Sebelum Jawab?
Sekarang bayangkan skenario berbeda:
FLOW YANG IDEAL:
User: "Berapa harga paket premium BuildWithAngga?"
AI Process:
├── SEBELUM jawab, cari dokumen yang relevan
├── Temukan: "pricing-2024.pdf" → Paket Premium: Rp 299.000
├── Baca dan pahami dokumen tersebut
├── Jawab BERDASARKAN dokumen yang ditemukan
└── Sertakan referensi: "Berdasarkan pricing-2024.pdf..."
Result:
├── Jawaban akurat karena dari source yang valid
├── Bisa di-verify karena ada citation
└── Bisa di-update dengan update dokumen
Ini exactly yang RAG (Retrieval Augmented Generation) lakukan.
RAG adalah teknik yang memungkinkan AI untuk "baca dokumen dulu, baru jawab" — bukan mengandalkan "ingatan" yang bisa outdated atau salah.
Apa yang Akan Kamu Pelajari di Artikel Ini
Artikel ini akan cover A-Z tentang RAG:
ROADMAP ARTIKEL:
Bagian 1: Masalah AI Hallucination ✓ (kamu di sini)
├── Kenapa AI bisa ngawur
└── Kenapa ini masalah serius
Bagian 2: Apa Itu RAG
├── Definisi yang gampang dipahami
├── Cara kerja step-by-step
└── Comparison dengan approaches lain
Bagian 3: Setup Knowledge Base
├── Pilih dan siapkan dokumen
├── Chunking strategies
├── Embeddings dan vector database
└── Build retrieval pipeline
Bagian 4: Prompt Engineering untuk RAG
├── Prompt structure khusus RAG
├── Rules untuk prevent hallucination
└── Templates copy-paste ready
Bagian 5: Testing dan Monitoring
├── Metrics yang harus di-track
├── Testing strategies
├── Troubleshooting common issues
Bagian 6: Action Plan
├── Week-by-week implementation guide
├── Tech stack recommendations
└── Resources untuk lanjut belajar
Siapa yang Harus Baca Artikel Ini?
Artikel ini untuk kamu yang:
- AI Product Engineer yang mau build AI products yang reliable dan trustworthy
- Developer yang integrate AI ke aplikasi dan frustrated dengan hallucination
- Founder/Product Manager yang mau AI untuk customer support atau internal tools
- Anyone yang mau AI-nya jawab berdasarkan FAKTA, bukan khayalan
Kalau kamu pernah deploy AI dan kena masalah hallucination — atau mau prevent itu terjadi sebelum launch — artikel ini untuk kamu.
Mari kita mulai dengan understanding apa sebenarnya RAG itu.
Bagian 2: Apa Itu RAG dan Cara Kerjanya
Definisi yang Gampang Dipahami
RAG stands for Retrieval Augmented Generation.
Kalau diterjemahkan secara harfiah:
- Retrieval = Mengambil/mencari informasi
- Augmented = Diperkaya/ditambahkan
- Generation = Pembuatan respons
Tapi definisi yang lebih gampang dipahami:
RAG adalah teknik di mana AI "baca dokumen dulu, baru jawab" — instead of mengandalkan ingatan yang bisa salah.
Atau lebih singkat lagi:
RAG = AI yang humble. Dia cek referensi dulu sebelum ngomong.
Berbeda dengan LLM biasa yang langsung jawab dari "ingatan" (training data), RAG punya step tambahan: cari dan baca dokumen yang relevan SEBELUM generate jawaban.
Analogi: Open Book Exam vs Closed Book Exam
Cara paling gampang untuk understand RAG adalah dengan analogi ujian:
CLOSED BOOK EXAM (LLM Biasa):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
├── Tidak boleh buka catatan
├── Andalkan ingatan saja
├── Kalau lupa? Tebak-tebakan
├── Confidence level: Sama semua
└── Hasil: Bisa benar, bisa ngawur
OPEN BOOK EXAM (RAG):
━━━━━━━━━━━━━━━━━━━━━━
├── Boleh buka catatan/referensi
├── Cari halaman yang relevan dulu
├── Jawab berdasarkan referensi
├── Confidence level: Based on source quality
└── Hasil: Lebih akurat, bisa di-verify
Dalam open book exam, kamu tetap perlu PINTAR untuk:
- Tau referensi mana yang relevan
- Menemukan informasi dengan cepat
- Synthesize informasi jadi jawaban yang koheren
Tapi kamu gak perlu HAFAL semua. Kamu punya "contekan" yang legitimate.
RAG memberikan "contekan" yang sama ke AI — dalam bentuk knowledge base yang berisi dokumen-dokumen kamu.
Cara Kerja RAG: 4 Step Flow
Mari kita breakdown cara kerja RAG step by step:
RAG FLOW:
┌─────────────────────────────────────────────────────────────────┐
│ USER QUERY │
│ "Bagaimana cara refund pesanan?" │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ STEP 1: RETRIEVAL │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Cari dokumen yang RELEVAN dengan pertanyaan user │ │
│ │ │ │
│ │ Query: "cara refund pesanan" │ │
│ │ ↓ │ │
│ │ Search di Knowledge Base (vector similarity) │ │
│ │ ↓ │ │
│ │ Found: │ │
│ │ ├── refund-policy.pdf (score: 0.92) │ │
│ │ ├── faq-returns.md (score: 0.87) │ │
│ │ └── customer-service-guide.pdf (score: 0.76) │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ STEP 2: AUGMENTATION │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Gabungkan dokumen yang ditemukan dengan prompt │ │
│ │ │ │
│ │ CONTEXT: │ │
│ │ """ │ │
│ │ [Dari refund-policy.pdf] │ │
│ │ Refund dapat diajukan dalam 14 hari setelah pembelian. │ │
│ │ Caranya: 1) Buka menu Pesanan 2) Klik Ajukan Refund... │ │
│ │ │ │
│ │ [Dari faq-returns.md] │ │
│ │ Q: Berapa lama proses refund? │ │
│ │ A: 3-5 hari kerja setelah approval... │ │
│ │ """ │ │
│ │ │ │
│ │ USER QUESTION: "Bagaimana cara refund pesanan?" │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ STEP 3: GENERATION │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ LLM generate jawaban BERDASARKAN context │ │
│ │ │ │
│ │ AI reads context + understands question │ │
│ │ ↓ │ │
│ │ AI generates answer using ONLY the provided context │ │
│ │ ↓ │ │
│ │ Output: Structured, accurate response │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ FINAL RESPONSE │
│ │
│ "Untuk melakukan refund pesanan, ikuti langkah berikut: │
│ │
│ 1. Buka menu 'Pesanan Saya' │
│ 2. Pilih pesanan yang ingin di-refund │
│ 3. Klik tombol 'Ajukan Refund' │
│ 4. Pilih alasan refund │
│ 5. Submit pengajuan │
│ │
│ Catatan: Refund dapat diajukan dalam 14 hari setelah │
│ pembelian. Proses refund memakan waktu 3-5 hari kerja │
│ setelah pengajuan disetujui. │
│ │
│ Sumber: refund-policy.pdf, faq-returns.md" │
└─────────────────────────────────────────────────────────────────┘
Key insight: AI tidak menjawab dari "ingatan". AI menjawab dari dokumen yang KAMU berikan. Kalau dokumen-nya akurat dan up-to-date, jawaban AI juga akurat dan up-to-date.
Komponen Utama RAG System
RAG system terdiri dari beberapa komponen yang bekerja bersama:
| Komponen | Fungsi | Analogi Perpustakaan |
|---|---|---|
| Knowledge Base | Tempat menyimpan semua dokumen/data | Koleksi buku di perpustakaan |
| Embedding Model | Mengubah text menjadi vector (angka) | Sistem katalog yang categorize buku |
| Vector Database | Menyimpan vectors dan enable similarity search | Rak buku yang terorganisir by topic |
| Retriever | Mencari dokumen yang relevan dengan query | Pustakawan yang carikan buku |
| LLM (Generator) | Generate jawaban dari context | Peneliti yang baca dan tulis laporan |
Setiap komponen punya peran penting. Kalau satu weak, seluruh system jadi kurang optimal.
RAG vs Fine-tuning: Kapan Pakai yang Mana?
Pertanyaan umum: "Kenapa gak fine-tune aja model-nya supaya tau informasi kita?"
Good question. Mari kita compare:
| Aspek | RAG | Fine-tuning |
|---|---|---|
| Setup Time | Jam - Hari | Hari - Minggu |
| Data Requirement | Dokumen dalam format apapun | Dataset format khusus (Q&A pairs) |
| Update Data | Mudah — tambah dokumen baru | Sulit — perlu re-train model |
| Cost | Lebih murah (hanya inference) | Mahal (training + inference) |
| Flexibility | Tinggi — ganti dokumen kapanpun | Rendah — locked dalam model |
| Transparency | Bisa cite sumber | Black box |
| Best For | Faktual info, data yang sering berubah | Behavior/style changes, specific tasks |
Kapan pakai RAG:
- Kamu punya dokumen/data yang perlu di-reference
- Data sering update (pricing, policy, product info)
- Kamu perlu transparency (cite sumber)
- Budget terbatas
- Mau deploy cepat
Kapan pakai Fine-tuning:
- Kamu mau ubah CARA AI menjawab (tone, style)
- Task sangat spesifik (classification, extraction)
- Data relatif stabil (gak sering berubah)
- Punya resource untuk training
Real talk: Untuk kebanyakan use case di production (customer support, internal assistant, document Q&A), RAG adalah pilihan yang lebih practical.
Real-World Use Cases
RAG bukan cuma teori. Ini sudah dipakai di banyak production systems:
USE CASES RAG DI PRODUCTION:
1. CUSTOMER SUPPORT BOT
├── Knowledge base: FAQ, product docs, troubleshooting guides
├── Benefit: Jawaban akurat sesuai official docs
└── Example: Intercom, Zendesk AI features
2. INTERNAL COMPANY ASSISTANT
├── Knowledge base: Policies, procedures, employee handbook
├── Benefit: Employees dapat info HR/policy dengan cepat
└── Example: Slack AI, Microsoft Copilot
3. LEGAL DOCUMENT ASSISTANT
├── Knowledge base: Contracts, regulations, case law
├── Benefit: Lawyers cari precedent dengan cepat
└── Example: Harvey AI, Casetext
4. MEDICAL INFORMATION SYSTEM
├── Knowledge base: Research papers, clinical guidelines
├── Benefit: Doctors akses latest research
└── Example: Various hospital systems
5. CODEBASE Q&A
├── Knowledge base: Code documentation, README, comments
├── Benefit: Developers onboard lebih cepat
└── Example: GitHub Copilot Chat, Sourcegraph
6. E-COMMERCE PRODUCT ASSISTANT
├── Knowledge base: Product catalog, specifications, reviews
├── Benefit: Customers dapat product recommendations akurat
└── Example: Amazon Rufus, various e-commerce bots
Semua use case di atas punya common pattern: Ada dokumen/data yang perlu di-reference untuk jawab pertanyaan dengan akurat.
Kalau use case kamu masuk pattern ini, RAG adalah solusi yang tepat.
Bagian 3: Cara Setup Knowledge Base yang Efektif
Knowledge Base: Fondasi yang Menentukan Segalanya
Ini bagian yang paling sering di-underestimate, tapi actually PALING PENTING.
Ada prinsip klasik di dunia data: Garbage In, Garbage Out.
Untuk RAG, ini bahkan lebih critical:
- Kalau knowledge base berantakan → retrieval gak akurat
- Kalau retrieval gak akurat → AI dapat context yang salah
- Kalau context salah → jawaban tetap ngawur
RAG gak bisa fix knowledge base yang jelek.
Jadi sebelum kita bahas technical implementation, kita perlu pastikan fondasi-nya solid.
Step 1: Pilih dan Siapkan Dokumen
Jenis dokumen yang bisa dipakai:
SUPPORTED DOCUMENT TYPES:
├── Text Documents
│ ├── PDF (manuals, reports, contracts)
│ ├── Word (.docx)
│ ├── Markdown (.md)
│ ├── Plain text (.txt)
│ └── Google Docs (export dulu)
│
├── Web Content
│ ├── HTML pages
│ ├── Blog posts
│ └── Documentation sites
│
├── Structured Data
│ ├── CSV/Excel (product catalogs, pricing)
│ ├── JSON (API responses, configs)
│ └── Database exports
│
└── Specialized
├── Code files (untuk codebase Q&A)
├── Presentations (extract text)
└── Emails (untuk support history)
Quality Checklist — WAJIB sebelum masuk ke knowledge base:
DOCUMENT QUALITY CHECKLIST:
ACCURACY
□ Apakah informasi di dokumen ini BENAR?
□ Kapan terakhir di-update?
□ Apakah masih relevan?
□ Ada yang contradictory dengan dokumen lain?
CLARITY
□ Apakah bahasanya jelas dan tidak ambiguous?
□ Apakah terminology consistent?
□ Apakah ada jargon yang perlu di-explain?
COMPLETENESS
□ Apakah informasi cukup lengkap untuk jawab pertanyaan?
□ Ada gap yang perlu di-fill?
□ Apakah context cukup jelas?
STRUCTURE
□ Apakah ada headers yang clear?
□ Apakah information terorganisir dengan baik?
□ Apakah mudah di-parse secara programmatic?
CLEANLINESS
□ Tidak ada typos yang bisa mislead?
□ Formatting consistent?
□ Tidak ada duplicate content?
Contoh: Setup Knowledge Base untuk Customer Support
Misalnya kamu mau build AI customer support untuk e-commerce:
KNOWLEDGE BASE STRUCTURE:
/knowledge-base
├── /products
│ ├── product-catalog.csv
│ ├── product-specifications.md
│ └── product-faqs.md
│
├── /policies
│ ├── refund-policy.pdf
│ ├── shipping-policy.pdf
│ ├── privacy-policy.pdf
│ └── terms-of-service.pdf
│
├── /support
│ ├── troubleshooting-guide.md
│ ├── common-issues.md
│ └── escalation-procedures.md
│
├── /pricing
│ ├── pricing-2024.csv
│ ├── promo-codes-active.md
│ └── subscription-plans.md
│
└── /company
├── about-us.md
├── contact-info.md
└── business-hours.md
Pro tip: Organize by TOPIC, bukan by format. Ini memudahkan maintenance dan update.
Step 2: Chunking — Potong Dokumen Jadi Bagian Kecil
Dokumen kamu mungkin panjang — bisa puluhan atau ratusan halaman. Tapi AI punya batasan context window dan retrieval works better dengan chunks yang focused.
Kenapa perlu chunking?
KENAPA CHUNKING PENTING:
1. CONTEXT WINDOW LIMIT
├── Claude: ~100K-200K tokens (besar, tapi tetap ada limit)
├── GPT-4: ~128K tokens
└── Kalau dokumen kamu 500 halaman? Gak muat semua
2. RETRIEVAL ACCURACY
├── Retrieve 1 dokumen 100 halaman → banyak noise
├── Retrieve 3 chunks @ 500 kata → focused, relevant
└── Smaller chunks = more precise matching
3. COST EFFICIENCY
├── More tokens = more expensive
├── Retrieve only what's needed
└── Avoid paying for irrelevant context
4. RESPONSE QUALITY
├── Terlalu banyak context → AI bingung prioritize
├── Focused context → clear, concise answers
└── Less noise = better signal
Chunking Strategies:
| Strategy | Cara Kerja | Best For | Chunk Size |
|---|---|---|---|
| Fixed Size | Potong per N karakter/tokens | General purpose, simple setup | 500-1000 tokens |
| Paragraph | Potong per paragraf | Articles, blogs, narrative content | Varies |
| Semantic | Potong by topic/section | Technical docs, manuals | 500-1500 tokens |
| Sentence | Potong per kalimat | Q&A datasets, short content | 1-3 sentences |
| Recursive | Split by headers, then paragraphs, then sentences | Structured documents | 500-1000 tokens |
Recommended approach untuk kebanyakan kasus: Recursive dengan overlap
RECURSIVE CHUNKING EXAMPLE:
Original Document (refund-policy.pdf):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Kebijakan Refund
## 1. Syarat Pengajuan Refund
Refund dapat diajukan dalam 14 hari setelah pembelian.
Produk harus dalam kondisi original dengan packaging lengkap.
Bukti pembelian (invoice/receipt) wajib disertakan.
## 2. Cara Mengajukan Refund
Buka halaman "Pesanan Saya" di aplikasi atau website.
Pilih pesanan yang ingin di-refund.
Klik tombol "Ajukan Refund" dan pilih alasan.
Upload foto produk jika diminta.
Submit pengajuan.
## 3. Proses Refund
Pengajuan akan di-review dalam 1-2 hari kerja.
Jika disetujui, dana akan dikembalikan dalam 3-5 hari kerja.
Metode pengembalian sesuai metode pembayaran awal.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
After Chunking (with 20% overlap):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CHUNK 1:
"# Kebijakan Refund
## 1. Syarat Pengajuan Refund
Refund dapat diajukan dalam 14 hari setelah pembelian.
Produk harus dalam kondisi original dengan packaging lengkap.
Bukti pembelian (invoice/receipt) wajib disertakan."
CHUNK 2:
"Bukti pembelian (invoice/receipt) wajib disertakan. ← overlap
## 2. Cara Mengajukan Refund
Buka halaman "Pesanan Saya" di aplikasi atau website.
Pilih pesanan yang ingin di-refund.
Klik tombol "Ajukan Refund" dan pilih alasan.
Upload foto produk jika diminta.
Submit pengajuan."
CHUNK 3:
"Upload foto produk jika diminta. ← overlap
Submit pengajuan.
## 3. Proses Refund
Pengajuan akan di-review dalam 1-2 hari kerja.
Jika disetujui, dana akan dikembalikan dalam 3-5 hari kerja.
Metode pengembalian sesuai metode pembayaran awal."
Kenapa overlap penting?
Tanpa overlap, informasi penting bisa "terpotong" di boundary:
TANPA OVERLAP (PROBLEM):
─────────────────────────
Chunk 1: "...garansi berlaku selama"
Chunk 2: "2 tahun sejak pembelian..."
User tanya: "Berapa lama garansi?"
Retrieved: Chunk 1 (gak lengkap!) atau Chunk 2 (gak lengkap!)
DENGAN OVERLAP (SOLVED):
────────────────────────
Chunk 1: "...garansi berlaku selama 2 tahun"
Chunk 2: "Garansi berlaku selama 2 tahun sejak pembelian..."
User tanya: "Berapa lama garansi?"
Retrieved: Either chunk gives complete answer!
Recommended settings:
- Chunk size: 500-1000 tokens
- Overlap: 10-20% of chunk size
- Include metadata: source file, section header, page number
Step 3: Generate Embeddings
Sekarang kita punya chunks. Tapi bagaimana computer bisa "understand" text dan cari yang similar?
Jawabannya: Embeddings.
Apa itu embedding?
EMBEDDING EXPLAINED:
Text → Vector (angka)
─────────────────────────────────────────────────
"cara refund pesanan" → [0.12, -0.45, 0.78, ..., 0.33]
(1536 dimensi untuk OpenAI)
"bagaimana mengembalikan → [0.11, -0.43, 0.80, ..., 0.31]
barang yang dibeli" (MIRIP dengan vector di atas!)
"cuaca hari ini cerah" → [0.89, 0.23, -0.56, ..., -0.12]
(BERBEDA jauh dari dua vector di atas)
Key insight: Text yang SECARA MAKNA mirip akan punya vector yang DEKAT satu sama lain.
Ini yang memungkinkan "semantic search" — cari berdasarkan MEANING, bukan exact keyword match.
TRADITIONAL SEARCH vs SEMANTIC SEARCH:
Traditional (keyword matching):
──────────────────────────────
Query: "cara refund"
Matches: Documents containing exact words "cara" AND "refund"
Problem: Miss "proses pengembalian dana" (same meaning, different words)
Semantic Search (embedding-based):
──────────────────────────────────
Query: "cara refund" → [0.12, -0.45, ...]
Compare with all chunk embeddings
Return chunks with CLOSEST vectors
Result: Finds "proses pengembalian dana" too! (similar meaning = similar vector)
Embedding Models Populer:
| Model | Provider | Dimensi | Harga | Best For |
|---|---|---|---|---|
| text-embedding-3-small | OpenAI | 1536 | $0.02/1M tokens | General purpose, cost-effective |
| text-embedding-3-large | OpenAI | 3072 | $0.13/1M tokens | Higher accuracy needs |
| voyage-2 | Voyage AI | 1024 | $0.10/1M tokens | Code, technical docs |
| BGE-M3 | BAAI | 1024 | Free (open source) | Multilingual, self-hosted |
| Cohere embed-v3 | Cohere | 1024 | $0.10/1M tokens | Multilingual |
Recommendation: Start dengan text-embedding-3-small dari OpenAI. Good balance antara quality, cost, dan ease of use.
Code Example: Generate Embeddings
// Dengan OpenAI SDK
import OpenAI from 'openai';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
async function generateEmbedding(text) {
const response = await openai.embeddings.create({
model: "text-embedding-3-small",
input: text,
});
return response.data[0].embedding; // Array of 1536 numbers
}
// Generate embeddings untuk semua chunks
async function embedAllChunks(chunks) {
const embeddings = [];
for (const chunk of chunks) {
const embedding = await generateEmbedding(chunk.content);
embeddings.push({
content: chunk.content,
embedding: embedding,
metadata: chunk.metadata // source, page, etc
});
}
return embeddings;
}
Step 4: Setup Vector Database
Embeddings sudah di-generate. Sekarang kita perlu tempat untuk MENYIMPAN dan MENCARI vectors dengan cepat.
Regular database (PostgreSQL, MySQL) tidak optimized untuk vector similarity search. Kita butuh vector database.
Vector Database Options:
| Database | Type | Ease of Setup | Free Tier | Best For |
|---|---|---|---|---|
| Supabase pgvector | Managed PostgreSQL + vector | ⭐⭐⭐⭐⭐ | Generous | Full-stack apps, sudah pakai Supabase |
| Pinecone | Managed vector DB | ⭐⭐⭐⭐ | Limited | Production scale, dedicated vector DB |
| Weaviate | Self-host / Cloud | ⭐⭐⭐ | Yes | Flexibility, advanced features |
| Chroma | Local / embedded | ⭐⭐⭐⭐⭐ | Free | Development, prototyping |
| Qdrant | Self-host / Cloud | ⭐⭐⭐ | Yes | High performance needs |
Recommendation untuk mulai: Supabase dengan pgvector
Kenapa?
- Kamu dapat PostgreSQL (bisa untuk data lain juga)
- Setup mudah (enable extension, done)
- Free tier generous
- Familiar SQL syntax
- Bagus untuk production
Setup Supabase pgvector:
-- 1. Enable vector extension
create extension if not exists vector;
-- 2. Create table untuk documents
create table documents (
id bigserial primary key,
content text not null,
embedding vector(1536), -- dimensi sesuai model
metadata jsonb,
created_at timestamp with time zone default now()
);
-- 3. Create index untuk faster search
create index on documents
using ivfflat (embedding vector_cosine_ops)
with (lists = 100);
Insert documents dengan embeddings:
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_KEY
);
async function insertDocument(content, embedding, metadata) {
const { data, error } = await supabase
.from('documents')
.insert({
content: content,
embedding: embedding,
metadata: metadata
});
if (error) throw error;
return data;
}
Step 5: Build Retrieval Pipeline
Sekarang semua komponen ready. Mari kita gabungkan menjadi retrieval pipeline yang working.
Complete Retrieval Flow:
// retrieval.js
import OpenAI from 'openai';
import { createClient } from '@supabase/supabase-js';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const supabase = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_KEY
);
// Step 1: Generate embedding untuk user query
async function getQueryEmbedding(query) {
const response = await openai.embeddings.create({
model: "text-embedding-3-small",
input: query,
});
return response.data[0].embedding;
}
// Step 2: Search similar documents di Supabase
async function searchDocuments(queryEmbedding, topK = 5) {
const { data, error } = await supabase.rpc('match_documents', {
query_embedding: queryEmbedding,
match_threshold: 0.7, // minimum similarity score
match_count: topK
});
if (error) throw error;
return data;
}
// Step 3: Complete retrieval function
async function retrieveRelevantDocs(userQuery, topK = 5) {
// Generate embedding untuk query
const queryEmbedding = await getQueryEmbedding(userQuery);
// Search similar documents
const documents = await searchDocuments(queryEmbedding, topK);
// Return documents dengan content dan metadata
return documents.map(doc => ({
content: doc.content,
similarity: doc.similarity,
source: doc.metadata?.source || 'unknown'
}));
}
// Usage
const query = "Bagaimana cara refund pesanan?";
const relevantDocs = await retrieveRelevantDocs(query, 3);
console.log(relevantDocs);
Supabase function untuk similarity search:
-- Create function untuk match documents
create or replace function match_documents (
query_embedding vector(1536),
match_threshold float,
match_count int
)
returns table (
id bigint,
content text,
metadata jsonb,
similarity float
)
language sql stable
as $$
select
documents.id,
documents.content,
documents.metadata,
1 - (documents.embedding <=> query_embedding) as similarity
from documents
where 1 - (documents.embedding <=> query_embedding) > match_threshold
order by similarity desc
limit match_count;
$$;
Tuning Parameters:
RETRIEVAL PARAMETERS TO TUNE:
1. TOP K (berapa chunks di-retrieve)
├── Too low (1-2): Might miss relevant info
├── Too high (10+): Too much noise, expensive
└── Sweet spot: 3-5 untuk kebanyakan kasus
2. SIMILARITY THRESHOLD
├── Too high (0.9+): Miss slightly relevant docs
├── Too low (0.5): Include irrelevant docs
└── Sweet spot: 0.7-0.8
3. METADATA FILTERING
├── Filter by category: "WHERE metadata->>'category' = 'refund'"
├── Filter by date: "WHERE created_at > '2024-01-01'"
└── Use case: Scope search ke specific domain
Checkpoint: Knowledge Base Siap
Kalau kamu sudah sampai sini, kamu punya:
KNOWLEDGE BASE CHECKLIST:
✅ Documents tersusun rapi dan ter-curate
✅ Chunking strategy yang sesuai dengan content
✅ Embeddings ter-generate untuk semua chunks
✅ Vector database setup dan terisi
✅ Retrieval pipeline yang working
NEXT: Bagaimana cara pakai retrieved context
untuk generate accurate answers?
→ Lanjut ke Bagian 4: Prompt Engineering untuk RAG
Lanjut ke Bagian 4: Prompt Engineering untuk RAG →
Bagian 4: Prompt Engineering untuk RAG
RAG Butuh Prompt yang Berbeda
Kamu sudah punya retrieval pipeline yang working. Documents ter-retrieve dengan baik. Tapi ini baru setengah jalan.
Masalah yang sering terjadi:
SCENARIO YANG SERING TERJADI:
Retrieved Context:
"Refund dapat diajukan dalam 14 hari setelah pembelian."
User Question:
"Berapa lama waktu untuk refund?"
AI Response (dengan prompt biasa):
"Proses refund biasanya memakan waktu 7-14 hari kerja."
PROBLEM: AI IGNORE context dan jawab dari "ingatan" sendiri!
Padahal context bilang 14 hari untuk PENGAJUAN,
bukan proses refund-nya.
Ini terjadi karena prompt-nya tidak INSTRUCT AI untuk:
- Prioritize context yang diberikan
- Ignore "ingatan" yang mungkin bertentangan
- Admit kalau context tidak mengandung jawaban
Prompt engineering untuk RAG adalah seni memastikan AI PAKAI context yang kamu berikan.
Anatomy of RAG Prompt
Prompt untuk RAG punya struktur khusus:
RAG PROMPT STRUCTURE:
┌─────────────────────────────────────────────────────────────────┐
│ SYSTEM PROMPT │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. ROLE DEFINITION │
│ "Kamu adalah customer support assistant untuk [Company]" │
│ │
│ 2. CONTEXT INSTRUCTIONS │
│ "Jawab HANYA berdasarkan context yang diberikan" │
│ │
│ 3. BEHAVIORAL RULES │
│ "Jika context tidak mengandung jawaban, katakan tidak tau" │
│ │
│ 4. OUTPUT FORMAT │
│ "Jawab dengan bahasa Indonesia yang ramah dan jelas" │
│ │
│ 5. CITATION RULES (optional) │
│ "Sebutkan sumber informasi di akhir jawaban" │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ USER MESSAGE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. CONTEXT SECTION │
│ """ │
│ [Retrieved documents dari vector search] │
│ """ │
│ │
│ 2. USER QUESTION │
│ "Pertanyaan actual dari user" │
│ │
└─────────────────────────────────────────────────────────────────┘
Rules WAJIB di System Prompt
Ada beberapa rules yang HARUS ada di setiap RAG system prompt. Tanpa ini, AI akan tetap hallucinate.
5 RULES WAJIB UNTUK RAG PROMPT:
RULE 1: "Only answer based on the provided context"
────────────────────────────────────────────────────
Kenapa penting:
└── Tanpa ini, AI akan mix context dengan "ingatan"
└── Bisa dapat info yang outdated atau salah
Contoh gagal tanpa rule ini:
├── Context: "Harga paket Basic: Rp 99.000"
├── User: "Berapa harga paket Basic?"
└── AI: "Harga paket Basic sekitar Rp 150.000" (dari ingatan lama!)
RULE 2: "If the context doesn't contain the answer, say you don't know"
────────────────────────────────────────────────────────────────────────
Kenapa penting:
└── Mencegah AI ngarang kalau info tidak ada
└── Lebih baik jujur daripada confident tapi salah
Contoh gagal tanpa rule ini:
├── Context: [Info tentang refund policy]
├── User: "Jam berapa kantor buka?"
└── AI: "Kantor buka jam 9 pagi" (NGARANG! Info ini gak ada di context)
RULE 3: "Do not make up or infer information not explicitly stated"
─────────────────────────────────────────────────────────────────────
Kenapa penting:
└── AI suka "helpful" dengan menambahkan info
└── Tapi info tambahan ini bisa salah
Contoh gagal tanpa rule ini:
├── Context: "Refund diproses dalam 3-5 hari kerja"
├── User: "Kapan refund saya masuk?"
└── AI: "Refund akan masuk hari Jumat" (ASUMSI! Gak ada di context)
RULE 4: "If there are conflicting information, mention both"
──────────────────────────────────────────────────────────────
Kenapa penting:
└── Kadang knowledge base punya info yang conflict
└── AI harus transparent, bukan pilih salah satu
Contoh yang benar:
├── Context A: "Garansi 1 tahun"
├── Context B: "Garansi 2 tahun untuk member premium"
├── User: "Berapa lama garansi?"
└── AI: "Garansi standar 1 tahun. Untuk member premium, garansi 2 tahun."
RULE 5: "Cite your sources when possible"
──────────────────────────────────────────
Kenapa penting:
└── Membangun trust dengan user
└── User bisa verify kalau ragu
└── Memudahkan debugging kalau ada error
Contoh yang baik:
└── AI: "...Proses refund 3-5 hari kerja. (Sumber: refund-policy.pdf)"
Template RAG Prompt (Copy-Paste Ready)
Berikut template yang bisa langsung kamu pakai:
Template 1: Customer Support Bot
SYSTEM PROMPT:
─────────────────────────────────────────────────────────────────
Kamu adalah customer support assistant untuk [NAMA PERUSAHAAN].
INSTRUKSI PENTING:
1. Jawab pertanyaan HANYA berdasarkan context yang diberikan di bawah.
2. Jika context tidak mengandung informasi untuk menjawab pertanyaan,
katakan: "Mohon maaf, saya tidak memiliki informasi tentang itu.
Silakan hubungi tim support kami di [EMAIL/PHONE]."
3. JANGAN mengarang atau mengasumsikan informasi yang tidak ada di context.
4. Jika ada informasi yang bertentangan dalam context, sebutkan keduanya.
5. Jawab dengan bahasa Indonesia yang ramah, jelas, dan helpful.
6. Jika relevan, sebutkan sumber informasi di akhir jawaban.
FORMAT JAWABAN:
- Gunakan bullet points untuk langkah-langkah
- Berikan jawaban yang langsung ke point
- Tawarkan bantuan lanjutan di akhir
─────────────────────────────────────────────────────────────────
USER MESSAGE:
─────────────────────────────────────────────────────────────────
CONTEXT:
"""
{retrieved_documents}
"""
PERTANYAAN CUSTOMER:
{user_question}
Template 2: Internal Knowledge Assistant
SYSTEM PROMPT:
─────────────────────────────────────────────────────────────────
Kamu adalah knowledge assistant internal untuk karyawan [NAMA PERUSAHAAN].
PERAN:
Membantu karyawan menemukan informasi dari dokumentasi perusahaan
termasuk policies, procedures, dan guidelines.
ATURAN KETAT:
1. Jawab HANYA dari context yang diberikan. Ini adalah dokumen
official perusahaan yang harus dijadikan satu-satunya sumber.
2. Jika informasi tidak ada dalam context:
- Katakan dengan jelas bahwa informasi tidak ditemukan
- Sarankan untuk menghubungi HR/department terkait
3. DILARANG KERAS membuat asumsi tentang policy yang tidak tertulis.
4. Untuk pertanyaan sensitif (gaji, terminasi, dll), selalu sarankan
untuk konfirmasi langsung ke HR.
OUTPUT:
- Bahasa: Indonesia formal tapi friendly
- Sertakan referensi dokumen di akhir jawaban
- Format dengan jelas (bullets, numbering jika perlu)
─────────────────────────────────────────────────────────────────
USER MESSAGE:
─────────────────────────────────────────────────────────────────
DOKUMEN REFERENSI:
"""
{retrieved_documents}
"""
PERTANYAAN KARYAWAN:
{user_question}
Template 3: Document Q&A (General Purpose)
SYSTEM PROMPT:
─────────────────────────────────────────────────────────────────
Kamu adalah AI assistant yang menjawab pertanyaan berdasarkan dokumen.
INSTRUKSI:
1. Baca context dengan teliti sebelum menjawab.
2. Jawab HANYA berdasarkan informasi dalam context.
3. Jika jawaban tidak ada dalam context, katakan:
"Berdasarkan dokumen yang tersedia, saya tidak menemukan
informasi tentang [topik]. Apakah ada pertanyaan lain?"
4. Jangan tambahkan informasi dari pengetahuan umum.
5. Jika pertanyaan ambigu, minta klarifikasi.
FORMAT:
- Jawab dengan ringkas tapi lengkap
- Gunakan kutipan langsung jika relevan: "..."
- Akhiri dengan sumber: (Sumber: nama_dokumen)
─────────────────────────────────────────────────────────────────
USER MESSAGE:
─────────────────────────────────────────────────────────────────
CONTEXT:
"""
{retrieved_documents}
"""
PERTANYAAN:
{user_question}
Handling Edge Cases
Real-world itu messy. Berikut cara handle edge cases yang sering terjadi:
Edge Case 1: Context tidak relevan dengan pertanyaan
PROBLEM:
User tanya tentang shipping, tapi retrieval return dokumen tentang refund
(karena similarity score-nya tertinggi meskipun gak relevan)
SOLUTION - Tambahkan di prompt:
"Jika context yang diberikan tidak relevan dengan pertanyaan user,
jangan paksakan jawaban dari context tersebut. Katakan bahwa
informasi yang relevan tidak ditemukan."
IMPLEMENTATION:
├── Check similarity score threshold (min 0.75)
├── Jika semua results < threshold, return "no relevant docs"
└── Prompt handle case ini dengan graceful response
Edge Case 2: User tanya di luar scope
PROBLEM:
Knowledge base untuk e-commerce, tapi user tanya tentang cuaca
SOLUTION - Tambahkan di prompt:
"Kamu adalah assistant khusus untuk [DOMAIN]. Jika pertanyaan
di luar domain ini, politely redirect:
'Saya adalah assistant untuk [DOMAIN]. Untuk pertanyaan tentang
[TOPIK USER], silakan [ALTERNATIF]. Ada yang bisa saya bantu
tentang [DOMAIN]?'"
Edge Case 3: Pertanyaan butuh multiple documents
PROBLEM:
User: "Bandingkan paket Basic dan Premium"
Butuh info dari 2 dokumen berbeda
SOLUTION:
├── Retrieve lebih banyak chunks (top_k = 5-7)
├── Pastikan chunks dari berbagai dokumen ter-include
└── Prompt: "Jika pertanyaan membutuhkan perbandingan,
synthesize informasi dari multiple sources dalam context."
Edge Case 4: User follow-up tanpa context lengkap
PROBLEM:
User: "Bagaimana cara refund?"
AI: "Buka menu Pesanan, klik Ajukan Refund..."
User: "Kalau sudah lewat 14 hari gimana?"
Follow-up question butuh context dari conversation sebelumnya
SOLUTION:
├── Maintain conversation history
├── Include relevant previous Q&A dalam context
└── Atau: Re-retrieve dengan query yang di-augment:
"refund setelah lewat 14 hari policy"
Code Implementation: RAG dengan Prompt
Berikut complete implementation yang menggabungkan retrieval + prompt:
// rag-complete.js
import Anthropic from '@anthropic-ai/sdk';
import { retrieveRelevantDocs } from './retrieval.js';
const anthropic = new Anthropic({ apiKey: process.env.CLAUDE_API_KEY });
const SYSTEM_PROMPT = `Kamu adalah customer support assistant untuk BuildWithAngga.
INSTRUKSI PENTING:
1. Jawab pertanyaan HANYA berdasarkan context yang diberikan.
2. Jika context tidak mengandung informasi untuk menjawab, katakan:
"Mohon maaf, saya tidak memiliki informasi tentang itu.
Silakan hubungi tim support di [email protected]"
3. JANGAN mengarang informasi yang tidak ada di context.
4. Jawab dengan bahasa Indonesia yang ramah dan helpful.
5. Sertakan sumber di akhir jawaban jika relevan.`;
async function generateRAGResponse(userQuestion) {
// Step 1: Retrieve relevant documents
const relevantDocs = await retrieveRelevantDocs(userQuestion, 5);
// Step 2: Check if we have relevant docs
if (relevantDocs.length === 0 || relevantDocs[0].similarity < 0.7) {
return {
answer: "Mohon maaf, saya tidak menemukan informasi yang relevan. " +
"Silakan hubungi tim support kami.",
sources: [],
confidence: "low"
};
}
// Step 3: Format context
const contextText = relevantDocs
.map((doc, i) => `[Dokumen ${i + 1}: ${doc.source}]\\n${doc.content}`)
.join('\\n\\n---\\n\\n');
// Step 4: Build user message dengan context
const userMessage = `CONTEXT:
"""
${contextText}
"""
PERTANYAAN CUSTOMER:
${userQuestion}`;
// Step 5: Call Claude API
const response = await anthropic.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
system: SYSTEM_PROMPT,
messages: [
{ role: "user", content: userMessage }
]
});
return {
answer: response.content[0].text,
sources: relevantDocs.map(d => d.source),
confidence: relevantDocs[0].similarity > 0.85 ? "high" : "medium"
};
}
// Usage
const result = await generateRAGResponse("Bagaimana cara refund pesanan?");
console.log(result.answer);
console.log("Sources:", result.sources);
Pro Tips untuk RAG Prompts
PRO TIPS:
1. TEST DENGAN ADVERSARIAL QUESTIONS
├── Tanya hal yang gak ada di knowledge base
├── Tanya dengan phrasing yang tricky
└── Pastikan AI gak ngarang
2. ITERATE PROMPT BERDASARKAN FAILURES
├── Log semua responses
├── Review yang salah/kurang baik
└── Update prompt untuk handle case tersebut
3. VERSION CONTROL PROMPTS
├── Simpan setiap versi prompt
├── Track changes dan kenapa
└── Bisa rollback kalau ada regression
4. JANGAN OVER-ENGINEER
├── Start simple, tambah rules kalau perlu
├── Terlalu banyak rules = AI bingung
└── Keep it clear and focused
5. A/B TEST DIFFERENT PROMPTS
├── Test 2-3 variasi prompt
├── Measure: accuracy, user satisfaction
└── Pick the winner
Bagian 5: Testing dan Monitoring RAG System
RAG Bukan "Set and Forget"
Ini mindset yang salah tapi sangat common:
MINDSET YANG SALAH:
─────────────────────
"RAG udah working, retrieval bagus, prompt udah di-tune...
Deploy! Done! Move on ke project lain!"
REALITA:
────────
├── User questions lebih beragam dari yang kamu bayangkan
├── Knowledge base perlu di-update berkala
├── AI behavior bisa berubah dengan input berbeda
├── Edge cases muncul seiring waktu
└── Tanpa monitoring = ticking time bomb
Deploy adalah START, bukan END. RAG system butuh continuous monitoring dan improvement.
Metrics yang Harus Di-track
Untuk tau apakah RAG kamu working dengan baik, kamu perlu track metrics yang tepat:
| Metric | Apa yang Diukur | Target | Cara Ukur |
|---|---|---|---|
| Retrieval Precision | Berapa % chunks yang di-retrieve actually relevan? | >80% | Manual review sample |
| Retrieval Recall | Apakah semua info relevan ter-retrieve? | >70% | Test dengan known Q&A pairs |
| Answer Accuracy | Apakah jawaban AI benar? | >90% | Manual review + user feedback |
| Hallucination Rate | Seberapa sering AI ngarang? | <5% | Flag answers not in context |
| "I Don't Know" Rate | Seberapa sering AI bilang gak tau? | 5-15% | Log analysis |
| Response Latency | Seberapa cepat response? | <3 detik | Automatic timing |
| User Satisfaction | Rating dari users | >4/5 | Thumbs up/down, surveys |
| Cost per Query | Berapa biaya per query? | Depends | Track API usage |
Penjelasan beberapa metrics:
RETRIEVAL PRECISION:
────────────────────
Dari 5 chunks yang di-retrieve, berapa yang actually relevan?
Example:
├── Query: "cara refund"
├── Retrieved: [refund-policy, shipping-info, refund-faq, returns, promo-codes]
├── Actually relevant: [refund-policy, refund-faq, returns]
└── Precision: 3/5 = 60% (kurang bagus, perlu improve retrieval)
HALLUCINATION RATE:
───────────────────
Seberapa sering AI kasih info yang GAK ADA di context?
Detection approach:
├── Log: context yang diberikan
├── Log: response yang di-generate
├── Check: Apakah facts di response ada di context?
└── Flag: Responses dengan facts yang gak ada di context
"I DON'T KNOW" RATE:
────────────────────
├── Terlalu tinggi (>20%): Knowledge base kurang lengkap
├── Terlalu rendah (<2%): AI mungkin terlalu "percaya diri" (hallucinating?)
└── Sweet spot: 5-15% (realistic bahwa gak semua pertanyaan ter-cover)
Testing Before Launch
Sebelum launch ke production, kamu WAJIB testing dengan comprehensive:
Step 1: Build Test Dataset
TEST DATASET STRUCTURE:
/test-cases
├── /happy-path (50+ questions)
│ ├── Questions yang pasti ada jawabannya di KB
│ ├── Variasi phrasing untuk pertanyaan sama
│ └── Expected answers untuk comparison
│
├── /edge-cases (20+ questions)
│ ├── Questions yang partially covered
│ ├── Questions yang butuh multiple docs
│ └── Ambiguous questions
│
├── /out-of-scope (20+ questions)
│ ├── Questions yang TIDAK ada di KB
│ ├── Questions di luar domain
│ └── Expected: AI should say "I don't know"
│
└── /adversarial (10+ questions)
├── Trick questions
├── Questions dengan false premises
└── Attempts to make AI hallucinate
Step 2: Automated Testing
// test-rag.js
import { generateRAGResponse } from './rag-complete.js';
import testCases from './test-cases.json';
async function runTests() {
const results = {
passed: 0,
failed: 0,
failures: []
};
for (const testCase of testCases) {
const response = await generateRAGResponse(testCase.question);
// Check based on test type
let passed = false;
if (testCase.type === 'happy-path') {
// Should contain expected answer
passed = response.answer.toLowerCase()
.includes(testCase.expectedKeyword.toLowerCase());
}
else if (testCase.type === 'out-of-scope') {
// Should admit not knowing
passed = response.answer.includes("tidak memiliki informasi") ||
response.answer.includes("tidak ditemukan");
}
else if (testCase.type === 'adversarial') {
// Should NOT contain hallucinated info
passed = !testCase.hallucinations
.some(h => response.answer.toLowerCase().includes(h.toLowerCase()));
}
if (passed) {
results.passed++;
} else {
results.failed++;
results.failures.push({
question: testCase.question,
expected: testCase.expected,
actual: response.answer
});
}
}
console.log(`Passed: ${results.passed}/${testCases.length}`);
console.log(`Failed: ${results.failed}`);
if (results.failures.length > 0) {
console.log('\\nFailures:');
results.failures.forEach(f => {
console.log(`Q: ${f.question}`);
console.log(`Expected: ${f.expected}`);
console.log(`Got: ${f.actual}\\n`);
});
}
return results;
}
runTests();
Step 3: Human Evaluation
Automated tests gak bisa catch everything. Kamu perlu human review:
HUMAN EVALUATION RUBRIC:
For each response, rate 1-5:
ACCURACY (Is the information correct?)
├── 5: Completely accurate
├── 4: Mostly accurate, minor issues
├── 3: Partially accurate
├── 2: Mostly inaccurate
└── 1: Completely wrong
RELEVANCE (Does it answer the question?)
├── 5: Directly answers the question
├── 4: Answers with some extra/missing info
├── 3: Partially answers
├── 2: Tangentially related
└── 1: Doesn't answer at all
GROUNDEDNESS (Is it based on context?)
├── 5: Fully grounded in provided context
├── 4: Mostly grounded, minor additions
├── 3: Mix of grounded and made-up
├── 2: Mostly made-up
└── 1: Completely hallucinated
HELPFULNESS (Would user be satisfied?)
├── 5: Exceeds expectations
├── 4: Meets expectations
├── 3: Acceptable
├── 2: Disappointing
└── 1: Frustrating
Sample size: Minimum 50 responses
Target scores: All categories average >4
Monitoring in Production
Setelah launch, setup monitoring untuk catch issues early:
What to Log:
LOGGING STRUCTURE:
{
"timestamp": "2024-01-15T10:30:00Z",
"query_id": "uuid-xxx",
// User input
"user_query": "Bagaimana cara refund?",
// Retrieval results
"retrieved_chunks": [
{
"chunk_id": "chunk-123",
"source": "refund-policy.pdf",
"similarity": 0.89,
"content_preview": "Refund dapat diajukan..."
}
],
"retrieval_latency_ms": 150,
// Generation results
"generated_response": "Untuk melakukan refund...",
"generation_latency_ms": 800,
"tokens_used": {
"input": 1200,
"output": 350
},
// Quality signals
"confidence": "high",
"sources_cited": ["refund-policy.pdf"],
// User feedback (kalau ada)
"user_feedback": {
"rating": "positive",
"timestamp": "2024-01-15T10:31:00Z"
}
}
Alerting Setup:
ALERTS TO CONFIGURE:
🚨 CRITICAL:
├── Hallucination rate > 10% (1 hour window)
├── Error rate > 5%
└── Latency p95 > 10 seconds
⚠️ WARNING:
├── "I don't know" rate > 25% (might indicate KB gaps)
├── Negative feedback rate > 20%
├── Cost spike > 2x normal
└── Retrieval precision drop > 10%
📊 DAILY DIGEST:
├── Total queries
├── Average satisfaction score
├── Top 10 unanswered questions
├── Cost summary
└── Latency percentiles
Dashboard Essentials:
RAG MONITORING DASHBOARD:
┌─────────────────────────────────────────────────────────────────┐
│ REAL-TIME METRICS │
├──────────────────┬──────────────────┬───────────────────────────┤
│ Queries Today │ Avg Latency │ Satisfaction Score │
│ 1,247 │ 1.8s │ 4.2/5 ⭐ │
├──────────────────┴──────────────────┴───────────────────────────┤
│ │
│ Response Quality (Last 24h) │
│ ████████████████████████░░░░ 85% Accurate │
│ ██████░░░░░░░░░░░░░░░░░░░░░ 12% "I Don't Know" │
│ █░░░░░░░░░░░░░░░░░░░░░░░░░░ 3% Hallucinated │
│ │
├─────────────────────────────────────────────────────────────────┤
│ TOP UNANSWERED QUESTIONS │
│ 1. "Apakah bisa bayar dengan crypto?" (23x) │
│ 2. "Jam berapa live session?" (18x) │
│ 3. "Cara dapat sertifikat?" (15x) │
│ → Action: Add these to knowledge base! │
├─────────────────────────────────────────────────────────────────┤
│ COST TRACKING │
│ Today: $12.50 | This Week: $78.20 | This Month: $245.00 │
│ Avg per query: $0.01 │
└─────────────────────────────────────────────────────────────────┘
Continuous Improvement Loop
Monitoring tanpa action itu useless. Ini loop yang harus kamu jalankan:
WEEKLY IMPROVEMENT CYCLE:
MONDAY: Review Metrics
─────────────────────
├── Check dashboard untuk anomalies
├── Review alert history
└── Identify top issues
TUESDAY-WEDNESDAY: Analyze Failures
───────────────────────────────────
├── Sample 20 "bad" responses
├── Categorize failure types:
│ ├── Retrieval failure (wrong docs)
│ ├── Generation failure (wrong interpretation)
│ ├── Knowledge gap (info not in KB)
│ └── Prompt issue (unclear instructions)
└── Prioritize fixes
THURSDAY: Implement Fixes
─────────────────────────
├── Add missing docs to KB
├── Improve chunking if retrieval issues
├── Update prompts if generation issues
└── Re-embed if needed
FRIDAY: Test & Deploy
─────────────────────
├── Run automated tests
├── Human review sample
├── Deploy updates
└── Monitor for regressions
ONGOING: Knowledge Base Maintenance
───────────────────────────────────
├── Add new docs as they're created
├── Remove outdated docs
├── Update docs when info changes
└── Re-embed periodically
Troubleshooting Common Issues
| Problem | Possible Causes | Solutions |
|---|---|---|
| Wrong answers | Poor retrieval, wrong docs fetched | Improve chunking, tune similarity threshold, add metadata filtering |
| "I don't know" too often | Knowledge gaps, threshold too high | Add more docs, lower similarity threshold |
| Hallucinations | Prompt too permissive, context ignored | Tighten prompt rules, add explicit "don't make up" instructions |
| Slow responses | Large context, slow DB queries | Reduce top_k, optimize vector index, add caching |
| Inconsistent answers | No caching, temperature too high | Add response caching, lower temperature to 0 |
| High costs | Over-fetching, large contexts | Tune retrieval, compress chunks, use smaller model for simple queries |
| Out of context answers | Poor prompt, model ignoring context | Strengthen context instructions, test with different models |
Tools untuk Monitoring RAG
| Tool | Type | Best For | Pricing |
|---|---|---|---|
| Langfuse | Open source | Full RAG observability | Free self-host, paid cloud |
| Helicone | Managed | Easy setup, cost tracking | Free tier available |
| LangSmith | Managed | LangChain users | Free tier available |
| Weights & Biases | Managed | ML teams, experiments | Free tier available |
| Custom + Supabase | DIY | Full control, budget | Supabase free tier |
Recommendation: Start dengan Langfuse (open source) atau custom logging ke Supabase. Upgrade ke managed solution kalau volume tinggi.
Bagian 6: Action Plan dan Resources
Recap: Kenapa RAG Game-Changer
Sebelum kita wrap up, mari recap kenapa RAG worth the effort:
AI TANPA RAG:
─────────────
├── Jawab dari "ingatan" (training data)
├── Knowledge bisa outdated
├── Gak tau data spesifik kamu
├── Hallucinate dengan confident
├── Gak bisa di-verify
└── = UNRELIABLE untuk production
AI DENGAN RAG:
──────────────
├── Jawab dari dokumen yang kamu berikan
├── Knowledge bisa di-update kapanpun
├── Tau semua yang ada di knowledge base kamu
├── Grounded dalam context
├── Bisa cite sumber
└── = RELIABLE untuk production
Bottom line: RAG adalah perbedaan antara AI sebagai "toy" versus AI sebagai "tool" yang bisa dipercaya untuk real business use cases.
Summary: Key Takeaways
10 KEY TAKEAWAYS:
1. HALLUCINATION adalah masalah serius
└── AI bisa confident tapi salah. Ini berbahaya untuk production.
2. RAG = "Baca dulu, baru jawab"
└── AI retrieve relevant docs, lalu generate answer dari docs tersebut.
3. Knowledge Base adalah FONDASI
└── Garbage in = garbage out. Invest di kualitas dokumen.
4. Chunking matters
└── 500-1000 tokens per chunk, 10-20% overlap. Don't skip this.
5. Embeddings enable semantic search
└── Text → Vector → Find similar meanings, bukan just keywords.
6. Vector database is essential
└── Supabase pgvector untuk start. Scale ke Pinecone kalau perlu.
7. Prompt engineering untuk RAG itu BEDA
└── Harus explicit instruct AI untuk pakai context, bukan ingatan.
8. 5 Rules wajib di RAG prompt
└── Only use context, admit if don't know, don't make up, cite sources.
9. Testing dan monitoring is ONGOING
└── Deploy bukan akhir. Track metrics, improve continuously.
10. Start simple, iterate
└── Jangan over-engineer. Launch, learn, improve.
4-Week Action Plan
Ini roadmap practical untuk implement RAG dari nol:
WEEK 1: FOUNDATION
━━━━━━━━━━━━━━━━━━
Day 1-2: Pilih Use Case
├── Identify 1 use case sederhana
│ ├── Customer support FAQ bot
│ ├── Internal documentation assistant
│ └── Product Q&A
└── Kumpulkan 10-20 dokumen untuk knowledge base
Day 3-4: Setup Infrastructure
├── Create Supabase project
├── Enable pgvector extension
├── Create documents table
└── Setup OpenAI API key
Day 5-7: Document Processing
├── Clean dan organize dokumen
├── Implement chunking (start dengan fixed size 500 tokens)
├── Generate embeddings untuk semua chunks
└── Insert ke Supabase
WEEK 2: BASIC RAG
━━━━━━━━━━━━━━━━━
Day 1-2: Build Retrieval
├── Create similarity search function
├── Test dengan berbagai queries
├── Tune threshold dan top_k
└── Verify relevant docs ter-retrieve
Day 3-4: Build Generation
├── Write RAG system prompt
├── Implement complete RAG pipeline
├── Test basic Q&A
└── Iterate prompt based on results
Day 5-7: Build Simple UI
├── Create chat interface (React/Next.js)
├── Connect ke RAG backend
├── Add loading states
└── Basic error handling
WEEK 3: REFINEMENT
━━━━━━━━━━━━━━━━━━
Day 1-2: Testing
├── Create test dataset (30+ questions)
├── Run automated tests
├── Identify failures
└── Categorize issues
Day 3-4: Improve Retrieval
├── Tune chunking based on failures
├── Add metadata untuk filtering
├── Experiment dengan chunk size
└── Re-embed jika perlu
Day 5-7: Improve Generation
├── Refine prompt based on failures
├── Add edge case handling
├── Test adversarial questions
└── Iterate until >85% accuracy
WEEK 4: PRODUCTION
━━━━━━━━━━━━━━━━━━
Day 1-2: Setup Monitoring
├── Implement logging
├── Create simple dashboard
├── Setup alerts
└── Test monitoring works
Day 3-4: Final Testing
├── Full regression test
├── Human evaluation (20+ responses)
├── Performance testing
└── Security review
Day 5-7: Launch
├── Deploy to production
├── Monitor closely first 48 hours
├── Collect user feedback
└── Document learnings
Recommended Tech Stack
| Layer | Recommended | Alternative | Notes |
|---|---|---|---|
| Vector DB | Supabase pgvector | Pinecone, Weaviate | Supabase kalau sudah pakai untuk backend |
| Embeddings | OpenAI text-embedding-3-small | Voyage, Cohere | Best balance quality/cost |
| LLM | Claude 3.5 Sonnet | GPT-4o, GPT-4o-mini | Claude bagus untuk following instructions |
| Framework | Custom code | LangChain, LlamaIndex | Start custom, add framework kalau perlu |
| Monitoring | Langfuse | Helicone, custom | Langfuse free dan comprehensive |
| Frontend | Next.js + React | Any | Vercel deployment gampang |
| Hosting | Vercel + Supabase | Railway, Fly.io | Free tiers untuk start |
Cost Estimate untuk Start:
MONTHLY COST (Low Traffic - ~1000 queries/month):
├── Supabase: $0 (free tier)
├── OpenAI Embeddings: ~$0.50
├── Claude API: ~$5-10
├── Vercel: $0 (free tier)
└── Total: ~$5-15/month
MONTHLY COST (Medium Traffic - ~10,000 queries/month):
├── Supabase: $25 (pro tier)
├── OpenAI Embeddings: ~$5
├── Claude API: ~$50-100
├── Vercel: $20
└── Total: ~$100-150/month
Kelas BuildWithAngga untuk Lanjut Belajar
Untuk deepen skills yang sudah kamu pelajari:
RECOMMENDED CLASSES:
AI & Development:
├── Full-Stack AI-Powered Developer: Dari Nol Sampai Mahir
│ └── Comprehensive AI development skills
├── Membuat Chatbot AI dengan No-Code
│ └── Quick wins dengan chatbot tanpa coding
└── Ebook Full-Stack AI-Powered Developer
└── Reference untuk AI concepts
Backend & Database:
├── Laravel 12 Fundamental
│ └── Backend skills untuk API
├── REST API dengan Node.js
│ └── Kalau prefer JavaScript
└── Supabase classes
└── Database dan backend
Frontend:
├── React JS Fundamental
│ └── Build UI untuk RAG app
└── Next.js classes
└── Full-stack framework
Vibe Coding:
├── Full-Stack Web Designer Developer with Lovable AI
│ └── Rapid prototyping
└── UI/UX Vibe Coding classes
└── Build UI dengan AI assistance
Resources untuk Deep Dive
DOCUMENTATION:
├── Anthropic Docs: docs.anthropic.com
├── OpenAI Cookbook: cookbook.openai.com
├── Supabase pgvector: supabase.com/docs/guides/ai
└── LangChain: docs.langchain.com
ARTICLES & PAPERS:
├── "Retrieval-Augmented Generation" (original paper)
├── Anthropic's RAG guide
└── Pinecone learning center
YOUTUBE:
├── Fireship - AI/RAG explanations
├── AI Jason - RAG tutorials
└── Sam Witteveen - Advanced RAG
COMMUNITIES:
├── BuildWithAngga community
├── AI Discord servers
└── Reddit: r/MachineLearning, r/LocalLLaMA
Final Message
Kamu sekarang punya pengetahuan untuk build RAG system yang production-ready.
Tapi knowledge tanpa action itu useless.
Jadi ini challenge untuk kamu:
7-DAY CHALLENGE:
Day 1: Pilih use case
Day 2: Kumpulkan 10 dokumen
Day 3: Setup Supabase + embeddings
Day 4: Build retrieval pipeline
Day 5: Write RAG prompt
Day 6: Build simple UI
Day 7: Test dan share hasilnya!
RAG bukan rocket science. Dengan tools yang ada sekarang, siapa saja bisa build AI yang akurat dan reliable.
Yang membedakan adalah siapa yang actually eksekusi.
Mulai dengan simple. Iterate. Improve. Ship.
AI kamu gak perlu ngawur dan halusinasi lagi.
Make your AI grounded in facts, not fantasies. 🚀
Punya pertanyaan tentang RAG? Drop di kolom komentar atau reach out via social media.
Akses kelas AI dan development di BuildWithAngga: buildwithangga.com