Jawaban Tetap Hidup Saat LLM Mati
LLM router mati saat streaming jawaban. Yang muncul: event meta fallback lalu pengunjung tetap dapat jawaban dari cache atau knowledge.
/articles/ask yang harusnya streaming jawaban lewat SSE, tiba-tiba mati di test. Logs nge-repeat error terus, LLM provider timeout. Saya reload, coba query lain, tetap gagal total. Kotak pencarian AI di blog ini langsung jadi pajangan.
Tapi terus saya perhatiin stream-nya lebih teliti. Sebelum timeout, ada event yang muncul:
event: meta
data: {"fallback":"cache", "provider":"..."}
Dan setelah event itu, jawaban tetap keluar. Bukan jawaban yang paling cerdas, tapi ada. Pengunjung nggak lihat error.
Saya kira Redis-nya udah gila, serving data expired. Ternyata nggak. Yang terjadi jauh lebih sistematis dari itu, dan semuanya datang dari satu commit.
Cache invalidation tanpa SCAN
Sebelum commit 151147a, kalau LLM mati, selesai. Nggak ada fallback, nggak ada cache, jawaban gagal mentah-mentah.
Sekarang cache-nya pakai Redis dengan TTL 24 jam [4] source 4. Tapi yang bikin cerdik bukan TTL-nya, melainkan cara invalidasi kunci lama diatur.
Setiap cache key pakai prefix versi: askver:kn untuk knowledge, askver:art untuk artikel. Versi ini naik pakai INCR [5] source 5 setiap kali knowledge di-mutasi atau artikel baru dipublish. INCR itu atomic, O(1), dan mulai dari 0 kalau kunci belum ada. Begitu versi naik, semua kunci lama yang pakai versi sebelumnya langsung unreachable. Nggak perlu SCAN, nggak perlu DEL massal. Kunci lama bakal hancur sendiri habis TTL-nya habis [4] source 4.
# bump versi saat knowledge berubah -> kunci lama invalid otomatis
redis-cli INCR askver:kn
# cache key dibangun dengan versi terkini:
# askcache:kn:42:<query> <- 42 = nilai askver:kn saat jawaban dibuat
# setelah bump ke 43, semua kunci ...:42:... tak akan dibaca lagi,
# lalu hancur sendiri saat TTL 24 jam habis
Saya pribadi lebih suka pendekatan ini daripada invalidasi per-key atau SCAN pattern. Lebih predictable, lebih sedikit surprise di production.
Retrieval mode dan chain fallback
Endpoint /articles/ask sekarang terima parameter mode: search, about, atau mixed. Mode about yang menarik. Dia jawab sebagai persona pemilik situs dari knowledge terkurasi, cuma pull artikel yang relevan banget sama query-nya. Mode search lebar-lebar kayak sebelumnya. mixed campuran keduanya.
Jadi kalo pengunjung nanya "kamu pakai framework apa buat blog ini", mode about bisa jawab langsung dari knowledge sambil nunjukin artikel terkait. Nggak perlu LLM generate dari nol.
Dan di sinilah chain fallback jadi kunci. Tiga lapis:
async def stream_answer(query, mode):
# 1. coba LLM
try:
async for chunk in llm_stream(query, mode):
yield chunk
except LLMError:
# 2. replay dari cache
cached = get_cached_answer(query)
if cached:
yield sse_event("meta", {"fallback": "cache"})
yield cached
return
# 3. persona tanpa LLM dari knowledge
answer = persona_answer(query, knowledge)
yield sse_event("meta", {"fallback": "no_llm"})
yield answer
Output dari fallback nggak pernah di-cache. Stream kosong juga nggak lagi dianggap sukses. Dua aturan kecil yang mencegah data sampah numpuk di Redis.
SSE-nya sendiri pakai MIME text/event-stream [6] source 6, jadi client tinggal dengerin stream tanpa polling.
Aturan "fallback nggak pernah di-cache" itu bukan detail kosmetik. Jawaban persona dibuat dari knowledge yang bisa berubah lima menit lagi, dan replay cache hanya valid karena kuncinya diikat versi namespace. Kalau output fallback ikut di-cache tanpa versi, jawaban darurat bisa hidup lebih lama dari masalahnya. Sama-sama menjaga memori: TTL 24 jam memang otomatis, tapi membiarkan Redis jalan dengan AOF berarti jawaban cache selamat walau server restart.
Yang paling saya suka justru event meta-nya. Saat terjadi keanehan, saya tinggal baca stream: fallback=cache artinya LLM mati tapi jawaban lama masih relevan, fallback=no_llm artinya jawaban dirakit dari knowledge tanpa model sama sekali. Debugging jadi soal membaca satu baris, bukan menebak-nebak dari log server.
Ketiga fitur ini saling nahan: cache invalidation yang clean bikin jawaban cache tetap relevan, fallback berlapis dengan meta event yang bisa di-debug, dan mode retrieval yang bikin jawaban tetap berguna meskipun LLM mati total. Sebelum commit ini, satu komponen mati = semuanya mati. Sekarang setiap lapis punya jalan keluar sendiri.
Detail implementasi lengkapnya ada di commit 151147a di repo [9] source 9.
Lanjutan teknisnya ada di artikel jawaban AI streaming lewat fetch biasa.