Semantic Caching untuk LLM: Hemat Biaya Pertanyaan Berulang

Foto oleh PantheraLeo1359531 on Wikimedia Commons
Semantic caching menyimpan vektor embedding bersama setiap pasangan pertanyaan dan jawaban yang di-cache, lalu membandingkan embedding kueri baru dengan vektor-vektor tersimpan itu menggunakan cosine similarity. Jika kueri tersimpan cukup dekat dengan kueri baru, jawaban yang tersimpan dikembalikan tanpa perlu memanggil model lagi. Ini memungkinkan pertanyaan dengan kata-kata berbeda namun makna sama berbagi satu respons cache yang sama.
Prompt caching, seperti yang dibangun ke dalam Anthropic dan Amazon Bedrock, mencocokkan prefix yang identik byte demi byte seperti system prompt atau dokumen yang berulang, lalu melewati perhitungan ulang bagian tersebut. Semantic caching mencocokkan berdasarkan makna, bukan teks persis, sehingga bisa menangkap pertanyaan pengguna yang diparafrasakan yang tidak bisa dilihat oleh prompt caching. Kedua teknik ini menyasar bagian request yang berbeda dan bekerja baik jika digabungkan.
Mulailah dengan konservatif, sekitar 0,85 hingga 0,9 cosine similarity, dan validasikan terhadap set kueri pengguna nyata berlabel sebelum melonggarkannya. Ambang batas yang lebih rendah meningkatkan hit rate tetapi juga meningkatkan kemungkinan mengembalikan jawaban untuk pertanyaan yang sebenarnya tidak ditanyakan pengguna. Konten yang jawabannya salah bisa berdampak nyata, seperti harga atau kepatuhan, sebaiknya menggunakan ambang batas lebih tinggi dibanding trafik FAQ umum.
Ya, ini disebut false hit dan menjadi risiko utama teknik ini. Hal ini terjadi ketika dua pertanyaan memiliki embedding yang berdekatan meskipun maksudnya berbeda secara signifikan, dan ambang batasnya cukup longgar untuk menganggap keduanya cocok. Mitigasinya adalah menetapkan ambang batas yang konservatif, mencatat skor kemiripan pada setiap hit, dan memvalidasi ulang ambang batas setiap kali model embedding diganti.
Hindari untuk beban kerja di mana setiap permintaan benar-benar baru, seperti penulisan kreatif atau pembuatan kode, karena tidak ada yang bisa digunakan ulang dan pencarian embedding hanya menambah latensi. Berhati-hatilah juga pada domain berisiko tinggi seperti saran medis atau hukum kecuali ambang batas diatur sangat konservatif dan dipadukan dengan invalidasi cache yang ketat, karena jawaban yang basi atau kira-kira benar di sana bisa menimbulkan kerugian nyata.

