Skip to content

Ritreval Augmented Generation 1

Bogor, 16 Desember 2025: Hujan, Noah nangis ngamuk2 mau cookies

Pada catatan ini insyaAllah akan terdapat bagaimana cara

  1. Memberi indeks pada kumpulan dokumen dan bagaimana cara membersihkan (biasa disebut dengan preprocessing) sehingga aplikasi yg kita bentuk dapat menemukan informasi yang relevan terhadap pertanyaan.
  2. Mengambil atau menarik data eksternal melalui indeks tersebut dan menggunakan sebagai konteks untuk LLM sehingga dapat menghasilkan nilai keluaran yang berlandaskan data.

Pertama catatan ini akan menjelaskan bagaimana melibatkan proses awal (preprocessing) pada dokumen (misalkan PDF, word, atau data dalam bentuk dokumen lainnya) kedalam format yang dapat dipahami dan dicari oleh LLM. Teknik tersebut adalah RAG, retrieaval-augmented generation.

Untuk menggapainya ada 4 langkah;

  1. Mengambil nilai text dari dokumen.
  2. Memecahnya kedalam bentuk yang teratur.
  3. Mengkonversi kalimat atau kata kedalam bentuk angka yang dapat dipahami oleh komputer.
  4. Menyimpan kumpulan angka representasi dari text sehingga dapat mudah dicari dan dipanggil yang berguna sebagai data atau konteks yg relevan untuk menjawab pertanyaan.

Image

alt text

Embeddings (Konversi text kedalam angka)

Embedding adalah sebuah proses merubah tipe data text kedalam deret angka. Namun kita tidak dapat mengembalikan deret angka tersebut ke bentuk text awalnya, maka dari itu kita kedua data tersebut disimpan.

Embeddings sebelum adanya LLM

Lama sebelum LLM, proses embedding ini sudah dilakukan, misalkan untuk mencari full-text dari sebah website atau klasifikasi apakah sebuah email ini spam atau tidak. Contohnya dapat dilihat disini

  • What a sunny day
  • Such bright skies today
  • I haven't seen a sunny day in weeks.

Kalimat I haven't seen a sunny day in weeks memliki nilai sequance 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 1. Deret angka tersebut adalah sparse embedding atau sparse vector. Kita dapat menggunakan sparse vector untuk beberapa hal, misalkan; mencari text pada beberapa dokumen, kita dapat menghitung dari dokumen-dokumen tersebut kata kunci yang sesuai pada dokumen yang dicari.

Limitasinya model tidak memperhatikan makna dari kata, hanya apakah mengandung kata tersebut atau tidak. Misalkan kalimat Sunny Day dan Bright Skies adalah makna yang sama, akan tetapi dengan model embedding ini kata tersebut dianggap tidak sama.

InsyaAllah pada catatan ini juga akan dibahas terkait Semantic Embeddings, dimana metode tersebut mampu menghadapi limitasi dengan memanfaatkan deret angka dalam memahami text alih-alih hanya menggunakan arti makna satu kata.

Embeddings pada LLM

Kita akan memahami apa itu Embeddings Model. Embddings model adalah salah satu cabang atau salah astu bagian dari proses pelatihan LLM. Pada awal catatan ini, kita mengetahui bahwa saat proses pelatihan (supply data) LLM dilatih menggunakan informasi dalam bentuk kata-kata yang sangat banyak sehingga membuat model dapat memprediksi dan men-generate kalimat selanjutan dari input yang diberikan.

Kemampuan dalam melanjutkan kata ini berasal dari pemahaman dari kata makna (semantics, understanding of meaning),belajar untuk memahmai bagaimana kata digunakan, bagaimana makna kata digabung dengan kata lainnya. Pehaman dari makna juga dapat dirubah kedalam angka, proses embeddings yang mana dapat kita gunakan untuk beberapa tujuan tertentu. Pada praktiknya, Embeddings model ini dibangun khusus untuk tujuan tersebut sehingga kualitas dari proses embeddings ini berkualitas.

Sehingga dapat kita katakan bahwa Embeddings model adalah sebuah algoritma yang menerima nilai masukan dalam betuk text dan nilai keluaran dalam bentuk kumpulan angka. Bisa dalam bentuk vector atau dense/matrix.

