LangChain ile OpenAI ve Ollama Modellerini Birlikte Kullanma
Yapay zeka projelerinde en çok karşılaşılan sorulardan biri şu: “OpenAI’yi mi kullansam, yoksa kendi sunucumda çalışan bir modeli mi?” Aslında bu sorunun cevabı “ikisini birden” olabilir. LangChain tam da bu noktada devreye giriyor; hem bulut tabanlı OpenAI modellerini hem de yerel çalışan Ollama modellerini aynı kod tabanında kullanmanı sağlıyor. Bu yazıda gerçek dünya senaryolarıyla bu iki yaklaşımı nasıl birleştireceğini, ne zaman hangisini tercih etmen gerektiğini ve production ortamında nasıl yöneteceğini ele alacağız.
LangChain Nedir, Neden Kullanılır?
LangChain, büyük dil modelleriyle (LLM) uygulama geliştirmek için tasarlanmış bir framework. Ham API çağrıları yazmak yerine, model bağımsız bir soyutlama katmanı sunuyor. Yani bugün OpenAI kullandığın kodu yarın minimum değişiklikle Ollama’ya taşıyabiliyorsun.
Sysadmin perspektifinden bakacak olursak, LangChain şu problemleri çözüyor:
- Model bağımlılığını azaltır: Bir vendor lock-in olmadan farklı modeller arasında geçiş yapabilirsin
- Zincir yapısı sunar: Birden fazla işlemi sıralı veya paralel olarak yönetebilirsin
- Hafıza yönetimi sağlar: Konuşma geçmişini otomatik olarak takip edebilirsin
- Araç entegrasyonu kolaylaştırır: Web arama, hesap makinesi, veritabanı sorguları gibi araçları modele bağlayabilirsin
Ortam Kurulumu
Önce gerekli paketleri yükleyelim. Sanal ortam kullanmak iyi pratik, özellikle farklı projeler arasında paket çakışmalarını önlemek için:
# Python sanal ortamı oluştur
python3 -m venv langchain-env
source langchain-env/bin/activate # Linux/macOS
# Windows için: langchain-envScriptsactivate
# Gerekli paketleri yükle
pip install langchain langchain-openai langchain-ollama langchain-community
pip install python-dotenv tiktoken
# Versiyonları kontrol et
pip list | grep -E "langchain|openai|ollama"
Ollama’yı kurmak için:
# Linux'ta Ollama kurulumu
curl -fsSL https://ollama.ai/install.sh | sh
# Ollama servisini başlat
systemctl start ollama
systemctl enable ollama
# Temel modelleri indir
ollama pull llama3.2
ollama pull mistral
ollama pull codellama
# Modellerin çalıştığını test et
ollama list
ollama run llama3.2 "Merhaba, nasılsın?"
Şimdi API anahtarlarını güvenli şekilde yönetmek için bir .env dosyası oluşturalım. Bunu asla git reposuna commit etme:
# .env dosyası oluştur
cat > .env << 'EOF'
OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxxxxxxxxxx
OPENAI_MODEL=gpt-4o-mini
OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_MODEL=llama3.2
EOF
# .gitignore'a ekle
echo ".env" >> .gitignore
Temel Model Bağlantıları
LangChain ile her iki modeli de nasıl başlatacağımıza bakalım. Burada önemli nokta, her iki modelin de aynı arayüzü (interface) paylaşması:
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_ollama import ChatOllama
from langchain_core.messages import HumanMessage, SystemMessage
load_dotenv()
# OpenAI modeli başlat
openai_model = ChatOpenAI(
model=os.getenv("OPENAI_MODEL", "gpt-4o-mini"),
api_key=os.getenv("OPENAI_API_KEY"),
temperature=0.7,
max_tokens=1000,
timeout=30,
max_retries=3
)
# Ollama modeli başlat (yerel)
ollama_model = ChatOllama(
model=os.getenv("OLLAMA_MODEL", "llama3.2"),
base_url=os.getenv("OLLAMA_BASE_URL", "http://localhost:11434"),
temperature=0.7,
num_ctx=4096, # context window boyutu
timeout=120 # yerel modeller daha yavaş olabilir
)
# Test mesajı oluştur
messages = [
SystemMessage(content="Sen yardımcı bir asistansın. Kısa ve net cevaplar ver."),
HumanMessage(content="Python'da liste comprehension nedir? Kısaca açıkla.")
]
# Her iki modeli de test et
print("=== OpenAI Yanıtı ===")
response_openai = openai_model.invoke(messages)
print(response_openai.content)
print("n=== Ollama Yanıtı ===")
response_ollama = ollama_model.invoke(messages)
print(response_ollama.content)
Model Yönlendirici (Router) Yapısı
Gerçek dünya senaryolarında, bazı görevler için ucuz/yerel modeli, bazıları için güçlü bulut modelini kullanmak isteyebilirsin. Örneğin basit sınıflandırma için Ollama yeterli olurken, karmaşık analiz için GPT-4 gerekebilir. İşte bu mantığı otomatize eden bir router:
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from enum import Enum
class TaskComplexity(Enum):
SIMPLE = "simple"
COMPLEX = "complex"
class ModelRouter:
def __init__(self, fast_model, smart_model):
self.fast_model = fast_model # Ollama - ücretsiz, hızlı
self.smart_model = smart_model # OpenAI - güçlü, ücretli
def classify_task(self, query: str) -> TaskComplexity:
"""Görevin karmaşıklığını belirle"""
simple_keywords = [
"ne demek", "tanımla", "kısaca", "özet",
"basit", "nedir", "kaç", "liste"
]
complex_keywords = [
"analiz et", "karşılaştır", "kod yaz", "tasarla",
"debug", "optimize et", "mimari", "strateji"
]
query_lower = query.lower()
simple_score = sum(1 for kw in simple_keywords if kw in query_lower)
complex_score = sum(1 for kw in complex_keywords if kw in query_lower)
if complex_score > simple_score or len(query) > 200:
return TaskComplexity.COMPLEX
return TaskComplexity.SIMPLE
def invoke(self, query: str, force_model: str = None) -> dict:
"""Uygun modeli seç ve çalıştır"""
if force_model == "openai":
model = self.smart_model
model_name = "OpenAI"
elif force_model == "ollama":
model = self.fast_model
model_name = "Ollama"
else:
complexity = self.classify_task(query)
if complexity == TaskComplexity.COMPLEX:
model = self.smart_model
model_name = "OpenAI (complex task)"
else:
model = self.fast_model
model_name = "Ollama (simple task)"
prompt = ChatPromptTemplate.from_messages([
("system", "Sen faydalı bir asistansın."),
("human", "{query}")
])
chain = prompt | model | StrOutputParser()
response = chain.invoke({"query": query})
return {
"model_used": model_name,
"query": query,
"response": response
}
# Router'ı test et
router = ModelRouter(fast_model=ollama_model, smart_model=openai_model)
test_queries = [
"Python nedir?",
"Bir e-ticaret platformu için mikroservis mimarisi tasarla ve güvenlik stratejisini analiz et"
]
for query in test_queries:
result = router.invoke(query)
print(f"Sorgu: {result['query'][:50]}...")
print(f"Kullanılan Model: {result['model_used']}")
print(f"Yanıt: {result['response'][:200]}n")
print("-" * 50)
Fallback Mekanizması
Production ortamında OpenAI API’si zaman zaman yavaşlayabilir veya rate limit hatasıyla karşılaşabilirsin. Bu durumda otomatik olarak Ollama’ya geçiş yapan bir fallback mekanizması hayat kurtarır:
from langchain_core.runnables import RunnableWithFallbacks
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
import time
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def create_resilient_chain(primary_model, fallback_model):
"""
Primary model başarısız olursa fallback model devreye girer.
Bu özellikle OpenAI API kotasını aştığında çok işe yarar.
"""
prompt = ChatPromptTemplate.from_messages([
("system", "Sen bir sistem yöneticisi asistanısın. "
"Teknik sorulara açık ve pratik cevaplar ver."),
("human", "{question}")
])
# Primary chain: OpenAI
primary_chain = prompt | primary_model | StrOutputParser()
# Fallback chain: Ollama
fallback_chain = prompt | fallback_model | StrOutputParser()
# Fallback mekanizmasını bağla
resilient_chain = primary_chain.with_fallbacks(
[fallback_chain],
exceptions_to_handle=(Exception,)
)
return resilient_chain
# Kullanım
resilient_chain = create_resilient_chain(
primary_model=openai_model,
fallback_model=ollama_model
)
# Test et
questions = [
"Nginx'te rate limiting nasıl yapılır?",
"Docker container'lar arası ağ iletişimi nasıl çalışır?"
]
for question in questions:
try:
start_time = time.time()
response = resilient_chain.invoke({"question": question})
elapsed = time.time() - start_time
logger.info(f"Yanıt süresi: {elapsed:.2f}s")
print(f"Soru: {question}")
print(f"Yanıt: {response[:300]}n")
except Exception as e:
logger.error(f"Her iki model de başarısız: {e}")
Konuşma Hafızası ile Chatbot
Sysadmin araçlarında en çok işe yarayan özelliklerden biri konuşma geçmişi. Bir troubleshooting senaryosunda modelin önceki adımları hatırlaması kritik:
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.prompts import MessagesPlaceholder
# Hafıza deposu (production'da Redis veya PostgreSQL kullan)
session_store = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in session_store:
session_store[session_id] = ChatMessageHistory()
return session_store[session_id]
# Sistem yöneticisi chatbot'u oluştur
sysadmin_prompt = ChatPromptTemplate.from_messages([
("system", """Sen deneyimli bir Linux sistem yöneticisisin.
Kullanıcının sorunlarını adım adım çözüyorsun.
Önceki konuşma geçmişini dikkate alarak cevap ver.
Komutları her zaman kod bloğu içinde göster."""),
MessagesPlaceholder(variable_name="history"),
("human", "{input}")
])
# Hangi modeli kullanacağımıza karar ver (üretim vs. geliştirme)
import os
USE_LOCAL = os.getenv("USE_LOCAL_MODEL", "false").lower() == "true"
active_model = ollama_model if USE_LOCAL else openai_model
chain = sysadmin_prompt | active_model | StrOutputParser()
chatbot = RunnableWithMessageHistory(
chain,
get_session_history,
input_messages_key="input",
history_messages_key="history"
)
# Örnek troubleshooting senaryosu
session_id = "troubleshoot-001"
conversation = [
"Sunucumda disk doluluk uyarısı aldım, ne yapmalıyım?",
"Hangi dizinlerin en çok yer kapladığını nasıl bulabilirim?",
"Buldum, /var/log dizini çok büyük. Logları temizlemek güvenli mi?"
]
for message in conversation:
print(f"Kullanıcı: {message}")
response = chatbot.invoke(
{"input": message},
config={"configurable": {"session_id": session_id}}
)
print(f"Asistan: {response}n")
print("-" * 60)
Streaming ile Gerçek Zamanlı Yanıtlar
Uzun yanıtlar beklerken kullanıcıyı boş ekranda bırakmak kötü bir deneyim. Streaming özelliği ile yanıt oluşturulurken kelime kelime ekrana yazdırabilirsin:
# Streaming test için bağımsız script
cat > streaming_test.py << 'EOF'
from langchain_openai import ChatOpenAI
from langchain_ollama import ChatOllama
from langchain_core.messages import HumanMessage
import sys
def stream_response(model, query: str, model_name: str):
print(f"n[{model_name}] Yanıt akışı başlıyor...n")
print("-" * 40)
full_response = ""
for chunk in model.stream([HumanMessage(content=query)]):
if chunk.content:
print(chunk.content, end="", flush=True)
full_response += chunk.content
print("n" + "-" * 40)
print(f"Toplam karakter: {len(full_response)}")
return full_response
# Test
from langchain_openai import ChatOpenAI
from langchain_ollama import ChatOllama
openai_m = ChatOpenAI(model="gpt-4o-mini", streaming=True)
ollama_m = ChatOllama(model="llama3.2")
query = "Linux'ta bir process'in tüm açık dosyalarını listeleyen komutu açıkla."
# Her iki modeli de stream et
stream_response(openai_m, query, "OpenAI GPT-4o-mini")
stream_response(ollama_m, query, "Ollama LLaMA 3.2")
EOF
python3 streaming_test.py
Log Analizi İçin Pratik Bir Araç
Şimdi gerçekten işe yarar bir şey yapalım: Sunucu loglarını analiz eden ve sorunları tespit eden bir araç. Bu senaryo için hem modeli hem de araçları birlikte kullanacağız:
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
import subprocess
import re
@tool
def read_system_logs(log_file: str, lines: int = 100) -> str:
"""Sistem log dosyasını okur ve son N satırı döndürür."""
try:
result = subprocess.run(
["tail", f"-{lines}", log_file],
capture_output=True,
text=True,
timeout=10
)
if result.returncode == 0:
return result.stdout
return f"Hata: {result.stderr}"
except Exception as e:
return f"Log okuma hatası: {str(e)}"
@tool
def check_disk_usage(path: str = "/") -> str:
"""Belirtilen path için disk kullanımını kontrol eder."""
try:
result = subprocess.run(
["df", "-h", path],
capture_output=True,
text=True,
timeout=5
)
return result.stdout
except Exception as e:
return f"Disk kontrol hatası: {str(e)}"
@tool
def count_error_patterns(log_content: str, pattern: str) -> str:
"""Log içeriğinde belirli bir pattern'i arar ve sayar."""
matches = re.findall(pattern, log_content, re.IGNORECASE)
unique_matches = list(set(matches))
return (
f"Toplam eşleşme: {len(matches)}n"
f"Benzersiz eşleşmeler: {len(unique_matches)}n"
f"Örnekler: {unique_matches[:5]}"
)
# Agent kurulumu
tools = [read_system_logs, check_disk_usage, count_error_patterns]
agent_prompt = ChatPromptTemplate.from_messages([
("system", """Sen bir Linux sistem yöneticisi asistanısın.
Sistem loglarını analiz edip sorunları tespit ediyorsun.
Kullandığın araçları ve bulgularını açıkça belirt.
Kritik sorunlar için acil aksiyon öner."""),
("human", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad")
])
# OpenAI tool calling için daha iyi destek sunuyor
llm_with_tools = ChatOpenAI(
model="gpt-4o-mini",
temperature=0
).bind_tools(tools)
agent = create_tool_calling_agent(
llm=ChatOpenAI(model="gpt-4o-mini", temperature=0),
tools=tools,
prompt=agent_prompt
)
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True,
max_iterations=5,
handle_parsing_errors=True
)
# Örnek kullanım
result = agent_executor.invoke({
"input": "Sistem disk kullanımını kontrol et ve kritik durum var mı değerlendir."
})
print("n=== Analiz Sonucu ===")
print(result["output"])
Model Performans Karşılaştırması ve İzleme
Hangi modelin ne kadar sürede yanıt verdiğini ve maliyetini takip etmek, özellikle production ortamında önemli:
import time
import json
from datetime import datetime
from langchain.callbacks.base import BaseCallbackHandler
from langchain_core.outputs import LLMResult
class PerformanceTracker(BaseCallbackHandler):
"""Model performansını takip eden callback handler"""
def __init__(self):
self.metrics = []
self.start_time = None
def on_llm_start(self, serialized, prompts, **kwargs):
self.start_time = time.time()
def on_llm_end(self, response: LLMResult, **kwargs):
elapsed = time.time() - self.start_time
# Token kullanımını al (OpenAI için)
token_usage = {}
if response.llm_output and "token_usage" in response.llm_output:
token_usage = response.llm_output["token_usage"]
metric = {
"timestamp": datetime.now().isoformat(),
"latency_seconds": round(elapsed, 3),
"prompt_tokens": token_usage.get("prompt_tokens", 0),
"completion_tokens": token_usage.get("completion_tokens", 0),
"total_tokens": token_usage.get("total_tokens", 0)
}
self.metrics.append(metric)
print(f"[Perf] Yanıt süresi: {elapsed:.3f}s | "
f"Toplam token: {token_usage.get('total_tokens', 'N/A')}")
def save_metrics(self, filepath: str):
with open(filepath, "w") as f:
json.dump(self.metrics, f, indent=2)
print(f"Metrikler kaydedildi: {filepath}")
# Tracker'ları oluştur
openai_tracker = PerformanceTracker()
ollama_tracker = PerformanceTracker()
# Modelleri tracker ile başlat
tracked_openai = ChatOpenAI(
model="gpt-4o-mini",
callbacks=[openai_tracker]
)
tracked_ollama = ChatOllama(
model="llama3.2",
callbacks=[ollama_tracker]
)
# Benchmark sorguları çalıştır
benchmark_queries = [
"Bash script'te hata yönetimi nasıl yapılır?",
"Docker Compose ile multi-container uygulama nasıl yönetilir?"
]
from langchain_core.messages import HumanMessage
for query in benchmark_queries:
print(f"nSorgu: {query[:60]}...")
print("OpenAI:")
r1 = tracked_openai.invoke([HumanMessage(content=query)])
print("Ollama:")
r2 = tracked_ollama.invoke([HumanMessage(content=query)])
# Metrikleri kaydet
tracked_openai.callbacks[0].save_metrics("/tmp/openai_metrics.json")
tracked_ollama.callbacks[0].save_metrics("/tmp/ollama_metrics.json")
# Özet rapor
print("n=== Performans Özeti ===")
for metric in openai_tracker.metrics:
print(f"OpenAI - Süre: {metric['latency_seconds']}s, "
f"Token: {metric['total_tokens']}")
for metric in ollama_tracker.metrics:
print(f"Ollama - Süre: {metric['latency_seconds']}s")
Production Ortamında Dikkat Edilmesi Gerekenler
Gerçek bir ortama geçmeden önce bilmen gereken birkaç kritik nokta var:
Rate Limiting ve Quota Yönetimi
- OpenAI API’sinin dakika başına token limiti var; bunu aşarsan 429 hatası alırsın
- Ollama’da donanım kapasiteni aşarsan bellek hataları çıkabilir
tenacitykütüphanesi ile exponential backoff uygula- Üretim ortamında API anahtarlarını HashiCorp Vault veya AWS Secrets Manager’da sakla
Güvenlik
- Kullanıcı girdilerini doğrudan modele geçirmeden önce sanitize et
- Prompt injection saldırılarına karşı dikkatli ol
- Ollama’yı dışarıya açmak zorundaysan en azından Nginx reverse proxy ve auth middleware ekle
- API anahtarlarını loglara düşürme
Maliyet Optimizasyonu
- Kısa ve tekrarlayan sorgular için Ollama’yı birincil seçenek yap
- Yanıtları cache’lemek için Redis kullanmayı düşün; aynı soruyu tekrar API’ye gönderme
gpt-4o-minigenelliklegpt-4o‘nun yüzde onu maliyetiyle benzer sonuçlar veriyor- Token sayısını minimize etmek için sistem promptlarını verimli yaz
Monitoring
- LangSmith (LangChain’in kendi monitoring aracı) ile trace topla
- Prometheus ve Grafana ile latency ve hata oranlarını izle
- Her model çağrısını bir request ID ile logla; troubleshoot etmek kolaylaşır
Sonuç
LangChain ile OpenAI ve Ollama’yı birlikte kullanmak, hem esneklik hem de maliyet açısından güçlü bir strateji sunuyor. Geliştirme aşamasında Ollama ile sınırsız denemeler yapabilir, production’da hassas veya karmaşık görevler için OpenAI’ye geçebilirsin. Üstelik fallback mekanizmaları sayesinde sistem bir model hata verse bile çalışmaya devam eder.
Sysadmin olarak bu araçları log analizi, otomatik raporlama, runbook asistanları veya monitoring sistemlerine entegrasyon için kullanabilirsin. Önemli olan noktaları tekrar özetleyecek olursak: model seçimini iş gereksinimlerine göre yap, her zaman fallback planı hazırla, maliyet ve performansı izle, güvenliği asla ikinci plana atma.
Sonraki adım olarak LangChain’in RAG (Retrieval Augmented Generation) yeteneklerine bakmanı öneririm. Kendi dökümanlarını, runbook’larını veya log geçmişini modele context olarak besleyebilirsin. Bu, modellerle çalışmayı tamamen farklı bir seviyeye taşır.
