İçeriğe geç

Meta-Prompting ve Otomatik Prompt Optimizasyonu: DSPy Rehberi

Kırılgan elle yazılmış promptların ötesine geçmek: meta-prompting ve DSPy ile programatik, ölçüye dayalı otomatik prompt optimizasyonunun pratik rehberi.

SYK
Şükrü Yusuf KAYA
AI Expert · Kurumsal AI Danışmanı

TL;DR — Elle yazdığınız prompt'lar bir modelde harika çalışıp diğerinde çuvallıyorsa, her yeni kullanım senaryosunda baştan "prompt mühendisliği" yapıyorsanız ve ekip içindeki her kişi aynı görev için farklı bir prompt yazıyorsa, bu yazı tam size göre. Meta-prompting, bir LLM'i başka bir LLM'in prompt'unu yazması, eleştirmesi ve iyileştirmesi için kullanmak demek. Otomatik prompt optimizasyonu ise bunu sistematik hale getirip, elinizdeki küçük bir etiketli veri seti ve bir metrik üzerinden prompt varyantlarını arayıp en iyisini bulmak. DSPy, bu fikri bir programlama modeline dönüştüren açık kaynak bir framework: prompt yazmak yerine "signature" ile ne istediğinizi tanımlıyorsunuz, "module" ile nasıl akıl yürüteceğini seçiyorsunuz, "optimizer" (teleprompter) ile de programınızı elinizdeki veri ve metriğe göre otomatik "compile" ediyorsunuz. Bu yazıda hem kavramsal çerçeveyi hem DSPy'ın somut yapı taşlarını hem de kurumsal bir ekibin bunu nasıl adım adım devreye alacağını, nelere dikkat etmesi gerektiğini (overfitting, maliyet, tekrarlanabilirlik, KVKK) anlatıyorum.

Neden bu konuyu yazıyorum

Son iki yıldır kurumsal eğitimlerde ve danışmanlık projelerinde tekrar tekrar aynı sahneye tanık oluyorum: Bir ekip üyesi haftalarca uğraşıp mükemmel bir prompt buluyor, sistem canlıya alınıyor, herkes memnun. Sonra model sağlayıcısı bir güncelleme yapıyor, ya da ekip GPT-4 sınıfı bir modelden daha ucuz bir modele geçmeye karar veriyor, ya da basitçe girdi verisinin dağılımı biraz değişiyor — ve o "mükemmel" prompt aniden tutarsız cevaplar üretmeye başlıyor. Ekip tekrar prompt'u açar, birkaç cümle ekler, birkaç örnek çıkarır, dener, umut eder. Bu döngü bana her seferinde şunu hatırlatıyor: elle prompt yazmak, esasen gözle regresyon test etmeye benziyor. Çalışıyor, ama ölçeklenmiyor ve kırılgan.

Bu yazıda anlatacağım şey bir "sihirli değnek" değil. Meta-prompting ve otomatik prompt optimizasyonu, prompt yazma işini el işçiliğinden çıkarıp, en azından kısmen, ölçülebilir ve tekrarlanabilir bir mühendislik disiplinine taşıma girişimi. Bunun en olgun, en somut aracı da bugün itibarıyla DSPy. Bu yazıda hem "neden" hem "nasıl" kısmına eşit ağırlık vermeye çalışacağım, çünkü sahada gördüğüm en büyük hata, ekiplerin bu araçları "gerekçesiz" biçimde, sadece trend olduğu için devreye almaya çalışması.

Elle yazılmış prompt'ların temel sorunu

Prompt mühendisliğini küçümsemiyorum — iyi bir prompt hâlâ değerli, hâlâ gerekli. Ama elle yazılmış, "hardcoded" prompt'ların yapısal olarak taşıdığı birkaç zayıflık var:

Model bağımlılığı. Bir modele özgü ipuçları ("Adım adım düşün", belirli bir formatlama tarzı, belirli kelime seçimleri) başka bir modelde işe yaramayabilir, hatta zarar verebilir. Bir modelin çok iyi tepki verdiği "Sen uzman bir..." kalıbı başka bir modelde gereksiz token israfına dönüşebilir. Bu, çoklu model stratejisi olan (maliyet optimizasyonu için farklı görevlerde farklı model kullanan) kurumsal ekipler için ciddi bir bakım yükü.

Görev bağımlılığı. Bir sınıflandırma görevi için optimize edilmiş prompt yapısı, bir özetleme görevine doğrudan taşınamaz. Her yeni görev, sıfırdan deneme-yanılma demek.

Görünmez bağlam kaybı. Prompt'u yazan kişi ayrılınca, o prompt'un neden şu anki haliyle olduğu — hangi denemeler başarısız oldu, hangi kenar durum için şu cümle eklendi — genelde kaybolur. Prompt bir "tarihsiz commit" gibi kalır.

