İçeriğe atla

2025-12-03

LangChain Production'da: Çalışan Patternler ve İşe Yaramayan Anti-Patternler

LangChain uygulamalarını production'a taşırken öğrendiklerim. Başarısızlığa yol açan anti-patternler, başarıyı sağlayan patternler, çalışan kod örnekleri ve maliyet optimizasyon stratejileri.

Production Gap’i

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 dakikalarca döngüye giren agent’lar, aydan aya %30-40 büyüyen token harcaması ve yalnızca kullanıcı şikayetleriyle keşfedilen 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.

Bu yazıda, gerçek production deployment’larından ve onların sağladığı derslerden edindiğim pratik patternleri paylaşacağım. Maliyet, latency ve güvenilirlik odaklı çözümler sunuyorum. Her anti-pattern için hem tespit yöntemleri hem de uygulanabilir çözümler var—böylece mevcut sistemlerinde sorunlu alanları hızlıca belirleyip düzeltebilirsin. Bu patternler özellikle agent tabanlı uygulamalarda kritik; basit chat interface’lerden farklı olarak agentlar birden fazla LLM çağrısı yapar ve hata birikimi katlanır.

Framework Trade-off’unu Anlamak

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 istek başına 1+ saniye overhead biriktirebiliyor; prototip için kabul edilebilir, production için sorunlu.

Framework inflection noktası, ekibinin framework davranışını debug etmeye feature geliştirmekten daha fazla zaman harcadığı an. Production’da bu genellikle ilk ciddi latency sorunu veya token faturası şoku ile ortaya çıkar. Bazı ekipler bunu hızlı yaşıyor, bazıları hiç yaşamıyor. Bu çizgiyi ne zaman geçtiğini anlamak kritik; o noktada ya abstraction katmanlarını sadeleştirmek ya da framework’ten vazgeçmek gerekebilir. Bu kararı ertelemek sadece teknik borcu artırır—ekip moralı ve delivery hızı düşer.

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

Etki: Token maliyetleri konuşmalar uzadıkça aylık %30-40 artıyor. Her request’in tüm geçmişi içermesi latency’yi düşürüyor. Sonunda context window’lar taşıp hatalara yol açıyor. Özellikle uzun destek konuşmalarında bu pattern maliyet patlamasına neden olur; müşteri her mesajda artan token faturası öder.

Tespit: Token kullanım trendlerini zaman içinde izle. Konuşmalar ilerledikçe artan response sürelerine dikkat et.

Çözüm: Explicit limitlerle ConversationSummaryBufferMemory kullan (veya LangGraph persistence’a migrate et):

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
)
# Sonuç: Context kalitesini korurken %30 maliyet düşüşü

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)
# Gerçek olay: Agent search ve summarize tool'lar arasında 14 dakika döngüde kaldı

Etki: Agentlar sonsuz döngüye girebiliyor, bütçeyi tüketiyor ve korkunç kullanıcı deneyimi yaratıyor. Bir deployment’ta agent search ve summarize tool’ları arasında sonuca ulaşamadan 14 dakika döngüde kaldı. max_iterations ve max_execution_time olmadan production’da bu tür vakalar kaçınılmaz; tek bir kullanıcı binlerce token harcayabilir.

Tespit: Production’dan önce maliyet alarmları ve execution süresi izleme kur.

