Belakangan ini RAG terkadang dipandang buruk, entah karena orang menganggapnya sangat sepele (memang mudah untuk memulai, tetapi tidak untuk menskalakannya) atau merasa RAG telah digantikan oleh ‘sistem agentik’ (yang dalam banyak kasus, jika ditelaah, segera terlihat sangat mirip dengan RAG…)
Blog ini menyajikan beberapa contoh praktis untuk menunjukkan cara kami mengatasi tantangan umum berikut:
Menangani gabungan data teks dan numerik serta alasan pendekatan itu menggagalkan RAG sederhana: kata kunci bertabrakan dan angka tidak memiliki makna semantik.
Mengapa embedding yang mengutamakan ringkasan membantu: buat ringkasan deskriptif singkat untuk setiap potongan, lalu lakukan embedding dan kueri berdasarkan ringkasan tersebut.
Cara membuat ringkasan kontekstual: sertakan konteks dokumen induk agar statistik yang bentuknya serupa tetap dapat dibedakan.
Kapan harus mengandalkan kode dan model Pydantic: jika konten harus dipertahankan kata demi kata, gabungkan kode khusus dan/atau model Pydantic dengan panggilan LLM agar hasilnya andal.
Dasar-dasarnya
Sistem RAG mendukung berbagai hal, mulai dari bot dukungan hingga asisten pengetahuan internal.
Di balik layar, biasanya Anda akan:
Membagi dokumen sumber menjadi beberapa potongan
Menyematkan setiap potongan ke dalam ruang vektor
Mengambil K potongan teratas saat kueri dijalankan
Menghasilkan jawaban berdasarkan potongan-potongan tersebut
Toolkit populer—LangChain, LlamaIndex, Filestore milik OpenAI—membuat langkah-langkah tersebut nyaris sepele. Namun, dalam pipeline dunia nyata, Anda akan menjumpai data yang bukan sekadar teks padat, dan RAG dasar bisa kesulitan menanganinya. Pada bagian berikut, kami akan menunjukkan contoh konkret tantangan data dan membangun solusinya secara bertahap seiring bertambahnya kompleksitas.
Ketika data Anda bukan sekadar teks (sebenarnya cukup umum)
Perhatikan potongan data berikut dalam konteks gim:
JSON
Embedding berfungsi berkat hubungan antarkata yang dipelajari melalui makna semantik dan tata bahasa. Data di atas merupakan gabungan teks dan angka. Di luar konteks tersebut, angka-angkanya tidak memiliki hubungan dengan kata-katanya. Jadi, potongan data ini pada dasarnya merupakan gabungan kata-kata yang cukup deskriptif dan sejumlah angka acak.
Sebenarnya hal ini tidak akan menjadi masalah jika hanya jenis data ini yang kita miliki, karena kita masih dapat mengambilnya berdasarkan embedding dari beberapa kata deskriptif yang tersedia (atau cukup menggunakan text-to-SQL). Namun, bagaimana jika potongan ini terkubur di antara banyak potongan berisi teks padat yang juga memuat kata-kata tersebut? Contohnya:
JSON
Sekarang bayangkan kita ingin mengambil jawaban untuk “What is the attack range with Draconic Ascension?” Kemungkinan besar kita tidak dapat mengambil potongan relevan yang diinginkan karena potongan itu tenggelam dalam derau dari potongan lain yang memuat kata kunci sama.
Akar masalahnya adalah kita tidak dapat membedakan potongan-potongan data ini dengan baik, meskipun masing-masing memuat jenis informasi yang berbeda tentang topik yang sama. Bisakah kita memperkaya atau menyempurnakannya? Tentu saja bisa:smile:
Perkaya data Anda dengan meringkasnya—ya, Anda tidak salah baca
Alih-alih langsung melakukan embedding terhadap potongan itu sendiri, kita dapat terlebih dahulu membuat ringkasan yang menjelaskan isi datanya, lalu melakukan embedding dan pengambilan berdasarkan ringkasan tersebut. Pada tahap pembuatan jawaban, kita tetap menggunakan data asli yang ditautkan ke ringkasan tersebut.
Jadi, untuk dua contoh potongan di atas, kita akan membuat ringkasan seperti berikut:
Attack statistics of range, speed, and damage (default and with Draconic Ascension).
Description and details of the Draconic Ascension, including activation conditions, visual effects, and lore.
Kemudian, kita juga memperkaya kueri agar “selaras” dengan ringkasan. Misalnya, kita akan mengubah “What is the attack range with Draconic Ascension?” menjadi “What is the statistics of attack range with Draconic Ascension?” Hal ini sangat penting ketika kueri pengambilan berasal dari pengguna nonteknis yang bertanya dengan bahasa manusia ~~“bebas”~~ biasa. Lagi pula, mereka tidak perlu mengetahui atau memikirkan cara kerja RAG untuk memaksimalkan presisi dan recall.


