Takip et

FastAPI ve Ollama ile Hibrit RAG API Oluşturma: Seyrek ve Yoğun Geri Getirimin Gücü

Büyük Dil Modelleri (LLM’ler) güncel olmayan veya spesifik alan bilgisi gerektiren konularda sınırlılıklar gösterebilir. Geri Getirim Destekli Üreti…

FastAPI ve Ollama ile Hibrit RAG API Oluşturma: Seyrek ve Yoğun Geri Getirimin Gücü

Büyük Dil Modelleri (LLM’ler) güncel olmayan veya spesifik alan bilgisi gerektiren konularda sınırlılıklar gösterebilir. Geri Getirim Destekli Üretim (RAG) bu sorunu çözmek için güçlü bir mimaridir. Bu makalede, RAG’ın yeteneklerini bir adım öteye taşıyarak hem seyrek (sparse) hem de yoğun (dense) geri getirim stratejilerini birleştiren hibrit bir RAG API’sini nasıl inşa edeceğimizi inceleyeceğiz. FastAPI ve Ollama kullanarak pratik bir çözüm sunacağız.

RAG (Retrieval Augmented Generation) Nedir?

RAG, Büyük Dil Modellerinin (LLM) bilgi tabanını genişletmek ve halüsinasyonları azaltmak için kullanılan bir yapıdır. Temel olarak, bir kullanıcı sorgusu geldiğinde, LLM doğrudan yanıt vermek yerine, önceden indekslenmiş bir veri tabanından (bilgi tabanı) ilgili bilgileri “geri getirir” ve bu bilgileri kullanarak daha doğru ve bağlamsal bir yanıt üretir.

RAG’ın Çalışma Prensibi

RAG süreci genellikle üç ana adımdan oluşur: Veri İndeksleme, Geri Getirim (Retrieval) ve Üretim (Generation).

  • Veri İndeksleme: Belgeleriniz parçalara ayrılır (chunking) ve bu parçalar indekslenir. Bu indeksleme, daha sonra hızlı ve etkili bir şekilde ilgili parçaları bulmak için kullanılır.
  • Geri Getirim: Kullanıcının sorgusu alındığında, indekslenmiş veri tabanından sorguyla en alakalı belge parçaları bulunur. Bu adım, genellikle anlamsal benzerlik veya anahtar kelime eşleştirme yöntemleriyle yapılır.
  • Üretim: Geri getirilen ilgili belge parçaları, LLM’ye bağlam olarak sunulur. LLM, bu bağlamı kullanarak kullanıcı sorgusuna dayalı bir yanıt üretir. Bu sayede LLM, kendi genel bilgisinin ötesinde, spesifik ve güncel bilgilere erişebilir.

RAG’ın Avantajları

RAG, LLM’lerin doğruluğunu artırır, halüsinasyon riskini azaltır, güncel bilgilere erişim sağlar ve LLM’yi yeniden eğitme maliyetini ortadan kaldırır. Ayrıca, yanıtların hangi kaynaklardan geldiğini izlenebilir kılar, bu da şeffaflık açısından önemlidir.

Seyrek ve Yoğun Geri Getirim: Neden Hibrit Yaklaşım?

Geri getirim stratejileri temel olarak ikiye ayrılır: seyrek (sparse) ve yoğun (dense). Her birinin kendine özgü avantajları ve dezavantajları vardır. Hibrit yaklaşım, bu iki yöntemin güçlü yönlerini birleştirerek daha sağlam ve esnek bir geri getirim mekanizması sunar.

Seyrek Geri Getirim (Sparse Retrieval)

