Modül 6 · RAG and Agentic Search · ⏱ 7 dakika

Multi-index RAG pipeline

A Multi-Index RAG pipeline

Orijinal İngilizce video — Anthropic Academy / JWPlayer. Aşağıdaki Türkçe metin dersi birebir özetler.

Bu modül için tahmini süre: 7 dakika

Genel bakış

Semantic search (vector embedding’ler) ve lexical search (BM25) için ayrı implementasyonlar kurduk. Bu derste, ikisini birleştiren bir Retriever sınıfı kuruyor ve reciprocal rank fusion (RRF) tekniği ile sonuçları birleştirip hybrid bir search pipeline’ı oluşturuyoruz.

Semantic search (vector embedding’ler kullanan) ve lexical search (BM25 kullanan) için ayrı implementasyonlar kurduk. Şimdi sıra, iki yaklaşımın güçlerinden yararlanan birleşik bir search pipeline’ı oluşturmaya geldi.

Multi-index mimarisi

Hem VectorIndex hem de BM25Index sınıflarımız neredeyse aynı API’yi paylaşıyor — ikisinin de add_document() ve search() metodları var. Bu tutarlılık, onları Retriever adlı yeni bir sınıfta birlikte sarmalamayı basitleştiriyor.

Retriever, kullanıcı sorgularını her iki index’e ileten, sonuçlarını toplayan ve reciprocal rank fusion adlı bir teknikle birleştiren bir koordinatör olarak çalışır.

Reciprocal rank fusion’ı anlamak

Farklı search yöntemlerinden gelen sonuçları birleştirmek, listeleri art arda eklemek kadar basit değildir. Her yöntem farklı scoring sistemleri kullanır, bu yüzden ranking’leri adil bir şekilde normalize edip birleştirmenin bir yoluna ihtiyacımız var.

Reciprocal rank fusion’ın bir örnekle nasıl çalıştığını görelim. “INC-2023-Q4-011” hakkında bilgi aradığımızı ve şu sonuçları aldığımızı varsayalım:

  • VectorIndex döndürür: Section 2 (rank 1), Section 7 (rank 2), Section 6 (rank 3)
  • BM25Index döndürür: Section 6 (rank 1), Section 2 (rank 2), Section 7 (rank 3)

Bunları tek bir tabloda birleştirir, her text chunk’ının her iki index’teki rank’ini gösteririz, sonra RRF formülünü uygularız:

RRF_score(d) = Σ(1 / (k + rank_i(d)))

Burada k bir sabittir (genelde 60, ama daha net sonuçlar için 1 kullanacağız) ve rank_i(d), d belgesinin i’inci ranking’deki rank’idir.

Örneğimiz için:

  • Section 2: 1.0/(1+1) + 1.0/(1+2) = 0.833
  • Section 7: 1.0/(1+2) + 1.0/(1+3) = 0.583
  • Section 6: 1.0/(1+3) + 1.0/(1+1) = 0.75

Final ranking şöyle olur: Section 2 (0.833), Section 6 (0.75), Section 7 (0.583). Bu sezgisel olarak mantıklıdır — Section 2 her iki index’te de iyi performans gösterdiği için en üste çıkıyor.

Implementasyon detayları

Retriever sınıfı birden fazla search index’i sarmalar ve birleşik bir interface sağlar:

class Retriever:
    def __init__(self, *indexes: SearchIndex):
        if len(indexes) == 0:
            raise ValueError("At least one index must be provided")
        self._indexes = list(indexes)
    
    def add_document(self, document: Dict[str, Any]):
        for index in self._indexes:
            index.add_document(document)
    
    def search(self, query_text: str, k: int = 1, k_rrf: int = 60):
        # Get results from all indexes
        all_results = []
        for idx, results in enumerate(all_results):
            for rank, (doc, _) in enumerate(results):
                # Track document ranks across indexes
                # Apply RRF scoring formula
        # Return merged and sorted results

Temel fikir şu: farklı search implementasyonları arasında tutarlı API’ler koruyarak, sıkı bir bağımlılık olmadan onları kolayca birleştirebiliriz.

Hybrid yaklaşımı test etmek

Daha önce “what happened with INC-2023-Q4-011?” araması yaptığımızda yalnızca vector tabanlı yaklaşımın beklenmeyen sonuçlar döndürdüğü problemi hatırla. Cybersecurity incident (Section 10) önce geldi, ama daha alakalı software engineering bölümü yerine financial analysis (Section 3) ikinci sırada yer aldı.

Hybrid retriever’ımızla artık çok daha iyi sonuçlar alıyoruz:

  • Section 10: Cybersecurity Analysis — Incident Response Report (en alakalı)
  • Section 2: Software Engineering — Project Phoenix Stability Enhancements (ikinci en alakalı)
  • Section 5: Legal Developments (üçüncü)

Bu, semantic ve lexical search’ü birleştirmenin, her birinin tek başına kullanıldığında ortaya çıkan kısıtları nasıl aştığını gösterir.

Genişletilebilirlik

Bu mimarinin güzelliği, genişletilebilir olmasıdır. Tüm index’ler add_document() ve search() metodlarıyla aynı SearchIndex protocol’ünü uyguladığı için, yeni search yöntemlerini kolayca ekleyebilirsin:

Keyword-based bir index mi eklemek istiyorsun? Graph-based bir search mı? Özelleştirilmiş bir domain index mi? Sadece aynı interface’i uygula; Retriever onu fusion sürecine otomatik dahil eder.

Bu modüler yaklaşım, her search implementasyonunu odaklı ve test edilebilir tutarken, son sistemde güçlerini birleştirmenin temiz bir yolunu sağlar.

Temel çıkarımlar

  • Retriever sınıfı, birden fazla search index’i birleşik bir interface arkasında sarmalar
  • Reciprocal Rank Fusion (RRF), farklı scoring sistemlerinden gelen sonuçları fair şekilde birleştirir
  • RRF formülü: Σ(1 / (k + rank_i(d))); her index’te iyi sıralanan belgeler üste çıkar
  • Hybrid retriever, yalnızca vector veya yalnızca BM25’ten daha sağlam sonuç verir
  • SearchIndex protocol’üne uyan herhangi bir yeni index (graph, keyword, domain) sisteme dahil edilebilir