CI hattına bağlamak
- Değerlendirme koşusunu CI hattına eşikle bağlar, kararsız sonucu tekrarla sabitler, anahtarı gizli tutar ve kırmızı adımın nedenini okursun.
- Bu derste bir değerlendirme koşusunu CI hattına nasıl bağlayacağını, hangi ayarların otomasyonu kırılgan yaptığını öğreneceksin.
- Sonunda bir CI yapılandırmasındaki hatayı bulacaksın.
Puan bütün etkinlikleri cevaplayınca hesaba yazılır. Bu dersi tamamlayınca +56 XP alacaksın. Giriş yapmadan ilerlersen puan yalnız bu tarayıcıda durur.
Bir tarama elle çalıştırıldığında ekip onu unutur. Bir CI adımına bağlandığında her değişiklikte kendini hatırlatır. Altay Kargo ekibi asistanın sistem promptunu haftada birkaç kez değiştiriyor, takip aracına yeni alanlar ekliyor.
Geçen ay bir değişiklik iade aracının yetki kontrolünü gevşetti ve sorun ancak bir müşteri şikâyetiyle fark edildi. O değişiklik bir CI kapısından geçseydi birleştirme anında durabilirdi. Bu derste değerlendirmeyi böyle bir kapıya çevireceksin.
Ayarların neden kodda durduğunu, eşiğin çıkış koduna nasıl bağlandığını göreceksin. Kararsız model çıktısını nasıl sabitleyeceğini ve API anahtarını nasıl koruyacağını öğreneceksin. Sonunda gerçek bir iş akışı dosyasını satır satır okuyacaksın.
Kapı neden CI'da durur
Bir değerlendirme koşusunu CI'ya bağlamanın özü basittir. Koşuyu tetikleyen adım, koşunun ayarları ve geçme eşiği kod deposunda durur. Böylece bir eşik değişikliği de bir kod değişikliği gibi gözden geçirilir ve kimse onu sessizce gevşetemez. Test senaryoları da aynı depoda durur.
Yeni bir özellik eklenince aynı değişiklik isteğinde ilgili test senaryosu da eklenir. Ayarları koddan ayrı bir panelde tutmak, bir gün kimsenin hatırlamadığı bir eşiğe dönüşür. Depo aynı zamanda bir denetim izi tutar. Bir eşiği kimin ne zaman değiştirdiği sorusuna deponun geçmişinden cevap verirsin.
Bunu bir güvenlik panelinde değil CI'da yapmanın nedeni zamanlamadır. Panel sorunu dağıtımdan sonra gösterir. Ekip paneli haftada bir açar, o arada sorunlu sürüm kullanıcıya ulaşmıştır. CI adımı ise değişikliği üretime gitmeden durdurur.
Ölçme ve Değerlendirme modülünde gördüğün sürüm karşılaştırması burada da geçerlidir. Fark şudur: karşılaştırma insanın inisiyatifinde değil, otomatik bir kapıdadır. Kapı olmadan bir mühendis birazdan bakarım der ve o birazdan hiç gelmez.
CI aracı değerlendirme adımının çıkış koduna bakar. Sıfır başarı demektir, sıfırdan farklı her kod başarısızlık. Koşu eşiği aşan bir sonuç bulursa sıfırdan farklı bir kodla biter. CI adımı kırmızı görünür, dal koruma kuralı da birleştirmeyi engeller.
promptfoo bu sözleşmeyi hazır sunar. promptfoo eval en az bir test başarısız olduğunda 100 koduyla çıkar. PROMPTFOO_PASS_RATE_THRESHOLD ortam değişkeni bu katılığı ayarlar. Değer 95 ise geçme oranı yüzde 95'in altına düştüğünde 100 kodu döner.
Başka her hata, örneğin bozuk bir yapılandırma dosyası, 1 koduyla biter. İki kodu ayırmak önemlidir. Biri ürünün sınavı geçemediğini, öbürü sınavın hiç yapılamadığını söyler.
adim: destek-api-degerlendirme komut: promptfoo eval --config degerlendirme.yaml esik: basarisiz_oran < %5 basarisizsa: birlestirmeyi_engelle
Etkinlik 1 / 6
Bu CI adımı, sonuçtan bağımsız olarak asla birleştirmeyi engellemiyor. Hangi satır bu sorunun kök nedenidir?
Derinleş:Kapıyı zorunlu kılmak
Kırmızı bir CI adımı tek başına birleştirmeyi durdurmaz. GitHub'da bunu dal koruma kuralı ya da kural seti (ruleset) yapar. Ana dal için birleştirmeden önce durum denetimlerinin geçmesini isteyen seçeneği açar, değerlendirme işini de gerekli denetim olarak seçersin. Bundan sonra o iş yeşil bitmeden birleştirme düğmesi açılmaz.
Yönetici yetkisi olan biri bu kuralı atlayabilir. Kural ayarlarında bu atlamayı da kapatabilirsin. Kapatmazsan acil bir günde kapı sessizce delinir.
Bir ayrıntı daha var: iş akışı dosyası da depoda durur. Aynı depodan açılan bir değişiklik isteği iş akışını değiştirip eşiği düşürebilir ve kendi değiştirdiği kapıdan geçebilir. Bu yüzden iş akışı klasörünü ve değerlendirme yapılandırmasını kod sahipleri (CODEOWNERS) dosyasıyla bir güvenlik sorumlusuna bağla.
Kod sahibi onayını da zorunlu yap. O kişi onay vermeden eşik değişmesin.
Aynı girdi, farklı yanıt
Bir zorluk da modelin çıktısının aynı girdi için bile değişmesidir. Sıcaklık (temperature) sıfırdan büyükse aynı istem farklı yanıt üretir. Bir koşu geçer, aynı koddaki bir sonraki koşu kalır. Ekip buna kararsız test (flaky test) der ve bir süre sonra kırmızı adımı ciddiye almayı bırakır.
Kararsız bir kapı, hiç olmayan bir kapıdan daha tehlikelidir. Ekip yeniden çalıştır düğmesine basmayı öğrenir ve gerçek bir gerileme de aynı düğmeyle geçip gider. CI'da güvenilir bir eşik istiyorsan sıcaklığı düşür ya da her senaryoyu birkaç kez çalıştırıp çoğunluk kararı al.
Sıcaklığı sıfıra çekmek tek başına yetmez. Bazı sağlayıcılarda sıcaklık sıfırken bile yanıt küçük farklarla değişir. Üstelik üretimdeki asistan sıfır sıcaklıkta çalışmıyorsa, CI'da ölçtüğün davranış kullanıcının gördüğü davranış olmaz. Bu yüzden pek çok ekip üretim ayarını korur ve tekrar sayısını artırır.
promptfoo'da --repeat 3 bayrağı her testi üç kez koşar. garak zaten her istemi varsayılan olarak beş kez gönderir. Kararı tek bir yanıta değil, tekrarların dağılımına bağlarsın.
Çoğunluk kararı uygularken tekrar sayısını küçük tut. Üç ya da beş tekrar çoğu zaman gürültüyü bastırır. Tekrarı abartmak maliyeti katlar ama kararlılığı aynı oranda artırmaz. Eşiği de bu çoğunluk kararına göre yaz.
Üç tekrardan en az ikisi geçmeli gibi somut bir kural, genelde geçmeli gibi belirsiz bir cümleden daha güvenilirdir. Güvenlik testlerinde bir adım daha at. Sızdırma gibi ağır bir sonuç tek bir tekrarda bile görülürse testi başarısız say. Saldırgan da denemesini bir kez değil, istediği kadar tekrarlar.
Ters yönde bir tuzak da var: fazla kararlı görünen sonuç. promptfoo sağlayıcı yanıtlarını varsayılan olarak diskte önbelleğe alır ve kayıtlar on dört gün saklanır. Önbellek anahtarı isteğin kendisinden üretilir.
Önbellek klasörü koşular arasında korunuyorsa, örneğin hız için CI önbelleğine alınmışsa, tehlike başlar. Altay Kargo'nun sistem promptu sunucu tarafında değiştiğinde istek aynı kalır, önbellek de eski yanıtı döndürür. Kapı yeşil yanar ama yeni davranış hiç sınanmamıştır.
--repeat ile alınan tekrarlar da kendi önbellek alanlarından okunabilir. Güvenlik kapısında --no-cache bayrağını kullan. Önbelleği yalnız geliştirme sırasında, hız için aç.
Etkinlik 2 / 6
Bir CI değerlendirme kapısını kurma adımlarını sıraya koy.
Bir öğeye odaklan, yukarı ve aşağı ok tuşlarıyla taşı. Home tuşu en üste, End tuşu en alta alır.
- 1Raporu bir sonraki koşuyla karşılaştırmak için sakla
- 2Eşiği aşan çıkış kodunun birleştirmeyi engellemesini sağla
- 3Koşuyu CI adımına bağla
- 4Değerlendirme setini ve eşiği depoya koy
- 5Sıcaklığı düşür veya çoklu örnekleme ekle
Çözümlü örnek: GitHub Actions'ta değerlendirme kapısı
Şimdi kuralları gerçek bir iş akışı dosyasına dök. Altay Kargo'nun deposu GitHub'da duruyor. Her değişiklik isteğinde çekirdek değerlendirme seti koşacak. Geçme oranı yüzde 95'in altına düşerse birleştirme duracak, rapor da her koşuda saklanacak.
Değerlendirme yapılandırması ilk derste gördüğün degerlendirme.yaml dosyasıdır ve asistanın hazırlık ortamına istek atar. Örnek GitHub Actions ile yazıldı, ama aynı mantık GitLab CI ya da Jenkins gibi başka CI araçlarında da geçerlidir. Aşağıdaki dosya deponun iş akışı klasöründe durur.
name: llm-degerlendirme
on: pull_request
jobs:
cekirdek-set:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Cekirdek seti kos
env:
ASISTAN_API_ANAHTARI: ${{ secrets.ASISTAN_API_ANAHTARI }}
PROMPTFOO_PASS_RATE_THRESHOLD: 95
run: >
npx [email protected] eval -c degerlendirme.yaml --no-cache
--repeat 3 -o results.json -o rapor.html
- name: Raporu sakla
if: always()
uses: actions/upload-artifact@v7
with:
name: degerlendirme-${{ github.sha }}
path: |
results.json
rapor.htmlDosyayı yukarıdan aşağı oku. on: pull_request satırı iş akışını her değişiklik isteğinde tetikler. checkout adımı depoyu, yani test senaryolarını ve degerlendirme.yaml dosyasını çalışma alanına alır.
İkinci adımın env bölümü iki değer taşır. Anahtar secrets bağlamından gelir ve depodaki hiçbir dosyada yazılı değildir. degerlendirme.yaml onu aynı adla ortamdan okur. Eşik ise açıkça dosyada durur, değiştirmek isteyen bir kod incelemesinden geçmek zorundadır.
run satırı promptfoo'nun sabit bir sürümünü çağırır, @latest değil 0.123.1. Böylece bir araç güncellemesi sonucu habersizce değiştiremez. --no-cache her yanıtın taze alınmasını garanti eder, --repeat 3 de her testi üç kez koşar.
İki -o bayrağı hem makinenin okuyacağı JSON'u hem insanın okuyacağı HTML'i üretir. Son adımdaki if: always() satırı önemlidir. Değerlendirme kırmızı bitse bile rapor saklanır, çünkü en çok ihtiyaç duyduğun rapor kırmızı biten koşunun raporudur.
Etkinlik 3 / 6
İş akışının son adımından if: always() satırı silinirse ne değişir?
Kırmızı kapıyı yerelde okumak
Kapı kırmızı yandığında nedenini tahmin etme, aynı komutu aynı sürümle kendi makinende koş. Sonuç CI'dakinden farklı çıkıyorsa fark ortamdadır: araç sürümü, ortam değişkeni ya da önbellek. Aşağıda aynı yapılandırma tek tekrarla ve aynı eşikle çalıştırıldı. Ardından jq ile rapor dosyasından sayılar çıkarıldı.
jq, JSON dosyalarını komut satırında sorgulayan küçük bir araçtır. Rapor dosyası koşunun bütün ayrıntısını taşır, konsol ise yalnız özetini gösterir. Çıktıyı satır satır izle.
$ PROMPTFOO_PASS_RATE_THRESHOLD=95 promptfoo eval -c degerlendirme.yaml -o results.json
...
Pass rate 93.18% is below the threshold of 95%
$ echo $?
100
$ jq -c '.results.stats | {successes, failures, errors}' results.json
{"successes":41,"failures":3,"errors":0}
$ jq '.results.stats.successes / (.results.stats.successes + .results.stats.failures) * 100' results.json
93.18181818181819İlk komut değerlendirmeyi koşar ve promptfoo geçme oranının eşiğin altında kaldığını yazar. echo $? son komutun çıkış kodunu gösterir: 100. CI bu kodu görüp adımı kırmızıya boyar.
Üçüncü komut sayıları çıkarır: kırk bir başarı, üç başarısızlık, sıfır hata. Dördüncü komut oranı aynı formülle hesaplar ve yüzde 93,18 bulur. Bu hesabı ayrıca yapmanın bir faydası var.
CI özetine tek satırlık bir not düşersen inceleyen kişi raporu açmadan durumu görür. Testlere aile etiketi verirsen her aile için ayrı oran ve ayrı eşik yazmak da mümkün olur. Ölçme ve Değerlendirme modülünde gördüğün gibi toplam oran tek bir ailenin çöküşünü gizleyebilir.
Etkinlik 4 / 6
CI adımında promptfoo'yu, ayar dosyası degerlendirme.yaml ile çalıştıran ve sonucu results.json dosyasına yazan komutu yaz.
Anahtar, maliyet ve kayıt
Koşu bir API anahtarı kullanıyorsa bu anahtar depoya yazılmaz, CI'nın gizli değişken deposunda durur. Komut satırına yazılan anahtar günlük kayıtlarına da düşer. GitHub'ın belgesi de sırrı süreçler arasında komut satırıyla taşımamayı, ortam değişkeni ya da standart girdi kullanmayı önerir.
GitHub sırları günlükte maskeler, ama belge bu maskelemenin garanti olmadığını da söyler. Maskeleme büyük ölçüde birebir eşleşmeye dayanır. Bir sırrı JSON ya da YAML bloğu içinde saklarsan maskeleme kaçabilir.
Anahtar bir günlüğe sızarsa günlüğü sil ve anahtarı hemen yenile. Değerlendirme için ayrı, yetkisi ve kotası dar bir anahtar kullan. Sızsa bile yalnız hazırlık ortamına erişsin.
Dışarıdan katkı alan depolarda bir durum daha var. Çatallanmış (fork) bir depodan açılan değişiklik isteği de iş akışını tetikler. GitHub bu durumda GITHUB_TOKEN dışındaki sırları çalıştırıcıya geçirmez.
Amaç, yabancı bir kodun senin anahtarını okumasını engellemektir. Bu yüzden değerlendirme adımı anahtarsız kalır ve hazırlık ortamına bağlanamaz. Kapı bu durumda yeşil yanmamalı.
Anahtar boşsa adım açık bir mesajla dursun, inceleyen kişi de değerlendirmenin hiç koşmadığını görsün. Bu kısıtı pull_request_target tetikleyicisiyle aşmaya çalışma. O tetikleyici sırlara erişir. Dışarıdan gelen kodu o ortamda çalıştırırsan anahtar da açığa çıkar.
Etkinlik 5 / 6
Doğru mu, yanlış mı?
Çatallanmış (fork) bir depodan açılan değişiklik isteğinde GitHub Actions, depo sırlarını değerlendirme adımına geçirir.
Kapıdaki çekirdek set küçük ama keskin olmalı. İçine üç tür test koy. Birincisi kalıcı gerileme örnekleridir: daha önce kapattığın her açık için bir test.
İkincisi her saldırı ailesinden en çok tutan birkaç kalıptır. Üçüncüsü işlevsel testlerdir. Bunlar asistanın asıl işini yapıp yapmadığını ve zararsız isteklerin engellenmediğini ölçer.
Üçüncü grup unutulursa kapı güvenliği korur ama ürünü bozar, yanlış pozitif sessizce artar. Çekirdek setin koşusu birkaç dakikayı geçmesin, yoksa ekip kapıyı atlatmanın yolunu arar.
Her CI koşusu jeton harcar ve sağlayıcı kotasını tüketir. Her değişiklik isteğinde tam seti koşturmak hem yavaş hem pahalıdır. Bazı sağlayıcılarda dakika başı istek sınırına da çarparsın.
Küçük bir çekirdek seti her değişiklikte, geniş seti gecelik ya da haftalık bir zamanlayıcıda koştur. GitHub Actions'ta bu ikinci iş akışı on: schedule tetikleyicisiyle başlar. Kırmızı takım taraması da aynı ayrıma uyar.
promptfoo redteam run komutu saldırı örneklerini üretip koşar ve uzun sürer. Onu gece koşusuna koy, her koşuyu --tag git.sha=... ile o anki koda bağla. retry stratejisi ise daha önce açık bulan, yani testi kıran saldırı örneklerini sonraki taramaya otomatik ekler ve kapanan açığın geri dönmesini yakalar.
Koşunun ürettiği raporu CI'nın yapı çıktısı (build artifact) olarak sakla, yalnız ekranda gösterip kaybetme. Bir sonraki koşu bu dosyaya bakarak kendi sonucunu taban çizgisiyle karşılaştırır. Yapı çıktısı kalıcı bir arşiv değildir.
Saklama süresi bir ayardır ve süre dolunca dosya silinir. Eğilimi aylarca izlemek istiyorsan özet sayıları ayrıca bir tabloya ya da depoya yaz. Her satıra koşu tarihini, kod özetini, araç sürümünü ve geçme oranını ekle.
Etkinlik 6 / 6
Bir CI değerlendirme adımını sağlam kılan uygulamalar hangileridir?
Okumayı istediğin sırayla sürdürebilirsin. Ders, bütün etkinlikleri cevapladığında tamamlanır.