Penjelasan Semantic Embeddings

Katakan ada 3 kata Pet, Dog dan Lion, kira2 dari ketiga kata tersebut yang mana yang mirip dengan yang lainnya. Umumnya kita akan menjawab yang mirip adalah pet dan dog. Namun komputer tidak memahami hal tersebut, ingat komputer hanya memahami angka. Dibelakang layar kata tersebut haru kita embedding untuk menghasilkan sebuah vektor. Vektor tersebut digunakan sebagai nilai input _cosine similiarity_ untuk mengetahui kemiripan dari setiap kata. Jika nilai Cosine Similiarty 1 atau mendekati satu maka kemiripan dari kata tersebut tinggi dan jika mendekati ke atau hingga ke nilai 0 maka tingkat kemiripan rendah.

Lab cosine similiarity

Lab consine similiarty

Lalu apa benang merahnya ?🪵🪵🪵🪵

Dengan kemampuan untuk merubah data dalam bentuk kata atau kalimat kedalam embeddings yang juga didalamnya ada makna dari setiap kalimat lalu mengukur kemiripan dari makna setiap kata membuat LLM dapat menemukan dokumen atau data yang relevan untuk menjawab pertanyaan.

Merubah dokumen ke dalam text

Dokumen bisa datang dalam berbagai bentuk, PDF, text, CSV atau bisa didalam bentuk Web. Langchain memliki beberapa class loader yang dapat membantu mengambil data dari dokumen kedalam bentuk text dalam bentuk class Document.

Code

from langchain_community.document_loaders import TextLoader
text_data = TextLoader("C:/Users/Administrator/Documents/LangChainLabAssets/Data.txt")
text_data.load()

## [Document(metadata={'source': 'C:/Users/Administrator/Documents/LangChainLabAssets/Data.txt'}, page_content="I'm the data inside file name Data.txt")]

More detail lihat di lab document;

Beberapa contoh diatas, salah satunya melakukan ekstraksi dari dokumen kedalam class Document. Namun ada masalah lain, yaitu bagaimana jika dokumen memiliki banyak halaman dan mengandung banyak kalimat informasi. Untuk mengatasi masalah ini kita harus memecah data tersebut kedalam potongan yang dapat diatur. Setelah pemotongan tersebutlah kita baru melakukan embedding dan melakukan pencarian makna (semantic search). Oke next step kita akan membahas hal tersebut.

Splitting Text kedalam bentuk Chunks

Sekilas jika dipikirkan memang terlihat sulit untuk memecah kumpulan kalimat kedalam pecahan2 namun dengan mempertahankan makna dari setiap kata. Untungnya langchain memliki RecursiveCharacterTextSplitter untuk mempermudah memecah kalimat kedalam bentuk yang lebih kecil namun masih memliki makna.

Yg dilakukan oleh RecursiveCharacterTextSplitter adalah sebagai berikut;

  1. Membuat list seperator paragraf \n\n, new line \n dan space
  2. Memotong berdasarkan chunk size, katakan 1000, maka maksimal kalimat yang diambil tidak bisa lebih dari 1000. Jika paragraf melebih besaran chunk size maka yang diambil kedalam class document adalah text yang dipotong berdasarkan makna, sehingga pada class document bisa jadi berisi kurang dari 1000 karakter.
  3. Jika class document sudah semuanya terpenuhi sesuai besaran chunk size maka class document selanjutnya berisi panjang kata (makna) sebesar chunk_overlap. Pelakukan yang sama yang diambil adalah kata dengan makna maksimal sebesar chunk_overlap tidak langsung potong.

Create Documents class from raw text

Katakan data yang kita miliki tidak bisa digunakan pada document loader, misalkan kita sudah memliki text mentah raw string. Kita dapat menggunakan method create_documents untuk membtuh documents class.

Generating Text Embeddings

Langchain memliki Embedding class untuk berinteraksi dengan Embeddings Model. Class tersebut menyediakan 2 fungsi;

  1. Untuk embeddings document
  2. Untuk embeddings query (pertanyaan)

Kita akan menggunakan interface OllamaEmbeddings untuk beriteraksi dengan model ollama khusus untuk embeddings2. Pada catatan kali ini saya menggunakan model nomic-embed-text. Setelah membuat object Ollama Embeddings kita dapat memanggil fungsi embed_documents atau embed_query.

