Saat Pencarian AI Perlu Tahu Isi Situsnya Sendiri
Ketik hai ke pencarian AI dulu dapat error no_results. Biang keladinya batas panjang kata FULLTEXT, bukan prompt yang salah.
Pengguna mengetik hai ke kotak pencarian AI di blog. Dulu, endpoint /articles/ask cuma membalas error no_results. Sekarang, pertanyaan yang sama dijawab dengan informasi tentang situs, lengkap dengan sumber dari halaman Beranda, Tentang, Aktivitas, atau Portofolio.
Awalnya saya mengira masalahnya ada di prompt. Mungkin model terlalu kaku membaca kueri pendek. Atau mungkin konteks artikel yang dikirim ke model terlalu sedikit.
Ternyata modelnya nggak sempat bekerja. Query hai lebih dulu masuk ke MariaDB, lalu diproses lewat MATCH...AGAINST dalam natural language mode. Hasil pencariannya kosong, jadi tidak ada konteks yang bisa diberikan ke AI.
MariaDB punya batas panjang kata untuk FULLTEXT. Pada InnoDB, kata yang panjangnya kurang dari 3 karakter tidak masuk index. MyISAM memakai batas 4 karakter.[1] Jadi kata pendek seperti hai bisa hilang sebelum proses retrieval dimulai. Natural language mode juga emang mencari kata yang cocok dengan isi artikel, bukan memahami maksud small talk atau pertanyaan umum tentang situs.[1]
Masalahnya bukan cuma di panjang kata
Kueri tentang apa ini juga bisa gagal walaupun sebagian katanya lebih panjang dari batas minimum. FULLTEXT tetap mencari kecocokan kata di artikel. Ia nggak otomatis tahu bahwa pengguna sedang menanyakan identitas situs, bukan mencari artikel yang memuat kata tentang.
Saya bisa saja menambah sinonim ke isi artikel atau menurunkan batas pencarian. Tapi itu cuma menambal kasus tertentu. Menambah vector database juga terlalu berat untuk kebutuhan ini. Situsnya kecil, dan informasi seperti “situs ini berisi apa” sudah tersedia dalam bentuk data yang jelas di frontend.
Saya pilih menambah satu lapisan retrieval sederhana: knowledge situs yang dikurasi.
Commit 86ba567 menambahkan file site_knowledge.json. Isinya empat entri untuk halaman Beranda, Tentang, Aktivitas, dan Portofolio. Data itu diambil dari file frontend, lalu setiap halaman diberi keywords dalam dua bahasa.
Contoh satu entrinya kira-kira seperti ini:
{
"url_path": "/about",
"title_id": "Tentang",
"title_en": "About",
"keywords_id": ["tentang", "jasa", "keahlian", "pengalaman"],
"keywords_en": ["about", "services", "skills", "experience"],
"summary_id": "Layanan web development, DevOps, dan integrasi AI.",
"summary_en": "Web development, DevOps, and AI integration services."
}
Endpoint /articles/ask sekarang menjalankan dua pencarian. Pertama, query tetap dicari ke artikel lewat FULLTEXT. Kedua, query dicocokkan ke keywords dari site_knowledge.json.
Struktur sederhananya seperti ini:
def _match_site_knowledge(query: str) -> list[dict]:
q = query.lower()
matches = []
for entry in SITE_KNOWLEDGE:
kws = entry["keywords_id"] + entry["keywords_en"]
if any(kw in q for kw in kws):
matches.append(entry)
if len(matches) == 2:
break
return matches
Implementasi aslinya tetap perlu menangani normalisasi query dan format response. Batasnya sengaja maksimal dua halaman per jawaban. Untuk pertanyaan pendek, mengirim seluruh isi situs ke model cuma menambah noise.
Jawaban tetap jalan saat FULLTEXT kosong
Perubahan pentingnya bukan cuma menambah JSON. Flow jawaban sekarang tidak berhenti saat hasil FULLTEXT kosong.
Kalo query cocok dengan knowledge situs, halaman-halaman itu masuk sebagai sumber walaupun skor FULLTEXT-nya null. Sumber halaman situs juga dipasang di depan daftar sumber, supaya model punya konteks yang lebih relevan untuk pertanyaan umum.
Contohnya, query hai bisa menghasilkan konteks dari Beranda. Query tentang apa ini bisa cocok ke halaman Tentang. Query apa yang sedang dikerjakan bisa mengarah ke Aktivitas.
Artikel tetap dicari lewat database untuk pertanyaan berbasis topik. Knowledge terkurasi menangani pertanyaan tentang situsnya sendiri. Dua kebutuhan ini emang beda, jadi memaksa satu mesin retrieval menangani semuanya bukan pilihan yang bagus.
Ada konteks lain yang tetap perlu dijaga di endpoint FastAPI, terutama validasi request dan header API. Langkah keamanan dasar seperti ini sebaiknya dibereskan sebelum menambah logika AI.[2] Tapi untuk kasus no_results, menambah security middleware bukan jawabannya. Masalahnya ada di strategi retrieval.
Saya lebih pilih JSON sederhana daripada vector DB untuk kasus sekecil ini. File itu gampang direview, gampang diubah, nggak butuh proses embedding, dan nggak menambah service baru. Kalo nanti isi situs sudah ribuan halaman atau query semantik mulai dominan, barulah vector retrieval layak dipertimbangkan.
Untuk sekarang, empat entri terkurasi sudah cukup menutup celah yang paling terasa. Detail perubahan bisa dilihat di [commit 86ba567 di GitHub][3].
Hasilnya bukan sistem yang lebih pintar. Sistemnya cuma punya jalur yang tepat untuk jenis pertanyaan yang berbeda. FULLTEXT tetap dipakai buat mencari artikel, sementara knowledge kecil berbasis keyword menangani identitas situs dan small talk.
Pola ini juga saya pakai sebagai pengingat: RAG untuk situs kecil nggak harus dimulai dari vector database. Kadang satu file JSON, beberapa keywords, dan fallback yang benar sudah cukup.
Kalau ingin melihat konteks teknis lain di blog ini, lanjut ke jawaban AI streaming di blog lewat fetch biasa.