Seyrek geri getirim yöntemleri, metinleri anahtar kelimeler veya terim frekansları üzerinden eşleştirir. En bilinen örnekleri TF-IDF ve BM25’tir. Bu yöntemler, sorgudaki kelimelerle belge parçalarındaki kelimeler arasında doğrudan bir eşleşme arar.

  • Avantajları: Anahtar kelime eşleşmesinde çok başarılıdır, terimlerin tam olarak geçtiği durumlarda yüksek hassasiyet sağlar, hızlıdır ve genellikle daha az hesaplama kaynağı gerektirir.
  • Dezavantajları: Anlamsal eşleşmeyi göz ardı eder, eş anlamlı kelimeleri veya farklı ifade biçimlerini algılayamaz. Örneğin, “araba” ve “otomobil” kelimelerini farklı terimler olarak görür.

Yoğun Geri Getirim (Dense Retrieval)

Yoğun geri getirim yöntemleri, metinleri yüksek boyutlu vektör uzaylarında temsil eden “embedding”leri kullanır. Sorgu ve belge parçaları, bir embedding modeli aracılığıyla vektörlere dönüştürülür ve bu vektörler arasındaki anlamsal benzerlik (genellikle kosinüs benzerliği) ölçülerek en alakalı parçalar bulunur.

  • Avantajları: Anlamsal benzerliği yakalar, eş anlamlıları ve farklı ifade biçimlerini anlayabilir, daha karmaşık ve bağlamsal sorgulara yanıt verebilir.
  • Dezavantajları: Anahtar kelime eşleşmesinde bazen zayıf kalabilir, hesaplama açısından daha yoğun olabilir ve uygun bir embedding modelinin seçimi önemlidir. “Nadir” veya çok spesifik anahtar kelimelerin geçtiği durumlarda performansı düşebilir.

Hibrit Geri Getirimin Gücü

Hibrit RAG, hem seyrek hem de yoğun geri getirim sonuçlarını birleştirerek her iki yöntemin zayıf yönlerini telafi eder ve güçlü yönlerini bir araya getirir. Örneğin, bir sorgu hem belirli anahtar kelimeleri içeriyorsa (seyrek geri getirim için ideal) hem de anlamsal olarak karmaşıksa (yoğun geri getirim için ideal), hibrit yaklaşım en iyi sonuçları sunar. Birleştirme genellikle Reciprocal Rank Fusion (RRF) gibi algoritmalarla yapılır.

Özellik Seyrek Geri Getirim Yoğun Geri Getirim Hibrit Geri Getirim
Temel Mekanizma Anahtar kelime eşleştirme (BM25, TF-IDF) Anlamsal vektör benzerliği (Embeddings) Her ikisinin kombinasyonu
Ana Avantaj Anahtar kelime hassasiyeti, hız Anlamsal anlayış, bağlam Kapsamlılık, doğruluk, esneklik
Ana Dezavantaj Anlamsal boşluklar Anahtar kelime zayıflığı Karmaşıklık, daha fazla kaynak

Araç Kutumuz: FastAPI ve Ollama

Bu hibrit RAG API’sini oluşturmak için modern ve güçlü araçlar kullanacağız: FastAPI ve Ollama.

FastAPI: Hızlı ve Modern API Geliştirme

FastAPI, Python ile API’ler oluşturmak için modern, hızlı (Starlette ve Pydantic sayesinde), yüksek performanslı bir web çatısıdır. Otomatik interaktif API dokümantasyonu (Swagger UI ve ReDoc) sunar ve asenkron programlamayı (async/await) doğal olarak destekler.

  • Neden FastAPI? Yüksek performans, geliştirici verimliliği, tip ipuçları ile daha az hata, otomatik dokümantasyon ve asenkron işlemler için mükemmel destek sunar.

Ollama: Yerel LLM’leri Çalıştırmak

Ollama, büyük dil modellerini (LLM’ler) yerel makinenizde çalıştırmayı kolaylaştıran bir platformdur. Llama 2, Mistral, Gemma gibi popüler modelleri indirip çalıştırabilir, hatta kendi modellerinizi oluşturabilirsiniz. Bir API arayüzü sunarak uygulamalarınıza kolayca entegre edilebilir.

  • Neden Ollama? Gizlilik (veriler yerel kalır), maliyet etkinliği (API ücreti yok), esneklik (çeşitli modelleri destekler), çevrimdışı çalışma yeteneği ve kolay entegrasyon sağlar.

