Skip to content
~/sph.sh

RAG Mimari Desenleri: Temel Vector Search'ün Ötesinde

Hybrid search, reranking, GraphRAG ve self-corrective pattern'ler dahil gelişmiş RAG tekniklerine dair kapsamlı rehber. Production AWS implementasyonu örnekleriyle.

Özet

Retrieval-Augmented Generation (RAG) sistemleri genellikle temel vector similarity search ile başlar, ancak bu yaklaşım multi-hop reasoning, exact keyword match ve complex query'lerde yetersiz kalır. Bu rehber, hybrid search, multi-stage reranking, intelligent chunking stratejileri, self-corrective retrieval (CRAG) ve knowledge graph'ler (GraphRAG) aracılığıyla bu sınırlamaları aşan gelişmiş RAG mimari desenlerini inceliyor. AWS Bedrock Knowledge Bases ve OpenSearch kullanarak pratik implementasyon pattern'lerini, production'da latency, maliyet ve accuracy arasındaki trade-off'ları ele alıyor ve RAGAS metrikleriyle evaluation framework'leri kuruyoruz. Çalışan kod örnekleri her bir pattern'i gerçekçi performance benchmark'larıyla gösteriyor.

Temel RAG'in Problemleri

RAG sistemleriyle çalışırken vector similarity search'ün tek başına production uygulamalarında önemli açıklar yarattığını gördüm. Karşılaştığım spesifik challenge'ları paylaşayım.

Exact Match Eksikliği

Vector embedding'ler semantic anlam yakalamada mükemmel ama precise match'lerde zorlanır. Kullanıcılar "AWS CDK", "GAN architecture" veya spesifik product code'ları aradığında, pure semantic search bu exact term'leri sıklıkla kaçırır. Embedding model "GAN"i (Generative Adversarial Network) genel "neural network" içeriğine semantically benzer olarak görür ve precision düşer.

python
# Basic RAG implementasyonu - yaygın başlangıç noktasıfrom langchain_community.vectorstores import Chromafrom langchain_openai import OpenAIEmbeddings
vectorstore = Chroma.from_documents(    documents=docs,    embedding=OpenAIEmbeddings(model="text-embedding-3-small"))
# Vector similarity searchquery = "GAN architecture nedir?"results = vectorstore.similarity_search(query, k=5)
# Problem: Exact "GAN" terimi olan dökümanları kaçırabilir# Bunun yerine semantically benzer "neural network" dökümanları döner

Multi-Hop Reasoning Başarısızlıkları

Basic RAG, single-step similarity'ye göre döküman getirir. Birden fazla döküman arasında connection gerektiren complex query'ler sistematik olarak başarısız olur:

  • "2020'de launch olan ve en düşük cold start time'a sahip AWS servisi hangisi?"
  • "Serverless database'leri Lambda ile kullanmanın security implication'ları neler?"

Bu sorular farklı kaynaklardan bilgi sentezi gerektirir, single-step retrieval bunu başaramaz.

Quality Verification Yok

Standard RAG pipeline'ları retrieve edilen dökümanları relevance doğrulaması yapmadan doğrudan LLM'e geçirir. İlgisiz context hallucination'lara ve düşük answer quality'ye sebep olur. Deneyimlerime göre, retrieval marginally ilgili dökümanlar döndürdüğünde ve LLM bunları authoritative olarak değerlendirdiğinde bu özellikle problematik hale geliyor.

Hybrid Search: Semantic ve Keyword Retrieval Kombinasyonu

RAG sistemlerinde yaptığım ilk pratik upgrade, dense vector search'ü sparse keyword matching ile kombine ediyor. Bu hybrid yaklaşım exact match problemini çözerken semantic anlayışı korur.

İmplementasyon Stratejisi

Hybrid search iki paralel retrieval çalıştırır:

  1. Dense retrieval: Vector similarity (semantic understanding)
  2. Sparse retrieval: BM25 keyword matching (exact term matching)

Sonuçlar Reciprocal Rank Fusion (RRF) kullanılarak merge edilir:

RRF(d) = Σ 1/(k + rank(d))

Burada k bir sabit (tipik 60) ve rank(d) dökümanın her result set'teki pozisyonu.

Çalışan İmplementasyon

python
from langchain.retrievers import EnsembleRetrieverfrom langchain_community.vectorstores import Chromafrom langchain_community.retrievers import BM25Retrieverfrom langchain_openai import OpenAIEmbeddings
# Dense vector retrievervectorstore = Chroma.from_documents(    documents=docs,    embedding=OpenAIEmbeddings(model="text-embedding-3-small"))vector_retriever = vectorstore.as_retriever(    search_kwargs={"k": 10})
# Sparse keyword retrieverbm25_retriever = BM25Retriever.from_documents(docs)bm25_retriever.k = 10
# Hybrid ensemble with RRFensemble_retriever = EnsembleRetriever(    retrievers=[vector_retriever, bm25_retriever],    weights=[0.5, 0.5]  # Eşit ağırlık)
# Her iki method ile retrieve etresults = ensemble_retriever.invoke(    "GAN architecture nedir?")

Performance Özellikleri

