Ajan "hazır" yazdı ama özellik çalışmıyor: yapay zeka ajanının işi nasıl doğrulanır
Ajan "hazır" yazar, çünkü işini kendisi değerlendirir ve yalnızca doğruladığını görür: genellikle unit testleri, oysa bunlar modüller arası bağlantıları, migration'ları, yapılandırmayı ve uçtan uca senaryoyu görmez. Çözüm istemde değil yapıdadır: AGENTS.md içinde Definition of Done, zorunlu bir E2E kontrolü, yalnızca passing işaretini koyabilen bir doğrulama betiği ve temiz context'li bağımsız bir değerlendirici.
Neden böyle olur
Yapay zeka ajanıyla çalışan herkesin tanıdığı bir durum: ajan "hazır" yazdı, ama özellik çalışmıyor. Ajan bile bile yalan söylemez. Yalnızca kendi doğruladığını bildirir ve bunun yeterli olduğuna samimiyetle inanır. Beyan ile gerçeklik arasındaki fark, belirli bir modelin kusuru değil, "uygulayıcı kendi işini değerlendirir" düzeninin özelliğidir.
Üç neden vardır. Birincisi: modeller aşırı özgüvenlidir; yanıtlarının kesinliği doğruluğundan fazladır. İkincisi: ajan işini, yaptığı context'in içinde değerlendirir. Çözümün doğru olduğuna zaten kendini ikna etmiştir ve "daha objektif ol" demek context'i değiştirmez. Üçüncüsü ve en pratik olanı: ajanın erişebildiği doğrulama kısmidir. Unit testler hızla yeşile döner, uçtan uca senaryo ise kimse tarafından çalıştırılmamıştır.
Bir yanlış "hazır"ın öyküsü
Ajana şifre sıfırlama görevi verildi. Veritabanı şemasını değiştirdi, bir endpoint ekledi, e-posta şablonunu bağladı, unit testleri çalıştırdı; hepsi yeşil. Sohbette özgüvenle "Hazır" yazıldı.
Gerçekte şunlar oldu:
- migration ortada çöktü ve şema tutarsız bir durumda kaldı;
- e-posta şablonu var, ama posta sunucusu (SMTP) ortamda yapılandırılmamış, dolayısıyla mail gitmiyor;
- "Kullanıcı bağlantıya tıklar → yeni şifreyi girer → giriş yapar" senaryosu hiç çalıştırılmadı.
Bu sorunların hiçbirini unit testler yakalayamazdı: veritabanı mock'lanmıştı, mail gönderimi mock'lanmıştı ve uçtan uca bir çalıştırma hiç yoktu.
Yeşil testler neden özelliğin çalıştığını kanıtlamaz
Bir unit test modülü izole ederek kontrol eder: bağımlılıklar mock'lanır, dış dünya kapatılır. Bu hız ve kesinlik sağlar, ama sistematik bir kör nokta da yaratır. Bu kör nokta testlerin hatası değil, tasarımlarının bir sonucudur. Yeşil unit testler yalnızca her parçanın tek başına çalıştığını kanıtlar, parçaların birlikte çalıştığını değil.
Unit testin hiçbir zaman göremediği dört sınıf hata vardır:
| Hata sınıfı | Örnek | Unit test neden yakalamaz |
|---|---|---|
| Arayüzlerin uyuşmaması | Bir bileşen göreli yol verir, diğeri mutlak yol bekler | Her biri kendi mock'uyla test edilir ve mock her biçimi kabul eder |
| Durumun yayılması | Şema değişti, ama ORM'in önbelleği eski | Unit testte ne gerçek veritabanı var ne önbellek |
| Kaynakların yaşam döngüsü | Dosya tanımlayıcıları ve bağlantılar kapanmıyor, büyük dosyalarda bellek sızıyor | Test küçük veride çalışır ve kaynaklar tükenmeden biter |
| Ortam ve yapılandırma | Ortam değişkeni yok, SMTP ayarlı değil, migration uygulanmadı | Test uygulamayı gerçek yapılandırmada çalıştırmaz |
Kontrol seviyeleri iç içedir: unit testlerin yakaladığı her şeyi integration testleri de yakalar, integration testlerin yakaladığı her şeyi uçtan uca (E2E) çalıştırma da yakalar. Her üst seviye, bir alttakinden kesinlikle daha fazlasını görür. Bu yüzden "hazır" deme yetkisi yalnızca en üst seviyeye ait olmalıdır.
Verification gap nasıl ölçülür
Düzeltmeden önce sayın. Ajanın kapattığı son 10 görevi alın. Her biri için iki soruyu yanıtlayın: ajan "hazır" dedi mi, ve bağımsız bir kontrol bunu doğruladı mı (siz senaryoyu çalıştırdınız, E2E testi yeşil)? Bu ikisi arasındaki uyuşmazlığın oranı verification gap'inizdir.
Bu sayı iki nedenle faydalıdır. Sorunun büyüklüğünü "evet, iyi çalışıyor gibi" tartışmasına girmeden gösterir. Ve bir başlangıç ölçüsü verir: her değişiklikten sonra benzer görevler üzerinde yeniden hesaplayıp işe yarayıp yaramadığına bakarsınız.
Gerçekten nasıl doğrulanır: üç katmanlı kontrol
Kullanışlı bir benzetme: bir araba tamir atölyesinin üç istasyonundan geçer ve hiçbirini atlayamaz.
- Teşhis (saniyeler): sözdizimi, tipler, linter. Yazım hatalarını ve kaba hataları yakalar.
- Test standı (dakikalar): unit ve integration testleri, uygulama açılır. Motor çalışıyor, ama araba hâlâ yerinde duruyor.
- Test sürüşü: uçtan uca senaryo, kullanıcının çalıştırdığı şekliyle: API, tarayıcı veya CLI üzerinden, gerçek veritabanı ve yapılandırmayla.
İlerleme kuralı: teşhiste hatalar varken test sürüşüne çıkılmaz ve test sürüşü geçilmeden araba teslim edilmez. Yeşil unit testler ikinci istasyonu geçmek demektir, üçüncüyü değil.
Her şeyi tek bir giriş noktasında toplamak uygundur:
# Makefile
check:
ruff check .
mypy app
pytest tests/unit tests/integration
e2e:
docker compose up -d db mailhog
alembic upgrade head
pytest tests/e2e
verify: check e2e
Burada E2E gerçek bir veritabanı ve test posta sunucusu kaldırır, migration'ları uygular ve senaryoyu baştan sona çalıştırır. Bozuk bir migration veya yapılandırılmamış bir posta hemen görünür, müşteride değil.
AGENTS.md içinde Definition of Done
Ajan "hazır"ın ne anlama geldiğine kendi başına karar vermemelidir. Ölçüt talimat dosyasına yazılır ve doğrulanabilir biçimde ifade edilir:
## Definition of Done
Özellik ancak TÜM maddeler yerine getirildiğinde hazırdır:
1. make check kod 0 ile tamamlandı
2. make e2e kod 0 ile tamamlandı, özelliğin senaryosu tests/e2e içinde
3. Migration'lar temiz bir veritabanında uygulanıyor ve geri alınıyor
4. Yeni ortam değişkenleri .env.example içinde tanımlı
5. PROGRESS.md içinde ne yapıldığı ve nasıl doğrulandığı yazılı
Hazır olduğunun kanıtı SAYILMAZ:
- "Kod yazıldı ve doğru görünüyor"
- "Unit testler geçiyor"
- "Bende yerelde çalışıyor"
Neyin hazır sayılmadığını açıkça listelemek, beklenenden daha iyi çalışır: ajan ilk yeşil çalıştırmada durmayı bırakır.
Passing işaretini kim koyar
Bir sonraki adım, hazır olma işaretini ajanın elinden tamamen almaktır. Özellikler bir listede tutulur (örneğin feature_list.json), alanları şunlardır: "davranış", "doğrulama komutu", "durum". Bir özelliği passing yapabilecek tek şey bir betiktir: genel make check'i çalıştırır, sonra o özelliğin doğrulama komutunu çalıştırır ve başarılıysa durumu commit hash'iyle birlikte kanıt olarak kaydeder. Geçmezse özellik aktif kalır; "neredeyse oldu" diye bir şey yoktur.
{
"id": "F04",
"behavior": "Kullanıcı bağlantılı e-postayı alır ve şifresini değiştirir",
"verification": "pytest tests/e2e/test_password_reset.py",
"state": "active"
}
Burada bir tuzak var: betik yalnızca verification alanına yazılanı kontrol eder. Bir mimari kontrol Definition of Done'da var ama özelliğin komutunda yoksa, bu açık görünmez kalır. Bu yüzden genel make check, yalnızca testleri değil tüm zorunlu kontrolleri içermelidir.
Bağımsız değerlendirici
Uygulayıcının kendi raporu asla bağımsız bir gerçeklik kaynağı değildir. Çözüm, rolleri ayırmaktır:
- planlayıcı gereksinimi kabul kriterleri olan özelliklere böler ve kod yazmaz;
- üretici bir özelliği uygular ve testleri çalıştırır, ama hazır olup olmadığına karar vermez;
- değerlendirici kontrol listesindeki kriterlere göre hüküm verir.
Değerlendirici, üreticinin gerekçelerini görmemelidir, yoksa onun özgüvenini devralır. Yalnızca gereksinimi, diff'i ve çalıştırma sonuçlarını almalıdır. Claude Code'da bu, kendi temiz context'i olan bir subagent ile kolayca yapılır. Daha da iyisi, değerlendirici başka bir model olsun, çünkü onun kör noktaları farklıdır. "Makul görünüyor" türünden bir değerlendirme yasaktır: her sonuç bir dosyaya, satıra veya loga dayanmalıdır.
Ajanın kendisi düzeltebileceği hata mesajları
Doğrulama, çıktısı düzeltmeye götürdüğünde değerlidir. "Test failed" ya da çıplak bir AssertionError ajana bir şey söylemez. İyi bir mesaj şunları içerir: neyin bozulduğu, kuralın neden var olduğu ve nasıl düzeltileceği:
ERROR arch-check: app/services/refund.py, app/db/session.py dosyasını import ediyor
WHY: servisler veritabanıyla doğrudan konuşmaz, yalnızca repository'ler üzerinden
FIX: app/repositories/refund.py içindeki RefundRepository'yi kullanın
Bu biçim kontrolü bir yasaktan bir öneriye çevirir: ajan ihlali sizin yardımınız olmadan düzeltir.
İnsan kontrolü hâlâ nerede gerekli
Otomasyon insanı ortadan kaldırmaz, yalnızca rolünü daraltır. Kriterin resmileştirilmesinin zor olduğu yerlerde elle kontrol gerekir: arayüzün görünümü, metinler, senaryonun kullanım kolaylığı. Bunu "biraz tıklayıp bakmak" yerine kabul kriterlerinden oluşan bir kontrol listesiyle yapın. Aynı yorumu ajana ikinci kez yazıyorsanız, bunu otomatik bir kontrole dönüştürme işareti budur: bir test, bir linter kuralı veya make check içinde bir grep-guard.
E2E, ajanın davranışını çalıştırmadan önce değiştirir
Uçtan uca testin değeri yalnızca yakalanan hatalarda değildir. Ajan işinin bir E2E senaryosuyla kontrol edileceğini biliyorsa, kodu farklı yazar: yalnızca kendi fonksiyonunu değil komşu bileşenle olan sözleşmeyi düşünür, hata yollarını ele alır ve mimari sınırları çiğnemez, çünkü ihlal çalıştırmada ortaya çıkacaktır. Kontrol girdide bir sınır gibi çalışır, yalnızca çıktıda bir filtre gibi değil.
Bir koşul vardır: E2E ancak sistemin sınırları netse anlamlıdır. Spagetti mimaride uçtan uca bir test yalnızca spagettinin çalıştığını kanıtlar ve tasarımın nerede bozulduğunu göstermez. Bu yüzden önce katmanları ve bağımlılık kurallarını sabitleyin, sonra uçtan uca senaryoları kurun. Ajanlar depodaki mevcut desenleri, iyi olanları da kötüleri de, kolayca kopyalar; sınırları ilk günden koyun.
Sık yapılan hatalar
- Ajandan "daha dikkatli kontrol et" istemek. Context aynı kalır, değerlendirme yine şişik çıkar.
- 100% kapsamı garanti sanmak. Unit kapsamı bağlantıları kapsamaz.
- Özellik onaylanmadan refactoring yapmak. Sıra katıdır: doğruluk → performans → stil.
- En çok bozulan şeyi E2E'de mock'lamak. Veritabanı, migration'lar ve yapılandırma uçtan uca testte gerçek çalışmalıdır; ücretli dış API'ler test sunucusuyla değiştirilebilir.
Sonuç
Ajan "hazır" yazdığında ve özellik çalışmadığında, sorun neredeyse her zaman modelde değil doğrulama sürecindedir. Verification gap'i sayın, AGENTS.md içinde Definition of Done yazın, E2E'yi zorunlu kılın, hazır işaretini bir betiğe bırakın ve bağımsız bir değerlendirici ekleyin. Bu düzen, gerçek bir proje üzerinde uygulamalı bir kursta ayrıntılı olarak gösterilmektedir.
Sık sorulan sorular
Ajanın E2E testlerini kendisinin çalıştırmasını sağlayabilir miyim?
Evet, komut AGENTS.md içinde Definition of Done'un zorunlu bir parçası olarak yazılmışsa ve ajanın gerekli araçlara erişimi varsa. Ajanın hafızasına bağlı kalmamak için ayrıca bir hook veya CI ile de çalıştırın.
E2E testleri yavaş; her değişiklikte mi çalıştırmalıyım?
Hayır. Lint, tipler ve unit testler her zaman çalışsın. E2E, birden fazla katmanı etkileyen değişikliklerde ve özelliği hazır ilan etmeden önce çalıştırılsın.
Ajanın yaptığı frontend nasıl test edilir?
Tarayıcıda gerçek arayüze tıklayan uçtan uca bir testle (örneğin Playwright), görünüm için ise kontrol listesiyle elle kontrol veya değerlendiricinin inceleyeceği ekran görüntüleriyle.
Değerlendirici yerine kod incelemesi yeterli olmaz mı?
İnsan incelemesi faydalıdır, ama pahalıdır ve saatlerce süren özerk çalışmaya ölçeklenmez. Rubrikli bir değerlendirici bariz sorunları insana ulaşmadan yakalar; tartışmalı konularda karar yine insanındır.
Bu konu “Harness Engineering: AI Ajanları İçin Güvenilirlik Mühendisliği” kursunda uygulamalı olarak ayrıntılı işleniyor.
- Ajan 'Hazır' Dedi, Özellik Çalışmıyor: Doğrulama Boşluğu ve Definition of Done
- Tüm Testler Yeşil, Özellik Bozuk: AI Ajanları İçin E2E Testler ve Mimari Kurallar