Jangan mengabaikan konteks (berlaku juga dalam kehidupan secara umum)
Skenario berikutnya adalah menangani sangat banyak potongan data yang tampak sama, seperti di bawah ini:
Plain Text
Jika tetap memakai pendekatan yang sama, bayangkan kita menanyakan “what is character X’s attack range?” Kita akan mengandalkan keberuntungan untuk menebak dari ringkasan yang baru dibuat karena semuanya juga akan tampak sangat mirip. Lalu, bagaimana cara membedakannya?
Jawaban sederhananya: berikan konteks. Kita cukup menyertakan referensi ke dokumen induk potongan tersebut dalam potongan data, misalnya {”character”: “X”} untuk kasus ini. Dengan begitu, kita dapat mengambil data yang tepat untuk karakter X secara akurat meskipun memiliki data yang sama untuk karakter Y dan Z.
Namun, pendekatan yang lebih baik dan lebih mudah diterapkan secara umum adalah membuat ringkasan kontekstual untuk potongan tersebut. Artinya, alih-alih hanya membuat ringkasan potongan data itu sendiri, kita dapat memasukkan dokumen induk sekaligus potongannya untuk membuat ringkasan kontekstual umum yang menjelaskan hubungan potongan tersebut dengan dokumen induknya, misalnya:
This chunk provides detailed statistics of … for character X. The chunk fits into the full document by showing X’s strength in attack speed…
This chunk provides detailed statistics of … for character Y. The chunk fits into the full document by showing Y’s boosted stats with her special ability…
This chunk provides detailed statistics of … for character Z. The chunk fits into the full document by showing Z’s stats that’s well suited as a tank in team matches…
Metode ini (yang sebagian terinspirasi oleh Anthropic) mungkin tampak berlebihan untuk contoh di atas. Namun, metode ini sangat efektif untuk potongan yang dapat disalahartikan jika “di luar konteks”. Selain itu, metode ini menyediakan pendekatan terpadu bagi semua potongan sehingga pipeline rekayasanya tetap rapi.


Ketika Anda perlu bersikap ~~gila kontrol~~ teliti
Biasanya, kita menerima data secara utuh lalu membaginya menjadi beberapa potongan untuk sistem RAG. Dalam contoh ini, kami menunjukkan kasus yang agak berbeda—data sudah dibagi menjadi potongan, tetapi pembagiannya buruk. Potongan tersebut merupakan bagian acak dari satu potongan logis yang sebenarnya perlu digabungkan kembali. Potongan logis berarti bagian konten yang secara alami seharusnya tetap menyatu, seperti subbagian dokumen atau paragraf yang koheren.


Upaya pertama kami adalah memasukkan semuanya ke dalam satu panggilan LLM, memintanya mengelompokkan bagian-bagian tersebut sebagaimana mestinya, lalu mengembalikan konten yang telah dikelompokkan. LLM seharusnya cukup andal untuk tugas ini, bukan? Ya dan tidak.
Dalam beberapa kesempatan lain, kami juga mendapati bahwa LLM cenderung bekerja seadanya dan tidak andal saat kita memerlukan konten yang lengkap dan persis, terutama jika konteksnya panjang. Dan itu sangat masuk akal. Namun, hal tersebut tidak dapat diterima untuk kasus penggunaan ini karena kami benar-benar membutuhkan konten yang persis kata demi kata—tanpa ringkasan dan tanpa melewatkan bagian apa pun dari konten asli. Tidak boleh ada detail yang terlewat.
Tentu saja, sisi “ya”-nya adalah LLM sangat baik dalam memahami semantik dan struktur potongan-potongan yang terpecah tersebut. Asalkan LLM tidak menolak mengutip kembali konten persisnya. Sial:/
Lalu, bagaimana kita dapat memanfaatkan keunggulan LLM sekaligus menghindari aspek yang tidak dapat diandalkan? Kami beralih kepada sahabat lama: kode (baca: fungsi Python khusus). Ditambah model Pydantic yang “sangat sederhana”. Berikut solusinya:
Telusuri setiap bagian sambil mempertahankan satu potongan logis aktif
Pada setiap bagian, tanyakan kepada LLM: apakah bagian ini termasuk dalam potongan logis aktif? Jawab ya atau tidak (mengikuti model Pydantic).
Jika ya, tambahkan bagian itu ke potongan; jika tidak, keluarkan potongan logis aktif karena sudah lengkap, lalu mulai potongan baru dengan bagian tersebut.


Tentu saja, pendekatan ini menggunakan sedikit lebih banyak token daripada memproses seluruh konten sekaligus. Namun, untuk kasus penggunaan yang memprioritaskan kemampuan mempertahankan konten persisnya, sedikit biaya tambahan itu sangat sepadan.
Ini adalah solusi yang sangat sederhana, tetapi mengikuti prinsip penting: ketika ketelitian dibutuhkan, kita tidak boleh hanya mengandalkan LLM karena pada dasarnya LLM bersifat probabilistik.
Kode/fungsi khusus dan model Pydantic dapat digunakan untuk memperoleh hasil yang konsisten dan andal sekaligus tetap memanfaatkan kemampuan LLM sepenuhnya.
Membangun solusi AI generatif bukan hanya tantangan AI, tetapi juga tantangan rekayasa. Kami berharap contoh-contoh ini menginspirasi Anda untuk mengatasi tantangan unik Anda sendiri. Untuk membaca lebih lanjut tentang solusi AI generatif yang mengutamakan rekayasa, lihat artikel blog kami mengenai desain sistem agentik berbasis router.