Model Bahasa Besar (LLM) seperti GPT-4 dan Claude sangat kuat, tetapi mereka memiliki keterbatasan mendasar: pengetahuan mereka terhenti pada saat pelatihan. Mereka tidak dapat mengakses dokumen internal Anda, database Anda, atau informasi real-time. Retrieval-Augmented Generation (RAG) memecahkan masalah ini dengan menggabungkan kekuatan generatif LLM dengan kemampuan untuk mengambil informasi dari sumber eksternal.
Masalah: Keterbatasan LLM
Sebelum membahas RAG, penting untuk memahami mengapa kita membutuhkannya.
- Pengetahuan statis: LLM hanya mengetahui apa yang dilihatnya selama pelatihan. Jika Anda bertanya tentang peristiwa yang terjadi setelah batas waktu pelatihannya, ia tidak dapat menjawab.
- Halusinasi: Ketika LLM tidak mengetahui jawabannya, ia cenderung mengarangnya, menghasilkan informasi yang masuk akal tetapi sepenuhnya salah.
- Tidak ada akses ke data privat: LLM generik tidak memiliki akses ke dokumentasi internal perusahaan Anda, tiket, atau basis kode.
RAG mengatasi ketiga masalah ini dengan menyediakan konteks yang relevan yang diambil dari sumber eksternal pada saat kueri.
Apa itu RAG?
Retrieval-Augmented Generation adalah arsitektur yang memperkaya prompt yang dikirim ke LLM dengan informasi yang diambil dari basis pengetahuan eksternal. Alih-alih hanya mengandalkan pengetahuan parametrik model, RAG terlebih dahulu mencari informasi yang relevan kemudian menyuntikkannya ke dalam prompt, memungkinkan model menghasilkan respons yang akurat dan berdasar.
Cara Kerja RAG secara Detail
Arsitektur RAG terdiri dari dua fase utama: Pengindeksan (offline) dan Pengambilan + Pembuatan (online).
Fase 1: Pengindeksan (Ingesti Dokumen)
Fase pengindeksan mempersiapkan dokumen Anda untuk pencarian semantik. Terdiri dari empat langkah.
1. Pemuatan Dokumen
Dokumen dapat berasal dari sumber mana pun: file PDF, halaman web, database, file Markdown, API. Document Loader membaca dokumen-dokumen ini dan mengubahnya menjadi teks terstruktur.
2. Pemisahan Teks (Chunking)
LLM memiliki jendela konteks yang terbatas, dan dokumen bisa sangat panjang. Text Splitter membagi dokumen menjadi fragmen-fragmen kecil yang disebut chunks. Kualitas chunking sangat penting: chunks yang terlalu kecil kehilangan konteks, sementara chunks yang terlalu besar mengencerkan relevansi.
Strategi yang paling umum adalah:
- Recursive Character Splitting: Memisahkan teks secara rekursif menggunakan pemisah seperti
\n\n,\n,., dengan menghormati struktur dokumen. - Semantic Splitting: Menggunakan embeddings untuk menemukan titik pemisahan alami dalam teks.
- Chunk Overlap: Menyertakan tumpang tindih antara chunks berurutan untuk menjaga konteks di batas.
3. Embedding
Setiap chunk diubah menjadi vektor numerik (embedding) melalui model embedding (seperti text-embedding-3-small dari OpenAI). Vektor-vektor ini menangkap makna semantik teks: kalimat dengan makna serupa akan memiliki vektor yang berdekatan dalam ruang multidimensi.
4. Vector Store
Vektor-vektor disimpan dalam Vector Store (atau database vektor), seperti ChromaDB, Pinecone, Weaviate, atau FAISS. Database ini dioptimalkan untuk pencarian kesamaan: diberikan sebuah kueri, ia menemukan vektor-vektor yang paling mirip (dan karenanya chunks teks yang paling relevan).
Fase 2: Pengambilan + Pembuatan
Ketika pengguna mengajukan pertanyaan:
- Pertanyaan diubah menjadi embedding menggunakan model embedding yang sama.
- Vector Store menemukan chunks yang paling mirip melalui pencarian kesamaan (biasanya cosine similarity atau jarak Euclidean).
- Chunks yang diambil disisipkan ke dalam prompt sebagai konteks.
- LLM menghasilkan respons berdasarkan konteks yang diberikan.
Membangun Pipeline RAG dengan LangChain
LangChain adalah framework Python (dan JavaScript) paling populer untuk membangun aplikasi berbasis LLM. Framework ini menyediakan abstraksi tingkat tinggi untuk setiap komponen pipeline RAG.
Instalasi
pip install langchain langchain-openai langchain-community chromadb
Langkah 1: Muat Dokumen
LangChain menyediakan puluhan Document Loader untuk berbagai sumber data.
from langchain_community.document_loaders import ( PyPDFLoader, WebBaseLoader, DirectoryLoader, TextLoader, ) # Load a PDF pdf_loader = PyPDFLoader("docs/manual.pdf") pdf_docs = pdf_loader.load() # Load a web page web_loader = WebBaseLoader("https://docs.example.com/guide") web_docs = web_loader.load() # Load all .md files from a directory dir_loader = DirectoryLoader("./knowledge_base", glob="**/*.md", loader_cls=TextLoader) md_docs = dir_loader.load() all_docs = pdf_docs + web_docs + md_docs
Langkah 2: Pisahkan Dokumen menjadi Chunks
from langchain.text_splitter import RecursiveCharacterTextSplitter text_splitter = RecursiveCharacterTextSplitter( chunk_size=1000, chunk_overlap=200, separators=["\n\n", "\n", ". ", " ", ""], ) chunks = text_splitter.split_documents(all_docs) print(f"Original documents: {len(all_docs)}, Chunks: {len(chunks)}")
Parameter chunk_overlap sangat penting: ia menciptakan tumpang tindih antara chunks berurutan sehingga konteks tidak hilang di batas.
Langkah 3: Buat Embeddings dan Vector Store
from langchain_openai import OpenAIEmbeddings from langchain_community.vectorstores import Chroma embedding_model = OpenAIEmbeddings(model="text-embedding-3-small") vectorstore = Chroma.from_documents( documents=chunks, embedding=embedding_model, persist_directory="./chroma_db", )
Langkah 4: Buat Retriever
Retriever adalah komponen yang, diberikan sebuah kueri, mengambil chunks yang paling relevan dari vector store.
retriever = vectorstore.as_retriever( search_type="similarity", search_kwargs={"k": 4}, ) relevant_docs = retriever.invoke("How does authentication work?") for doc in relevant_docs: print(doc.page_content[:200]) print("---")
Langkah 5: Bangun RAG Chain
Sekarang mari kita satukan semuanya dengan LLM dan template prompt.
from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate from langchain_core.runnables import RunnablePassthrough from langchain_core.output_parsers import StrOutputParser llm = ChatOpenAI(model="gpt-4o", temperature=0) prompt = ChatPromptTemplate.from_template(""" Answer the question based only on the provided context. If the context does not contain enough information, say you don't know. Context: {context} Question: {question} Answer: """) def format_docs(docs): return "\n\n".join(doc.page_content for doc in docs) rag_chain = ( {"context": retriever | format_docs, "question": RunnablePassthrough()} | prompt | llm | StrOutputParser() ) response = rag_chain.invoke("How does authentication work in the system?") print(response)
Teknik RAG Lanjutan
Pipeline dasar bekerja dengan baik, tetapi ada beberapa teknik untuk meningkatkan kualitas respons secara signifikan.
Pengambilan Multi-Kueri
Terkadang kueri pengguna ambigu atau tidak selaras dengan bahasa yang digunakan dalam dokumen. Multi-Query Retriever secara otomatis menghasilkan varian dari pertanyaan asli untuk menangkap berbagai perspektif.
from langchain.retrievers import MultiQueryRetriever multi_retriever = MultiQueryRetriever.from_llm( retriever=vectorstore.as_retriever(), llm=llm, ) docs = multi_retriever.invoke("What are the security best practices?")
Kompresi Kontekstual
Tidak semua konten dalam sebuah chunk relevan dengan kueri. Contextual Compression Retriever menggunakan LLM untuk mengekstrak hanya bagian yang relevan dari setiap chunk yang diambil.
from langchain.retrievers import ContextualCompressionRetriever from langchain.retrievers.document_compressors import LLMChainExtractor compressor = LLMChainExtractor.from_llm(llm) compression_retriever = ContextualCompressionRetriever( base_compressor=compressor, base_retriever=retriever, )
Pencarian Hibrida
Pencarian murni semantik tidak selalu optimal. Pencarian Hibrida menggabungkan pencarian semantik (embeddings) dengan pencarian leksikal (BM25, pencocokan kata kunci) untuk mencapai hasil yang lebih baik.
from langchain.retrievers import EnsembleRetriever from langchain_community.retrievers import BM25Retriever bm25_retriever = BM25Retriever.from_documents(chunks) bm25_retriever.k = 4 semantic_retriever = vectorstore.as_retriever(search_kwargs={"k": 4}) hybrid_retriever = EnsembleRetriever( retrievers=[bm25_retriever, semantic_retriever], weights=[0.4, 0.6], )
RAG Percakapan (dengan Memori)
Untuk membangun chatbot RAG yang mengingat konteks percakapan, Anda perlu menambahkan memori yang merumuskan ulang pertanyaan pengguna dengan mempertimbangkan riwayat percakapan.
from langchain.chains import create_history_aware_retriever from langchain_core.prompts import MessagesPlaceholder contextualize_prompt = ChatPromptTemplate.from_messages([ ("system", "Given the chat history and the user's latest question, " "reformulate the question so it is understandable without the history."), MessagesPlaceholder("chat_history"), ("human", "{input}"), ]) history_aware_retriever = create_history_aware_retriever( llm, retriever, contextualize_prompt )
Praktik Terbaik
- Pilih ukuran chunk yang tepat: Bereksperimen dengan berbagai ukuran (500-1500 token). Chunks lebih kecil untuk jawaban presisi, lebih besar untuk konteks yang lebih luas.
- Gunakan metadata dokumen: Tambahkan sumber, tanggal, dan kategori sebagai metadata ke chunks. Ini memungkinkan pemfilteran hasil selama pengambilan.
- Evaluasi kualitas: Gunakan framework seperti RAGAS untuk mengukur metrik seperti faithfulness, relevancy, dan context precision.
- Kelola pembaruan dokumen: Implementasikan pipeline re-ingesti untuk menjaga vector store tetap tersinkronisasi dengan sumber data Anda.
- Tambahkan re-ranker: Setelah pengambilan awal, gunakan model re-ranking (seperti Cohere Rerank) untuk mengurutkan ulang hasil berdasarkan relevansi aktual.
Kesimpulan
RAG telah menjadi arsitektur standar untuk membangun aplikasi AI yang membutuhkan akses ke pengetahuan spesifik dan terkini. LangChain sangat menyederhanakan implementasi, menyediakan abstraksi untuk setiap komponen pipeline.
Langkah selanjutnya:
- Bereksperimen secara lokal: Mulai dengan ChromaDB dan beberapa dokumen untuk membiasakan diri dengan pipeline.
- Jelajahi LangSmith: Gunakan LangSmith untuk memantau dan men-debug chain Anda di produksi.
- Coba model embedding yang berbeda: Bandingkan model seperti
text-embedding-3-small,text-embedding-3-large, dan model open-source dari Sentence Transformers. - Periksa dokumentasi: Dokumentasi LangChain adalah sumber yang sangat baik dan terus diperbarui.