Veri Hazırlığı ve İndeksleme: Hibrit RAG’ın Temelleri

Hibrit RAG sisteminin temelini sağlam bir veri hazırlığı ve indeksleme süreci oluşturur. Hem seyrek hem de yoğun geri getirim için ayrı ayrı indeksler oluşturmamız gerekecek.

Belge Yükleme ve Parçalama (Chunking)

İlk adım, bilgi tabanımızı oluşturan belgeleri yüklemek ve daha küçük, yönetilebilir parçalara ayırmaktır. Bu parçalama işlemi (chunking), geri getirilen bilgilerin LLM için daha anlamlı ve odaklı olmasını sağlar.


from langchain_community.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

# Örnek bir belge yükleme
loader = TextLoader("data/my_document.txt")
documents = loader.load()

# Belgeleri parçalara ayırma
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50
)
chunks = text_splitter.split_documents(documents)

Seyrek İndeksleme (BM25)

Seyrek geri getirim için BM25 gibi bir algoritma kullanacağız. Bu, belge parçalarının metin içeriğini doğrudan analiz ederek bir indeks oluşturur. Python'da rank_bm25 kütüphanesi bu iş için kullanılabilir.


from rank_bm25 import BM25Okapi

# Her bir chunk'ın içeriğini token'lara ayır
tokenized_corpus = [chunk.page_content.split(" ") for chunk in chunks]
bm25 = BM25Okapi(tokenized_corpus)

Yoğun İndeksleme (Vektör Veritabanı)

Yoğun geri getirim için, belge parçalarını vektörlere dönüştürecek bir embedding modeli ve bu vektörleri saklayacak bir vektör veritabanı (örneğin ChromaDB, FAISS veya Pinecone) kullanacağız. Burada sentence-transformers kütüphanesinden bir model ve ChromaDB kullanabiliriz.


from langchain_community.embeddings import OllamaEmbeddings
from langchain_community.vectorstores import Chroma

# Ollama ile embedding modeli yükleme (örneğin "nomic-embed-text")
ollama_embeddings = OllamaEmbeddings(model="nomic-embed-text")

# Vektör veritabanı oluşturma ve belge parçalarını ekleme
vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=ollama_embeddings,
    persist_directory="./chroma_db"
)
vectorstore.persist()

Hibrit Geri Getirim Mantığının Uygulanması

Artık hem seyrek hem de yoğun indekslerimiz olduğuna göre, bir kullanıcı sorgusu geldiğinde bu iki kaynaktan sonuçları alıp bunları birleştirmemiz gerekiyor. Reciprocal Rank Fusion (RRF) bu birleştirme için popüler ve etkili bir yöntemdir.

Geri Getirim Fonksiyonları

Öncelikle, her bir geri getirim yönteminden sonuçları alacak fonksiyonlar tanımlayalım.


def sparse_retrieve(query: str, bm25_retriever, chunks, k=5):
    tokenized_query = query.split(" ")
    doc_scores = bm25_retriever.get_scores(tokenized_query)
    top_n = sorted(range(len(doc_scores)), key=lambda i: doc_scores[i], reverse=True)[:k]
    return [(chunks[i], doc_scores[i]) for i in top_n]

async def dense_retrieve(query: str, vectorstore, k=5):
    # Langchain'in retriever arayüzünü kullanıyoruz
    docs = await vectorstore.asimilarity_search(query, k=k)
    # Chroma'dan gelen docs'lar zaten Document nesnesi, skorları da eklenebilir
    return [(doc, 1.0) for doc in docs] # Skorlar için basitleştirilmiş örnek

Reciprocal Rank Fusion (RRF) ile Birleştirme

RRF, farklı geri getirimcilerden gelen sonuçların sıralamalarını birleştirir. Her bir belgeye, sıralamasına göre bir puan verir ve bu puanları toplayarak nihai bir sıralama oluşturur. Daha düşük sıralamalar (yani daha iyi sonuçlar) daha yüksek puan alır.