Technical documentation ile testler:

  • Named entity retrieval önemli ölçüde iyileşiyor (Biden, NATO, spesifik şirketler)
  • Abbreviation handling güvenilir hale geliyor (GAN, RAG, AWS, CDK)
  • Latency pure vector search'e göre sadece %5-10 artıyor
  • Recall precision kaybetmeden %15-25 iyileşiyor

Weighted fusion'da alpha parametresi balance'ı kontrol eder:

python
# Alternatif: RRF yerine weighted fusiondef weighted_fusion(vector_results, bm25_results, alpha=0.5):    """    alpha = 0.0: Pure keyword search    alpha = 0.5: Eşit ağırlık    alpha = 1.0: Pure semantic search    """    fused_scores = {}
    for doc in vector_results:        fused_scores[doc.id] = alpha * doc.score
    for doc in bm25_results:        if doc.id in fused_scores:            fused_scores[doc.id] += (1 - alpha) * doc.score        else:            fused_scores[doc.id] = (1 - alpha) * doc.score
    return sorted(        fused_scores.items(),        key=lambda x: x[1],        reverse=True    )

Multi-Stage Reranking: Recall'dan Sonra Precision

Hybrid search recall'u iyileştirir, ama production sistemler genellikle daha yüksek precision gerektirir. Multi-stage reranking bunu two-phase yaklaşımla çözer.

Mimari Pattern

  1. Stage 1: High-recall retrieval - Geniş ağ at (k=50-100)
  2. Stage 2: Cross-encoder reranking - Candidate'lar üzerinde precision scoring
  3. Stage 3: Top-k selection - LLM için final set (k=5-10)

Bu pattern retrieval'ı (hızlı, geniş) relevance scoring'den (yavaş, precise) ayırır.

Cross-Encoder vs Bi-Encoder

Farkı anlamak implementasyon için önemli:

  • Bi-encoder (traditional embeddings): Query ve dökümanları ayrı encode eder, vector'leri karşılaştırır
  • Cross-encoder: Query + döküman pair'lerini direkt relevance score için BERT-based model'e besler

