{"slug": "bikin-chatbot-sendiri-yang-bisa-jawab-pertanyaan-dari-dokumen-kamu", "title": "Bikin Chatbot Sendiri yang Bisa Jawab Pertanyaan dari Dokumen kamu", "summary": "A project called Simple RAG Chatbot, which allows users to build a chatbot that answers questions based on their own documents (such as Markdown or text files) rather than relying on the model's general knowledge. It explains the RAG (Retrieval-Augmented Generation) technique, which uses embeddings to convert text into vectors, stores them in a vector database, and retrieves relevant document chunks to generate accurate, source-based answers. The project is designed for beginners, uses free or low-cost tools for self-hosting, and currently supports only `.md`, `.markdown`, and `.txt` file formats.", "body_md": "Di tulisan ini kita akan membahas soal sebuah proyek kecil bernama **Simple RAG Chatbot**. Tujuannya sederhana: bikin chatbot yang bisa menjawab pertanyaan berdasarkan dokumen yang **kamu** punya — bukan ngarang dari pengetahuan model. Cocok buat kamu yang baru pertama kali denger istilah RAG dan pengen tahu cara kerjanya tanpa pusing.\n\nRepository proyek aplikasi : [https://github.com/ardisaurus/simple-rag-chatbot](https://github.com/ardisaurus/simple-rag-chatbot)\n\n## Masalah yang Ingin Diselesaikan\n\nCoba bayangin kamu punya banyak banget dokumentasi produk: file Markdown, catatan teknis, panduan instalasi, FAQ. Setiap kali ada orang baru gabung, mereka selalu nanya hal yang sebenernya udah ada di dokumen — tapi siapa juga yang sempat baca semuanya?\n\nSolusi instan yang mungkin terlintas: \"kasih aja ke ChatGPT terus suruh jawab.\" Tapi ada dua masalah:\n\n- Model AI seperti ChatGPT\n**tidak tahu** isi dokumen pribadimu. - Kalau kamu paste seluruh dokumen ke chat, biayanya mahal dan sering kepotong limit token.\n\nDi sinilah teknik bernama **RAG** masuk.\n\n## Apa Itu RAG?\n\nRAG kepanjangannya **Retrieval-Augmented Generation**. Kedengeran ribet, padahal idenya simpel banget:\n\n-\n**Retrieval**(pengambilan): cari potongan dokumen yang paling relevan dengan pertanyaan user. -\n**Augmented**(diperkaya): tempelin potongan itu ke prompt yang dikirim ke LLM (model bahasa besar). -\n**Generation**(pembuatan jawaban): LLM jawab berdasarkan potongan yang barusan dia terima.\n\nJadi LLM-nya gak perlu hapal seluruh dokumenmu. Dia cuma perlu baca beberapa paragraf yang relevan untuk setiap pertanyaan. Hemat token, hemat biaya, dan jawabannya jadi lebih akurat karena ada bukti sumbernya.\n\n## Cara Komputer Cari Potongan yang Relevan: Embeddings\n\nPertanyaannya: gimana komputer tau potongan dokumen mana yang relevan sama pertanyaan user?\n\nJawabannya pakai konsep **embeddings**. Embedding itu cara mengubah teks menjadi sebuah deretan angka (vektor) yang merepresentasikan **makna** teks tersebut. Dua kalimat yang artinya mirip akan menghasilkan vektor yang mirip juga, walaupun kata-katanya berbeda.\n\nContoh:\n\n- \"How do I install the product?\" →\n`[0.12, -0.34, 0.88, ...]`\n\n- \"Cara memasang aplikasinya gimana?\" →\n`[0.11, -0.33, 0.86, ...]`\n\nWalaupun bahasanya beda, vektornya berdekatan karena maknanya mirip.\n\nLalu kita simpan semua vektor dokumen di sebuah **vector database**. Saat user nanya, kita ubah pertanyaannya jadi vektor, terus cari vektor dokumen yang paling dekat. Itulah potongan yang paling relevan.\n\n## Tools yang Dipakai di Proyek Ini\n\nAku sengaja pilih tools yang gratis atau super murah supaya kamu bisa self-hosting di laptop sendiri:\n\n-\n**Next.js (App Router)**— framework React untuk bikin web app. -\n**TypeScript**— biar kode lebih aman dan mudah di-maintain. -\n**LangChain.js**— library yang mempermudah semua langkah RAG: split dokumen, bikin embedding, retrieval, sampai panggil LLM. -\n**ChromaDB**— vector database yang bisa jalan lokal pakai Docker. -\n**OpenRouter**— gateway buat berbagai LLM (GPT, Claude, Llama, dll). Bayar per token, dan model default-nya cuma beberapa rupiah per pertanyaan. -\n**@xenova/transformers**— library untuk bikin embedding** langsung di laptopmu**, tanpa API. Jadi gak perlu langganan OpenAI cuma buat embedding. -\n**Tailwind CSS**— supaya tampilannya rapi tanpa nulis CSS panjang.\n\n## Format Dokumen yang Didukung & Batasannya\n\nSebelum kita masuk ke alur teknisnya, penting kamu tahu dulu **dokumen apa aja yang bisa diproses** sama proyek ini, dan apa aja **batasan** dari implementasi sekarang. Jangan sampai kamu masukin file PDF terus heran kenapa chatbot-nya jawab \"saya tidak tahu\" — ternyata file-nya emang gak pernah ke-index.\n\n### Format yang Didukung Saat Ini\n\nHanya tiga ekstensi yang dibaca oleh script ingest:\n\n-\n`.md`\n\n— Markdown standar. -\n`.markdown`\n\n— sama aja, beda ekstensi. -\n`.txt`\n\n— teks polos.\n\nDaftar ini didefinisikan di `lib/ingest.ts`\n\nsebagai `SUPPORTED_EXTENSIONS`\n\n. File dengan ekstensi lain akan **di-skip diam-diam** (tanpa warning) saat script jalan menjelajahi folder. Subfolder ikut dibaca rekursif, jadi kamu bebas mengelompokkan dokumen ke dalam beberapa folder.\n\n### Format yang Belum Didukung\n\nBerikut yang **belum** bisa langsung kamu masukin tanpa modifikasi kode:\n\n-\n**PDF**— perlu library tambahan kayak`pdf-parse`\n\n. Cara nambahinnya udah dijelasin di file`data/docs/wiki/08-extending.md`\n\n. -\n**DOCX / Office documents**— perlu parser tersendiri (misal`mammoth`\n\nuntuk DOCX). -\n**HTML**— bisa diproses, tapi butuh parser untuk strip tag HTML supaya yang ke-embed cuma teksnya. -\n**MDX**— walaupun mirip Markdown, ekstensinya`.mdx`\n\njadi gak akan ke-pickup. Kalau kamu yakin gak ada JSX di dalamnya, gampang aja: tambahin`.mdx`\n\nke`SUPPORTED_EXTENSIONS`\n\n. -\n**Gambar / scan / screenshot**— proyek ini gak punya OCR. Kalau dokumenmu berbentuk gambar, perlu pipeline tambahan (misal Tesseract atau API OCR) untuk ngubah jadi teks dulu. -\n**CSV / spreadsheet / JSON terstruktur**— bisa di-index sebagai teks polos, tapi hasilnya kurang optimal karena splitter-nya gak paham struktur tabel.\n\n## Cara Kerja Aplikasinya, Step by Step\n\n### 1. Ingestion: Masukin Dokumen ke Database\n\nPertama, kita siapin semua dokumen di folder `data/docs/`\n\n. Bisa Markdown atau TXT. Lalu jalanin:\n\n```\nnpm run ingest\n```\n\nYang terjadi di balik layar:\n\n- Script jalanin\n**walk** di folder`data/docs/`\n\n, ngambil semua file yang didukung. - Setiap file dipotong-potong jadi chunk kecil (sekitar 1000 karakter, dengan overlap 150 karakter biar konteksnya gak putus).\n- Setiap chunk diubah jadi vektor pakai model embedding lokal.\n- Vektor + chunk + metadata (nama file, nomor chunk) disimpan ke Chroma.\n\nSekarang Chroma punya \"ingatan\" tentang dokumenmu, siap dipakai untuk pencarian.\n\n### 2. Pertanyaan dari User\n\nUser buka [http://localhost:3000](http://localhost:3000), ketik pertanyaan, terus pencet enter. Pertanyaan itu dikirim ke `/api/chat`\n\n.\n\n### 3. Retrieval: Cari Chunk yang Relevan\n\nBackend ngambil pertanyaan, ubah jadi vektor pakai model embedding yang sama, lalu tanya ke Chroma: \"kasih aku 5 chunk yang vektornya paling dekat dengan ini.\"\n\nChroma balikin 5 chunk beserta metadata-nya.\n\n### 4. Generation: Tanya ke LLM\n\n5 chunk tadi dirakit jadi sebuah prompt seperti ini (disederhanakan):\n\n```\nKamu adalah asisten dokumentasi. Jawab HANYA berdasarkan konteks\nberikut. Kalau jawabannya tidak ada di konteks, bilang kamu tidak tahu.\n\nKonteks:\n[1] (instalasi.md #0) Untuk memasang aplikasi, unduh installer ...\n[2] (instalasi.md #1) Setelah terpasang, jalankan dari menu ...\n[3] ...\n\nPertanyaan: Bagaimana cara memasang aplikasinya?\n```\n\nPrompt itu dikirim ke OpenRouter, dan model jawab berdasarkan konteks yang udah dikasih. Karena ada instruksi tegas \"jawab hanya dari konteks\", model gak akan ngarang.\n\n### 5. Streaming: Jawaban Muncul Sedikit-Sedikit\n\nDaripada nunggu jawaban full baru ditampilkan, jawabannya **distreaming** token per token ke browser. User langsung lihat huruf demi huruf mengalir, mirip pengalaman ChatGPT. Lebih enak dilihat dan kerasa cepat.\n\n### 6. Sumber Ditampilkan\n\nDi bawah setiap jawaban ada tombol \"Sources\" yang bisa diklik. Isinya daftar chunk yang dipakai untuk menjawab, lengkap dengan nama file dan nomor chunk-nya. Jadi user bisa verifikasi jawaban si bot.\n\n## Kenapa Pilihan Teknisnya Begini?\n\n### Kenapa embedding-nya lokal, bukan pakai OpenAI?\n\nKarena bisa **gratis**. Model `Xenova/all-MiniLM-L6-v2`\n\ncukup pintar buat dokumentasi pendek, ukurannya kecil (~30 MB), dan jalan langsung di Node.js tanpa internet. Cocok buat self-hosting.\n\n### Kenapa LLM-nya pakai OpenRouter?\n\nOpenRouter ngasih akses ke banyak model dengan satu API key. Kamu bisa ganti dari `gpt-4o-mini`\n\nke `claude-3.5-sonnet`\n\nke `llama-3.1`\n\ncuma dengan ubah satu env var. Bagus buat eksperimen.\n\n### Kenapa Chroma, bukan Pinecone atau Weaviate?\n\nChroma gratis, jalan lokal, dan setup-nya cuma satu perintah Docker. Untuk skala kecil-menengah (puluhan ribu chunk) udah lebih dari cukup.\n\n### Kenapa wipe-and-replace saat ingest, bukan update incremental?\n\nBiar simpel. Kalau ada file yang dihapus atau di-rename, gak akan ada chunk orphan yang tertinggal. Untuk dokumentasi yang gak terlalu besar, re-ingest cepat banget — hitungan detik.\n\n## Cara Coba di Laptopmu\n\nSingkat aja, langkah-langkahnya:\n\n```\n# 1. Install dependency\nnpm install\n\n# 2. Setup env (isi OPENROUTER_API_KEY dari https://openrouter.ai/keys)\ncp .env.example .env\n\n# 3. Jalanin Chroma di Docker\nnpm run chroma\n\n# 4. Index dokumentasi sample yang udah disediain\nnpm run ingest\n\n# 5. Jalanin aplikasinya\nnpm run dev\n```\n\nBuka [http://localhost:3000](http://localhost:3000), tanya apa aja soal dokumentasi yang udah di-index. Misalnya: \"What is the default chunk size?\"\n\n### Batasan Lain yang Perlu Dipahami\n\nSelain soal format file, ada beberapa batasan implementasi yang penting kamu tahu:\n\n-\n**Encoding harus UTF-8.** Kalau file`.txt`\n\nkamu disimpan dalam encoding lain (misal Windows-1252), karakter non-ASCII (è, ñ, é, dll) bisa berantakan saat dibaca. -\n**Splitter berbasis karakter, bukan struktur.** Splitter default-nya`RecursiveCharacterTextSplitter`\n\n, yang motong teks berdasarkan jumlah karakter. Artinya tabel Markdown atau code block panjang bisa kepotong di tengah. Untuk Markdown yang lebih rapi splitnya, kamu bisa ganti ke`MarkdownTextSplitter`\n\nyang lebih sadar struktur heading. -\n**Embedding model default-nya English-leaning.**`Xenova/all-MiniLM-L6-v2`\n\ndilatih dominan pada teks bahasa Inggris. Untuk dokumentasi Bahasa Indonesia, hasilnya masih lumayan tapi gak seoptimal model multilingual. Kalau kualitas pencarian dirasa kurang, ganti ke model multilingual seperti`Xenova/multilingual-e5-small`\n\n(jangan lupa re-ingest karena dimensi vektornya beda). -\n**Ingest itu wipe-and-replace, bukan incremental.** Setiap kali`npm run ingest`\n\ndijalankan, seluruh collection di Chroma dihapus dan dibangun ulang dari nol. Konsekuensinya: gak bisa update satu file aja. Tapi enaknya: gak akan ada chunk basi tertinggal. -\n**Gak ada akses kontrol per-dokumen.** Semua orang yang bisa akses chatbot akan dapet jawaban dari semua dokumen yang ke-index. Kalau ada dokumen sensitif, pisahin di collection berbeda atau jangan di-index sama sekali. -\n**Chunk besar bisa boros token.** Default`CHUNK_SIZE = 1000`\n\nkarakter ×`RETRIEVAL_K = 5`\n\nchunk = ~5000 karakter konteks per pertanyaan. Kalau modelmu mahal atau context window-nya kecil, turunin salah satu angka itu.\n\nIntinya: proyek ini sengaja dibuat **simpel dulu**. Format yang lebih kompleks dan fitur incremental update memang sengaja gak dimasukin supaya kode-nya tetep gampang dibaca. Kalau butuh, semuanya bisa kamu tambahin sendiri dengan modifikasi kecil di `lib/ingest.ts`\n\n.\n\n## Penutup\n\nRAG itu konsep yang awalnya kelihatan ribet, padahal intinya cuma: **cari potongan yang relevan, tempelin ke prompt, biarkan LLM jawab berdasarkan itu**.\n\nProyek ini sengaja dibuat **minimal** dan **mudah dibaca**. Total kode inti cuma di folder `lib/`\n\ndan `app/`\n\n— tiap file punya tanggung jawab yang jelas. Kalau kamu pengen belajar gimana sistem RAG bekerja dari nol, baca aja file-filenya satu per satu, dimulai dari `lib/ingest.ts`\n\n, lalu `lib/rag.ts`\n\n.\n\nSelamat ngoprek, dan semoga proyek kecil ini bisa jadi titik awal kamu bikin asisten dokumentasi sendiri!\n\nRepository proyek aplikasi : [https://github.com/ardisaurus/simple-rag-chatbot](https://github.com/ardisaurus/simple-rag-chatbot)", "url": "https://wpnews.pro/news/bikin-chatbot-sendiri-yang-bisa-jawab-pertanyaan-dari-dokumen-kamu", "canonical_source": "https://dev.to/ardisaurus/bikin-chatbot-sendiri-yang-bisa-jawab-pertanyaan-dari-dokumen-kamu-5464", "published_at": "2026-05-21 09:50:26+00:00", "updated_at": "2026-05-21 10:05:00.825147+00:00", "lang": "en", "topics": ["artificial-intelligence", "machine-learning", "large-language-models", "open-source", "developer-tools"], "entities": ["Simple RAG Chatbot", "ChatGPT", "RAG", "LLM"], "alternates": {"html": "https://wpnews.pro/news/bikin-chatbot-sendiri-yang-bisa-jawab-pertanyaan-dari-dokumen-kamu", "markdown": "https://wpnews.pro/news/bikin-chatbot-sendiri-yang-bisa-jawab-pertanyaan-dari-dokumen-kamu.md", "text": "https://wpnews.pro/news/bikin-chatbot-sendiri-yang-bisa-jawab-pertanyaan-dari-dokumen-kamu.txt", "jsonld": "https://wpnews.pro/news/bikin-chatbot-sendiri-yang-bisa-jawab-pertanyaan-dari-dokumen-kamu.jsonld"}}