GPT-4 ve Claude gibi Büyük Dil Modelleri (LLM) olağanüstü güçlüdür, ancak temel bir sınırlamadan muzdariptirler: bilgileri eğitim anında dondurulmuştur. İç belgelerinize, veritabanınıza veya gerçek zamanlı bilgilere erişemezler. Getirme ile Zenginleştirilmiş Üretim (RAG), LLM'lerin üretim gücünü harici kaynaklardan bilgi getirme yeteneğiyle birleştirerek tam olarak bu sorunu çözer.
Sorun: LLM Sınırlamaları
RAG hakkında konuşmadan önce, neden buna ihtiyacımız olduğunu anlamak önemlidir.
- Statik bilgi: Bir LLM yalnızca eğitim sırasında gördüklerini bilir. Kesim tarihinden sonra meydana gelen bir olay hakkında sorarsanız, yanıt veremez.
- Halüsinasyonlar: Bir LLM yanıtı bilmediğinde, makul ama tamamen yanlış bilgiler üreterek uydurma eğilimindedir.
- Özel verilere erişim yok: Genel bir LLM, şirketinizin iç belgelerine, destek taleplerine veya kod tabanına erişimi yoktur.
RAG, sorgu anında harici kaynaklardan getirilen ilgili bağlamı modele sağlayarak bu üç sorunun tamamını ele alır.
RAG Nedir?
Getirme ile Zenginleştirilmiş Üretim, bir LLM'ye gönderilen istemi harici bir bilgi tabanından getirilen bilgilerle zenginleştiren bir mimaridir. Modelin yalnızca parametrik bilgisine güvenmek yerine, RAG önce ilgili bilgileri arar ve ardından isteme enjekte eder, böylece modelin doğru, temelli yanıtlar üretmesini sağlar.
RAG Nasıl Detaylı Çalışır
RAG mimarisi iki ana aşamadan oluşur: İndeksleme (çevrimdışı) ve Getirme + Üretim (çevrimiçi).
Aşama 1: İndeksleme (Belge Alımı)
İndeksleme aşaması belgelerinizi semantik arama için hazırlar. Dört adımdan oluşur.
1. Belge Yükleme
Belgeler herhangi bir kaynaktan gelebilir: PDF dosyaları, web sayfaları, veritabanları, Markdown dosyaları, API'ler. Document Loader bu belgeleri okur ve yapılandırılmış metne dönüştürür.
2. Metin Bölme (Chunking)
LLM'lerin sınırlı bağlam penceresi vardır ve belgeler çok uzun olabilir. Text Splitter belgeleri chunks adı verilen daha küçük parçalara böler. Chunking kalitesi kritiktir: çok küçük chunks bağlamı kaybeder, çok büyük chunks ise ilgililik düzeyini seyreltir.
En yaygın stratejiler şunlardır:
- Özyinelemeli Karakter Bölme:
\n\n,\n,.gibi ayırıcılar kullanarak belge yapısına saygı göstererek metni özyinelemeli olarak böler. - Semantik Bölme: Metinde doğal kırılma noktalarını bulmak için embeddings kullanır.
- Chunk Örtüşmesi: Sınırlarda bağlamı korumak için ardışık chunks arasında örtüşme içerir.
3. Embedding
Her chunk, bir embedding modeli (OpenAI'nin text-embedding-3-small gibi) aracılığıyla bir sayısal vektöre (embedding) dönüştürülür. Bu vektörler metnin anlamsal anlamını yakalar: benzer anlamlara sahip cümleler, çok boyutlu uzayda birbirine yakın vektörlere sahip olacaktır.
4. Vector Store
Vektörler, ChromaDB, Pinecone, Weaviate veya FAISS gibi bir Vector Store'da (veya vektör veritabanında) kaydedilir. Bu veritabanı benzerlik araması için optimize edilmiştir: bir sorgu verildiğinde, en benzer vektörleri (ve dolayısıyla en ilgili metin chunks'larını) bulur.
Aşama 2: Getirme + Üretim
Kullanıcı bir soru sorduğunda:
- Soru, aynı embedding modeli kullanılarak bir embedding'e dönüştürülür.
- Vector Store, benzerlik araması (genellikle kosinüs benzerliği veya Öklid mesafesi) yoluyla en benzer chunks'ları bulur.
- Getirilen chunks, bağlam olarak isteme eklenir.
- LLM, sağlanan bağlama dayalı bir yanıt üretir.
LangChain ile RAG Pipeline Oluşturma
LangChain, LLM destekli uygulamalar oluşturmak için en popüler Python (ve JavaScript) çerçevesidir. RAG pipeline'ının her bileşeni için üst düzey soyutlamalar sağlar.
Kurulum
pip install langchain langchain-openai langchain-community chromadb
Adım 1: Belgeleri Yükle
LangChain, farklı veri kaynakları için düzinelerce Document Loader sağlar.
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
Adım 2: Belgeleri Chunks'lara Böl
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)}")
chunk_overlap parametresi kritik öneme sahiptir: ardışık chunks arasında örtüşme oluşturur, böylece sınırlarda bağlam kaybedilmez.
Adım 3: Embeddings ve Vector Store Oluştur
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", )
Adım 4: Retriever Oluştur
Retriever, bir sorgu verildiğinde vector store'dan en ilgili chunks'ları getiren bileşendir.
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("---")
Adım 5: RAG Zinciri Oluştur
Şimdi her şeyi bir LLM ve bir istem şablonuyla bir araya getirelim.
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)
Gelişmiş RAG Teknikleri
Temel pipeline iyi çalışır, ancak yanıt kalitesini önemli ölçüde artırmak için çeşitli teknikler vardır.
Çoklu Sorgu Getirme
Bazen kullanıcının sorgusu belirsizdir veya belgelerde kullanılan dille uyumlu değildir. Multi-Query Retriever, birden fazla bakış açısı yakalamak için orijinal sorunun varyantlarını otomatik olarak üretir.
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?")
Bağlamsal Sıkıştırma
Bir chunk'taki tüm içerik sorguyla ilgili değildir. Contextual Compression Retriever, getirilen her chunk'tan yalnızca ilgili kısımları çıkarmak için bir LLM kullanır.
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, )
Hibrit Arama
Tamamen semantik arama her zaman optimal değildir. Hibrit Arama, daha iyi sonuçlar elde etmek için semantik aramayı (embeddings) sözcüksel arama (BM25, anahtar kelime eşleştirme) ile birleştirir.
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], )
Konuşmalı RAG (Bellekli)
Konuşma bağlamını hatırlayan bir RAG sohbet robotu oluşturmak için, konuşma geçmişini dikkate alarak kullanıcının sorularını yeniden formüle eden bir bellek eklemeniz gerekir.
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 )
En İyi Uygulamalar
- Doğru chunk boyutunu seçin: Farklı boyutlarla (500-1500 token) deneyler yapın. Kesin yanıtlar için küçük chunks, daha geniş bağlam için büyük chunks.
- Belge meta verilerini kullanın: Chunks'lara meta veri olarak kaynak, tarih ve kategori ekleyin. Bu, getirme sırasında sonuçları filtrelemeye olanak tanır.
- Kaliteyi değerlendirin: faithfulness, relevancy ve context precision gibi metrikleri ölçmek için RAGAS gibi çerçeveleri kullanın.
- Belge güncellemelerini yönetin: Vector store'u veri kaynaklarınızla senkronize tutmak için yeniden alım pipeline'ı uygulayın.
- Re-ranker ekleyin: İlk getirmeden sonra, sonuçları gerçek ilgililiğe göre yeniden sıralamak için bir re-ranking modeli (Cohere Rerank gibi) kullanın.
Sonuç
RAG, belirli, güncel bilgiye erişim gerektiren AI uygulamaları oluşturmak için standart mimari haline gelmiştir. LangChain, pipeline'ın her bileşeni için soyutlamalar sağlayarak uygulamayı büyük ölçüde basitleştirir.
Sonraki adımlar:
- Yerel olarak deneyin: Pipeline'a aşina olmak için ChromaDB ve birkaç belgeyle başlayın.
- LangSmith'i keşfedin: Üretimde zincirlerinizi izlemek ve hata ayıklamak için LangSmith kullanın.
- Farklı embedding modelleri deneyin:
text-embedding-3-small,text-embedding-3-largeve Sentence Transformers'dan açık kaynak modelleri karşılaştırın. - Belgeleri kontrol edin: LangChain belgeleri mükemmel ve sürekli güncellenen bir kaynaktır.