Ölçek sorunu. Elle prompt iyileştirme, bir kişinin dikkatiyle sınırlı. Onlarca örnek üzerinde "bu daha iyi görünüyor" diyerek ilerleyebilirsiniz ama yüzlerce örnek üzerinde sistematik, istatistiksel olarak anlamlı bir karşılaştırma yapmak insan gözüyle pratik değil.

A/B testinin prompt seviyesinde yokluğu. Web ürünlerinde her buton rengini A/B test ederiz ama prompt'ları çoğu zaman "bana iyi geldi" sezgisiyle canlıya alırız. Oysa prompt da bir üründür ve ölçülmesi gerekir.

Bu dört sorunun ortak paydası şu: prompt yazmak, esasen bir optimizasyon problemi. Elimizde bir arama uzayı var (mümkün prompt varyantları), bir hedef fonksiyon var (görevi ne kadar iyi yaptığı) ve bir kısıt var (token maliyeti, gecikme). İnsan beyni bu aramayı elle, sınırlı örneklemle ve yavaş yapıyor. Soru şu: bu aramayı kısmen otomatikleştirebilir miyiz?

Meta-prompting: LLM'i LLM üzerinde kullanmak

Meta-prompting, en basit tanımıyla, bir dil modelini başka bir dil modeli için (ya da kendisi için) prompt üretme, eleştirme veya iyileştirme amacıyla kullanmaktır. Fikir yeni değil — "prompt yazmak için bir prompt yazmak" kavramı ChatGPT'nin yaygınlaşmasından kısa süre sonra pratikte ortaya çıktı ve akademik olarak da çeşitli çalışmalarda formalize edildi (örneğin "Automatic Prompt Engineer" gibi çalışmalar, bir LLM'e talimat üretmesini ve bu talimatları bir metrik üzerinden puanlamasını önerir).

Pratikte meta-prompting birkaç biçimde karşımıza çıkar:

1. Üretici meta-prompting. Modele görev tanımını (girdi-çıktı örnekleri, kısıtlar) veriyorsunuz, model size bu görev için bir sistem prompt'u taslağı üretiyor. Örneğin: "Aşağıdaki 10 müşteri şikâyeti ve bunlara verilmiş ideal kısa yanıtları inceleyerek, bu tarz yanıtları tutarlı biçimde üretecek bir sistem prompt'u yaz."

2. Eleştirel meta-prompting (self-critique / reflexion). Model önce bir cevap üretir, sonra kendisine (ya da başka bir modele) "bu cevap nerede zayıf, hangi talimat eksikliğinden dolayı hata yaptı" diye sorulur, çıkan eleştiri prompt'a geri beslenir. Bu, "Reflexion" ve benzeri çalışmalarda görülen bir örüntü.

3. Karşılaştırmalı meta-prompting. İki ya da daha fazla prompt varyantı aynı girdilerle çalıştırılır, bir "hakem" model (ya da otomatik metrik) hangisinin daha iyi olduğuna karar verir, kazanan varyant temel alınarak yeni varyantlar üretilir. Bu, genetik algoritmalara benzer bir "mutasyon + seçilim" döngüsüdür.

Meta-prompting'in gücü şurada: prompt yazma işini kısmen modele devrederek, insanın gözden kaçırabileceği ifade varyasyonlarını (kelime sırası, örnek seçimi, talimatın nerede durduğu) modelin kendisinin denemesini sağlıyorsunuz. Ama meta-prompting tek başına bir süreç değil, bir tekniktir. Bunu sistematik, ölçülebilir bir döngüye oturtmak için otomatik prompt optimizasyonu kavramına ihtiyacımız var.

Otomatik prompt optimizasyonu: arama + metrik

Otomatik prompt optimizasyonunun temel bileşenleri üç tanedir:

  1. Bir arama uzayı — deneyebileceğiniz prompt/talimat/örnek kombinasyonlarının kümesi.
  2. Bir metrik (ödül fonksiyonu) — bir prompt varyantının ne kadar "iyi" olduğunu ölçen, otomatik hesaplanabilen bir fonksiyon (doğruluk, F1, bir LLM-hakem puanı, kural tabanlı bir kontrol vb.).
  3. Bir arama stratejisi — bu uzayı nasıl tarayacağınız: rastgele deneme, tepe tırmanma (hill climbing), ışın araması (beam search), Bayes optimizasyonu, ya da bir meta-LLM'in önerilerine dayalı yönlendirilmiş arama.

Mantık şu: elinizde küçük ama temsil edici bir etiketli veri seti (train/dev split) olduğunu varsayalım — mesela 50-200 örnek, her biri bir girdi ve o girdi için "doğru" ya da "kabul edilebilir" çıktı içeriyor. Otomatik optimizasyon süreci, farklı prompt varyantlarını bu veri seti üzerinde çalıştırır, metrik ile puanlar, en iyi performans gösteren varyant(lar)ı seçer ya da bu varyantlardan yola çıkarak yenilerini üretir. Bu, klasik makine öğrenmesindeki hiperparametre aramasına çok benzer — sadece burada "hiperparametre" bir sayı değil, bir metin (talimat cümlesi, few-shot örnek seçimi, akıl yürütme formatı).

Burada kritik bir nokta var: otomatik prompt optimizasyonu, elle prompt yazmanın yerine geçen bir "sihir" değil, elinizdeki değerlendirme altyapısının kalitesi kadar iyi çalışan bir arama motorudur. Metriğiniz zayıfsa (örneğin gerçek iş hedefini yansıtmıyorsa), optimizasyon süreci "metriğe göre mükemmel ama gerçek dünyada işe yaramaz" bir prompt bulur. Bu yüzden bu konuyu değerlendirme (evaluation) konusundan ayrı düşünmek mümkün değil — birazdan buna geri döneceğim.

DSPy nedir, neden farklı bir zihniyet önerir

DSPy (Stanford NLP grubunun geliştirdiği, açık kaynak bir Python framework'ü), prompt mühendisliğine "string düzenleme" olarak değil, "program yazma ve derleme" olarak yaklaşmamızı öneriyor. İsim de bunu ima ediyor: "Declarative Self-improving Python" (bazı kaynaklarda "programlama, prompting değil" sloganıyla anılır). Zihniyet farkı şu:

  • Geleneksel yaklaşım: "Şu prompt string'ini yaz, dene, elle düzelt, tekrar dene."
  • DSPy yaklaşımı: "Görevin girdi-çıktı davranışını bildir (signature). Bu davranışı hangi akıl yürütme stratejisiyle gerçekleştireceğini seç (module). Elindeki küçük bir örnek seti ve bir metrikle, bu programı otomatik olarak 'compile' et — yani optimizer, senin yerine talimatları ve few-shot örneklerini ayarlasın."

Bu, yazılım mühendisliğindeki "prompt'u kaynak kod, LLM çıktısını derlenmiş binary" gibi düşünmek gibi bir analojiye benziyor. Siz yüksek seviyeli niyeti tanımlıyorsunuz, "derleyici" (optimizer) düşük seviyeli detayları (tam talimat metni, hangi örneklerin gösterileceği, sıralaması) sizin yerinize buluyor.

Şimdi DSPy'ın üç temel yapı taşına tek tek girelim: signature, module, optimizer (teleprompter).

1. Signature: davranışı bildirmek, nasıl yapılacağını değil

Bir signature, bir LLM çağrısının girdi ve çıktı alanlarını, kısa bir doğal dil açıklamasıyla birlikte tanımlar. Amaç, "bu görev ne yapmalı" sorusuna cevap vermek — "bunu nasıl yapmalı" sorusuna değil.

Basit bir signature, satır içi string olarak yazılabilir:

Kod Bloğu
import dspy

# "question -> answer" biçiminde basit bir signature
qa = dspy.Predict("question -> answer")

Daha zengin, alan bazlı açıklamalar gerektiğinde sınıf tabanlı signature kullanılır:

Kod Bloğu
class MusteriSikayetSiniflandirma(dspy.Signature):
    """Bir müşteri şikayetini önceden tanımlı kategorilere ayır ve aciliyet seviyesini belirle."""

    sikayet_metni: str = dspy.InputField(desc="Müşterinin yazdığı ham şikayet metni")
    kategori: str = dspy.OutputField(desc="Fatura, Teknik, Iade, Diger kategorilerinden biri")
    aciliyet: str = dspy.OutputField(desc="Dusuk, Orta, Yuksek")

Dikkat edin: burada "adım adım düşün", "kısa cevap ver", "JSON formatında yaz" gibi talimatlar yok. Signature, sözleşmeyi (contract) tanımlıyor; talimatın tam metnini, hangi kelimelerin kullanılacağını DSPy'ın kendisi (ya da optimizer) belirliyor. Bu, geleneksel prompt yazımından en büyük kopuş noktası: siz "ne" istediğinizi net biçimde beyan ediyorsunuz, "nasıl" ifade edileceğine dair kararı sisteme bırakıyorsunuz.

2. Module: akıl yürütme stratejisini seçmek

Signature "ne" sorusuna cevap veriyorsa, module "nasıl akıl yürüteceği" sorusuna cevap veriyor. DSPy'da öne çıkan birkaç temel module var:

dspy.Predict — en basit modül. Signature'ı doğrudan bir LLM çağrısına çevirir, ekstra akıl yürütme adımı eklemez. Basit sınıflandırma, kısa çıkarım, format dönüştürme gibi görevler için uygundur.

dspy.ChainOfThought — modele, cevap vermeden önce bir "akıl yürütme" (rationale/reasoning) alanı üretmesini otomatik olarak ekler. Yani sizin signature'ınızda sadece soru -> cevap tanımlı olsa bile, ChainOfThought bunun arkasına görünmez bir rationale alanı ekler ve modelden önce gerekçesini, sonra cevabını üretmesini ister. Çok adımlı akıl yürütme gerektiren görevlerde (matematik, mantık, çok adımlı çıkarım) Predict'e göre belirgin iyileşme sağlar.

Kod Bloğu
cevapla = dspy.ChainOfThought("soru -> cevap")
sonuc = cevapla(soru="Bu sözleşmedeki cayma bedeli maddesi hangi koşulda geçerli?")
print(sonuc.cevap)
print(sonuc.rationale)  # modelin ürettiği ara akıl yürütme

dspy.ReAct — "Reasoning + Acting" düşünce/eylem döngüsünü uygular: model bir düşünce üretir, bir araç (tool/fonksiyon) çağırır, sonucu gözlemler, tekrar düşünür, gerektiğinde tekrar araç çağırır, en sonunda cevabı verir. Bu, arama motoru sorgusu, veritabanı sorgusu, hesap makinesi gibi dış araçlara ihtiyaç duyan ajan tarzı görevler için kullanılır.

Kod Bloğu
def musteri_kaydi_getir(musteri_id: str) -> str:
    """Müşteri kayıt sistemine bağlanır ve müşteri bilgisini döner (örnek fonksiyon)."""
    ...

ajan = dspy.ReAct("soru -> cevap", tools=[musteri_kaydi_getir])

Bunların dışında dspy.MultiChainComparison (birden fazla akıl yürütme zincirini üretip karşılaştıran), dspy.ProgramOfThought (kod üreterek akıl yürüten) gibi modüller de var, ama üç temel yapı taşı yukarıdaki üçü.

Modülleri Python'da normal fonksiyon/sınıf gibi zincirleyip birleştirebilirsiniz — DSPy programları genelde birden fazla modülün art arda ya da iç içe kullanıldığı bir dspy.Module alt sınıfı olarak yazılır:

Kod Bloğu
class SikayetIsleyici(dspy.Module):
    def __init__(self):
        super().__init__()
        self.siniflandir = dspy.Predict(MusteriSikayetSiniflandirma)
        self.yanit_uret = dspy.ChainOfThought("sikayet_metni, kategori -> onerilen_yanit")

    def forward(self, sikayet_metni):
        siniflandirma = self.siniflandir(sikayet_metni=sikayet_metni)
        yanit = self.yanit_uret(
            sikayet_metni=sikayet_metni,
            kategori=siniflandirma.kategori,
        )
        return dspy.Prediction(
            kategori=siniflandirma.kategori,
            aciliyet=siniflandirma.aciliyet,
            onerilen_yanit=yanit.onerilen_yanit,
        )

Bu noktada henüz hiçbir "prompt metni" yazmadığımıza dikkat edin. Sadece davranışı ve akış mimarisini tanımladık. Prompt'un gerçek metni — talimat cümleleri, örnekler — devreye optimizer girdiğinde ortaya çıkacak.

3. Optimizer / Teleprompter: programı veriye göre "derlemek"

DSPy'ın asıl gücü burada. "Teleprompter" (yeni sürümlerde çoğunlukla "optimizer" olarak anılıyor), elinizdeki küçük bir etiketli veri seti ve bir metrik fonksiyonu alıp, programınızın modüllerindeki talimatları ve/veya few-shot örneklerini otomatik olarak arayıp iyileştiren bileşendir. Birkaç önemli optimizer türü var:

BootstrapFewShot — elinizdeki birkaç etiketli örnekten yola çıkarak, programı bu örnekler üzerinde çalıştırır, metriğe göre "başarılı" sayılan çalıştırmaların ara adımlarını (örneğin ChainOfThought'un ürettiği rationale'ları) da dahil ederek otomatik few-shot demonstrasyonları üretir. Yani siz elle "işte üç örnek, şöyle cevap verilmeli" yazmak yerine, sistem kendi başarılı çalıştırmalarından örnek "damıtır" (bootstrap) ve bunları prompt'a otomatik olarak few-shot örnek olarak ekler.

BootstrapFewShotWithRandomSearch — yukarıdakinin üzerine, farklı few-shot örnek kombinasyonlarını rastgele deneyip dev set üzerinde en iyi performans göstereni seçen bir katman ekler.

COPRO (Coordinate Prompt Optimization) — talimat metninin kendisini (few-shot örneklerden bağımsız olarak) koordinat inişi benzeri bir arama ile iyileştirir; bir LLM'i "meta-optimizer" olarak kullanıp mevcut talimatın varyantlarını üretir, dev set üzerinde puanlar, en iyisini tutar.

MIPRO / MIPROv2 (Multi-prompt Instruction Proposal Optimizer) — hem talimat metnini hem few-shot örnek seçimini birlikte, Bayes optimizasyonuna benzer bir stratejiyle optimize eden, günümüzde DSPy'ın en gelişmiş optimizerlarından biri. Programınızın veri setinizin özelliklerini (örnek girdi-çıktı dağılımını) analiz ederek daha bilgilendirilmiş talimat önerileri üretir; bu "bilgilendirilmiş öneri üretme" adımı da esasen bir meta-prompting kullanımıdır — bir LLM, sizin programınız için talimat adayları üretir.

Somut kullanım şöyle görünür:

Kod Bloğu
from dspy.teleprompt import BootstrapFewShotWithRandomSearch

def dogruluk_metrigi(ornek, tahmin, trace=None):
    dogru_kategori = ornek.kategori == tahmin.kategori
    dogru_aciliyet = ornek.aciliyet == tahmin.aciliyet
    return dogru_kategori and dogru_aciliyet

optimizer = BootstrapFewShotWithRandomSearch(
    metric=dogruluk_metrigi,
    max_bootstrapped_demos=4,
    num_candidate_programs=10,
)

egitilmis_program = optimizer.compile(
    SikayetIsleyici(),
    trainset=train_ornekleri,   # etiketli 50-150 arası ornek
    valset=dev_ornekleri,       # ayri bir dogrulama seti
)

compile çağrısı arka planda şunu yapar: farklı talimat/few-shot kombinasyonlarını dener, her denemeyi trainset/valset üzerinde dogruluk_metrigi ile puanlar, en iyi skoru veren kombinasyonu seçer ve bunu programın içine "gömülü" hale getirir. Sonuç olarak elinizde artık, sizin elle yazmadığınız ama sizin tanımladığınız görev ve veriye göre otomatik ayarlanmış bir prompt/program çifti olur. Bu "derlenmiş" programı diskte saklayabilir (egitilmis_program.save(...)), versiyonlayabilir ve prod ortamında yükleyebilirsiniz.

Burada bir noktanın altını çizmek istiyorum: DSPy optimizer'ları, altta yatan LLM'in ağırlıklarını değiştirmez (fine-tuning yapmaz — bazı gelişmiş senaryolarda ince ayar destekleyen optimizer'lar da var, ama temel kullanım prompt/demonstrasyon seviyesindedir). Yaptığı şey, aynı dondurulmuş modele farklı talimat metinleri ve farklı few-shot örnekleri vererek, çıktı kalitesini metriğinize göre en üst seviyeye çıkarmaktır. Yani "model eğitimi" değil, "prompt/demonstrasyon arama ve seçimi" yapıyorsunuz.

Meta-prompting DSPy içinde nerede devreye giriyor

Burada bağı netleştirmek istiyorum, çünkü sık karıştırılıyor. Meta-prompting, DSPy'ın optimizer'larının iç mekanizmasının bir parçası. Örneğin COPRO ve MIPRO, yeni talimat adayları üretirken bir LLM'e "işte mevcut talimat, işte bazı başarılı/başarısız örnekler, bu talimatın daha iyi bir versiyonunu öner" tarzı bir meta-prompt gönderir. Yani siz DSPy kullanırken, aslında kapağın altında meta-prompting'in otomatikleştirilmiş, sistematik ve metrik güdümlü bir versiyonunu kullanmış oluyorsunuz. DSPy, meta-prompting'i "elle bir kere dene" seviyesinden çıkarıp "onlarca varyant üret, hepsini objektif olarak puanla, istatistiksel olarak en iyisini seç" seviyesine taşıyor.

Ne zaman bu yaklaşım işe yarar, ne zaman gereksiz

Bunu bir eğitimde anlattığımda katılımcılardan gelen en doğal soru şu oluyor: "Her prompt için DSPy mi kullanmalıyız?" Cevap kesinlikle hayır. Aşağıdaki tabloyu, karar verirken kullandığım kaba bir çerçeve olarak paylaşıyorum.

DurumÖneri
Tek seferlik, düşük riskli görev (iç kullanım için basit özet)Basit, elle yazılmış prompt yeterli
Görev sabit, model sabit, hacim düşükElle prompt + birkaç iterasyon yeterli
Görev üretim ortamında yüksek hacimde çalışıyor, hata maliyeti yüksekOtomatik optimizasyon değerli
Aynı görev birden fazla model üzerinde çalıştırılacak (maliyet/performans optimizasyonu için model değiştirme)Otomatik optimizasyon çok değerli — prompt'u modele göre yeniden aramanıza gerek kalmaz
Karmaşık çok adımlı ajan (araç çağırma, çok modüllü akış)DSPy'ın module + optimizer kombinasyonu ciddi katkı sağlar
Elinizde etiketli veri/eval seti yok ve oluşturmaya yatırım yapmak istemiyorsunuzOtomatik optimizasyon şimdilik anlamsız — önce eval seti kurun
Görev sık sık değişiyor (haftalık yeni kategori, yeni kural)Dikkatli olun: her değişiklikte yeniden compile maliyeti var, basit prompt + insan gözetimi daha çevik olabilir

Kısacası: otomatik prompt optimizasyonu, hacim, tekrarlanabilirlik ve ölçülebilir bir başarı tanımı olan görevlerde anlamlı. Tek seferlik, düşük hacimli, sezgisel değerlendirmeye tabi işler için (örneğin bir pazarlama metni taslağı) elle prompt yazmak hâlâ en hızlı, en pratik yol.

Evaluation olmadan optimizasyon olmaz

Bunu ayrı bir başlık altında vurgulamam gerekiyor çünkü sahada en sık atlanan adım bu: otomatik prompt optimizasyonunun ön koşulu, iyi bir değerlendirme (evaluation) altyapısıdır. DSPy'da compile fonksiyonuna verdiğiniz metric parametresi, tüm arama sürecinin pusulası. Bu pusula yanlışsa, ne kadar sofistike bir optimizer kullanırsanız kullanın, sonuç yanlış yöne optimize olur.

Pratikte iyi bir metrik/eval seti kurmak için şunlara ihtiyacınız var:

  • Temsil edici bir örneklem. Üretimde karşılaşacağınız girdi çeşitliliğini (farklı müşteri profilleri, farklı dil kayıtları, kenar durumlar) yansıtan, en az 50-100 örnekten oluşan bir küme. Çok küçük bir küme (10-15 örnek), optimizer'ın "gürültüye" aşırı uyum sağlamasına (overfit) yol açar.
  • Net bir "doğru" tanımı. Bazı görevlerde bu kolay (sınıflandırma doğruluğu gibi net bir metrik var). Bazılarında (serbest metin üretimi, ton, yaratıcılık) net değil — burada genelde bir "LLM-hakem" (LLM-as-a-judge) yaklaşımı ya da kural tabanlı kısmi kontroller (belirli anahtar bilgilerin metinde geçip geçmediği, yasaklı ifadelerin olmaması gibi) kullanılır.
  • Train/dev (validation) ayrımı. Optimizer'ı sadece trainset üzerinde çalıştırıp aynı veride başarı ölçmek, laboratuvarda "mükemmel" ama gerçek dünyada zayıf bir program üretir. Ayrı bir valset (ve mümkünse ayrı, hiç dokunulmamış bir test seti) şart.
  • Düzenli güncelleme. Üretim verisi zamanla değişir (yeni ürün kategorileri, yeni müşteri şikâyet türleri). Eval setinizi statik bırakırsanız, optimize ettiğiniz program giderek gerçeklikten uzaklaşır.

Bu nedenle DSPy'ı devreye almadan önce sorduğum ilk soru hep şu oluyor: "Elinizde bu görevi objektif olarak puanlayacak bir metrik ve en az birkaç düzine etiketli örnek var mı?" Yoksa, önce oraya yatırım yapmak gerekiyor — DSPy'a değil.

Kurumsal bir Türk ekibi için pratik iş akışı

Danışmanlık yaptığım ekiplere önerdiğim adım adım akış şu şekilde:

1. Görevi ve başarı tanımını netleştirin. "Müşteri e-postalarını kategorilere ayır" gibi soyut bir tanım yetmez. "Kategori ve aciliyet seviyesini, insan etiketçinin verdiği etiketle %90 üzerinde eşleştir" gibi somut, ölçülebilir bir hedef belirleyin.

2. Küçük ama gerçekçi bir eval seti oluşturun. Gerçek üretim verisinden (mümkünse anonimleştirilmiş ya da maskelenmiş) 50-150 örnek seçin, bunları en az bir insan uzmana etiketletin. Burada Türkiye'de faaliyet gösteren ekipler için önemli bir uyarı: eğer eval setinize gerçek müşteri verisi (isim, TCKN, telefon, adres gibi kişisel veriler) dahil ediyorsanız, bu KVKK kapsamında kişisel veri işleme sayılır. Mümkünse verileri maskeleyin/anonimleştirin, gerekliyse hukuki dayanağınızı (açık rıza, meşru menfaat vb.) netleştirin ve eval setini üçüncü taraf bir optimizasyon servisine (özellikle yurt dışı barındırmalı) göndermeden önce veri işleme envanterinizi ve varsa KVKK uyum sürecinizi kontrol edin.

3. Basit bir baseline ile başlayın. İlk sürümde dspy.Predict veya elle yazılmış basit bir prompt kullanın, mevcut performansı eval setinizde ölçün. Bu, "optimizasyon gerçekten fark yaratıyor mu" sorusuna cevap verebilmeniz için referans noktanız olacak.

4. Modül mimarisini seçin. Görev basitse Predict, akıl yürütme gerektiriyorsa ChainOfThought, dış sistemlere (CRM, bilgi tabanı, hesap makinesi) erişim gerektiriyorsa ReAct kullanın.

5. Metriği kodlayın. Mümkünse kural tabanlı, objektif bir metrik tercih edin (regex kontrolü, alan eşleşmesi, sayısal doğruluk). Bu mümkün değilse LLM-hakem kullanın ama hakem promptunun kendisini de ayrıca doğrulayın (hakem, insan değerlendirmesiyle ne kadar örtüşüyor, küçük bir örneklemde kontrol edin).

6. Optimizer'ı çalıştırın, ama ucuz optimizer ile başlayın. BootstrapFewShot gibi görece hafif bir optimizer ile başlayıp, sonuçlar tatmin etmiyorsa MIPROv2 gibi daha kapsamlı (ve daha maliyetli) bir optimizer'a geçin.

7. Test setinde doğrulayın. Optimizer'ı hiç görmediği bir test setinde son performansı ölçün. Dev set skorunun test set skoruna yakın çıkması, overfitting olmadığının bir işareti.

8. Derlenmiş programı versiyonlayın. save() ile diske yazılan programı, kod gibi git'e (ya da bir model/prompt kayıt sistemine) commit edin, hangi veri setiyle, hangi optimizer parametreleriyle üretildiğini not edin.

9. Üretimde izleyin. Canlıya aldıktan sonra gerçek kullanım verisinden örnekleyip periyodik olarak eval setinize yeni örnekler ekleyin, performansı düzenli olarak yeniden ölçün.

Somut bir uçtan uca pseudocode örneği

Aşağıda, tüm parçaları bir araya getiren, sadeleştirilmiş bir akış örneği paylaşıyorum (gerçek DSPy API'sinin sadeleştirilmiş bir yansıması olarak okuyun):

Kod Bloğu
import dspy

# 1) Dil modelini tanımla (örnek: bir LLM sağlayıcı ucu)
lm = dspy.LM("openai/gpt-4o-mini")
dspy.configure(lm=lm)

# 2) Görevi signature ile tanımla
class SozlesmeOzetleme(dspy.Signature):
    """Bir hukuki sozlesme metnini, taraflarin temel yukumluluklerini
    kaybetmeden, sade Turkce ile 3-5 cumlede ozetle."""

    sozlesme_metni: str = dspy.InputField()
    ozet: str = dspy.OutputField(desc="3-5 cumlelik sade ozet")

# 3) Modul mimarisini sec
ozetleyici = dspy.ChainOfThought(SozlesmeOzetleme)

# 4) Metrigi tanimla (ornek: anahtar maddelerin ozet icinde gecip gecmedigini kontrol eden basit kural + LLM-hakem karisimi)
def ozet_metrigi(ornek, tahmin, trace=None):
    anahtar_kelimeler_var = all(
        kelime.lower() in tahmin.ozet.lower() for kelime in ornek.zorunlu_anahtar_kelimeler
    )
    uzunluk_uygun = 1 <= tahmin.ozet.count(".") <= 6
    return anahtar_kelimeler_var and uzunluk_uygun

# 5) Egitim ve dogrulama setlerini hazirla (insan etiketli, gercek/anonimlestirilmis ornekler)
train_ornekleri = [...]   # dspy.Example listesi, orn. 80 ornek
dev_ornekleri = [...]     # orn. 30 ornek
test_ornekleri = [...]    # optimizer'in hic gormedigi 30 ornek

# 6) Optimizer ile programi derle
from dspy.teleprompt import MIPROv2

optimizer = MIPROv2(metric=ozet_metrigi, auto="medium")
derlenmis_ozetleyici = optimizer.compile(
    ozetleyici,
    trainset=train_ornekleri,
    valset=dev_ornekleri,
)

# 7) Test setinde bagimsiz dogrulama
basari_orani = sum(
    ozet_metrigi(ornek, derlenmis_ozetleyici(sozlesme_metni=ornek.sozlesme_metni))
    for ornek in test_ornekleri
) / len(test_ornekleri)

print(f"Test seti basari orani: {basari_orani:.2%}")

# 8) Derlenmis programi kaydet, versiyonla
derlenmis_ozetleyici.save("sozlesme_ozetleyici_v1.json")

Bu akışta dikkat edilmesi gereken şey: hiçbir noktada "işte tam prompt metni bu" diye bir şey yazmadık. Signature, modül ve metrik üzerinden görevi tanımladık; compile adımı, bizim yerimize talimat metnini ve few-shot örneklerini üretti/seçti.

Tuzaklar: nelere dikkat etmeli

Eval setine aşırı uyum (overfitting). Optimizer, dev set üzerinde en yüksek skoru veren varyantı seçer. Eğer dev set küçük veya çeşitlilikten yoksunsa, optimizer "dev sette işe yarayan ama genel dağılımda işe yaramayan" tuhaf kalıplar bulabilir (örneğin belirli bir kelimeyi her cevaba eklemek, dev setteki bir tesadüfi korelasyondan dolayı skoru artırıyorsa). Bunu azaltmanın yolu: yeterince büyük ve çeşitli bir dev set, ayrı bir test set ile bağımsız doğrulama, ve mümkünse çapraz doğrulama (k farklı dev set bölünmesiyle tutarlılığı kontrol etmek).

Optimizasyon çalıştırmasının maliyeti. Özellikle MIPROv2 gibi kapsamlı optimizer'lar, onlarca-yüzlerce LLM çağrısı yaparak talimat/örnek kombinasyonlarını dener. Büyük modellerle (özellikle pahalı API'lerle) bu, tek bir optimizasyon koşusunda anlamlı bir maliyete yol açabilir. Pratik öneri: optimizasyon aşamasında daha ucuz/hızlı bir model kullanıp, bulunan programı daha sonra hedef modelde doğrulamak, ya da optimizer'ın "auto" modunda maliyet/kapsam ayarını (light/medium/heavy gibi) düşük tutarak başlamak.

Tekrarlanabilirlik (reproducibility). LLM çağrıları doğası gereği bir miktar stokastiktir (sıcaklık/temperature parametresi sıfır olsa bile sağlayıcı tarafında küçük değişkenlikler olabilir). Aynı optimizer aynı veriyle her çalıştırıldığında bire bir aynı sonucu vermeyebilir. Bunun için: (a) rastgelelik tohumunu (seed) sabitleyin, (b) derlenmiş programı bulduğunuz anda diske kaydedin ve bir daha "yeniden derlemeyin" — üretimde her zaman kaydedilmiş, sabit programı kullanın, (c) hangi optimizer sürümü, hangi veri seti, hangi model sürümüyle derlendiğini not düşün (bir tür "model kartı" gibi düşünün).

Metrik kısayolları (metric hacking). Otomatik optimizasyon, tanımladığınız metriği harfiyen maksimize eder — sizin niyetinizi değil. Metriğiniz "cevapta X kelimesi geçsin" gibi basitse, optimizer o kelimeyi bağlamdan kopuk şekilde her yanıta sıkıştırmayı "öğrenebilir". Metriği elden geldiğince gerçek iş hedefine yakın tasarlamak, ve zaman zaman optimize edilmiş çıktıları insan gözüyle örnekleyerek kontrol etmek şart.

"Kara kutu" optimize edilmiş prompt'lar. Optimizer'ın bulduğu nihai talimat metni bazen insan gözüyle "neden bu ifade seçildi" sorusuna tatmin edici cevap vermeyebilir. Bu, denetlenebilirlik ve açıklanabilirlik gerektiren kurumsal ortamlarda (özellikle finans, sağlık, kamu gibi düzenlemeye tabi sektörlerde) bir sorun olabilir. Derlenmiş prompt'u üretime almadan önce mutlaka insan gözden geçirmesinden geçirin — "optimizer ne dediyse odur" yaklaşımı doğru değil.

Veri sızıntısı (data leakage). Train ve test setlerinizin gerçekten ayrık olduğundan emin olun; aynı müşteri kaydının hem eğitim hem test setinde (farklı biçimlerde) yer alması, yapay olarak şişirilmiş bir başarı skoru gösterebilir.

Meta-prompting'i DSPy olmadan da kullanabilir misiniz

Evet — DSPy tek yol değil. Daha hafif bir yaklaşım olarak, elinizdeki bir prompt'u periyodik olarak bir "eleştirmen" LLM'e gösterip ("bu prompt'un zayıf yönleri neler, hangi kenar durumlarda başarısız oluyor, nasıl geliştirilebilir") iyileştirme önerileri almak, sonra bunları elle deneyip eval setinizde ölçmek de bir tür meta-prompting uygulamasıdır — sadece otomasyon seviyesi daha düşüktür. Bunu "DSPy'a giden bir ara adım" olarak düşünebilirsiniz: önce eval setinizi ve metriğinizi kurun, meta-prompting'i yarı-manuel biçimde deneyin, süreç olgunlaştıkça ve hacim arttıkça DSPy gibi bir framework'e geçin. Otomasyonun kendisi amaç değil — hedef, öngörülebilir ve ölçülebilir prompt kalitesi.

Kapanış gözlemi

Eğitimlerimde sık sık şunu söylüyorum: prompt mühendisliği, LLM'lerin ilk yıllarının "el işçiliği" evresiydi ve muhtemelen kalıcı olmayacak bir ara aşama. Yazılım mühendisliğinde assembly'den yüksek seviyeli dillere, elle bellek yönetiminden garbage collection'a geçtiğimiz gibi, prompt yazımından da "davranış tanımlama + otomatik optimizasyon" modeline doğru bir geçiş yaşıyoruz. DSPy bu geçişin bugün elimizdeki en somut, en olgun aracı. Ama bu geçişin faydasını görmek için önce sağlam bir değerlendirme kültürü kurmanız gerekiyor — metriksiz, eval setsiz bir ekip için DSPy, sadece karmaşıklığı artıran bir katman olur. Önce ölçün, sonra otomatikleştirin.

Danismanlik Baglantilari

Bu yazıya en yakın consulting sayfaları

Bu içerikten sonraki mantıklı adım için en ilgili solution, role ve industry landing'lerini burada görebilirsin.

Yorumlar

Yorumlar