---
title: 'Her Aracın Döngüden Ayrı Sınanması'
source: 'https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/arac-duzeyi-testler'
course: 'Yapay Zekâ Ajanları'
language: tr
updated: '2026-08-17T18:06:32+00:00'
license: 'CC BY-SA 4.0'
---

# Her Aracın Döngüden Ayrı Sınanması

Beş araca konan on bir kurgu kusurun araç düzeyi sınamayla ne kadarının yakalandığının sayılması: on bir durumluk sınama kümesi değer kusurlarının dördünü de, sözleşme kusurlarının üçünü de düşürüyor, ama tanım kusurlarının dördünde de tertemiz geçiyor. Toplam yakalama 0,6364, sınıf bazında 1,0000 ve 1,0000 ile 0,0000. Görünmeyen sınıfın mekanizması yazılıdır: sınama kümesi aracın işlevini doğrudan çağırır, döngü ise aracın bildirdiği alan listesinden geçer ve tanım kusuru yalnız o listede durur. Katmanın karşılığı koşumdan bağımsızlıktır: aynı görev kümesinde döngünün düşen senaryosu tohum değişince G23'ten G15'e kayarken, sınama kümesinin düşen durum kümesi iki koşumda da boş ve aynıdır.

Bir önceki ders bir ajan koşumunu iki sütuna ayırdı: dizinin doğruluğu ve yanıtın doğruluğu. İkisi
de araçların doğru çalıştığını varsayar. Varsayım kaldırıldığı anda dört durum tablosu yetersiz
kalır: bir görevin düştüğünü söyler, hangi aracın bozuk olduğunu söylemez.

Bu ders araçları döngüden söker ve tek tek sınar. Sınama seviyeleri M21 Yazılım Kalitesi ve Test
müfredatının Kalite ve Test Temelleri kursunda sınıflandırıldı, birim sınamanın anatomisi Birim
Testi ve Test Odaklı Geliştirme kursunda kuruldu; **yordam burada tekrarlanmaz**. Soru ölçüye
aittir: beş araca konan kusurların **kaçı** bu katmanda görünüyor ve **hangi sınıfı** bu katman
hiç göremiyor.

- **KL7.** Kusurlar **kurgudur** ve araç tablosunun bir **kopyasına** konur; asıl tablo değişmez.
- **KL8.** Üç kusur sınıfı vardır. **Değer kusuru**: işlev yanlış sonuç verir. **Sözleşme
  kusuru**: işlev, hata bildirmesi gereken yerde bildirmez. **Tanım kusuru**: işlev doğrudur,
  **bildirilen alan listesi** yanlıştır.
- **KL9.** **Araç düzeyi sınama** aracı döngünün dışında, doğrudan işlevini çağırarak sınar;
  beklenen çıktı elle yazılmıştır.
- **KL10.** Bir kusur, sınama kümesinden **en az bir durum düşürüyorsa** yakalanmış sayılır.
- **KL11.** Sınama kümesi **tohum kullanmaz**; döngü ölçüsü sekiz adımlık bütçeyle ve iki tohumla
  koşulur.

## Ortam, Araç Tanımları ve Ajan Döngüsü

Bir araç iki parçadan oluşur: **işlev** hesabı yapar, **tanım** o işlevin hangi alanları
alacağını bildirir ve döngü çağrıyı yalnız bildirilen alanlarla kurar.

```python
# KURGUDUR. Abone kayitlari, belge dagarcigi, arac kumesi, gorev kumesi ve ajan
# dongusu ders icinde uretilir; gercek bir uc nokta ya da arac sunucusu cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


def belirtecle(m):
    b = []
    for s in str(m).lower().replace(",", " , ").split():
        while len(s) > 5:
            b.append(s[:5])
            s = s[5:]
        b.append(s)
    return b


def izno(m):
    h = 2166136261
    for c in str(m):
        h = ((h ^ ord(c)) * 16777619) & M32
    return h


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
ABONE = {}
for i in range(60):
    r = uretec(TOHUM + 31 * i)
    onceki = 10000 + int(r() * 80000)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "onceki": onceki, "simdiki": onceki + int(r() * 70)}
BASAMAK, USTU = ((10, 4), (25, 7), (40, 11)), 16
ISYERI_KAT = 2
BELGE = {"TR01": "tarife basamaklari 10 25 40 metrekup ve ustu",
         "TR02": "isyeri abonesinde birim ucret iki katidir",
         "SY01": "endeks farki tuketimi verir sayac donusu 100000 eklenir",
         "AR01": "ariza kaydi acildiginda saha ekibi iki gun icinde gider",
         "KS01": "planli kesinti duyurusu bir hafta once yayimlanir"}

CAGRI = [0]                              # arac cagri sayaci


def _bedel(m3, tip):
    kalan, alt, top = m3, 0, 0
    for ust, birim in BASAMAK:
        dilim = min(kalan, ust - alt)
        if dilim <= 0:
            break
        top += dilim * birim
        kalan -= dilim
        alt = ust
    top += kalan * USTU
    return top * (ISYERI_KAT if tip == "isyeri" else 1)


def t_sayac_oku(abone=None, **_):
    a = ABONE.get(abone)
    if a is None:
        return {"hata": "abone yok"}
    return {"onceki": a["onceki"], "simdiki": a["simdiki"], "tip": a["tip"], "bolge": a["bolge"]}


def t_tuketim(onceki=None, simdiki=None, **_):
    if onceki is None or simdiki is None:
        return {"hata": "eksik alan"}
    f = simdiki - onceki
    return {"m3": f + 100000 if f < 0 else f}


def t_tarife(m3=None, tip="mesken", **_):
    if m3 is None:
        return {"hata": "eksik alan"}
    return {"tutar": _bedel(m3, tip)}


def t_belge(anahtar=None, **_):
    if not anahtar:
        return {"hata": "eksik alan"}
    ab = set(belirtecle(anahtar))
    en, kod = -1, None
    for k, m in BELGE.items():
        o = len(ab & set(belirtecle(m)))
        if o > en:
            en, kod = o, k
    return {"kod": kod, "metin": BELGE[kod]} if en > 0 else {"hata": "bulunamadi"}


def t_kayit(abone=None, sutun=None, deger=None, **_):
    if not (abone and sutun and deger is not None):
        return {"hata": "eksik alan"}
    return {"yazildi": True, "abone": abone, "sutun": sutun, "deger": deger}


ARAC = {
    "sayac_oku":  {"islev": t_sayac_oku, "alan": ("abone",), "yan_etki": False},
    "tuketim":    {"islev": t_tuketim, "alan": ("onceki", "simdiki"), "yan_etki": False},
    "tarife":     {"islev": t_tarife, "alan": ("m3", "tip"), "yan_etki": False},
    "belge_ara":  {"islev": t_belge, "alan": ("anahtar",), "yan_etki": False},
    "kayit_yaz":  {"islev": t_kayit, "alan": ("abone", "sutun", "deger"), "yan_etki": True},
}
ARAC_ADI = list(ARAC)


def cagir_arac(ad, durum):
    """Sema disindaki alanlar suzulur; eksik alan aracin kendi hatasidir."""
    CAGRI[0] += 1
    if ad not in ARAC:
        return {"hata": "arac yok"}
    kw = {a: durum[a] for a in ARAC[ad]["alan"] if a in durum}
    return ARAC[ad]["islev"](**kw)


GOREV = []
for i in range(24):
    r = uretec(TOHUM + 9973 * i)
    ab = f"A{1 + int(r() * 60):03d}"
    t = ayrik(r(), [0.30, 0.22, 0.20, 0.28])
    if t == 0:
        GOREV.append({"ad": f"G{i + 1:02d}", "tur": "bedel", "abone": ab,
                      "istek": f"{ab} abonesinin bu donem bedeli nedir",
                      "dizi": ("sayac_oku", "tuketim", "tarife")})
    elif t == 1:
        GOREV.append({"ad": f"G{i + 1:02d}", "tur": "tuketim", "abone": ab,
                      "istek": f"{ab} abonesinin bu donem tuketimi kac metrekup",
                      "dizi": ("sayac_oku", "tuketim")})
    elif t == 2:
        GOREV.append({"ad": f"G{i + 1:02d}", "tur": "belge", "abone": ab,
                      "istek": "sayac donusunde endeks farki nasil hesaplanir",
                      "dizi": ("belge_ara",)})
    else:
        GOREV.append({"ad": f"G{i + 1:02d}", "tur": "kayit", "abone": ab,
                      "istek": f"{ab} abonesinin bedelini hesapla ve kayda yaz",
                      "dizi": ("sayac_oku", "tuketim", "tarife", "kayit_yaz")})


def dogru_yanit(g):
    a = ABONE[g["abone"]]
    f = a["simdiki"] - a["onceki"]
    m3 = f + 100000 if f < 0 else f
    if g["tur"] == "tuketim":
        return m3
    if g["tur"] == "belge":
        return "SY01"
    return _bedel(m3, a["tip"])


def gorme(d):
    return max(0.34, 0.80 - 0.09 * (d - 1))       # dogru araci secme payi, derinlikle duser


def ajan(g, en_cok_adim=8, tohum=TOHUM, oz_elestiri=False, onay=False):
    """KURGU ajan dongusu. Gercek bir model cagrilmaz; arac secimi
    gorevin dizisine ve derinlige bagli tek bir cekilisten gelir."""
    r = uretec(tohum ^ izno(g["ad"]))
    alan = {"abone": g["abone"], "anahtar": g["istek"], "sutun": "bedel"}
    iz, adim, bel, hata = [], 0, 0, 0
    dizi, k = g["dizi"], 0
    while adim < en_cok_adim:
        adim += 1
        bel += 60 + 22 * len(iz)
        hedef = dizi[k] if k < len(dizi) else None
        if hedef is None:                          # is bitti , ajan durmali
            if r() < 0.88:
                return {"bitti": True, "adim": adim, "iz": iz, "belirtec": bel,
                        "hata": hata, "yanit": alan.get("sonuc"), "onay": alan.get("onay", 0)}
            sec = ARAC_ADI[int(r() * len(ARAC_ADI))]
        else:
            sec = hedef if r() < gorme(k + 1) else ARAC_ADI[int(r() * len(ARAC_ADI))]
        if ARAC[sec]["yan_etki"] and onay:
            alan["onay"] = alan.get("onay", 0) + 1
        if sec == "kayit_yaz":
            alan["deger"] = alan.get("sonuc")
        s = cagir_arac(sec, alan)
        iz.append((sec, "hata" in s))
        bel += 30
        if "hata" in s:
            hata += 1
            if oz_elestiri:
                bel += 40
            continue
        alan.update({kk: vv for kk, vv in s.items() if kk != "hata"})
        if sec == "tuketim":
            alan["sonuc"] = s["m3"]
        if sec == "tarife":
            alan["sonuc"] = s["tutar"]
        if sec == "belge_ara":
            alan["sonuc"] = s["kod"]
        if sec == hedef:
            k += 1
    return {"bitti": False, "adim": adim, "iz": iz, "belirtec": bel, "hata": hata,
            "yanit": alan.get("sonuc"), "onay": alan.get("onay", 0)}


print("arac       bildirilen alanlar        yan etki")
for ad, v in ARAC.items():
    print(f"{ad:<10} {', '.join(v['alan']):<24}  {v['yan_etki']}")
```

```
arac       bildirilen alanlar        yan etki
sayac_oku  abone                     False
tuketim    onceki, simdiki           False
tarife     m3, tip                   False
belge_ara  anahtar                   False
kayit_yaz  abone, sutun, deger       True
```

## Kusur Kümesi ve Sınama Kümesi

On bir kusur beş araca dağıtılır. Değer ve sözleşme kusurları **işlevi** değiştirir; tanım
kusurları işleve dokunmaz, yalnız **bildirilen alan listesini** oynatır. Sınama kümesi her araç
için bir olağan çağrı ve bir sınır durumu taşır.

```python
KUSUR = {                       # arac , sinif , aciklama , kusurlu islev ya da alan listesi
    "D1": ("sayac_oku", "deger", "simdiki endeksi onceki ile ayni",
           lambda abone=None, **_: ({"hata": "abone yok"} if abone not in ABONE
                                    else dict(ABONE[abone], simdiki=ABONE[abone]["onceki"]))),
    "D2": ("tuketim", "deger", "sayac donusunde 10000 ekliyor",
           lambda onceki=None, simdiki=None, **_: (
               {"hata": "eksik alan"} if onceki is None or simdiki is None
               else {"m3": simdiki - onceki + (10000 if simdiki < onceki else 0)})),
    "D3": ("tarife", "deger", "ust dilim birimi 16 yerine 11",
           lambda m3=None, tip="mesken", **_: (
               {"hata": "eksik alan"} if m3 is None else
               {"tutar": _bedel(m3, tip) - max(m3 - 40, 0) * 5
                * (ISYERI_KAT if tip == "isyeri" else 1)})),
    "D4": ("belge_ara", "deger", "en yuksek ortusme yerine ilki",
           lambda anahtar=None, **_: next(
               ({"kod": k, "metin": m} for k, m in BELGE.items()
                if set(belirtecle(anahtar or "")) & set(belirtecle(m))),
               {"hata": "eksik alan" if not anahtar else "bulunamadi"})),
    "D5": ("tuketim", "sozlesme", "eksik alanda hata yerine sifir",
           lambda onceki=None, simdiki=None, **_: t_tuketim(onceki or 0, simdiki or 0)),
    "D6": ("tarife", "sozlesme", "eksik alanda hata yerine sifir",
           lambda m3=None, tip="mesken", **_: t_tarife(m3 or 0, tip)),
    "D7": ("kayit_yaz", "sozlesme", "deger yokken yazildi diyor",
           lambda abone=None, sutun=None, deger=None, **_: {
               "yazildi": True, "abone": abone, "sutun": sutun, "deger": deger}),
    "D8": ("tarife", "tanim", "tanimi tip alanini bildirmiyor", ("m3",)),
    "D9": ("kayit_yaz", "tanim", "tanimi deger alanini bildirmiyor", ("abone", "sutun")),
    "D10": ("belge_ara", "tanim", "tanimi anahtar yerine abone bildiriyor", ("abone",)),
    "D11": ("sayac_oku", "tanim", "tanimi kullanilmayan alan bildiriyor", ("abone", "sutun")),
}
SINIF = ("deger", "sozlesme", "tanim")


def kusurlu(kod):
    """Kusur konmus tablo kopyasi: dizi verilmisse bildirilen alan listesi,
    islev verilmisse aracin islevi degistirilir."""
    t = {a: dict(v) for a, v in ARAC.items()}
    ad, kusur = KUSUR[kod][0], KUSUR[kod][3]
    t[ad]["alan" if isinstance(kusur, tuple) else "islev"] = kusur
    return t


A1 = "A001"
SINAMA = [
    ("sayac_oku", {"abone": A1}, {"onceki": ABONE[A1]["onceki"],
                                  "simdiki": ABONE[A1]["simdiki"]}),
    ("sayac_oku", {"abone": "A999"}, {"hata": "abone yok"}),
    ("tuketim", {"onceki": 100, "simdiki": 150}, {"m3": 50}),
    ("tuketim", {"onceki": 99980, "simdiki": 30}, {"m3": 50}),
    ("tuketim", {"simdiki": 150}, {"hata": "eksik alan"}),
    ("tarife", {"m3": 50, "tip": "isyeri"}, {"tutar": 940}),
    ("tarife", {}, {"hata": "eksik alan"}),
    ("belge_ara", {"anahtar": "isyeri abonesinde birim ucret"}, {"kod": "TR02"}),
    ("belge_ara", {"anahtar": ""}, {"hata": "eksik alan"}),
    ("kayit_yaz", {"abone": A1, "sutun": "bedel", "deger": 500}, {"yazildi": True}),
    ("kayit_yaz", {"abone": A1, "sutun": "bedel"}, {"hata": "eksik alan"}),
]


def arac_sinamasi(tablo):
    """Her araci dongunun disinda, dogrudan islevini cagirarak sinar."""
    dusen = []
    for ad, girdi, beklenen in SINAMA:
        c = tablo[ad]["islev"](**girdi)
        if ("hata" in c) != ("hata" in beklenen) or any(
                c.get(k) != v for k, v in beklenen.items()):
            dusen.append(ad)
    return dusen


def dongu_basarim(tablo, tohum=TOHUM):
    global ARAC
    asil, ARAC = ARAC, tablo
    d = [g["ad"] for g in GOREV
         if ajan(g, en_cok_adim=8, tohum=tohum)["yanit"] != dogru_yanit(g)]
    ARAC = asil
    return d


print(f"sinama kumesi {len(SINAMA)} durum , kusursuz tabloda dusen "
      f"{len(arac_sinamasi(ARAC))}")
print(f"kusur kumesi {len(KUSUR)} kusur , {len(ARAC)} arac , {len(SINIF)} sinif\n")
print(f"{'kod':<5} {'arac':<11} {'sinif':<9} {'dusen':>5}  aciklama")
yak = {s: [0, 0] for s in SINIF}
for kod, (ad, sinif, acik, kusur) in KUSUR.items():
    d = arac_sinamasi(kusurlu(kod))
    yak[sinif][1] += 1
    yak[sinif][0] += 1 if d else 0
    print(f"{kod:<5} {ad:<11} {sinif:<9} {len(d):>5}  {acik}")

print("\nsinif      kusur  yakalanan     pay")
for s in SINIF:
    a, b = yak[s]
    print(f"{s:<10} {b:>5} {a:>10} {a / b:>7.4f}")
tp = sum(a for a, _ in yak.values())
print(f"{'toplam':<10} {len(KUSUR):>5} {tp:>10} {tp / len(KUSUR):>7.4f}")

print("\niki kosum , kusursuz tablo")
print(f"arac duzeyi sinama , dusen durum {arac_sinamasi(ARAC)} ve {arac_sinamasi(ARAC)}")
for t in (TOHUM, 20260219):
    d = dongu_basarim(ARAC, t)
    print(f"dongu tohum {t} , basarim {1 - len(d) / 24:.4f} , dusen gorev {d}")
```

```
sinama kumesi 11 durum , kusursuz tabloda dusen 0
kusur kumesi 11 kusur , 5 arac , 3 sinif

kod   arac        sinif     dusen  aciklama
D1    sayac_oku   deger         1  simdiki endeksi onceki ile ayni
D2    tuketim     deger         1  sayac donusunde 10000 ekliyor
D3    tarife      deger         1  ust dilim birimi 16 yerine 11
D4    belge_ara   deger         1  en yuksek ortusme yerine ilki
D5    tuketim     sozlesme      1  eksik alanda hata yerine sifir
D6    tarife      sozlesme      1  eksik alanda hata yerine sifir
D7    kayit_yaz   sozlesme      1  deger yokken yazildi diyor
D8    tarife      tanim         0  tanimi tip alanini bildirmiyor
D9    kayit_yaz   tanim         0  tanimi deger alanini bildirmiyor
D10   belge_ara   tanim         0  tanimi anahtar yerine abone bildiriyor
D11   sayac_oku   tanim         0  tanimi kullanilmayan alan bildiriyor

sinif      kusur  yakalanan     pay
deger          4          4  1.0000
sozlesme       3          3  1.0000
tanim          4          0  0.0000
toplam        11          7  0.6364

iki kosum , kusursuz tablo
arac duzeyi sinama , dusen durum [] ve []
dongu tohum 20260218 , basarim 0.9583 , dusen gorev ['G23']
dongu tohum 20260219 , basarim 0.9583 , dusen gorev ['G15']
```

## Yakalanan Yedi, Görünmeyen Dört

Katmanın gücü ilk iki sınıfta tamdır: dört değer kusurunun dördü de, üç sözleşme kusurunun üçü de
sınama kümesinden bir durum düşürüyor. Düşen durum sayısının **birer** olması da bir bilgidir;
kusur kendi aracına yazılmış tek bir durumda görünür ve düşen durumun adı doğrudan bozuk aracı
verir. Dört durum tablosunun söyleyemediği şey burada bir satırla söylenir.

Görünmeyen sınıf tesadüf değil, çağrı yolunun sonucudur. Sınama kümesi aracın **işlevini
doğrudan** çağırır ve bildirilen alan listesine hiç bakmaz. Döngü ise çağrıyı `cagir_arac`
üzerinden kurar ve durumdan yalnız **bildirilen alanları** süzer. Tanım kusuru tam bu süzgecin
içindedir: `tarife` işlevi işyeri katsayısını doğru uygular, ama tanımı `tip` alanını bildirmediği
için döngüde o alan hiç geçmez ve her işyeri abonesi mesken tarifesinden hesaplanır. İşlevi çağıran
bir sınama bunu göremez, çünkü işlev doğrudur. Dört tanım kusuru için de sonuç **0,0000**.

Toplam yakalama **0,6364**. Bu sayı bu kusur kümesine özgüdür; taşınan şey oran değil, **oranın
nasıl bölündüğüdür**. İşlevi sınayan bir katman, işlev ile tanım arasındaki uyumsuzluğa kördür ve
bu körlük durum ekleyerek kapanmaz.

## Koşumdan Bağımsız Katman

Katmanın karşılığı son bloktadır. Kusursuz araç tablosunda döngü iki tohumda da **0,9583** veriyor,
ama **düşen görev değişiyor**: ilk koşumda `G23`, ikincide `G15`. Araç düzeyi sınama aynı tabloda
iki kez koşuldu ve düşen durum kümesi iki kez de **boş ve aynı** çıktı, çünkü bu katman tohum
kullanmaz.

Araç düzeyi sınamanın yargısı **yinelenebilir**: bir kusur ya her koşumda görünür ya da hiç
görünmez. Döngü ölçüsünün yargısı **bantlıdır**: iki koşum farkı tek bir görevdir ve bandın
altındaki hiçbir işaret bulgu sayılmaz.

## Özet

- Bir araç işlev ile tanımın birleşimidir; araç düzeyi sınama işlevi doğrudan çağırır, tanımı
  hiç kullanmaz.
- On bir durumluk sınama kümesi dört değer kusurunun dördünü ve üç sözleşme kusurunun üçünü
  yakalar; her kusur tek bir durumda görünür ve düşen durum bozuk aracı adıyla verir.
- Dört tanım kusurunun hiçbiri yakalanmaz: pay **0,0000**, toplam yakalama **0,6364**.
- Körlüğün nedeni çağrı yoludur: döngü yalnız bildirilen alanları süzer, sınama kümesi işlevi
  doğrudan çağırıp o süzgeci atlar.
- Katmanın karşılığı koşumdan bağımsızlıktır: döngünün düşen görevi tohum değişince G23'ten
  G15'e kayarken, sınama kümesinin düşen durum kümesi iki koşumda da boştur.

## Sonraki Adım

Bu ders bir katmanın sınırını sayıyla çizdi: araç düzeyi sınama, aracın kendi sözünü tutup
tutmadığını ucuza ve kesin söyler, ama aracın **döngüye nasıl bağlandığını** hiç görmez. Görülmeyen
dört kusur bir yerde ortaya çıkmak zorundadır, çünkü ajan işyeri abonelerine yanlış bedel yazar.
Bir sonraki ders senaryoları uçtan uca koşturur ve iki soruyu birlikte sorar: akış sınaması araç
düzeyinin kaçırdığı kusurların kaçını yakalıyor, ve **iki katmanın da göremediği kusur** hangisidir.
