AI Agent Framework'leri — Derinlemesine Best Practices Rehberi (LangGraph, PydanticAI, LlamaIndex)
LangGraph · PydanticAI · LlamaIndex (RAG)
Python ile production seviyesinde agentic sistemler inşa etmek isteyen senior/principal seviyesi geliştiriciler için referans doküman. Bu rehber, API sözdiziminden ziyade framework’ler değişse bile değerini koruyan mühendislik kavramlarına (state, dayanıklılık/durability, structured output, retrieval pipeline’ları) odaklanır.
İçindekiler
- LangGraph — Orkestrasyonun Omurgası
- PydanticAI — Agent Katmanı
- LlamaIndex — Bir Disiplin Olarak RAG
- Framework’ler Arası Mimari Pattern’ler
- Son Kontrol Listesi
1. LangGraph — Orkestrasyonun Omurgası
1.1 Neden LangGraph’e En Fazla Yatırımı Yapmalısın
Buradaki temel içgörü şu: framework değişebilir/atılabilir, ama kavramlar değişmez. LangGraph şu an için şunları öğrenmek adına iyi bir araç:
- State management (durum yönetimi)
- Durable execution (dayanıklı çalıştırma)
- Checkpointing
- Retries (yeniden deneme)
- Branching (dallanma)
- Human-in-the-loop (HITL — insanın döngüde olması)
- Multi-agent orchestration (çoklu agent orkestrasyonu)
- Failure recovery (hata sonrası kurtarma)
Temporal’a, AWS Step Functions’a, özel bir actor sistemine ya da gelecekteki başka bir framework’e geçsen bile bu sekiz kavram gerçek beceri olarak kalır. LangGraph’i sadece “LLM’i döngü içinde çağırma yöntemi” olarak değil, dayanıklı, stateful, dağıtık sistemler için bir öğretim aracı olarak ele al.
1.2 State Management
Zihinsel model: Graph’ın bir state machine (durum makinesi). Her node neredeyse saf bir fonksiyon: f(state) -> partial_state_update. Graph motoru bu güncellemeleri reducer’lar aracılığıyla birleştirir.
Best Practices
- Herhangi bir node yazmadan önce state şemanı tasarla. Buna bir veritabanı şeması gibi davran — şekli erkenden doğru kur, migration’lar sonradan çok acı verir.
- State için
TypedDictveya Pydantic modelleri kullan, ham dict kullanma. IDE autocomplete, runtime doğrulama (Pydantic ile) ve kendi kendini belgeleyen kod elde edersin. - “Public” state’i (kullanıcıya/diğer agent’lara görünen) “scratch” state’ten (dahili muhakeme, tool çağrısı ara sonuçları) ayır. Dahili debug gürültüsünün persist edilen/dönen veriye sızmasına izin verme.
- Reducer’ları bilinçli seç. Varsayılan “son yazan kazanır” reducer’ı skaler değerler dışında neredeyse her şey için yanlıştır. Listeler için (örn. mesajlar, tool çağrıları)
add/appendreducer’ı kullan. Sayaçlar için biriktirme (accumulation) kullan. Fact kümeleri için merge/union reducer’ı kullan. - State’i JSON-serialize edilebilir tut. Temiz şekilde serialize edemeyeceğin her şey (açık dosya handle’ları, DB bağlantıları, ham exception objeleri) state içinde yaşamamalı — bunun yerine referans/ID geçir ve node içinde tekrar canlandır (re-hydrate).
- State şemanı versiyonla. Bir alan ekleyip/kaldırıp/yeniden adlandırdığında, migration fonksiyonu yazmazsan production’daki eski checkpoint’ler bozulur. Buna tam olarak bir DB şema migration’ı gibi davran.
from typing import Annotated, TypedDict
from operator import add
class AgentState(TypedDict):
messages: Annotated[list, add] # sadece ekleme yapılan geçmiş
retries: Annotated[int, add] # retry sayısını biriktir
plan: str | None # burada son-yazan-kazanır sorun değil
scratch: dict # sadece dahili, kullanıcıya asla dönmez
Anti-Pattern’ler
- Devasa blob’ları (tam dokümanlar, embedding’ler) doğrudan graph state’inde saklamak — bunun yerine bir store’a pointer/ID geçir.
- Bir node içinde state’i delta döndürmek yerine yerinde (in-place) mutasyona uğratmak. Bu, reducer semantiğini bozar ve debug’ı non-deterministik hale getirir.
- State’i “belki ileride lazım olur” alanları için çöp kutusu gibi kullanmak. Her alan bir bakım yüküdür.
1.3 Durable Execution (Dayanıklı Çalıştırma)
Zihinsel model: Uzun süre çalışan bir agent workflow’u, tam olarak bir veritabanı işleminin server yeniden başlatmasına dayanması gibi, process restart’larını, deploy’ları ve crash’leri atlatabilmeli.
Best Practices
- Process’in çalışma ortasında ölebileceğini varsay. Her node’u güvenle yeniden çalıştırılabilir (idempotent) olacak şekilde tasarla ya da son checkpoint’ten yeniden çalıştırmanın yan etkileri tekrarlamayacağından emin ol.
- Yan etkileri (API çağrıları, ödemeler, e-posta gönderimi) node’ların kenarlarına it ve yan etki çağrısının kendisini idempotent yap (idempotency key’leri, request ID ile dedupe) böylece crash sonrası bir replay müşteriyi iki kez faturalandırmaz ya da e-postayı iki kez göndermez.
- “Devam ettirilebilir” (resumable) state’i “geçici” (ephemeral) state’ten ayır. Açık socket bağlantıları gibi şeyler checkpoint’lenemez — sadece onların niyeti (örn. “X endpoint’ini Y payload’ıyla çağır”) persist edilmeli ve gerçek çağrı resume sırasında yeniden çalıştırılmalı.
- Saga mantığıyla düşün. Dış yan etkileri olan çok adımlı workflow’lar için, her ileri adım için telafi edici aksiyonlar (geri alma adımları) tasarla, böylece workflow ortasındaki başarısızlıklar tutarsız state bırakmak yerine temizce geri alınabilir.
Anti-Pattern’ler
- Durable execution’ı “sadece retry ekle” olarak ele almak. Idempotency olmadan retry = tekrarlanan yan etkiler.
- Tüm workflow’un kesintisiz, uzun ömürlü tek bir Python process’inde çalışacağını varsaymak. Production’da böyle olmayacak.
1.4 Checkpointing
Zihinsel model: Bir checkpoint, state + graph içindeki konumun, dayanıklı bir depoya (Postgres, Redis, dev için SQLite) persist edilmiş bir anlık görüntüsüdür ve çalıştırmanın tam kaldığı yerden devam etmesini sağlar.
Best Practices
- Sadece sonda değil, her node’dan sonra checkpoint al. Checkpoint’lerinin granülerliği, hata durumunda etki alanını (blast radius) belirler — çok kaba checkpoint alırsan, bir crash zaten para ödediğin pahalı işi (örn. LLM çağrıları) tekrar yaptırır.
- Production’da gerçek, dayanıklı bir backend kullan (Postgres/Redis), in-memory checkpointer’ı değil — o sadece lokal geliştirme/test için.
- Her checkpoint anahtarına bir
thread_id(konuşma/oturum tanımlayıcısı) dahil et. Bu, rastgele bir konuşma yerine doğru konuşmayı devam ettirebilmeni sağlayan şey. - Snapshot semantiği önemlidir: tek bir satırın üzerine yazmak yerine immutable, sadece-ekleme yapılan checkpoint logları tercih et. Bu, sana neredeyse bedavaya zaman-yolculuğu (time-travel) debug imkanı verir (önceki herhangi bir checkpoint’ten replay).
- Eski checkpoint’leri periyodik olarak temizle/arşivle — sınırsız büyüyen bir checkpoint tablosu eninde sonunda performans ve maliyet problemi olur.
Design Pattern: Zaman-Yolculuğu (Time-Travel) Debugging
Checkpoint’ler sadece persist edilmiş state anlık görüntüleri olduğu için şunları yapabilirsin:
- Bir
thread_idiçin herhangi bir geçmiş checkpoint’i yükle. - Değiştirilmiş bir input ile o noktadan çalıştırmayı fork’la.
- Yeni izlenimi orijinaliyle karşılaştır.
Bu, tüm çok turlu konuşmayı sıfırdan tekrar çalıştırmadan “agent neden X yaptı” sorusunu debug etmek için paha biçilmez.
1.5 Retries (Yeniden Deneme)
Zihinsel model: Tüm hatalar eşit değildir. Bir network timeout’u, bir rate-limit hatası, bozuk bir LLM çıktısı ve gerçek bir iş mantığı hatası, her biri farklı bir retry stratejisi gerektirir.
Best Practices
- Nasıl retry yapacağına karar vermeden önce hataları sınıflandır:
- Geçici/altyapı hataları (timeout’lar, 5xx, rate limit’ler) → üstel geri çekilme (exponential backoff) + jitter, sınırlı maksimum deneme.
- Bozuk/geçersiz LLM çıktısı (şema doğrulama hatası) → aynı prompt’u körlemesine tekrar denemek yerine, doğrulama hatasını içeren bir onarım (repair) prompt‘uyla tekrar dene.
- Gerçek iş mantığı hataları (örn. “kullanıcı bulunamadı”) → retry ETME; hemen yüzeye çıkar.
- Retry’ları bir circuit breaker ile sınırla. Belirli bir tool/API için N ardışık başarısızlıktan sonra durup eskale et (bir insana, bir fallback yola, ya da run’ı temizce başarısız kıl).
- Retry sayısını yerel bir değişken yerine state’in parçası yap, böylece retry’lar bir process restart’ından sağ çıkar ve sessizce sıfırlanmaz.
- Her retry’ı nedeniyle birlikte logla/trace et. “3 kez retry edildi” bilgisi, her denemenin neden başarısız olduğunu bilmeden işe yaramaz — bu, production’daki birincil debug sinyalindir.
def call_llm_with_repair(state):
try:
result = llm.invoke(state["messages"])
validated = OutputSchema.model_validate_json(result.content)
return {"result": validated, "retries": 0}
except ValidationError as e:
if state["retries"] >= MAX_RETRIES:
return {"error": "max_retries_exceeded", "retries": state["retries"]}
repair_prompt = f"Son çıktın doğrulamadan geçmedi: {e}. Düzelt ve sadece geçerli JSON döndür."
return {"messages": [repair_prompt], "retries": state["retries"] + 1}
1.6 Branching (Dallanma)
Zihinsel model: Gerçek agent workflow’ları lineer pipeline’lar değildir — koşullu kenarlara, paralel fan-out’a ve birleşme noktalarına sahip graph’lardır.
Best Practices
- Kararları tek bir mega-node içine gömülü
if/elseyerine açık koşullu kenarlar olarak modelle. Açık dallar graph görselleştirmesinde görünür, debug edilebilir ve izole şekilde test edilebilir. - Routing fonksiyonlarını saf ve yan etkisiz tut. Bir router sadece state okumalı ve sonraki node ismini/isimlerini döndürmeli — kendisi LLM çağırmamalı veya state’i mutasyona uğratmamalı (bunu önceki bir node’da yap, sonra sonuca göre routing yap).
- Bağımsız paralel alt görevler için fan-out/fan-in kullan (örn. “3 kaynağı paralel araştır, sonra birleştir”) ve state reducer’larının paralel dallardan gelen eşzamanlı güncellemeleri doğru birleştirdiğinden emin ol.
- Dallanma faktörünü sınırla. Sınırsız dinamik fan-out (örn. “her arama sonucu için bir dal oluştur”) maliyeti/gecikmeyi patlatabilir — sınırla ve bu sınırı yapılandırılabilir yap.
- Birleşme (convergence) node’larını kısmi başarısızlığı ele alacak şekilde tasarla — 5 paralel daldan 2’si başarısız olursa, kısmi sonuçlarla devam etmeyi, sadece başarısız dalları tekrar denemeyi ya da tüm run’ı başarısız kılmayı açıkça karar ver.
Design Pattern: Supervisor Routing
Yaygın ve sağlam bir dallanma pattern’i: bir “supervisor” node, mevcut state/niyeti sınıflandırır ve birkaç uzman node/subgraph’tan birine yönlendirir, bunlar da kontrolü supervisor’a geri döndürür. Bu, routing mantığını dağınık ad-hoc koşullar yerine merkezi ve denetlenebilir tutar.
1.7 Human-in-the-Loop (HITL)
Zihinsel model: Bazı kararlar tam otomasyona bırakılamayacak kadar maliyetli, belirsiz ya da geri döndürülemez. HITL, UI katmanına sonradan eklenen bir şey değil, birinci sınıf bir interrupt mekanizmasıdır.
Best Practices
- Polling veya sleep yapmak yerine iyi tanımlanmış checkpoint noktalarında interrupt kullan. Graph çalıştırmayı duraklatmalı, state’i persist etmeli ve kontrolü gerçekten geri vermeli — bu dakikalar, saatler, hatta günler sonra olabilir.
- “Resume” payload sözleşmesini açıkça tasarla. Bir insanın onayı/düzenlemesi/reddi veri yapısı olarak nasıl görünür? Bunu önceden şema olarak tanımla (örn.
{"decision": "approve"} | {"decision": "edit", "new_value": ...} | {"decision": "reject", "reason": ...}). - HITL noktalarını hardcode yerine yapılandırılabilir yap. Agent’a güven arttıkça, “her tool çağrısını onayla"dan “sadece yüksek riskli aksiyonları onayla"ya kod yeniden yazmadan geçmek isteyeceksin — bunu dağınık
input()çağrılarından değil, bir policy/config’ten yönlendir. - İnsana karar vermesi için her zaman yeterli bağlam ver — sadece ham tool çağrısını gösterme; muhakeme izini, state diff’ini ve onayın sonucunu göster.
- Her insan kararını audit trail’in parçası olarak logla. Bu, gelecekteki otomasyonu daraltmak için eğitim verisi haline gelir ve sıklıkla bir uyumluluk (compliance) gerekliliğidir.
Anti-Pattern’ler
- Bir insan için sınırsız süre beklerken senkron bir web isteğini bloklamak — HITL’i her zaman async tasarla (persist et, bildir, sonra resume et), request/response döngüsünde bloklayan bir çağrı olarak asla değil.
- Her tek aksiyon için “her zaman onay iste"yi hardcode etmek, bu kullanıcıları körlemesine “onayla"ya tıklamaya alıştırır (onay yorgunluğu) — gerçekten bir insana ihtiyaç duyan kararlar konusunda seçici ol.
1.8 Multi-Agent Orchestration (Çoklu Agent Orkestrasyonu)
Zihinsel model: Birden çok uzmanlaşmış agent’ın (ya da subgraph’ın) işbirliği yapması, temelde bir dağıtık sistemler problemidir: mesaj geçişi, paylaşılan state, sahiplik sınırları ve hata izolasyonu.
Best Practices
- Her alt-agent’a dar, iyi tanımlanmış bir sorumluluk ve açık input/output sözleşmesi ver. “Her şeyi yapan” bir agent debug edilemez ve test edilemez.
- Topolojini bilinçli seç:
- Supervisor/worker: bir orkestratör uzmanlara yönlendirir — akıl yürütmesi ve debug’ı en kolay olan, iyi bir varsayılan.
- Peer-to-peer/swarm: agent’lar doğrudan birbirine devreder — daha esnek, debug edilmesi ve sınırlandırılması çok daha zor (sonsuz devir döngülerine dikkat et).
- Hiyerarşik: supervisor’ların supervisor’ı — gerçekten büyük sistemler için gerekli, gerçek operasyonel karmaşıklık ekler.
- Her alt-agent’ın scratch state’ini paylaşılan state’ten izole et. Alt-agent’lar sadece sözleşmelerinde açıkça belirtilen şeyi okumalı/yazmalı, tüm global state’i değil (kazara coupling’i önler).
- Agent’lar arası devir/tur sayısına kesin bir üst sınır koy böylece birbirine sürekli erteleyen agent’lar arasında sonsuz döngüyü önle.
- Kısmi başarısızlık için tasarla. Bir alt-agent başarısız olursa, orkestratörün açık bir politikası olmalı: o alt-agent’ı tekrar dene, bir fallback ile değiştir ya da genel run’ı başarısız kıl — bir alt-agent’taki sessiz bir exception’ın diğerleri için paylaşılan state’i bozmasına asla izin verme.
Design Pattern: Sözleşme-Öncelikli (Contract-First) Alt-Agent’lar
Herhangi bir alt-agent’ın prompt’unu ya da mantığını yazmadan önce, onun arayüz sözleşmesini yaz: tam input şeması, tam output şeması ve başarısızlık modları. Her alt-agent’a bir API sözleşmesi olan bir mikroservis gibi davran — çoklu agent sistemlerini ölçekte gerçekten sürdürülebilir kılan şey budur.
1.9 Failure Recovery (Hata Sonrası Kurtarma)
Zihinsel model: Failure recovery, bir demo’yu production sisteminden ayıran şeydir. Node’ların, tool’ların ve modellerin başarısız OLACAĞINI varsay; sadece mutlu yol (happy path) için değil, zarif bozulma (graceful degradation) için tasarla.
Best Practices
- Sistemin için açık bir hata taksonomisi tanımla: geçici/tekrar denenebilir, degrade (kısmi başarı kabul edilebilir), ve fatal (durmalı ve eskale etmeli). Her birini farklı yönlendir.
- Her zaman bir fallback yol olsun. Ana LLM/tool’un retry’lardan sonra başarısız olursa, fallback nedir? Daha ucuz/basit bir model? Bir uyarıyla birlikte cache’lenmiş/eski bir yanıt? Bir insana devretme? Bunu her node için implicit değil açıkça karar ver.
- Kısmi başarıyı gerçek, modellenmiş bir sonuç yap — her şey ikili başarı/başarısızlık değildir. 5 kaynaktan 3’ünü alan bir araştırma agent’ı, tüm run’ı başarısız kılmak yerine belgelenmiş bir eksiklikle devam edebilmeli.
- Başarısızlık anında sonradan debug edebilmek için yeterli bağlamı persist et — başarısızlık anındaki tam state, hata ve retry geçmişi. Buna dağıtık bir sistem için stack trace gibi davran.
- Bireysel başarısızlıklar değil, başarısızlık oranı anomalilerinde uyarı ver. Bireysel geçici başarısızlıklar normaldir; belirli bir node/tool için başarısızlık oranındaki bir sıçrama eyleme geçirilebilir sinyaldir.
2. PydanticAI — Agent Katmanı
2.1 Bu Kombinasyon Neden İşe Yarıyor
Zaten Python + Pydantic kullanıyorsan, değer PydanticAI’nin spesifik API’sinde değil — seni şunlar hakkında doğru düşünmeye zorlamasında:
- Tool tasarımı
- Structured output
- Dependency injection
- Validation (doğrulama)
- Agent sınırları
- Context management
- Testing
Bunlar, herhangi bir tip’li, test edilebilir agent katmanı inşa ederken sahip olacağın aynı endişelerdir — PydanticAI bunlar için sadece iyi varsayılanlar ve koruma çitleri sağlar.
2.2 Tool Tasarımı
Zihinsel model: Bir tool, non-deterministik bir çağırana (LLM) verdiğin tip’li bir fonksiyon sözleşmesidir. Onu, güvenilmeyen, ara sıra kafası karışan bir istemci için tasarladığın bir public API gibi tasarla.
Best Practices
- Her tool parametresi Pydantic ile güçlü tip’li olmalı, kısıtlamalar dahil (örn.
Field(ge=0, le=100), serbest metin string’ler yerine kapalı seçim kümeleri için enum’lar). Bu, “LLM çöp bir değer geçti"yi “LLM, kendi kendine düzeltebileceği açık bir doğrulama hatası alıyor"a dönüştürür. - Docstring’leri diğer geliştiriciler için değil, LLM için yaz. Docstring, tool’un prompt’unun TA KENDİSİDİR — ne zaman kullanılacağı, neyi döndürdüğü ve edge case’ler konusunda açık ol (“Sonuç bulunamazsa boş liste döndürür, geçerli bir sorgu için asla hata fırlatmaz”).
- 15 opsiyonel parametreli tek bir mega-tool yerine, tool’ları dar ve kompoze edilebilir tut. Bir LLM, bir
modeayırt edicisine sahip tek bir “travel_action” tool’undan çok “search_flights” + “book_flight” hakkında daha iyi muhakeme eder. - Mümkün olduğunda tool’ları idempotent yap, özellikle yan etkisi olan herhangi bir tool — çünkü LLM onu birden fazla kez çağırabilir (retry’lar, yeniden planlama), tekrar eden çağrılar gerçek dünya etkilerini tekrarlamamalı.
- Tool’lardan ham string yerine yapılandırılmış, tip’li sonuçlar döndür — çağıran agent kodu ve herhangi bir downstream tool, tekrar parse etmesi gereken bir string değil, geriye bir Pydantic model almalı.
- Tool hatalarını kendi kendini düzeltebilir olacak şekilde tasarla. Bir tool hata fırlattığında, hata mesajı LLM’in bir sonraki adımda göreceği şeyin parçası olur — hata mesajlarını modele tam olarak neyi düzelteceğini söyleyecek şekilde yaz.
from pydantic import BaseModel, Field
from pydantic_ai import Agent, RunContext
class FlightSearchParams(BaseModel):
origin: str = Field(description="IATA havalimanı kodu, örn. 'JFK'")
destination: str = Field(description="IATA havalimanı kodu, örn. 'LAX'")
date: str = Field(description="ISO 8601 tarih, örn. '2026-09-01'")
max_results: int = Field(default=5, ge=1, le=20)
class Flight(BaseModel):
flight_number: str
price_usd: float
departure_time: str
agent = Agent("openai:gpt-4o", deps_type=FlightAPI)
@agent.tool
def search_flights(ctx: RunContext[FlightAPI], params: FlightSearchParams) -> list[Flight]:
"""İki havalimanı arasında belirli bir tarihte mevcut uçuşları arar.
Hiçbiri bulunamazsa boş liste döndürür. Geçerli, iyi biçimlendirilmiş
bir arama için asla hata fırlatmaz — sadece bozuk IATA kodlarında hata verir."""
return ctx.deps.search(params)
Anti-Pattern’ler
- Modelin zihinsel olarak “parse etmesi” gereken devasa yapılandırılmamış metin blob’ları döndüren tool’lar — bu, structured tool’ların kaldırmaya çalıştığı tam kırılganlığı yeniden getirir.
- Amacı örtüşen belirsiz isimli/açıklamalı tool’lar (
get_datavsfetch_datavsretrieve_data) — model tutarsız şekilde seçim yapar.
2.3 Structured Output
Zihinsel model: LLM’in nihai cevabına chat metni gibi değil, bir API yanıtı gibi davran — herhangi bir dış girdiyi doğruladığın gibi doğrula, çünkü aslında öyledir.
Best Practices
- “Ürün” konuşma gibi hissettirse bile, agent’ın nihai cevabı için her zaman açık bir Pydantic output şeması tanımla — yapılandırılmış bir objeyi UI katmanında düz yazı olarak render edebilirsin, ama içeride garantiler istiyorsun.
- Belirsizliği kapatmak için iç içe geçmiş modeller + enum’lar kullan.
status: Literal["approved", "rejected", "needs_review"],status: str‘den çok daha güvenlidir. - Alan düzeyinde açıklamalar ekle — bunlar modelin gördüğü şemanın parçası haline gelir ve çıktı kalitesini ciddi şekilde artırır, sadece dokümantasyon değildirler.
- İlgiliyse güven/belirsizliği açıkça doğrula — örn. bir
confidence: floatya daneeds_human_review: boolalanı ekle, böylece downstream mantığın (HITL routing gibi) serbest metinden çekingen dil koklamaya çalışmak yerine somut bir dala sahip olur. - Doğrulama başarısızlıklarını, bir retry sınırıyla sınırlı otomatik yeniden-sorma ile ele al (PydanticAI bunu native olarak result retries ile destekler) — tek bir bozuk çıktının tüm run’ı çökertmesine izin verme.
- Aşırı iç içe geçirme. Derin iç içe geçmiş output şemaları (5+ seviye), modellerin doğru doldurması için daha zor ve senin doğrulaman/debug etmen için daha zordur — makul olduğunda düzleştir.
from typing import Literal
from pydantic import BaseModel, Field
class TicketTriage(BaseModel):
category: Literal["billing", "technical", "account", "other"]
priority: Literal["low", "medium", "high", "urgent"]
summary: str = Field(description="Sorunun tek cümlelik özeti")
needs_human_review: bool = Field(description="Belirsiz veya yüksek riskliyse True")
confidence: float = Field(ge=0.0, le=1.0)
triage_agent = Agent("openai:gpt-4o", result_type=TicketTriage, retries=2)
2.4 Dependency Injection
Zihinsel model: Agent’ının davranışı koddur (deterministik), agent’ının muhakemesi LLM’dir (non-deterministik). Dependency injection, deterministik kısımları test edilebilir, değiştirilebilir ve gizli global state’ten arınmış tutmanın yoludur.
Best Practices
- Tüm dış bağımlılıkları (DB bağlantıları, API istemcileri, config, mevcut kullanıcı/tenant) tip’li bir
depsobjesi üzerinden geçir, modül seviyesi global’ler veya tool fonksiyonlarına dağılmış environment variable lookup’ları üzerinden asla. deps‘i açık tiplere sahip bir Pydantic modeli veya düz bir dataclass yap — bu senin agent’ının “ortamı"dır ve bir fonksiyonun parametre listesi kadar açık olmalı.- Testlerde bağımlılıklar için sahte/mock nesneler enjekte et — DI’nin tüm amacı budur: tool’ların
ctx.deps.db.query(...)çağırır, testlerde ise gerçek bir veritabanına ya da gerçek bir API’ye hiç dokunmadan bellek içi bir sahteyle değiştirirsin. - Bağımlılıkları doğru kapsamda tut — istek-başına (belirli bir kullanıcının auth token’ı), oturum-başına (konuşma hafıza deposu) ve process-başına (paylaşılan bir bağlantı havuzu) farklı ömürlerdir; bunları tek bir global torbada birleştirme.
- Kaçınılabiliyorsa, sırları doğrudan
depsiçine düz string olarak koyma — ham API anahtarlarını, loglanabilecek ya da bir checkpoint’e serialize edilebilecek agent state’i üzerinden geçirmek yerine, zaten kimliği doğrulanmış bir client enjekte et.
from dataclasses import dataclass
@dataclass
class AppDeps:
db: DatabaseClient
payments_api: PaymentsClient
current_user_id: str
agent = Agent("openai:gpt-4o", deps_type=AppDeps)
@agent.tool
def get_account_balance(ctx: RunContext[AppDeps]) -> float:
"""Mevcut kullanıcının hesap bakiyesini döndürür."""
return ctx.deps.db.get_balance(ctx.deps.current_user_id)
2.5 Validation (Doğrulama)
Zihinsel model: Doğrulama sadece “JSON parse oluyor mu” değildir — modelin ne döndürdüğünü düşündüğü ile sisteminin neye ihtiyacı olduğu arasındaki sessiz semantik kaymaya karşı birincil savunmandır.
Best Practices
- Her sınırda doğrula: tool girdileri, tool çıktıları ve nihai agent çıktısı — sadece nihai cevap değil.
- Doğruluk önemli olduğunda tip-sadece doğrulayıcılar yerine semantik doğrulayıcıları tercih et. Bir tarihin geçmişte olmadığını ya da bir toplamın kalem toplamlarıyla eşleştiğini kontrol eden bir
@field_validator, tip kontrolünün tek başına asla yakalayamayacağı hataları yakalar. - Yüksek sesle ve spesifik olarak başarısız ol. Bir doğrulama hata mesajı, sadece “geçersiz girdi” demek yerine modele (ya da geliştiriciye) tam olarak hangi alanın ve nedenini söylemeli.
- “Yeniden-sorma ile kurtarılabilir” doğrulama hatalarını “sert” olanlardan ayır. Eksik opsiyonel bir alan yeniden-sormayı tetikleyebilir; güvenlikle ilgili bir alanın doğrulaması başarısız olursa (örn. bir ödeme için negatif bir tutar) sessizce bir edge case’e retry yapmak yerine sert şekilde başarısız olmalı.
- Pydantic modellerini agent’tan bağımsız olarak unit test et. Doğrulayıcıların kendi test suite’i olmalı — bu katmanda LLM davranışını değil, iş kurallarını test ediyorsun.
2.6 Agent Sınırları
Zihinsel model: Bir “agent”, her şeye kadir sihirli bir aktör değildir — tam olarak bir mikroservis gibi, tanımlı bir yetki kapsamına sahip sınırlı bir bileşendir.
Best Practices
- Bir agent’ın neye karar vermesine izin verildiğini ve neyin hardcode/deterministik olması gerektiğini açıkça tanımla. Örn. bir agent hangi iade katmanının uygulanacağına karar verebilir, ama gerçek iade tutarı hesaplaması LLM’in serbest metinde “hesapladığı” bir şey değil, deterministik kod olmalı.
- Bir agent’ın tool’larının belirtilen yetkisini asla aşmasına izin verme — bir agent “müşteri desteği triyajı"na kapsanmışsa, teknik olarak çalışsa bile ona bir production veritabanı kaydını silebilecek bir tool verme; tool’ları agent’ın gerçek işiyle eşleşecek şekilde kapsamlandır.
- “Agent karar verir” ile “agent çalıştırır” arasında sert bir çizgi çiz. Geri döndürülemez/yüksek riskli çalıştırmayı (ödemeler, silmeler, gerçek e-posta gönderimi) tek başına LLM yargısının arkasında değil, açık bir onay veya deterministik koruma çitinin arkasında tut.
- Her agent’ın sınırını bir API sözleşmesi gibi belgele: ne yapacağı, neyi reddedeceği, neyi devredeceği ve kime.
- Bir geniş agent inşa etmek yerine dar agent’ları kompoze et — bu, mikroservisler vs. monolit ile aynı prensiptir ve aynı nedenlerle geçerlidir (test edilebilirlik, değiştirilebilirlik, hasar alanının sınırlandırılması).
2.7 Context Management
Zihinsel model: Context window, kıt ve pahalı bir kaynaktır — içine ne koyduğuna, sonsuz bir taslak defteri gibi değil, bir fonksiyonun argümanlarına koyduğun şeye uyguladığın aynı disiplinle davran.
Best Practices
- Konuşma geçmişini sınırsız büyümesine izin vermek yerine özetle/sıkıştır — bir politika uygula (örn. son N turu kelimesi kelimesine tut + daha eskisinin çalışan bir özetini tut) körlemesine kesmek ya da her şeyi sonsuza kadar göndermek yerine.
- Büyükse, tüm tool çıktılarını kelimesi kelimesine context’e koyma — ilgili kısımları çıkar/özetle ve sadece talep üzerine genişlet (örn. model gerçekten tam belgeye ihtiyacı olduğuna karar verirse “get_full_document” tool’unu çağırabilir).
- Sistem/talimat context’ini dinamik tur-başına context’ten net şekilde ayır, böylece prompt yapın kararlı ve cache-dostu kalır (prompt caching maliyet tasarrufu için önemli).
- Token bütçelerini açıkça takip et — mevcut tura gelmeden önce sistem prompt’un + tool’ların + son geçmişin yaklaşık kaç token tükettiğini bil ve pay bırak.
- Hangi bağımlılık/state verisinin gerçekten prompt içinde olması gerektiği ile sadece
depsüzerinden tool’lara erişilebilir olması gerektiği konusunda bilinçli ol.depsiçindeki her şey context’e serialize edilmemeli — çoğu sadece tool koduna erişilebilir olmalı.
2.8 Testing
Zihinsel model: İki farklı şeyi test ediyorsun — deterministik kod (tool’lar, doğrulayıcılar, routing) ve non-deterministik davranış (LLM’in seçimleri) — ve bunlar farklı test stratejileri gerektirir.
Best Practices
- Tool’ları ve doğrulayıcıları LLM’den tamamen izole şekilde unit test et — bunlar düz Python fonksiyonları/Pydantic modelleridir; bunları tam olarak diğer kod gibi, %100 determinizm bekleyerek test et.
- Hızlı, deterministik CI için kayıtlı/replay edilen LLM yanıtlarıyla (cassette’ler) agent davranışını test et — her CI çalıştırmasında gerçek bir LLM API’sine gitme; bu yavaş, pahalı ve tutarsızdır (flaky).
- Hızlı CI suite’inden ayrı olarak, gerçek, canlı-model “eval” testlerinden küçük bir suite tut — bunlar gerçek modelin hâlâ beklenildiği gibi davrandığını kontrol eder (prompt regresyonları, model versiyon değişiklikleri), daha az sıklıkla çalışır (gece/talep üzerine) ve bir miktar non-determinizme tolerans gösterir (tam string’ler yerine özellikler/şemalar üzerinde assert et).
- DI sınırını doğrudan test et — sahte
depsenjekte et ve tool’larının tamamen kontrol edilen sahte bir ortama karşı doğru davrandığını assert et. - Doğrulama başarısızlığı yollarını açıkça test et — bozuk model çıktısını Pydantic şemandan geçiren ve yeniden-sorma/retry/sert-başarısızlık davranışının tasarlandığı gibi tetiklendiğini doğrulayan testler yaz.
- LLM tarafından üretilen serbest metin için tam kelimeler yerine yapı üzerinde assert et — şema uyumluluğunu, alan kısıtlamalarını ve anahtar gerçekleri kontrol et, string eşitliğini değil.
3. LlamaIndex — Bir Disiplin Olarak RAG
3.1 RAG’ı Bir Kütüphane Değil, Bir Pipeline Olarak Öğrenmek Neden Önemli
LlamaIndex’e kalıcı bir bağımlılık olarak commit olmak zorunda değilsin. Gerçekten ihtiyacın olan şey, aşağıdaki pipeline aşamalarını derinlemesine anlamak — bunu yaptığında, LlamaIndex, LangChain, Haystack veya elle yazılmış bir RAG stack’i arasında geçiş yapmak önemsiz hale gelir, çünkü her yerde aynı yedi aşama vardır.
Document ingestion (Doküman alımı)
↓
Chunking (Parçalama)
↓
Embedding
↓
Index
↓
Retrieval (Getirme)
↓
Reranking (Yeniden sıralama)
↓
Context construction (Bağlam oluşturma)
↓
LLM
3.2 Document Ingestion (Doküman Alımı)
Zihinsel model: Çöp girer, çöp getirilir. Bu aşama olduğundan az değer görür ve gerçek dünya RAG kalitesinin çoğu burada kazanılır ya da kaybedilir.
Best Practices
- Parse etme sırasında yapıyı koru (başlıklar, tablolar, listeler, sayfa numaraları) — bir PDF’i tek bir farklılaştırılmamış metin blob’una düzleştirme; yapı, retrieval sinyalidir.
- Tabloları ve görselleri ayrı ele al — naif metin çıkarma, tablo verisini bozar; taranmış/karmaşık dokümanlar için özel bir tablo parser’ı ya da vision modeli düşün.
- Alım anında zengin metadata ekle: kaynak URI, bölüm/sayfa numarası, yazar, tarih, doküman versiyonu, erişim kontrol etiketleri. Bu metadata daha sonra filtreleme/yeniden sıralama sinyalin haline gelir — alım sırasında yakalamaktan çok daha zor sonradan doldurulur.
- Neredeyse özdeş dokümanları indekslemeden önce tekilleştir (deduplicate) — tekrarlanan içerik retrieval sonuçlarını kirletir ve embedding maliyetini boşa harcar.
- Alım pipeline’ının kendisini versiyonla. Parser’ını ya da chunking mantığını değiştirirsen, genellikle her şeyi yeniden almalı ve yeniden embed etmelisin — buna bir hot patch değil, bir migration gibi davran.
- Alım başarısızlıklarını açıkça ele al (bozuk dosyalar, desteklenmeyen formatlar, OCR başarısızlıkları) — başarısız dokümanları sessizce düşürmek yerine logla ve karantinaya al.
3.3 Chunking (Parçalama)
Zihinsel model: Bir chunk, retrieval’ın atomik birimidir — boyutu ve sınırları, LLM’in sonunda göreceği bağlamı doğrudan belirler. Bu, muhtemelen RAG’daki en yüksek etkiye sahip tek ayar düğmesidir.
Best Practices
- Sabit karakter sayıları yerine semantik sınırlar boyunca parçala — her N karakterde körlemesine dilimlemek yerine başlıklar, paragraflar ya da cümle grupları üzerinde böl; bu genellikle bir cümleyi ya da tablo satırını ikiye böler.
- Chunk’lar arasında örtüşme (overlap) kullan (örn. chunk boyutunun %10-20’si) böylece bir sınıra yakın bilgi her iki chunk’ın bağlamından da kaybolmasın.
- Chunk boyutunu evrensel bir varsayılana değil, retrieval + generation görevine göre ayarla — daha küçük chunk’lar (100-300 token) fact lookup için retrieval hassasiyetini artırır; daha büyük chunk’lar (500-1000+ token) sentez ağırlıklı cevaplar için daha fazla bağlam korur. İkisini de test et.
- Hiyerarşik/ebeveyn-çocuk (parent-child) chunking düşün: hassasiyet için küçük chunk’lar üzerinde embed et ve ara, ama LLM’in bağlamı için daha büyük ebeveyn chunk’ı/bölümü getir — her ikisinin de en iyisi.
- Chunk’tan kaynağa izlenebilirliği koru — her chunk’ın atıflar ve kötü retrieval’ları debug etmek için kaynak doküman/bölümüne geri dönen kararlı bir ID’ye ihtiyacı vardır.
- Embedding modelin değiştiğinde yeniden parçala — chunk boyutu tatlı noktası (sweet spot), bir dereceye kadar modele bağımlıdır (context uzunluğu, modelin metni temsil etmek için nasıl eğitildiği).
3.4 Embedding
Zihinsel model: Bir embedding modeli, sabit boyutlu bir vektöre kayıplı bir sıkıştırma fonksiyonudur — kalitesi tüm retrieval tavanını sınırlar; hiçbir downstream yeniden sıralama, kötü bir embedding’i tam olarak telafi edemez.
Best Practices
- Embedding modelini domain’ine eşleştir. Genel amaçlı embedding modelleri, oldukça özelleşmiş domain’lerde (hukuk, tıp, kod) düşük performans gösterir — retrieval kalitesi önemli olduğunda domain’e özgü ya da fine-tune edilmiş embedding modellerini değerlendir.
- Her şeyi yeniden embed etmeden bir index içinde asla embedding modellerini karıştırma — farklı modellerden gelen vektörler karşılaştırılabilir değildir ve onları sessizce karıştırmak benzerlik arama sonuçlarını bozar.
- Embedding’leri cache’le — her pipeline çalıştırmasında değişmemiş dokümanları yeniden embed etmek boşa harcanan maliyettir; içeriği hash’le ve değişmemiş chunk’ları atla.
- Embedding çağrılarını, tek seferde bir chunk yerine throughput ve maliyet verimliliği için toplu (batch) yap.
- Embedding modeli + versiyonunu index metadata’sı olarak takip et böylece tam bir yeniden embed’in tam olarak ne zaman gerekli olduğunu bilirsin (örn. yeni bir model versiyonuna yükselttikten sonra).
- Embedding sağlayıcın destekliyorsa asimetrik embedding stratejileri düşün (sorgular için farklı, dokümanlar için farklı encoding) — bazı modeller özellikle bunun için eğitilmiştir ve ölçülebilir şekilde retrieval’ı iyileştirir.
3.5 Index
Zihinsel model: Index, retrieval veri yapındır — buradaki trade-off alanı hız vs. recall vs. maliyet vs. güncelleme esnekliğidir ve farklı index tipleri çok farklı trade-off’lar yapar.
Best Practices
- Index tipini bilinçli seç: küçük corpus’lar için flat/brute-force (kesin, en basit, ölçekte yavaş), ölçekte yaklaşık en yakın komşu için HNSW/graph tabanlı (hızlı, ayarlanabilir recall), kabul edilebilir recall trade-off’ları olan çok büyük corpus’lar için IVF.
- Sadece tam yeniden inşa değil, artımlı (incremental) güncellemeler için tasarla — bir production bilgi tabanı sürekli değişir; corpus’un gerçekten statik olmadıkça, index stratejinin “tek doküman ekle/güncelle/sil” yolu olmalı, sadece “her gece her şeyi yeniden inşa et” değil.
- Yoğun (embedding) ve seyrek (anahtar kelime/BM25) index’leri birleştir — hybrid search — sadece yoğun retrieval genellikle tam eşleşme ihtiyaçlarını (ürün SKU’ları, hata kodları, özel isimler) kaçırır; hybrid search pratikte her ikisinden de tutarlı şekilde daha iyi performans gösterir.
- Filtreleme için metadata’yı vektörlerle birlikte sakla (tarih aralıkları, doküman tipi, erişim kontrolü) — vektör arama öncesinde/sırasında metadata ile ön-filtreleme yapmak, tüm bunları embedding’in kendisine kodlamaya çalışmaktan genellikle çok daha etkilidir.
- Index’inin erişim kontrol modelini önceden planla — retrieval zamanı izin filtreleme (bir kullanıcının asla erişemediği dokümanlardan chunk almaması için) bir güvenlik gerekliliğidir, hoş bir ekstra değil, ve sonradan eklemek çok daha zordur.
3.6 Retrieval (Getirme)
Zihinsel model: Retrieval bir arama-alaka düzeyi (search-relevance) problemidir ve buraya bir arama motoru mühendisinin uygulayacağı aynı titizliği uygulamalısın — bu “sadece .similarity_search() çağır ve devam et” değildir.
Best Practices
top_k‘yı bilinçli ayarla — çok az ilgili bağlamı kaçırma riski taşır, çok fazla context window’u gürültüyle sulandırır (ve maliyet/gecikmeyi artırır); bu, evrensel bir sabit değil, kullanım durumuna özgü bir ayar parametresidir.- Çoğu gerçek dünya corpus’u için varsayılan olarak saf vektör arama yerine hybrid retrieval (yoğun + seyrek) kullan.
- Sadece embedding’in bu kısıtlamaları kodlamasına güvenmek yerine retrieval zamanında metadata filtreleri ekle (tarih, kaynak, erişim hakları).
- Sorgu dönüşüm tekniklerini düşün: sorgu genişletme, HyDE (hipotetik doküman embedding’leri), ya da çoklu-sorgu retrieval (kullanıcının sorusunun birkaç yeniden ifadesini üret, her biri için al, sonuçları birleştir) — bunlar belirsiz ya da yetersiz belirtilmiş sorgular için recall’u ciddi şekilde iyileştirir.
- Retrieval’ı generation’dan bağımsız olarak değerlendir — retrieval hassasiyetini/recall’unu (doğru chunk top-k’de görünüyor mu?) nihai LLM cevabının “iyi görünüp görünmediğinden” ayrı, kendi metriği olarak ölç, çünkü iyi bir LLM kötü retrieval’ı maskeleyebilir ve kötü bir LLM harika retrieval’ı boşa harcayabilir.
- Offline değerlendirme ve debug için her retrieval’ı logla (sorgu, alınan chunk ID’leri, skorlar) — ölçmediğin şeyi iyileştiremezsin.
3.7 Reranking (Yeniden Sıralama)
Zihinsel model: İlk retrieval ölçekte recall için optimize eder (hızlı, yaklaşık, tüm corpus üzerinde); reranking küçük bir aday kümesinde hassasiyet için optimize eder (yavaş, kesin, sadece top-k üzerinde) — her birini iyi olduğu şey için kullan.
Best Practices
- Her zaman ihtiyacından fazlasını al, sonra yeniden sırala — örn. hızlı bir yöntemle top-50’yi al, daha pahalı bir cross-encoder modeliyle top-5’e yeniden sırala; hızlı yöntemi doğrudan hassas yapmaya çalışmak yerine.
- Yeniden sıralama aşaması için bi-encoder benzerliği değil, cross-encoder yeniden sıralayıcılar kullan — cross-encoder’lar sorgu ve dokümana birlikte dikkat eder ve alaka düzeyi yargısında ölçülebilir şekilde daha doğrudur, bedeli tüm corpus üzerinde çalıştırılamayacak kadar yavaş olmalarıdır (bu yüzden: sadece küçük aday kümesi üzerinde).
- Sadece semantik alaka düzeyi değil, birden çok sinyal üzerinde yeniden sıralamayı düşün — güncellik, kaynak otoritesi/güven ve metadata eşleşmesi, kullanım durumuna bağlı olarak nihai bir sıralama skoruna karıştırılabilir.
- Yeniden sıralamanın SENİN corpus’un/sorgu dağılımın için gerçekten yardımcı olup olmadığını ölç — evrensel olarak büyük bir kazanç değildir; eklenen gecikme/maliyete değeceğini varsaymadan önce benchmark yap.
3.8 Context Construction (Bağlam Oluşturma)
Zihinsel model: Bu, alınan içeriğe uygulanan prompt mühendisliğidir — alınan chunk’ları LLM’in bağlamına nasıl bir araya getirdiğin, retrieval kalitesinin kendisinden bağımsız olarak cevap kalitesini ciddi şekilde etkiler.
Best Practices
- Sıra önemlidir — birçok model uzun bağlamların başına ve sonuna daha güvenilir şekilde dikkat eder (“ortada kaybolma” etkisi); en ilgili chunk’ını/chunk’larını ortada gömülü değil, ilk ve/veya son sıraya koy.
- Her chunk ile birlikte satır içi kaynak atfını her zaman dahil et (örn.
[Kaynak: policy_doc.pdf, s.12]) böylece LLM kaynakları alıntılayabilir ve sen halüsinasyonları retrieval’daki belirli bir eksikliğe ya da hataya geri izleyebilirsin. - Bağlamı oluşturmadan önce neredeyse özdeş alınan chunk’ları tekilleştir — gereksiz chunk’lar bağlam bütçesini boşa harcar ve bir kaynakta bir hata varsa ve tekrarlanıyorsa yanlış bir cevabı pekiştirebilir.
- Modele yetersiz bağlamı nasıl ele alacağını açıkça talimatlandır (“Bağlam cevabı içermiyorsa, bunu söyle — tahmin etme”) — bu, RAG sistemlerinde halüsinasyona karşı en yüksek etkiye sahip tek müdahalelerden biridir.
- Alınan bağlam bölümünü kullanıcının sorusundan ve sistem talimatlarından yapısal olarak ayrı tut (açık ayırıcılar/başlıklar) böylece model alınan içeriği talimatlarla karıştırmaz (bu, prompt injection direncinde de önemlidir, çünkü alınan dokümanlar, güven perspektifinden, güvenilmeyen kullanıcı-bitişik girdidir).
3.9 LLM (Generation)
Zihinsel model: İçerik LLM’e ulaştığında, kalite tavanının çoğu zaten ingestion → chunking → embedding → retrieval → reranking → context construction tarafından belirlenmiştir. Generation adımı, kalan hataların genellikle yaratıldığı değil, açığa çıktığı yerdir.
Best Practices
- Modeli her iddiayı sağlanan bağlamda temellendirmesi ve bunun ötesine ekstrapolasyon yaptığında açıkça belirtmesi için talimatlandır.
- Alıntıları çıktının yapılandırılmış bir parçası olarak iste (örn. bir
sources: list[str]alanı), sadece düz yazı alıntıları değil — bu hem downstream daha kullanışlı hem de programatik olarak doğrulanması daha kolaydır. - Sadece generation kalitesini değil, tüm pipeline’ı uçtan uca değerlendir — faithfulness (cevap alınan bağlam tarafından destekleniyor mu?) ve cevap alaka düzeyi gibi metrikleri, retrieval metriklerinden ayrı kullan, böylece kötü bir cevabın aslında nereden geldiğini lokalize edebilirsin.
- Kalite kötü olduğunda önce daha önceki pipeline aşamalarında yinele — yaygın bir hata, kötü retrieval’ı telafi etmek için generation aşamasında daha sert prompt mühendisliği yapmaktır; retrieval’ı düzelt, üzerini prompt’la kapatma.
4. Framework’ler Arası Mimari Pattern’ler
Bu üç öğrenme yolu, bir production sisteminde doğal olarak birleşir:
- LangGraph dış orkestrasyon katmanı olarak: genel workflow’u, state’i, checkpoint’leri, HITL interrupt’larını ve çoklu agent routing’ini yönetir.
- PydanticAI her bir LangGraph node’u içindeki agent/tool katmanı olarak: “muhakeme” yapması gereken her node, kendi bağımlılıkları için tip’li tool’lar, tip’li çıktı ve DI ile bir PydanticAI agent’ını sarar.
- LlamaIndex tarzı bir RAG pipeline’ı, herhangi bir agent’ın çağırabileceği bir tool/servis olarak: retrieval’ı, bir PydanticAI agent’ının çağırdığı tip’li bir tool (
search_knowledge_base(query: str) -> list[RetrievedChunk]) olarak açığa çıkar, workflow’un retrieval sonuçlarına göre dallanması gerektiğinde LangGraph tarafından orkestre edilir (örn. “yetersiz bağlam bulundu → insana eskale et”).
LangGraph (workflow, state, dayanıklılık, HITL)
└── Node: "answer_question"
└── PydanticAI Agent (tip'li tool'lar, structured output)
└── Tool: search_knowledge_base()
└── RAG pipeline (ingest → chunk → embed → index →
retrieve → rerank → context → LLM)
Genel Kapsamlı Best Practices
- Önce gözlemlenebilirlik (observability). Bir production olayını debug etmen gerekmeden ÖNCE, her katmanı (graph node giriş/çıkışı, tool çağrıları, retrieval sorguları, LLM çağrıları) yapılandırılmış izleme (örn. OpenTelemetry) ile enstrümante et, sonra değil.
- Bileşen başına maliyet takibi. LLM çağrıları, embedding çağrıları ve yeniden sıralama çağrıları arasında istek başına maliyet dökümünü bil — bu, bilinçli trade-off’lar yapmanı sağlayan şeydir (örn. “bu kullanım durumu için yeniden sıralama eklenen maliyete değer mi?”).
- Çıktı determinizmini etkileyen her şeyi versiyonla: prompt’lar, model versiyonları, embedding model versiyonları, chunking parametreleri. Prompt/config değişikliklerine kod değişiklikleriyle aynı titizlikle davran (PR’lar, review, changelog’lar) — bunlar production davranışını kod kadar etkiler.
- İlk günden değerlendirme için tasarla. Erkenden küçük, seçilmiş bir değerlendirme veri seti (sorgular + iyi cevapların beklenen özellikleri) oluştur ve bunu prompt’larda, modellerde ya da pipeline aşamalarında her anlamlı değişiklikte yeniden çalıştır — bu, temelde non-deterministik bir sistem için senin regresyon test suite’in.
5. Son Kontrol Listesi
LangGraph
- State şeması tip’li ve versiyonlu
- Reducer’lar her alan için bilinçli seçilmiş
- Checkpointer production’da dayanıklı bir backend
- Retry’lar hata tiplerini sınıflandırıyor ve backoff ile sınırlı
- Dallanma açık, saf routing fonksiyonları kullanıyor
- HITL interrupt’ları async, tanımlı bir resume sözleşmesiyle
- Çoklu agent sözleşmeleri mikroservis API’leri gibi belgelenmiş
- Failure recovery açık fallback yolları ve kısmi başarı ele alımına sahip
PydanticAI
- Tool’lar güçlü tip’li, kısıtlı parametrelere ve açık docstring’lere sahip
- Output şemaları açık, mümkün olduğunda düz, alan açıklamalarıyla
- Bağımlılıklar tip’li bir
depsobjesi üzerinden enjekte ediliyor, global’ler değil - Doğrulama her sınırda gerçekleşiyor, kurtarılabilir vs. sert başarısızlıklar ayırt ediliyor
- Agent yetkisi/kapsamı belgelenmiş ve tool erişilebilirliği ile uygulanıyor
- Context window kullanımı aktif olarak yönetiliyor (özetleme, seçici dahil etme)
- Testler temizce deterministik unit testler ve periyodik canlı-model eval’lere ayrılmış
LlamaIndex / RAG
- Ingestion yapıyı koruyor ve zengin metadata yakalıyor
- Chunking, bilinçli örtüşme ve boyutla semantik sınırları takip ediyor
- Embedding modeli domain’e uyuyor ve versiyonlanmış
- Index hybrid (yoğun + seyrek) arama ve artımlı güncellemeleri destekliyor
- Retrieval, generation’dan bağımsız olarak ayarlanmış ve değerlendirilmiş
- Reranking benchmark edilmiş, yardımcı olduğu varsayılmamış
- Bağlam oluşturma kaynak atfı ve “bilmiyorum” talimatları içeriyor
- Tam pipeline, faithfulness/alaka düzeyi metrikleriyle uçtan uca değerlendiriliyor