Neden önemli?
Bugün profesyonel geliştiricilerin büyük kısmı günlük işinde bir yapay zekâ asistanı kullanıyor: sohbet arayüzünde Claude ya da ChatGPT, editörün içinde GitHub Copilot ya da Cursor, terminalde Claude Code gibi ajanlar. Bu araçlar iskelet kodu, test, belge ve yeniden düzenlemeyi dakikalara indirir. Ama asistan emin görünen bir dille yanılabilir: var olmayan bir fonksiyonu çağırabilir, iki yıl önce kaldırılmış bir API kullanabilir, testsiz bırakıldığında yalnızca "mutlu yol"u çalıştıran kod yazabilir. Bu derste asistanı hızlandırıcı olarak kullanmayı, ama son sözü her zaman testlere ve senin muhakemene bırakmayı öğreneceksin. LLM API dersinde modeli kendi kodundan çağırmayı görüyorsun; burada asistanı bir çalışma arkadaşı olarak kullanmaya odaklanıyoruz.
| Araç türü | Örnek | En iyi olduğu iş | Dikkat |
|---|---|---|---|
| Sohbet asistanı | Claude, ChatGPT, Gemini | Tasarım tartışması, açıklama, fonksiyon/test yazma, hata ayıklama (debugging) | Projenin tamamını görmez; bağlamı sen verirsin |
| Editör içi tamamlama | GitHub Copilot, Cursor | Satır/blok tamamlama, tekrarlayan kalıplar | Hızlı kabul alışkanlığı; okumadan Tab'a basma |
| Kodlama ajanı | Claude Code ve benzerleri | Çok dosyalı değişiklik, test çalıştırıp düzeltme döngüsü | Değişikliği diff olarak incele, yetkileri sınırla |
İyi istemin anatomisi
Asistan senin projeni, veri biçimini, Python sürümünü ve kurum kurallarını bilmez. Ne kadar belirsiz sorarsan o kadar "ortalama internet kodu" alırsın. İyi bir kodlama istemi (prompt) şu parçaları içerir:
| Parça | Örnek |
|---|---|
| Rol ve bağlam | "Anadolu Market'in sipariş servisinde çalışıyorum; Python 3.12, yalnız standart kütüphane." |
| Görev | "Siparişleri şehre göre gruplayıp ciroyu hesaplayan fonksiyon yaz." |
| Girdi-çıktı örneği | "Girdi: [{"sehir": "İzmir", "tutar": 100}] → Çıktı: {"İzmir": 100.0}" |
| Kısıtlar | "Para için Decimal kullan, tip ipuçları (type hint) ekle, harici paket yok, iptal siparişleri sayma." |
| Kenar durumlar | "Boş liste, eksik şehir alanı, negatif tutar ne olmalı?" |
| Çıktı biçimi | "Önce fonksiyonu, sonra pytest testlerini ayrı kod bloklarında ver; açıklama 3 maddeyi geçmesin." |
ZAYIF: python ile ciro hesaplama kodu yaz
GÜÇLÜ: Python 3.12, yalnız standart kütüphane.
Görev: sehir_ciro(siparisler: list[dict]) -> dict[str, Decimal]
Her sipariş {"sehir": str, "tutar": str, "durum": str}; tutar metin ("149.90").
Kurallar: durum "iptal" olanları atla; şehir yoksa "Bilinmiyor".
Örnek: [{"sehir":"İzmir","tutar":"100.50","durum":"teslim"}] -> {"İzmir": Decimal("100.50")}
Önce bu kurallar için 5 pytest testi yaz, sonra testleri geçen fonksiyonu yaz.
Varsayım yapman gerekirse kodun üstünde madde olarak yaz.
Doğrulama döngüsü: güven ama test et
Asistanın kodunu üç adımda doğrularsın: oku (anlamadığın satırı kabul etme), çalıştır (gerçek örnekle), test et (kenar durumlarla). Aşağıda bir asistanın yazdığı, ilk bakışta doğru görünen bir fonksiyon var; küçük bir test kümesi hatayı hemen ortaya çıkarıyor.
def ortalama_sepet(tutarlar):
"""Yapay zekâ çıktısı: sepet tutarlarının ortalaması."""
return sum(tutarlar) / len(tutarlar)
denemeler = [[100, 200, 300], [49.9], []]
for girdi in denemeler:
try:
print(girdi, "->", ortalama_sepet(girdi))
except Exception as e:
print(girdi, "-> HATA:", type(e).__name__, e)
Boş liste, "mutlu yol" testinde hiç akla gelmeyen ama üretimde ilk gün karşına çıkacak durumdur. Asistana "bu fonksiyonun kenar durumlarını listele ve her biri için test yaz" demek, en ucuz kalite yatırımıdır. Testleri sen de okumalısın: asistan bazen yanlış kodu "doğrulayan" yanlış test yazar.
Yapay zekânın tipik hataları
| Hata türü | Örnek | Nasıl yakalarsın? |
|---|---|---|
| Uydurma API (halüsinasyon) | liste.push(x), metin.contains("a"), math.clamp | Çalıştır; resmi belgeye bak; hasattr/help() |
| Eski sürüm kodu | from collections import Mapping (3.10'da kaldırıldı), pandas df.append (2.0'da kaldırıldı) | Sürümünü istemde belirt; uyarıları (DeprecationWarning) oku |
| Sessiz mantık hatası | Değiştirilebilir varsayılan argüman, sayfalamada bir eksik/fazla, metin olarak sayı sıralama, float ile para | Kenar durum testleri, özellik (property) testleri |
| Güvenlik açığı | f-string ile SQL, eval, shell=True, random ile token | Web güvenliği kontrol listesi, bandit |
| Aşırı geniş hata yakalama | except Exception: return None | Hangi hatanın neden yakalandığını sor |
| Gereksiz bağımlılık / karmaşıklık | Tek satırlık iş için paket önermek | "Yalnız standart kütüphane" kısıtı |
Uydurma API şüphesinde kendi ortamında hızlıca kontrol et:
import builtins, importlib
def api_var_mi(yol: str) -> bool:
modul_adi, *oznitelikler = yol.split(".")
try:
nesne = importlib.import_module(modul_adi)
except ImportError:
if not hasattr(builtins, modul_adi):
return False
nesne = getattr(builtins, modul_adi)
for ad in oznitelikler:
if not hasattr(nesne, ad):
return False
nesne = getattr(nesne, ad)
return True
for yol in ["str.removeprefix", "str.contains", "list.push", "math.isclose", "math.clamp", "json.loads"]:
print(f"{yol:18} {api_var_mi(yol)}")
Kod inceleme kontrol listesi
- Anlıyor muyum? Her satırı açıklayabiliyor musun? Açıklayamıyorsan asistana sor ya da yeniden yazdır.
- Çalışıyor mu? Gerçek bir örnekle çalıştır; hatasız çalışması doğru olduğu anlamına gelmez.
- Testi var mı? Mutlu yol + boş girdi + sınır değerler + hatalı girdi.
- API gerçek mi, güncel mi? Kullandığın Python ve paket sürümüyle uyumlu mu?
- Güvenli mi? SQL parametreli mi, kullanıcı girdisi doğrulanıyor mu, gizli bilgi koda gömülü mü?
- Projeye uyuyor mu? Adlandırma, hata yönetimi, loglama ve klasör yapısı ekip standardına uygun mu?
- Lisans sorunu var mı? Uzun, tanıdık görünen bir blok bilinen bir projeden kopya olabilir.
Yeniden düzenleme ve belge yazdırma
Asistanlar çalışan ama dağınık kodu toparlamada çok iyidir: "davranışı değiştirmeden fonksiyonu küçük parçalara böl, tip ipuçları ve docstring ekle" de. Kural: önce test, sonra yeniden düzenleme. Testler, asistanın davranışı sessizce değiştirmediğini kanıtlar. Docstring'e örnek koyarsan doctest ile belge de test olur:
import doctest
from decimal import Decimal
def kdv_ekle(tutar: Decimal, oran: Decimal = Decimal("0.20")) -> Decimal:
"""Tutara KDV ekler ve kuruşa yuvarlar.
>>> kdv_ekle(Decimal("100"))
Decimal('120.00')
>>> kdv_ekle(Decimal("49.90"), Decimal("0.10"))
Decimal('54.89')
"""
return (tutar * (1 + oran)).quantize(Decimal("0.01"))
runner = doctest.DocTestRunner()
for test in doctest.DocTestFinder().find(kdv_ekle, "kdv_ekle", globs={"Decimal": Decimal, "kdv_ekle": kdv_ekle}):
runner.run(test)
print("doctest:", runner.tries, "örnek,", runner.failures, "hata")
Yapay zekâ çıktısını programla işlemek
Asistanı bir boru hattında (pipeline) kullandığında (ör. müşteri yorumlarını sınıflandırma) yanıtı insan değil kod okur. Modelden JSON iste, ama gelen metnin her zaman temiz JSON olacağını varsayma: başında açıklama cümlesi, etrafında üç ters tırnaklı kod çiti, sonunda fazladan virgül olabilir. Önce ayıkla, sonra json.loads, sonra şemaya göre doğrula; doğrulama başarısızsa hatayı modele geri gönderip yeniden dene.
import json, re
CIT = chr(96) * 3 # üç ters tırnak
yanit = ("Tabii, sonuç aşağıda:\n" + CIT + "json\n"
'{"yorum_id": 17, "duygu": "olumsuz", "konu": "kargo", "guven": 0.92}\n' + CIT)
eslesme = re.search(CIT + r"(?:json)?\s*(\{.*?\})\s*" + CIT, yanit, re.S)
veri = json.loads(eslesme.group(1) if eslesme else yanit)
hatalar = []
if veri.get("duygu") not in {"olumlu", "olumsuz", "notr"}:
hatalar.append("duygu geçersiz")
if not (isinstance(veri.get("guven"), (int, float)) and 0 <= veri["guven"] <= 1):
hatalar.append("guven 0-1 aralığında olmalı")
print(veri)
print("hatalar:", hatalar)
Pydantic dersinde gördüğün modellerle bu doğrulamayı tek satıra indirebilirsin; birçok LLM API'si de "yapılandırılmış çıktı" (structured output) ya da araç çağrısı (tool use) özelliğiyle şemaya uyan JSON üretmeyi destekler.
Gizli bilgi, kişisel veri ve lisans
Lisans ve telif tarafında: üretilen kod bazen açık kaynak bir projeden neredeyse birebir parça içerebilir; GPL gibi "copyleft" lisanslı kod, kapalı kaynak ürününe girerse hukuki risk doğurur. Uzun, özgün görünen blokları kaynak aramasıyla kontrol et, editör araçlarının "kamuya açık kodla eşleşmeyi engelle" ayarlarını aç ve kurumunun yapay zekâ kullanım politikasına uy. Yazdığın koddan, kim üretmiş olursa olsun, sen sorumlusun.
Derinleş: ajanlı kodlama iş akışı
Sohbet asistanı sana kod parçası verir; kodlama ajanı (coding agent) ise depoyu okur, dosyaları düzenler, testleri ve komutları çalıştırır, çıktıya bakıp kendini düzeltir. Claude Code (terminal ve IDE), Cursor'ın ajan modu ve GitHub'daki ajanlar bu sınıftadır. Güç arttıkça senin rolün "kodu yazan"dan görevi tanımlayan, sınırları çizen ve sonucu inceleyen kişiye kayar. Bu bölüm o rolü anlatıyor.
Döngü: keşfet → planla → uygula → doğrula
- Keşfet: Ajandan önce kodu okumasını iste ("ilgili dosyaları bul, henüz değiştirme"). Yanlış varsayımlar en ucuz bu aşamada yakalanır.
- Planla: Değişikliği adımlara bölmesini ve planı sana göstermesini iste. Planı onayla ya da düzelt.
- Uygula: Küçük adımlarla; her adım ayrı commit. Büyük tek seferlik değişiklik incelenemez.
- Doğrula: Testler, linter, tip denetimi ajanın kendi çalıştırabileceği komutlar olmalı. "Bitti" demesi değil, testlerin yeşil olması ölçüttür.
İyi görev tanımı bir iş biletine benzer: amaç, kapsam (hangi dosyalar/modüller), kapsam dışı, kabul ölçütleri (hangi testler geçmeli), kısıtlar (yeni bağımlılık yok, genel API değişmez). Büyük işi bağımsız alt görevlere böl: "CSV içe aktarmayı yeniden yaz" yerine önce "mevcut davranışı belgeleyen testleri yaz", sonra "ayrıştırıcıyı ayır", sonra "hata raporlamayı ekle".
gorevler = {
"testleri_yaz": [],
"ayristiriciyi_ayir": ["testleri_yaz"],
"hata_raporu": ["ayristiriciyi_ayir"],
"belgeleri_guncelle": ["ayristiriciyi_ayir"],
}
from graphlib import TopologicalSorter
print(list(TopologicalSorter(gorevler).static_order()))Kurallar dosyası: projenin hafızası
Ajan her oturuma sıfır bağlamla başlar. Projenin kökündeki kurallar dosyası (Claude Code için CLAUDE.md, Cursor için .cursor/rules, birçok araç için AGENTS.md) her oturumda otomatik okunur. İçine yalnız kalıcı ve doğrulanabilir bilgi koy: nasıl kurulur, nasıl test edilir, kod stili, dokunulmayacak yerler. Uzun ve belirsiz dosya ("temiz kod yaz") işe yaramaz; kısa ve komut odaklı olsun.
# CLAUDE.md — Anadolu Market sipariş servisi ## Komutlar - Kurulum: uv sync - Test: uv run pytest -q (her değişiklikten sonra çalıştır) - Lint/biçim: uv run ruff check . && uv run ruff format --check . ## Kurallar - Python 3.12, tip ipuçları zorunlu; para tutarları Decimal. - Yeni bağımlılık eklemeden önce sor. - migrations/ ve config/prod.yaml dosyalarını DEĞİŞTİRME. - Veritabanı sorgularında f-string YOK; parametre kullan. ## Mimari - api/ (FastAPI uç noktaları) → servis/ (iş kuralları) → depo/ (SQL). Katman atlama yok.
MCP ile araç bağlamak
Model Context Protocol (MCP), ajanın dış araçlara (veritabanı, hata takip sistemi, belge deposu, tarayıcı) standart bir arayüzle bağlanmasını sağlar. Bir MCP sunucusu araçlarını adı, açıklaması ve JSON şemalı girdileriyle ilan eder; ajan gerektiğinde çağırır. LLM API dersindeki araç çağırma döngüsünün standartlaşmış hâlidir. Güvenlik kuralı: ajana yalnız gereken araçları ve en az yetkiyi ver (salt okunur veritabanı kullanıcısı, test ortamı anahtarı). Araçtan gelen metin de güvenilmeyen girdidir; istem enjeksiyonu taşıyabilir.
// .mcp.json (proje kökü) — örnek yapı
{
"mcpServers": {
"market-db": {
"command": "uvx",
"args": ["mcp-server-sqlite", "--db-path", "./yerel_kopya.db"]
}
}
}Ajan çıktısını PR olarak incelemek
Ajanın değişikliği bir dal (branch) ve çekme isteği (pull request) olarak gelmeli; ana dala doğrudan yazmamalı. İncelerken bir insanın kodunu incelerken baktığın her şeye ek olarak ajanlara özgü risklere bak:
| Kontrol | Neden? |
|---|---|
| Diff kapsamı görevle uyumlu mu? İlgisiz dosya değişmiş mi? | Ajanlar "bu arada" düzeltmeler yapar; incelenmemiş değişiklik riski. |
Testler silinmiş, atlanmış (skip) ya da beklenen değer kodu tutacak şekilde değiştirilmiş mi? | Testi geçirmenin en kolay yolu testi bozmaktır. |
| Yeni bağımlılık var mı? Paket gerçekten var mı, adı doğru mu? | Uydurma ya da adı benzeyen kötü niyetli paketler (typosquatting). |
| Sır, anahtar, kişisel veri eklenmiş mi? | Ajan log ya da örnek veriyi koda gömebilir. |
Hata yutma: çıplak except, pass, sessiz None | Çalışıyor gibi görünür, hatayı gizler. |
| CI yeşil mi ve sen de yerelde çalıştırdın mı? | "Testler geçti" cümlesi kanıt değildir. |
rm -rf, üretim veritabanı, git push --force, dağıtım komutları insan onayı istemeli. Mümkünse ajanı konteyner ya da ayrı bir çalışma ağacında (git worktree) çalıştır.Projeye kalıcı bağlam ver
Kodlama ajanları proje kökündeki talimat dosyalarını (ör. Claude Code için CLAUDE.md) okuyabilir: Python sürümü, test komutu, kod stili, yasaklı kalıplar, klasör yapısı. Her istemde aynı bağlamı tekrar yazmak yerine bir kez yazarsın. Ajanın çalıştırabileceği komutları ve erişebileceği dosyaları sınırla; değişikliği her zaman diff olarak incele.
Testi sözleşme olarak kullan
Önce kendi yazdığın (ya da okuyup onayladığın) testler, sonra "bu testleri geçen kodu yaz" istemi: buna yapay zekâ ile TDD diyebilirsin. Testler sözleşmedir; asistan kodu değiştirdikçe testler korur. Testin de yanlış olabileceğini unutma: bilerek hatalı bir sürüme karşı testi çalıştırıp başarısız olduğunu görmek (mutasyon testi fikri), testin işe yaradığını kanıtlar.
Değerlendirme (eval) kümesi
Yapay zekâyı bir ürün özelliğinde (ör. yorum sınıflandırma) kullanıyorsan, beklenen çıktısı bilinen 50-200 örneklik bir değerlendirme kümesi kur; istem ya da model değiştiğinde doğruluğu yeniden ölç. Sezgiyle "daha iyi oldu" demek yerine sayıyla karar verirsin.