Dinamik Yapay Zeka Promptları: LangChain Template Sistemi
Bir AI uygulaması geliştirirken en sık karşılaşılan sorunlardan biri şudur: Kullanıcıdan gelen farklı girdileri alıp bunları tutarlı, kontrollü ve verimli bir şekilde dil modeline nasıl iletirsiniz? Ham string birleştirme ile başlarsınız, sonra f-string’lere geçersiniz, ardından her şey karmaşık bir hal alır ve bakımı imkansız bir kod yığınıyla baş başa kalırsınız. LangChain’in template sistemi tam bu noktada devreye girer ve prompt yönetimini profesyonel bir seviyeye taşır.
Neden Template Sistemi Kullanmalısınız?
Düşünün ki bir müşteri destek botu geliştiriyorsunuz. Farklı dillerde, farklı tonlarda ve farklı bağlamlarda yanıtlar üretmeniz gerekiyor. Her senaryo için ayrı prompt yazmak hem zaman kaybı hem de tutarsızlık kaynağıdır. Üstüne üstlük, prompt’u değiştirmeniz gerektiğinde kodun dört bir yanını karıştırmanız gerekir.
Template sistemi bu problemi şu şekilde çözer:
- Yeniden kullanılabilirlik: Aynı template’i farklı değişkenlerle defalarca kullanırsınız
- Bakım kolaylığı: Prompt mantığını tek bir yerde tutarsınız
- Tip güvenliği: Hangi değişkenlerin gerekli olduğunu açıkça tanımlarsınız
- Kompozisyon: Küçük template parçalarını birleştirerek büyük yapılar oluşturursunuz
- Test edilebilirlik: Template’leri bağımsız olarak test edebilirsiniz
Kurulum ve Temel Yapı
LangChain ile çalışmaya başlamadan önce ortamınızı hazırlayın:
pip install langchain langchain-openai langchain-core python-dotenv
Temel bir PromptTemplate kullanımına bakalım:
from langchain_core.prompts import PromptTemplate
# Basit bir template tanımı
template = PromptTemplate(
input_variables=["urun_adi", "hedef_kitle", "ton"],
template="""
Sen bir {ton} tonunda yazan bir pazarlama uzmanısın.
Aşağıdaki ürün için {hedef_kitle} kitlesine yönelik
kısa bir tanıtım metni yaz:
Ürün: {urun_adi}
Metin 3 cümleyi geçmesin ve güçlü bir call-to-action ile bitsin.
"""
)
# Template'i formatlama
formatted_prompt = template.format(
urun_adi="ErgoDesk Pro",
hedef_kitle="uzaktan çalışan profesyoneller",
ton="profesyonel ama samimi"
)
print(formatted_prompt)
Bu basit örnek bile ham string’e göre çok daha okunabilir. Ama asıl güç, bu template’leri chain’lere bağladığınızda ortaya çıkar.
ChatPromptTemplate ile Konuşma Bazlı Prompt’lar
Modern LLM uygulamalarının büyük çoğunluğu chat modelleri kullanır. GPT-4, Claude veya Gemini gibi modeller mesaj bazlı bir yapıyla çalışır. ChatPromptTemplate tam da bu ihtiyaç için tasarlanmıştır:
from langchain_core.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
from langchain_openai import ChatOpenAI
# Sistem mesajı template'i
sistem_template = SystemMessagePromptTemplate.from_template(
"""Sen {sirket_adi} için çalışan bir {rol} asistanısın.
Uzmanlık alanın: {uzmanlik}
Yanıtlarında her zaman {dil} kullan.
Belirsiz sorularda kullanıcıdan netleştirme iste."""
)
# Kullanıcı mesajı template'i
kullanici_template = HumanMessagePromptTemplate.from_template(
"{kullanici_sorusu}"
)
# Chat prompt template'i oluşturma
chat_prompt = ChatPromptTemplate.from_messages([
sistem_template,
kullanici_template
])
# Kullanım
mesajlar = chat_prompt.format_messages(
sirket_adi="TechCorp",
rol="teknik destek",
uzmanlik="bulut altyapısı ve DevOps",
dil="Türkçe",
kullanici_sorusu="Kubernetes pod'um neden CrashLoopBackOff durumunda?"
)
# Modele gönderme
model = ChatOpenAI(model="gpt-4o", temperature=0.7)
yanit = model.invoke(mesajlar)
print(yanit.content)
Sistemi ve kullanıcı mesajını birbirinden ayırmanın ne kadar temiz bir yapı ortaya çıkardığına dikkat edin. Sistem prompt’unu değiştirmek istediğinizde sadece o kısmı güncellemeniz yeterli.
Partial Templates: Kısmi Doldurma
Gerçek dünyada bazı değişkenler sabit, bazıları dinamiktir. Örneğin şirket bilgileri her zaman aynıdır ama kullanıcı sorusu her seferinde değişir. Partial template’ler bu durumu zarif bir şekilde çözer:
from langchain_core.prompts import PromptTemplate
from datetime import datetime
# Tarih bilgisini otomatik ekleyen partial template
temel_template = PromptTemplate(
input_variables=["tarih", "kullanici_adi", "soru"],
template="""
Tarih: {tarih}
Merhaba {kullanici_adi},
Sorunuz: {soru}
Lütfen güncel bilgilerle yanıt verin ve tarih bağlamını göz önünde bulundurun.
"""
)
# Tarihi sabit olarak bağla, diğerleri dinamik kalsın
tarihli_template = temel_template.partial(
tarih=lambda: datetime.now().strftime("%d %B %Y, %H:%M")
)
# Artık sadece kullanici_adi ve soru gerekiyor
hazir_prompt = tarihli_template.format(
kullanici_adi="Ahmet",
soru="Redis cache invalidation stratejileri nelerdir?"
)
print(hazir_prompt)
Bu yaklaşım özellikle şunlar için mükemmeldir:
- Ortam bilgileri: Production/staging/dev gibi sabit bağlam bilgileri
- Şirket politikaları: Her prompt’a eklenmesi gereken sabit kurallar
- Kullanıcı profili: Oturum boyunca değişmeyen kullanıcı tercihleri
FewShotPromptTemplate ile Örnek Bazlı Öğretim
Dil modellerini belirli bir çıktı formatına yönlendirmenin en etkili yollarından biri few-shot örnekler vermektir. LangChain bunu sistematik bir yapıya kavuşturur:
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
# Her örnek için kullanılacak template
ornek_template = PromptTemplate(
input_variables=["giris", "cikis"],
template="Giriş: {giris}nÇıkış: {cikis}n"
)
# Log analizi için örnekler
log_ornekleri = [
{
"giris": "ERROR 2024-01-15 14:23:11 - Connection refused to 192.168.1.100:5432",
"cikis": "SORUN: Veritabanı bağlantı hatası | OLASI_NEDEN: PostgreSQL servisi çalışmıyor veya firewall engeli | ONCELIK: Kritik"
},
{
"giris": "WARN 2024-01-15 14:25:00 - High memory usage detected: 87%",
"cikis": "SORUN: Yüksek bellek kullanımı | OLASI_NEDEN: Memory leak veya yoğun trafik | ONCELIK: Yüksek"
},
{
"giris": "INFO 2024-01-15 14:26:30 - Scheduled backup completed successfully",
"cikis": "SORUN: Yok | OLASI_NEDEN: - | ONCELIK: Bilgi"
}
]
# Few-shot template oluşturma
log_analiz_template = FewShotPromptTemplate(
examples=log_ornekleri,
example_prompt=ornek_template,
prefix="Aşağıdaki log satırlarını analiz et ve belirtilen formatta yanıt ver:",
suffix="Giriş: {log_satiri}nÇıkış:",
input_variables=["log_satiri"]
)
# Kullanım
analiz_sonucu = log_analiz_template.format(
log_satiri="ERROR 2024-01-15 15:00:00 - Disk space critically low: 95% used on /var"
)
print(analiz_sonucu)
Bu yapıyı bir monitoring sistemine entegre ettiğinizde, modeliniz artık tutarlı ve parse edilebilir çıktılar üretecektir.
Pipeline’da Template Kullanımı: LCEL ile Zincirleme
LangChain Expression Language (LCEL) ile template’leri, modelleri ve output parser’ları zincirleme şekilde bağlayabilirsiniz. Bu modern LangChain’in kalbidir:
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# Kod review için template
kod_review_template = ChatPromptTemplate.from_messages([
("system", """Sen {dil} konusunda uzman bir senior developer'sın.
Kodu güvenlik, performans ve best practices açısından incele.
Yanıtını şu formatta ver:
- SORUNLAR: (varsa listele)
- IYILESTİRMELER: (öneriler)
- PUAN: (1-10 arası)"""),
("human", "Şu kodu inceler misin?nn```{dil}n{kod}n```")
])
model = ChatOpenAI(model="gpt-4o", temperature=0.3)
parser = StrOutputParser()
# Chain oluşturma
review_chain = kod_review_template | model | parser
# Çalıştırma
sonuc = review_chain.invoke({
"dil": "Python",
"kod": """
def kullanici_getir(user_id):
query = "SELECT * FROM users WHERE id = " + user_id
return db.execute(query)
"""
})
print(sonuc)
Bu örnekte SQL injection açığı olan bir kodu modele gönderdik. Zincirin temiz yapısına bakın: template, model ve parser birbirinden bağımsız ama bir arada çalışıyor.
Dinamik Example Selector ile Akıllı Few-Shot
Sabit örnekler vermek bazen yeterli değildir. Kullanıcının girdisine göre en alakalı örnekleri seçmek çok daha etkilidir:
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
from langchain_core.example_selectors import SemanticSimilarityExampleSelector
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
# Geniş bir örnek havuzu
ornek_havuzu = [
{"komut": "nginx restart", "aciklama": "Nginx web sunucusunu yeniden başlatır"},
{"komut": "systemctl status postgresql", "aciklama": "PostgreSQL servisinin durumunu gösterir"},
{"komut": "df -h", "aciklama": "Disk kullanımını okunabilir formatta listeler"},
{"komut": "netstat -tulpn", "aciklama": "Açık portları ve dinleyen servisleri gösterir"},
{"komut": "journalctl -u nginx -n 100", "aciklama": "Nginx'in son 100 log satırını gösterir"},
{"komut": "top -bn1", "aciklama": "Anlık sistem kaynak kullanımını gösterir"},
{"komut": "ss -s", "aciklama": "Socket istatistiklerini özetler"},
]
# Örnek template'i
ornek_fmt = PromptTemplate(
input_variables=["komut", "aciklama"],
template="Komut: {komut}nAçıklama: {aciklama}"
)
# Semantik benzerlik ile örnek seçici
ornek_secici = SemanticSimilarityExampleSelector.from_examples(
ornek_havuzu,
OpenAIEmbeddings(),
Chroma,
k=3 # En benzer 3 örneği seç
)
# Dinamik few-shot template
dinamik_template = FewShotPromptTemplate(
example_selector=ornek_secici,
example_prompt=ornek_fmt,
prefix="Linux komutlarını açıkla. İşte bazı örnekler:",
suffix="Komut: {komut}nAçıklama:",
input_variables=["komut"]
)
# Ağ ile ilgili bir sorgu - ağ örneklerini seçecek
sonuc = dinamik_template.format(komut="iptables -L -n -v")
print(sonuc)
Bu yaklaşım özellikle büyük örnek havuzlarında çok değerlidir. Yüzlerce örneğiniz olabilir ama modele her seferinde sadece en alakalı birkaçını gönderirsiniz, bu da hem maliyet hem de kalite açısından kazanç sağlar.
Gerçek Dünya Senaryosu: Çok Dilli Destek Sistemi
Şimdi tüm öğrendiklerimizi bir araya getirerek gerçekçi bir senaryo oluşturalım. Bir SaaS ürünü için çok dilli müşteri destek sistemi:
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.runnables import RunnableLambda, RunnableParallel
import json
# Kullanıcı profilinden dil ve tonunu belirleyen fonksiyon
def profil_isle(data):
profil = data.get("kullanici_profili", {})
return {
**data,
"dil": profil.get("tercih_dil", "Türkçe"),
"abonelik": profil.get("abonelik_turu", "Ücretsiz"),
"onceki_sorunlar": profil.get("onceki_sorunlar", 0)
}
# Destek template'i - abonelik tipine göre ton ayarlanabilir
destek_template = ChatPromptTemplate.from_messages([
("system", """Sen {sirket} müşteri destek uzmanısın.
Kullanıcı Bilgileri:
- Tercih edilen dil: {dil}
- Abonelik tipi: {abonelik}
- Önceki destek talepleri: {onceki_sorunlar}
Kurallar:
- Her zaman {dil} dilinde yanıt ver
- Premium kullanıcılara öncelikli ve detaylı yanıt ver
- Çözüm adımlarını numaralandır
- Gerekirse escalation öner
Yanıtı JSON formatında ver:
{{"yanit": "...", "cozum_adimlari": [...], "escalation_gerekli": true/false}}"""),
("human", "{soru}")
])
model = ChatOpenAI(model="gpt-4o", temperature=0.5)
parser = JsonOutputParser()
# Pipeline oluşturma
destek_pipeline = (
RunnableLambda(profil_isle)
| destek_template
| model
| parser
)
# Test
test_input = {
"sirket": "CloudStack",
"kullanici_profili": {
"tercih_dil": "Türkçe",
"abonelik_turu": "Premium",
"onceki_sorunlar": 2
},
"soru": "API anahtarım çalışmıyor, 401 hatası alıyorum"
}
yanit = destek_pipeline.invoke(test_input)
print(json.dumps(yanit, ensure_ascii=False, indent=2))
Bu sistemin güzelliği şu: Farklı bir şirket için aynı pipeline’ı kullanmak istediğinizde sadece sirket parametresini değiştirmeniz yeterli. Template değişmez, mantık değişmez.
Template Versiyonlama ve Yönetim
Üretim ortamında prompt’larınızı dosya sisteminde veya veritabanında saklamak ve versiyonlamak isteyeceksiniz:
import yaml
from pathlib import Path
from langchain_core.prompts import ChatPromptTemplate
# templates/destek_v2.yaml dosyası içeriği
TEMPLATE_YAML = """
version: "2.1"
description: "Müşteri destek template'i - geliştirilmiş empati"
template:
system: |
Sen {sirket} için empati odaklı bir destek uzmanısın.
Önce kullanıcının durumunu anladığını göster, sonra çözüm sun.
Teknik bilgi seviyesi: {teknik_seviye}
human: "{soru}"
metadata:
created_by: "platform-team"
last_tested: "2024-01-15"
avg_satisfaction: 4.7
"""
def template_yukle(yaml_icerik: str) -> dict:
"""YAML'dan template yükler ve metadata'yı korur"""
veri = yaml.safe_load(yaml_icerik)
template = ChatPromptTemplate.from_messages([
("system", veri["template"]["system"]),
("human", veri["template"]["human"])
])
return {
"template": template,
"version": veri["version"],
"metadata": veri.get("metadata", {})
}
# Template'i yükle ve kullan
yuklenen = template_yukle(TEMPLATE_YAML)
print(f"Template v{yuklenen['version']} yüklendi")
print(f"Ortalama memnuniyet: {yuklenen['metadata']['avg_satisfaction']}")
# Kullanım
prompt = yuklenen["template"].format_messages(
sirket="DataPipe",
teknik_seviye="orta",
soru="Pipeline'ım timeout veriyor, ne yapmalıyım?"
)
Bu yaklaşım ekip çalışmasında çok değerlidir. Farklı kişiler farklı template versiyonlarını test edebilir, A/B testi yapabilir ve en iyi performans göstereni production’a taşıyabilir.
Performans ve Maliyet Optimizasyonu
Template’lerin doğru kullanımı token maliyetlerini ciddi ölçüde düşürür. Şu noktalara dikkat edin:
- Gereksiz tekrardan kaçının: Sistem prompt’unu her mesajda tekrarlamayın, conversation history yapısını kullanın
- Dinamik uzunluk kontrolü: Template’e gelecek değişkenlerin maximum uzunluğunu sınırlayın
- Few-shot örnek sayısını optimize edin: 2-3 örnek genellikle 10 örnekle aynı kaliteyi verir, dörtte bir maliyetle
- Partial template’leri önbelleğe alın: Sabit kısımları her seferinde yeniden oluşturmayın
Template sistemi sadece kod organizasyonu için değil, token ekonomisi için de kritik bir araçtır.
Sonuç
LangChain’in template sistemi, yapay zeka uygulamalarındaki prompt karmaşasını çözen olgun bir çerçeve sunar. PromptTemplate ile başlayan yolculuk, ChatPromptTemplate, FewShotPromptTemplate ve dinamik example selector’larla devam eder. LCEL ile bu template’leri chain’lere bağladığınızda gerçek anlamda bakımı kolay, ölçeklenebilir sistemler kurabilirsiniz.
Pratikte önerim şu şekildedir: Yeni bir proje başlarken önce template yapınızı tasarlayın, sonra model seçimine geçin. Hangi değişkenlerin sabit, hangilerinin dinamik olduğunu erken belirlemek sonraki refactoring acısını önler. Template’lerinizi YAML veya benzeri bir formatta saklayın, bu sayede kod değişikliği yapmadan prompt’larınızı güncelleyebilirsiniz.
Son olarak, template sistemi bir standart getirir. Ekibinizdeki her geliştirici prompt’ların nerede ve nasıl tanımlandığını bilir, yeni birisi projeye katıldığında kaybolmaz. Bu da uzun vadede en büyük kazanımdır.
