İçeriğe atla

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.

Ayhan Sipahi Ayhan Sipahi

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ç.

Hit

Miss

Gelen Request

Cache Key Olustur

Cache Lookup

Aninda Response

LLM API Call

Cache'e Sakla

Response Dondur

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ı:

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 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.

Basit

Karmasik

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: 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#

İlgili yazılar