Lab

Untuk hands-on dapat melihat pada lab embeddings text

Menyimpan Embeddings pada Vector Store

Vector store adalah sebuah database yang dibentuk untuk menyimpan vector dan melakukan berbagaimacam kalkulasi seperti consine similiarity dsb. Berbeda dengan database umum lainnya yang menyimpan dalam bentuk yang terstruktur, vector store dapat menampung unstructured data seperti text dan gambar. Namun mirip dengan DB pada umunnya vector store juga dapat menjalankan operasi CRUD.

alur data

alt text

Gambar diatas menjelaskan bagaimana data dirubah kedalam bentuk vector, disimpan lalu dipanggil kembali ketika membutuhkan informasi.

Diluar sana ada beberapa penyedia vector store, kita dapat memilih sesuai kebutuhan kita. Vector store mana yang kita pilih tergantung kebutuhan misalkan dari sisi biaya, ukurannya, meta data filteringnya, serta performa, dan masih banyak lainnya.

InsyaAllah pada catatan kali ini saya akan menggunakan Postgresl SQL, kita bisa menggunakan PSQL umumnya namun dengan tambahan ekstensi pgvector.

Another provicers

Ada providers lain yang saya sudah buatkan catatan-nya. Yaitu ChromaDB, untuk melihatnya dapat merujuk ke chroma DB

Berkenalan dengan PSQL ekstensi pgvector

Saat ini saya menggungnakan pgvector yang sudah tinggal pakai menggunakan docker. Images yang digunakan adalah images PSQL yang sudah terinstall ekstensi pgvector sehingga kita tida perlu lagi menginstall ekstension tersebut. Berikut adalah image yang digunakan berikut syntax men-download image pgvcetor.

Code

docker pull pgvector/pgvector:pg18-trixie

How to run

Untuk installasi pgevector dapat merujuk ke how to install ekstension of pgvector

Jika images sudah berjalan, kita perlu menjalankan images, kita dapat menggunakan perintah run pada docker seperti dibawah ini

Code

docker run 
    --name pg_vector_farras 
    -p 6060:5432 
    -d 
    -e POSTGRES_USER=farras
    -e POSTGRES_PASSWORD=farras
    -e POSTGRES_DB=farraslang
    pgvector/pgvector:pg18-trixie

Sedikit catatan tentang Doceker (qaddarullah catatan hilang, masih ada di drive lama dan kemungkinan besar hilang) perintah diatas untuk membuat sebuah container (images yang berjalan) dengan nama container pg_vector_farras.

Parameter -p adalah untuk membypas port pada container yang mana 5432 adalah default psql ke port outside 6060, sehingga psql dapat kita akses menggunakan port tersebut.

Parameter -d adalah perintah detach agar container jalan dibackgroud.

Parameter -e adalah perintah ke docker agar membuat parameter dengan key-value yang diberikan.

Teriakhir adalah image yang ingin dijalankan, pada case ini kita mau menjalankan image pg_vecto yang baru saja kita pull.

List env pg_vector

Diatas kita melihat baha parameter -e memberikan nilai key-value, untuk melihat daftar env yang tersedia pada image dapat dilihat pada docker hub env psql

Jika kita seudah menjalankan perintah run pada docker, insyaALlah jika tidak ada error maka akan muncul ID container. Untuk memastikan apakah container sudah berjalan atau belum kita dapat menggunakan perintah berikut;

Code

docker ps -a
Output
CONTAINER ID   IMAGE                           COMMAND                  CREATED         STATUS                    PORTS                                         NAMES
524be1c5c7ae   pgvector/pgvector:pg18-trixie   "docker-entrypoint.s…"   3 seconds ago   Up 2 seconds              0.0.0.0:6060->5432/tcp, [::]:6060->5432/tcp   pg_vector_farras

Selanjutnya kita perlu masuk kedalam container tersebut menggunakan perintah exec. Hmmm ini seperti kita masuk ke terminal linux server atau linux menggunakan cli alih-alih user interface, Namun yang memmdekan adalah dengan perintah ini kita dapat langsung memberikan instruksi pada container untuk menjalan perintah apa. pada case ini kita akan menjalan perintah psql untuk masuk kedalam database-nya.

