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.
Retrieval-Augmented Generation (RAG) sistemleri genellikle düz vector similarity search ile başlar. Bu taban çizgisi üç noktada tıkanır: exact keyword eşleşmeleri, cevabı birkaç dökümana yayılan sorular ve relevance açısından kimsenin bakmadığı context.
İlk kurulacak varsayılan, hybrid search ile parent-child chunking birleşimi. Reranking, self-corrective retrieval (CRAG) ve knowledge graph’ler (GraphRAG) bunun üzerine binen birer üst basamak; ölçüm varsayılanın yetmediğini gösterdiğinde devreye girerler. Aşağıdaki pattern’lerin AWS Bedrock Knowledge Bases ve OpenSearch karşılıkları da var; hangi basamağın kendini amorti ettiğine RAGAS metrikleri karar veriyor.
Temel RAG’in Problemleri#
Vector similarity tek başına production’da üç somut açık bırakır.
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.
# Basic RAG implementasyonu - yaygın başlangıç noktası
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
vectorstore = Chroma.from_documents(
documents=docs,
embedding=OpenAIEmbeddings(model="text-embedding-3-small")
)
# Vector similarity search
query = "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 Sorularda Tıkanma#
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.
Doğrulama Eksikliği#
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. Retrieval marginally ilgili dökümanlar döndürdüğünde ve LLM bunları authoritative olarak değerlendirdiğinde bu sorun özellikle belirginleşiyor.
Hybrid Search: Semantic ve Keyword Retrieval’ı Birleştirmek#
RAG sistemlerindeki ilk pratik upgrade, dense vector search’ü sparse keyword matching ile kombine eder. Bu hybrid yaklaşım exact match problemini çözerken semantic anlayışı korur.
İmplementasyon Stratejisi#
Hybrid search iki paralel retrieval çalıştırır:
- Dense retrieval: Vector similarity (semantic understanding)
- 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 Örnek#
from langchain.retrievers import EnsembleRetriever
from langchain_community.vectorstores import Chroma
from langchain_community.retrievers import BM25Retriever
from langchain_openai import OpenAIEmbeddings
# Dense vector retriever
vectorstore = Chroma.from_documents(
documents=docs,
embedding=OpenAIEmbeddings(model="text-embedding-3-small")
)
vector_retriever = vectorstore.as_retriever(
search_kwargs={"k": 10}
)
# Sparse keyword retriever
bm25_retriever = BM25Retriever.from_documents(docs)
bm25_retriever.k = 10
# Hybrid ensemble with RRF
ensemble_retriever = EnsembleRetriever(
retrievers=[vector_retriever, bm25_retriever],
weights=[0.5, 0.5] # Eşit ağırlık
)
# Her iki method ile retrieve et
results = ensemble_retriever.invoke(
"GAN architecture nedir?"
)
Performance Özellikleri#
İkinci retriever’ın değiştirdikleri:
- Named entity’ler güvenilir şekilde geliyor; embedding modelinin komşuluğa yaydığı stringi BM25 harfi harfine eşleştiriyor
- Kısaltmalar da aynı şekilde davranıyor (GAN, RAG, AWS, CDK)
- Latency, vector sorgusuyla paralel çalışan bir retrieval pass’i kadar artıyor
- Recall, precision’dan taviz vermeden yükseliyor; iki sonuç kümesi kesiştirilmek yerine birleştiriliyor
Weighted fusion’da alpha parametresi balance’ı kontrol eder:
# Alternatif: RRF yerine weighted fusion
def 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#
- Stage 1: High-recall retrieval - Geniş ağ at (k=50-100)
- Stage 2: Cross-encoder reranking - Candidate’lar üzerinde precision scoring
- 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#
from sentence_transformers import CrossEncoder
import numpy as np
# Stage 1: High-recall retrieval
initial_results = vectorstore.similarity_search(
query,
k=50 # Geniş ağ
)
# Stage 2: Cross-encoder reranking
reranker = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')
# Query-document pair'leri oluştur
query_doc_pairs = [
(query, doc.page_content)
for doc in initial_results
]
# Tüm pair'leri score et
scores = reranker.predict(query_doc_pairs)
# Stage 3: Score'a göre sırala ve top-k al
reranked_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 kullan
print(f"En yüksek score: {final_scores[0]:.3f}")
Reranking Ne Kazandırır#
Reranking sonuç kümesinin boyutunu değil sırasını değiştirir. Cross-encoder query ile dökümanı birlikte okuduğu için, bi-encoder’ın neredeyse aynı vector’e düşürdüğü iki chunk’ı birbirinden ayırabilir. Kazanç MRR@5 gibi ranking metriklerinde ve domain’e özgü sorgularda precision’da görünür; çünkü modelin yazacağı cevabı ilk üç sıra belirler.
Bedeli, her candidate için bir model inference’ı. İmplementasyonlar bu pair’leri batch halinde geçiriyor; 50 candidate modele 50 seri pass yerine birkaç batch’lik forward pass’te giriyor. Bunun sub-second bütçeye sığıp sığmadığı model boyutuna, batch boyutuna ve donanıma bağlı. GPU’da veya hosted bir reranking endpoint’inde çalışan küçük bir cross-encoder sığar; CPU’daki büyük bir model sığmaz. Kararı vermeden önce bu kombinasyonu ölç.
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: Bağlamı Korumak#
Dökümanları nasıl chunk’lara böldüğün retrieval quality’yi önemli ölçüde etkiler. Kötü chunk’lanmış içerik başka türlü sağlam RAG implementasyonlarını mahvedebilir.
Strateji Karşılaştırması#
Fixed-Size Chunking (Baseline)
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
from langchain_experimental.text_splitter import SemanticChunker
from 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 varsayılan strateji olarak iyi çalışır.
from langchain.retrievers import ParentDocumentRetriever
from langchain.storage import InMemoryStore
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from 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ı sakla
docstore = InMemoryStore()
# Vector store child chunk'ları indexler
vectorstore = Chroma(
embedding_function=OpenAIEmbeddings(model="text-embedding-3-small")
)
# Retriever konfigürasyonu
retriever = ParentDocumentRetriever(
vectorstore=vectorstore,
docstore=docstore,
child_splitter=child_splitter,
parent_splitter=parent_splitter,
)
# Document'ları ekle
retriever.add_documents(documents)
# Retrieval child chunk'larda arar ama parent'ları döner
results = retriever.invoke(
"Lambda cold start'ları nasıl optimize ederim?"
)
# Sonuçlar full parent context içerir
Performance Etkisi#
Parent-child retrieval, coherence’ı storage ile satın alır. Korpus iki kez durur: parent’lar bir document store’da, child’lar vector index’te. Sorgu anındaki maliyet düz vector search’e yakın kalır; eklenen tek iş, vector hit’inden sonra docstore’da yapılan bir key lookup.
Model artık cümle ortasında başlayıp tablo ortasında biten 400 token’lık bir dilim yerine, o dilimin ait olduğu bölümün tamamını okur.
Best Practice’ler#
İçerik türünden bağımsız işleyen kurallar:
- Natural boundary’lerde bitir: Chunk’ları cümle veya paragraf break’lerinde sonlandır
- Metadata ekle: Chunk metadata’sına döküman title, section header’ları dahil et
- Strategic overlap: Boundary’lerdeki bilgi kaybını önlemek için %10-20 overlap
- İç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: Kalite Doğrulaması#
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:
- Dökümanları retrieve et
- Her dökümanın relevance’ını grade et
- 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#
from langgraph.graph import StateGraph, END
from langchain_tavily import TavilySearch
from langchain_openai import ChatOpenAI
from typing import TypedDict, List
from langchain.schema import Document
# Workflow state tanımla
class RAGState(TypedDict):
query: str
documents: List[Document]
relevance_score: float
web_results: List[Document]
answer: str
# Component'ları initialize et
vectorstore = 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ştur
workflow = StateGraph(RAGState)
# Node'ları ekle
workflow.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 ekle
workflow.set_entry_point("retrieve")
workflow.add_edge("retrieve", "grade")
# Grading sonrası conditional routing
workflow.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ır
app = workflow.compile()
# Execute et
result = app.invoke({
"query": "En son AWS Lambda optimization teknikleri neler?"
})
print(result["answer"])
CRAG Nerede İşe Yarar#
Sonucu değiştiren şey evaluator’ın kendisi. Grading’den geçemeyen döküman prompt’a hiç ulaşmaz, dolayısıyla model cevabını ona dayandıramaz. Bu en çok retrieval güveninin düşük olduğu sorgularda ve knowledge base’in tamamen dışında kalan sorularda fark yaratır; düz bir pipeline’ın tam bir özgüvenle cevap uydurduğu yer de tam olarak burasıdır.
Her döküman bir grading çağrısına mal olur, düşük güvenli rota da üstüne bir web search turu ekler. Bir CRAG sorgusu böylece single-pass sorgunun birkaç katı latency ve token harcar; fatura canını yaktığında ilk küçültülecek model de grader olur.
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’in Sınırları#
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
- Entity’leri extract et (servisler, kavramlar, teknolojiler)
- İlişkileri extract et (integrates_with, depends_on, alternative_to)
- Claim’leri extract et (faktual ifadeler)
- Directed graph oluştur
Phase 2: Community Detection
- Hierarchical clustering için Leiden algorithm uygula
- Her level’da community summary’leri oluştur
- 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#
from langchain_neo4j import Neo4jGraph, GraphCypherQAChain
from langchain_openai import ChatOpenAI
# Neo4j graph database initialize et
graph = 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 et
def 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 retrieval
qa_chain = GraphCypherQAChain.from_llm(
llm=ChatOpenAI(model="gpt-4o"),
graph=graph,
verbose=True,
return_intermediate_steps=True
)
# Multi-hop query
response = 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ı#
Maliyetler:
- Preprocessing: entity ve relationship extraction her döküman için LLM çağrısı çalıştırır, ingestion faturasını da bu kalem belirler
- Storage: vector store’un yanında işletilecek bir graph database
- Complexity: Cypher’ı ve graph şemasını ekipten birinin sahiplenmesi gerekir
Faydalar:
- Multi-hop recall; traversal, similarity search’ün göremediği ilişkileri takip eder
- Global sorular (“X hakkında anlatılan her şeyi özetle”) community summary’lerden cevaplanır; top-k penceresi böyle bir bütünü hiç kuramaz
- İzlenebilir cevaplar; traversal yolunun kendisi aynı zamanda kaynak zinciridir
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, yukarıdaki pattern’lerin çoğunu managed bir servis olarak paketliyor. Hybrid search, hierarchical chunking ve reranking burada, işletmesini başkasının üstlendiği bir pipeline üzerinde birer konfigürasyon flag’i olarak geliyor.
Mimari Seçenekler#
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 Yaklaşımı#
1. RetrieveAndGenerate API (Fully Managed)
Tüm RAG pipeline’ını handle eder:
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çerir
for citation in citations:
print(f"Kaynak: {citation['retrievedReferences'][0]['location']}")
2. Retrieve API (Custom Control)
Pipeline üzerinde daha fazla kontrol için:
# Dökümanları retrieve et
retrieve_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 et
retrieved_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#
# 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 chunking
chunking_config = {
'chunkingStrategy': 'SEMANTIC',
'semanticChunkingConfiguration': {
'maxTokens': 300,
'bufferSize': 0,
'breakpointPercentileThreshold': 95
}
}
# Gelişmiş: Custom Lambda chunking function
chunking_config = {
'chunkingStrategy': 'NONE', # Default chunking'i disable et
'customChunkingConfiguration': {
'lambdaArn': 'arn:aws:lambda:us-east-1:123456789:function:custom-chunker'
}
}
Reranking Entegrasyonu#
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ı#
Bir Bedrock kurulumunda en çok ağırlığı olan ayarlar:
- numberOfResults artır: Default 5 genellikle yetersiz; complex query’ler için 10-15 kullan
- Hybrid Search aktif et: Named entity ve abbreviation retrieval’ı iyileştirir
- Reranking implement et: Technical query’lerde top-k’yı hangi dökümanların dolduracağını değiştirir
- Uygun Chunking seç: Technical doc’lar için hierarchical, narrative için semantic
- Token Usage monitor et: Embedding ve generation cost’larını ayrı track et
- Customer-Managed KMS kullan: Sensitive data encryption için
- Strategic Cache: Embedding’leri ve yaygın query sonuçlarını cache’le
Infrastructure as Code (CDK)#
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 bucket
docs_bucket = s3.Bucket(
self, "DocsBucket",
versioned=True,
encryption=s3.BucketEncryption.S3_MANAGED
)
# OpenSearch Serverless collection
vector_collection = opensearch.CfnCollection(
self, "VectorCollection",
name="rag-vectors",
type="VECTORSEARCH"
)
# Knowledge Base için IAM role
kb_role = iam.Role(
self, "KBRole",
assumed_by=iam.ServicePrincipal("bedrock.amazonaws.com")
)
docs_bucket.grant_read(kb_role)
# Bedrock Knowledge Base
kb = 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 sistemlerinde iyileştirme ölçüm gerektirir. RAGAS framework hem retrieval hem generation quality için automated, reference-free metrikler sağlıyor.
Temel Metrikler#
Retrieval Metrikleri:
- Context Precision: Relevant chunk’lar irrelevant olanlardan daha yüksek mı rank’leniyor?
- Context Recall: Gerekli tüm bilgiyi retrieve ettik mi?
- Context Relevancy: Retrieve edilen içeriğin ne kadarı gerçekten relevant?
Generation Metrikleri:
- Faithfulness: Cevap retrieve edilen context’te faktual olarak ground’lanmış mı?
- Answer Relevancy: Cevap soruyu address ediyor mu?
İmplementasyon#
from ragas import evaluate
from ragas.metrics import (
context_precision,
context_recall,
faithfulness,
answer_relevancy,
)
from datasets import Dataset
# Evaluation dataset hazırla
eval_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 et
result = 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#
import wandb
from ragas.integrations.wandb import log
# Monitoring initialize et
wandb.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 kullan
daily_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']
)
Metrikleri Okumak#
Örnek score’lar ve her birinin neyi değiştirmeni söylediği:
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ı: Demir Üçgen#
Her RAG mimari kararı üç rekabet eden faktörü dengeler: latency, maliyet ve accuracy. Birini iyileştirmek genellikle diğer ikisinden harcamak demektir.
Latency Breakdown#
Yavaş bir RAG endpoint’inde ilk suçlanan generation olur. Single-pass pipeline’da bu kabaca doğrudur. Yeniden retrieve edip yeniden grade eden konfigürasyonlarda ise doğru olmaktan çıkar: retrieval birkaç kez çalışır ve her tur kendi dökümanlarını prompt’a ekler, dolayısıyla prefill her geçişte büyür. Generation duvar saatinin azınlık payına düşer.
Bir şeyi tune etmeden önce bu dağılımı ölç. Retrieval ve prefill baskınsa generation modelini değiştirmek neredeyse hiçbir şeyi değiştirmez.
Stratejilere Göre Trade-off’lar#
| Strateji | Latency | Maliyet | Kazanç nerede |
|---|---|---|---|
| Basic vector search | Baseline | Baseline | Baseline |
| Hybrid search | Paralel çalışan bir ek pass | Bakımı yapılacak ikinci bir index | Exact term, isim, kısaltma |
| Cross-encoder reranking | Her candidate için bir inference | Reranker model çağrıları | Top-k’nın sıralaması |
| Multi-query (RAG-Fusion) | Her rewrite için bir retrieval | Sorgu başına rewrite + retrieval | Belirsiz veya eksik tanımlı sorgular |
| GraphRAG | Bedelin çoğu ingestion’da | Korpusun tamamında LLM extraction | Multi-hop ve global sorular |
| Parent-child retrieval | Vector hit’inden sonra docstore lookup | Korpus iki kez saklanır | Context bütünlüğü |
| Self-RAG/CRAG | Grading pass’i ve opsiyonel web search | Döküman başına bir grading çağrısı | Retrieval güvenilmezken faithfulness |
Optimizasyon Stratejileri#
1. Caching Stratejisi
from functools import lru_cache
import hashlib
import time
# Embedding'leri cache'le
@lru_cache(maxsize=10000)
def get_embedding(text: str):
return embedding_model.embed(text)
# Retrieval sonuçlarını cache'le
class 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ım
cache = 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
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 pipeline’da çağrı sayısını routing ve grading, token sayısını generation domine eder. Tasarruf da küçük modeli sık çağrılara koymakta; kullanıcının okuduğu cevap yine güçlü modelden gelir.
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)
Varsayılan ve Sınırları#
İlk kurulacak konfigürasyon, parent-child chunking’li hybrid search. En erken canını yakan iki sorunu, kaçan exact term’leri ve parçalanmış context’i onarır; bir keyword index’in ötesinde altyapı da istemez. Geri kalan her şey bir override ve her birinin bir koşulu var. Cross-encoder reranking, domain’e özgü sorgularda precision darboğaza dönüştüğünde inference bütçesini hak eder. CRAG, retrieval kalitesi öngörülemez olduğunda ve kendinden emin yanlış bir cevabın bedeli yüksek olduğunda ek geçişlerini hak eder. GraphRAG’in ingestion faturası ise ancak sorular gerçekten dökümanlar arasına yayıldığında ve korpus gerçek ilişkiler barındıracak kadar büyük olduğunda karşılığını verir; bin dökümanın altında veya çoğunlukla single-hop sorularda getirdiğinden fazlasını götürür.
RAGAS evaluation’ı bunların hepsinden önce kur; faithfulness ve context precision için bir baseline yoksa her yükseltme ilerleme gibi görünür.
Kaynaklar#
- Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks (yeni sekmede açılır) - Lewis ve ark. tarafından özgün RAG makalesi (2020), NeurIPS
- LangChain Retrieval Dokümantasyonu (yeni sekmede açılır) - RAG sistemleri oluşturmak için resmi LangChain rehberi
- LlamaIndex RAG - Giriş (yeni sekmede açılır) - Retrieval ve indeksleme için LlamaIndex framework rehberi
- Bağlamsal Geri Alma - Anthropic (yeni sekmede açılır) - Bağlamsal chunking üzerine Anthropic araştırması; %49-67 hata azaltımı
- OpenAI Vektör Gömme Rehberi (yeni sekmede açılır) - Matryoshka truncation dahil embedding model dokümantasyonu
İlgili yazılar
Bedrock Knowledge Base aslında nedir, hangi veri kaynakları ve vektör depoları birinci sınıf desteklenir ve konsol varsayılanı küçük bir korpusa neden nadiren uyar.
aws · aws-bedrock · rag +3
RAG sistemleri için doküman parsing, chunking stratejileri, bağlamsal zenginleştirme ve embedding optimizasyonunu kapsayan kapsamlı kılavuz
rag · llm · embeddings +2
AI/LLM alanında pratik, implementation odaklı bir sözlük. Token'lardan agent'lara, RAG'dan fine-tuning'e, kod örnekleri ve dürüst değerlendirmelerle.
llm · ai-agents · rag +6
AWS Bedrock + Knowledge Bases + OpenSearch Serverless üstüne CDK ile TypeScript kullanarak RAG agent kurmak: mimari, IAM bağlantısı, otomatik ingestion ve chat UI.
aws-bedrock · aws-cdk · rag +3
Kurumsal AI entegrasyonu için pratik 6 seviyeli framework: ChatGPT, RAG, MCP agent veya fine-tuning ne zaman kullanılmalı, PII ve finans uyumluluğu odaklı.
ai-adoption-strategy · rag · mcp +2