Jawaban AI Terpotong, Ternyata Cuma Soal Budget Token
Jawaban about-mode terpotong di tengah kalimat. Ternyata bukan model yang error, tapi konstanta batas token output 512 yang saya salin dari template lama.
Ringkasan
Jawaban about-mode terpotong di tengah kalimat karena konstanta _ASK_MAX_ANSWER_TOKENS = 512 disalin dari template lama tanpa pernah diukur. Budget itu mepet untuk format multi-bagian yang menggabungkan ringkasan, section PENJELASAN, dan CTA dalam satu generasi. Perbaikannya satu baris, naikkan jadi 900, plus pelajaran: ukur bentuk jawaban target dulu sebelum menetapkan batas token.
Saya ngecek halaman tentang di blog, jawaban about-mode yang harusnya berisi ringkasan, section PENJELASAN, lalu CTA malah berhenti di tengah kalimat. Nggak ada error, nggak ada respons kosong. Teksnya muncul, tapi kalimat terakhirnya kayak digigit. Toast sukses muncul, riwayat chat nyimpan, semuanya kelihatan normal kecuali isi jawabannya nggak lengkap.
Dugaan pertama saya tentu salah arah: modelnya lagi aneh, atau koneksi ke penyedia API putus di tengah jalan. Saya coba ulang beberapa kali, hasilnya sama. Jawaban selalu mentok di panjang yang kira-kira konsisten. Pola seperti ini justru melemahkan dugaan koneksi putus, karena koneksi yang drop biasanya nggak konsisten dari percobaan ke percobaan.
Satu Konstanta, Satu Baris
Bukan bug di model. Bukan masalah jaringan. Saya buka use_cases.py, cari konstanta yang ngatur batas token output, dan ketemu _ASK_MAX_ANSWER_TOKENS = 512. Angka itu saya salin dari template lama tanpa pernah ngukur seberapa panjang jawaban about-mode yang sebenarnya. Masalahnya, jawaban about-mode bukan satu paragraf pendek: ada ringkasan, ada section PENJELASAN yang bisa panjang, ada CTA di akhir, dan semuanya di-generate dalam satu completion. Budget 512 token buat format multi-bagian kayak gini itu mepet.
Perbaikannya satu baris: 512 jadi 900. Setelah itu jawaban terpanjang sekalipun lolos utuh dengan sisa ruang. Total waktu nye-selidiki lebih lama dari waktu memperbaiki, seperti biasa.
# before: multi-part answers truncated mid-sentence
_ASK_MAX_ANSWER_TOKENS = 512
# after: fits the longest grounded answer with headroom
_ASK_MAX_ANSWER_TOKENS = 900
Batas Token Itu Budget Output, Bukan Sisa Konteks
Parameter batas token di penyedia API besar bekerja dengan kontrak yang sama: membatasi jumlah token yang boleh di-generate sebagai jawaban, berdiri sendiri dari panjang prompt. Dokumentasi Google Cloud merangkum konversinya: satu token kira-kira empat karakter, jadi 100 token setara sekitar 60 sampai 80 kata [4]. Kalau target jawaban about-mode sekitar 480 kata, budget 512 token sudah pasti kurang bahkan sebelum generasi dimulai.
Semua penyedia besar juga ngasih sinyal resmi kalau jawaban berhenti karena limit, bukan karena model selesai. Di API OpenAI, finish_reason bernilai length kalau batas token yang saya tentukan sendiri tercapai [1]. Sinyal ini bisa dibaca begini: model siap nulis lagi, tapi saya yang menyuruhnya berhenti.
Polanya mirip di penyedia lain. Di Claude, stop_reason bernilai max_tokens, dan dokumentasinya langsung menyebut dua pilihan: naikkan nilai max_tokens atau lanjutkan respons [2]. Di Gemini, finishReason bernilai MAX_TOKENS dengan arti serupa [3]. Tiga nama berbeda untuk satu kejadian yang sama.
Satu catatan khusus OpenAI: parameter max_tokens kini ditandai deprecated, digantikan max_completion_tokens, dan nama lama itu nggak kompatibel dengan model o-series [1]. Angka batas yang saya pasang di kode berlaku sebagai plafon ketat output; tanpa parameter itu pun, plafon jendela konteks model tetap ada dan mekanisme sinyalnya tetap sama.
Ukur Dulu, Angka Kemudian
Angka 512 di kode saya lahir dari kebiasaan, bukan dari pengukuran. Dokumentasi OpenAI sendiri menyebut parameter ini bisa dipakai untuk mengontrol biaya teks yang di-generate via API [1], jadi wajar kalau orang tergoda pasang angka kecil. Tapi angka hemat yang memotong jawaban di tengah kalimat bukan hemat, itu risiko pengalaman pembaca rusak di halaman yang justru jadi etalase.
Cara yang lebih sehat: tulis dulu bentuk jawaban target, hitung kira-kira butuh berapa kata, konversi ke token dengan patokan 100 token sekitar 60 sampai 80 kata [4], lalu pasang headroom secukupnya. Kalau ternyata jawaban yang terlanjur terpotong masih berharga, dokumentasi Claude menunjukkan jalur lanjut: kirim ulang respons yang sudah ada lalu minta model meneruskan dari titik berhenti [2]. Di kasus saya, menaikkan plafon jauh lebih murah daripada membangun mekanisme lanjut.
Opini saya: jangan pernah menyalin angka default dari template atau repo orang lain. Ukur bentuk jawaban yang memang mau ditampilkan, baru tetapkan angkanya. Angka 512 kelihatan aman selama kita belum pernah lihat jawaban sendiri terpotong di halaman live, dan persis di titik itu angka semacam ini berhenti bersalah tanpa terlihat.
Kasus jawaban yang selamat dari kegagalan lain pernah saya tulis di jawaban tetap hidup saat LLM mati, dan soal marker CTA yang bocor ke percakapan ada di marker CTA yang bocor ke chat. Dua-duanya sama pelajarannya: detail kecil di jalur jawaban baru kelihatan pas sudah live.
Sumber: