Yapay zekâ araçlarının çıktısının belli bir kalite eşiğini aşması ve tecrübeli yazılımcıların da artık bir cismin yaklaştığına ikna olmasının üzerinden bir yıl ya geçmiştir ya geçmemiştir.
Biz de bir süredir Sobamail’in yazılım geliştirme süreçlerini yapay zekâ etrafında yeniden şekillendirme konusuna da vakit ayırıyoruz. Bu çalışmaların sonucunda bazı süreçlerin yavaş yavaş netleştiğini görünce, edindiğimiz deneyimleri paylaşmak ve üzerine fikir alışverişi yapabileceğimiz bir zemin oluşturmak için bu yazıyı yazmaya karar verdim.
Öncelikle şu kısım net: Yazılım hâlâ yazılım. Zaman baskısıyla işin gereklerini göz ardı ederseniz, "gelecekteki siz"in başını yine belaya sokuyorsunuz. Ürettiğiniz kodun arkasını düzenli olarak toplamanız hâlâ gerekiyor. Karmaşıklıkla mücadele de aynı kararlılıkla devam ediyor.
Yani evet, büyük dil modeli teknolojisi -doğru kullanıldığında- ekibinizin çalışan koda ulaşmasını ciddi biçimde hızlandırıyor. Ama sizi doğru yola sokmuyor. Yalakalığa programlanmış bir dil modelinden de zaten bu seviyede bir beceri beklemek doğru değil.
Biz yeni oyuncağımıza biraz temkinli yaklaştık diyebilirdim ama başkalarıyla konuştuğumda gördüğüm hikâye az çok aynı. Önce web arayüzünden kod kopyala-yapıştır yaparak, sonra da IDE entegrasyonları üzerinden ufak tefek fonksiyonlar yazdırarak yama üretme işi bugün yeni özelliklerin uçtan uca dil modeli tarafından gerçeklendiği bir dünyaya yerini bırakmış durumda.
Bir dil modelinin bir işi tamamlayabilmesi için, istenen işin bütün detayları ve işin sonucunun model tarafından belirlenen bağlam kapasitesini aşmaması gerekli. Hatta tecrübelerimizden biliyoruz ki, bağlamdaki veri arttıkça modelin kendisine verilen talimatları göz ardı etme olasılığı da artıyor. Bu da dil modelleri üzerine kurulan bütün araçların karşılaştığı ortak soruyla bizi yüzleştiriyor: Modelden istenen işin eksiksiz yapılabilmesi için modelin bağlamında hangi veri bulunmalı?
Bizim durumumuza uyarladığımızda ise sormamız gereken soru biraz şekil değiştirse de özü aynı: Uzun süren işlemlerde modelin yolunu kaybetmemesi için ne yapmak gerekiyor?
Bunu başta işi bir bağlam birimine sığacak parçalara bölüp modele doğru çıktıyı ürettirecek promptlar vererek çözmeye çalıştık. Ancak ilk büyük işte (yamalı bohçaya dönmüş RPC modülümüzdeki kodun tekrar classlara dağıtılması) bu zulme katlanılmayacağını net bir şekilde anladık. Eğer yıllarını kendisini mesleğinde geliştirmeye adamış bir mühendisin bütün mesaisi dil modelinin elinden tutmakla harcanacaksa, burada ne etkin bir kaynak kullanımından söz edilebilir ne de moralleri yüksek tutmak mümkün olabilir.
Eğer yazılım projesi bir ağaçsa, kod bu ağacın yapraklarıdır.
Yaprak diğer yaprakları bilmese de olur. Bağlı olduğu dalı ve bu dal üzerinden ağacın bütünüyle olan ilişkisini bilmesi yeterlidir.
Bu prensipten hareketle Opus’la bir meta-tartışmaya girdim ve neticede modelin “handoff looping” dediği bir skill ortaya çıktı.
Handoff loop mantığında yapılması gereken işi bir paragraf yazı olarak modele
aktarıyorsunuz. Model bu paragraf rehberliğinde mevcut kodunuzda bir keşfe
çıkıyor ve sizin paragrafınızı sizin kodunuzun sunduğu imkânlar açısından
inceleyip eksikleri belirliyor ve keşif raporunu ve bu eksikleri nasıl parça
parça gidereceğini (bu parçalara da “item” diyor) detaylıca anlattığı birkaç
tane .md belgesi (bunlara “doc set” diyor) ve bir shell script (buna
“driver” diyor) hazırlayarak size dönüş yapıyor.
Bundan sonra (modelin “signoff round” dediği) toplantı aşamasına geçiliyor ve model size sorularını soruyor. Siz bu soruları cevapladıkça (cevaplara “owner verdict” diyor) bunları döngü belgelerine olduğu gibi işliyor ve ortaya bir iş planı çıkıyor. Bu aşamanın amacı modelin yol boyunca karşılaşacağı kararları peşinen almak: hangi testlerin devre dışı bırakılabileceği, hangi dosyalara dokunulamayacağı gibi izinler (bunlara “standing sign-off” diyor) baştan belgeye yazılıyor ki model döngü sırasında her adımda size dönmek zorunda kalmasın. Bütün cevaplar gelince loop çalışmaya hazır (yani “armed”) hale geliyor.
Modelle toplantınız bittikten sonra driver scriptini çalıştırıyorsunuz; böylece mühendisin mesaisi bitiyor ve model arayüzsüz çalışmaya geçiyor. Driver script, modeli sürekli aynı prompt ile çalıştırarak verilen işte ilerledikçe güncellediği belgeleri tekrar tekrar okumasını sağlıyor. Model her adımda bir parçanın tamamını ya da sığdığı kadar adımını (bir adımı yarıda bırakmadan) yapıp commitlerini yapıyor, belgeleri kaldığı yeri gösterecek şekilde güncelliyor ve kendisini kapatıyor. Bu sayede modelin temiz bağlamla sürekli bir sonraki işi yaptığı bir yapı ortaya çıkıyor. Döngü ya işi tamamlayıp duruyor ya da takıldığı yerlerde size soru bırakıp diğer işlerle devam ediyor; yapabileceği iş kalmayınca duruyor, varsa soruları cevaplayarak driver’ı tekrar çalıştırıyorsunuz.
Böylece her iş parçası, döngü belgelerinden ağacın ilgili kısmını ve yaprağı ekleyeceği dalı öğrenip yeni bir yaprak ekliyor. Daha önce eklenen yaprakları bütün detayıyla bilmiyor ama zaten bilmesi de gerekmiyor.
Modelleri bu mantıkla günlerce çalıştırmak mümkün. Ancak bu, sonuç için günlerce
beklediğimiz bir kara kutu ile çalıştığımız anlamına gelmiyor. Arada bir, temiz
bir oturumda report on <döngü adı> loop status gibi bir prompt ile yapılan iş
denetlenebiliyor. Hatta modelin işi sürerken arka planda modelin mühendis için
bıraktığı soruları (buna “parked questions” diyor) yanıtlayarak veya iş planında
değişiklikler yaptırarak süregiden çalışmaya yön vermek de mümkün.
Bunları yaparken modelin kendi belgelerini okumayı pek tavsiye etmiyorum. Özeti de, bıraktığı sorulara cevaplarınızı belgeye işlemeyi de temiz bir oturuma yaptırmak çok daha kolay.
Modelin işi bittikten sonra ise sıra işi modelden teslim almaya geliyor. Bu başlı başına bir konu, onu belki ayrı bir yazıda anlatırım. Gerçi yeni bir konu da değil: dil modellerinden önceki hayatta da yazılımcıdan işi teslim almak bir iş kalemiydi. Burada yalnızca döngüyü ilgilendiren kısmına değineyim: teslim alırken yakaladığım hataları ya arayüzlü modda hızlıca düzeltiyorum ya da döngü kapsamını genişleterek modeli arayüzsüz modda çalıştırmaya devam ediyorum.
Bazen işin daha fazla uzamaması adına yeni kodu canlıda kapalı olacak şekilde merge edip büyük eksikleri ikinci, hatta üçüncü döngüler kurarak gidermek gerekebiliyor. Handoff looping yöntemi döngü belgelerini küçük tutmakta iyi olsa da, kapsam genişletildiğinde keşif raporu eskiyor: her geçiş eski raporu bir de o zamandan beri yapılanlarla birlikte okuyup aradaki farkı kendi başına kapatmak zorunda kalıyor. Bu da yine bağlamı doldurarak model çıktısının kalitesinin düşmesine sebep oluyor. Bunun yerine tekrar keşif yaparak beyaz bir sayfadan başlamak daha iyi sonuç veriyor.
Özetle handoff loop mühendisi işin ortasından çekip iki ucuna koyuyor: işi başlamadan önce tarif ediyorsunuz, bittikten sonra teslim alıyorsunuz; aradaki yaprakları model yazıyor.
Skill’i GitHub’a koydum. Eğer projeniz modern bir cross-platform C++ uygulaması değilse biraz adaptasyon gerekecektir. Deneyen olursa sonucunu duymak isterim.