Foto oleh PantheraLeo1359531 on Wikimedia Commons
Amati analitik chatbot produksi mana pun selama seminggu dan sebuah pola akan terlihat jelas: sekelompok kecil pertanyaan menyumbang sebagian besar trafik, dan hampir tidak ada yang ditulis dengan kata-kata identik. Pengguna bertanya apa kebijakan pengembalian dana, lalu bagaimana jika saya ingin uang saya kembali, lalu apakah saya bisa dapat refund. Tiga kalimat berbeda, satu maksud yang sama, tiga panggilan model penuh dengan biaya penuh.
Exact-match caching tidak bisa melihat pola ini. Ia melakukan hashing terhadap string prompt secara literal, sehingga perubahan kata, tanda baca, atau urutan kata langsung dianggap cache miss. Semantic caching mengatasi masalah ini dengan membandingkan makna, bukan karakter, menggunakan vektor embedding dan ambang batas kemiripan untuk memutuskan kapan jawaban tersimpan cukup mirip untuk digunakan kembali. Artikel ini membahas cara kerjanya, cara menentukan ambang batas, cara melakukan invalidasi entri yang basi, dan kapan teknik ini lebih unggul dibanding exact-match prompt caching bawaan penyedia seperti Anthropic dan Amazon Bedrock.
Prompt caching sisi penyedia, seperti yang disediakan secara native oleh Anthropic dan Amazon Bedrock, bekerja pada prefix yang identik byte demi byte. Ini sangat efektif untuk konteks panjang dan statis seperti system prompt, dokumen, atau skema tool yang berulang di setiap giliran percakapan, karena penyedia layanan cukup melewati perhitungan ulang status attention untuk prefix yang persis sama itu. Namun ia tidak berbuat apa-apa untuk bagian pertanyaan pengguna yang sesungguhnya, karena bagian itu memang berubah di setiap permintaan. Semantic caching menyasar tepat pada celah tersebut, yaitu bagian bahasa alami yang variatif dari permintaan yang tidak bisa disentuh oleh cache milik penyedia.
Mekanismenya sederhana meskipun manfaatnya besar. Setiap kueri yang masuk diubah menjadi vektor embedding berpanjang tetap menggunakan model embedding yang kecil dan murah, lalu dibandingkan dengan vektor kueri yang tersimpan sebelumnya menggunakan cosine similarity, yang mengukur sudut antara dua vektor, bukan jarak mentahnya. Jika vektor tersimpan yang paling dekat melewati ambang batas kemiripan, jawaban yang tersimpan langsung dikembalikan dalam hitungan milidetik dan permintaan tidak pernah menyentuh model bahasa. Jika tidak ada yang melewati ambang batas, permintaan diteruskan ke model seperti biasa, lalu kueri baru, embeddingnya, dan jawaban segar tersebut ditulis kembali ke dalam cache untuk digunakan berikutnya. Di balik layar, sebagian besar penerapan produksi tidak melakukan pemindaian brute-force ke setiap vektor tersimpan. Mereka mengandalkan indeks tetangga terdekat aproksimasi, umumnya graf HNSW atau struktur berbasis IVF, sehingga pencarian tetap berada di kisaran milidetik meskipun ada jutaan entri tersimpan. Redis, Postgres dengan pgvector, dan basis data vektor terkelola semuanya mendukung pola ini, dan pemilihan indeks lebih banyak bergantung pada apa yang sudah dioperasikan tim Anda daripada performa mentah, karena perbedaannya baru terasa pada skala yang sangat besar.
# Read-through semantic cache lookup (pseudocode)
def handle_query(user_message: str) -> dict:
query_vec = embed(user_message) # e.g. 1024-d model
hit = vector_store.search(query_vec, k=3) # cosine distance index
if hit and hit.score >= SIMILARITY_THRESHOLD: # e.g. 0.85
return {"response": hit.answer, "source": "cache"}
answer = call_llm(user_message) # cache miss -> real call
vector_store.upsert(query_vec, answer, ttl=86400)
return {"response": answer, "source": "llm"}
Simpan versi model embedding bersama setiap entri cache. Jika suatu saat Anda mengganti model embedding, vektor lama tidak bisa dibandingkan dengan vektor baru dan setiap entri harus dihitung ulang, bukan hanya dibaca ulang.
Ambang batas adalah satu-satunya pengaturan yang menentukan apakah cache Anda menghemat biaya atau justru menyajikan jawaban yang salah. Terlalu rendah, pertanyaan yang tidak berkaitan mulai mengembalikan jawaban satu sama lain, yang lebih buruk daripada cache miss karena pengguna mendapat informasi yang salah dengan penuh keyakinan tanpa sinyal kesalahan. Terlalu tinggi, Anda hampir tidak memperoleh peningkatan dibanding exact-match caching, karena hanya frasa yang hampir identik yang lolos. Implementasi yang dipublikasikan cenderung berkumpul pada rentang sempit: cosine similarity sekitar 0,8 hingga 0,85 menjadi titik awal umum untuk tanya jawab umum, mengetat menuju 0,9 atau lebih tinggi untuk konten yang jawabannya salah bisa berdampak nyata, seperti harga, kelayakan refund, atau konten medis maupun hukum.
| Rentang ambang batas | Kasus penggunaan umum | Profil risiko |
|---|---|---|
| 0,60 hingga 0,75 | Pengelompokan topik luas, saran pencarian internal | Risiko false-hit tinggi, hindari untuk jawaban langsung |
| 0,80 hingga 0,88 | FAQ umum dan chatbot dukungan pelanggan | Seimbang, perlu pemantauan dan umpan balik |
| 0,90 ke atas | Jawaban harga, kepatuhan, medis, atau hukum | Risiko false-hit rendah, hit rate lebih rendah |
Jangan menentukan ambang batas sekali lalu melupakannya. Perilaku model embedding bisa bergeser antar versi, dan ambang batas yang dikalibrasi untuk satu model bisa diam-diam menghasilkan false hit setelah pembaruan. Validasi ulang menggunakan set kueri berlabel setiap kali Anda mengganti model embedding.
Semantic cache yang tidak pernah kedaluwarsa adalah beban, bukan fitur, karena dunia di balik jawaban yang tersimpan terus berubah meskipun pertanyaannya tetap sama. Bangun mekanisme invalidasi sejak hari pertama, bukan sebagai tugas tambahan belakangan.
Teknik ini bukan peningkatan universal. Ia unggul pada pola trafik tertentu dan justru bisa merugikan pada pola lainnya.
Tim yang melaporkan penghematan terbesar hampir selalu memadukan semantic caching dengan normalisasi kueri dasar terlebih dahulu, yaitu mengubah ke huruf kecil, menghapus spasi berlebih, dan membuang kata pengisi sebelum di-embed. Satu langkah itu saja seringkali menaikkan hit rate lebih banyak daripada perubahan ambang batas apa pun.
Jika Anda menambahkan ini ke chatbot atau pipeline RAG yang sudah ada, versi paling minimal membutuhkan empat komponen: model embedding yang sudah Anda percaya, indeks vektor yang mendukung pencarian tetangga terdekat aproksimasi dengan skor kemiripan yang dikembalikan bersama setiap hit, ambang batas yang telah Anda validasi terhadap set kueri nyata berlabel, bukan sekadar tebakan, serta jalur TTL dan invalidasi yang terikat pada konten apa pun yang menjadi dasar jawaban tersebut. Mulailah dengan ambang batas yang konservatif, ukur hit rate dan false-hit rate secara terpisah menggunakan umpan balik yang tercatat, dan longgarkan ambang batas secara bertahap daripada mulai longgar lalu mengetat setelah terjadi insiden. Pantau tiga angka sejak minggu pertama: cache hit rate, estimasi penghematan biaya per hari berdasarkan harga per token model, dan sampel hit yang ditinjau secara manual untuk menangkap false positive yang tidak bisa terlihat oleh logging otomatis Anda sendiri. Sebagian besar tim menemukan penghematan nyata pertama dalam hitungan hari, dan penghematan tambahan terbesar setelah itu datang dari normalisasi kueri dan penyetelan ambang batas, bukan dari mengganti model embedding atau basis data vektor.