irhamni commited on
Commit
50c7bda
Β·
verified Β·
1 Parent(s): 3e41cc8

Upload 4 files

Browse files
Files changed (4) hide show
  1. IPLM_QnA_Chatbot.jsonl +29 -0
  2. README.md +37 -14
  3. app.py +199 -0
  4. requirements.txt +6 -0
IPLM_QnA_Chatbot.jsonl ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {"id": "iplm-001", "question": "Apa itu IPLM dan mengapa penting?", "answer": "IPLM (Indeks Pembangunan Literasi Masyarakat) adalah instrumen resmi untuk mengukur capaian literasi masyarakat berbasis kontribusi perpustakaan. Penting karena menjadi Indikator Kinerja Kunci (IKK) urusan perpustakaan sesuai Permendagri 18/2020, sekaligus dasar perumusan kebijakan literasi nasional menuju Indonesia Emas 2045.", "alt_phrases": ["Bisakah jelaskan: Apa itu IPLM dan mengapa penting?", "Saya ingin tahu, apa itu iplm dan mengapa penting?"], "source_doc": "Juknis IPLM 2025 (Pelaksanaan)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "iplm, mengapa, penting?"}
2
+ {"id": "iplm-002", "question": "Siapa saja yang menjadi sasaran IPLM?", "answer": "Sasaran utama adalah: Perpusnas (koordinasi nasional), Perpustakaan Provinsi & Kabupaten/Kota (pelaksana teknis), semua jenis perpustakaan (umum, sekolah, perguruan tinggi, khusus, TBM/komunitas), dan pemangku kepentingan pendidikan, kebudayaan, pembangunan daerah.", "alt_phrases": ["Bisakah jelaskan: Siapa saja yang menjadi sasaran IPLM?", "Saya ingin tahu, siapa saja yang menjadi sasaran iplm?"], "source_doc": "Juknis IPLM 2025 (Pelaksanaan)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "iplm?, menjadi, saja, sasaran, siapa, yang"}
3
+ {"id": "iplm-003", "question": "Bagaimana metode pengumpulan datanya?", "answer": "Sensus penuh untuk perpustakaan jumlah terbatas (umum, perguruan tinggi, khusus OPD/KL). Sampling representatif untuk perpustakaan jumlah besar (SD, SMP, TBM). Semua berbasis daring di platform Perpusnas.", "alt_phrases": ["Bisakah jelaskan: Bagaimana metode pengumpulan datanya?", "Saya ingin tahu, bagaimana metode pengumpulan datanya?"], "source_doc": "Juknis IPLM 2025 (Pelaksanaan)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "bagaimana, datanya?, metode, pengumpulan"}
4
+ {"id": "iplm-004", "question": "Bagaimana data diproses sebelum menghasilkan skor?", "answer": "Tahapan: verifikasi β†’ validasi β†’ transformasi (fungsi Yeo-Johnson) β†’ normalisasi (min-max 0–1) β†’ pembobotan dimensi (kepatuhan 30%, kinerja 70%) β†’ agregasi skor β†’ standardisasi (Z-score) β†’ klasifikasi kategori (sangat tinggi, tinggi, sedang, rendah).", "alt_phrases": ["Bisakah jelaskan: Bagaimana data diproses sebelum menghasilkan skor?", "Saya ingin tahu, bagaimana data diproses sebelum menghasilkan skor?"], "source_doc": "Juknis IPLM 2025 (Pelaksanaan)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "bagaimana, data, diproses, menghasilkan, sebelum, skor?"}
5
+ {"id": "iplm-005", "question": "Mengapa diperlukan transformasi dan normalisasi data?", "answer": "Transformasi: memastikan distribusi data lebih normal, mengurangi pencilan. Normalisasi: menyetarakan skala indikator agar bisa dibandingkan adil.", "alt_phrases": ["Bisakah jelaskan: Mengapa diperlukan transformasi dan normalisasi data?", "Saya ingin tahu, mengapa diperlukan transformasi dan normalisasi data?"], "source_doc": "Juknis IPLM 2025 (Pelaksanaan)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "data?, diperlukan, mengapa, normalisasi, transformasi"}
6
+ {"id": "iplm-006", "question": "Apa saja dimensi IPLM?", "answer": "Kepatuhan (30%): koleksi, tenaga, anggaran. Kinerja (70%): pelayanan, penyelenggaraan/pengelolaan, kerja sama, kegiatan literasi, jumlah pemustaka, inovasi layanan.", "alt_phrases": ["Bisakah jelaskan: Apa saja dimensi IPLM?", "Saya ingin tahu, apa saja dimensi iplm?"], "source_doc": "Peraturan Perpusnas 2025 (Pedoman IPLM)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "dimensi, iplm?, saja"}
7
+ {"id": "iplm-007", "question": "Mengapa bobot kinerja lebih besar daripada kepatuhan?", "answer": "Karena kinerja mencerminkan dampak nyata layanan perpustakaan terhadap literasi masyarakat. Kepatuhan hanya fondasi administratif, sedangkan kinerja adalah hasil dan manfaat langsung.", "alt_phrases": ["Bisakah jelaskan: Mengapa bobot kinerja lebih besar daripada kepatuhan?", "Saya ingin tahu, mengapa bobot kinerja lebih besar daripada kepatuhan?"], "source_doc": "Juknis IPLM 2025 (Pelaksanaan)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "besar, bobot, daripada, kepatuhan?, kinerja, lebih, mengapa"}
8
+ {"id": "iplm-008", "question": "Bagaimana peran tiap level perpustakaan?", "answer": "Perpusnas: metodologi, validasi, analisis nasional, publikasi, bimbingan teknis. Provinsi: kumpulkan & verifikasi data SMA/SMK, SLB, perpustakaan khusus provinsi. Kabupaten/Kota: kumpulkan & verifikasi data perpustakaan umum, SD/SMP, TBM, perpustakaan khusus daerah.", "alt_phrases": ["Bisakah jelaskan: Bagaimana peran tiap level perpustakaan?", "Saya ingin tahu, bagaimana peran tiap level perpustakaan?"], "source_doc": "Juknis IPLM 2025 (Pelaksanaan)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "bagaimana, level, peran, perpustakaan?, tiap"}
9
+ {"id": "iplm-009", "question": "Bagaimana menjamin kualitas data IPLM?", "answer": "Melalui pelatihan operator, supervisi lapangan, validasi silang antar level, berita acara verifikasi & validasi sebelum analisis.", "alt_phrases": ["Bisakah jelaskan: Bagaimana menjamin kualitas data IPLM?", "Saya ingin tahu, bagaimana menjamin kualitas data iplm?"], "source_doc": "Juknis IPLM 2025 (Pelaksanaan)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "bagaimana, data, iplm?, kualitas, menjamin"}
10
+ {"id": "iplm-010", "question": "Apa manfaat IPLM bagi daerah?", "answer": "Menjadi dasar laporan LPPD, menyediakan data objektif untuk kebijakan literasi, mendapatkan rekomendasi program peningkatan literasi, dasar penghargaan/insentif bagi daerah dengan peningkatan signifikan.", "alt_phrases": ["Bisakah jelaskan: Apa manfaat IPLM bagi daerah?", "Saya ingin tahu, apa manfaat iplm bagi daerah?"], "source_doc": "Juknis IPLM 2025 (Pelaksanaan)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "bagi, daerah?, iplm, manfaat"}
11
+ {"id": "iplm-011", "question": "Apakah semua perpustakaan di wilayah kami wajib mengisi instrumen IPLM?", "answer": "Ya, semua unit wajib berpartisipasi. Sensus untuk perpustakaan terbatas, sampling untuk jumlah besar.", "alt_phrases": ["Bisakah jelaskan: Apakah semua perpustakaan di wilayah kami wajib mengisi instrumen IPLM?", "Saya ingin tahu, apakah semua perpustakaan di wilayah kami wajib mengisi instrumen iplm?"], "source_doc": "Juknis IPLM 2025 (Pelaksanaan)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "apakah, instrumen, iplm?, kami, mengisi, perpustakaan, semua, wajib, wilayah"}
12
+ {"id": "iplm-012", "question": "Bagaimana mekanisme jika ada perpustakaan yang tidak mengisi atau terlambat mengirim data?", "answer": "Data dianggap tidak lengkap dan memengaruhi skor daerah. Ketepatan waktu menjadi faktor penting.", "alt_phrases": ["Bisakah jelaskan: Bagaimana mekanisme jika ada perpustakaan yang tidak mengisi atau terlambat mengirim data?", "Saya ingin tahu, bagaimana mekanisme jika ada perpustakaan yang tidak mengisi atau terlambat mengirim data?"], "source_doc": "Juknis IPLM 2025 (Pelaksanaan)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "atau, bagaimana, data?, jika, mekanisme, mengirim, mengisi, perpustakaan, terlambat, tidak, yang"}
13
+ {"id": "iplm-013", "question": "Apakah ada sanksi jika daerah tidak mengirimkan data IPLM?", "answer": "Tidak ada sanksi administratif langsung, tetapi skor daerah rendah dan berdampak pada evaluasi LPPD.", "alt_phrases": ["Bisakah jelaskan: Apakah ada sanksi jika daerah tidak mengirimkan data IPLM?", "Saya ingin tahu, apakah ada sanksi jika daerah tidak mengirimkan data iplm?"], "source_doc": "Juknis IPLM 2025 (Pelaksanaan)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "apakah, daerah, data, iplm?, jika, mengirimkan, sanksi, tidak"}
14
+ {"id": "iplm-014", "question": "Apakah dinas bisa melihat hasil sementara sebelum publikasi nasional?", "answer": "Ya, hasil rekap bisa diakses daerah untuk keperluan internal. Publikasi resmi tetap melalui Perpusnas.", "alt_phrases": ["Bisakah jelaskan: Apakah dinas bisa melihat hasil sementara sebelum publikasi nasional?", "Saya ingin tahu, apakah dinas bisa melihat hasil sementara sebelum publikasi nasional?"], "source_doc": "Juknis IPLM 2025 (Pelaksanaan)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "apakah, bisa, dinas, hasil, melihat, nasional?, publikasi, sebelum, sementara"}
15
+ {"id": "iplm-015", "question": "Apakah ada bimbingan teknis bagi operator di daerah?", "answer": "Ada, Perpusnas menyiapkan pelatihan berjenjang (nasional β†’ provinsi β†’ kabupaten/kota).", "alt_phrases": ["Bisakah jelaskan: Apakah ada bimbingan teknis bagi operator di daerah?", "Saya ingin tahu, apakah ada bimbingan teknis bagi operator di daerah?"], "source_doc": "Juknis IPLM 2025 (Pelaksanaan)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "apakah, bagi, bimbingan, daerah?, operator, teknis"}
16
+ {"id": "iplm-016", "question": "Bagaimana cara menangani perbedaan data antara laporan daerah dan hasil validasi pusat?", "answer": "Dilakukan masa sanggah/rekonsiliasi. Daerah diberi kesempatan klarifikasi/perbaikan sebelum finalisasi skor.", "alt_phrases": ["Bisakah jelaskan: Bagaimana cara menangani perbedaan data antara laporan daerah dan hasil validasi pusat?", "Saya ingin tahu, bagaimana cara menangani perbedaan data antara laporan daerah dan hasil validasi pusat?"], "source_doc": "Juknis IPLM 2025 (Pelaksanaan)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "antara, bagaimana, cara, daerah, data, hasil, laporan, menangani, perbedaan, pusat?, validasi"}
17
+ {"id": "iplm-017", "question": "Apakah hasil IPLM bisa digunakan untuk mengusulkan anggaran daerah?", "answer": "Bisa, IPLM memberikan evidence-based data yang sahih untuk Renstra atau RKPD.", "alt_phrases": ["Bisakah jelaskan: Apakah hasil IPLM bisa digunakan untuk mengusulkan anggaran daerah?", "Saya ingin tahu, apakah hasil iplm bisa digunakan untuk mengusulkan anggaran daerah?"], "source_doc": "Juknis IPLM 2025 (Pelaksanaan)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "anggaran, apakah, bisa, daerah?, digunakan, hasil, iplm, mengusulkan, untuk"}
18
+ {"id": "iplm-018", "question": "Apakah IPLM juga mengukur kontribusi TBM atau perpustakaan komunitas?", "answer": "Ya, TBM dan rumah baca masuk dalam populasi/sampel IPLM untuk mencerminkan ekosistem literasi luas.", "alt_phrases": ["Bisakah jelaskan: Apakah IPLM juga mengukur kontribusi TBM atau perpustakaan komunitas?", "Saya ingin tahu, apakah iplm juga mengukur kontribusi tbm atau perpustakaan komunitas?"], "source_doc": "Juknis IPLM 2025 (Pelaksanaan)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "apakah, atau, iplm, juga, komunitas?, kontribusi, mengukur, perpustakaan"}
19
+ {"id": "iplm-019", "question": "Bagaimana jika data perpustakaan sekolah sulit diperoleh karena kewenangan ada di Dinas Pendidikan?", "answer": "Dinas Perpustakaan perlu koordinasi formal dengan Dinas Pendidikan agar data sekolah bisa dihimpun.", "alt_phrases": ["Bisakah jelaskan: Bagaimana jika data perpustakaan sekolah sulit diperoleh karena kewenangan ada di Dinas Pendidikan?", "Saya ingin tahu, bagaimana jika data perpustakaan sekolah sulit diperoleh karena kewenangan ada di dinas pendidikan?"], "source_doc": "Juknis IPLM 2025 (Pelaksanaan)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "bagaimana, data, dinas, diperoleh, jika, karena, kewenangan, pendidikan?, perpustakaan, sekolah, sulit"}
20
+ {"id": "iplm-020", "question": "Apakah ada insentif bagi daerah yang skornya tinggi?", "answer": "Ya, Perpusnas mempertimbangkan penghargaan/insentif bagi daerah yang datanya lengkap dan kinerjanya meningkat.", "alt_phrases": ["Bisakah jelaskan: Apakah ada insentif bagi daerah yang skornya tinggi?", "Saya ingin tahu, apakah ada insentif bagi daerah yang skornya tinggi?"], "source_doc": "Juknis IPLM 2025 (Pelaksanaan)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "apakah, bagi, daerah, insentif, skornya, tinggi?, yang"}
21
+ {"id": "iplm-021", "question": "Apa dasar hukum terbaru pelaksanaan IPLM?", "answer": "Peraturan Perpusnas Nomor 7 Tahun 2025 tentang Pedoman IPLM, sebagai acuan resmi nasional.", "alt_phrases": ["Bisakah jelaskan: Apa dasar hukum terbaru pelaksanaan IPLM?", "Saya ingin tahu, apa dasar hukum terbaru pelaksanaan iplm?"], "source_doc": "Peraturan Perpusnas 2025 (Pedoman IPLM)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "dasar, hukum, iplm?, pelaksanaan, terbaru"}
22
+ {"id": "iplm-022", "question": "Siapa penyelenggara resmi IPLM?", "answer": "Penyelenggara adalah Perpusnas, dibantu Perpustakaan Provinsi dan Kabupaten/Kota.", "alt_phrases": ["Bisakah jelaskan: Siapa penyelenggara resmi IPLM?", "Saya ingin tahu, siapa penyelenggara resmi iplm?"], "source_doc": "Peraturan Perpusnas 2025 (Pedoman IPLM)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "iplm?, penyelenggara, resmi, siapa"}
23
+ {"id": "iplm-023", "question": "Apa saja tahapan resmi IPLM menurut Peraturan Perpusnas?", "answer": "1) pengumpulan data, 2) verifikasi & validasi, 3) penghitungan (transformasi, normalisasi, bobot), 4) penetapan hasil, 5) pemantauan & evaluasi.", "alt_phrases": ["Bisakah jelaskan: Apa saja tahapan resmi IPLM menurut Peraturan Perpusnas?", "Saya ingin tahu, apa saja tahapan resmi iplm menurut peraturan perpusnas?"], "source_doc": "Peraturan Perpusnas 2025 (Pedoman IPLM)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "iplm, menurut, peraturan, perpusnas?, resmi, saja, tahapan"}
24
+ {"id": "iplm-024", "question": "Bagaimana jika daerah tidak sepakat dengan hasil penghitungan IPLM?", "answer": "Daerah dapat mengajukan keberatan dalam 14 hari setelah menerima hasil awal. Perpusnas akan melakukan penghitungan ulang bersama daerah.", "alt_phrases": ["Bisakah jelaskan: Bagaimana jika daerah tidak sepakat dengan hasil penghitungan IPLM?", "Saya ingin tahu, bagaimana jika daerah tidak sepakat dengan hasil penghitungan iplm?"], "source_doc": "Peraturan Perpusnas 2025 (Pedoman IPLM)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "bagaimana, daerah, dengan, hasil, iplm?, jika, penghitungan, sepakat, tidak"}
25
+ {"id": "iplm-025", "question": "Siapa yang bertanggung jawab mengumpulkan data sekolah dan TBM?", "answer": "Kabupaten/Kota: SD, SMP, desa/kelurahan, kecamatan, TBM, perpustakaan khusus daerah. Provinsi: SMA/SMK, SLB, perpustakaan khusus provinsi. Pusat: perguruan tinggi, perpustakaan khusus K/L, sekolah di bawah Kemenag.", "alt_phrases": ["Bisakah jelaskan: Siapa yang bertanggung jawab mengumpulkan data sekolah dan TBM?", "Saya ingin tahu, siapa yang bertanggung jawab mengumpulkan data sekolah dan tbm?"], "source_doc": "Juknis IPLM 2025 (Pelaksanaan)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "bertanggung, data, jawab, mengumpulkan, sekolah, siapa, tbm?, yang"}
26
+ {"id": "iplm-026", "question": "Apakah ada ketentuan tentang pendanaan IPLM?", "answer": "Ya, pendanaan IPLM bersumber dari APBN. Daerah tetap perlu dukungan administratif/teknis.", "alt_phrases": ["Bisakah jelaskan: Apakah ada ketentuan tentang pendanaan IPLM?", "Saya ingin tahu, apakah ada ketentuan tentang pendanaan iplm?"], "source_doc": "Peraturan Perpusnas 2025 (Pedoman IPLM)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "apakah, iplm?, ketentuan, pendanaan, tentang"}
27
+ {"id": "iplm-027", "question": "Apakah indikator IPLM sama dengan Juknis?", "answer": "Ya, dimensi kepatuhan (koleksi, tenaga) dan kinerja (pelayanan, penyelenggaraan) tetap sama, dengan indikator resmi nasional.", "alt_phrases": ["Bisakah jelaskan: Apakah indikator IPLM sama dengan Juknis?", "Saya ingin tahu, apakah indikator iplm sama dengan juknis?"], "source_doc": "Peraturan Perpusnas 2025 (Pedoman IPLM)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "apakah, dengan, indikator, iplm, juknis?, sama"}
28
+ {"id": "iplm-028", "question": "Bagaimana hasil IPLM digunakan di daerah?", "answer": "Hasil IPLM bisa diakses daerah sebagai bahan evaluasi internal dan perencanaan literasi, serta laporan LPPD.", "alt_phrases": ["Bisakah jelaskan: Bagaimana hasil IPLM digunakan di daerah?", "Saya ingin tahu, bagaimana hasil iplm digunakan di daerah?"], "source_doc": "Juknis IPLM 2025 (Pelaksanaan)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "bagaimana, daerah?, digunakan, hasil, iplm"}
29
+ {"id": "iplm-029", "question": "Apakah ada evaluasi berkala IPLM?", "answer": "Ya, evaluasi dilakukan setiap tahun atau sewaktu-waktu bila diperlukan.", "alt_phrases": ["Bisakah jelaskan: Apakah ada evaluasi berkala IPLM?", "Saya ingin tahu, apakah ada evaluasi berkala iplm?"], "source_doc": "Juknis IPLM 2025 (Pelaksanaan)", "source_locator": "", "last_updated": "2025-09-29", "language": "id", "keywords": "apakah, berkala, evaluasi, iplm?"}
README.md CHANGED
@@ -1,14 +1,37 @@
1
- ---
2
- title: IPLM Chatbot
3
- emoji: πŸš€
4
- colorFrom: green
5
- colorTo: green
6
- sdk: gradio
7
- sdk_version: 5.47.2
8
- app_file: app.py
9
- pinned: false
10
- license: cc-by-nc-4.0
11
- short_description: Prototype chatbot iplm
12
- ---
13
-
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ # πŸ“š RAG + LLM Chatbot (Hugging Face Spaces)
3
+
4
+ Chatbot berbasis **RAG (Retrieval-Augmented Generation)** yang membaca **JSONL Q&A** dan menjawab menggunakan **LLM** via **Hugging Face Inference API**.
5
+
6
+ ## Struktur Repo
7
+ ```
8
+ /
9
+ β”œβ”€ app.py
10
+ β”œβ”€ requirements.txt
11
+ └─ IPLM_QnA_Chatbot.jsonl # file JSONL Anda (tiap baris: {"question": "...", "answer": "..."})
12
+ ```
13
+
14
+ ## Cara Deploy di Hugging Face Spaces
15
+ 1. Buat Space baru β†’ pilih **Gradio**.
16
+ 2. Unggah `app.py`, `requirements.txt`, dan `IPLM_QnA_Chatbot.jsonl`.
17
+ 3. Buka **Settings β†’ Secrets** dan tambahkan:
18
+ - `HF_TOKEN` = User Access Token Anda (scopes default).
19
+ - (Opsional) `LLM_MODEL` (default: `meta-llama/Meta-Llama-3.1-8B-Instruct`)
20
+ - (Opsional) `EMB_MODEL` (default: `sentence-transformers/all-MiniLM-L6-v2`)
21
+ - (Opsional) `HF_CHAT_URL` jika memakai endpoint TGI sendiri.
22
+ 4. Jalankan Space. UI siap dipakai.
23
+
24
+ ## Format JSONL
25
+ Setiap baris adalah objek JSON:
26
+ ```json
27
+ {"question": "Apa itu IPLM?", "answer": "IPLM adalah ... "}
28
+ ```
29
+ Alias yang didukung: `pertanyaan/jawaban`, `q/a`.
30
+
31
+ ## Catatan
32
+ - Jika `HF_TOKEN` belum diisi, aplikasi tetap berjalan dan retrieval akan muncul, namun LLM akan memberi peringatan (non-generatif).
33
+ - Cache embedding disimpan sebagai `embeddings.pkl` agar startup berikutnya cepat.
34
+ - Anda bisa **upload JSONL** baru dari panel kanan untuk memperbarui basis pengetahuan.
35
+
36
+ ## Lisensi
37
+ MIT
app.py ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import os, re, json, pickle, hashlib, requests
3
+ from pathlib import Path
4
+ import gradio as gr
5
+ import pandas as pd
6
+ import numpy as np
7
+ from sklearn.neighbors import NearestNeighbors
8
+ from sentence_transformers import SentenceTransformer
9
+
10
+ # =================== Config ===================
11
+ DATA_PATH = Path(os.getenv("DATA_PATH", "IPLM_QnA_Chatbot.jsonl")) # default filename
12
+ CACHE_EMB = Path("embeddings.pkl")
13
+ CACHE_META = Path("meta.json")
14
+
15
+ # Embedding model for retrieval
16
+ EMB_MODEL = os.getenv("EMB_MODEL", "sentence-transformers/all-MiniLM-L6-v2")
17
+
18
+ # LLM endpoint (HF Inference API / TGI-compatible / OpenAI-compatible route)
19
+ HF_CHAT_URL = os.getenv("HF_CHAT_URL", "https://api-inference.huggingface.co/v1/chat/completions")
20
+ HF_TOKEN = os.getenv("HF_TOKEN", "")
21
+ LLM_MODEL = os.getenv("LLM_MODEL", "meta-llama/Meta-Llama-3.1-8B-Instruct")
22
+
23
+ TOP_K_DEFAULT = int(os.getenv("TOP_K_DEFAULT", "4"))
24
+ TEMPERATURE_DEFAULT = float(os.getenv("TEMPERATURE_DEFAULT", "0.3"))
25
+ MAX_TOKENS = int(os.getenv("MAX_TOKENS", "512"))
26
+
27
+ SYSTEM_PROMPT = os.getenv("SYSTEM_PROMPT",
28
+ "You are an Indonesian librarian assistant. Jawab ringkas, akurat, dan sopan. "
29
+ "Gunakan HANYA informasi dari konteks yang diberikan. Jika konteks tidak memuat jawabannya, "
30
+ "katakan bahwa data tidak tersedia di basis pengetahuan."
31
+ )
32
+
33
+ # =================== Utils ===================
34
+ def norm(s: str) -> str:
35
+ if s is None: return ""
36
+ s = str(s).strip()
37
+ s = re.sub(r"\s+", " ", s)
38
+ return s
39
+
40
+ def dataset_hash(rows: list) -> str:
41
+ m = hashlib.md5()
42
+ for r in rows:
43
+ m.update((norm(r.get("question", "")) + "|" + norm(r.get("answer", ""))).encode("utf-8"))
44
+ return m.hexdigest()
45
+
46
+ def load_jsonl(path: Path) -> list:
47
+ if not path.exists():
48
+ raise FileNotFoundError(f"JSONL tidak ditemukan: {path.resolve()}")
49
+ rows = []
50
+ with path.open("r", encoding="utf-8") as f:
51
+ for line in f:
52
+ line = line.strip()
53
+ if not line: continue
54
+ obj = json.loads(line)
55
+ # support various key names
56
+ q = obj.get("question") or obj.get("pertanyaan") or obj.get("q")
57
+ a = obj.get("answer") or obj.get("jawaban") or obj.get("a")
58
+ if q and a:
59
+ rows.append({"question": norm(q), "answer": norm(a)})
60
+ if not rows:
61
+ raise ValueError("JSONL kosong atau tidak mengandung pasangan 'question'/'answer'.")
62
+ # drop dup by question
63
+ seen = set()
64
+ uniq = []
65
+ for r in rows:
66
+ if r["question"] in seen:
67
+ continue
68
+ seen.add(r["question"])
69
+ uniq.append(r)
70
+ return uniq
71
+
72
+ # =================== Index ===================
73
+ class FAQIndex:
74
+ def __init__(self):
75
+ self.rows = None
76
+ self.model = None
77
+ self.emb = None
78
+ self.nn = None
79
+
80
+ def build(self, rows: list, force=False):
81
+ self.rows = rows
82
+ # try load cache
83
+ if not force and CACHE_EMB.exists() and CACHE_META.exists():
84
+ try:
85
+ meta = json.loads(CACHE_META.read_text(encoding="utf-8"))
86
+ if meta.get("hash") == dataset_hash(rows) and meta.get("emb_model") == EMB_MODEL:
87
+ cached = pickle.loads(CACHE_EMB.read_bytes())
88
+ self.emb = cached["emb"]
89
+ self.nn = cached["nn"]
90
+ if self.model is None:
91
+ self.model = SentenceTransformer(EMB_MODEL)
92
+ return
93
+ except Exception:
94
+ pass
95
+ # build fresh
96
+ self.model = SentenceTransformer(EMB_MODEL)
97
+ # encode "Q: ...\nA: ..." for better grounding
98
+ qas = [f"Q: {r['question']}\nA: {r['answer']}" for r in rows]
99
+ self.emb = self.model.encode(qas, normalize_embeddings=True, convert_to_numpy=True, show_progress_bar=False)
100
+ self.nn = NearestNeighbors(n_neighbors=min(10, len(qas)), metric="cosine").fit(self.emb)
101
+ CACHE_EMB.write_bytes(pickle.dumps({"emb": self.emb, "nn": self.nn}))
102
+ CACHE_META.write_text(json.dumps({"hash": dataset_hash(rows), "emb_model": EMB_MODEL}, ensure_ascii=False))
103
+
104
+ def retrieve(self, query: str, top_k: int = TOP_K_DEFAULT):
105
+ if not query.strip():
106
+ return []
107
+ q_vec = self.model.encode([query], normalize_embeddings=True, convert_to_numpy=True, show_progress_bar=False)
108
+ dists, idxs = self.nn.kneighbors(q_vec, n_neighbors=min(top_k, len(self.rows)))
109
+ sims = 1.0 - dists[0]
110
+ out = []
111
+ for i, sim in zip(idxs[0], sims):
112
+ r = self.rows[int(i)]
113
+ out.append({"question": r["question"], "answer": r["answer"], "score": float(sim)})
114
+ return out
115
+
116
+ # =================== LLM Caller ===================
117
+ def call_hf_chat(messages, temperature=TEMPERATURE_DEFAULT, max_tokens=MAX_TOKENS):
118
+ if not HF_TOKEN:
119
+ # allow non-LLM fallback with a clear message
120
+ return "⚠️ HF_TOKEN belum diatur. Buka Settings β†’ Secrets dan tambahkan HF_TOKEN agar LLM aktif."
121
+ headers = {"Authorization": f"Bearer {HF_TOKEN}"}
122
+ payload = {
123
+ "model": LLM_MODEL,
124
+ "messages": messages,
125
+ "temperature": float(temperature),
126
+ "max_tokens": int(max_tokens),
127
+ "stream": False
128
+ }
129
+ r = requests.post(HF_CHAT_URL, headers=headers, json=payload, timeout=90)
130
+ try:
131
+ r.raise_for_status()
132
+ j = r.json()
133
+ return j["choices"][0]["message"]["content"]
134
+ except Exception as e:
135
+ return f"❌ Gagal memanggil LLM: {e}\nResp: {r.text[:500]}"
136
+
137
+ # =================== RAG Orchestrator ===================
138
+ def build_context(retrieved):
139
+ blocks = []
140
+ for i, r in enumerate(retrieved, 1):
141
+ blocks.append(f"[DOC {i} | score={r['score']:.2f}]\nQ: {r['question']}\nA: {r['answer']}")
142
+ return "\n\n".join(blocks)
143
+
144
+ def rag_answer(user_msg, top_k=TOP_K_DEFAULT, temperature=TEMPERATURE_DEFAULT):
145
+ hits = faq.retrieve(user_msg, top_k=int(top_k))
146
+ if not hits:
147
+ return "Maaf, saya tidak menemukan referensi di basis pengetahuan Anda."
148
+ context = build_context(hits)
149
+ messages = [
150
+ {"role": "system", "content": SYSTEM_PROMPT},
151
+ {"role": "user", "content": f"KONTEKS:\n{context}\n\nPERTANYAAN:\n{user_msg}\n\nInstruksi: Jawab berbasis KONTEKS. Jika tidak ada di konteks, jawab 'Data tidak tersedia.' "}
152
+ ]
153
+ out = call_hf_chat(messages, temperature=float(temperature), max_tokens=MAX_TOKENS)
154
+ bullets = "\n".join([f"- ({h['score']:.2f}) {h['question']}" for h in hits])
155
+ return f"{out}\n\n**Sumber terdekat:**\n{bullets}"
156
+
157
+ # =================== Data load on start ===================
158
+ faq = FAQIndex()
159
+ rows = load_jsonl(DATA_PATH)
160
+ faq.build(rows, force=False)
161
+
162
+ # =================== Upload new JSONL ===================
163
+ def upload_jsonl(file_obj):
164
+ if file_obj is None:
165
+ return gr.update(value="Tidak ada file.")
166
+ tmp = Path(file_obj.name)
167
+ tmp.replace(DATA_PATH)
168
+ if CACHE_EMB.exists(): CACHE_EMB.unlink()
169
+ if CACHE_META.exists(): CACHE_META.unlink()
170
+ global rows, faq
171
+ rows = load_jsonl(DATA_PATH)
172
+ faq = FAQIndex()
173
+ faq.build(rows, force=True)
174
+ return f"βœ… Basis pengetahuan diperbarui. Total Q&A: {len(rows)}."
175
+
176
+ # =================== UI ===================
177
+ with gr.Blocks(title="RAG + LLM (JSONL)") as demo:
178
+ gr.Markdown("# πŸ“š RAG + LLM β€” dari JSONL Q&A\n"
179
+ "Masukkan pertanyaan β†’ sistem mengambil Q&A paling relevan β†’ LLM merangkum/menjawab berdasarkan konteks.")
180
+ with gr.Row():
181
+ with gr.Column(scale=2):
182
+ chat = gr.ChatInterface(
183
+ fn=lambda msg, hist, k, t: rag_answer(msg, top_k=int(k), temperature=float(t)),
184
+ additional_inputs=[
185
+ gr.Slider(1, 10, value=TOP_K_DEFAULT, step=1, label="Top-K dokumen"),
186
+ gr.Slider(0.0, 1.0, value=TEMPERATURE_DEFAULT, step=0.05, label="Temperatur")
187
+ ],
188
+ title="Asisten Perpustakaan (RAG)",
189
+ description="Jawab *berdasarkan konteks* dari dokumen JSONL Anda.",
190
+ examples=["Apa itu IPLM?", "Bagaimana perhitungan TGM?", "Apa saja tahap pengolahan data?"]
191
+ )
192
+ with gr.Column(scale=1):
193
+ gr.Markdown("### πŸ”„ Perbarui Basis Data")
194
+ uploader = gr.File(label="Upload JSONL Q&A (keys: question, answer)")
195
+ out = gr.Textbox(label="Status", interactive=False)
196
+ uploader.change(fn=upload_jsonl, inputs=uploader, outputs=out)
197
+ gr.Markdown("Set **HF_TOKEN** di Settings β†’ Secrets untuk mengaktifkan LLM.")
198
+ if __name__ == "__main__":
199
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+
2
+ gradio==4.44.1
3
+ pandas
4
+ sentence-transformers==2.2.2
5
+ scikit-learn
6
+ requests