Code

docker exec -it pg_vector_farras psql -U farras -p 5432 -d farraslang

Jika sudah masuk, berikan perintah \l untuk me-listing daftar database yang ada. Kita akan melihat bahwa database yang kita buat diawal saat me-running image ada.

Output
psql (18.1 (Debian 18.1-1.pgdg13+2))
Type "help" for help.

farraslang=# \l
                                                List of databases
    Name    | Owner  | Encoding | Locale Provider |  Collate   |   Ctype    | Locale | ICU Rules | Access privileges
------------+--------+----------+-----------------+------------+------------+--------+-----------+-------------------
farraslang | farras | UTF8     | libc            | en_US.utf8 | en_US.utf8 |        |           |
postgres   | farras | UTF8     | libc            | en_US.utf8 | en_US.utf8 |        |           |
template0  | farras | UTF8     | libc            | en_US.utf8 | en_US.utf8 |        |           | =c/farras        +
            |        |          |                 |            |            |        |           | farras=CTc/farras
template1  | farras | UTF8     | libc            | en_US.utf8 | en_US.utf8 |        |           | =c/farras        +
            |        |          |                 |            |            |        |           | farras=CTc/farras
(4 rows)

farraslang=#

Test Ekstensi pg_vector

Saat belajar saya menggunakan referensi dari dokumentasi pada github/pgvector.

Code

farraslang=# CREATE EXTENSION vector;
CREATE EXTENSION

CREATE TABLE contoh_table1 (id bigserial PRIMARY KEY, embedding vector(3));
CREATE TABLE

insert into contoh_table1 (embedding) VALUES ('[3,4,5]'),('[1,2,3]');
INSERT 0 2

farraslang=# select * from contoh_table1;
id | embedding
----+-----------
1 | [3,4,5]
2 | [1,2,3]
(2 rows)

Menggunakan vector pada langchain

Pertama kita harus menginstall library langchain_postgrest untuk menggunakan pg_vector.

Code

pip install langchain-postgres

Code

Untuk hands-on dapat melihat pada lab Menyimpan, mengakses dan merubah data pad vector store

from langchain_postgres.vectorstores import PGVector
text_data_store_vector = TextLoader(f'{root_path}/BigData.txt')

## Memeuat loader kedalam bentuk class Documents
docs = text_data_store_vector.load()

## Potong big data kedalam potongan2an
splitter_store_vector = RecursiveCharacterTextSplitter(
    chunk_size = 1000,
    chunk_overlap = 200
)

## Class document baru setelah di potong
new_docs = splitter_store_vector.split_documents(docs)

## Import langchain Ollama
model_embeddings = OllamaEmbeddings(model="nomic-embed-text")

## Dsini kita tidak perlu memanggil embeed documents lagi, kita langsung masukan kedalam parameter PGVector
connection = "postgresql+psycopg://farras:farras@localhost:6060/farraslang"

class_vector_store = PGVector.from_documents(documents=new_docs,embedding=model_embeddings,connection=connection)

Kode diatas adalah gabungan dari apa yang telah kita lalui diatas, seperti memuat text dan menjadikannya dalam bentuk documents, lalu memotong setiap data pada bagian tertentu tanpa menghilangkan makna dari kalimat, merubah kedalam bentuk embeddings dan menyimpannya dalam vectore store (catatan ini menggunakan pgstore).

Semantic query (mencari makna terdekat dari vector)

Data yang telah dikonversi kedalam vector sudah tersimpan didalam vector store. Selanjutnya kita pasti butuh untuk mengambil data tersebut sesuai dengan yang kita mau. Pencarian makna, permintaan atau semantic query seperti yang telah kita bahas saat membicaran tentang perbedaan dari 2 kata menggunakan consine similiarty.

Alhamdulillah Langchain PGVector memilki method similiary_search 3 yang mengembalikan documents yang paling mirip.

Method ini memiliki 2 parameter yang bisa kita gunakan;

  • k berapa banyak documents pada nilai keluaran
  • filter penyaringan nilai kembalian berdasarkan metadata

Berdasarkan dokumentasi langchain, kemiripan penyematan (embedding similiary) dihitung menggunakan

  1. Cosine Similiary
  2. Euclidean Distance
  3. Dot Product

Code

