Guard Konteks Prompt: Menutup Lubang Senyap di Sisi Input
Perakitan konteks yang gagal diam-diam bisa kirim pertanyaan telanjang ke chat AI. Guard dua ambang di sisi input menjaga rantai riset tetap jujur.
Ringkasan
Pipeline riset AI bocor diam-diam karena prompt tanpa konteks lolos begitu aja, padahal sisi output udah dijaga ketat. Fix-nya sederhana: fungsi context_present() ngecek prompt minimal 400 karakter atau ada URL, kalo nggak ada cuma nulis warning plus tag context missing di JSON. Sengaja fail-soft, nggak blok, soalnya guard yang kejam malah bikin orang ngabaikin alarm.
Log cycle pagi ini nunjukin pagi yang tenang: rantai chat riset selesai, file jawaban tersimpan, JSON hasil bilang ok: true. Nggak ada satu pun baris error. Terus saya buka prompt yang baru saja dikirim ke chat AI: satu pertanyaan telanjang tanpa URL, tanpa potongan ledger, tanpa konteks apa pun. Perakitan konteks di pipeline gagal diam-diam, dan jawaban generik dari chat tetap diproses kayak sintesis riset yang sahih. Di titik itu jelas: lubangnya bukan di jawaban, tapi di kiriman.
Dugaan pertama saya mengarah ke model. Mungkin engine-nya lagi lemah, mungkin halusinasi biasa. Salah besar. Commit e283418 nemu penyebab yang lebih sederhana tapi lebih akurat: pipeline nggak pernah mengecek apa yang dia kirim. Semua pemeriksaan jalan di arah sebaliknya. Jawaban dicek panjang minimum, dicek sentinel, dicek pola kegagalan. Prompt yang masuk? Lolos apa adanya.
Sisi output udah beres, sisi input belum
Dua pekan lalu tool yang sama, tools/chat-research.py, baru saya bereskan di sisi output: kata kunci kegagalan cuma dipercaya sebelum jawaban ada, gate selesai wajib heading ## Sumber yang dijangkar (cerita lengkapnya di sini). Rapi di ujung belakang, tapi rantai tetap bocor dari ujung depan. Membaca jawaban yang diracik dari pertanyaan telanjang itu kayak menyetujui laporan dari staf yang nggak dikasih brief. Masalahnya bukan kualitas modelnya, tapi alurnya: artefak yang dikirim butuh gerbang yang sama dengan artefak yang diterima.
Cek kehadiran, bukan kebenaran
Solusinya sengaja dibikin membosankan. Fungsi context_present() ngecek dua hal: panjang prompt minimal MIN_PROMPT_LEN = 400 karakter, atau ada URL http(s) di dalamnya. Dua-duanya absen berarti konteks absen. Nggak ada analisis semantik, nggak ada model penilai; ambang OR khas ini dipilih justru karena murah dan hampir mustahil menolak niat yang sah. Pola gerbang antar langkah juga standar di rantai LLM: Anthropic menyebut pemeriksaan programatik di tengah rantai sebagai cara menjaga proses tetap on track[1].
Hasil pemeriksaannya pun dibikin tombolnya kecil. Konteks absen? Skrip tetap jalan, cuma nulis warning ke stderr dan menempelkan tag "context": "missing" ke JSON hasil, di jalur sukses maupun gagal. Konteks ada, tag-nya "context": "ok". Prinsipnya fail-soft: exit code nggak pernah berubah, karena menjalankan Lane B manual untuk pertanyaan sempit itu sah-sah aja. Yang butuh lampu adalah cron-nya, dan lampu itu tinggal dibaca siapa pun yang mengonsumsi JSON-nya. Mau dilanjut atau dihentikan, itu keputusan pemanggil, bukan tugas guard-nya.
Warn, bukan blok
Godaan bikin guard yang keras itu besar: anomali ketemu, skrip berhenti. Dalam automasi multi-tahap, justru itu resep dimatikannya monitoring. Google SRE nulis ini sejak lama: alert yang datang tanpa konteks jelas melatih manusia buat mengabaikannya, sampai halaman yang asli ikut kelewat[2]. Gate yang menolak niat operator yang sah bakal kenal nasib serupa: diakali, dilonggarkan, atau dibuang.
Kerangka yang saya pakai buat menilai detektor khas ini pinjam dari buku SRE: presisi, berapa banyak alarm yang bener-bener penting, lawan recall, berapa banyak kejadian penting yang berhasil kedeteksi[3]. Ambang OR di context_present() sengaja recall-first. Biaya false positive-nya satu baris warning; biaya false negative-nya satu siklus riset yang nggak sadar dirinya nyampah. Nggak seimbang sama sekali.
Satu hal yang sengaja nggak saya tambah: penilaian isi. Guard ini nggak peduli pertanyaannya bagus atau bodoh, cuma peduli pertanyaannya berdiri di atas konteks apa nggak. Batas itu penting supaya fungsinya tetap bisa dijelaskan dalam satu kalimat, dan perilakunya tetap bisa ditebak.
Perubahan ini ditutup lima asserts baru di selftest, dari 64 ke 69: prompt pendek, prompt kosong, lantai 400 karakter, plus URL http maupun https. Kecil memang. Tapi pola yang dibawanya jalan panjang: di pipeline yang merakit prompt secara programatik, validasi artefak yang dikirim itu bagian dari kontrak, bukan pelengkap. Sekarang tiap hasil rantai bawa label kejujurannya sendiri. Jawaban generik dari pertanyaan telanjang nggak lagi lewat senyap; dia lewat dengan lampu menyala.