LangChain Production'da: Çalışan Patternler ve İşe Yaramayan Anti-Patternler
LangChain'i production'da çalıştırırken öğrenilen dersler: hataya yol açan anti-patternler, işe yarayan patternler, kod örnekleri ve maliyet optimizasyonu.
Prototip ile Production Arasındaki Boşluk#
LangChain uygulamalarını prototipten production’a taşıdığında dokümantasyon örnekleri ile gerçek dünya gereksinimleri arasındaki fark ortaya çıkıyor. Development’ta mükemmel çalışan şeyler, production yükü altında pahalı, yavaş veya güvenilmez hale gelebiliyor.
Prototip iş yükleri yalnızca ölçekte görünen hata biçimlerini gizler: belirsiz girdiler üzerinde döngüye giren agent’lar, her konuşma turuyla artan token harcaması ve sana kullanıcı şikayeti olarak ulaşan sessiz hatalar. Framework’ün soyutlamaları prototyping’i hızlandırır ama production yükü altında ihtiyaç duyduğun maliyet, gecikme ve güvenilirlik kollarını gizler.
Ayakta kalan varsayılan şu: her kaynağı explicit sınırla (memory token’ı, agent iteration’ı, execution süresi, output uzunluğu), ilk chain çalışmadan önce tracing’i devreye al ve abstraction’ın hiçbir şey kazandırmadığı yerde provider SDK’sına in.
Framework Trade-off Dengesi#
LangChain, erken dönem LLM entegrasyon karmaşıklığını çözdü: Promptlar, chain’ler, agentlar ve memory yönetimi için standart abstraksiyonlar sundu. Bu prototyping’i önemli ölçüde hızlandırdı; direkt API çağrılarıyla haftalar sürecek işler günlerde yapılabilir hale geldi.
Ancak bu abstraksiyonlar kendi zorluklarını getiriyor:
Velocity-control trade-off’u: Hızlı prototyping, şeffaflık pahasına geliyor. Production’da bir şeyler ters gittiğinde, birden fazla abstraction katmanında debug yapmak direkt API çağrısını debug etmekten çok daha zor.
Gizli davranışlar: Framework içsel kararlar alıyor ve bunlar her zaman görünür değil; memory trimming stratejileri, otomatik retry’lar, callback yürütme sırası. İşler yolunda giderken sorun yok, ama ters gittiğinde nedenini anlamak kaynak koduna dalmayı gerektiriyor.
Performance overhead’i: Her abstraction katmanı latency ekliyor. Memory wrapper’ları, callback sistemleri ve otomatik işlemler her istekte çalışıyor. LLM çağrısının baskın olduğunu varsaymadan önce profille; kısa promptlarda framework’ün kendi işi LLM çağrısıyla yarışabiliyor.
Framework inflection noktası, ekibinin framework davranışını debug etmeye feature geliştirmekten daha fazla zaman harcadığı an. Bazı ekipler bunu hızlı yaşıyor, bazıları hiç yaşamıyor.
7 Ölümcül Anti-Pattern#
1. Sınırsız Memory Birikimi#
Default ConversationBufferMemory sınırsız konuşma geçmişi saklıyor:
from langchain.memory import ConversationBufferMemory
# Anti-pattern: Sınırsız geçmiş biriktiriyor
# Not: ConversationBufferMemory deprecated - yeni projeler için
# LangGraph persistence veya RunnableWithMessageHistory kullan
memory = ConversationBufferMemory()
# 50 mesaj sonrası: Devasa context, yavaş response'lar, yüksek maliyetler
Her request tüm geçmişi yeniden gönderiyor; yani hem maliyet hem latency konuşma uzunluğuyla birlikte büyüyor. Sonunda context window taşıyor ve requestler doğrudan hata vermeye başlıyor. Token kullanım trendlerini zaman içinde izle, konuşmalar ilerledikçe artan response sürelerini işaretle.
Çözüm, explicit limitlerle ConversationSummaryBufferMemory kullanmak (veya LangGraph persistence’a migrate etmek):
from langchain.memory import ConversationSummaryBufferMemory
# Not: ConversationSummaryBufferMemory deprecated
# Yeni projeler için LangGraph persistence veya RunnableWithMessageHistory kullan
memory = ConversationSummaryBufferMemory(
llm=llm,
max_token_limit=500, # Son context'i kompakt tut
return_messages=True
)
# Eski turlar özete iniyor, böylece request başına token sınırlı kalıyor
2. Korkuluksuz Agent#
Execution kontrolleri olmayan agentlar yaratmak:
from langchain.agents import AgentExecutor
# Anti-pattern: Execution üzerinde hiç limit yok
executor = AgentExecutor(agent=agent, tools=tools)
# Burada agent'ı search ile summarize arasında sonsuza dek gidip gelmekten alıkoyan hiçbir şey yok
Durdurma koşulunu hiç sağlamayan bir ReAct döngüsü, dışarıdan biri kesene kadar tool çağırmaya devam ediyor. Her iterasyon ücretli bir LLM çağrısı ve kullanıcı bu süre boyunca spinner’a bakıyor. Maliyet alarmları ve execution süresi izleme, production’dan önce kurulmuş olmalı.
Konfigürasyonda explicit kontroller bunu durduruyor:
from langchain.agents import AgentExecutor
from langchain.callbacks import get_openai_callback
executor = AgentExecutor(
agent=agent,
tools=tools,
max_iterations=5, # Sonsuz döngüleri engelle
max_execution_time=30, # 30 saniye sonra timeout
early_stopping_method="generate"
)
# Not: get_openai_callback yeni agent tipleriyle maliyetleri yakalamayabilir
# Kapsamlı maliyet takibi için LangSmith kullanmayı değerlendir
with get_openai_callback() as cb:
result = executor.run(query)
print(f"Tokens: {cb.total_tokens}, Maliyet: ${cb.total_cost}")
3. Basit Tasklar İçin Aşırı Abstraction#
Basit işlemler için full LangChain abstraksiyonları kullanmak:
// Anti-pattern: Basit completion için 5 katman abstraction
import { ChatOpenAI } from "langchain/chat_models/openai";
import { ChatPromptTemplate } from "langchain/prompts";
import { StringOutputParser } from "langchain/schema/output_parser";
const chatModel = new ChatOpenAI();
const outputParser = new StringOutputParser();
const prompt = ChatPromptTemplate.fromMessages([
["system", "Sen yardımsever bir çevirmensin."],
["user", "{text} metnini {language} diline çevir"]
]);
const chain = prompt.pipe(chatModel).pipe(outputParser);
// Direkt API: Aynı sonuç, framework overhead'i yok
import OpenAI from "openai";
const openai = new OpenAI();
const response = await openai.chat.completions.create({
model: "gpt-4",
messages: [
{ role: "system", content: "Sen yardımsever bir çevirmensin." },
{ role: "user", content: `${text} metnini ${language} diline çevir` }
]
});
Gereksiz karmaşıklık, zorlaşan debugging ve abstraksiyonlardan fayda görmeyen tasklar için ekip cognitive load’u birikir. Code review’da katmanları say: basit bir completion için dört veya daha fazla modül import ediyorsan, bu direkt API’ye geçme sinyalidir.
4. Gizli Latency Overhead’i#
Framework komponentleri önemli latency ekleyebiliyor:
from langchain.memory import ConversationBufferWindowMemory
# Anti-pattern: her çağrıda çalışan, hiç ölçülmemiş wrapper işi
memory = ConversationBufferWindowMemory(k=5)
# Profillemeye değer: serialization ve callback dispatch her invocation'da oluyor
Kötü kullanıcı deneyimi ve yüksek request hacimlerine scale etme zorluğu peşinden geliyor. Framework komponentleriyle ve olmadan profille, end-to-end latency’yi direkt API çağrı süresiyle karşılaştırarak gerçek farkı gör.
Performance-critical path’ler için custom lightweight bir alternatif bu overhead’i kaldırıyor:
# Custom trimmed memory - son N mesajı verimli tutar
class LightweightMemory:
def __init__(self, max_messages=10):
self.messages = []
self.max_messages = max_messages
def add_message(self, message):
self.messages.append(message)
if len(self.messages) > self.max_messages:
self.messages = self.messages[-self.max_messages:]
def get_context(self):
return self.messages
# Hot path'te callback dispatch de yok, serialization gidiş-dönüşü de
5. Production’a Development Default’larıyla Çıkmak#
Development default’larıyla production deployment’ı:
# Anti-pattern: Production'da development default'ları
from langchain.chat_models import ChatOpenAI
llm = ChatOpenAI()
# Caching yok, output limitleri yok, maliyet kontrolleri yok
Yüksek operasyonel maliyetler, yavaş response’lar ve disk’i dolduran verbose logging, production launch’tan önce maliyet ve latency metrikleri için baseline oluşturmadığın sürece görünmez kalır.
Explicit production konfigürasyonu üçüne de bir taban çizer:
from langchain.chat_models import ChatOpenAI
from langchain.cache import RedisCache
from langchain.globals import set_llm_cache
import redis
# Production-ready konfigürasyon
set_llm_cache(RedisCache(
redis_=redis.Redis(host="localhost", port=6379)
))
llm = ChatOpenAI(
model="gpt-4",
temperature=0.7,
max_tokens=512, # Output uzunluğunu sınırla
request_timeout=30, # API çağrıları için timeout
max_retries=2 # Kontrollü retry davranışı
)
6. Black-Box Agent Davranışı#
Observability olmadan agent deployment’ı:
# Anti-pattern: Agent kararlarına görünürlük yok
executor = AgentExecutor(agent=agent, tools=tools)
result = executor.run(query)
# Bu sessizce başarısız olduğunda, nedenini bilemiyorsun
Sessiz hatalar, imkansız debugging ve sorunu ancak kullanıcı şikayetiyle keşfetmek birbirini izliyor. Trace olmadan tespit edilecek bir şey yok; sorun zaten tam olarak bu.
İlk günden LangSmith tracing bunu değiştiriyor:
import os
from langchain.callbacks.tracers import LangChainTracer
# Environment'ta tracing'i aktifleştir
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "api-anahtarin"
# Tüm chain'ler, agentlar, tool'lar otomatik trace ediliyor
# Track: latency, maliyetler, token'lar, hatalar, karar path'leri
executor = AgentExecutor(agent=agent, tools=tools)
result = executor.run(query)
# Tüm execution detayları artık LangSmith dashboard'unda görünür
7. Data Ingestion’ı Hafife Almak#
RAG pipeline karmaşıklığını hafife almak:
# Anti-pattern: Doküman yüklemenin "öylece çalıştığını" varsaymak
from langchain.document_loaders import PyPDFLoader
loader = PyPDFLoader("document.pdf")
documents = loader.load()
# RAG projeleri asıl burada tıkanır: parser seçimi, encoding ve chunking
Doküman tiplerine uygun olmayan PDF parser’ı, uluslararası metinlerde encoding sorunları ve retrieval kalitesini düşüren chunking problemleri; doküman işlemede yüksek hata oranı ve kötü retrieval sonucu olarak ortaya çıkıyor.
Birden fazla stratejiyle data loader’ların kapsamlı testi bunu erken yakalıyor:
from langchain.document_loaders import PyPDFLoader, PDFMinerLoader, UnstructuredPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
# Birden fazla parser dene, gerçek dokümanlarla test et
parsers = [
PyPDFLoader,
PDFMinerLoader,
UnstructuredPDFLoader
]
for ParserClass in parsers:
try:
loader = ParserClass("document.pdf")
docs = loader.load()
# Çıktı kalitesini doğrula
if validate_extraction(docs):
break
except Exception as e:
print(f"{ParserClass.__name__} başarısız oldu: {e}")
# Düşünülmüş chunking stratejisi
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200, # Chunk'lar arası context'i koru
length_function=len
)
chunks = splitter.split_documents(docs)
Production-Ready Patternler#
LCEL-First Architecture#
Modern LangChain uygulamaları daha iyi composability için LCEL (LangChain Expression Language) kullanıyor:
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
# LCEL: Built-in streaming ile okunabilir pipe syntax'ı
chain = (
ChatPromptTemplate.from_template("Analiz et: {input}")
| ChatOpenAI(model="gpt-4", streaming=True)
| StrOutputParser()
)
# Streaming, batching, async out of the box destekleniyor
for chunk in chain.stream({"input": query}):
print(chunk, end="", flush=True)
LCEL net composition, built-in async desteği ve legacy chain’lere kıyasla daha kolay debugging sağlıyor; en çok da birden fazla LLM çağrısı, transformation veya conditional logic zincirleyen karmaşık workflow’larda karşılığını veriyor.
Explicit Resource Kontrolleri#
Production konfigürasyonu limitleri explicit yapmalı:
from langchain.agents import AgentExecutor
from langchain.callbacks import get_openai_callback
# Tüm limitler explicit ve dokümante
executor = AgentExecutor(
agent=agent,
tools=tools,
max_iterations=5, # 5 tool çağrısından sonra dur
max_execution_time=30, # 30 saniyede hard timeout
early_stopping_method="generate", # Graceful degradation
verbose=False # Production'da debug logging'i kapat
)
# Her request'te maliyet tracking
with get_openai_callback() as cb:
result = executor.run(query)
# Maliyetler threshold'u aşarsa alert
if cb.total_cost > 0.10:
send_alert(f"Yüksek maliyetli request: ${cb.total_cost}")
Implementation checklist:
- Memory ve output’lar üzerinde token limitleri
- Agent iteration cap’leri ve timeout’lar
- Maliyet bütçeleri ve alertler
- Retry limitleri ve exponential backoff
Multi-Tier Caching Stratejisi#
Caching maliyetleri ve latency’yi dramatik şekilde düşürüyor:
from langchain.cache import InMemoryCache, SQLiteCache, RedisCache
from langchain.globals import set_llm_cache
import redis
# Development: In-memory cache
# set_llm_cache(InMemoryCache())
# Lokal persistence: SQLite
# set_llm_cache(SQLiteCache(database_path=".langchain.db"))
# Production: Distributed Redis cache
set_llm_cache(RedisCache(
redis_=redis.Redis(
host="redis.production.internal",
port=6379,
db=0
)
))
# Cache konfigürasyonu
# TTL: Statik content için 1 yıl, dinamik için 1 gün
# Invalidation: Güncellenen content için manual veya event-driven
Cache hit’i API çağrısını tamamen atlıyor; yani kazanç, trafiğinin ne kadar tekrarlı olduğuna bağlı. Tasarrufu saymadan önce hit oranını ölç.
Observability-First Development#
İlk chain’ini yazmadan önce tracing kur:
import os
from langchain.callbacks.base import BaseCallbackHandler
# LangSmith tracing konfigürasyonu
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "langsmith-api-anahtarin"
os.environ["LANGCHAIN_PROJECT"] = "production-app"
# Business metrikleri için custom callback
class ProductionMetricsCallback(BaseCallbackHandler):
def on_llm_start(self, serialized, prompts, **kwargs):
self.start_time = time.time()
def on_llm_end(self, response, **kwargs):
latency = time.time() - self.start_time
tokens = response.llm_output.get("token_usage", {})
# Monitoring sistemine gönder
metrics.record("llm.latency", latency)
metrics.record("llm.tokens", tokens.get("total_tokens", 0))
metrics.record("llm.cost", calculate_cost(tokens))
# Tüm chain execution'larında kullan
callbacks = [ProductionMetricsCallback()]
result = chain.invoke({"input": query}, config={"callbacks": callbacks})
İzlenmeye değer metrikler:
- Performance: QPS, latency percentile’ları (p50, p95, p99), time-to-first-token
- Maliyet: Toplam token’lar, request başına maliyet, günlük burn rate
- Kalite: Hata oranları, retry sayıları, kullanıcı feedback’i
- Agent davranışı: Tool seçimleri, iteration sayıları, karar path’leri
Smart Model Routing#
Request’leri karmaşıklığa göre uygun modellere yönlendir:
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
# Maliyet/kabiliyet trade-off'larıyla modelleri tanımla
cheap_model = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.7)
premium_model = ChatOpenAI(model="gpt-4", temperature=0.7)
def route_to_model(query: str):
"""Query karmaşıklığına göre route et"""
complexity_score = analyze_complexity(query)
if complexity_score < 0.3:
return cheap_model # GPT-3.5-turbo: $0.0005/1K input, $0.0015/1K output
else:
return premium_model # GPT-4: $0.03/1K input, $0.06/1K output
# Uygun maliyetli seçenek için GPT-4o mini: $0.00015/1K input, $0.0006/1K output
# Chain'de dinamik routing
def create_chain(query: str):
model = route_to_model(query)
prompt = ChatPromptTemplate.from_template("{input}")
return prompt | model
# Örnek karmaşıklık analizi
def analyze_complexity(query: str) -> float:
"""Basit heuristic-based karmaşıklık skorlaması"""
score = 0.0
# Uzunluk-based skorlama
if len(query.split()) > 50:
score += 0.3
# Teknik terim tespiti
technical_terms = ["mimari", "algoritma", "performans", "optimizasyon"]
if any(term in query.lower() for term in technical_terms):
score += 0.4
# Multi-step reasoning göstergeleri
if any(word in query.lower() for word in ["karşılaştır", "analiz et", "neden açıkla"]):
score += 0.3
return min(score, 1.0)
Tasarruf, ucuz modelin soğurabildiği trafik payıyla ölçekleniyor ve her yanlış yönlendirme bu tasarrufu daha kötü bir cevapla satın alıyor. Router’a güvenmeden önce etiketli bir örneklem üzerinde skorla.
Pydantic ile Structured Output’lar#
Type-safe output’lar post-processing bug’larını azaltıyor:
from langchain.output_parsers import PydanticOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field
# Output schema'sını tanımla
class UrunAnalizi(BaseModel):
duygu: str = Field(description="pozitif, negatif veya notr")
anahtar_ozellikler: list[str] = Field(description="bahsedilen özelliklerin listesi")
fiyat_bahsedildi: bool = Field(description="fiyatın tartışılıp tartışılmadığı")
guven_skoru: float = Field(description="0'dan 1'e güven skoru")
# Schema validation'lı parser
parser = PydanticOutputParser(pydantic_object=UrunAnalizi)
# Prompt format talimatlarını içeriyor
prompt = PromptTemplate(
template="Bu ürün yorumunu analiz et:\n{yorum}\n{format_instructions}",
input_variables=["yorum"],
partial_variables={"format_instructions": parser.get_format_instructions()}
)
chain = prompt | ChatOpenAI(model="gpt-4") | parser
# Type-safe output
result: UrunAnalizi = chain.invoke({"yorum": yorum_metni})
print(f"Duygu: {result.duygu}, Güven: {result.guven_skoru}")
Type safety, otomatik validation ve LLM ile downstream kod arasında net contract’lar bunun karşılığı.
Migration Karar Matrisi#
Doğru yaklaşımı seçmek senin spesifik gereksinimlerine bağlı:
LangChain Ne Zaman Kullanılmalı#
- Orchestration gerektiren karmaşık multi-agent sistemler
- Birden fazla retriever ve re-ranking içeren RAG
- Collaboration için standart abstraksiyonlara ihtiyaç duyan ekipler
- Production hardening planlarıyla hızlı prototyping aşaması
- LangSmith observability ekosisteminden ağır faydalanma
Örnek: LinkedIn’in SQL Bot’u production-grade multi-agent koordinasyonu için LangGraph node’larına sarılmış LangChain chain’leri kullanıyor.
LlamaIndex Alternatifi#
- Search ve retrieval’a birincil odak
- Büyük dataset indexleme gereksinimleri
- Verimli semantic similarity search ihtiyacı
- Genel orchestration’dan daha basit, odaklı use case
Framework’ü Tamamen Atlamak#
- Basit chatbot veya completion taskları
- Net, değişmeyen gereksinimler
- Latency’nin önemli olduğu performance-critical uygulamalar
- Full kontrol isteyen küçük ekip
- Minimal external dependency isteniyor
Örnek implementation:
from openai import OpenAI
client = OpenAI()
# Net, explicit, hızlı
response = client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": "Sen yardımsever bir asistansın."},
{"role": "user", "content": prompt}
],
max_tokens=512,
temperature=0.7
)
answer = response.choices[0].message.content
Migrasyon Zamanının İşaretleri#
Migration’ı düşün:
- Ekip framework davranışını debug etmeye feature geliştirmekten daha fazla zaman harcıyor
- Performance profiling kritik yolda framework overhead’ini LLM çağrısının önüne koyuyor
- Gereksinimler LangChain’in pattern’larına uymuyor ve framework’le savaşıyorsun
- Dependency yönetimi maintenance yükü haline geldi
Migration yaklaşımı: En yüksek etkili komponentlerden başlayarak incremental replacement. Çalışanı koru, çalışmayanı değiştir.
LangGraph: Production Evrimi#
LangGraph 2024’te production odaklı bir evrim olarak ortaya çıktı, LangChain agentlarını deploy ederken öğrenilen derslerden tasarlandı:
Temel farklar:
- Gizli davranışlar olmadan low-level, kontrol edilebilir framework
- Gizli prompt’lar veya otomatik cognitive architecture yok
- Karmaşık agentic sistemler için durable execution
- Long-running workflow’larda state yönetimi
Hybrid pattern:
from langgraph.graph import StateGraph, END
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
# State'i tanımla
class AgentState(dict):
messages: list[str]
current_step: str
# LLM etkileşimleri için LangChain kullan
analysis_chain = (
ChatPromptTemplate.from_template("Analiz et: {input}")
| ChatOpenAI(model="gpt-4")
)
# Orchestration için LangGraph node'larına sar
workflow = StateGraph(AgentState)
def analyze_node(state: AgentState):
result = analysis_chain.invoke({"input": state["messages"][-1]})
state["messages"].append(result)
return state
workflow.add_node("analyze", analyze_node)
workflow.add_edge("analyze", END)
workflow.set_entry_point("analyze")
# İkisinin de en iyisi: LangChain composability + LangGraph kontrol
app = workflow.compile()
Ne zaman upgrade et: AgentExecutor’dan LangGraph’e geçiş, multi-agent koordinasyon ihtiyacı, long-running workflow’larda state yönetimi, production güvenilirlik gereksinimleri.
Production’da LangGraph kullanan şirketler: Uber, LinkedIn, Replit, Elastic.
Maliyet Optimizasyon Stratejileri#
Token Yönetimi#
Token kullanımını agresif şekilde takip et ve kontrol et:
from langchain.callbacks import get_openai_callback
# 1. Her şeyi takip et
# Not: get_openai_callback yeni agent implementasyonlarıyla limitasyonları var
# Tüm agent tiplerinde kapsamlı takip için LangSmith kullan
with get_openai_callback() as cb:
result = chain.invoke({"input": query})
print(f"Token'lar: {cb.total_tokens}, Maliyet: ${cb.total_cost:.4f}")
# 2. Context'i son N exchange'e trim et
from langchain.memory import ConversationBufferWindowMemory
# Not: ConversationBufferWindowMemory deprecated
# Yeni projeler için LangGraph persistence veya RunnableWithMessageHistory kullan
memory = ConversationBufferWindowMemory(
k=5, # Sadece son 5 exchange'i tut
return_messages=True
)
# 3. Eski context için smart summarization
from langchain.memory import ConversationSummaryBufferMemory
# Not: ConversationSummaryBufferMemory deprecated
# Production uygulamalar için LangGraph persistence'a migrate et
memory = ConversationSummaryBufferMemory(
llm=llm,
max_token_limit=500,
return_messages=True
)
# 4. Explicit output limitleri
llm = ChatOpenAI(
model="gpt-4",
max_tokens=512 # Kısa response'lar
)
Tasarruf Nereden Geliyor#
Dört kol birbirinin üstüne biniyor ve bunu belirli bir sırayla yapıyor. Routing token başına fiyatı değiştiriyor; en büyük etkiye ve en büyük kalite riskine sahip olduğu için önce onu uygula. Memory trim’i ve output limitleri request başına token sayısını değiştiriyor, bu da router’ın seçtiği fiyatla çarpılıyor. Caching ise requestleri tümüyle ortadan kaldırdığı için diğer üçünün üzerine oturuyor.
Kimseye bir şey vaat etmeden önce kendi sayılarını modelle: yönlendirdiğin modellerin yayınlanmış token fiyatlarını al, ölçtüğün request başına token sayısıyla çarp ve ancak ondan sonra gözlemlediğin cache hit oranını uygula.
Monitoring ve Observability#
Temel Production Metrikleri#
import time
from langchain.callbacks.base import BaseCallbackHandler
class ProductionMetrics(BaseCallbackHandler):
"""Kapsamlı production monitoring"""
def on_chain_start(self, serialized, inputs, **kwargs):
self.chain_start = time.time()
def on_chain_end(self, outputs, **kwargs):
duration = time.time() - self.chain_start
metrics.gauge("chain.duration", duration)
def on_llm_start(self, serialized, prompts, **kwargs):
self.llm_start = time.time()
metrics.increment("llm.requests")
def on_llm_end(self, response, **kwargs):
# Performance metrikleri
latency = time.time() - self.llm_start
metrics.gauge("llm.latency", latency)
# Maliyet metrikleri
usage = response.llm_output.get("token_usage", {})
total_tokens = usage.get("total_tokens", 0)
cost = calculate_cost(usage)
metrics.gauge("llm.tokens", total_tokens)
metrics.gauge("llm.cost", cost)
def on_llm_error(self, error, **kwargs):
metrics.increment("llm.errors")
logger.error(f"LLM hatası: {error}")
def on_tool_start(self, serialized, input_str, **kwargs):
tool_name = serialized.get("name", "unknown")
metrics.increment(f"tool.{tool_name}.calls")
def on_agent_action(self, action, **kwargs):
metrics.increment("agent.actions")
LangSmith Entegrasyonu#
LangSmith kod değişikliği olmadan otomatik tracing sağlıyor:
import os
# Environment konfigürasyonu
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "langsmith-api-anahtarin"
os.environ["LANGCHAIN_PROJECT"] = "production-app"
# Optional: Filtreleme için metadata ekle
from langchain.callbacks.tracers import LangChainTracer
tracer = LangChainTracer(
project_name="production-app",
tags=["prod", "version-2.1"]
)
# Tüm chain execution'ları otomatik trace ediliyor
result = chain.invoke(
{"input": query},
config={"callbacks": [tracer]}
)
LangSmith neyi takip ediyor:
- Her adım için timing’li execution trace’leri
- Request başına token kullanımı ve maliyetler
- Agent karar path’leri ve tool seçimleri
- Hata oranları ve başarısızlık patternleri
- Metadata tag’leriyle A/B test karşılaştırmaları
Migration Patternleri#
LangChain’den Custom Code’a#
Kademeli yaklaşım riski minimize ediyor:
# Hafta 1: Maliyet/latency oranı en kötü bileşeni profilleyerek bul
# Hafta 2: Custom replacement oluştur
class EfficientMemory:
def __init__(self, max_messages=10):
self.messages = []
self.max_messages = max_messages
def add(self, message):
self.messages.append(message)
if len(self.messages) > self.max_messages:
self.messages = self.messages[-self.max_messages:]
def get_context(self):
return "\n".join(self.messages)
# Hafta 3: A/B test implementasyonları
# Grup A: LangChain memory (baseline)
# Grup B: Custom memory (test)
# Hafta 4: p95 latency, request başına token ve cevap kalitesi üzerinden karşılaştır
# Hafta 5+: Kademeli rollout
# 2 haftada 10% → 50% → 100%
Legacy Chain’lerden LCEL’e#
LangChain migration tooling sunuyor:
# Otomatik migration yardımı
langchain migrate --legacy-to-lcel chain.py
Manuel migration örneği:
# Legacy: initialize_agent pattern (deprecated)
from langchain.agents import initialize_agent, AgentType
agent = initialize_agent(
tools=tools,
llm=llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION
)
# Modern: LangGraph prebuilt (önerilen)
from langgraph.prebuilt import create_react_agent
agent = create_react_agent(
model=llm,
tools=tools
)
Daha iyi composability, built-in streaming, daha net debugging ve agent davranışı üzerinde tam kontrol, geçişi savunan gerekçeler.
Pratikte Görülen Hata Kalıpları#
Prototip-Production Tuzağı#
Default’larla çalışan bir prototip development’ta gayet iyi görünür, sonra production yüksek maliyetleri, yavaş yanıtları ve sessiz hataları ortaya çıkarır. İlk günden production için tasarla: resource limitlerini belirle, caching ekle ve observability’yi ilk deployment’tan önce devreye al.
Framework Lock-In#
Hızlı prototyping için LangChain ile başlamak kolay seçimdir. Framework kullanımı sınırlarda tutulmamış ve core business logic framework-agnostik bırakılmamışsa, altı ay sonra derinlemesine kenetlenmiş mimaride migration aylarca iş haline gelir.
Observability’yi Sonraya Bırakmak#
Tracing veya monitoring olmadan launch etmek, production sorunlarının kullanıcı şikayetiyle ortaya çıkması ve ne olduğunu debug edecek hiçbir yolun olmaması demek; bu, LangSmith veya eşdeğeri proje başında devreye alınana kadar sürer.
Korkuluksuz Agent Özerkliği#
Agent’ı kontrolsüz “halleder” diye güvenmek, tool çağrı döngüsünün bütçe ya da kullanıcının sabrı bitene kadar dönmesine izin verir. Max iteration, timeout ve maliyet bütçeleri zorunlu konfigürasyon olmalı, opsiyonel ayar değil.
Bu Varsayılan Nerede Geçerli#
Bu, LangChain deployment’larının çoğunu kapsıyor: RAG servisleri, iç asistanlar ve birkaç tool’lu agent workflow’ları. Bu disiplinin bedeli birkaç düzine satır konfigürasyon ve bir observability bağımlılığı; bir kez ödeniyor.
İki yönde override edilir. Alt sınırda, gereksinimleri sabit olan tek promptluk bir servisin framework’e hiç ihtiyacı yok; provider SDK’sı artı bir cache hem yazılacak hem debug edilecek kodu azaltıyor. Üst sınırda, uzun süreli state tutan multi-agent sistemler LangChain’i aşmadan çok önce AgentExecutor’ı aşıyor; orchestration’ı LangGraph’e taşıyıp LLM çağrıları için LCEL chain’lerini korumanın anı orası.
Kaynaklar#
- LangChain Dokümantasyonu (yeni sekmede açılır) - Chain’ler, agent’lar, bellek ve retrieval soyutlamalarını kapsayan resmi LangChain genel bakışı
- LangSmith Gözlemlenebilirlik (yeni sekmede açılır) - Üretimde LangChain uygulamalarını izleme, takip etme ve değerlendirme rehberi
- LangGraph Genel Bakış (yeni sekmede açılır) - Uzun süreli agent’lar için durable execution, persistence ve state yönetimini sağlayan orchestration framework’ü
- GitHub - langchain-ai/langchain (yeni sekmede açılır) - Kaynak kodu, örnekler ve topluluk entegrasyonlarını içeren ana LangChain deposu
- LangChain Python API Referansı (yeni sekmede açılır) - LangChain Python sınıfları, callback’ler ve LCEL ifadeleri için tam API referansı
- LangChain Models (yeni sekmede açılır) - Model başlatma, timeout, retry, token limitleri ve structured output ayarları
İlgili yazılar
Kurumsal LLM uygulamaları için production-grade prompt engineering rehberi: sistematik tasarım, güvenlik, observability ve maliyet optimizasyonu.
prompt-engineering · llm · ai-tools +6
Kod ajanı kötü çıktı verince refleks daha güçlü model. Sınırlı görevlerde harness skoru en az kademe yükseltmek kadar oynatıyor; hangi kolu çekeceğinizi söyleyen kural.
ai-agents · ai-tools · llm +3
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
Prompt caching, model routing, token budget'ları ve semantic caching: production LLM harcamasını kaliteden ödün vermeden öngörülebilir tutmanın yolları.
aws · cost-optimization · llm +3
PostgreSQL ve MongoDB için sistematik profiling: gecikmeyi ve altyapı maliyetini yaratan query'ler nasıl bulunur, hangi düzeltmeler gerçekten işe yarar.
data-storage-orm · postgresql · performance +4