Çözüm: Konfigürasyonda explicit kontroller:

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` }
  ]
});

Etki: Gereksiz karmaşıklık, zorlaşan debugging, abstraksiyonlardan fayda görmeyen tasklar için ekip cognitive load’u.

Tespit: Code review; basit işlemler için abstraction katmanlarını say. Basit bir completion için 4+ modül import ediyorsan, direkt API kullanımını değerlendir.

4. Gizli Latency Overhead’i

Framework komponentleri önemli latency ekleyebiliyor:

from langchain.memory import ConversationBufferWindowMemory

# Anti-pattern: Memory wrapper çağrı başına 1+ saniye ekliyor
memory = ConversationBufferWindowMemory(k=5)
# Profiling gösterdi: wrapper işlem süresi > gerçek LLM çağrı süresi

Etki: Kötü kullanıcı deneyimi, yüksek request hacimlerine scale etme zorluğu.

Tespit: Framework komponentleriyle ve olmadan profil. End-to-end latency ile direkt API çağrı süresini ölç.

Çözüm: Performance-critical path’ler için custom lightweight alternatifler:

# 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

# Sonuç: Request başına latency'de 1.2 saniye düşüş

5. Default Konfigürasyon Körlüğü

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

Etki: Yüksek operasyonel maliyetler, yavaş response’lar, disk’i dolduran verbose logging.

Tespit: Production launch’tan önce maliyet ve latency metrikleri için baseline oluştur.

Çözüm: Explicit production konfigürasyonu:

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

Etki: Sessiz hatalar, imkansız debugging, sadece kullanıcı şikayetleriyle sorun keşfi.

Tespit: Gözlemleyemediğin şeyi tespit edemezsin; sorun bu zaten.

Çözüm: İlk günden LangSmith tracing:

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 Naifliği

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()
# Gerçek deneyim: Mühendislik zamanının %40'ı data ingestion sorunlarında geçti

Etki: Doküman tiplerine uygun olmayan PDF parser’ı, uluslararası metinlerde encoding sorunları, retrieval kalitesini düşüren chunking problemleri.

Tespit: Doküman işlemede yüksek hata oranları, kötü retrieval sonuçları.

Çözüm: Birden fazla stratejiyle data loader’ların kapsamlı testi:

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

Pattern 1: 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)

Faydalar: Net composition, built-in async desteği, legacy chain’lerle karşılaştırıldığında daha kolay debugging.

Ne zaman kullan: Birden fazla LLM çağrısı, transformation’lar veya conditional logic gerektiren karmaşık workflow’lar.

Pattern 2: 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

Pattern 3: 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

Gerçek etki: Cache’lenmiş response’lar için %40 maliyet düşüşü ve %80 latency iyileşmesi.

Hit yuzde 40

Miss yuzde 60

Gelen Request

Cache Key Olustur

Cache Lookup

Aninda Response

LLM API Call

Cache'e Sakla

Response Dondur

Pattern 4: 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})

İzlenecek key 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

Pattern 5: 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)

Sonuç: Tipik deployment’lar basit query’leri ucuz modellere yönlendirerek %50-60 maliyet düşüşü görüyor.

Pattern 6: 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}")

Faydalar: Type safety, otomatik validation, LLM ile downstream kod arasında net contract’lar.

Migration Karar Matrisi

Doğru yaklaşımı seçmek senin spesifik gereksinimlerine bağlı:

Basit Chatbot

Orta RAG/Chains

Karmasik Multi-Agent

Search Odakli

Genel Amacli

LLM Uygulama Ihtiyaci

Use Case Karmasikligi

Direkt OpenAI/Anthropic API

Birincil Ihtiyac

LangGraph

LlamaIndex

LCEL ile LangChain

Observability Ekle

Production Deployment

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 Ne Zaman Kullanılmalı

  • 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

Direkt API’ler Ne Zaman Kullanılmalı

  • 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

LangChain’den Ne Zaman Migrate Edilmeli

Migration’ı düşün:

  • Ekip framework davranışını debug etmeye feature geliştirmekten daha fazla zaman harcıyor
  • Performance profiling framework overhead’ini bottleneck olarak gösteriyor (>1s eklenen latency)
  • 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
)

Gerçek Maliyet Etkisi

Deployment case study sonuçları:

  • Custom memory implementation: %30 maliyet düşüşü
  • Redis caching: %40 maliyet düşüşü, %80 latency iyileşmesi
  • Model routing: %62 token maliyet düşüşü
  • Kombine yaklaşım: %50-70 toplam maliyet düşüşü

Basit yuzde 50

Karmasik yuzde 50

Request

Karmasiklik

GPT-3.5-turbo

0.0005 Dolar per 1K input

GPT-4

0.03 Dolar per 1K input

Memory Trim

Son 5 mesaj

max_tokens esittir 512

Sonucu Cache le

Maliyetleri Takip Et

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: En yüksek maliyetli bileşeni tespit et
# Profil: Memory yönetimi 1.2s latency ekliyor

# 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: Sonuçları ölç
# Custom memory: -1.2s latency, -30% token, aynı kalite

# 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: Direkt LangGraph yaklaşımı (önerilen)
from langgraph.prebuilt import create_react_agent

agent = create_react_agent(
    model=llm,
    tools=tools
)

Faydalar: Daha iyi composability, built-in streaming, daha net debugging, agent davranışı üzerinde tam kontrol.

Yaygın Tuzaklar ve Dersler

Tuzak 1: Prototip-Production Tuzağı

Pattern: Default’larla prototip development’ta iyi çalışıyor. Production yüksek maliyetler, yavaş yanıtlar, sessiz hatalar ortaya çıkarıyor.

Ders: İlk günden production için tasarla. İlk production deployment’tan önce resource limitleri belirle, caching implementasyonu yap, observability ekle.

Tuzak 2: Framework Lock-In Körlüğü

Pattern: Hızlı prototyping için LangChain ile başla. Altı ay sonra derinlemesine kenetlenmiş mimari migration’ı aylarca iş haline getiriyor.

Ders: Framework kullanımını sınırlarda tut. Core business logic framework-agnostik olmalı. Bu gelecekteki değişiklikleri yönetilebilir kılar.

Tuzak 3: Observability Sonradan Düşünülmesi

Pattern: Tracing veya monitoring olmadan launch et. Production sorunlarını debug edemeyerek kullanıcı şikayetleriyle keşfet.

Ders: LangSmith veya eşdeğeri proje başından itibaren, sorunlar ortaya çıktıktan sonra değil.

Tuzak 4: Korkuluksuz Agent Özerkliği

Pattern: Agent’ı kontrolsüz “halleder” diye güven. Gerçek incident: 14 dakikalık execution loop, bütçe tüketildi.

Ders: Max iteration, timeout ve maliyet bütçeleri zorunlu, opsiyonel değil. Agentlar güçlü ama explicit kısıtlamalar gerektiriyor.

Key Takeaway’ler

LangChain bir tool, gereklilik değil. Framework overhead’inin senin spesifik use case’in için abstraksiyonları haklı çıkarıp çıkarmadığını değerlendir.

Prototip konfigürasyonları production’da çalışmaz. Default’lar development hızı için optimize edilmiş, production güvenilirliği veya maliyet verimliliği için değil.

Observability zorunlu. Production sorunlarını debug ederken sonradan değil, ilk günden LangSmith veya eşdeğeri.

Agent davranışını explicit kontrol et. Max iteration’lar, timeout’lar ve maliyet bütçeleri pahalı sürprizleri önlüyor.

Memory yönetimi maliyetleri doğrudan etkiliyor. Sınırsız memory sınırsız token kullanımına ve düşen performance’a yol açıyor.

Basit daha iyi olabilir. Direkt API çağrılarının daha net ve hızlı olduğu basit tasklar için framework abstraksiyonları kullanma.

Migration mümkün. Gereksinimler framework’ün pattern’larını aştığında ekipler başarıyla LangChain’den uzaklaşıyor.

Production agentlar için LangGraph. Prototiplerin ötesine geçerken, LangGraph production sistemlerinin ihtiyaç duyduğu kontrol ve dayanıklılığı sağlıyor.

Maliyet optimizasyonu sürekli. İterasyonlar halinde monitor et, profille et, optimize et. İlk deployment sadece başlangıç noktası.

Öğrenme için zaman ayır. Framework abstraksiyonları bazı taskları hızlandırıyor ama gizli davranışları anlamak ve debugging tekniklerinde yatırım gerektiriyor.

LangChain ile production’da çalışmak düşünülmüş mimari kararlar, dikkatli konfigürasyon ve sürekli monitoring gerektiriyor. Framework uygun kullanıldığında değerli abstraksiyonlar sağlıyor, ama başarı onun limitlerini anlamaya ve baştan itibaren bunların etrafında tasarım yapmaya bağlı. Anti-pattern’leri erken tespit etmek—özellikle memory accumulation ve callback overhead—maliyet ve latency sorunlarını önlemek için kritik. Bu rehberdeki pattern’ler gerçek production sistemlerinden uyarlanmış; senin context’inde küçük uyarlamalar gerekebilir ama temel ilkeler geçerli kalacaktır.

İlgili yazılar

Production Sistemleri için Prompt Engineering: Sistematik Bir Mühendislik Yaklaşımı

Kurumsal LLM uygulamaları için production-grade prompt engineering sistemleri oluşturmak üzerine kapsamlı bir teknik rehber: sistematik tasarım, güvenlik, observability ve maliyet optimizasyonu.

prompt-engineeringllmai-development+6
AI/LLM Sözlüğü: Her Geliştiricinin Bilmesi Gereken 82 Terim

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.

llmgenaiai-agents+9
AI Workload'ları için FinOps: Production'da LLM Maliyet Yönetimi

Token-based pricing, production LLM uygulamaları için benzersiz maliyet zorlukları yaratır. Prompt caching, model routing ve token budget'ları ile kaliteden ödün vermeden maliyetleri %60-80 azaltmak için sistematik optimizasyon stratejilerini öğren.

awsfinopsllm+5
Veritabanı Query Profiling: Sistematik Optimizasyon Yolculuğu

Sistematik veritabanı profiling ve optimizasyonu ile yıllık altyapı maliyetlerini 100K dolar azalttığımız hikaye. PostgreSQL ve MongoDB performance deneyimleri.

database-optimizationpostgresqlmongodb+7
AWS CDK Link Shortener Bölüm 4: Production Deployment ve Optimizasyon

Multi-environment deployment stratejileri, ölçekte performans optimizasyonu, ve maliyet yönetimi. Production deneyimleri ve öğrenilen dersler ile doğru monitoring ve incident response pattern'ları.

aws-cdklambdadynamodb+6