RAG + chatbot — menjawab dari knowledge base sendiri
RAG di Fase 5 dibahas sebagai satu pertanyaan sekali jalan. Chatbot menambahkan satu lapisan kompleksitas: riwayat percakapan yang harus ikut memengaruhi apa yang dicari, bukan cuma pesan terakhir saja.
Intisari
- Menggabungkan RAG dengan chatbot berarti setiap giliran bicara melakukan retrieval berdasarkan konteks percakapan, bukan cuma pesan terakhir yang berdiri sendiri.
- Masalah klasik: pertanyaan lanjutan seperti 'kalau untuk yang paruh waktu?' tidak masuk akal dicari sendirian tanpa tahu topik sebelumnya ('cuti melahirkan') — perlu query rewriting sebelum retrieval.
- Query rewriting: pakai LLM untuk menyusun ulang pertanyaan jadi mandiri (self-contained) berdasarkan riwayat percakapan, baru dipakai sebagai query pencarian.
- Chunk hasil retrieval disisipkan sebagai konteks tambahan di system prompt atau pesan saat itu — bukan disimpan permanen sebagai bagian riwayat, supaya tidak menumpuk di setiap turn berikutnya.
- Selalu tampilkan atau simpan sumber jawaban (dokumen/chunk mana yang dipakai) — penting untuk kepercayaan pengguna dan untuk debugging saat jawabannya salah.
Masalah baru: pertanyaan lanjutan yang tidak berdiri sendiri
RAG satu-pertanyaan di Fase 5 mencari langsung berdasarkan teks pertanyaan. Dalam percakapan multi-turn, ini gagal untuk pertanyaan lanjutan yang bergantung konteks sebelumnya:
User: "Berapa lama cuti melahirkan di perusahaan ini?"
Bot: "Cuti melahirkan di perusahaan ini adalah 4 bulan..." [dari RAG]
User: "Kalau untuk yang paruh waktu?"
↑ dicari sendirian, embedding-nya TIDAK tahu ini masih soal "cuti melahirkan"
→ retrieval kemungkinan besar menemukan chunk yang salah/tidak relevan
Solusinya: query rewriting
Sebelum melakukan retrieval, susun ulang pertanyaan terakhir jadi versi yang berdiri sendiri (self-contained) dengan bantuan LLM, memakai riwayat percakapan sebagai konteks:
def tulis_ulang_query(riwayat: list, pertanyaan_baru: str) -> str:
ringkasan_riwayat = "\n".join(f"{m['role']}: {m['content']}" for m in riwayat[-4:])
prompt = f"""Berdasarkan riwayat percakapan ini, tulis ulang PERTANYAAN TERAKHIR
menjadi pertanyaan yang berdiri sendiri (tanpa perlu konteks sebelumnya untuk dipahami).
Jawab HANYA dengan pertanyaan hasil tulis ulang, tanpa penjelasan.
Riwayat:
{ringkasan_riwayat}
Pertanyaan terakhir: {pertanyaan_baru}"""
return client.messages.create(
model="claude-haiku-4-5", max_tokens=100, temperature=0, # model kecil cukup, tugas sederhana
messages=[{"role": "user", "content": prompt}],
).content[0].text
# "Kalau untuk yang paruh waktu?" → "Berapa lama cuti melahirkan untuk karyawan paruh waktu?"
Ini contoh nyata model routing dari Fase 0: tugas menulis ulang query cukup sederhana sehingga model kecil & murah (mis. Claude Haiku) sudah memadai — tidak perlu model paling mahal untuk setiap langkah internal. Jawaban final ke pengguna baru memakai model yang lebih mampu kalau memang dibutuhkan.
Pipeline lengkap: chatbot + RAG
def jawab_chatbot_rag(conversation_id: str, pesan_baru: str) -> str:
riwayat = ambil_riwayat_dari_db(conversation_id)
query_pencarian = tulis_ulang_query(riwayat, pesan_baru) if riwayat else pesan_baru
chunks = cari_dokumen_mirip(query_pencarian, k=3) # dari Fase 4-5
konteks = "\n\n".join(c["isi"] for c in chunks)
riwayat.append({"role": "user", "content": pesan_baru}) # riwayat simpan pesan ASLI, bukan hasil rewrite
response = client.messages.create(
model="claude-sonnet-4-5", max_tokens=1024,
system=f"Jawab berdasarkan konteks berikut kalau relevan:\n<konteks>{konteks}</konteks>",
messages=riwayat,
)
balasan = response.content[0].text
riwayat.append({"role": "assistant", "content": balasan})
simpan_riwayat_ke_db(conversation_id, riwayat)
return balasan, chunks # kembalikan juga sumbernya
Konteks RAG disisipkan lewat system, bukan ditambah permanen ke riwayat. Kalau
chunk hasil retrieval ikut disimpan sebagai bagian permanen messages, riwayat akan membengkak
dengan konteks yang mungkin sudah tidak relevan di turn berikutnya. Cari ulang & sisipkan segar di
setiap turn — sedikit lebih mahal per turn, tapi konteksnya selalu relevan dengan pertanyaan saat
itu.
Tampilkan sumbernya
Mengembalikan chunks yang dipakai (seperti di kode di atas) bukan cuma untuk debugging — di
UI, menunjukkan "jawaban ini berdasarkan dokumen X, bagian Y" membangun kepercayaan pengguna dan memberi
mereka jalan untuk memverifikasi sendiri, terutama untuk domain yang konsekuensinya nyata (kebijakan HR,
medis, hukum).
Latihan: pakai knowledge base fiktif dari latihan Fase 5, lalu simulasikan percakapan 3 giliran
di mana giliran kedua dan ketiga adalah pertanyaan lanjutan yang butuh konteks giliran sebelumnya (seperti
contoh "paruh waktu" di atas). Bandingkan hasil retrieval dengan dan tanpa tulis_ulang_query.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.