def reciprocal_rank_fusion(results: list[list[tuple]], k=60):
    fused_scores = {}
    for retriever_results in results:
        for rank, (doc, score) in enumerate(retriever_results):
            doc_id = doc.page_content # Basit bir id olarak içerik kullanıyoruz
            if doc_id not in fused_scores:
                fused_scores[doc_id] = 0
            fused_scores[doc_id] += 1 / (k + rank)
    
    # Skorlara göre sırala ve orijinal Document nesnelerini geri döndür
    sorted_results = sorted(fused_scores.items(), key=lambda item: item[1], reverse=True)
    
    # Orijinal chunk nesnelerini bulup döndürmek için bir harita oluştur
    doc_map = {chunk.page_content: chunk for chunk in chunks} # chunks globalden geliyor
    
    final_docs = []
    for doc_content, score in sorted_results:
        if doc_content in doc_map:
            final_docs.append(doc_map[doc_content])
            
    return final_docs

Ollama ile Üretim ve FastAPI API Entegrasyonu

Geri getirilen belge parçalarını LLM'ye iletmek ve bir yanıt üretmek için Ollama'yı kullanacağız. Tüm bu süreci bir FastAPI API'si aracılığıyla dış dünyaya açacağız.

Ollama ile LLM Entegrasyonu

Ollama'nın yerel API'sini kullanarak bir LLM'den yanıt alabiliriz. Langchain'in ChatOllama sınıfı bu entegrasyonu kolaylaştırır.


from langchain_community.chat_models import ChatOllama
from langchain.prompts import ChatPromptTemplate

# Ollama modelini başlatma
llm = ChatOllama(model="llama2") # Yerelinizde llama2 yüklü olmalı

# Prompt şablonu oluşturma
prompt_template = ChatPromptTemplate.from_messages(
    [
        ("system", "Sen yardımsever bir yapay zeka asistanısın. Aşağıdaki bağlamı kullanarak kullanıcı sorusunu yanıtla: {context}"),
        ("human", "{question}"),
    ]
)

FastAPI API Endpoin'i Oluşturma

Şimdi tüm bu bileşenleri bir araya getiren bir FastAPI endpoint'i tanımlayalım.


from fastapi import FastAPI
from pydantic import BaseModel
import uvicorn

# ... (önceki indeksleme ve retriever kodları buraya gelecek) ...

app = FastAPI(
    title="Hibrit RAG API",
    description="FastAPI ve Ollama ile Seyrek ve Yoğun Geri Getirim Destekli Üretim API'si"
)

class QueryRequest(BaseModel):
    query: str
    top_k: int = 5

@app.post("/ask")
async def ask_rag(request: QueryRequest):
    # 1. Seyrek Geri Getirim
    sparse_results = sparse_retrieve(request.query, bm25, chunks, k=request.top_k)

    # 2. Yoğun Geri Getirim
    dense_results = await dense_retrieve(request.query, vectorstore, k=request.top_k)

    # 3. Hibrit Birleştirme (RRF)
    combined_docs = reciprocal_rank_fusion([sparse_results, dense_results])
    
    # Sadece en iyi 3-5 belgeyi LLM'ye gönderelim
    context = "\n\n".join([doc.page_content for doc in combined_docs[:request.top_k]])

    # 4. LLM ile Üretim
    chain = prompt_template | llm
    response = await chain.ainvoke({"context": context, "question": request.query})

    return {"answer": response.content, "context_used": context}

if __name__ == "__main__":
    # Uygulamayı başlatmadan önce indekslerin oluşturulduğundan emin olun
    # Bu kısım sadece geliştirme için, üretimde indeksler önceden yüklenmeli
    # İlk çalıştırmada bm25 ve vectorstore'u oluştur
    # ... (veri yükleme, parçalama, bm25 ve vectorstore oluşturma kodları) ...
    
    uvicorn.run(app, host="0.0.0.0", port=8000)

