ChatGPT’ye bir soru sorduğunuzda etkileyici cevaplar alırsınız. Peki ya şirketinizin iç dokümanlarından cevap vermesini, bir API çağırıp sonucu yorumlamasını veya geçmiş konuşmalarınızı hatırlamasını isterseniz? İşte tam bu noktada LangChain devreye giriyor.
Büyük Dil Modelleri (LLM) tek başına oldukça güçlü. Ancak gerçek dünya uygulamalarında genellikle şunlara ihtiyaç duyuyoruz:
- Dış veri entegrasyonu: Dokümanlar, veritabanları, API’ler
- Çok adımlı iş akışları: Soru → arama → özet → doğrulama → cevap
- Bellek ve durum yönetimi: Sohbet geçmişi, kullanıcı profili
- Araç kullanımı: Tool calling, function calling
LangChain, bu ihtiyaçları “lego parçaları” gibi birleştirerek LLM tabanlı uygulamalar geliştirmeyi kolaylaştıran bir Python (ve JavaScript) çerçevesidir.
Not: LangChain ekosistemi hızla gelişiyor. Aşağıdaki örnekler temel mantığı öğretmeyi hedefliyor. Kullandığınız sürüme göre bazı import yolları veya sınıf isimleri farklılık gösterebilir.
LangChain Neyi Çözer?
LangChain’i anlamanın en iyi yolu şu perspektiften bakmak: LLM’i bir ürünün parçası haline getirmek.
Tek başına bir LLM’e “Türkiye’nin başkenti neresi?” diye sorduğunuzda doğru cevap alırsınız. Peki ya şunu sorsanız: “Şirketimizin 2024 Q3 satış raporundaki en düşük performanslı ürün hangisi?” İşte burada LLM tek başına yetersiz kalır çünkü bu veriye erişimi yok.
Tipik Kullanım Senaryoları
| Senaryo | Açıklama | Kullanılan Bileşenler |
|---|---|---|
| RAG Asistanı | Şirket dokümanlarından cevap veren chatbot | Loader, Splitter, Embedding, Retriever, Chain |
| Tool Agent | “Siparişi iptal et, mail gönder” gibi aksiyonlar | Tools, Agent, Memory |
| Pipeline İşleri | “Veriyi çek, analiz et, raporla” | Chain, Output Parser |
| Conversational AI | Bağlam hatırlayan sohbet botu | Memory, Chain, Prompt |
Temel Kavramlar
LangChain’in yapı taşlarını tek tek inceleyelim. Her kavramı önce açıklayacak, sonra kod örneğiyle pekiştireceğiz.
1. Prompt (İstem)
LLM’e ne istediğinizi söylemenin en etkili yolu iyi tasarlanmış bir prompt’tur. LangChain’de prompt’lar şablon (template) olarak tutulur ve dinamik değişkenlerle zenginleştirilir.
Neden Şablon Kullanmalı?
Prompt’u doğrudan kod içine yazmak yerine şablon kullanmanın avantajları:
- Yeniden kullanılabilirlik: Aynı şablonu farklı değişkenlerle çağırabilirsiniz
- Bakım kolaylığı: Prompt’u değiştirmek için kodu değiştirmeniz gerekmez
- A/B testi: Farklı prompt versiyonlarını kolayca test edebilirsiniz
- Versiyon kontrolü: Prompt’ları ayrı dosyalarda tutup Git ile takip edebilirsiniz
Prompt Bileşenleri
Bir ChatPromptTemplate tipik olarak şu mesaj türlerinden oluşur:
┌─────────────────────────────────────────────────────────┐
│ SYSTEM: Modelin kişiliği, kuralları, sınırları │
│ "Sen yardımcı bir asistansın. Kısa ve öz cevap ver." │
├─────────────────────────────────────────────────────────┤
│ USER: Kullanıcının sorusu veya isteği │
│ "Python'da liste nasıl oluşturulur?" │
├─────────────────────────────────────────────────────────┤
│ ASSISTANT: (Opsiyonel) Örnek cevap - few-shot için │
│ "numbers = [1, 2, 3]" │
└─────────────────────────────────────────────────────────┘
Code language: JavaScript (javascript)Kod Örneği
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
# Basit şablon
simple_prompt = ChatPromptTemplate.from_template(
"Şu konuyu 3 cümlede açıkla: {topic}"
)
# Çok mesajlı şablon
chat_prompt = ChatPromptTemplate.from_messages([
("system", "Sen {role} rolünde bir asistansın. Cevaplarını {language} dilinde ver."),
("user", "{question}")
])
# Değişkenleri doldur
messages = chat_prompt.format_messages(
role="tecrübeli bir yazılım mühendisi",
language="Türkçe",
question="Microservices mimarisi nedir?"
)
# Sonuç: LLM'e gönderilecek mesaj listesi
print(messages)
# [SystemMessage(content='Sen tecrübeli bir yazılım mühendisi rolünde...'),
# HumanMessage(content='Microservices mimarisi nedir?')]
Code language: Python (python)Few-Shot Prompting
Modele örnek vererek istediğiniz çıktı formatını gösterebilirsiniz:
few_shot_prompt = ChatPromptTemplate.from_messages([
("system", "Verilen metinden anahtar kelimeleri çıkar."),
("user", "Python programlama dili, web geliştirme için kullanılır."),
("assistant", "python, programlama, web geliştirme"),
("user", "Makine öğrenimi, yapay zekanın bir alt dalıdır."),
("assistant", "makine öğrenimi, yapay zeka"),
("user", "{input_text}") # Gerçek girdi
])
Code language: Python (python)2. Model (LLM / ChatModel)
LangChain, farklı model sağlayıcılarını ortak bir arayüzle kullanmanızı sağlar. Bu soyutlama katmanı sayesinde:
- Model değiştirirken kodunuz kırılmaz
- Geliştirmede ucuz model, production’da güçlü model kullanabilirsiniz
- Farklı modelleri kolayca karşılaştırabilirsiniz
Desteklenen Sağlayıcılar
┌────────────────┬──────────────────────┬─────────────────────┐
│ Sağlayıcı │ Kurulum │ Örnek Model │
├────────────────┼──────────────────────┼─────────────────────┤
│ OpenAI │ langchain-openai │ gpt-4o, gpt-4o-mini │
│ Anthropic │ langchain-anthropic │ claude-sonnet │
│ Google │ langchain-google │ gemini-pro │
│ Ollama (Yerel) │ langchain-ollama │ llama3.2, mistral │
│ Hugging Face │ langchain-huggingface│ Llama, Falcon │
└────────────────┴──────────────────────┴─────────────────────┘
Code language: CSS (css)Kod Örneği
from langchain_openai import ChatOpenAI
from langchain_anthropic import ChatAnthropic
from langchain_ollama import ChatOllama
# OpenAI
openai_llm = ChatOpenAI(
model="gpt-4o-mini",
temperature=0.7, # Yaratıcılık seviyesi (0-1)
max_tokens=1000, # Maksimum çıktı uzunluğu
timeout=30 # Zaman aşımı (saniye)
)
# Anthropic - aynı arayüz
anthropic_llm = ChatAnthropic(
model="claude-sonnet-4-20250514",
temperature=0.7
)
# Yerel model (Ollama) - API maliyeti yok
local_llm = ChatOllama(
model="llama3.2",
base_url="http://localhost:11434"
)
# Hepsi aynı şekilde çağrılır
response = openai_llm.invoke("Merhaba, nasılsın?")
print(response.content)
Code language: Python (python)Temperature Nedir?
Temperature parametresi modelin “yaratıcılık” seviyesini kontrol eder:
- temperature=0: Deterministik, her seferinde aynı cevap. Kod üretimi, analiz için ideal.
- temperature=0.7: Dengeli. Genel kullanım için uygun.
- temperature=1.0: Yüksek çeşitlilik. Yaratıcı yazarlık, beyin fırtınası için.
3. Chain (Zincir) ve LCEL
Zincir, birden fazla adımı sırayla çalıştıran bir iş akışıdır. Modern LangChain’de zincirler LCEL (LangChain Expression Language) ile oluşturulur.
LCEL Nedir?
LCEL, LangChain bileşenlerini | (pipe) operatörüyle birleştirmenizi sağlar. Unix pipe’larına benzer şekilde, bir adımın çıktısı sonraki adımın girdisi olur.
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Prompt │ ──▶ │ Model │ ──▶ │ Parser │ ──▶ │ Çıktı │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
│ │ │
│ {"topic": │ AIMessage │ "Özet metni"
│ "Python"} │ (ham çıktı) │ (string)
│ │ │
▼ ▼ ▼
prompt.format() → model.invoke() → parser.parse()
Code language: JavaScript (javascript)Temel Zincir Örneği
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
# 1. Bileşenleri tanımla
prompt = ChatPromptTemplate.from_template(
"Şu konuyu 3 cümlede özetle: {topic}"
)
model = ChatOpenAI(model="gpt-4o-mini")
parser = StrOutputParser() # AIMessage → string dönüşümü
# 2. Zinciri oluştur
chain = prompt | model | parser
# 3. Çalıştır
result = chain.invoke({"topic": "Kuantum bilgisayarlar"})
print(result)
# "Kuantum bilgisayarlar, klasik bilgisayarlardan farklı olarak..."
Code language: Python (python)Neden | Operatörü?
Alternatif olarak her adımı ayrı ayrı çağırabilirdiniz:
# Pipe olmadan (uzun yol)
formatted_prompt = prompt.format_messages(topic="Kuantum bilgisayarlar")
model_response = model.invoke(formatted_prompt)
final_result = parser.parse(model_response)
# Pipe ile (kısa yol)
result = (prompt | model | parser).invoke({"topic": "Kuantum bilgisayarlar"})
Code language: Python (python)LCEL’in avantajları:
- Okunabilirlik: Veri akışı soldan sağa, net görünür
- Composability: Zincirleri kolayca birleştirip dallandırabilirsiniz
- Built-in özellikler: Streaming, async, batch otomatik desteklenir
Paralel ve Koşullu Zincirler
from langchain_core.runnables import RunnableParallel, RunnableLambda
# Paralel çalışma: Aynı anda iki farklı işlem
parallel_chain = RunnableParallel(
summary=prompt | model | parser,
keywords=keyword_prompt | model | parser
)
result = parallel_chain.invoke({"topic": "Yapay zeka"})
# {"summary": "...", "keywords": "..."}
# Koşullu dallanma
def route(input):
if "kod" in input["question"].lower():
return code_chain
return general_chain
conditional_chain = RunnableLambda(route)
Code language: Python (python)4. Output Parsers (Çıktı Ayrıştırıcıları)
LLM’ler metin üretir, ancak uygulamanızda genellikle yapılandırılmış veri (JSON, liste, nesne) istersiniz. Output Parsers bu dönüşümü sağlar.
Parser Türleri
| Parser | Çıktı Türü | Kullanım Alanı |
|---|---|---|
StrOutputParser | String | Basit metin çıktıları |
JsonOutputParser | Dict | API yanıtları, yapılandırılmış veri |
PydanticOutputParser | Pydantic Model | Tip güvenli, doğrulanmış veri |
CommaSeparatedListOutputParser | List[str] | Virgülle ayrılmış listeler |
Pydantic ile Tip Güvenli Çıktı
from langchain_core.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
from typing import List
# Çıktı şemasını tanımla
class MovieReview(BaseModel):
title: str = Field(description="Film adı")
rating: int = Field(description="1-10 arası puan")
pros: List[str] = Field(description="Olumlu yönler")
cons: List[str] = Field(description="Olumsuz yönler")
# Parser oluştur
parser = PydanticOutputParser(pydantic_object=MovieReview)
# Prompt'a format talimatlarını ekle
prompt = ChatPromptTemplate.from_messages([
("system", "Film yorumu yap. {format_instructions}"),
("user", "{movie}")
])
# Zincir
chain = prompt | model | parser
result = chain.invoke({
"movie": "Inception",
"format_instructions": parser.get_format_instructions()
})
# Sonuç: MovieReview nesnesi
print(result.title) # "Inception"
print(result.rating) # 9
print(result.pros) # ["Özgün senaryo", "Görsel efektler"]
Code language: Python (python)5. Document Loaders ve Text Splitters
RAG sistemlerinin ilk adımı: veriyi yüklemek ve işlenebilir parçalara bölmek.
Document Loaders
LangChain 100+ farklı kaynak için loader sunar:
# PDF
from langchain_community.document_loaders import PyPDFLoader
loader = PyPDFLoader("rapor.pdf")
# Word
from langchain_community.document_loaders import Docx2txtLoader
loader = Docx2txtLoader("belge.docx")
# Web sayfası
from langchain_community.document_loaders import WebBaseLoader
loader = WebBaseLoader("https://example.com/article")
# CSV
from langchain_community.document_loaders import CSVLoader
loader = CSVLoader("data.csv")
# YouTube transkripti
from langchain_community.document_loaders import YoutubeLoader
loader = YoutubeLoader.from_youtube_url("https://youtube.com/watch?v=...")
# Notion
from langchain_community.document_loaders import NotionDirectoryLoader
loader = NotionDirectoryLoader("notion_export/")
# Tüm loader'lar aynı şekilde kullanılır
documents = loader.load()
Code language: Python (python)Text Splitters
Dokümanları LLM’in context window’una sığacak ve anlamlı parçalara bölmek kritiktir.
┌────────────────────────────────────────────────────────────────┐
│ UZUN DOKÜMAN │
│ Bölüm 1: Giriş │
│ Lorem ipsum dolor sit amet, consectetur adipiscing elit... │
│ Bölüm 2: Yöntem │
│ Sed do eiusmod tempor incididunt ut labore et dolore... │
└────────────────────────────────────────────────────────────────┘
│
▼
Text Splitter (chunk_size=500, overlap=50)
│
┌───────────────────┼───────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Chunk 1 │ │ Chunk 2 │ │ Chunk 3 │
│ 500 char │◀──────▶│ 500 char │◀──────▶│ 500 char │
│ │ overlap│ │ overlap│ │
└──────────┘ 50 └──────────┘ 50 └──────────┘
Code language: JavaScript (javascript)Neden Overlap? Cümlelerin ortasından kesilmesini önler, bağlamın kaybolmasını engeller.
from langchain.text_splitter import (
RecursiveCharacterTextSplitter,
CharacterTextSplitter,
TokenTextSplitter
)
# En yaygın kullanılan: RecursiveCharacterTextSplitter
# Önce paragraflara, sonra cümlelere, sonra kelimelere böler
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # Her parça maksimum 1000 karakter
chunk_overlap=200, # Parçalar arası 200 karakter örtüşme
length_function=len, # Uzunluk ölçme fonksiyonu
separators=["\n\n", "\n", ".", " ", ""] # Bölme önceliği
)
chunks = splitter.split_documents(documents)
print(f"Doküman {len(chunks)} parçaya bölündü")
Code language: Python (python)Chunk Size Nasıl Belirlenir?
| Boyut | Avantaj | Dezavantaj | Önerilen Kullanım |
|---|---|---|---|
| Küçük (200-500) | Hassas arama | Bağlam kaybı | Sık sorulan sorular |
| Orta (500-1000) | Dengeli | – | Genel kullanım |
| Büyük (1000-2000) | Zengin bağlam | Gürültülü sonuçlar | Teknik dokümanlar |
6. Embeddings ve Vektör Veritabanları
Embedding, metni sayısal vektöre dönüştürme işlemidir. Benzer anlamlı metinler, vektör uzayında birbirine yakın konumlanır.
"Kediler tatlı hayvanlardır" ──▶ [0.2, 0.8, 0.1, 0.9, ...]
"Cats are cute animals" ──▶ [0.21, 0.79, 0.12, 0.88, ...] ← Benzer!
"Ekonomi büyüyor" ──▶ [0.7, 0.1, 0.6, 0.2, ...] ← Farklı
Code language: JavaScript (javascript)Embedding Modelleri
# OpenAI
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
# Ücretsiz alternatif: HuggingFace
from langchain_huggingface import HuggingFaceEmbeddings
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
# Yerel: Ollama
from langchain_ollama import OllamaEmbeddings
embeddings = OllamaEmbeddings(model="nomic-embed-text")
# Kullanım
vector = embeddings.embed_query("Merhaba dünya")
print(len(vector)) # 1536 (OpenAI için)
Code language: Python (python)Vektör Veritabanları Karşılaştırması
| Veritabanı | Tip | Avantaj | Dezavantaj |
|---|---|---|---|
| Chroma | Yerel/Embedded | Kolay kurulum, geliştirme için ideal | Ölçeklenebilirlik sınırlı |
| FAISS | Yerel | Facebook tarafından, çok hızlı | Persistence yok (ek kütüphane gerekli) |
| Qdrant | Self-hosted/Cloud | Filtering, hybrid search | Kurulum karmaşıklığı |
| Pinecone | Managed Cloud | Sıfır bakım, yüksek ölçek | Maliyet, vendor lock-in |
| Weaviate | Self-hosted/Cloud | GraphQL API, çok modalite | Öğrenme eğrisi |
Daha sonra vektör veritabanları üzerine bir yazı yazmayı planlıyorum.
Vektör DB Örneği (Chroma)
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
# Embedding modeli
embeddings = OpenAIEmbeddings()
# Vektör DB oluştur (bellekte)
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings
)
# Kalıcı depolama için
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db" # Diske kaydet
)
# Benzerlik araması
results = vectorstore.similarity_search(
query="Satış performansı nasıl?",
k=3 # En benzer 3 sonuç
)
# Retriever olarak kullan (RAG için)
retriever = vectorstore.as_retriever(
search_type="similarity", # veya "mmr" (çeşitlilik için)
search_kwargs={"k": 5}
)
Code language: Python (python)7. RAG (Retrieval Augmented Generation)
RAG, LLM’in “ezbere cevap vermesi” yerine dış kaynaklardan bilgi alarak cevap üretmesini sağlar. Bu yaklaşım:
- Halüsinasyonu azaltır: Model uydurma yerine gerçek veriye dayanır
- Güncel bilgi sağlar: Eğitim verisinde olmayan bilgilere erişim
- Kaynak gösterir: Cevabın nereden geldiği takip edilebilir
RAG Akışı
┌──────────────────────────────────────────────────────────────────┐
│ INDEXLEME (Bir kere) │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Doküman │───▶│ Parçala │───▶│ Embedding│───▶│ Vektör │
│ Yükle │ │ (Chunk) │ │ Oluştur │ │ DB Kaydet│
└──────────┘ └──────────┘ └──────────┘ └──────────┘
┌──────────────────────────────────────────────────────────────────┐
│ SORGULAMA (Her soru) │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Soru │───▶│ Embedding│───▶│ Benzer │───▶│ LLM'e │
│ │ │ Oluştur │ │ Parçaları│ │ Context │
│ │ │ │ │ Getir │ │ + Soru │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
│
▼
┌──────────┐
│ CEVAP │
└──────────┘
Tam RAG Örneği
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
# ========== 1. INDEXLEME ==========
# Doküman yükle
loader = PyPDFLoader("sirket_el_kitabi.pdf")
documents = loader.load()
print(f"Yüklenen sayfa sayısı: {len(documents)}")
# Parçala
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
chunks = splitter.split_documents(documents)
print(f"Oluşan parça sayısı: {len(chunks)}")
# Embedding ve vektör DB
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./company_db"
)
# ========== 2. SORGULAMA ==========
# Retriever
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
# Prompt
template = """Aşağıdaki bağlamı kullanarak soruyu cevapla.
Eğer bağlamda cevap yoksa, "Bu bilgi dokümanlarda bulunmuyor" de.
Cevabını verirken kaynak bölümlerini de belirt.
Bağlam:
{context}
Soru: {question}
Cevap:"""
prompt = ChatPromptTemplate.from_template(template)
# Model
model = ChatOpenAI(model="gpt-4o", temperature=0)
# Yardımcı fonksiyon: Document listesini stringe çevir
def format_docs(docs):
return "\n\n---\n\n".join(doc.page_content for doc in docs)
# RAG zinciri
rag_chain = (
{
"context": retriever | format_docs,
"question": RunnablePassthrough()
}
| prompt
| model
| StrOutputParser()
)
# Soru sor
answer = rag_chain.invoke("Yıllık izin hakkım kaç gün?")
print(answer)
Code language: Python (python)8. Tools ve Agents
Bazen LLM’in sadece metin üretmesi yetmez; harici sistemlerle etkileşime girmesi gerekir: API çağırmak, veritabanı sorgulamak, dosya oluşturmak gibi.
Tool vs Agent Farkı
| Kavram | Açıklama | Karar Veren |
|---|---|---|
| Tool | LLM’in çağırabileceği tek bir fonksiyon | Siz (geliştirici) |
| Agent | Birden fazla tool’u planlayıp yürüten sistem | LLM (otonom) |
Tool Tanımlama
from langchain_core.tools import tool
from typing import Optional
import requests
@tool
def hava_durumu(sehir: str) -> str:
"""
Belirtilen şehrin güncel hava durumunu getirir.
Args:
sehir: Hava durumu sorgulanacak şehir adı (örn: "İstanbul")
"""
# Gerçek uygulamada API çağrısı yapılır
# response = requests.get(f"https://api.weather.com/{sehir}")
return f"{sehir}: 18°C, parçalı bulutlu, nem %65"
@tool
def hesap_makinesi(ifade: str) -> str:
"""
Matematiksel ifadeyi hesaplar.
Args:
ifade: Hesaplanacak matematiksel ifade (örn: "2 + 2 * 3")
"""
try:
# Güvenlik: sadece basit matematik
allowed = set("0123456789+-*/.(). ")
if not all(c in allowed for c in ifade):
return "Hata: Geçersiz karakterler"
return str(eval(ifade))
except Exception as e:
return f"Hata: {str(e)}"
@tool
def web_arama(sorgu: str, sonuc_sayisi: int = 3) -> str:
"""
Web'de arama yapar ve sonuçları döner.
Args:
sorgu: Arama sorgusu
sonuc_sayisi: Döndürülecek sonuç sayısı
"""
# Gerçek uygulamada Serper, Google API vb. kullanılır
return f"'{sorgu}' için {sonuc_sayisi} sonuç bulundu..."
Code language: Python (python)Tool Binding
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4o")
# Araçları modele bağla
model_with_tools = model.bind_tools([hava_durumu, hesap_makinesi, web_arama])
# Model artık hangi aracı çağıracağına kendisi karar verir
response = model_with_tools.invoke("İstanbul'da hava nasıl ve 15*7 kaç eder?")
# Yanıt tool_calls içerir
print(response.tool_calls)
# [{'name': 'hava_durumu', 'args': {'sehir': 'İstanbul'}},
# {'name': 'hesap_makinesi', 'args': {'ifade': '15*7'}}]
Code language: Python (python)Agent Oluşturma (LangGraph)
Agent, tool’ları zincirleyerek karmaşık görevleri otonom yürütür:
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
# Agent oluştur
agent = create_react_agent(
model=ChatOpenAI(model="gpt-4o"),
tools=[hava_durumu, hesap_makinesi, web_arama]
)
# Çalıştır
result = agent.invoke({
"messages": [("user", """
İstanbul ve Ankara'nın hava durumlarını karşılaştır.
Sıcaklık farkını hesapla.
Hangi şehir daha sıcak?
""")]
})
# Agent şu adımları otomatik yürütür:
# 1. hava_durumu("İstanbul") çağır
# 2. hava_durumu("Ankara") çağır
# 3. hesap_makinesi("18 - 12") çağır
# 4. Sonucu yorumla ve cevapla
Code language: Python (python)9. Memory (Bellek)
Sohbet uygulamalarında kullanıcının önceki mesajlarını hatırlamak kritiktir. Aksi halde her mesaj bağımsız işlenir ve bağlam kaybolur.
Bellek Türleri
┌────────────────────────────────────────────────────────────────┐
│ BELLEK STRATEJİLERİ │
├────────────────────────────────────────────────────────────────┤
│ ConversationBufferMemory : Tüm geçmişi tut │
│ ConversationBufferWindowMemory : Son N mesajı tut │
│ ConversationSummaryMemory : Geçmişi özetle │
│ ConversationKGMemory : Knowledge graph olarak tut │
└────────────────────────────────────────────────────────────────┘
Bellek Örneği
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_openai import ChatOpenAI
# Oturum bazlı geçmiş saklama
store = {}
def get_session_history(session_id: str):
if session_id not in store:
store[session_id] = InMemoryChatMessageHistory()
return store[session_id]
# Prompt (geçmiş mesajlar için placeholder)
prompt = ChatPromptTemplate.from_messages([
("system", "Sen yardımcı bir asistansın."),
MessagesPlaceholder(variable_name="history"),
("user", "{question}")
])
# Zincir
chain = prompt | ChatOpenAI(model="gpt-4o-mini")
# Bellek ekle
chain_with_memory = RunnableWithMessageHistory(
chain,
get_session_history,
input_messages_key="question",
history_messages_key="history"
)
# Kullanım
config = {"configurable": {"session_id": "user_123"}}
# İlk mesaj
response1 = chain_with_memory.invoke(
{"question": "Benim adım Ali, yazılım mühendisiyim."},
config=config
)
print(response1.content)
# İkinci mesaj - öncekini hatırlar
response2 = chain_with_memory.invoke(
{"question": "Adım ne ve ne iş yapıyorum?"},
config=config
)
print(response2.content)
# "Adınız Ali ve yazılım mühendisisiniz."
Code language: Python (python)10. Streaming (Gerçek Zamanlı Yanıt)
Uzun cevaplarda kullanıcının tüm yanıtı beklemesi kötü deneyimdir. Streaming ile yanıt parça parça gelir.
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_template("Şu konuda detaylı bir makale yaz: {topic}")
model = ChatOpenAI(model="gpt-4o", streaming=True)
chain = prompt | model
# Streaming ile çalıştır
for chunk in chain.stream({"topic": "Yapay zeka etiği"}):
print(chunk.content, end="", flush=True)
# Çıktı: "Yapay" "zeka" "etiği," "günümüzde" ...
# Her kelime anında ekrana basılır
Code language: Python (python)Async Streaming (Web uygulamaları için)
import asyncio
async def stream_response():
async for chunk in chain.astream({"topic": "Yapay zeka"}):
print(chunk.content, end="", flush=True)
asyncio.run(stream_response())
Code language: JavaScript (javascript)Hata Yönetimi ve Debugging
Production sistemlerde hatalar kaçınılmazdır. LangChain’de sağlam hata yönetimi şöyle yapılır:
Fallback (Yedek Model)
from langchain_openai import ChatOpenAI
from langchain_anthropic import ChatAnthropic
# Ana model
primary = ChatOpenAI(model="gpt-4o")
# Yedek model
fallback = ChatAnthropic(model="claude-sonnet-4-20250514")
# Fallback zinciri: primary başarısız olursa fallback çalışır
model_with_fallback = primary.with_fallback([fallback])
# Kullanım aynı
response = model_with_fallback.invoke("Merhaba")
Code language: Python (python)Retry (Tekrar Deneme)
from langchain_core.runnables import RunnableRetry
# 3 kez dene, her denemede 2 saniye bekle
chain_with_retry = chain.with_retry(
stop_after_attempt=3,
wait_exponential_jitter=True
)
Code language: Python (python)LangSmith ile Debugging
LangSmith, LangChain ekibinin geliştirdiği observability platformudur:
import os
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "ls__..."
os.environ["LANGCHAIN_PROJECT"] = "my-project"
# Artık tüm zincir çağrıları LangSmith'te görünür
chain.invoke({"topic": "Test"})
Code language: Python (python)LangSmith’te görebilecekleriniz:
- Her adımın giriş/çıkışı
- Token kullanımı ve maliyet
- Latency (gecikme) metrikleri
- Hata stacktrace’leri
Performans Optimizasyonu
Caching
Aynı sorguları tekrar tekrar LLM’e göndermek hem yavaş hem pahalıdır:
from langchain_core.globals import set_llm_cache
from langchain_community.cache import SQLiteCache
# SQLite cache aktif et
set_llm_cache(SQLiteCache(database_path=".langchain_cache.db"))
# İlk çağrı: LLM'e gider, ~2 saniye
result1 = chain.invoke({"topic": "Python"})
# İkinci çağrı: Cache'den gelir, ~0.01 saniye
result2 = chain.invoke({"topic": "Python"})
Code language: Python (python)Async ve Batch
import asyncio
# Async: I/O beklerken diğer işleri yap
async def process_async():
result = await chain.ainvoke({"topic": "AI"})
return result
# Batch: Birden fazla isteği paralel gönder
results = chain.batch([
{"topic": "Python"},
{"topic": "JavaScript"},
{"topic": "Rust"}
])Code language: Python (python)Güvenlik Konuları
API Key Yönetimi
# ❌ YANLIŞ: Kod içinde API key
openai_api_key = "sk-abc123..."
# ✅ DOĞRU: Environment variable
import os
os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY")
# ✅ DOĞRU: .env dosyası ile
from dotenv import load_dotenv
load_dotenv()
Code language: Python (python)Prompt Injection Koruması
Kullanıcı girdisinin prompt’u manipüle etmesini önleyin:
# ❌ YANLIŞ: Kullanıcı girdisi direkt prompt'a
prompt = f"Özet yap: {user_input}"
# ✅ DOĞRU: Girdiyi ayrı değişken olarak geç
prompt = ChatPromptTemplate.from_messages([
("system", "Sen bir özetleyicisin. SADECE verilen metni özetle."),
("user", "Metin: {user_input}")
])
Code language: Python (python)Hızlı Başlangıç: Adım Adım İlk Proje
1. Kurulum
# Sanal ortam oluştur
python -m venv langchain-env
source langchain-env/bin/activate # Windows: langchain-env\Scripts\activate
# Paketleri kur
pip install langchain langchain-openai langchain-community chromadb python-dotenv
Code language: PHP (php)2. Proje Yapısı
my-rag-app/
├── .env # API anahtarları
├── data/
│ └── documents/ # PDF, docx dosyaları
├── src/
│ ├── indexer.py # Doküman indexleme
│ ├── retriever.py # RAG sorgulama
│ └── app.py # Ana uygulama
└── requirements.txt
Code language: PHP (php)3. .env Dosyası
OPENAI_API_KEY=sk-...
LANGCHAIN_TRACING_V2=true
LANGCHAIN_API_KEY=ls__...
Code language: JavaScript (javascript)4. Basit RAG Uygulaması
# src/app.py
import os
from dotenv import load_dotenv
from langchain_community.document_loaders import DirectoryLoader, PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
load_dotenv()
class RAGAssistant:
def __init__(self, docs_path: str = "./data/documents"):
self.docs_path = docs_path
self.vectorstore = None
self.chain = None
def index_documents(self):
"""Dokümanları yükle ve indexle"""
# Yükle
loader = DirectoryLoader(
self.docs_path,
glob="**/*.pdf",
loader_cls=PyPDFLoader
)
documents = loader.load()
print(f"✓ {len(documents)} sayfa yüklendi")
# Parçala
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
chunks = splitter.split_documents(documents)
print(f"✓ {len(chunks)} parça oluşturuldu")
# Vektör DB
self.vectorstore = Chroma.from_documents(
documents=chunks,
embedding=OpenAIEmbeddings(),
persist_directory="./chroma_db"
)
print("✓ Vektör veritabanı oluşturuldu")
def setup_chain(self):
"""RAG zincirini kur"""
retriever = self.vectorstore.as_retriever(search_kwargs={"k": 3})
prompt = ChatPromptTemplate.from_template("""
Aşağıdaki bağlamı kullanarak soruyu cevapla.
Eğer bağlamda cevap yoksa, bunu belirt.
Bağlam:
{context}
Soru: {question}
Cevap:""")
model = ChatOpenAI(model="gpt-4o-mini", temperature=0)
self.chain = (
{"context": retriever | self._format_docs, "question": RunnablePassthrough()}
| prompt
| model
| StrOutputParser()
)
print("✓ RAG zinciri hazır")
def _format_docs(self, docs):
return "\n\n".join(doc.page_content for doc in docs)
def ask(self, question: str) -> str:
"""Soru sor"""
if not self.chain:
raise ValueError("Önce setup_chain() çalıştırın")
return self.chain.invoke(question)
# Kullanım
if __name__ == "__main__":
assistant = RAGAssistant()
assistant.index_documents()
assistant.setup_chain()
while True:
question = input("\nSoru: ")
if question.lower() in ["çıkış", "exit", "q"]:
break
answer = assistant.ask(question)
print(f"\nCevap: {answer}")
Code language: Python (python)LangChain Ekosistemi
| Bileşen | Açıklama | Ne Zaman Kullanılır |
|---|---|---|
| langchain-core | Temel soyutlamalar, LCEL | Her zaman (dependency) |
| langchain | Zincirler, retrieval | Genel kullanım |
| langchain-community | 3. parti entegrasyonlar | Spesifik araçlar için |
| langchain-openai | OpenAI entegrasyonu | OpenAI kullanırken |
| LangGraph | Stateful, döngüsel agent’lar | Karmaşık agent iş akışları |
| LangSmith | Observability, debugging | Production izleme |
| LangServe | API olarak sunma | Deployment |
Alternatifler ve Karşılaştırma
| Framework | Güçlü Yönler | Zayıf Yönler |
|---|---|---|
| LangChain | Geniş ekosistem, çok entegrasyon | Karmaşık, hızlı değişen API |
| LlamaIndex | RAG odaklı, basit | Agent desteği sınırlı |
| Haystack | Production-ready, modüler | Daha az topluluk |
| Semantic Kernel | Microsoft ekosistemi | Daha az olgun |
| Direkt API | Tam kontrol, basit | Her şeyi kendin yaz |
Ne Zaman LangChain Kullanmalı?
✅ Kullanın
- RAG sistemleri geliştirirken
- Birden fazla LLM sağlayıcısını desteklemeniz gerektiğinde
- Tool calling ve agent yapıları kurduğunuzda
- Hızlı prototipleme yaparken
- Standart patternleri tekrar tekrar yazamak istemediğinizde
⚠️ Düşünün
- Basit, tek seferlik LLM çağrıları için (direkt API yeterli)
- Çok özelleştirilmiş, düşük seviyeli kontrol gerektiren durumlarda
- Performans kritik uygulamalarda (soyutlama overhead’i)
- API’nin sık değişmesi sizi rahatsız ediyorsa
Sonuç
LangChain, LLM’leri gerçek dünya uygulamalarına entegre etmenin en popüler yollarından biri. Prompt yönetiminden agent orkestrasyonuna, RAG’dan tool calling’e kadar geniş bir yelpazede çözümler sunuyor.
Öğrenme eğrisi başlangıçta dik görünebilir, ancak temel kavramları anladığınızda parçalar yerine oturuyor:
- Prompt: LLM’e ne istediğinizi söyleyin
- Model: Farklı sağlayıcıları tek arayüzle kullanın
- Chain: Adımları birleştirin
- Retriever: Dış veriyi getirin
- Tools/Agents: Aksiyon alın
- Memory: Bağlamı koruyun
Tavsiyem: Küçük küçük başlayın. Önce basit bir zincir kurun, çalıştığını görün, sonra adım adım karmaşıklık ekleyin.
Kaynaklar
Resmi Dokümantasyon
Öğrenme Kaynakları
Topluluk