Untuk hands-on dapat melihat pada lab Mencari similiary data

docs_for_similiary_search = class_vector_store.similarity_search("visi utama", k=1)
docs_for_similiary_search
Output
[Document(id='79564502-be51-4076-8bd7-b678b35e95ef', metadata={'source': 'C:/Users/maruf/Documents/LangChainLabAssets/BigData.txt'}, page_content='Visi utama dari VenSys adalah menjadi perusahaan teknologi informasi terkemuka di Indonesia. Untuk mencapai visi tersebut, perusahaan menawarkan rangkaian solusi berkualitas yang memberikan keseimbangan terbaik antara nilai investasi dan kesuksesan bagi para pelanggan, mitra, karyawan, dan pemegang saham. Komitmen perusahaan tercermin dalam empat pilar utama: memberikan layanan berkualitas tinggi, menjadi organisasi yang berorientasi pada pelanggan (customer-oriented), melakukan pengembangan sumber daya manusia secara berkelanjutan melalui jalur karier terstruktur dan transfer pengetahuan, serta membangun hubungan jangka panjang yang didasari oleh komitmen konstan terhadap peningkatan solusi dan layanan.')]

Untuk hands-on dapat melihat pada lab Mencari similiary data

docs_for_similiary_search = class_vector_store.similarity_search("jenenge sopo", k=1)
docs_for_similiary_search
Output
[Document(id='79564502-be51-4076-8bd7-b678b35e95ef', metadata={'source': 'C:/Users/maruf/Documents/LangChainLabAssets/BigData.txt'}, page_content='Visi utama dari VenSys adalah menjadi perusahaan teknologi informasi terkemuka di Indonesia. Untuk mencapai visi tersebut, perusahaan menawarkan rangkaian solusi berkualitas yang memberikan keseimbangan terbaik antara nilai investasi dan kesuksesan bagi para pelanggan, mitra, karyawan, dan pemegang saham. Komitmen perusahaan tercermin dalam empat pilar utama: memberikan layanan berkualitas tinggi, menjadi organisasi yang berorientasi pada pelanggan (customer-oriented), melakukan pengembangan sumber daya manusia secara berkelanjutan melalui jalur karier terstruktur dan transfer pengetahuan, serta membangun hubungan jangka panjang yang didasari oleh komitmen konstan terhadap peningkatan solusi dan layanan.')]

Dari dua contoh diatas kita memberikan parameter k dengan nilai 1, artinya return value adalah hanya 1 document yg memiliki kemiripan paling tinggi, ingat, kemiripan yang paling tinggi. Pertama kita mencari kata kunci "visi utama" yg mana kita dapat melihat retur value-nya mengandung informasi terkait visi misi. Akan tetapi pada percobaan kedua dengan kata kunci "jenenge sopo" yang mana dokumen yang kita berikan sama sekali tidak mengandung informasi tersebut namun method similiary_search tetap mengembalikan value, karena method tersebut mengembalian derajat kemiripin yang palin tinggi diantara semua record pada vector store.

Informasi yang tidak relavan yang diberikan oleh LLM sangat tidak baik, karena bisa membuat jawaban menjadi BIAS. Bagusnya, Langchain menyediakan method lain, similarity_search_with_relevance_scores yang mana selain mengemabalikan document juga mengembalikan relevan scores dengan nilai [0-1];

  • Semakin mendekati 0 maka semakin tidak cocok.
  • Semakin mendekati 1 maka semakin cocok.

Official Documentations

Berikut adalah dokumentasi resmi untuk mengetahui lebih dalam method similiary_search_with_relevance_scores

Code

Untuk hands-on dapat melihat pada lab Mencari similiary data dengan score

docs_for_similiary_search = class_vector_store.similarity_search_with_relevance_scores ("Visi utama dari VenSys adalah menjadi perusahaan teknologi", k=1)
docs_for_similiary_search[0][1]
Output
0.7873913678527401

Untuk hands-on dapat melihat pada lab Mencari similiary data dengan score

docs_for_similiary_search = class_vector_store.similarity_search_with_relevance_scores ("Semoga tahun ini adalah tahun terkahir di Vensys", k=1)
docs_for_similiary_search[0][1]
Output
0.6768340092253472

Adding documents kedalam vector store

Referensi