Cross-encoder'lar daha accurate relevance score üretir ama büyük collection'lara scale olmazlar (her candidate'i ayrı score etmeli). Bu onları reranking stage için mükemmel yapar.

İmplementasyon

python
from sentence_transformers import CrossEncoderimport numpy as np
# Stage 1: High-recall retrievalinitial_results = vectorstore.similarity_search(    query,    k=50  # Geniş ağ)
# Stage 2: Cross-encoder rerankingreranker = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')
# Query-document pair'leri oluşturquery_doc_pairs = [    (query, doc.page_content)    for doc in initial_results]
# Tüm pair'leri score etscores = reranker.predict(query_doc_pairs)
# Stage 3: Score'a göre sırala ve top-k alreranked_indices = np.argsort(scores)[::-1][:10]final_docs = [initial_results[i] for i in reranked_indices]final_scores = [scores[i] for i in reranked_indices]
# final_docs'u LLM generation için kullanprint(f"En yüksek score: {final_scores[0]:.3f}")

Performance Metrikleri

Legal ve technical documentation ile testlerde:

  • MRR@5'te (Mean Reciprocal Rank) %59 absolute iyileşme
    • Baseline (reranking yok): MRR = 0.160
    • Reranking ile: MRR = 0.750
  • Domain-specific query'lerde precision'da %15 iyileşme
  • Latency trade-off: Query time'a %50-100 ekler (cross-encoder inference)

Quality sub-second response time'dan daha önemli olduğunda, bu trade-off değer.

Reranking Ne Zaman Kullanılır

Reranking implementasyonu yapılacak durumlar:

  • Accuracy gereksinimleri %85 precision'ı aşıyor
  • Nuanced relevance gerektiren complex technical query'ler
  • Yüksek accuracy stake'li legal, medical, financial domain'ler
  • Cross-encoder inference için computational resource var

Reranking atlanacak durumlar:

  • Sub-500ms latency gereksinimleri
  • Simple FAQ sistemleri
  • Sınırlı computational budget
  • Basic semantic matching yeterli

Chunking Stratejileri: Context Preservation

Dökümanları nasıl chunk'lara böldüğün retrieval quality'yi önemli ölçüde etkiler. Bu dersi kötü chunk'lanmış içeriğin başka türlü sağlam RAG implementasyonlarını mahvettiğini izleyerek öğrendim.

Strateji Karşılaştırması

Fixed-Size Chunking (Baseline)

python
from langchain.text_splitter import CharacterTextSplitter
splitter = CharacterTextSplitter(    chunk_size=512,    chunk_overlap=50)chunks = splitter.split_documents(documents)

Problemler:

  • Cümleleri arbitrary kırar
  • Code block'ları fonksiyon ortasında böler
  • Logical sınırlara saygı göstermez

Semantic Chunking

python
from langchain_experimental.text_splitter import SemanticChunkerfrom langchain_openai import OpenAIEmbeddings
splitter = SemanticChunker(    embeddings=OpenAIEmbeddings(model="text-embedding-3-small"),    breakpoint_threshold_type="percentile")chunks = splitter.split_documents(documents)

Faydalar:

  • Topic coherence korur
  • Natural section boundary'leri
  • Daha yüksek preprocessing maliyeti

Parent-Child Hierarchical Chunking

Bu yaklaşım precision için küçük chunk'larda arama yapar ama context için daha büyük parent chunk'ları döner. Technical documentation için default stratejim oldu.

python
from langchain.retrievers import ParentDocumentRetrieverfrom langchain.storage import InMemoryStorefrom langchain.text_splitter import RecursiveCharacterTextSplitterfrom langchain_community.vectorstores import Chromafrom langchain_openai import OpenAIEmbeddings
# Parent splitter (context için büyük chunk'lar)parent_splitter = RecursiveCharacterTextSplitter(    chunk_size=2000,    chunk_overlap=200)
# Child splitter (precision için küçük chunk'lar)child_splitter = RecursiveCharacterTextSplitter(    chunk_size=400,    chunk_overlap=50)
# Parent doc'ları ayrı sakladocstore = InMemoryStore()
# Vector store child chunk'ları indexlervectorstore = Chroma(    embedding_function=OpenAIEmbeddings(model="text-embedding-3-small"))
# Retriever konfigürasyonuretriever = ParentDocumentRetriever(    vectorstore=vectorstore,    docstore=docstore,    child_splitter=child_splitter,    parent_splitter=parent_splitter,)
# Document'ları ekleretriever.add_documents(documents)
# Retrieval child chunk'larda arar ama parent'ları dönerresults = retriever.invoke(    "Lambda cold start'ları nasıl optimize ederim?")# Sonuçlar full parent context içerir

Performance Etkisi

Benchmark testlerinde:

  • Baseline fixed-size chunking'e göre %65 win rate
  • +0.2 saniye latency (minimal etki)
  • 2-3x storage overhead (parent + child chunk'lar indexlenir)
  • Context coherence'da önemli iyileşme

Best Practice'ler

İmplementasyon deneyimlerinden:

  1. Natural boundary'lerde bitir: Chunk'ları cümle veya paragraf break'lerinde sonlandır
  2. Metadata ekle: Chunk metadata'sına döküman title, section header'ları dahil et
  3. Strategic overlap: Boundary'lerdeki bilgi kaybını önlemek için %10-20 overlap
  4. İçeriğe göre strateji seç:
    • Technical doc'lar → Semantic veya hierarchical
    • Code → Function/class-level chunk'lar
    • Narrative → Overlap'li sliding window
    • Structured data → Metadata'lı parent-child

Self-RAG ve Corrective RAG: Quality Verification

Basic RAG retrieve edilen dökümanların relevant olduğunu varsayar. Bu varsayım production'da sık başarısız olur, hallucination'lara ve kötü answer'lara sebep olur. Self-correcting pattern'ler explicit quality check'lerle bunu çözer.

CRAG Pattern

Corrective RAG (CRAG), generation'dan önce döküman relevance'ını grade eden bir retrieval evaluator ekler. Confidence score'lara göre sistem farklı processing path'lerine yönlendirir.

Workflow:

  1. Dökümanları retrieve et
  2. Her dökümanın relevance'ını grade et
  3. Aggregate confidence'a göre route et:
    • Yüksek confidence (>0.7): Knowledge refinement ile devam
    • Düşük confidence (<0.3): Web search tetikle
    • Orta confidence (0.3-0.7): Web search + refinement kombine et

Knowledge Refinement dökümanları "knowledge strip'lere" böler, her strip'i grade eder ve LLM'e geçirmeden önce irrelevant içeriği filtreler.

LangGraph ile İmplementasyon

python
from langgraph.graph import StateGraph, ENDfrom langchain_tavily import TavilySearchfrom langchain_openai import ChatOpenAIfrom typing import TypedDict, Listfrom langchain.schema import Document
# Workflow state tanımlaclass RAGState(TypedDict):    query: str    documents: List[Document]    relevance_score: float    web_results: List[Document]    answer: str
# Component'ları initialize etvectorstore = Chroma.from_documents(docs, OpenAIEmbeddings(model="text-embedding-3-small"))llm = ChatOpenAI(model="gpt-4o-mini")web_search = TavilySearch(max_results=3)
# Node fonksiyonlarıdef retrieve(state):    """Initial dökümanları retrieve et"""    docs = vectorstore.similarity_search(state["query"], k=5)    return {"documents": docs}
def grade_documents(state):    """Döküman relevance'ını grade et"""    prompt = f"""    Bu dökümanın query'ye relevance'ını 0-1 arasında score et.    Query: {state["query"]}    Döküman: {state["documents"][0].page_content[:500]}
    Sadece 0 ile 1 arasında bir sayı döndür.    """
    # Her dökümanı grade et    scores = []    for doc in state["documents"]:        response = llm.invoke(prompt)        score = float(response.content.strip())        scores.append(score)
    avg_score = sum(scores) / len(scores)    return {"relevance_score": avg_score}
def route_query(state):    """Relevance score'a göre route et"""    score = state["relevance_score"]
    if score > 0.7:        return "correct"    elif score < 0.3:        return "incorrect"    else:        return "ambiguous"
def perform_web_search(state):    """Fallback web search"""    web_results = web_search.invoke(state["query"])    web_docs = [        Document(page_content=result["content"])        for result in web_results    ]    return {"web_results": web_docs}
def refine_knowledge(state):    """Döküman strip'leri partition et ve filtrele"""    refined_docs = []
    for doc in state["documents"]:        # Basit strip partitioning (cümle seviyesi)        sentences = doc.page_content.split('. ')
        for sentence in sentences:            # Her cümleyi grade et            grade_prompt = f"""            Bu cümle query ile alakalı mı: {state["query"]}?            Cümle: {sentence}            Sadece şöyle cevap ver: evet veya hayır            """            response = llm.invoke(grade_prompt)
            if "evet" in response.content.lower():                refined_docs.append(sentence)
    return {"documents": [Document(page_content=". ".join(refined_docs))]}
def generate_answer(state):    """Final cevabı generate et"""    context_docs = state.get("documents", [])    web_docs = state.get("web_results", [])
    all_context = context_docs + web_docs    context_text = "\n\n".join([doc.page_content for doc in all_context])
    prompt = f"""    Bu context'e göre soruyu cevapla:
    Context:    {context_text}
    Soru: {state["query"]}
    Cevap:    """
    response = llm.invoke(prompt)    return {"answer": response.content}
# Graph'ı oluşturworkflow = StateGraph(RAGState)
# Node'ları ekleworkflow.add_node("retrieve", retrieve)workflow.add_node("grade", grade_documents)workflow.add_node("web_search", perform_web_search)workflow.add_node("refine", refine_knowledge)workflow.add_node("generate", generate_answer)
# Edge'leri ekleworkflow.set_entry_point("retrieve")workflow.add_edge("retrieve", "grade")
# Grading sonrası conditional routingworkflow.add_conditional_edges(    "grade",    route_query,    {        "correct": "refine",        "incorrect": "web_search",        "ambiguous": "web_search"    })
workflow.add_edge("refine", "generate")workflow.add_edge("web_search", "generate")workflow.add_edge("generate", END)
# Compile ve çalıştırapp = workflow.compile()
# Execute etresult = app.invoke({    "query": "En son AWS Lambda optimization teknikleri neler?"})
print(result["answer"])

Performance Faydaları

CRAG ile production testlerinde:

  • Hallucination'larda %30-40 azalma (faithfulness score ile ölçülür)
  • Retrieval'ın uncertain olduğu query'lerde iyileşmiş accuracy
  • Knowledge base dışındaki query'lerin daha iyi handling'i
  • Latency trade-off: Grading ve potansiyel web search yüzünden %100-150 artış

GraphRAG: Multi-Hop Reasoning için Knowledge Graph'ler

Traditional RAG query'ye semantic similarity'ye göre retrieve eder. Bu single-hop sorular için çalışır ama cevaplar birden fazla döküman arasında bilgi connection'ı gerektirdiğinde başarısız olur. GraphRAG bunu knowledge graph construction ve graph-based retrieval ile çözer.

Traditional RAG Ne Zaman Başarısız Olur

GraphRAG'in daha iyi handle ettiği complex query'ler:

  • "Hem Lambda hem DynamoDB ile integrate olan AWS servisleri hangileri?"
  • "Serverless database pattern'lerinin security implication'ları neler?"
  • "Documentation boyunca bahsedilen tüm best practice'leri özetle"

Bunlar relationship anlayışı ve dökümanlar arası bilgi sentezi gerektirir.

GraphRAG Mimarisi

Phase 1: Knowledge Graph Construction

  1. Entity'leri extract et (servisler, kavramlar, teknolojiler)
  2. İlişkileri extract et (integrates_with, depends_on, alternative_to)
  3. Claim'leri extract et (faktual ifadeler)
  4. Directed graph oluştur

Phase 2: Community Detection

  1. Hierarchical clustering için Leiden algorithm uygula
  2. Her level'da community summary'leri oluştur
  3. Hierarchical index oluştur

Phase 3: Retrieval

  • Global search: Geniş sorular için community summary'leri kullan
  • Local search: Spesifik relationship query'leri için graph traverse et
  • Hybrid: Graph traversal'ı vector similarity ile kombine et

İmplementasyon Pattern

python
from langchain_neo4j import Neo4jGraph, GraphCypherQAChainfrom langchain_openai import ChatOpenAI
# Neo4j graph database initialize etgraph = Neo4jGraph(    url="bolt://localhost:7687",    username="neo4j",    password="your-password")
# Entity ve relationship extraction (basitleştirilmiş)def extract_entities_relationships(text: str):    """Graph element'lerini extract etmek için LLM kullan"""    prompt = f"""    Bu metinden entity'leri ve ilişkileri extract et.    Format: (Entity1)-[RELATIONSHIP]->(Entity2)
    Metin: {text}    """
    llm = ChatOpenAI(model="gpt-4o")    response = llm.invoke(prompt)    return response.content
# Graph'ı populate etdef build_knowledge_graph(documents):    for doc in documents:        # Structured data extract et        graph_data = extract_entities_relationships(doc.page_content)
        # Cypher query'lere convert et        # Örnek: CREATE (lambda:Service {name: 'AWS Lambda'})        # CREATE (lambda)-[:INTEGRATES_WITH]->(dynamodb)
        # Cypher execute et        # graph.query(cypher_statement)        pass
# Query-time retrievalqa_chain = GraphCypherQAChain.from_llm(    llm=ChatOpenAI(model="gpt-4o"),    graph=graph,    verbose=True,    return_intermediate_steps=True)
# Multi-hop queryresponse = qa_chain.invoke({    "query": "DynamoDB ile integrate olan ve event-driven architecture destekleyen serverless servisler hangileri?"})
print(response["result"])print("Cypher Query:", response["intermediate_steps"][0]["query"])

Performance Trade-off'ları

GraphRAG ile gerçek dünya deneyimi:

Maliyetler:

  • Preprocessing: Basic RAG'den 5-10x daha yüksek (entity extraction, graph construction)
  • Storage: Ek graph database infrastructure
  • Complexity: Graph database expertise gerektirir

Faydalar:

  • Multi-hop recall: Baseline'a göre +6.4 puan iyileşme
  • Hallucination azalması: Biomedical QA'da %18 (Dual-Pathway KG-RAG araştırması)
  • Query efficiency: Flat graph pipeline'lara göre 250x token azalması (ArchRAG)
  • Hız: Adaptive dual-mode retrieval ile 10-100x speedup (E²GraphRAG)

GraphRAG Ne Zaman Kullanılır

İyi fit:

  • Zengin relationship domain'leri (medical, legal, enterprise knowledge)
  • Multi-hop reasoning gereksinimleri
  • Büyük korpus boyunca holistic anlayış ihtiyacı
  • Mevcut preprocessing budget

Kötü fit:

  • Basit FAQ sistemleri
  • Öncelikle single-hop query'ler
  • Sınırlı preprocessing kaynakları
  • Küçük döküman collection'ları (<1000 döküman)

AWS Bedrock Knowledge Bases: Production İmplementasyonu

AWS Bedrock Knowledge Bases, tartıştığımız pattern'lerle entegre olan managed bir RAG çözümü sunuyor. AWS'de production'da gelişmiş RAG'i nasıl implement edeceğini görelim.

Mimari Seçenekleri

Vector Store Seçimleri:

  • Amazon OpenSearch Serverless: Production RAG için en yaygın, hybrid search destekler
  • Amazon Aurora PostgreSQL: pgvector extension ile, mevcut PostgreSQL kullanıcıları için iyi
  • Amazon Neptune Analytics: GraphRAG pattern'leri için
  • Third-party: MongoDB Atlas, Pinecone, Redis Enterprise Cloud

İki API Pattern

1. RetrieveAndGenerate API (Fully Managed)

Tüm RAG pipeline'ını handle eder:

python
import boto3
bedrock_agent_runtime = boto3.client('bedrock-agent-runtime')
response = bedrock_agent_runtime.retrieve_and_generate(    input={        'text': 'Lambda cold start'ları nasıl optimize ederim?'    },    retrieveAndGenerateConfiguration={        'type': 'KNOWLEDGE_BASE',        'knowledgeBaseConfiguration': {            'knowledgeBaseId': 'KB123456',            'modelArn': 'arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-sonnet-4-5-20250929-v1:0',            'retrievalConfiguration': {                'vectorSearchConfiguration': {                    'numberOfResults': 10,  # Default 5'ten artır                    'overrideSearchType': 'HYBRID'  # Hybrid search                }            }        }    })
answer = response['output']['text']citations = response['citations']
# Citation'lar source attribution içerirfor citation in citations:    print(f"Kaynak: {citation['retrievedReferences'][0]['location']}")

2. Retrieve API (Custom Control)

Pipeline üzerinde daha fazla kontrol için:

python
# Dökümanları retrieve etretrieve_response = bedrock_agent_runtime.retrieve(    knowledgeBaseId='KB123456',    retrievalQuery={        'text': 'Lambda cold start'ları nasıl optimize ederim?'    },    retrievalConfiguration={        'vectorSearchConfiguration': {            'numberOfResults': 20,            'overrideSearchType': 'HYBRID'        }    })
# Retrieve edilen chunk'ları process etretrieved_docs = retrieve_response['retrievalResults']for doc in retrieved_docs:    content = doc['content']['text']    score = doc['score']    source = doc['location']['s3Location']
    print(f"Score: {score:.3f} - Kaynak: {source['uri']}")
# Şimdi kontrol sende:# - Custom reranking logic# - Döküman filtering# - Prompt construction# - Generation için model selection

Gelişmiş Chunking Konfigürasyonu

python
# Hierarchical chunking (parent-child pattern)chunking_config = {    'chunkingStrategy': 'HIERARCHICAL',    'hierarchicalChunkingConfiguration': {        'levelConfigurations': [            {                'maxTokens': 1500  # Parent chunk boyutu            },            {                'maxTokens': 300   # Child chunk boyutu            }        ],        'overlapTokens': 60    }}
# Alternatif: Semantic chunkingchunking_config = {    'chunkingStrategy': 'SEMANTIC',    'semanticChunkingConfiguration': {        'maxTokens': 300,        'bufferSize': 0,        'breakpointPercentileThreshold': 95    }}
# Gelişmiş: Custom Lambda chunking functionchunking_config = {    'chunkingStrategy': 'NONE',  # Default chunking'i disable et    'customChunkingConfiguration': {        'lambdaArn': 'arn:aws:lambda:us-east-1:123456789:function:custom-chunker'    }}

Reranking Entegrasyonu

python
retrieve_config = {    'vectorSearchConfiguration': {        'numberOfResults': 50,  # High recall        'overrideSearchType': 'HYBRID',        'rerankingConfiguration': {            'type': 'BEDROCK_RERANKING_MODEL',            'bedrockRerankingConfiguration': {                'numberOfResults': 10,  # Reranking sonrası precision                'modelConfiguration': {                    'modelArn': 'arn:aws:bedrock:us-west-2::foundation-model/cohere.rerank-v3-5:0'                }            }        }    }}

Production Optimizasyon İpuçları

AWS implementasyonlarından:

  1. numberOfResults artır: Default 5 genellikle yetersiz; complex query'ler için 10-15 kullan
  2. Hybrid Search aktif et: Named entity ve abbreviation retrieval'da önemli iyileşme
  3. Reranking implement et: Technical query'lerde %40-60 quality iyileştirmesi
  4. Uygun Chunking seç: Technical doc'lar için hierarchical, narrative için semantic
  5. Token Usage monitor et: Embedding ve generation cost'larını ayrı track et
  6. Customer-Managed KMS kullan: Sensitive data encryption için
  7. Strategic Cache: Embedding'leri ve yaygın query sonuçlarını cache'le

Infrastructure as Code (CDK)

python
from aws_cdk import (    aws_bedrock as bedrock,    aws_opensearchserverless as opensearch,    aws_s3 as s3,    aws_iam as iam,)
# Dökümanlar için S3 bucketdocs_bucket = s3.Bucket(    self, "DocsBucket",    versioned=True,    encryption=s3.BucketEncryption.S3_MANAGED)
# OpenSearch Serverless collectionvector_collection = opensearch.CfnCollection(    self, "VectorCollection",    name="rag-vectors",    type="VECTORSEARCH")
# Knowledge Base için IAM rolekb_role = iam.Role(    self, "KBRole",    assumed_by=iam.ServicePrincipal("bedrock.amazonaws.com"))
docs_bucket.grant_read(kb_role)
# Bedrock Knowledge Basekb = bedrock.CfnKnowledgeBase(    self, "RAGKnowledgeBase",    name="production-rag-kb",    role_arn=kb_role.role_arn,    knowledge_base_configuration=bedrock.CfnKnowledgeBase.KnowledgeBaseConfigurationProperty(        type="VECTOR",        vector_knowledge_base_configuration=bedrock.CfnKnowledgeBase.VectorKnowledgeBaseConfigurationProperty(            embedding_model_arn="arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-embed-text-v2:0"        )    ),    storage_configuration=bedrock.CfnKnowledgeBase.StorageConfigurationProperty(        type="OPENSEARCH_SERVERLESS",        opensearch_serverless_configuration=bedrock.CfnKnowledgeBase.OpenSearchServerlessConfigurationProperty(            collection_arn=vector_collection.attr_arn,            vector_index_name="bedrock-knowledge-base-index",            field_mapping=bedrock.CfnKnowledgeBase.OpenSearchServerlessFieldMappingProperty(                vector_field="embedding",                text_field="text",                metadata_field="metadata"            )        )    ))
# Data source (S3)data_source = bedrock.CfnDataSource(    self, "S3DataSource",    name="s3-docs",    knowledge_base_id=kb.attr_knowledge_base_id,    data_source_configuration=bedrock.CfnDataSource.DataSourceConfigurationProperty(        type="S3",        s3_configuration=bedrock.CfnDataSource.S3DataSourceConfigurationProperty(            bucket_arn=docs_bucket.bucket_arn        )    ))

RAGAS ile Evaluation: RAG Quality Ölçümü

RAG sistemleriyle çalışmak bana iyileştirmenin ölçüm gerektirdiğini öğretti. RAGAS framework hem retrieval hem generation quality için automated, reference-free metrikler sağlıyor.

Core Metrikler

Retrieval Metrikleri:

  1. Context Precision: Relevant chunk'lar irrelevant olanlardan daha yüksek mı rank'leniyor?
  2. Context Recall: Gerekli tüm bilgiyi retrieve ettik mi?
  3. Context Relevancy: Retrieve edilen içeriğin ne kadarı gerçekten relevant?

Generation Metrikleri:

  1. Faithfulness: Cevap retrieve edilen context'te faktual olarak ground'lanmış mı?
  2. Answer Relevancy: Cevap soruyu address ediyor mu?

İmplementasyon

python
from ragas import evaluatefrom ragas.metrics import (    context_precision,    context_recall,    faithfulness,    answer_relevancy,)from datasets import Dataset
# Evaluation dataset hazırlaeval_data = {    'question': [        'AWS Lambda cold start optimization teknikleri neler?',        'DynamoDB partition key'leri nasıl handle eder?'    ],    'answer': [        'Lambda cold start\'ları provisioned concurrency ile optimize edilebilir, bu fonksiyonları initialized tutar ve Java fonksiyonları için initialization time\'ı azaltan SnapStart kullanılabilir.',        'DynamoDB partition key\'leri kullanarak data\'yı partition\'lar arasında dağıtır. Yüksek kardinaliteli partition key\'ler eşit dağıtım ve optimal performans sağlar.'    ],    'contexts': [        [            'Provisioned concurrency Lambda fonksiyonlarını initialized ve response\'a hazır tutar.',            'SnapStart Java fonksiyonları için initialized state cache\'leyerek cold start time\'ı azaltır.',            'Package size azaltma gibi fonksiyon optimizasyonları cold start performance\'ı iyileştirir.'        ],        [            'Partition key\'ler DynamoDB\'nin data\'yı partition\'lar arasında nasıl dağıtacağını belirler.',            'Eşit data dağıtımı sağlamak için yüksek kardinaliteli partition key\'ler seçin.',            'Düşük kardinaliteli key\'ler hot partition ve throttling yaratır.'        ]    ],    'ground_truth': [        'Provisioned concurrency, SnapStart ve fonksiyon optimizasyonu Lambda cold start\'ları azaltır',        'Partition key\'ler data dağıtır; yüksek kardinalite eşit dağıtım sağlar'    ]}
dataset = Dataset.from_dict(eval_data)
# Evaluate etresult = evaluate(    dataset,    metrics=[        context_precision,        context_recall,        faithfulness,        answer_relevancy,    ],)
print(f"Context Precision: {result['context_precision']:.3f}")print(f"Context Recall: {result['context_recall']:.3f}")print(f"Faithfulness: {result['faithfulness']:.3f}")print(f"Answer Relevancy: {result['answer_relevancy']:.3f}")print(f"\nRAGAS Score: {result.mean():.3f}")

Production Monitoring

python
import wandbfrom ragas.integrations.wandb import log
# Monitoring initialize etwandb.init(project="rag-production-monitoring")
def monitor_rag_quality(queries, answers, contexts, ground_truths):    """Production'da continuous evaluation"""    eval_data = Dataset.from_dict({        'question': queries,        'answer': answers,        'contexts': contexts,        'ground_truth': ground_truths    })
    result = evaluate(        eval_data,        metrics=[            context_precision,            context_recall,            faithfulness,            answer_relevancy        ]    )
    # W&B'ye log et    log(result, run=wandb.run)
    # Quality degradation'da alert et    if result['faithfulness'] < 0.7:        send_alert("Faithfulness threshold'un altına düştü!")
    if result['context_precision'] < 0.6:        send_alert("Context precision bozuldu!")
    return result
# Production'da kullandaily_queries = get_sample_queries()  # Production query sample'ıdaily_results = monitor_rag_quality(    queries=daily_queries['questions'],    answers=daily_queries['answers'],    contexts=daily_queries['contexts'],    ground_truths=daily_queries['ground_truths'])

Metrik İnterpretasyonu

Context Precision (0.85)

  • Relevant dökümanların %85'i üst pozisyonlarda
  • İyi ranking quality
  • Düşük score'lar irrelevant sonuçların çok yüksek rank'lendiğini gösterir

Context Recall (0.75)

  • Gerekli bilginin %75'i retrieve edildi
  • Relevant içeriğin %25'i kayıp
  • numberOfResults artır veya chunking'i iyileştir

Faithfulness (0.92)

  • Cevap claim'lerinin %92'si context tarafından destekleniyor
  • Düşük hallucination oranı
  • 0.7'nin altı ciddi problemleri gösterir

Answer Relevancy (0.88)

  • Cevap soru intent'inin %88'ini address ediyor
  • Minimal tangential bilgi
  • 0.7'nin altı answer drift gösterir

Production Trade-off'ları: The Iron Triangle

Her RAG mimari kararı üç rekabet eden faktörü dengelemeyi içerir: latency, maliyet ve accuracy. Optimizasyon hakkında öğrendiklerimi paylaşayım.

Latency Breakdown

Birden fazla re-retrieval pass içeren aggressive RAG konfigürasyonlarında breakdown beni şaşırttı:

Total end-to-end latency: ~30 saniye (iterative retrieval-grading cycle'lı sistemler için)

  • Retrieval: %36 (10.8s)
  • Ek prefill overhead: %45 (13.5s)
  • Generation: %19 (5.7s)

RAG component'leri bu aggressive senaryolarda total latency'nin %97'sini tüketiyor. Standard single-pass RAG tipik olarak 1-3 saniyede tamamlanır.

Strateji-Spesifik Trade-off'lar

StratejiLatency EtkisiMaliyet EtkisiAccuracy Artışı
Basic Vector SearchBaseline (1x)BaselineBaseline
Hybrid Search+%5-10+%20+%15-25
Cross-Encoder Reranking+%50-100+%30+%40-60
Multi-Query (RAG-Fusion)+%200+%300+%20-30
GraphRAG+%500 (preprocessing)+%400+%30-50 (multi-hop)
Parent-Child Retrieval+%10+%200 (storage)+%25-35
Self-RAG/CRAG+%100-150+%200+%30-40

Optimizasyon Stratejileri

1. Caching Stratejisi

python
from functools import lru_cacheimport hashlibimport time
# Embedding'leri cache'le@lru_cache(maxsize=10000)def get_embedding(text: str):    return embedding_model.embed(text)
# Retrieval sonuçlarını cache'leclass RAGCache:    def __init__(self, ttl_seconds=3600):        self.cache = {}        self.ttl = ttl_seconds
    def get_cache_key(self, query: str, k: int):        return hashlib.md5(f"{query}:{k}".encode()).hexdigest()
    def get(self, query: str, k: int):        key = self.get_cache_key(query, k)        if key in self.cache:            result, timestamp = self.cache[key]            if time.time() - timestamp < self.ttl:                return result        return None
    def set(self, query: str, k: int, result):        key = self.get_cache_key(query, k)        self.cache[key] = (result, time.time())
# Kullanımcache = RAGCache(ttl_seconds=3600)
def cached_retrieval(query: str, k: int = 10):    cached_result = cache.get(query, k)    if cached_result is None:        result = retriever.invoke(query)        cache.set(query, k, result)        return result    return cached_result

2. Model Routing

python
class AdaptiveRAG:    def __init__(self):        # Auxiliary task'ler için ucuz model'ler kullan        self.router_model = "gpt-4o-mini"        self.grader_model = "gpt-4o-mini"        # Sadece generation için güçlü model kullan        self.generator_model = "claude-sonnet-4-6-20250217"
    def classify_query_complexity(self, query: str):        """Küçük model ile route et"""        prompt = f"Bu basit mi complex bir query mi? {query}"        response = openai.chat.completions.create(            model=self.router_model,            messages=[{"role": "user", "content": prompt}],            max_tokens=10        )        return response.choices[0].message.content
    def grade_documents(self, query: str, docs: list):        """Küçük model ile grade et"""        # self.grader_model kullanarak grading logic        pass
    def generate_answer(self, query: str, docs: list):        """Güçlü model ile generate et"""        # self.generator_model kullanarak generation logic        pass

Bu yaklaşım final cevap quality'sinden taviz vermeden maliyetleri %60 azaltıyor.

Production Karar Framework

Low Latency Öncelik (<1s response):

  • Basic vector search veya lightweight hybrid
  • Multi-query pattern'ler ve heavy reranking'den kaçın
  • Aggressive caching
  • Quantized embedding model'ler
  • Tuned parametrelerle HNSW indexing

High Accuracy Öncelik (>%90 faithfulness):

  • Hybrid search + cross-encoder reranking
  • Quality verification için CRAG
  • Hierarchical/parent-child chunking
  • Multi-hop query'ler için GraphRAG
  • Daha yüksek latency ve maliyet kabul et

Cost-Constrained:

  • Daha küçük embedding model'leri
  • Sınırlı numberOfResults (5-10)
  • Multi-query pattern'lerden kaçın
  • Routing/grading için ucuz LLM'ler kullan
  • Aggressive caching
  • Approximate indexing (IVF+PQ)

Balanced Yaklaşım (En Yaygın):

  • RRF ile hybrid search
  • Lightweight reranking
  • Parent-child chunking
  • Orta numberOfResults (10-15)
  • Selective caching
  • Mid-tier model'ler (Claude Haiku, GPT-4o-mini)

Önemli Çıkarımlar

  1. Basic RAG production için yetersiz: Vector similarity tek başına exact match'leri kaçırır ve multi-hop reasoning'de başarısız olur

  2. Hybrid search hızlı kazanç sağlar: Minimal latency artışıyla %15-25 accuracy iyileşmesi

  3. Chunking stratejisi önemli ölçüde etkiler: Parent-child hierarchical chunking, fixed-size splitting'e göre %65 win rate elde ediyor

  4. Reranking precision'ı dramatik iyileştirir: Cross-encoder reranking kullanıldığında MRR@5'te %59 absolute iyileşme

  5. Quality check'ler hallucination'ı önler: Self-RAG ve CRAG retrieval validation ile hallucination'ları %30-40 azaltır

  6. GraphRAG complex reasoning'de mükemmel: 6.4 puanlık multi-hop recall iyileşmesi ama 5x preprocessing investment gerektirir

  7. Evaluation şart: RAGAS framework automated metriklerle data-driven optimizasyon sağlar

  8. Iron triangle'ı dengele: Gereksinimlere göre latency, maliyet veya accuracy için optimize et - hepsini aynı anda değil

  9. Model routing maliyetleri %60 düşürür: Auxiliary task'ler için küçük model'ler, sadece generation için güçlü model'ler kullan

  10. Mimari complexity ile eşleşmeli: Basit query'ler → Basic RAG; technical query'ler → Hybrid + Reranking; multi-hop → GraphRAG

  11. Continuous monitoring drift'i yakalar: Active evaluation olmadan production RAG quality zamanla bozulur

  12. Progressive enhancement en iyi çalışır: Basit başla, ölçümler gerekçelendirdiğinde complexity ekle

RAG sistemleriyle çalışırken en iyi mimarinin tamamen spesifik gereksinimlerine bağlı olduğunu öğrendim. Hybrid search ve parent-child chunking ile başla - bunlar manageable complexity ile substantial iyileştirmeler sağlar. Reranking ve quality verification pattern'lerini sadece metrikler ihtiyacı gösterdiğinde ekle. Ve her zaman ölç: RAGAS evaluation, sezginin tek başına kaçırdığı optimizasyon fırsatlarını ortaya çıkarır.

İlgili Yazılar