Sebagian besar prototipe RAG terlihat sama di balik layar. Seseorang memasukkan PDF ke dalam pipeline, memotong teks menjadi potongan (chunk) 512-token yang rapi, memasukkannya ke dalam database vektor, dan menganggap tugas selesai. Untuk sebuah demo singkat, ini bisa terlihat mengesankan. Namun dalam produksi, sistem ini akan berantakan.

Chunk yang tetap tidak peduli apa yang dipotongnya. Ia akan membelah kontrak hukum tepat di tengah klausul ganti rugi. Ia akan memasukkan lima endpoint API yang tidak terkait ke dalam jendela konteks yang sama dan menenggelamkan model dalam kebisingan (noise). Ia akan memaksa Anda untuk mengambil lebih banyak fragmen daripada yang diperlukan, membengkakkan latensi, dan menghabiskan token. Hasilnya adalah jawaban setengah-setengah, halusinasi, dan pengguna yang frustrasi.

Kami membongkar total lapisan retrieval kami dan membangunnya kembali dari nol. Hasilnya adalah sistem yang mencapai recall 95 persen sambil memangkas latensi sebesar 40 persen. Berikut adalah cara tepat bagaimana kami melakukannya.

Mengapa Chunk Tetap Gagal di Produksi

Default 512-token bukanlah sebuah pilihan desain. Itu adalah produk sampingan dari jendela konteks model embedding awal dan pengaturan default pustaka yang rapi. Mudah untuk diimplementasikan, namun sangat fatal jika diandalkan.

Dokumen tidaklah seragam. Sebuah klausul hukum dapat berjalan hingga tujuh ratus token tanpa jeda yang jelas. Jika Anda memotongnya pada lima ratus dua belas, Anda menciptakan dua fragmen yang terputus. Ketika seorang pengacara atau petugas kepatuhan bertanya tentang batas kewajiban, sistem hanya mengembalikan setengah dari kewajiban tersebut. Model bahasa kemudian berhalusinasi mengisi setengah bagian yang hilang, atau lebih buruk lagi, menyangkal bahwa batas tersebut ada.

Dokumentasi API mengalami masalah yang sebaliknya. Sebuah chunk berisi lima ratus token mungkin menelan seluruh modul: header autentikasi, kode kesalahan, batas kecepatan (rate limits), dan skema webhook. Ketika seorang pengembang bertanya cara menangani AUTH_4027, retriever menyajikan campuran fungsi-fungsi yang tidak saling terkait. Model tidak punya pilihan selain merata-ratakannya menjadi informasi yang tidak jelas dan generik.

Chunking yang buruk juga membengkakkan latensi. Fragmen yang lemah berarti Anda memerlukan top-k yang lebih besar untuk mencakup suatu topik. Lebih banyak chunk berarti prompt yang lebih panjang. Prompt yang lebih panjang berarti generasi yang lebih lambat dan biaya yang lebih tinggi. Pengalaman pengguna hancur perlahan karena akumulasi masalah-masalah kecil tersebut.

Sesuaikan Chunk dengan Dokumen

Kami berhenti menghitung token dan mulai membaca materinya. Strategi chunking yang tepat bergantung pada struktur sumbernya.

Dokumen hukum membutuhkan recursive character chunking dengan batas yang peka terhadap klausul. Splitter menghormati hierarki: ia mencari tajuk bagian terlebih dahulu, lalu paragraf bernomor, kemudian jeda kalimat alami. Ia tidak akan pernah memutus sub-klausul atau membelah frasa kewajiban di antara chunk. Saat Anda mengambil kutipan tentang ganti rugi, Anda mendapatkan klausul lengkap, batasnya, dan pengecualiannya.

Dokumentasi API menuntut structure-aware chunking. Kami melakukan parsing berdasarkan definisi fungsi, bukan anggaran token. Setiap chunk berisi tanda tangan fungsi yang lengkap, deskripsi parameternya, dan catatan penanganan kesalahan yang berdekatan langsung. Jika seorang pengembang mencari metode tertentu, mereka menerima seluruh kontrak, bukan fragmen yang terjebak di dalam pembagian arbitrer.

Tiket dukungan (support tickets) bersifat berisik dan non-linear. Sebuah utas mungkin dimulai dengan laporan bug, memperkenalkan solusi sementara (workaround), dan diakhiri dengan catatan eskalasi internal. Semantic chunking mendeteksi pergeseran topik dengan mengukur kemiripan embedding antar kalimat. Kami hanya mengizinkan jeda pada batas tematik yang alami, sehingga percakapan tentang kegagalan login tetap terpisah dari tindak lanjut tentang siklus penagihan.

Wiki adalah yang tersulit. Mereka luas, saling tertaut, dan terorganisir secara longgar. Kami menggunakan agentic chunking, di mana LLM ringan membaca sebuah halaman dan memutuskan jeda berdasarkan koherensi tematik. Ini memakan biaya sedikit lebih mahal pada saat ingestion, tetapi chunk yang dihasilkan bersifat mandiri dan siap untuk retrieval. Sebuah halaman tentang praktik terbaik penerapan (deployment) akan terbagi menjadi unit-unit logis: pemeriksaan pra-penerapan, prosedur rollback, dan pengaturan pemantauan, alih-alih blok teks sembarangan.

Hybrid Retrieval: Menggabungkan Kata Kunci dan Vektor

Pencarian vektor padat (dense vector search) memahami makna. Namun, ia sangat buruk dalam menangani string yang tepat. Jika pengguna mencari kode kesalahan yang presisi seperti AUTH_4027 atau nama pelanggan seperti "Stark Industries", embedding vektor dapat meleset dari target karena mereka mengoptimalkan kedekatan konseptual, bukan akurasi tingkat karakter.

Pencarian kata kunci murni melalui BM25 memiliki kelemahan sebaliknya. Ia akan menemukan AUTH_4027 dengan sempurna, tetapi ia akan melewatkan jembatan konseptual antara "kegagalan otorisasi" dan "login ditolak".

We run both in parallel. BM25 and vector search operate independently over the same corpus. Their result lists are merged using Reciprocal Rank Fusion, which reorders candidates by balancing their positional ranks. You do not need calibrated weights. You simply get the precision of exact match and the intuition of semantic search in a single ranked list.

Then we add a cross-encoder reranker. This is a separate model that scores each passage against the original query, producing a relevance signal far finer than either retriever alone. It adds about 50 milliseconds of latency. It increases recall by 15 percent. If you care about answer quality, that trade is non-negotiable.

Query Expansion: Fix the Search Before It Starts

Bad queries are the dirty secret of every retrieval system. Users do not write like your embedding space. They type "it broke." They paste truncated stack traces. They use internal jargon your index has never seen.

We transform the query before it ever touches the index. First, we expand a single query into three to five diverse search terms. If the original is "payment failed," we also search for "transaction error," "billing declined," and "charge unsuccessful