Bu kod bloğu, bir kullanıcıdan sorgu alan, hibrit geri getirim yapan ve Ollama ile yanıt üreten temel bir API endpoint'ini göstermektedir. Gerçek bir uygulamada, indekslerin önceden yüklenmesi ve bağımlılıkların uygun şekilde yönetilmesi önemlidir.

Sonuç

Bu makalede, FastAPI ve Ollama kullanarak güçlü bir hibrit RAG API'sinin nasıl inşa edileceğini detaylı bir şekilde inceledik. Seyrek ve yoğun geri getirim yöntemlerinin avantajlarını birleştirerek, LLM'lerin daha doğru, bağlamsal ve güncel bilgilere dayalı yanıtlar üretmesini sağlayan bir sistem kurduk. FastAPI'nin performansı ve Ollama'nın yerel LLM çalıştırma yeteneği sayesinde, esnek, gizliliğe önem veren ve maliyet etkin bir çözüm elde ettik. Bu yaklaşım, özel bilgi tabanlarına dayalı sohbet botları, soru-cevap sistemleri ve akıllı asistanlar geliştirmek için sağlam bir temel sunmaktadır.

Sıkça Sorulan Sorular (SSS)

Hibrit RAG neden sadece seyrek veya yoğun geri getirime göre daha iyidir?

Hibrit RAG, hem anahtar kelime eşleşmesinin hassasiyetini (seyrek) hem de anlamsal anlayışın derinliğini (yoğun) birleştirir. Bu sayede, hem doğrudan terimlerin geçtiği hem de farklı ifadelerle sorulan karmaşık sorulara daha kapsamlı ve doğru yanıtlar verebilir, her iki yöntemin zayıf yönlerini telafi eder.

Ollama kullanmanın başlıca avantajları nelerdir?

Ollama, LLM'leri yerel makinenizde çalıştırmanıza olanak tanır, bu da veri gizliliği, maliyet tasarrufu (API ücreti yok), çevrimdışı çalışma yeteneği ve farklı modeller arasında kolayca geçiş yapma esnekliği sağlar. Ayrıca, bulut sağlayıcılara bağımlılığı azaltır.

FastAPI'nin bu tür bir API için tercih edilme nedenleri nelerdir?

FastAPI, yüksek performanslıdır, asenkron işlemleri doğal olarak destekler (bu da LLM çağrıları ve vektör veritabanı sorguları için önemlidir), Pydantic tabanlı veri doğrulama ve otomatik interaktif API dokümantasyonu sunar. Bu özellikler, hızlı geliştirme ve kolay bakım sağlar.

Reciprocal Rank Fusion (RRF) nasıl çalışır?

RRF, birden fazla geri getirimciden gelen sonuç listelerini birleştirmek için kullanılan bir algoritmadır. Her bir belgenin farklı listelerdeki sıralamasını dikkate alarak bir puan hesaplar. Daha yüksek sıralamalara (yani listenin başında olan belgelere) daha fazla ağırlık verilir. Bu puanlar toplanarak belgeler için nihai bir sıralama oluşturulur ve böylece farklı geri getirim yöntemlerinin en iyi sonuçları birleştirilmiş olur.

Bu API'yi üretim ortamında kullanmak için nelere dikkat etmeliyim?

Üretim ortamında, indekslerin önceden oluşturulup kalıcı olarak saklandığından ve API başlatıldığında hızlıca yüklendiğinden emin olmalısınız. Ollama sunucusunun kararlı ve erişilebilir olduğundan, LLM modelinin uygun şekilde yüklendiğinden emin olun. Ayrıca, API'nizi güvenlik önlemleriyle (kimlik doğrulama, yetkilendirme) güçlendirmeli, hata yönetimi ve loglama mekanizmalarını eklemelisiniz. Ölçeklenebilirlik için gunicorn gibi bir ASGI sunucusu ile kullanmanız önerilir.

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.