---
title: 'Yapay Zekâ Ajanları'
source: 'https://academia.sh/tr/kurslar/yapay-zeka-ajanlari'
language: tr
updated: '2026-08-17T04:38:23+00:00'
license: 'CC BY-SA 4.0'
lessons: 22
outcomes:
    - 'Ajan döngüsünü algı, akıl yürütme, eylem ve gözlem bileşenlerine ayırarak kurar ve durdurma ölçütünü sayıyla belirler'
    - 'Araç tanımını, yetki yüzeyini ve belleği ayrı ayrı ölçer ve her birinin döngüye kattığı bedeli yazar'
    - 'Ajanı görev başarımı, adım verimliliği, iz düzeyi ve hata kurtarma ekseninde değerlendirir'
---

# Yapay Zekâ Ajanları

Bir ajanın sayısının tek bir yanıtın değil bütün döngünün sayısı olduğu varsayımıyla adımın ölçülmesi: tek adımlı araçsız çağrı ile döngünün görev başarımı, adım derinliği arttıkça bozulan araç seçimi, planlayıcı ile yürütücüyü ayırmanın kattığı ve ödettiği, adım bütçesi büyütüldüğünde işi biten ajanın dolaşması, öz eleştiri turunun yakaladığı yanlış yanıt ile katladığı belirteç, ikinci ajanın getirdiği iletişim maliyeti ve devirde kaybolan alan, onay noktası başına durdurulan yan etki, araç tanımının dört düzeyinde geçerli çağrı oranı, gözlem biçiminin döngü boyunca biriktirdiği belirteç, kaynak sınırının durdurduğu çağrı ile düşürdüğü görev, yetki yüzeyi daraldıkça tamamlanan görev, kaynak sınıfının doğru yanıta kattığı, pencere dolduğunda düşen bilgi, yazma politikasının geri çağırma isabetine kattığı, sıkıştırma oranı arttıkça atılan alanın gerektiği görev sayısı, eskitme eşiği sıkılaştıkça düşen bayat kayıt ile artan kayıp kayıt, doğru dizi ile doğru yanıtın dört durumlu tablosu, araç düzeyi sınamanın göremediği kusur sınıfı, iz düzeyinin başarısız görevin nedenini bulunabilir kıldığı pay, adım başına maliyet ile doğru yanıt başına maliyetin ayrıştığı nokta ve kurtarma politikasının başarıma kattığı.

## Ajan Mimarisi

Model, araç ve döngünün birleşiminin kurulması ve döngünün nerede duracağının sayılması: tek adımlı araçsız çağrı ile döngünün aynı görev kümesindeki başarımı, adım derinliği arttıkça düşen doğru araç seçme payı ve bunun dört görev türünde ayrı okunması, tek döngü ile planlayıcı ve yürütücüyü ayırmanın başarım, adım ve belirteç olarak karşılaştırılması, adım bütçesi iki adımdan on iki adıma çıkarıldığında başarımın önce yükselip sonra düşmesi ve bu düşüşün iki koşum bandının içinde kaldığı için bulgu sayılmaması, öz eleştiri turunun yakaladığı yanlış yanıt ile eklediği adım ve belirteç, aynı görev kümesinde ikinci ajanın kattığı başarım ile devredilen bağlamın belirteci ve devirde kaybolan alan sayısı, yan etkili araçta onay açıkken ve kapalıyken yazılan kayıt ile durdurulan yanlış yazım.

### Ajan Nedir

Source: https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/ajan-nedir

Model, araç ve döngünün birleşiminin kurulması ve tek bir yanıt yerine bütün döngünün sayılması: 24 kurgu görevde tek adımlı araçsız çağrı 0,0417 görev başarımı verirken sekiz adım bütçeli döngü 0,9583 veriyor, ikinci koşumda taban 0,0000'a inip döngü 0,9583'te kalıyor. Bedel 24 adımdan 97 adıma, sıfır araç çağrısından 74 çağrıya, 1440 belirteçten 11978 belirtece ve sıfırdan 8 araç hatasına çıkmaktır. Tür bazında okunduğunda üç adımlı ve daha kısa görevlerde döngü hepsini bitiriyor, dört adımlı yazma görevlerinde üçte ikisinde kalıyor: başarımın düştüğü yer adım derinliğidir. Ajan öğrenmez; koşum boyunca değişen tek şey elindeki gözlemlerdir.

Erişim Artırılmış Üretim kursu on altı derste tek bir soruyu ölçtü: modele **hangi bilginin**
gireceği. Parçalama, gömme, erişim ve yeniden sıralama o seçimin ayarlarıydı ve her ayarın bedeli
belirteçle yazıldı. Ama o hattın iki sessiz varsayımı vardı. Seçimi hep **biz** yaptık: hangi
parçanın getirileceğine kod karar verdi, model yalnız getirileni okudu. Ve hat **tek geçişliydi**:
istek geldi, bağlam kuruldu, bir yanıt üretildi, iş bitti.

Bu kurs iki varsayımı da kaldırır. Modelin kendisi hangi aracı çağıracağına karar verir, aracın
döndürdüğü sonucu görür ve yetersiz bulduğunda başka bir araç dener. Ortaya çıkan şey tek bir yanıt
değil bir **döngüdür** ve ölçülebilir yüzeyi de tek bir yanıtın değil bütün döngünün yüzeyidir. Bu
ders o döngüyü kurar. Döngünün **nerede duracağı** ayrı bir sorudur ve dördüncü derse kadar açık
kalır.

- **AM1.** Kod bir **benzeticidir** ve ortam **kurgudur**: ölçüm ağının kurgu abone kayıtları ve
  kurgu belge dağarcığı. Gerçek bir model uç noktası, gerçek bir araç sunucusu ya da gerçek bir
  çalışma zamanı çağrılmaz. Tohum **20260218**, ikinci koşum **20260219**.
- **AM2.** Ortam **60 kurgu abone**, beş bölge, iki abone tipi ve **5 kurgu belge** taşır.
- **AM3. Araç kümesi beş araçtır** ve her araç yalnız **bildirdiği alanları** görür; şema dışındaki
  alanlar süzülür, eksik alan **aracın kendi hatasıdır**.
- **AM4. Görev kümesi 24 görevdir** ve her görev bilinen bir araç dizisiyle çözülür. Dört tür
  vardır: belge (bir adım), tüketim (iki adım), bedel (üç adım), kayıt (dört adım).
- **AM5. Doğru aracı seçme payı derinlikle düşer.** Birinci adımda 0,80, sonra her adımda 0,09
  azalır ve 0,34'te taban yapar. Yanlış seçim beş araçtan rastgele biridir.
- **AM6. Gözlemler bir durum sözlüğünde birikir**; bir sonraki aracın girdisi önceki adımın
  çıktısıdır.
- **AM7.** İş bittiğinde ajan **0,88 payla durur**; kalan payda dolaşmaya devam eder ve biriken
  sonucu bozabilir.
- **AM8.** `kayit_yaz` **yan etkili** tek araçtır ve onay açıkken her çağrısı bir **onay noktası**
  sayar.
- **AM9. Görev başarımı yanıtın doğruluğudur**: döngünün ürettiği son sonuç, görevin bilinen doğru
  yanıtına eşit mi.
- **AM10. Taban çizgisi tek adımlı, araçsız çağrıdır.** Model yalnız istek metnini görür.
- **AM11.** Sayılan birimler **adım, araç çağrısı, belirteç, araç hatası ve görev başarımıdır**.
  Süre yazılmaz.
- **AM12.** Her ölçüm **iki tohumla** koşulur; iki koşum farkının altında kalan fark **ölçülmemiş
  sayılır**.

#### Model, Araç ve Döngü

Bir ajan üç parçanın birleşimidir. **Model** her adımda ne yapılacağına karar verir. **Araç** modelin
dışarıya uzanan tek elidir: bir kayda bakmak, bir hesap yapmak, bir satır yazmak. **Döngü** ikisini
bağlayan denetim akışıdır — model bir araç seçer, araç çalışır, sonucu bir **gözlem** olarak geri
döner ve model bir sonraki adımda o gözlemi de görür.

Üçünden biri eksikse ortada ajan yoktur. Modelsiz döngü sabit bir betiktir. Döngüsüz araç, Model
API'leri ile Uygulama Geliştirme kursunda ölçülen **tek araç çağrısıdır**: o ders çağrının
**biçimini** saydı, bu kurs çağrıların **dizisini** sayar. Araçsız model ise tek geçişli bir
çağrıdır ve bu dersin taban çizgisi tam olarak odur.

Makine Öğrenmesi kursunun pekiştirmeli öğrenme konusu da bir ajandan söz eder; oradaki ajan
deneyimden öğrenir ve davranışı koşumdan koşuma değişir, buradaki ajan ise **öğrenmez** — koşum
boyunca değişen tek şey elindeki gözlemlerdir.

#### Kurgu Ortam, Araç Kümesi ve Döngü

Aşağıdaki kod ortamı, beş aracı, yirmi dört görevi ve ajan döngüsünü kurar. Kurs boyunca aynı
tanım kullanılır.

```python
# BENZETICI -- KURGUDUR. Gercek bir model ucu, arac sunucusu ya da calisma
# zamani cagrilmaz; ortam da araclar da gorevler de kurgudur.
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


# ---- KURGU ortam: olcum aginin kayitlari
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)


# ---- KURGU gorevler: her biri bilinen bir arac dizisiyle cozulur
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"])


# ---- KURGU ajan: her adimda bir arac secer ya da biter
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)}


g = GOREV[0]
s = ajan(g)
print(f"ortam : {len(ABONE)} abone , {len(BELGE)} belge , {len(ARAC)} arac ,"
      f" {len(GOREV)} gorev")
print(f"{g['ad']} ({g['tur']}) : {g['istek']}")
print("  iz    :", " -> ".join(f"{a}{' [hata]' if h else ''}" for a, h in s["iz"]))
print(f"  yanit {s['yanit']}  dogru {dogru_yanit(g)}  adim {s['adim']}"
      f"  belirtec {s['belirtec']}  arac hatasi {s['hata']}")
```

```
ortam : 60 abone , 5 belge , 5 arac , 24 gorev
G01 (belge) : sayac donusunde endeks farki nasil hesaplanir
  iz    : tuketim [hata] -> kayit_yaz [hata] -> belge_ara
  yanit SY01  dogru SY01  adim 4  belirtec 462  arac hatasi 2
```

Tek bir iz döngünün bütün mekaniğini gösteriyor. Görev tek adımlıktır: `belge_ara` bir kez
çağrılsa yeter. Ajan ilk iki adımda başka araçlara uzanıyor ve ikisi de **araç hatası** veriyor,
çünkü durum sözlüğünde o araçların bildirdiği alanlar yok. Üçüncü adımda doğru aracı buluyor,
dördüncü adımda duruyor. Tek adımlık iş **dört adım** ve **462 belirteç** tuttu. Bir yanıtın
sayısına bakan bir ölçüm bu üç adımı hiç görmez.

#### Tek Adımlı Araçsız Çağrı ile Döngünün Sayısı

Taban çizgisi araçsızdır: model yalnız istek metnini görür, hiçbir araç çağrılmaz ve geri hiçbir
gözlem gelmez. Aynı yirmi dört görev iki kurulumda ve iki tohumda koşturulur.

```python
def tek_adim(g, tohum=TOHUM):
    """TABAN CIZGISI: tek adimli, ARACSIZ cagri; hicbir arac cagrilmaz."""
    r = uretec(tohum ^ izno("taban" + g["ad"]))
    yanit = (list(BELGE)[int(r() * len(BELGE))] if g["tur"] == "belge"
             else 10 + int(r() * 900))
    return {"adim": 1, "belirtec": 60, "hata": 0, "yanit": yanit}


def olc(kur, tohum=TOHUM):
    CAGRI[0] = 0
    bas = adim = bel = hata = 0
    for g in GOREV:
        s = kur(g, tohum)
        adim, bel, hata = adim + s["adim"], bel + s["belirtec"], hata + s["hata"]
        bas += s["yanit"] == dogru_yanit(g)
    return bas / len(GOREV), adim, CAGRI[0], bel, hata


def dongu(g, tohum):
    return ajan(g, en_cok_adim=8, tohum=tohum)


print("kurulum          basarim A  basarim B   adim  cagri  belirtec  hata")
for ad, kur in (("tek adim aracsiz", tek_adim), ("ajan dongusu", dongu)):
    a, b = olc(kur), olc(kur, TOHUM + 1)
    print(f"{ad:<16} {a[0]:>9.4f} {b[0]:>10.4f} {a[1]:>6} {a[2]:>6} {a[3]:>9} {a[4]:>5}")
print("dongu , tur bazinda :", " , ".join(
    f"{t} {sum(ajan(g)['yanit'] == dogru_yanit(g) for g in GOREV if g['tur'] == t)}"
    f"/{sum(g['tur'] == t for g in GOREV)}"
    for t in ("belge", "tuketim", "bedel", "kayit")))
```

```
kurulum          basarim A  basarim B   adim  cagri  belirtec  hata
tek adim aracsiz    0.0417     0.0000     24      0      1440     0
ajan dongusu        0.9583     0.9583     97     74     11978     8
dongu , tur bazinda : belge 7/7 , tuketim 8/8 , bedel 6/6 , kayit 2/3
```

Taban çizgisi neredeyse sıfırdır ve niçin sıfır olduğu tanımın kendisinde yazılıdır: sayaç
endeksleri ortamdadır, istek metninde değildir. Araçsız çağrı bir sayı üretebilir ama o sayı
tahmindir. İlk koşumdaki **0,0417** tek bir isabetten gelir ve o isabet de bir belge kodu
tahminidir; ikinci koşumda **0,0000**'a iniyor. İki koşum farkı taban çizgisinde 0,0417'dir, yani
taban çizgisinin tek başarısı **ölçüm bandının içindedir** ve bir yetenek sayılmaz.

Döngü aynı görev kümesinde **0,9583** veriyor ve bu sayı iki koşumda da aynı. Fark 0,9166'dır ve
bandın yirmi katı üstündedir; bu kazanç **ölçülmüştür**. Bedeli aynı satırda durur: adım **24'ten
97'ye** (4,04 kat), araç çağrısı **sıfırdan 74'e**, belirteç **1440'tan 11978'e** (8,32 kat) ve
araç hatası **sıfırdan 8'e**. Araçsız bir çağrının araç hatası olamaz; hata sayısı sıfırdan
büyükse ortada bir döngü var demektir.

Son satır kazancın nerede bittiğini gösteriyor. Bir, iki ve üç adımlık görevlerin hepsi
tamamlanıyor. Dört adımlık **kayıt** görevlerinde üç görevden **ikisi** doğru. Doğru aracı seçme
payı derinlikle düştüğü için, dizi uzadıkça bir adımı ıskalama olasılığı birikiyor. Üç görevlik bir
alt kümede 2/3 ile 3/3 arasındaki fark kendi başına bir bulgu değildir; ama düşüşün **hangi eksende**
olduğunu söylüyor ve o eksen adım derinliğidir.

#### Özet

- Bir ajan model, araç ve döngünün birleşimidir; döngüsüz araç tek bir araç çağrısı, araçsız model
  tek geçişli bir çağrıdır.
- Ortam, beş araç, yirmi dört görev ve döngü ders içinde standart kitaplıkla kurulur ve hepsi
  kurgudur; gerçek bir model ucu ya da araç sunucusu çağrılmaz.
- Tek adımlı araçsız çağrı 0,0417 görev başarımı veriyor, ikinci koşumda 0,0000; bu tek isabet
  ölçüm bandının içindedir ve bir yetenek sayılmaz.
- Sekiz adım bütçeli döngü iki koşumda da 0,9583 veriyor; kazanç 0,9166 ile bandın kat kat
  üstündedir.
- Bedel adımda 24'ten 97'ye, araç çağrısında sıfırdan 74'e, belirteçte 1440'tan 11978'e ve araç
  hatasında sıfırdan 8'e çıkmaktır.
- Tür bazında bir, iki ve üç adımlık görevlerin hepsi tamamlanıyor, dört adımlık yazma görevlerinde
  üçte ikisi tamamlanıyor.

#### Sonraki Adım

Bu derste döngü bir kutu olarak ölçüldü: giren görev, çıkan yanıt ve aradaki toplam bedel. Kutunun
içi tek bir izde göründü ve o iz iki yanlış araç seçimi taşıyordu. Tür bazındaki son satır da aynı
yöne işaret ediyor: iş uzadıkça başarım düşüyor. Sonraki ders döngünün içini adım adım açar — her
adımın algı, akıl yürütme, eylem ve gözlem olarak hangi parçaya karşılık geldiğini yazar ve doğru
araç seçme payını **adım derinliğine göre** sayar. Aynı sayı dört görev türünde ayrı ayrı okunur,
çünkü türlerin dizi uzunlukları farklıdır ve düşüşün tek bir ortalamada saklandığı yer tam da
budur.

### Ajan Döngüsü

Source: https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/ajan-dongusu

Döngünün bir adımının algı, akıl yürütme, eylem ve gözlem olarak açılması ve doğru araç seçme payının adım derinliğine göre sayılması: 24 kurgu görevde derinlik 1'de pay 0,8000, derinlik 4'te 0,5000; ikinci koşumda 0,7742 ve 0,5000. Aradaki 0,3000'lik düşüş en geniş iki koşum bandının (0,0644) kat kat üstündedir ve ölçülmüştür, ama komşu derinlikler arasındaki 0,0917 ve 0,0160'lık farklar bandın içinde kalır ve ölçülmemiş sayılır. Derinlik arttıkça ölçümün kendisi de inceliyor: seçim sayısı 30'dan 6'ya iniyor. Dört görev türünde okunduğunda bir, iki ve üç adımlık görevlerin başarımı iki koşumda da 1,0000, dört adımlık yazma görevlerinde 0,6667; görev başına adım 2,86'dan 7,67'ye çıkıyor. Yanlış seçim yalnız adım harcamıyor, biriken sonucun da üzerine yazabiliyor.

Önceki ders döngüyü bir kutu olarak ölçtü: giren yirmi dört görev, çıkan yanıtlar ve aradaki toplam
bedel. Kutunun içi yalnız tek bir izde göründü, o izde iki yanlış araç seçimi vardı ve tür bazındaki
son satır dört adımlık görevlerde başarımın düştüğünü söyledi. Düşüşün nereden geldiği ise açık
kaldı.

Bu ders kutuyu açar. Önce bir adımın hangi evrelerden geçtiğini yazar, sonra tek bir sayıyı
**adım derinliğine göre** sayar: ajan o adımda **doğru aracı** seçebildi mi. Bu dersin taban
çizgisi düzeneksiz döngü değil, **aynı döngünün birinci adımıdır**; derinlik arttıkça aynı ölçünün
ne olduğu sayılır.

- **AM13.** Ortam, araç kümesi, görev kümesi ve döngü önceki dersteki tanımın aynısıdır ve
  **kurgudur**. Tohum **20260218**, ikinci koşum **20260219**.
- **AM14. Bir adım dört evredir:** durumdaki alanların okunması (**algı**), sıradaki hedefin
  belirlenmesi (**akıl yürütme**), bir aracın çağrılması (**eylem**) ve dönen sonucun duruma
  yazılması (**gözlem**).
- **AM15. Derinlik, görevin dizisinde kaçıncı adımda olunduğudur** — harcanan adım sayısı değil.
  Üç kez ıskalanan bir hedef üç adım tüketir ama derinliği değiştirmez.
- **AM16. Doğru araç seçme payı**, o derinlikte yapılan seçimlerin kaçının hedef araç olduğudur.
  Yanlış seçim beş araçtan rastgele biri olduğu için **hedefe rastlantıyla da düşebilir**;
  mekanizmanın öngördüğü pay bu yüzden `gorme(d) + (1 - gorme(d)) / 5` olur.
- **AM17. İş bittikten sonraki adımlar seçim sayılmaz**; hedefleri yoktur.
- **AM18. Boşa çağrı**, hedefi tutmayan seçimdir. Adım harcar, belirteç harcar ve duruma
  **istenmeyen alan** yazabilir.
- **AM19.** Adım bütçesi bu derste sabit **sekizdir**; bütçe dördüncü dersin konusudur.
- **AM20.** Derinlik arttıkça oraya ulaşan görev azalır; **örneklem de incelir** ve her satırda
  yazılır.
- **AM21.** İki koşum farkının altında kalan fark **ölçülmemiş sayılır**.

#### Dört Evre ve Bir Görevin Dökümü

Döngünün her adımı aynı dört evreden geçer. **Algı**, durum sözlüğünde o an ne olduğudur: hangi
alanlar dolu, hangi gözlemler birikmiş. **Akıl yürütme**, sıradaki hedefin ne olduğuna karar
vermektir. **Eylem**, bir aracın çağrılmasıdır. **Gözlem**, aracın döndürdüğü alanların duruma
yazılmasıdır — ya da bir **araç hatasıdır**, çünkü çağrı aracın bildirdiği alanları bulamamıştır.

Aşağıdaki kod tanımı kurar ve dört adımlık bir yazma görevinin dökümünü basar.

```python
# BENZETICI -- KURGUDUR. Gercek bir model ucu, arac sunucusu ya da calisma
# zamani cagrilmaz; ortam da araclar da gorevler de kurgudur.
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


# ---- KURGU ortam: olcum aginin kayitlari
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)


# ---- KURGU gorevler: her biri bilinen bir arac dizisiyle cozulur
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"])


# ---- KURGU ajan: her adimda bir arac secer ya da biter
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)}


def oku(g, tohum=TOHUM):
    """Izi gorevin dizisiyle yeniden okur: her adimda akil yurutmenin hedefi,
    eylem olarak secilen arac ve gozlemin ne getirdigi."""
    s = ajan(g, tohum=tohum)
    k, cift, satir = 0, [], []
    for i, (sec, hatali) in enumerate(s["iz"], 1):
        hedef = g["dizi"][k] if k < len(g["dizi"]) else "yok , durmali"
        if k < len(g["dizi"]):
            cift.append((k + 1, sec == hedef))
        satir.append(f"  {i}. derinlik {min(k + 1, len(g['dizi'])):<2}"
                     f" hedef {hedef:<14} eylem {sec:<10}"
                     f" gozlem {'arac hatasi' if hatali else 'alan geldi'}")
        if sec == hedef and not hatali:
            k += 1
    return s, cift, satir


g = [x for x in GOREV if x["ad"] == "G23"][0]
s, _, satir = oku(g)
print(f"{g['ad']} ({g['tur']}) : {g['istek']}")
print("\n".join(satir))
print(f"  adim {s['adim']}  yanit {s['yanit']}  dogru yanit {dogru_yanit(g)}")
```

```
G23 (kayit) : A006 abonesinin bedelini hesapla ve kayda yaz
  1. derinlik 1  hedef sayac_oku      eylem sayac_oku  gozlem alan geldi
  2. derinlik 2  hedef tuketim        eylem tuketim    gozlem alan geldi
  3. derinlik 3  hedef tarife         eylem belge_ara  gozlem alan geldi
  4. derinlik 3  hedef tarife         eylem tuketim    gozlem alan geldi
  5. derinlik 3  hedef tarife         eylem tarife     gozlem alan geldi
  6. derinlik 4  hedef kayit_yaz      eylem tarife     gozlem alan geldi
  7. derinlik 4  hedef kayit_yaz      eylem belge_ara  gozlem alan geldi
  8. derinlik 4  hedef kayit_yaz      eylem kayit_yaz  gozlem alan geldi
  adim 8  yanit TR01  dogru yanit 1132
```

Bu döküm yanlış seçimin **iki ayrı** bedelini gösteriyor. Üçüncü ve dördüncü adımdaki `belge_ara`
ve `tuketim` çağrıları hedefi tutmuyor; ikisi de hata vermiyor, çünkü durumda bekledikleri alanlar
var. Adım harcanıyor, derinlik ilerlemiyor. Yedinci adım daha ağırdır: `belge_ara` başarıyla dönüyor
ve biriken **sonucun üzerine yazıyor**; durumda artık 1132 değil bir belge kodu duruyor. Sekizinci
adımda ajan nihayet hedefi tutturuyor, fakat `kayit_yaz` o bozuk değeri kayda geçiriyor. Başarıyla
dönen bir boşa çağrı adım harcamakla kalmıyor, **durumu bozuyor**.

#### Derinlik Arttıkça Doğru Araç Seçme Payı

Aynı sayı bütün görev kümesinde, derinlik derinlik toplanır. Mekanizmanın öngördüğü pay yanına
yazılır, çünkü yanlış seçim rastgele bir araçtır ve hedefe rastlantıyla da düşebilir.

```python
print("derinlik  beklenen   A: secim  dogru   pay    B: secim  dogru   pay")
for d in (1, 2, 3, 4):
    sat = [f"{d:>5} {gorme(d) + (1 - gorme(d)) / 5:>12.4f}"]
    for tohum in (TOHUM, TOHUM + 1):
        n = k = 0
        for x in GOREV:
            for dd, ok in oku(x, tohum)[1]:
                if dd == d:
                    n, k = n + 1, k + ok
        sat.append(f"{n:>10} {k:>6} {k / n:>6.4f}")
    print(" ".join(sat))

print()
print("tur      dizi  gorev   A: adim/gorev  bosa  hata  basarim"
      "    B: adim/gorev  bosa  hata  basarim")
for tur, boy in (("belge", 1), ("tuketim", 2), ("bedel", 3), ("kayit", 4)):
    alt = [x for x in GOREV if x["tur"] == tur]
    sat = [f"{tur:<8} {boy:>4} {len(alt):>6}"]
    for tohum in (TOHUM, TOHUM + 1):
        adim = bosa = hata = bas = 0
        for x in alt:
            y, c, _ = oku(x, tohum)
            adim, hata = adim + y["adim"], hata + y["hata"]
            bosa += sum(1 for _, ok in c if not ok)
            bas += y["yanit"] == dogru_yanit(x)
        sat.append(f"{adim / len(alt):>13.2f} {bosa:>5} {hata:>5} {bas / len(alt):>8.4f}")
    print(" ".join(sat))
```

```
derinlik  beklenen   A: secim  dogru   pay    B: secim  dogru   pay
    1       0.8400         30     24 0.8000         31     24 0.7742
    2       0.7680         24     17 0.7083         22     17 0.7727
    3       0.6960         13      9 0.6923         12      9 0.7500
    4       0.6240          6      3 0.5000          6      3 0.5000

tur      dizi  gorev   A: adim/gorev  bosa  hata  basarim    B: adim/gorev  bosa  hata  basarim
belge       1      7          2.86     5     5   1.0000          2.86     3     2   1.0000
tuketim     2      8          3.50     4     3   1.0000          3.62     5     1   1.0000
bedel       3      6          4.33     2     0   1.0000          5.00     6     3   1.0000
kayit       4      3          7.67     9     0   0.6667          6.33     4     1   0.6667
```

Birinci tablo iki şeyi birden söylüyor ve ikisi ayrı ayrı okunmalı. Uçtan uca fark **ölçülmüştür**:
derinlik 1'de pay **0,8000**, derinlik 4'te **0,5000**; ikinci koşumda **0,7742** ve **0,5000**.
Düşüş 0,3000 ve 0,2742'dir. Aynı derinliğin iki koşum farkı en geniş yerde **0,0644**'tür (derinlik
2), yani uçtan uca düşüş bandın dört katından fazladır. Plan uzadıkça seçim bozuluyor.

Komşu derinlikler arasındaki farklar ise başka bir hikaye anlatıyor. Birinci koşumda 1'den 2'ye
düşüş 0,0917, 2'den 3'e 0,0160'tır; ikinci koşumda ilki 0,0015, ikincisi ise 0,0227'dir.
Bunların hepsi bandın içinde ya da sınırındadır ve **ölçülmemiş sayılır**. Yalnız 3'ten 4'e geçiş
iki koşumda da bandı aşıyor: 0,1923 ve 0,2500. Tek tek adımların payını okumak isteyen bir ölçüm
daha büyük bir görev kümesine ihtiyaç duyar.

Bunun nedeni sağdaki seçim sütunlarında yazılı. Derinlik 1'de **30** seçim var, derinlik 4'te
yalnız **6**. Dört adımlık diziye sahip görev sayısı üçtür ve oraya ulaşan koşum sayısı daha da
azdır. **Derinleştikçe ölçümün kendisi inceliyor**: ajanın en zayıf olduğu yer, hakkında en az
sayıya sahip olduğumuz yerdir.

#### Dört Görev Türünde Ayrı Okuma

İkinci tablo aynı düşüşü tür bazında gösteriyor. Görev başına adım dizi uzunluğuyla birlikte
büyüyor: **2,86**, **3,50**, **4,33**, **7,67**. En az gereken adım sayısı sırasıyla 2, 3, 4 ve
5'tir — bir adım her zaman durmaya gider. Dört adımlık türde gerçekleşen adım en az gerekenin
**1,53 katıdır** ve bu oran ikinci koşumda 1,27'ye iner; ikisi arasındaki fark üç görevlik bir alt
kümede ölçülemez.

Ölçülebilen şey başarımdır. Bir, iki ve üç adımlık türler iki koşumda da **1,0000**, dört adımlık
tür iki koşumda da **0,6667** veriyor. Aynı ajan, aynı araçlar, aynı bütçe; değişen tek şey dizinin
uzunluğu. Araç hatası sütunu ise iki koşum arasında 5'ten 2'ye, 0'dan 3'e savruluyor: bu ölçekte
hata sayımı gürültüdür.

#### Özet

- Bir ajan adımı dört evredir: durumun okunması, hedefin belirlenmesi, aracın çağrılması ve dönen
  sonucun duruma yazılması.
- Yanlış seçim iki bedel öder: harcanan adım ve duruma yazılan istenmeyen alan; başarılı bir yanlış
  çağrı biriken sonucun üzerine yazabilir.
- Doğru araç seçme payı derinlik 1'de 0,8000, derinlik 4'te 0,5000; ikinci koşumda 0,7742 ve
  0,5000. Uçtan uca düşüş en geniş iki koşum bandının dört katından fazladır ve ölçülmüştür.
- Komşu derinlikler arasındaki 0,0917 ve 0,0160'lık farklar bandın içindedir ve ölçülmemiş sayılır;
  yalnız üçüncüden dördüncüye geçiş iki koşumda da bandı aşar.
- Derinleştikçe örneklem incelir: seçim sayısı 30'dan 6'ya iner, yani ajanın en zayıf olduğu yer en
  az sayıya sahip olduğumuz yerdir.
- Tür bazında görev başına adım 2,86'dan 7,67'ye çıkar; başarım üç kısa türde 1,0000, dört adımlık
  türde iki koşumda da 0,6667'dir.

#### Sonraki Adım

Bu derste tek bir döngü bütün işi kendi yaptı: hedefi de o belirledi, aracı da o çağırdı. Dizinin
dördüncü adımında hedefi tutturma payının yarıya inmesi bu birleşimin bedelidir, çünkü aynı çağrı
hem bütün planı hem de sıradaki eylemi taşımak zorunda. Akla gelen ilk çözüm işi bölmektir: bir yan
planı çıkarsın, öbür yan yalnız o planın adımlarını yürütsün. Sonraki ders bu bölmeyi kurar ve iki
soruyu birlikte sorar — bölme başarımı yükseltiyor mu ve bölünen iş adım ile belirteç olarak ne
kadar tutuyor.

### Planlayıcı ve Yürütücü Ayrımı

Source: https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/planlayici-yurutucu-ayrimi

Karar veren ile çalıştıran yanın ayrılmasının kattığı ve ödettiği: 24 kurgu görevde tek döngü 0,9583 görev başarımı, 97 adım, 74 araç çağrısı ve 11978 belirteç veriyor; planlayıcı ve yürütücü ayrıldığında başarım 0,8750, adım 118, çağrı 71 ve belirteç 10345 oluyor. Başarım farkı bölünmüş düzenin kendi iki koşum farkına eşittir ve ölçülmemiş sayılır. Fazladan adım tam olarak görev başına bir planlama adımıdır. Belirteç iki koşumda da yüzde on üçten fazla düşüyor, çünkü yürütücü akıl yürütme izini değil planı taşıyor. Planın konumlarının 0,8868'i doğru ve yirmi dört planın on sekizi baştan sona doğru; yanlış bir konum körlemesine yürütüldüğü için düzeltme ancak plan tükendikten sonra başlıyor.

Önceki ders tek bir döngünün derinlikle bozulduğunu ölçtü: dizinin dördüncü adımında doğru aracı
seçme payı yarıya iniyordu. Bunun bir nedeni yapısaldır — aynı çağrı hem bütün işin planını hem de
sıradaki tek eylemi taşıyor.

Akla gelen ilk düzenleme işi ikiye bölmektir. Bir yan işin **planını** tek seferde çıkarsın, öbür
yan o planın adımlarını **yürütsün** ve kendi başına araç seçmesin. Bu ders bölmeyi kurar ve iki
soruyu birlikte sorar: bölme başarımı yükseltiyor mu ve neyi ödetiyor. Taban çizgisi düzeneksiz
döngüdür — önceki dersin tek döngüsü.

- **AM22.** Ortam, araç kümesi, görev kümesi ve tek döngü önceki derslerdeki tanımın aynısıdır ve
  **kurgudur**. Tohum **20260218**, ikinci koşum **20260219**.
- **AM23. Bölünmüş düzen iki parçadır:** **planlayıcı** tek bir çağrıda bütün planı yazar,
  **yürütücü** planı sırayla çalıştırır ve kendi başına araç seçmez.
- **AM24. Planlayıcı gözlem görmez.** Bütün konumları aynı anda, aynı bilgiyle seçer; bu yüzden
  derinlik cezası ona işlemez ve her konumu düz `gorme(1)` payıyla seçer.
- **AM25. Planlama bir adımdır** ve bütçeden düşer. İki kurulum da aynı **sekiz adım** bütçesiyle
  koşulur.
- **AM26. Yürütücü planı körlemesine yürütür**: yanlış bir konum da çalıştırılır.
- **AM27.** Plan bitip iş bitmediyse kalan konumlar **tek döngünün kuralıyla** denenir; düzeltme
  ancak plan tükendikten sonra başlar.
- **AM28. Belirteç iki kurulumda ayrı sayılır.** Tek döngünün adımı `60 + 22 x iz uzunluğu`,
  planlayıcının çağrısı `60 + 25 x plan uzunluğu`, yürütücünün adımı `40 + 10 x iz uzunluğu`; her
  gözlem 30 belirteçtir. Yürütücü akıl yürütme izini değil **planı** taşır.
- **AM29.** Plan uzunluğu görevin dizi uzunluğudur; planlayıcı diziyi bilmez, uzunluğunu bilir.
- **AM30.** Sayılan birimler **başarım, adım, araç çağrısı, belirteç ve araç hatasıdır**; iki koşum
  farkının altında kalan fark **ölçülmemiş sayılır**.

#### Bölme: Bir Plan, Bir Yürütme

Bölünmüş düzende karar bir kez verilir. Planlayıcı isteği okur ve `sayac_oku -> tuketim -> tarife`
gibi bir dizi yazar. Yürütücü o diziyi sırayla çalıştırır ve seçim yapmadığı için adımı ucuzdur:
akıl yürütme izini değil planı ve biriken durumu taşır. Bölmenin bedeli aynı cümlede yazılıdır —
plan **gözlemden önce** yazıldığı için, yanlış bir konum çalıştırılana kadar yanlış olduğu
anlaşılmaz.

```python
# BENZETICI -- KURGUDUR. Gercek bir model ucu, arac sunucusu ya da calisma
# zamani cagrilmaz; ortam da araclar da gorevler de kurgudur.
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


# ---- KURGU ortam: olcum aginin kayitlari
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)


# ---- KURGU gorevler: her biri bilinen bir arac dizisiyle cozulur
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"])


# ---- KURGU ajan: her adimda bir arac secer ya da biter
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)}


def planla_yurut(g, en_cok_adim=8, tohum=TOHUM):
    """BOLUNMUS duzen: PLANLAYICI tek cagrida butun plani yazar; gozlem
    gormedigi icin her konumu duz gorme(1) payiyla secer. YURUTUCU plani
    sirayla calistirir ve secim yapmaz; plan bitip is bitmediyse kalan
    konumlar tek dongunun kuraliyla denenir."""
    r = uretec(tohum ^ izno("plan" + g["ad"]))
    dizi = g["dizi"]
    plan = [d if r() < gorme(1) else ARAC_ADI[int(r() * len(ARAC_ADI))] for d in dizi]
    alan = {"abone": g["abone"], "anahtar": g["istek"], "sutun": "bedel"}
    iz, adim, hata, k = [], 1, 0, 0            # planlama bir adimdir
    bel = 60 + 25 * len(plan)                  # planlayicinin cagrisi
    while adim < en_cok_adim:
        adim += 1
        bel += 40 + 10 * len(iz)               # yurutucu izi degil plani tasir
        hedef = dizi[k] if k < len(dizi) else None
        if hedef is None:
            if r() < 0.88:
                break
            sec = ARAC_ADI[int(r() * len(ARAC_ADI))]
        elif len(iz) < len(plan):
            sec = plan[len(iz)]                # yurutucu planin sirasini izler
        else:
            sec = hedef if r() < gorme(k + 1) else ARAC_ADI[int(r() * len(ARAC_ADI))]
        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
            continue
        alan.update({kk: vv for kk, vv in s.items() if kk != "hata"})
        for a, b in (("tuketim", "m3"), ("tarife", "tutar"), ("belge_ara", "kod")):
            if sec == a:
                alan["sonuc"] = s[b]
        if sec == hedef:
            k += 1
    return {"adim": adim, "iz": iz, "belirtec": bel, "hata": hata, "plan": plan,
            "yanit": alan.get("sonuc")}


g = [x for x in GOREV if x["ad"] == "G05"][0]
s = planla_yurut(g)
print(f"{g['ad']} ({g['tur']}) dogru dizi :", " -> ".join(g["dizi"]))
print("  plan      :", " -> ".join(s["plan"]))
print("  yurutulen :", " -> ".join(f"{a}{' [hata]' if h else ''}" for a, h in s["iz"]))
print(f"  adim {s['adim']}  belirtec {s['belirtec']}  arac hatasi {s['hata']}"
      f"  yanit {s['yanit']}  dogru {dogru_yanit(g)}")
```

```
G05 (bedel) dogru dizi : sayac_oku -> tuketim -> tarife
  plan      : tarife -> tuketim -> tarife
  yurutulen : tarife [hata] -> tuketim [hata] -> tarife [hata] -> sayac_oku -> tuketim -> tarife
  adim 8  belirtec 805  arac hatasi 3  yanit 550  dogru 550
```

Planın ilk konumu yanlıştır ve bu tek yanlış bütün planı boşa çıkarıyor. `tarife` durumda `m3`
bulamadığı için hata veriyor, `tuketim` endeksler okunmadığı için hata veriyor, üçüncü konum yine
`tarife` ve yine hata. Yürütücü plana bağlı olduğundan üçünü de çalıştırmak zorunda; düzeltme ancak
plan tükendiğinde başlıyor. Doğru yanıt sonunda çıkıyor ama **üç araç hatası** ödendikten sonra.

#### Bölmenin Kattığı ve Ödettiği

Aynı yirmi dört görev iki kurulumda, iki tohumda ve aynı sekiz adım bütçesiyle koşturulur.

```python
def olc(kur, tohum=TOHUM):
    CAGRI[0] = 0
    bas = adim = bel = hata = 0
    for x in GOREV:
        y = kur(x, tohum=tohum)
        adim, bel, hata = adim + y["adim"], bel + y["belirtec"], hata + y["hata"]
        bas += y["yanit"] == dogru_yanit(x)
    return bas / len(GOREV), adim, CAGRI[0], bel, hata


print("kurulum                kosum  basarim   adim  cagri  belirtec  hata")
for ad, kur in (("tek dongu", ajan), ("planlayici + yurutucu", planla_yurut)):
    for et, tohum in (("A", TOHUM), ("B", TOHUM + 1)):
        z = olc(kur, tohum)
        print(f"{ad:<22} {et:>5} {z[0]:>8.4f} {z[1]:>6} {z[2]:>6} {z[3]:>9} {z[4]:>5}")
top = sum(len(x["dizi"]) for x in GOREV)
for et, tohum in (("A", TOHUM), ("B", TOHUM + 1)):
    p = [planla_yurut(x, tohum=tohum)["plan"] for x in GOREV]
    kon = sum(a == b for pl, x in zip(p, GOREV) for a, b in zip(pl, x["dizi"]))
    tam = sum(list(pl) == list(x["dizi"]) for pl, x in zip(p, GOREV))
    print(f"plan {et} : dogru konum {kon}/{top} = {kon / top:.4f} ;"
          f" tamami dogru plan {tam}/{len(GOREV)}")
```

```
kurulum                kosum  basarim   adim  cagri  belirtec  hata
tek dongu                  A   0.9583     97     74     11978     8
tek dongu                  B   0.9583     98     74     11994     7
planlayici + yurutucu      A   0.8750    118     71     10345    10
planlayici + yurutucu      B   0.9583    116     70     10135    12
plan A : dogru konum 47/53 = 0.8868 ; tamami dogru plan 18/24
plan B : dogru konum 47/53 = 0.8868 ; tamami dogru plan 18/24
```

Başarım sütunu **bulgu vermiyor**. Tek döngü iki koşumda da 0,9583; bölünmüş düzen 0,8750 ve
0,9583. Bölünmüş düzenin kendi iki koşum farkı **0,0833**'tür; iki kurulum arasındaki fark birinci
koşumda tam olarak 0,0833, ikincisinde **sıfırdır**. Fark bandın içindedir ve **bölmenin başarıma
etkisi ölçülmemiş sayılır**. Gözle görülen bir fark, ölçülmüş bir fark değildir.

Adım sütununun farkı ölçülmüştür ve nereden geldiği tam olarak hesaplanabilir. Bölünmüş düzen her
göreve **bir planlama adımı** ekliyor, yani yirmi dört adım; 97 artı 24, 121 eder, gerçekleşen
118'dir. Aradaki üç adımın karşılığı yandaki sütundadır: araç çağrısı **74'ten 71'e** iniyor. Plan
yürütücüyü bir miktar boşa çağrıdan koruyor, ama planlamanın kendisi bu kazancın sekiz katını geri
alıyor.

Belirteç sütunu bölmenin **kattığı** yerdir: 11978 ve 11994 karşısında 10345 ve 10135, yani yüzde
13,6 ve yüzde 15,5 düşüş. İki kurulumun kendi bantları 16 ve 210 belirteçtir; fark bandın kat kat
üstündedir ve **ölçülmüştür**. Nedeni tanımdadır — tek döngüde her adım büyüyen akıl yürütme izini
yeniden taşır, yürütücüde ise adım sabit bir planı taşır.

Araç hatası ters yöne gidiyor: 8 ve 7 karşısında 10 ve 12. Yön iki koşumda da aynıdır ve mekanizması
bellidir; körlemesine yürütülen yanlış konum, durumda alanı bulunmayan bir aracı çağırır. Ama
bantlar 1 ve 2 iken fark birinci koşumda 2'dir: **yönü mekanizmadan biliyoruz, büyüklüğünü bu
ölçekte ölçemiyoruz.**

Son iki satır bölmenin sınırını veriyor. Planın konumlarının **0,8868**'i doğru, ama yirmi dört
planın yalnız **18**'i baştan sona doğru. Plan uzadıkça tamamının doğru olma olasılığı düşer ve tek
bir yanlış konum G05'teki gibi bütün planı boşa çıkarabilir. Bölmek kararı tek bir noktaya toplar;
o noktadaki hatanın bedelini de tek bir noktada toplar.

#### Özet

- Bölünmüş düzende planlayıcı bütün planı gözlemden önce yazar, yürütücü onu sırayla ve körlemesine
  çalıştırır; düzeltme ancak plan tükendikten sonra başlar.
- Başarım tek döngüde 0,9583 ve 0,9583, bölünmüş düzende 0,8750 ve 0,9583; fark bölünmüş düzenin
  kendi iki koşum farkına eşittir ve ölçülmemiş sayılır.
- Bölmenin eklediği adım tam olarak görev başına bir planlama adımıdır: 97 adıma 24 eklenir, araç
  çağrısı 74'ten 71'e indiği için toplam 118'de kalır.
- Belirteç 11978'den 10345'e ve 11994'ten 10135'e iner; düşüş bandın kat kat üstündedir, çünkü
  yürütücü akıl yürütme izini değil planı taşır.
- Araç hatası bölünmüş düzende iki koşumda da yüksektir (10 ve 12 karşısında 8 ve 7); yönü
  mekanizmadan bellidir, büyüklüğü bu ölçekte ölçülemez.
- Planın konumlarının 0,8868'i doğrudur ama tamamı doğru plan yirmi dörtte on sekizdir; tek bir
  yanlış konum bütün planı boşa çıkarabilir.

#### Sonraki Adım

İki kurulum da aynı yerde tökezliyor ve o yer üç derstir hiç sorulmadı: **döngü nerede duracak**.
Sekiz adım denildi ve sekiz adım yazıldı, ama bu sayı bir karardı ve gerekçesi yazılmadı. G05 plan
tükendikten sonra düzelmek için üç adıma daha ihtiyaç duydu; bütçe altı olsaydı yanlış yanıt
verecekti. Öte yandan işi biten bir ajan durmayı beceremezse elindeki doğru sonucu bozabilir.
Sonraki ders adım bütçesini iki, dört, altı, sekiz ve on iki değerlerinde süpürür ve durdurma
ölçütünü kursun kuralı olarak yazar.

### Durdurma Ölçütleri

Source: https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/durdurma-olcutleri

Döngünün nerede duracağının kursun kuralı olarak yazılması ve adım bütçesinin iki, dört, altı, sekiz ve on iki değerlerinde süpürülmesi: 24 kurgu görevde başarım 0,3750'den 1,0000'e çıkıyor, sonra 0,9583'te düzleşiyor; ikinci koşumda 0,4167, 0,8333, 0,9583, 0,9583, 0,9583. Altıncı adımdan sekizinciye düşüş 0,0417'dir ve altıncı adımdaki iki koşum farkı da 0,0417'dir, yani düşüş ölçüm bandının içindedir ve bir bulgu olarak ilan edilemez; mekanizması tek bir görevin izinde gösterilir ve etkisi ölçülmemiş sayılır. Bütçe altıdan on ikiye çıkarıldığında başarım değişmezken belirteç 10884'ten 12214'e çıkıyor. Bütçeyle durmak bir durdurma ölçütü değil, emniyet sınırıdır: altıncı adımda üç görev bütçe tükendiği için duruyor.

Önceki üç dersin hepsi aynı sayıyı sabit tuttu: adım bütçesi sekizdi. Bu bir karardı, gerekçesi
yazılmadı ve sonuçların hepsi o karara asılı. Bölünmüş düzende G05 planı tükendikten sonra düzelmek
için üç adıma daha ihtiyaç duydu; bütçe altı olsaydı yanlış yanıt verecekti. Tersi de doğrudur: işi
biten bir ajan durmayı beceremezse elindeki doğru sonucu bozabilir.

Bu ders o kararı ölçer. Adım bütçesi beş değerde süpürülür ve her değerde başarımın yanına bedeli
yazılır. Ders aynı zamanda kursun kuralını kurar: **bir ajanın sayısı tek bir yanıtın değil bütün
döngünün sayısıdır ve durma koşulu yazılmayan ajan ölçülmemiş sayılır.** Taban çizgisi en dar
bütçedir — iki adımlık döngü.

- **AM31.** Ortam, araç kümesi, görev kümesi ve döngü önceki derslerdeki tanımın aynısıdır ve
  **kurgudur**. Tohum **20260218**, ikinci koşum **20260219**.
- **AM32. Durdurma ölçütü döngünün parçasıdır**, dışarıdan konulan bir emniyet değil. Bu benzetici
  iki ölçüt taşır: **iş bitti kararı** ve **adım bütçesi**.
- **AM33. İş bitti kararı belirsizdir.** Sırada hedef kalmadığında ajan **0,88 payla** durur; kalan
  payda bir araç daha çağırır.
- **AM34. Dolaşan döngü**, hedefi kalmadığı halde adım atmaya devam eden döngüdür. Attığı her adım
  sayılır.
- **AM35. Adım bütçesi bir durdurma ölçütü değil, sert bir sınırdır.** Bütçe tükendiğinde döngü
  yarıda kesilir ve elindeki değer ne ise yanıt odur.
- **AM36. Süpürülen değerler 2, 4, 6, 8 ve 12'dir**; başka her şey sabittir.
- **AM37.** Sayılan birimler **başarım, adım, araç çağrısı, belirteç, araç hatası, kendi kararıyla
  biten koşum ve dolaşan adımdır**.
- **AM38. Görev kümesi 24 görevdir**, yani başarımın en küçük adımı **0,0417**'dir. Bundan küçük bir
  fark bu kümede temsil edilemez.
- **AM39.** İki koşum farkının altında kalan fark **ölçülmemiş sayılır**; bu ders kuralı kendi
  sonucuna da uygular.
- **AM40.** Süre yazılmaz.

#### Durdurma Ölçütü Nedir

Bir döngünün ne zaman duracağı, ne yapacağı kadar tasarım kararıdır. Ayırt edilmesi gereken iki şey
var. **İş bitti kararı** ajanın kendi kararıdır: sıradaki hedef kalmadı, elde bir sonuç var, dur.
**Adım bütçesi** ise dışarıdan konan sert bir sınırdır ve bir karar değildir; döngüyü nerede olursa
olsun keser. İkincisi birincisinin yerine geçemez, çünkü kesilen bir döngü elindeki değeri yanıt
diye verir ve o değerin doğruluğuna bakılmamıştır.

Ölçüt sınıfları bundan ibaret değildir: yinelenen adım, belirteç bütçesi ve dış iptal de birer
durdurma ölçütüdür. Bu benzetici yalnız ilk ikisini taşır ve bu derste yalnız onların sayısı
basılır.

```python
# BENZETICI -- KURGUDUR. Gercek bir model ucu, arac sunucusu ya da calisma
# zamani cagrilmaz; ortam da araclar da gorevler de kurgudur.
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


# ---- KURGU ortam: olcum aginin kayitlari
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)


# ---- KURGU gorevler: her biri bilinen bir arac dizisiyle cozulur
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"])


# ---- KURGU ajan: her adimda bir arac secer ya da biter
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)}


def dolasan(g, s):
    """Is bittikten sonra atilan adim sayisi: hedefi kalmadigi halde yapilan
    arac cagrilari. Durma karari verilmeyen her adim buraya yazilir."""
    k = n = 0
    for sec, hatali in s["iz"]:
        if k >= len(g["dizi"]):
            n += 1
        elif sec == g["dizi"][k] and not hatali:
            k += 1
    return n


def supur(b, tohum=TOHUM):
    CAGRI[0] = 0
    bas = adim = bel = hata = bitti = dol = 0
    for x in GOREV:
        y = ajan(x, en_cok_adim=b, tohum=tohum)
        adim, bel, hata = adim + y["adim"], bel + y["belirtec"], hata + y["hata"]
        bas += y["yanit"] == dogru_yanit(x)
        bitti += y["bitti"]
        dol += dolasan(x, y)
    return bas / len(GOREV), adim, CAGRI[0], bel, hata, bitti, dol


for et, tohum in (("A", TOHUM), ("B", TOHUM + 1)):
    print(f"kosum {et}   butce  basarim   adim  cagri  belirtec  hata  bitti  dolasan")
    for b in (2, 4, 6, 8, 12):
        z = supur(b, tohum)
        print(f"          {b:>6} {z[0]:>8.4f} {z[1]:>6} {z[2]:>6} {z[3]:>9}"
              f" {z[4]:>5} {z[5]:>6} {z[6]:>8}")
```

```
kosum A   butce  basarim   adim  cagri  belirtec  hata  bitti  dolasan
               2   0.3750     48     45      4758     8      3        1
               4   0.8750     84     64      9402     8     20        1
               6   1.0000     92     71     10884     8     21        1
               8   0.9583     97     74     11978     8     23        1
              12   0.9583     98     74     12214     8     24        1
kosum B   butce  basarim   adim  cagri  belirtec  hata  bitti  dolasan
               2   0.4167     48     45      4758     7      3        2
               4   0.8333     83     67      9366     7     16        3
               6   0.9583     96     74     11610     7     22        3
               8   0.9583     98     74     11994     7     24        3
              12   0.9583     98     74     11994     7     24        3
```

İlk üç satır tartışmasızdır. İki adımlık bütçe üç ve dört adımlık dizilere hiç şans tanımıyor;
başarım yalnız kısa görevlerden gelebiliyor ve **0,3750** ile **0,4167**'de kalıyor. Dördüncü
adımda 0,8750 ve 0,8333, altıncı adımda **1,0000** ve **0,9583** oluyor. İkiden altıya kazanç
0,6250 ve 0,5416'dır; en geniş iki koşum farkı **0,0417** olduğuna göre bu kazanç bandın on katından
fazladır ve **ölçülmüştür**. Dörtten altıya kazanç iki koşumda da 0,1250 ve o da bandın üstündedir.

`bitti` sütunu bu kazancın niteliğini değiştiriyor. İki adımlık bütçede yirmi dört koşumun yalnız
**üçü** kendi kararıyla duruyor; geri kalan yirmi biri bütçe tükendiği için kesiliyor. Altıncı
adımda bile üç koşum kesilerek bitiyor. Kesilen bir koşum yanıt üretmiş sayılmaz, elindeki değeri
teslim etmiştir. **Bütçe bir durdurma ölçütü değil, ölçütün yokluğunda son çare olan bir sınırdır.**

#### Altıncı Adımdan Sekizinciye: Mekanizma Var, Ölçü Yok

Tablonun dikkat çeken yeri altıncı satırdan sekizinciye geçiştir. Birinci koşumda başarım
**1,0000'den 0,9583'e** düşüyor. Düşüşün sorumlusu tek bir görevdir ve izi okunabilir.

```python
g = [x for x in GOREV if x["ad"] == "G23"][0]
print(f"{g['ad']} ({g['tur']}) dizi :", " , ".join(g["dizi"]),
      "| dogru yanit", dogru_yanit(g))
for b in (6, 8, 12):
    s = ajan(g, en_cok_adim=b)
    print(f"  butce {b:>2}  adim {s['adim']}  bitti {s['bitti']}  yanit {s['yanit']}")
    print("     iz :", " , ".join(a for a, _ in s["iz"]))
```

```
G23 (kayit) dizi : sayac_oku , tuketim , tarife , kayit_yaz | dogru yanit 1132
  butce  6  adim 6  bitti False  yanit 1132
     iz : sayac_oku , tuketim , belge_ara , tuketim , tarife , tarife
  butce  8  adim 8  bitti False  yanit TR01
     iz : sayac_oku , tuketim , belge_ara , tuketim , tarife , tarife , belge_ara , kayit_yaz
  butce 12  adim 9  bitti True  yanit TR01
     iz : sayac_oku , tuketim , belge_ara , tuketim , tarife , tarife , belge_ara , kayit_yaz
```

Altı adımlık bütçede G23 doğru yanıtı veriyor, ama **kendi kararıyla değil**: bütçe altıncı adımda
tükeniyor ve o an durumda duran değer 1132'dir, yani doğrudur. Sekiz adımlık bütçe iki adım daha
tanıyor. Yedinci adımda ajan `belge_ara`'yı seçiyor; bu seçim yanlıştır ama **hata vermez**, çünkü
aracın istediği alan durumda vardır. Başarıyla dönen çağrı biriken sonucun üzerine bir belge kodu
yazıyor. Sekizinci adımda hedef nihayet tutturuluyor ve `kayit_yaz` o bozuk değeri kayda geçiriyor.
On iki adımlık bütçe dokuzuncu adımda durma kararını da veriyor, ama düzeltecek bir şey kalmamıştır.

Mekanizma açıktır ve tek cümleyle yazılabilir: **doğru sonuç duruma bir kez yazıldıktan sonra her
fazladan adım onu bozma şansıdır.** Döngünün ikinci kanalı da aynı yöne bakar — iş bittiğinde ajan
0,12 payla durmayıp bir araç daha çağırır; bu koşumlarda dolaşan adım sayısı 1 ve 3'tür.

Buna karşılık **bu düşüş bir bulgu değildir**. Altıncı adımda iki koşumun verdiği sayılar 1,0000 ve
0,9583'tür, yani o noktadaki iki koşum farkı **0,0417**'dir. Altıdan sekize düşüş de tam olarak
0,0417'dir ve ikinci koşumda **sıfırdır**. Fark ölçüm bandının içinde kalıyor: **mekanizma
gösterilebilir, etkisi ölçülmemiş sayılır.**

Bu, ölçeğin dayattığı bir sınırdır. Yirmi dört görevlik bir kümede bir görev 0,0417 eder; band da
bir görev genişliğindedir, dolayısıyla bir görevlik etki bu kümede hiçbir zaman bandın üstüne
çıkamaz. Etkiyi ölçmek isteyen okurun iki yolu var. Birincisi **görev kümesini büyütmektir**: küme
birkaç katına çıkarsa çözünürlük artar. İkincisi **dolaşma payını bir ayar olarak süpürmektir**;
0,12 tek bir nokta olmaktan çıkıp eksene dönüşürse başarımın o eksen boyunca nasıl değiştiği tek bir
görev farkına bağlı kalmadan okunabilir.

Ölçülen şey ise bedeldir. Bütçe altıdan on ikiye çıkarıldığında birinci koşumda başarım hiç
değişmiyor, buna karşılık belirteç **10884'ten 12214'e**, adım 92'den 98'e, araç çağrısı 71'den
74'e çıkıyor. Sekizden on ikiye geçiş her iki koşumda da başarımı sabit bırakıyor. **Bütçeyi
büyütmenin kazancı ölçülemedi, bedeli ölçüldü.**

#### Özet

- Durdurma ölçütü döngünün parçasıdır; iş bitti kararı ajanın kendi kararı, adım bütçesi ise
  dışarıdan konan sert bir sınırdır ve birincisinin yerine geçemez.
- Başarım iki adımda 0,3750 ve 0,4167, altı adımda 1,0000 ve 0,9583; ikiden altıya kazanç en geniş
  iki koşum farkının on katından fazladır ve ölçülmüştür.
- İki adımlık bütçede yirmi dört koşumun yalnız üçü kendi kararıyla duruyor; altı adımda bile üç
  koşum bütçe tükendiği için kesiliyor.
- Altıdan sekize düşüş 0,0417'dir ve altıncı adımdaki iki koşum farkı da 0,0417'dir; mekanizma tek
  bir görevin izinde gösterilebilir ama etkisi ölçülmemiş sayılır.
- Etkiyi ölçmek görev kümesini büyütmeyi ya da dolaşma payını kendi ekseninde süpürmeyi gerektirir.
- Bütçe altıdan on ikiye çıktığında başarım değişmezken belirteç 10884'ten 12214'e çıkıyor: kazanç
  ölçülemedi, bedel ölçüldü.

#### Sonraki Adım

Dört derste döngü kuruldu, içi adım adım açıldı, ikiye bölündü ve nerede duracağı yazıldı. Bütün bu
ölçümlerin ortak bir dayanağı vardı ve o dayanak sorgulanmadı: doğru yanıtı **biz** biliyorduk.
Görev kümesi her görevin doğru yanıtını taşıyordu ve başarımı o listeyle karşılaştırarak hesapladık.
Ajanın kendisi durduğunda elindeki değere bir kez bile bakmadı; G23 bozuk bir sonucu kayda geçirdi
ve bunu bir sorun olarak görmedi. Sonraki ders bu boşluğa girer: ajan kendi çıktısını denetleyen bir
tur eklerse ne yakalar ve o tur kaç adım ile kaç belirtece mal olur.

### Öz Eleştiri ve Doğrulama

Source: https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/oz-elestiri-ve-dogrulama

Ajanın durduktan sonra kendi izine bakmasının kattığı ve katladığı: üç gözlenebilir işaret yirmi dört kurgu görevde okunduğunda tur, dar bütçelerde yanlış yanıtların hepsini bayrağa takıyor ama sekiz adımda temiz izli bir yanlış yanıtı hiç göremiyor. Dört adım bütçesinde başarım 21/24'ten 23/24'e çıkıyor, karşılığında adım 84'ten 152'ye ve belirteç 9402'den 17052'ye. Aynı adımlar tek bir uzun koşuma verildiğinde sekiz adım bütçesi 97 adım ve 11978 belirteçle aynı 23/24'ü veriyor. Altı adımda tur başarımı 24/24'ten 23/24'e düşürüyor; sekiz adımda hiç değiştirmiyor ve yedi bayrağın altısı gereksiz yeniden koşum. İki koşum farkı bir görevdir, bu yüzden altı ve sekiz adımdaki fark ölçülmemiş sayılır.

Önceki dört derste döngü kuruldu ve nerede duracağı yazıldı. Ajan bir araç seçiyor, dönen gözlemi
durumuna yazıyor, sonraki adımı ona bakarak atıyor; işi bittiğinde ya da bütçe dolduğunda duruyor.
Kapandığı anda elde bir yanıt kalıyor.

Dört ders boyunca bir kez sorulmayan şey şu: **o yanıt doğru mu.** Ajan durduğunda kendi izine
dönüp bakmıyor. Bu ders o eksik turu ekler ve iki şeyi ölçer: turun **yakaladığını** ve turun
**ödettiğini**. Aynı istemi birkaç kez koşturup yanıtları oylamak bundan ayrı bir düzenektir ve
İstem Mühendisliği kursunda ölçüldü; burada oylanacak birden çok yanıt yok, tek bir döngünün
bıraktığı ize bakılıyor.

- **AM41.** Kurulum ortaktır: ölçüm ağının **kurgu** abone kayıtları, **kurgu** belge dağarcığı,
  beş **kurgu** araç, yirmi dört **kurgu** görev ve ders içinde yazılmış bir **ajan benzeticisi**.
  Gerçek bir model ucu ya da araç sunucusu çağrılmaz.
- **AM42.** Taban çizgisi **düzeneksiz döngüdür**: aynı küme, aynı bütçe, tur kapalı. Bütçenin
  iki katına çıkarılmış tursuz satır da tabloda durur, çünkü turun harcadığı adım **tek bir uzun
  koşuma** da verilebilirdi.
- **AM43.** Tur **doğru yanıtı göremez**; yalnız üç gözlenebilir işarete bakar: döngü kendi mi
  durdu, araç hatası düştü mü, bir yanıt üretildi mi.
- **AM44.** Üçünden biri bozuksa görev **kuşkulu** sayılır ve döngü ikinci kez koşar. Tur
  **bir kezdir** ve yanıtı düzeltebildiği gibi bozabilir de.
- **AM45.** Turun bedeli her koşumdan sonra **bir adım** ve `45 + 18 x len(iz)` belirteçtir; turlu
  koşumda her araç hatası izin okunması için **40 belirteç** ekler.
- **AM46.** İkinci koşum tohum **20260219**; bu kümede **iki koşum farkı bir görevdir** (0,0417) ve
  altındaki her fark **ölçülmemiş sayılır**.

#### Turun Bakabildiği Üç İşaret

Turun elinde doğru yanıt yoktur; olsaydı ajana gerek kalmazdı. Elinde yalnız iz vardır.

```python
# ORTAK KURGU KURULUM -- olcum aginin 60 kurgu abonesi, 5 kurgu belge,
# 5 kurgu arac, 24 kurgu gorev ve ders icinde yazilmis ajan benzeticisi.
# Gercek bir model ucu, arac sunucusu ya da calisma zamani 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


# ---- KURGU ortam: olcum aginin kayitlari
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)


# ---- KURGU gorevler: her biri bilinen bir arac dizisiyle cozulur
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"])


# ---- KURGU ajan: her adimda bir arac secer ya da biter
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)}



# ---- Bu derste eklenen parca: oz elestiri turu


def kuskulu(s):
    """Turun bakabildigi uc isaret: dongu kendi mi durdu, arac hatasi
    dustu mu, bir yanit uretildi mi. Dogru yanit turun elinde yoktur."""
    return (not s["bitti"]) or s["hata"] > 0 or s["yanit"] is None


def denetim_bedeli(s):
    return 45 + 18 * len(s["iz"])


print("gorev  tur      bitti  hata  yanit  kuskulu  dogru")
for g in GOREV[:6]:
    s = ajan(g, en_cok_adim=2)
    print(f"{g['ad']:<7}{g['tur']:<9}{str(s['bitti']):<7}{s['hata']:<6}"
          f"{str(s['yanit'] is not None):<7}{str(kuskulu(s)):<9}"
          f"{s['yanit'] == dogru_yanit(g)}")


def dort_hucre(b, tohum):
    c = {"ky": 0, "kd": 0, "sy": 0, "sd": 0}
    for g in GOREV:
        s = ajan(g, en_cok_adim=b, tohum=tohum)
        c[("k" if kuskulu(s) else "s")
          + ("d" if s["yanit"] == dogru_yanit(g) else "y")] += 1
    return c


print("\nbutce     tohum  kuskulu+yanlis  kuskulu+dogru  sessiz+yanlis  sessiz+dogru")
for b in (2, 4, 8):
    for t in (TOHUM, TOHUM + 1):
        c = dort_hucre(b, t)
        print(f"{b:>5}{t:>10}{c['ky']:>14}{c['kd']:>15}{c['sy']:>15}{c['sd']:>14}")

print("\nsessiz kalan yanlis yanit (butce 8, tohum 20260219):")
for g in GOREV:
    s = ajan(g, en_cok_adim=8, tohum=TOHUM + 1)
    if not kuskulu(s) and s["yanit"] != dogru_yanit(g):
        print(" ", g["ad"], g["tur"], [a for a, h in s["iz"]])
        print("  yanit", s["yanit"], "-- dogrusu", dogru_yanit(g))
```

```
gorev  tur      bitti  hata  yanit  kuskulu  dogru
G01    belge    False  2     False  True     False
G02    belge    True   0     True   False    True
G03    tuketim  False  1     False  True     False
G04    tuketim  False  0     True   True     True
G05    bedel    False  0     True   True     False
G06    bedel    False  0     True   True     False

butce     tohum  kuskulu+yanlis  kuskulu+dogru  sessiz+yanlis  sessiz+dogru
    2  20260218            15              6              0             3
    2  20260219            14              7              0             3
    4  20260218             3              7              0            14
    4  20260219             4              6              0            14
    8  20260218             1              6              0            17
    8  20260219             0              6              1            17

sessiz kalan yanlis yanit (butce 8, tohum 20260219):
  G15 kayit ['sayac_oku', 'tuketim', 'tarife', 'tuketim', 'kayit_yaz']
  yanit 57 -- dogrusu 582
```

İlk tablo turun niçin ucuz olmadığını gösteriyor. Dar bütçede altı görevin beşi bayrağa
takılıyor; dördü gerçekten yanlış, biri (`G04`) doğru yanıt üretmiş. İşaret bozukluğu
**yanlışlığın kanıtı değil, bakma nedenidir**.

Dört hücrelik tablo bunu bütün kümede doğruluyor. Dar bütçelerde **sessiz kalan yanlış yanıt
yoktur**; bedel gereksiz bayraklardır, dört adımda üç yanlışa karşılık yedi tane. Sekiz adımda
tablo tersine dönüyor ve bloğun son satırları sessiz kalan görevi basıyor. `G15` bir kayıt
görevidir; dizisi tamamlanmış, döngü kendi durmuş, araç hatası düşmemiştir. Ama ajan işi bittikten
sonra dolaşıp `tuketim` aracını yeniden çağırmış, bu çağrı biriken sonucu **tutar yerine
tüketimle** ezmiş ve o değer kayda yazılmıştır. İz temiz görünüyor çünkü **her adım tek başına
geçerlidir**; bozuk olan adımların sırasıdır.

#### Turun Kattığı ve Katladığı

Tur şimdi bir düzenek olarak koşturulur.

```python
def kosum(b, tohum, tur=False):
    """tur=False duz dongudur. tur=True: dongu biter, tur ize bakar,
    kuskuluysa ayni gorev ikinci bir dongude bir kez daha kosar."""
    CAGRI[0] = 0
    d = {"dogru": 0, "adim": 0, "bel": 0, "bayrak": 0, "kurtaran": 0, "bozan": 0}
    for g in GOREV:
        s = ajan(g, en_cok_adim=b, tohum=tohum, oz_elestiri=tur)
        d["adim"] += s["adim"] + tur
        d["bel"] += s["belirtec"] + (denetim_bedeli(s) if tur else 0)
        ilk = s["yanit"] == dogru_yanit(g)
        if tur and kuskulu(s):
            d["bayrak"] += 1
            s = ajan(g, en_cok_adim=b, tohum=tohum ^ izno("tur2|" + g["ad"]),
                     oz_elestiri=True)
            d["adim"] += s["adim"] + 1
            d["bel"] += s["belirtec"] + denetim_bedeli(s)
        son = s["yanit"] == dogru_yanit(g)
        d["dogru"] += son
        d["kurtaran"] += son and not ilk
        d["bozan"] += ilk and not son
    d["cagri"] = CAGRI[0]
    return d


for t in (TOHUM, TOHUM + 1):
    print(f"\ntohum {t}")
    print("duzen            basarim   adim  cagri  belirtec  bayrak kurtaran bozan")
    for b in (2, 4, 6, 8):
        for ad, tur in (("tursuz", False), ("turlu ", True)):
            d = kosum(b, t, tur)
            ek = (f"{d['bayrak']:>8}{d['kurtaran']:>9}{d['bozan']:>6}" if tur
                  else f"{'-':>8}{'-':>9}{'-':>6}")
            print(f"butce {b:>2}, {ad}{d['dogru']:>6}/24{d['adim']:>7}"
                  f"{d['cagri']:>7}{d['bel']:>10}{ek}")
```

```
tohum 20260218
duzen            basarim   adim  cagri  belirtec  bayrak kurtaran bozan
butce  2, tursuz     9/24     48     45      4758       -        -     -
butce  2, turlu     13/24    135     84     13007      21        4     0
butce  4, tursuz    21/24     84     64      9402       -        -     -
butce  4, turlu     23/24    152     94     17052      10        2     0
butce  6, tursuz    24/24     92     71     10884       -        -     -
butce  6, turlu     23/24    164    102     19671       9        0     1
butce  8, tursuz    23/24     97     74     11978       -        -     -
butce  8, turlu     23/24    155     94     18821       7        1     1

tohum 20260219
duzen            basarim   adim  cagri  belirtec  bayrak kurtaran bozan
butce  2, tursuz    10/24     48     45      4758       -        -     -
butce  2, turlu     12/24    135     83     12839      21        4     2
butce  4, tursuz    20/24     83     67      9366       -        -     -
butce  4, turlu     22/24    151     96     16822      10        2     0
butce  6, tursuz    23/24     96     74     11610       -        -     -
butce  6, turlu     23/24    155     96     18493       7        0     0
butce  8, tursuz    23/24     98     74     11994       -        -     -
butce  8, turlu     23/24    152     92     18312       6        0     0
```

Dört adımlık bütçe turun en iyi göründüğü satırdır: başarım 21/24'ten **23/24**'e, iki koşumda da
**iki görev**. Bedel: adım 84'ten **152**'ye, çağrı 64'ten 94'e, belirteç 9402'den **17052**'ye.
On bayrağın yalnız ikisi bir yanıtı düzeltiyor.

Sekiz adımlık tursuz satır bu kazancı bağlamına oturtuyor: **23/24**, 97 adım, 11978 belirteç.
Turun 152 adımla ulaştığı yere düz döngü 97 adımda varıyor; fazlalık **daha uzun bir tek koşuma**
verildiğinde aynı başarım daha ucuza alınıyor. İki adımlık bütçede fark daha serttir: turlu koşum
135 adımla 13/24 verirken, dört adımlık düz koşum 84 adımda **21/24** veriyor.

Altı adımlık satır kursun ikinci iddiasının açık örneğidir: düz döngü orada **24/24** yapıyor, tur
eklendiğinde **23/24**'e iniyor. Dokuz bayrağın hiçbiri bir yanlışı düzeltmiyor, biri doğru bir
yanıtı bozuyor.

#### Bandın Altında Kalan

Bu tablodaki her fark bir bulgu değildir. Düz döngünün başarımı iki tohumda altı adımda 24/24 ve
23/24, sekizde 23/24 ve 23/24; ölçüm bandı **bir görevdir**. Turun altı adımda ödettiği bir
görevlik düşüş ve sekizdeki sıfır fark bu bandın içindedir ve **ölçülmemiş sayılır**: mekanizma
gösterilebilir, etkisi bu küme büyüklüğünde ölçülemez. Bandın üstünde kalan tek kazanç dört adımlık
bütçedir. Sekiz adımda turun tek somut sayısı bedeldir: yedi bayrak, altısı gereksiz ve sıfır
kazanılmış görev.

#### Özet

- Duran bir ajan kendi izine bakabilir ama **doğru yanıtı göremez**; bakabildiği üç işarettir:
  kendi durup durmadığı, araç hatası düşüp düşmediği, bir yanıt üretip üretmediği.
- İşaret bozukluğu yanlışlığın kanıtı değildir: dört adımlık bütçede on bayrağın yalnız üçü
  yanlış bir yanıta karşılık gelir.
- Geniş bütçelerde **temiz izli bir yanlış yanıt** çıkar; kendi durmuş ve hatasız bir döngü,
  dolaşırken biriken sonucu ezebilir ve tur bunu göremez.
- Turun en yüksek kazancı dört adımlık bütçede iki görevdir (21/24 → 23/24); adım 84'ten 152'ye,
  belirteç 9402'den 17052'ye çıkar. Aynı 23/24'ü sekiz adımlık düz döngü 97 adımda veriyor.
- Altı ve sekiz adımdaki farklar iki koşum bandının (bir görev) içindedir ve bulgu sayılmaz.

#### Sonraki Adım

Bu derste denetleyen ile denetlenen **aynı döngüydü** ve tur, izin yüzeyiyle sınırlı kaldı. Bakan
gözü **döngünün dışına** çıkarmanın bir yolu işi bölüp ikinci bir ajana vermektir.
Sonraki ders aynı görev kümesinde tek ajan ile iki ajanı yan yana koyar. Ölçülecek olan yalnız
başarım değildir; **devredilen bağlamın belirteci** ve devirde **kaybolan alan sayısı** ayrıca
sayılır, çünkü ikinci bir ajanın bedeli adımla değil iletişimle ödenir.

### Çoklu Ajan Düzenleri

Source: https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/coklu-ajan-duzenleri

Aynı görev kümesinde tek ajan ile iki ajanın yan yana ölçülmesi: sekiz adımlık bütçe ikiye bölünüp iş bir okuyucu ile bir hesapçıya dağıtıldığında toplam başarım iki koşumda da 23/24'te kalıyor, ama adım 97'den 140'a çıkıyor ve devredilen bağlam 363 belirteç götürüyor. Bölme, dört araçlık kayıt görevlerini 2/3'ten 3/3'e çıkarırken iki araçlık tüketim görevlerini 8/8'den 7/8'e düşürüyor. Devir paketi ucuzlatıldığında 178 belirteç kazanılıyor, karşılığında 68 alan kayboluyor ve başarım 23/24'ten 18/24'e iniyor; kaybolan alan abone tipi olduğu için işyeri abonelerinin bedeli tam yarısı çıkıyor. Dört adımlık bütçe ikiye bölündüğünde düzen 21/24'ten 19/24'e ve 20/24'ten 16/24'e düşüyor.

Önceki ders denetleyen gözü döngünün içinde bıraktı: ajan kendi izine kendi baktı ve iz temiz
göründüğünde hiçbir şey göremedi. Gözü dışarı çıkarmanın en doğrudan yolu **işi bölmektir**.

Bölme bedava değildir. Tek bir döngüde biriken durum ortak bir sözlükte durur; iki ajan aynı
belleği paylaşmaz ve aradaki her şey **açıkça devredilmek** zorundadır. Devredilen şey bir
metindir, yani belirteçle ölçülür. Bölme burada araç kümesine göredir; planlayıcı–yürütücü ayrımı
değildir, iki ajan da yürütür.

- **AM47.** Kurulum ortaktır: ölçüm ağının **kurgu** abone kayıtları, **kurgu** belge dağarcığı,
  beş **kurgu** araç, yirmi dört **kurgu** görev ve ders içinde yazılmış bir **ajan benzeticisi**.
  Gerçek bir model ucu ya da araç sunucusu çağrılmaz.
- **AM48.** Taban çizgisi **düzeneksiz döngüdür**: tek ajan, aynı küme, aynı toplam bütçe.
- **AM49.** Düzen iki roldür. **Okuyucu** yalnız `sayac_oku`, `tuketim` ve `belge_ara`, **hesapçı**
  yalnız `tarife` ve `kayit_yaz` araçlarını görür; rol döngüsü ortak döngünün aynısıdır.
- **AM50.** Toplam adım bütçesi **ikiye bölünür** ve devir **bir adım** sayılır.
- **AM51.** **Devir paketi** bir metindir: taşınacak alanlar `ad=deger` biçiminde yazılır, bedeli o
  metnin belirteç sayısıdır ve taşınmayan her alan **kaybolur**. **Tam devir** sekiz alanı taşır,
  **özet devir** yalnız `abone`, `sutun`, `m3` ve `sonuc` alanlarını.
- **AM52.** İkinci koşum tohum **20260219**; iki koşum farkı bu kümede **bir görevdir** (0,0417)
  ve altındaki her fark **ölçülmemiş sayılır**.

#### Devirde Kaybolan Alan

Önce tek bir görevde iki paket biçimi yan yana konur. `G23` bir kayıt görevidir, abonesi işyeri.

```python
# ORTAK KURGU KURULUM -- olcum aginin 60 kurgu abonesi, 5 kurgu belge,
# 5 kurgu arac, 24 kurgu gorev ve ders icinde yazilmis ajan benzeticisi.
# Gercek bir model ucu, arac sunucusu ya da calisma zamani 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


# ---- KURGU ortam: olcum aginin kayitlari
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)


# ---- KURGU gorevler: her biri bilinen bir arac dizisiyle cozulur
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"])


# ---- KURGU ajan: her adimda bir arac secer ya da biter
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)}



# ---- Bu derste eklenen parca: iki ajanli duzen ve devir paketi

ROL = {"okuyucu": ("sayac_oku", "tuketim", "belge_ara"),
       "hesapci": ("tarife", "kayit_yaz")}
DEVIR_ALAN = ("abone", "sutun", "onceki", "simdiki", "tip", "bolge", "m3", "sonuc")
OZET_ALAN = ("abone", "sutun", "m3", "sonuc")


def rol_ajani(g, rol, alan, en_cok_adim, tohum):
    """Ortak dongunun aynisi; arac kumesi ve baslangic durumu disaridan verilir."""
    r = uretec(tohum ^ izno(rol + "|" + g["ad"]))
    adlar = ROL[rol]
    dizi = [a for a in g["dizi"] if a in adlar]
    iz, adim, bel, k = [], 0, 0, 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:
            if r() < 0.88:
                break
            sec = adlar[int(r() * len(adlar))]
        else:
            sec = hedef if r() < gorme(k + 1) else adlar[int(r() * len(adlar))]
        if sec == "kayit_yaz":
            alan["deger"] = alan.get("sonuc")
        s = cagir_arac(sec, alan)
        iz.append(sec)
        bel += 30
        if "hata" in s:
            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 alan, adim, bel


def devir_paketi(alan, tasinan):
    """Devredilen baglam bir metindir; bedeli belirtecle olculur."""
    p = " ".join(f"{a}={alan[a]}" for a in tasinan if a in alan)
    kayip = [a for a in DEVIR_ALAN if a in alan and a not in tasinan]
    return p, len(belirtecle(p)), kayip


def paketi_ac(p):
    d = {}
    for parca in p.split():
        a, _, v = parca.partition("=")
        d[a] = int(v) if v.lstrip("-").isdigit() else v
    return d


g = GOREV[22]
alan = {"abone": g["abone"], "anahtar": g["istek"], "sutun": "bedel"}
alan, adim, bel = rol_ajani(g, "okuyucu", alan, 4, TOHUM)
print(g["ad"], g["tur"], "-- okuyucu bitti, adim", adim, "belirtec", bel)
for ad, tasinan in (("tam  ", DEVIR_ALAN), ("ozet ", OZET_ALAN)):
    p, bl, kayip = devir_paketi(alan, tasinan)
    b2, a2, e2 = rol_ajani(g, "hesapci", paketi_ac(p), 4, TOHUM)
    print(f"{ad} paket {bl:>2} belirtec, kayip {len(kayip)} {kayip}"
          f" -- yanit {b2.get('sonuc')}, dogrusu {dogru_yanit(g)}")
```

```
G23 kayit -- okuyucu bitti, adim 3 belirtec 306
tam   paket 18 belirtec, kayip 0 [] -- yanit 1132, dogrusu 1132
ozet  paket  8 belirtec, kayip 4 ['onceki', 'simdiki', 'tip', 'bolge'] -- yanit 566, dogrusu 1132
```

Fark on belirteçtir ve karşılığı tam bir yanlış yanıttır. Özet paket `tip` alanını taşımadığı için
`tarife` aracı kendi varsayılanına düşüyor ve bedeli mesken tarifesiyle hesaplıyor; sonuç tam
**yarısı** çıkıyor. Araç hata vermiyor ve o değer kayda yazılıyor. **Kaybolan alan, araç hatası
olarak değil, yanlış ama geçerli bir sonuç olarak görünür.**

#### İkinci Ajanın Kattığı ve Ödettiği

Şimdi bütün küme koşturulur; tür kırılımı da tabloda durur. Belge görevleri her düzende 7/7
çıktığı için tabloda yer almaz.

```python
TUR = ("belge", "tuketim", "bedel", "kayit")


def kosum(tohum, butce, tasinan=None):
    """tasinan None ise tek ajan. Degilse butce ikiye bolunur: okuyucu kendi
    isini bitirir, devir paketini yazar, hesapci paketle baslar."""
    CAGRI[0] = 0
    d = {"dogru": 0, "adim": 0, "bel": 0, "devir": 0, "kayip": 0,
         "tur": {t: [0, 0] for t in TUR}}
    for g in GOREV:
        if tasinan is None:
            s = ajan(g, en_cok_adim=butce, tohum=tohum)
            yanit, d["adim"], d["bel"] = (s["yanit"], d["adim"] + s["adim"],
                                          d["bel"] + s["belirtec"])
        else:
            alan = {"abone": g["abone"], "anahtar": g["istek"], "sutun": "bedel"}
            alan, a1, b1 = rol_ajani(g, "okuyucu", alan, butce // 2, tohum)
            paket, pb, kayip = devir_paketi(alan, tasinan)
            son, a2, b2 = rol_ajani(g, "hesapci", paketi_ac(paket),
                                    butce // 2, tohum)
            d["adim"] += a1 + 1 + a2
            d["bel"] += b1 + pb + b2
            d["devir"] += pb
            d["kayip"] += len(kayip)
            yanit = son.get("sonuc", alan.get("sonuc"))
        d["tur"][g["tur"]][1] += 1
        d["tur"][g["tur"]][0] += yanit == dogru_yanit(g)
        d["dogru"] += yanit == dogru_yanit(g)
    d["cagri"] = CAGRI[0]
    return d


for t in (TOHUM, TOHUM + 1):
    print(f"\ntohum {t}")
    print("butce duzen           tuket bedel kayit toplam  adim cagri"
          "  belirt devir kayip")
    for butce in (4, 8):
        for ad, tas in (("tek ajan", None), ("iki ajan, tam", DEVIR_ALAN),
                        ("iki ajan, ozet", OZET_ALAN)):
            d = kosum(t, butce, tas)
            p = "".join(f"{d['tur'][x][0]}/{d['tur'][x][1]:<5}" for x in TUR[1:])
            print(f"{butce:>5} {ad:<15}{p}{d['dogru']:>2}/24{d['adim']:>6}"
                  f"{d['cagri']:>6}{d['bel']:>8}{d['devir']:>6}{d['kayip']:>6}")
```

```
tohum 20260218
butce duzen           tuket bedel kayit toplam  adim cagri  belirt devir kayip
    4 tek ajan       8/8    5/6    1/3    21/24    84    64    9402     0     0
    4 iki ajan, tam  6/8    4/6    2/3    19/24   109    63    8162   358     0
    4 iki ajan, ozet 6/8    1/6    1/3    15/24   109    63    7984   180    68
    8 tek ajan       8/8    6/6    2/3    23/24    97    74   11978     0     0
    8 iki ajan, tam  7/8    6/6    3/3    23/24   140    69   11703   363     0
    8 iki ajan, ozet 7/8    2/6    2/3    18/24   140    69   11525   185    68

tohum 20260219
butce duzen           tuket bedel kayit toplam  adim cagri  belirt devir kayip
    4 tek ajan       8/8    4/6    1/3    20/24    83    67    9366     0     0
    4 iki ajan, tam  6/8    1/6    2/3    16/24   108    63    8083   361     0
    4 iki ajan, ozet 6/8    0/6    1/3    14/24   108    63    7895   173    72
    8 tek ajan       8/8    6/6    2/3    23/24    98    74   11994     0     0
    8 iki ajan, tam  8/8    5/6    3/3    23/24   143    75   12358   372     0
    8 iki ajan, ozet 8/8    2/6    2/3    19/24   143    75   12170   184    72
```

Sekiz adımlık bütçede toplam başarım iki düzende de **23/24** ve iki koşumda da aynı: ikinci ajan
toplama hiçbir şey katmıyor. Bedeli açık — adım 97'den **140**'a, devredilen bağlam **363
belirteç**. Çağrı ise 74'ten 69'a düşüyor, çünkü hesapçının işi olmayan on beş görevde ajan bir
adım harcayıp hiçbir araç çağırmadan çıkıyor. İletişim maliyeti bir çağrı kalemi değildir.

Kırılım toplamın gizlediğini gösteriyor. Dört araçlık **kayıt** görevleri iki koşumda da 2/3'ten
**3/3**'e çıkıyor: bölme derinliği her ajan için sıfırdan başlatıyor ve yanlış seçim beş araç
yerine iki ya da üç araç arasından geliyor. Aynı mekanizma kısa dizide ters çalışıyor — **tüketim**
görevleri birinci koşumda 8/8'den 7/8'e, **bedel** görevleri ikincide 6/6'dan 5/6'ya iniyor. Kayıt
alt kümesi üç görevliktir; yön aynı, büyüklüğü **ölçülmemiş sayılır**.

Özet devir satırı ikinci bedeli yazıyor. Paketi ucuzlatmak 363 belirteci **185**'e indiriyor;
karşılığı **68 kaybolan alan** ve 23/24'ten **18/24**'e düşen başarımdır. Kayıp bedel görevlerinde
toplanıyor: 6/6'dan **2/6**'ya. **Belirteçle ölçülen bir tasarruf, görevle ödeniyor.**

Dört adımlık bütçe satırlarında düzen tek ajanın **altında** kalıyor: 19/24 ve 16/24. Her ajana
kalan pay dizisini bitirmeye yetmiyor. **Bir işi bölmek, bölünecek kadar bütçe varsa bir
düzendir.**

#### Özet

- İki ajan ortak bellek paylaşmaz; aralarındaki her alan **açıkça devredilir** ve bedeli
  belirteçle ölçülür.
- Eşit toplam bütçede ikinci ajan başarıma iki koşumda da **hiçbir şey katmıyor** (23/24 → 23/24);
  adım 97'den 140'a çıkıyor, devredilen bağlam 363 belirteç tutuyor. Çağrı ise 74'ten 69'a
  **düşüyor**: iletişim maliyeti adım kalemindedir.
- Kırılım toplamın gizlediğini açıyor: kayıt görevleri 2/3'ten 3/3'e çıkarken tüketim ve bedel
  görevleri birer görev kaybediyor.
- Devir paketini ucuzlatmak 178 belirteç kazandırıp 68 alan kaybettiriyor ve başarımı 18/24'e
  indiriyor; kaybolan alan **yanlış ama geçerli bir sonuç** olarak görünüyor.
- Dört adımlık bütçe ikiye bölündüğünde düzen tek ajanın altına düşüyor (19/24 ve 16/24).

#### Sonraki Adım

İki derste denetim önce döngünün içine, sonra ikinci bir ajana verildi; ikisinde de karar
**düzeneğin kendisinde** kaldı. `G23` görevinde yanlış değer bir kayda yazıldı ve bunu durduran
hiçbir şey yoktu, çünkü `kayit_yaz` **yan etkili** tek araçtır ve öbür araçlarla aynı muameleyi
gördü. Sonraki ders o aracın önüne bir **onay noktası** koyar: onay açıkken ve kapalıyken kaç kayıt
yazılıyor, onay kaç yanlış yazımı durduruyor ve bunun için insana kaç kez soruluyor.

### İnsanı Döngüde Tutmak

Source: https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/insani-dongude-tutmak

Yan etkili aracın önüne konan onay noktasının ölçülmesi: onay kapalıyken yirmi dört kurgu görevde beş kayıt yazılıyor ve üçü yanlış, ikinci koşumda sekiz kayıt yazılıyor ve altısı yanlış. Onay yalnız yan etkili çağrıda açıldığında yazılan kayıt üçe, yanlış kayıt bire iniyor; bedel yedi ve on bir soru. Onay her adıma yayıldığında durdurulan yazım hiç değişmiyor ama soru sayısı 74 ve 76'ya, belirteç 12103'ten 14448'e çıkıyor. Görev başarımı üç kapsamda da 23/24'te sabit kalıyor, yani başarım yan etkiyi ölçmüyor. Durdurulan çağrı sayısı engellenen kayıt sayısından büyüktür, çünkü durdurulan çağrıların bir kısmı zaten eksik alanla düşecekti.

Önceki iki derste ajan kendi izine baktı, sonra ikinci bir ajan devreye girdi; ikisinde de son söz
düzenekteydi. Kurgu ortamın beş aracından dördü **okur**, biri **yazar**: `kayit_yaz` geri
alınmayan bir iz bırakır ve şimdiye kadar öbür dördüyle aynı muameleyi gördü.

Bu ders o aracın önüne bir **onay noktası** koyar: onay açıkken ve kapalıyken kaç kayıt yazılıyor,
kaçı yanlış ve insana kaç kez soruluyor. Durakları **adım aralığına** göre koymak Model API'leri
ile Uygulama Geliştirme kursunda ölçüldü — her adımda durmak 480 adımın 478'ini, sekiz adımda bir
durmak 355'ini doğru getiriyor. O yordam tekrarlanmaz; buradaki durak aralığa göre değil, **yan
etkiye** göre konuyor.

- **AM53.** Kurulum ortaktır: ölçüm ağının **kurgu** abone kayıtları, **kurgu** belge dağarcığı,
  beş **kurgu** araç, yirmi dört **kurgu** görev ve ders içinde yazılmış bir **ajan benzeticisi**.
  Gerçek bir model ucu ya da araç sunucusu çağrılmaz.
- **AM54.** Taban çizgisi **düzeneksiz döngüdür**: onay kapalı, sekiz adımlık bütçe.
- **AM55.** Bir yazım iki durumda **yanlıştır**: istek yazma istemiyorsa (**istenmeyen yan etki**)
  ya da yazılan değer doğru yanıt değilse (**yanlış değer**).
- **AM56.** Onay noktasında teklif edilen kayıt gösterilir. İki denetim **kuralla** yapılır (istek
  yazma istiyor mu, değer var mı), üçüncüsü **yeniden hesaplamadır** ve **0,60 payla** yapılır.
- **AM57.** Reddedilen çağrı **hiç yürütülmez**; ajan adımını harcar ve devam eder. Her soru
  ayrıca **35 belirteç** durum özeti götürür.
- **AM58.** **Yan etki kapsamı** yalnız `kayit_yaz` öncesi sorar; **her adım kapsamı** her adım
  öncesi sorar ama yine yalnız yan etkili çağrıyı durdurabilir.
- **AM59.** İkinci koşum tohum **20260219**; iki koşum farkı bu kümede **bir görevdir** (0,0417) ve
  altındaki her fark **ölçülmemiş sayılır**.

#### İki Yazım, İki Sonuç

Önce iki görev üç kapsamda koşturulur. `G09` bir belge görevidir ve yazma istemez, `G23` bir kayıt
görevidir.

```python
# ORTAK KURGU KURULUM -- olcum aginin 60 kurgu abonesi, 5 kurgu belge,
# 5 kurgu arac, 24 kurgu gorev ve ders icinde yazilmis ajan benzeticisi.
# Gercek bir model ucu, arac sunucusu ya da calisma zamani 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


# ---- KURGU ortam: olcum aginin kayitlari
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)


# ---- KURGU gorevler: her biri bilinen bir arac dizisiyle cozulur
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"])


# ---- KURGU ajan: her adimda bir arac secer ya da biter
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)}



# ---- Bu derste eklenen parca: yan etkili aracin onunde onay noktasi

GOR_DEGER = 0.60


def yanlis_mi(g, deger):
    """Yazim yanlistir: istek yazma istemiyorsa ya da deger dogru yanit degilse."""
    return "yaz" not in g["istek"] or deger != dogru_yanit(g)


def onay_ver(g, deger, r):
    """Onay noktasinda teklif edilen kayit gosterilir. Iki denetim kuralla,
    ucuncusu yeniden hesapla yapilir; hesaplamayan insan onaylar."""
    if "yaz" not in g["istek"]:
        return False
    if deger is None:
        return False
    if r() < GOR_DEGER:
        return deger == dogru_yanit(g)
    return True


def onayli_ajan(g, en_cok_adim, tohum, kapsam="kapali"):
    """Ortak dongu. kapsam 'yan_etki' ise yalniz yan etkili cagri oncesi,
    'her_adim' ise her adim oncesi insana sorulur; durdurma yalniz yan etkide."""
    r = uretec(tohum ^ izno(g["ad"]))
    ri = uretec(tohum ^ izno("insan|" + g["ad"]))
    alan = {"abone": g["abone"], "anahtar": g["istek"], "sutun": "bedel"}
    iz, adim, bel, k = [], 0, 0, 0
    s_ = {"yazim": 0, "yanlis_yazim": 0, "durdurulan": 0,
          "durdurulan_yanlis": 0, "soru": 0, "yazilan": None}
    while adim < en_cok_adim:
        adim += 1
        bel += 60 + 22 * len(iz)
        hedef = g["dizi"][k] if k < len(g["dizi"]) else None
        if hedef is None:
            if r() < 0.88:
                break
            sec = ARAC_ADI[int(r() * len(ARAC_ADI))]
        else:
            sec = hedef if r() < gorme(k + 1) else ARAC_ADI[int(r() * len(ARAC_ADI))]
        yan = ARAC[sec]["yan_etki"]
        if kapsam == "her_adim" or (kapsam == "yan_etki" and yan):
            s_["soru"] += 1
            bel += 35
            if yan and not onay_ver(g, alan.get("sonuc"), ri):
                s_["durdurulan"] += 1
                s_["durdurulan_yanlis"] += yanlis_mi(g, alan.get("sonuc"))
                iz.append(sec)
                continue
        if sec == "kayit_yaz":
            alan["deger"] = alan.get("sonuc")
        s = cagir_arac(sec, alan)
        iz.append(sec)
        bel += 30
        if s.get("yazildi"):
            s_["yazim"] += 1
            s_["yazilan"] = s["deger"]
            s_["yanlis_yazim"] += yanlis_mi(g, alan.get("deger"))
        if "hata" in s:
            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
    s_.update(adim=adim, bel=bel, yanit=alan.get("sonuc"))
    return s_


print("gorev  tur    kapsam    soru  yazim  yanlis  durdurulan  yazilan deger")
for i in (8, 22):
    g = GOREV[i]
    print(f"-- {g['ad']} dogru yanit {dogru_yanit(g)}")
    for ad in ("kapali", "yan_etki", "her_adim"):
        s = onayli_ajan(g, 8, TOHUM, ad)
        print(f"{g['ad']:<7}{g['tur']:<7}{ad:<10}{s['soru']:>4}{s['yazim']:>7}"
              f"{s['yanlis_yazim']:>7}{s['durdurulan']:>12}   {s['yazilan']}")
```

```
gorev  tur    kapsam    soru  yazim  yanlis  durdurulan  yazilan deger
-- G09 dogru yanit SY01
G09    belge  kapali       0      1      1           0   SY01
G09    belge  yan_etki     1      0      0           1   None
G09    belge  her_adim     2      0      0           1   None
-- G23 dogru yanit 1132
G23    kayit  kapali       0      1      1           0   TR01
G23    kayit  yan_etki     1      1      1           0   TR01
G23    kayit  her_adim     8      1      1           0   TR01
```

İki blok iki ayrı yanlışı gösteriyor. `G09` bir belge sorusudur; ajan doğru yanıtı bulduktan sonra
dolaşıp `kayit_yaz` çağırıyor ve **hiç istenmemiş bir kayıt** bırakıyor. Yazılan değer doğrudur,
yanlış olan yazmanın kendisidir. Onay noktası bunu **kuralla** durduruyor: istek yazma istemiyorsa
teklif reddedilir, hesap gerekmez.

`G23` ötekidir. İstek yazma istiyor ve ortada bir değer var, yani iki kural denetimi de geçiyor.
Ama ajan yazmadan önce dolaşıp `belge_ara` çağırmış ve biriken sonucu bir belge koduyla ezmiş:
kayda 1132 yerine `TR01` yazılıyor. Kural denetimi değerin **varlığına** bakar, **türüne** bakmaz;
bunu görmenin tek yolu yeniden hesaplamaktır ve insan bu koşumda hesaplamıyor. Kayıt üç kapsamda da
yazılıyor. **Kural yanlışı bedava yakalar, değer yanlışı ancak yeniden hesapla yakalanır.**

Kapsamın bedeli soru sütunundadır: `G23`'te her adımda sormak **sekiz kez**, yan etki kapsamı
**bir kez** soruyor ve sonuç aynı.

#### Onayın Durdurduğu ve Ödettiği

Şimdi bütün küme üç kapsamda koşturulur.

```python
def kosum(tohum, kapsam, en_cok_adim=8):
    CAGRI[0] = 0
    d = {"dogru": 0, "adim": 0, "bel": 0, "soru": 0, "yazim": 0,
         "yanlis_yazim": 0, "durdurulan": 0}
    for g in GOREV:
        s = onayli_ajan(g, en_cok_adim, tohum, kapsam)
        d["dogru"] += s["yanit"] == dogru_yanit(g)
        d["adim"] += s["adim"]
        d["bel"] += s["bel"]
        for a in ("soru", "yazim", "yanlis_yazim", "durdurulan"):
            d[a] += s[a]
    d["cagri"] = CAGRI[0]
    return d


for t in (TOHUM, TOHUM + 1):
    print(f"\ntohum {t}")
    print("kapsam    basarim   adim  cagri  belirtec  soru  yazim  yanlis"
          "  durdurulan")
    for kapsam in ("kapali", "yan_etki", "her_adim"):
        d = kosum(t, kapsam)
        print(f"{kapsam:<10}{d['dogru']:>4}/24{d['adim']:>7}{d['cagri']:>7}"
              f"{d['bel']:>10}{d['soru']:>6}{d['yazim']:>7}"
              f"{d['yanlis_yazim']:>8}{d['durdurulan']:>12}")
```

```
tohum 20260218
kapsam    basarim   adim  cagri  belirtec  soru  yazim  yanlis  durdurulan
kapali      23/24     97     74     11978     0      5       3           0
yan_etki    23/24     97     70     12103     7      3       1           4
her_adim    23/24     97     70     14448    74      3       1           4

tohum 20260219
kapsam    basarim   adim  cagri  belirtec  soru  yazim  yanlis  durdurulan
kapali      23/24     98     74     11994     0      8       6           0
yan_etki    23/24    100     68     12605    11      3       1           8
her_adim    23/24    100     68     14880    76      3       1           8
```

Birinci satır taban çizgisidir: onay kapalıyken küme **beş kayıt** bırakıyor ve **üçü yanlış**;
ikinci koşumda sekiz kayıt, altısı yanlış. Aynı satırda görev başarımı **23/24**. İki sayı yan yana
durduğunda ders açıktır: **görev başarımı yan etkiyi ölçmez.** Ajan doğru yanıtı verirken ortama üç
yanlış kayıt bırakıyor ve başarım bunu görmüyor.

Yan etki kapsamı açıldığında yazılan kayıt **üçe**, yanlış kayıt **bire** iniyor; ikinci koşumda
da üçe ve bire. Durdurulan yanlış yazım iki koşumda **2** ve **5**, ikisi de ölçüm bandının
üstünde. Bedel yedi ve on bir sorudur — durdurulan yanlış yazım başına **3,5** ve **2,2** soru.
Belirteç 11978'den 12103'e çıkıyor, çağrı 74'ten 70'e iniyor.

Durdurulan sütunu ile yazım sütunu arasındaki fark önemlidir. Onay noktası birinci koşumda **dört**
çağrıyı durduruyor ama engellediği kayıt **ikidir**; kalan iki çağrı elinde değer olmadığı için
zaten aracın hatasına düşecekti. **Durdurulan çağrı, engellenen kayıt değildir.**

Üçüncü satır kursun ikinci iddiasının bir örneğidir. Her adımda sormak yazılan kaydı, yanlış kaydı
ve durdurulan çağrıyı **hiç değiştirmiyor** — üç sütun da yan etki kapsamıyla birebir aynı. Değişen
tek şey bedeldir: soru 7'den **74**'e, belirteç 12103'ten **14448**'e. On kat soru, sıfır kazanç.
Durdurulabilecek tek çağrı türü yan etkilidir; kalan altmış yedi soruda insanın verebileceği tek
yanıt onaydır.

#### Özet

- Yan etkili araç ayrı ele alınmalıdır: `kayit_yaz` çağrıldığı anda geri alınmayan bir iz bırakır.
- Onay kapalıyken küme beş kayıt bırakıyor ve üçü yanlış (ikinci koşumda sekiz ve altı) — ama aynı
  koşumda görev başarımı 23/24. **Görev başarımı yan etkiyi ölçmez.**
- Onay yan etkili çağrıda açıldığında yazılan kayıt üçe, yanlış kayıt bire iniyor; durdurulan
  yanlış yazım iki koşumda 2 ve 5, bedeli yedi ve on bir soru.
- Kural yanlışı bedavaya yakalanır; değer yanlışı ancak **yeniden hesaplamayla** yakalanır.
  Durdurulan çağrı sayısı da engellenen kayıt sayısından büyüktür (dörde karşı iki).
- Onay kapsamını her adıma yaymak durdurulan yazımı hiç değiştirmiyor, soruyu 7'den 74'e ve
  belirteci 12103'ten 14448'e çıkarıyor.

#### Sonraki Adım

Yedi derste bir ajan kuruldu: döngü, adım derinliği, işin bölünmesi, durdurma ölçütü, kendi izine
bakan bir tur, ikinci bir ajan ve yan etkinin önündeki onay noktası. Her derste bedel yazıldı ve
eklenen adımın işi kötüleştirdiği yerler sayıyla gösterildi.

Bütün bu ölçümlerin altında sessiz bir varsayım duruyor: **araç hazırdı ve doğruydu.** `sayac_oku`
her zaman aynı alanları döndürdü, `tarife` varsayılanına düştüğünde bile bir sayı verdi, `kayit_yaz`
istendiği anda yazdı. Ajanın bu araçları **nasıl** seçebildiği hiç sorulmadı: adı, açıklaması ve
şeması ne söylüyordu, sonuç modele hangi biçimde geri veriliyordu, aracın çalıştığı yerin bir sınırı
var mıydı. Sonraki konu **aracın kendisine** bakar ve ilk soru tanımdır: bir aracın adı, açıklaması
ve şeması geçerli çağrı oranını ne kadar oynatıyor.

## Araçlar ve Bellek

Ajanın dokunduğu yüzeylerin ve taşıdığı durumun tek tek sayılması: araç tanımının dört düzeyinde (yalnız ad, ad ve açıklama, şema, şema ve örnek çağrı) geçerli çağrı oranı ile araç hatası, sonucun ham, kırpılmış ve özetlenmiş verilmesinin başarım ile biriken belirtece etkisi, bildirilen kaynak sınırını aşan çağrı sayısı ile sınırın durdurduğu ve durdurmanın düşürdüğü görev payı, tam, salt okuma ve izin listeli yetki kümelerinde tamamlanan görev ile durdurulan çağrı, üç kaynak sınıfı karışımında doğru yanıt payı ve yanlış kaynağın taşındığı adım sayısı, döngü uzadıkça adım başına taşınan belirteç ile pencereye sığan adım ve pencere dolduğunda düşen bilgi, her adım, yalnız sonuç ve seçici yazma politikalarında saklanan kayıt ile sonraki görevlerde kazanılan adım, sıkıştırma oranı arttıkça düşen belirteç ile atılan alanın gerektiği görev sayısı, eskitme eşiği sıkılaştıkça düşen bayat kayıt ile artan geçerli kayıt kaybı.

### Araç Tanımı

Source: https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/arac-tanimi

Aracın adının, açıklamasının ve şemasının döngüye ne kattığının ölçülmesi: 24 kurgu görevde tanım dört düzeyde verilir ve yalnız ad verildiğinde 157 çağrının 113'ü araç hatasıyla döner, geçerli çağrı oranı 0,2803'te kalır, görev başarımı 0,3333 olur. Açıklama eklendiğinde hata 56'ya iner ve başarım 0,6667'ye çıkar. Şema eklendiğinde çağrı sayısı 74'e, hata 8'e düşer ve geçerli oran 0,8919 olur, ama başarım 0,7917'de durur: şema alan adlarını verir, hangi alanın hangi değeri taşıdığını vermez ve yer değiştiren iki endeks şemadan geçen bir çağrıyla 52 yerine 99948 döndürür. Örnek çağrı bu payı sıfırlar, başarımı 0,9583'e çıkarır ve adım, çağrı ile araç hatasını hiç değiştirmez; bedeli kataloğun 97'den 164 belirtece büyümesidir ve doğru yanıt başına belirteç 1126'dan 1212'ye çıkar.

Önceki yedi derste ajan döngüsü kuruldu: algı, akıl yürütme, eylem ve gözlem sırası, planlayıcı ile
yürütücünün ayrılması, adım bütçesi, durdurma ölçütü, öz eleştiri turu, çoklu ajan düzeni ve yan
etkili araçta onay noktası. Hepsinde bir şey sessizce varsayıldı: **araç hazır ve doğruydu.**

Bu ders o varsayımı kaldırır: aracın **tanımı** döngüye ne katıyor? Model API'leri ile Uygulama
Geliştirme kursu **tek bir çağrının** biçimini ölçmüştü — katalog verilmediğinde geçerli çağrı
oranı 0,0500, adlar ve parametreler birlikte verildiğinde 0,6917. O ölçüm bir metnin
ayrıştırılması üzerineydi ve tekrarlanmaz; sayılar girdi olarak alınır. Buradaki çağrı bir işlev sınırından
geçer ve ölçülen şey tanımın **döngü boyunca** kattığıdır.

- **AB1.** Ortam, araç kümesi, görev kümesi ve ajan döngüsü **kurgudur**; gerçek bir model uç
  noktası, araç sunucusu ya da çalışma zamanı çağrılmaz. 60 kurgu abone, 5 kurgu belge, 5 araç ve
  bilinen bir araç dizisiyle çözülen 24 görev. Tohum **20260218**, ikinci koşum **20260219**.
- **AB2.** Araç tanımı dört düzeyde verilir: yalnız ad; ad ve açıklama; ad, açıklama ve şema; şema
  ve örnek çağrı. Her düzey bir öncekini kapsar.
- **AB3.** Tanım iki şeyi belirler: doğru aracı seçme payının çarpanını ve **bildirilen her alanın
  doldurulma payını**. Ad zayıf bir imdir; açıklama aracın ne yaptığını, şema hangi alanları
  istediğini söyler.
- **AB4.** Şema alan **adlarını** verir, alanların hangi değeri taşıdığını vermez. Aynı türden iki
  alan (`onceki` ve `simdiki`) 0,30 payla yer değiştirir; **örnek çağrı bu payı sıfırlar.**
- **AB5.** **Araç hatası** aracın kendi döndürdüğü hatadır: eksik alan ya da olmayan araç. Şema
  dışındaki alanlar çağrı sınırında süzülür; geçerli çağrı, hata döndürmeyendir.
- **AB6.** Katalog her adımda istemde durur; bedeli düzeyin belirteç sayısıdır.
- **AB7.** Taban çizgisi **yalnız ad** düzeyidir; düzeneksiz döngü budur. Adım bütçesi dört düzeyde
  de sekizdir.
- **AB8.** **Görev başarımı**, döngünün bıraktığı sonucun doğru yanıta eşit olmasıdır; ajanın kendi
  kendine durup durmadığından bağımsızdır.

#### Tanımın Dört Düzeyi

Aşağıdaki kod ortamı, araç kümesini, görev kümesini ve ajan döngüsünü kurar, sonra döngünün üzerine
tanım katmanını ekler. Çağrı sınırı şema dışı alanları yine süzer, fakat bildirilen bir alanın
**doldurulup doldurulmadığı** artık tanımın düzeyine bağlıdır.

```python
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


# ---- KURGU ortam: olcum aginin kayitlari
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)

# ---- KURGU gorevler: her biri bilinen bir arac dizisiyle cozulur
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"])


# ---- KURGU ajan: her adimda bir arac secer ya da biter
def gorme(d):
    return max(0.34, 0.80 - 0.09 * (d - 1))       # dogru araci secme payi, derinlikle duser


ACIKLAMA = {
    "sayac_oku": "abonenin onceki ve simdiki sayac endeksini dondurur",
    "tuketim":   "iki endeks arasindaki farki metrekup olarak verir",
    "tarife":    "metrekup ve abone tipinden donem bedelini hesaplar",
    "belge_ara": "anahtar metne en yakin kurgu belgeyi getirir",
    "kayit_yaz": "bir abonenin bir sutununa deger yazar ; yan etkilidir"}
ORNEK = {"sayac_oku": "sayac_oku ( abone = A007 )",
         "tuketim":   "tuketim ( onceki = 41200 , simdiki = 41255 )",
         "tarife":    "tarife ( m3 = 55 , tip = mesken )",
         "belge_ara": "belge_ara ( anahtar = endeks farki )",
         "kayit_yaz": "kayit_yaz ( abone = A007 , sutun = bedel , deger = 470 )"}
# duzey : ad , secim carpani , alan doldurma payi , yer degistirme payi
TANIM = {1: ("yalniz ad", 0.62, 0.45, 0.30),
         2: ("ad + aciklama", 0.86, 0.72, 0.30),
         3: ("ad + aciklama + sema", 1.00, 1.00, 0.30),
         4: ("sema + ornek cagri", 1.00, 1.00, 0.00)}


def katalog(duzey, ad):
    s = ad
    if duzey >= 2:
        s += " : " + ACIKLAMA[ad]
    if duzey >= 3:
        s += " ( " + " , ".join(ARAC[ad]["alan"]) + " )"
    if duzey >= 4:
        s += " ornek : " + ORNEK[ad]
    return s


TANIM_BEL = {d: sum(len(belirtecle(katalog(d, a))) for a in ARAC) for d in TANIM}


def cagir_tanimli(ad, durum, duzey, rt):
    """Ortak cagri sinirinin tanim katmanli hali: bildirilen alanin
    doldurulup doldurulmadigi tanimin duzeyine baglidir."""
    CAGRI[0] += 1
    if ad not in ARAC:
        return {"hata": "arac yok"}
    _, _, pay, karis = TANIM[duzey]
    kw = {a: durum[a] for a in ARAC[ad]["alan"] if a in durum and rt() < pay}
    if {"onceki", "simdiki"} <= set(kw) and rt() < karis:
        kw["onceki"], kw["simdiki"] = kw["simdiki"], kw["onceki"]
    return ARAC[ad]["islev"](**kw)


def ajan_tanim(g, duzey=4, en_cok_adim=8, tohum=TOHUM):
    """Ortak ajan dongusu; tek fark cagrinin tanim katmanindan gecmesi
    ve katalogun her adimda istemde durmasi."""
    r = uretec(tohum ^ izno(g["ad"]))
    rt = uretec(tohum ^ izno(g["ad"] + "tanim"))
    carpan = TANIM[duzey][1]
    alan = {"abone": g["abone"], "anahtar": g["istek"], "sutun": "bedel"}
    iz, adim, bel, hata, yabanci = [], 0, 0, 0, 0
    dizi, k = g["dizi"], 0
    while adim < en_cok_adim:
        adim += 1
        bel += 60 + 22 * len(iz) + TANIM_BEL[duzey]
        hedef = dizi[k] if k < len(dizi) else None
        if hedef is None:
            if r() < 0.88:
                break
            sec = ARAC_ADI[int(r() * len(ARAC_ADI))]
        else:
            sec = hedef if r() < gorme(k + 1) * carpan else ARAC_ADI[int(r() * len(ARAC_ADI))]
        if sec == "kayit_yaz":
            alan["deger"] = alan.get("sonuc")
        s = cagir_tanimli(sec, alan, duzey, rt)
        iz.append((sec, "hata" in s))
        bel += 30
        if "hata" in s:
            hata += 1
            continue
        yabanci += sec not in dizi
        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 {"adim": adim, "belirtec": bel, "hata": hata, "yabanci": yabanci,
            "yanit": alan.get("sonuc")}


for d in TANIM:
    print(f"duzey {d}  {TANIM[d][0]:<20} katalog {TANIM_BEL[d]:>3} belirtec")
print("  duzey 3 satiri :", katalog(3, "tuketim"))
print("  duzey 4 eki    : ornek :", ORNEK["tuketim"])
g = GOREV[2]
for d in (3, 4):
    s = ajan_tanim(g, d)
    print(f"  {g['ad']} {g['tur']:<8} duzey {d}  yanit {s['yanit']:>6}  dogru"
          f" {dogru_yanit(g)}  arac hatasi {s['hata']}")
```

```
duzey 1  yalniz ad            katalog  10 belirtec
duzey 2  ad + aciklama        katalog  71 belirtec
duzey 3  ad + aciklama + sema katalog  97 belirtec
duzey 4  sema + ornek cagri   katalog 164 belirtec
  duzey 3 satiri : tuketim : iki endeks arasindaki farki metrekup olarak verir ( onceki , simdiki )
  duzey 4 eki    : ornek : tuketim ( onceki = 41200 , simdiki = 41255 )
  G03 tuketim  duzey 3  yanit  99948  dogru 52  arac hatasi 1
  G03 tuketim  duzey 4  yanit     52  dogru 52  arac hatasi 1
```

Son iki satır bu dersin çekirdeğidir. Aynı görev, aynı tohum, aynı araç dizisi ve **aynı sayıda
araç hatası**. Üçüncü düzeyde ajan `tuketim` aracını şemanın bildirdiği iki alanla çağırdı, çağrı
şemadan geçti, araç hata döndürmedi — ve sonuç 52 yerine 99948 çıktı. Şema `onceki` ile `simdiki`
adlarını bildiriyor, hangisinin küçük olduğunu bildirmiyor. Alanlar yer değiştirdiğinde fark eksiye
düşüyor, sayaç dönüşü kuralı devreye giriyor ve araç yüz bine yakın bir tüketim döndürüyor. Bu çağrı
**geçerlidir ve yanlıştır.**

#### Dört Düzeyin Sayıları

Aynı 24 kurgu görev dört tanım düzeyinde koşturulur; başarım iki tohumla ayrı ayrı basılır.

```python
def olc(duzey, tohum=TOHUM):
    CAGRI[0] = 0
    ok = adim = bel = hata = yab = 0
    for g in GOREV:
        s = ajan_tanim(g, duzey, tohum=tohum)
        ok += s["yanit"] == dogru_yanit(g)
        adim += s["adim"]
        bel += s["belirtec"]
        hata += s["hata"]
        yab += s["yabanci"]
    return ok / len(GOREV), CAGRI[0], hata, yab, adim, bel


print("tanim duzeyi          cagri  hata  gecerli  basarim A  basarim B  adim"
      "  belirtec  yanit basina")
for d in TANIM:
    a, b = olc(d), olc(d, TOHUM + 1)
    print(f"{TANIM[d][0]:<20} {a[1]:>6} {a[2]:>5}  {(a[1] - a[2]) / a[1]:>7.4f}"
          f"  {a[0]:>9.4f}  {b[0]:>9.4f}  {a[4]:>4}  {a[5]:>8}  {a[5] / (a[0] * 24):>12.0f}")
a = olc(4)
print(f"en yuksek duzey : gecerli {a[1] - a[2]} cagrinin {a[3]} tanesi gorevin"
      f" dizisinde olmayan bir araci cagiriyor")
```

```
tanim duzeyi          cagri  hata  gecerli  basarim A  basarim B  adim  belirtec  yanit basina
yalniz ad               157   113   0.2803     0.3333     0.4167   167     27950          3494
ad + aciklama           111    56   0.4955     0.6667     0.5833   129     27489          1718
ad + aciklama + sema     74     8   0.8919     0.7917     0.7917    97     21387          1126
sema + ornek cagri       74     8   0.8919     0.9583     0.9583    97     27886          1212
en yuksek duzey : gecerli 66 cagrinin 8 tanesi gorevin dizisinde olmayan bir araci cagiriyor
```

Taban çizgisi ilk satırdır: yalnız ad bilindiğinde 157 çağrının **113'ü** araç hatasıyla dönüyor,
geçerli çağrı oranı **0,2803**, başarım **0,3333**. Hataların çoğu eksik alandır. Ad, aracın ne
istediğini söylemez; ajan elindeki durumdan hangi anahtarı geçireceğini bilemez. Bedeli adım
sütununda görünür: bütçe sekiz olduğu halde toplam adım **167**, görev başına yedinin üstünde.
Hatalı çağrı adımı harcar ve diziyi ilerletmez.

Açıklama eklendiğinde hata **56'ya**, çağrı **111'e** iniyor, başarım **0,6667** oluyor. Açıklama
iki iş yapar: doğru aracı seçme payını yükseltir ve alanların hangi bilgiyi istediğini ima eder.
Şema eklendiğinde çağrı **74'e**, hata **8'e** düşüyor, geçerli oran **0,8919** oluyor ve adım
**97'ye** iniyor. Kalan sekiz hata şemayla ilgili değildir: yanlış seçilen bir aracın kendi ön
koşulu tutmaz.

#### Şemanın Söylemediği

Üçüncü ve dördüncü satır aynı çağrıyı, aynı hatayı ve aynı adımı taşıyor; başarımları **0,7917 ile
0,9583** olarak ayrışıyor. Aradaki 0,1666, örnek çağrının kattığıdır ve döngüye tek bir adım
eklemeden gelir. İki koşum farkı ilk iki düzeyde 0,0834, son iki düzeyde sıfırdır; 0,1666 bu bandın
iki katının üstündedir ve **ölçülmüş sayılır**. Örnek çağrı, şemanın veremediği tek bilgiyi verir:
alanın hangi değeri taşıdığını.

Bedel belirteç sütunundadır. Katalog 97 belirteçten **164 belirtece** büyür ve her adımda istemde
durduğu için toplam belirteç **21387'den 27886'ya** çıkar. Doğru yanıt başına belirteç ise
**1126'dan 1212'ye** yükselir: örnek çağrı başarımı yükseltirken bu ölçekte birim maliyeti de
yükseltiyor. Bedeli yazılmayan düzenek ölçülmemiş sayılır; buradaki bedel altmış yedi belirteçtir.

Son satır şemanın en dıştaki sınırıdır. En yüksek düzeyde bile **geçerli 66 çağrının sekizi**
görevin dizisinde olmayan bir aracı çağırıyor. Model API'leri kursunda aynı kalıntı on dört yanlış
araç adıydı; orada çağrı bir metindi, burada bir işlev sınırından geçti ve araç düzgün çalıştı. Bir
tanım neyin **çağrılabileceğini** söyler, neyin **çağrılması gerektiğini** söylemez.

#### Özet

- Araç tanımı dört düzeye ayrılır: yalnız ad, ad ve açıklama, şema, şema ve örnek çağrı.
- Yalnız ad verildiğinde 157 çağrının 113'ü araç hatasıyla döner, geçerli oran 0,2803, başarım
  0,3333'tür; hatalı çağrı adım harcar ve toplam adımı 167'ye çıkarır.
- Açıklama hatayı 56'ya, şema 8'e indirir; şemayla çağrı 74'e, adım 97'ye iner, geçerli oran
  0,8919 olur.
- Şema alan adlarını verir, alanın hangi değeri taşıdığını vermez: yer değiştiren iki endeks
  şemadan geçen bir çağrıyla 52 yerine 99948 döndürür.
- Örnek çağrı başarımı 0,7917'den 0,9583'e çıkarır ve adımı, çağrıyı, hatayı hiç değiştirmez;
  bedeli kataloğun 164 belirtece büyümesi ve yanıt başına belirtecin 1126'dan 1212'ye çıkmasıdır.
- En yüksek düzeyde bile geçerli 66 çağrının sekizi görevin dizisinde olmayan aracı çağırır;
  geçerlilik doğruluk değildir.

#### Sonraki Adım

Bu derste araç çağrıldı ve döndürdüğü sözlük olduğu gibi duruma yazıldı; her alan bir sonraki adıma
taşındı ve her adımda istemde yeniden durdu. Belirteç sütununun görev başına bin belirtecin üstüne
çıkmasının bir nedeni budur. Sonucu olduğu gibi taşımak tek seçenek değildir: kırpılabilir,
özetlenebilir, yalnız gereken alan bırakılabilir. Her biri belirteci düşürür ve bir şeyi düşürme
riski taşır. Sonraki ders sonucun modele **hangi biçimde** geri verildiğini ölçer ve başarımı döngü
boyunca biriken belirteçle yan yana basar.

### Araç Çağrısının Yürütülmesi

Source: https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/arac-cagrisinin-yurutulmesi

Aracın döndürdüğü sonucun modele hangi biçimde geri verildiğinin ölçülmesi: 24 kurgu görevde ham gözlem 770 gözlem belirteci ve 10103 toplam belirteçle 0,9583 başarım verir. Gözlemi yedi sözcüğe kırpmak belirteci 9160'a indirir ama abone tipini düşürdüğü için dört işyeri faturası tam yarısı çıkar ve başarım 0,7917'ye iner. Dört sözcüğe kırpmak zinciri koparır: araç hatası 8'den 116'ya, adım 97'den 156'ya, toplam belirteç 15945'e fırlar ve başarım 0,2917'de kalır — ucuzlatmak pahalılaştırır. Taşınacak alanı bildiren özet, ham gözlemin başarımını iki koşumda da korurken gözlem belirtecini 401'e, doğru yanıt başına belirteci 439'dan 401'e indirir. Aynı özetten abone tipi çıkarıldığında başarım kırpmanınkine, 0,7917'ye düşer: özet, bildirimi kadar iyidir.

Önceki derste araç tanımı dört düzeye ayrıldı ve en yüksek düzeyde geçerli çağrı oranı 0,8919,
görev başarımı 0,9583 oldu. O ölçümde çağrının **dönüşü** hiç sorgulanmadı: araç ne döndürdüyse
bütün alanlarıyla duruma yazıldı ve bir sonraki adıma taşındı.

Bir döngüde bu kararsız bir varsayımdır. Araç sonucu modele geri verilen bir **gözlemdir** ve o
gözlem her adımda istemde yeniden durur. Bir belge aracının döndürdüğü tam metin, bir okuma
aracının döndürdüğü dört alan, bir yazma aracının onayı — hepsi döngü uzadıkça birikir. Bu dersin
sorusu şudur: gözlem hangi biçimde geri verilirse başarım ne olur ve döngü boyunca kaç belirteç
birikir.

- **AB9.** Ortam, araç kümesi, görev kümesi ve ajan döngüsü **kurgudur**; gerçek bir model uç
  noktası, araç sunucusu ya da çalışma zamanı çağrılmaz. 60 kurgu abone, 5 kurgu belge, 5 araç ve
  bilinen bir araç dizisiyle çözülen 24 görev. Tohum **20260218**, ikinci koşum **20260219**.
- **AB10.** Araç bir sözlük döndürür; **gözlem** o sözlüğün metne çevrilmiş halidir. Ajan durumunu
  sözlükten değil, **gözlem metninden geri okuyarak** günceller.
- **AB11.** Üç gözlem biçimi vardır. **Ham** gözlem bütün alanları taşır. **Kırpılmış** gözlem metni
  belirli bir sözcük sayısında keser; kesilen yerde değeri eksik kalan alan geri okunamaz.
  **Özetlenmiş** gözlem, araç başına **bildirilen** alanları taşır.
- **AB12.** Kırpma iki eşikte ölçülür: yedi sözcük ve dört sözcük.
- **AB13.** Biriken gözlem her adımda istemde yeniden gönderilir. Ortak döngü bu birikimi adım
  başına sabit bir katsayıyla sayar; bu ders o katsayının yerine **gerçekten taşınan gözlemin
  belirtecini** koyar. Belirteç kurgu belirteçleyiciyle sayılır.
- **AB14.** Hata döndüren çağrının gözlemi de birikir; hata metni de bir gözlemdir.
- **AB15.** Taban çizgisi **ham gözlemdir**; düzeneksiz döngü budur. Kırpma ve özetleme eklenen
  düzenektir. Adım bütçesi dört biçimde de sekizdir.
- **AB16.** Görev başarımı, döngünün bıraktığı sonucun doğru yanıta eşit olmasıdır.

#### Gözlemin Üç Biçimi

Aşağıdaki kod ortak ortamı, araç kümesini ve görev kümesini kurar, sonra gözlem katmanını ekler:
sözlüğü metne çeviren bir yazıcı, metinden alanları geri okuyan bir çözücü ve üç biçim.

```python
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


# ---- KURGU ortam: olcum aginin kayitlari
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)


# ---- KURGU gorevler: her biri bilinen bir arac dizisiyle cozulur
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"])


# ---- KURGU ajan: her adimda bir arac secer ya da biter
def gorme(d):
    return max(0.34, 0.80 - 0.09 * (d - 1))       # dogru araci secme payi, derinlikle duser


# Aracin dondurdugu sozluk, modele geri verilmeden once bir metne cevrilir.
OZET_ALAN = {"sayac_oku": ("onceki", "simdiki", "tip"), "tuketim": ("m3",),
             "tarife": ("tutar",), "belge_ara": ("kod",), "kayit_yaz": ("yazildi",)}
BICIM = {"ham": None, "kirpik 7": 7, "kirpik 4": 4, "ozet": None}


def sozlukten(s):
    return " , ".join(f"{k} = {v}" for k, v in s.items())


def coz(metin):
    """Gozlem metninden alanlari geri okur; degeri kesilmis alan dusulur."""
    d = {}
    for p in metin.split(" , "):
        a, _, v = p.partition(" = ")
        v = v.strip(" ,")
        if v:
            d[a.strip(" ,")] = int(v) if v.lstrip("-").isdigit() else v
    return d


def gozlemle(ad, sonuc, bicim):
    """Ham , kirpilmis ya da ozetlenmis gozlem metni."""
    if bicim == "ozet":
        sonuc = {k: v for k, v in sonuc.items() if k in OZET_ALAN.get(ad, ())}
    metin = sozlukten(sonuc)
    if BICIM[bicim]:
        metin = " ".join(metin.split()[:BICIM[bicim]])
    return metin


def ajan_gozlem(g, bicim="ham", en_cok_adim=8, tohum=TOHUM):
    """Ortak ajan dongusu; tek fark gozlemin bir metne cevrilip geri okunmasi
    ve biriken gozlem belirtecinin her adimda yeniden gonderilmesi."""
    r = uretec(tohum ^ izno(g["ad"]))
    alan = {"abone": g["abone"], "anahtar": g["istek"], "sutun": "bedel"}
    iz, adim, bel, hata, birik = [], 0, 0, 0, 0
    dizi, k = g["dizi"], 0
    while adim < en_cok_adim:
        adim += 1
        bel += 60 + birik                    # biriken gozlem her adimda yeniden gonderilir
        hedef = dizi[k] if k < len(dizi) else None
        if hedef is None:
            if r() < 0.88:
                break
            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 sec == "kayit_yaz":
            alan["deger"] = alan.get("sonuc")
        s = cagir_arac(sec, alan)
        iz.append((sec, "hata" in s))
        bel += 30
        metin = gozlemle(sec, s, bicim)
        birik += len(belirtecle(metin))
        if "hata" in s:
            hata += 1
            continue
        d = coz(metin)
        alan.update(d)
        if sec == "tuketim" and "m3" in d:
            alan["sonuc"] = d["m3"]
        if sec == "tarife" and "tutar" in d:
            alan["sonuc"] = d["tutar"]
        if sec == "belge_ara" and "kod" in d:
            alan["sonuc"] = d["kod"]
        if sec == hedef:
            k += 1
    return {"adim": adim, "belirtec": bel, "hata": hata, "gozlem": birik,
            "yanit": alan.get("sonuc")}


for ad in ("sayac_oku", "belge_ara"):
    ornek = ARAC[ad]["islev"](**{"abone": "A007", "anahtar": "endeks farki"})
    for b in ("ham", "kirpik 7", "ozet"):
        m = gozlemle(ad, ornek, b)
        print(f"{ad:<10} {b:<9} {len(belirtecle(m)):>2} belirtec  {m}")
    print(f"{ad:<10} kirpik 7 geri okunan alanlar :",
          " , ".join(coz(gozlemle(ad, ornek, "kirpik 7"))))
```

```
sayac_oku  ham       19 belirtec  onceki = 78189 , simdiki = 78241 , tip = mesken , bolge = merkez
sayac_oku  kirpik 7   9 belirtec  onceki = 78189 , simdiki = 78241
sayac_oku  ozet      14 belirtec  onceki = 78189 , simdiki = 78241 , tip = mesken
sayac_oku  kirpik 7 geri okunan alanlar : onceki , simdiki
belge_ara  ham       19 belirtec  kod = SY01 , metin = endeks farki tuketimi verir sayac donusu 100000 eklenir
belge_ara  kirpik 7   8 belirtec  kod = SY01 , metin = endeks
belge_ara  ozet       3 belirtec  kod = SY01
belge_ara  kirpik 7 geri okunan alanlar : kod , metin
```

İki araç aynı ham boyu taşıyor — on dokuz belirteç — ve iki biçim onları çok farklı yerlerinden
kesiyor. Kırpma **konuma** bakar: `sayac_oku` gözleminde son iki alan, `abone tipi` ve `bölge`,
yedinci sözcükten sonra kaldıkları için düşüyor. Özet **bildirime** bakar: aynı araçta tip
bildirilmiş olduğu için kalıyor, bölge bildirilmediği için gidiyor. Belge aracında sıralama tersine
işliyor; orada kod başta durduğu için kırpma onu koruyor, özet ise metnin tamamını atarak on dokuz
belirteci üçe indiriyor.

#### Dört Biçimin Sayıları

Aynı 24 kurgu görev dört biçimde koşturulur. Gözlem belirteci, döngü boyunca üretilen gözlem
metinlerinin toplam uzunluğudur; toplam belirteç ise onların **yeniden gönderilmesini** de içerir.

```python
def olc(bicim, tohum=TOHUM):
    CAGRI[0] = 0
    ok = adim = bel = hata = goz = 0
    for g in GOREV:
        s = ajan_gozlem(g, bicim, tohum=tohum)
        ok += s["yanit"] == dogru_yanit(g)
        adim += s["adim"]
        bel += s["belirtec"]
        hata += s["hata"]
        goz += s["gozlem"]
    return ok / len(GOREV), CAGRI[0], hata, adim, goz, bel


print("gozlem bicimi  cagri  hata  adim  gozlem bel  toplam bel  basarim A"
      "  basarim B  yanit basina")
for b in BICIM:
    a, c = olc(b), olc(b, TOHUM + 1)
    print(f"{b:<13} {a[1]:>6} {a[2]:>5} {a[3]:>5} {a[4]:>11} {a[5]:>11}"
          f"  {a[0]:>9.4f}  {c[0]:>9.4f}  {a[5] / (a[0] * 24):>12.0f}")
OZET_ALAN["sayac_oku"] = ("onceki", "simdiki")     # tip bildirimden dusuruldu
a = olc("ozet")
print(f"dar ozet ( tip bildirilmiyor ) : basarim {a[0]:.4f} , gozlem belirteci"
      f" {a[4]} , arac hatasi {a[2]}")
```

```
gozlem bicimi  cagri  hata  adim  gozlem bel  toplam bel  basarim A  basarim B  yanit basina
ham               74     8    97         770       10103     0.9583     0.9583           439
kirpik 7          74     8    97         428        9160     0.7917     0.7500           482
kirpik 4         149   116   156         616       15945     0.2917     0.2917          2278
ozet              74     8    97         401        9229     0.9583     0.9583           401
dar ozet ( tip bildirilmiyor ) : basarim 0.7917 , gozlem belirteci 311 , arac hatasi 8
```

Taban çizgisi ilk satırdır: ham gözlem 770 gözlem belirteci üretir, toplam 10103 belirteç harcar ve
başarımı **0,9583**'te tutar. İkinci satır kırpmanın kattığını ve düşürdüğünü yan yana koyar. Gözlem
belirteci **428'e**, toplam **9160'a** iner — ama başarım **0,7917** olur. Çağrı, adım ve araç
hatası hiç değişmez; düşen dört görevin hepsi işyeri abonesidir ve dördünde de fatura tam **yarısı**
çıkar. Tip düşünce tarife aracı bildirilmiş varsayılan değeri, yani meskeni kullanır. Kırpma bir
hata üretmedi, **sessiz bir yanlış** üretti.

Üçüncü satır kursun ikinci iddiasının açık bir örneğidir. Kırpmayı dört sözcüğe sıkmak `sayac_oku`
gözleminde ikinci endeksi de keser; `tuketim` aracı eksik alanla döner, zincir kopar ve ajan aynı
adımı yeniden dener. Araç hatası **8'den 116'ya**, çağrı 74'ten **149'a**, adım 97'den **156'ya**
çıkar. Sonuç şudur: gözlem belirteci ham biçimden hâlâ düşük (616), fakat **toplam belirteç
15945'e** fırlar — ham biçimin bir buçuk katı. Gözlemi ucuzlatmak döngüyü pahalılaştırdı.

#### Bildirim ve Konum

Son satır özetin sayısıdır ve tabloda tek başına durur: başarım iki koşumda da **0,9583**, yani ham
gözlemle aynı; gözlem belirteci **401**, yani hamın yarısından az; doğru yanıt başına belirteç
**439'dan 401'e** iner. Özet hem en ucuz hem en isabetli biçimdir, çünkü neyi atacağını konumdan
değil **bildirimden** okur.

Bu üstünlük bedava değildir ve son çıktı satırı bedelini yazar. Özetin bildiriminden yalnız `tip`
alanı düşürüldüğünde başarım **0,7917'ye** iner — kırpmanın düştüğü yere. Özet, bildirimi kadar
iyidir; hangi alanın sonraki adımda gerekeceğini bilen kişi onu bildirmek zorundadır ve bunu
bilmiyorsa özet, kırpmadan daha güvenli değildir.

Tabloda bir satır ters görünüyor: özetin gözlem belirteci kırpık yedininkinden düşük (401'e karşı
428) ama toplam belirteci yüksek (9229'a karşı 9160). Nedeni birikimin aritmetiğidir. Bir gözlem,
kendisinden sonra gelen her adımda yeniden gönderilir; bu yüzden **erken gözlem geç gözlemden
pahalıdır**. Özet, `sayac_oku` gözlemini 14 belirteçte tutar (kırpma 9'da) ve o gözlem çoğu görevin
**ilk** adımındadır. Buna karşılık özetin büyük kazancı `belge_ara`dadır (19'dan 3'e) ve o gözlem
tek adımlık görevlerin **son** adımıdır, yani hiç yeniden gönderilmez.

#### Özet

- Araç sonucu duruma doğrudan yazılmaz: bir gözlem metnine çevrilir, geri okunur ve her adımda
  istemde yeniden gönderilir.
- Ham gözlem 770 gözlem belirteci, 10103 toplam belirteç ve 0,9583 başarım verir; taban çizgisi
  budur.
- Yedi sözcüğe kırpmak belirteci 9160'a indirir ama abone tipini düşürdüğü için dört işyeri
  faturasını yarıya indirir ve başarımı 0,7917'ye çeker; araç hatası hiç artmaz, yanlış sessizdir.
- Dört sözcüğe kırpmak zinciri koparır: araç hatası 116'ya, adım 156'ya, toplam belirteç 15945'e
  çıkar ve başarım 0,2917'ye iner; gözlemi ucuzlatmak döngüyü pahalılaştırır.
- Bildirimli özet ham gözlemin başarımını korurken gözlem belirtecini 401'e ve doğru yanıt başına
  belirteci 401'e indirir; aynı özetten tip çıkarıldığında başarım 0,7917'ye düşer.
- Birikimde erken gözlem geç gözlemden pahalıdır: bir gözlem kendisinden sonraki her adımda yeniden
  gönderilir.

#### Sonraki Adım

Bu iki derste araçlar tanımlandı, çağrıldı ve dönüşleri biçimlendirildi. Hepsinin ortak bir yanı
vardı: her araç **sabit bir işi** yapıp döndü. Bir okuma sabit sayıda alan, bir hesap sabit sayıda
işlem, bir arama sabit bir metin. Bu yüzden bir çağrının ne kadar tutacağı hiç sorulmadı. Ajanın
**kendi yazdığı** bir programı çalıştıran bir araçta bu varsayım kalkar: kaç adım süreceği, kaç
hücre tutacağı ve kaç çıktı üreteceği tanımda yazmaz, ancak çalışırken görülür. Sonraki ders böyle
bir aracı sanal alan içinde kaynak sınırlarıyla kurar, bildirilen sınırı aşan çağrıları sayar ve
durdurmanın kaç görevi düşürdüğünü ayrıca yazar.

### Kod Yürütme Araçları

Source: https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/kod-yurutme-araclari

Ajanın yazdığı programı sanal alanda çalıştıran bir aracın kaynak sınırlarıyla ölçülmesi: aynı tutarı hesaplayan üç yazım biçimi 15, 172 ve 19 adım harcar, 2 ile 5 hücre tutar ve 1 ile 5 çıktı üretir. Gevşek sınırda hiçbir çağrı bildirilen sınırı aşmaz ama üç görev, çağrı başına sınır hiçbir şey durdurmadan 96 adımlık görev bütçesini geçer. Orta sınır 28 kod çağrısının 24'ünü durdurur ve durdurduğu sınıflar 20 adım ile 4 hücredir; sıkı sınırda aynı 24 çağrı bu kez 20 adım ve 4 çıktı sınıfından durur. Durdurma görevi doğrudan düşürmez, adım bütçesini tüketerek düşürür: kod görevlerinde başarım 0,7778'den 0,3333'e, ikinci koşumda 1,0000'den 0,5556'ya iner. Sıkı sınır orta sınırla aynı başarımı verirken boşa harcanan adımı 1356'dan 552'ye indirir. Koşan kod savunma tarafındadır.

Önceki iki derste her araç **sabit bir işi** yapıp döndü, bu yüzden bir çağrının ne tutacağı
sorulmadı. Ajanın **kendi yazdığı** bir programı çalıştıran araçta bu varsayım kalkar: aynı sonucu
veren iki program yüz kat farklı kaynak harcayabilir ve bu fark tanımda yazmaz.

Ders böyle bir aracı **sanal alan** içinde kurar ve bir saldırı dersi değildir: hiçbir kaçış,
atlatma ya da yetki yükseltme yordamı yazılmaz. **Koşan kod bütünüyle savunma tarafındadır** —
sayar, sınırlar ve durdurur. Yetki sınırlandırma Linux ve Sistem Yönetimi ile Siber Güvenlik
müfredatlarında **en az yetki ilkesi** olarak ölçüldü ve tekrarlanmaz; sayılan tek şey sınırın ne
durdurduğu ve durdurmanın neye mal olduğudur.

- **AB17.** Ortam, araç kümesi, görev kümesi, kod yürütme aracı ve ajan döngüsü **kurgudur**; gerçek
  bir çalışma zamanı, yürütme servisi ya da model uç noktası çağrılmaz. Tarife hesabı sabit bir
  araç olmaktan çıkar; `tarife` düşer, yerine `kod_yurut` gelir ve araç kümesi yine beş kalır.
  Tohum **20260218**, ikinci koşum **20260219**.
- **AB18.** Sanal alan küçük bir yazmaç makinesidir ve **bildirilen üç kaynak sınırı** taşır: adım,
  hücre, çıktı. Sınır aşıldığında çalışma durur ve **aşılan sınıf bildirilir**.
- **AB19.** Ajanın yazdığı program üç biçimden birindedir, **biçim görev başına bir kez** çekilir ve
  üç biçim de aynı tutarı hesaplar.
- **AB20.** Bir çağrının **gerçek tüketimi** kuru çalışmayla ölçülür: program sınırsıza yakın bir
  ayarla bir kez daha koşturulur, yalnız sayıma girer.
- **AB21.** **Görev bütçesi** 96 yürütme adımıdır ve görevin bütün kod çağrılarının toplamına bakar;
  sanal alan bunu görmez, çünkü sınırı **çağrı başınadır**.
- **AB22.** Taban çizgisi **gevşek sınırdır**, adım bütçesi sekizdir, kod görevi dizisinde
  `kod_yurut` bulunandır: 24 görevin dokuzu.

#### Sanal Alan ve Üç Yazım Biçimi

Aşağıdaki kod ortak ortamı ve görev kümesini kurar, sonra yazmaç makinesini, bildirilen sınırları
ve derleyiciyi ekler.

```python
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


# ---- KURGU ortam: olcum aginin kayitlari
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_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},
    "belge_ara":  {"islev": t_belge, "alan": ("anahtar",), "yan_etki": False},
    "kayit_yaz":  {"islev": t_kayit, "alan": ("abone", "sutun", "deger"), "yan_etki": True},
}


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)


# ---- KURGU gorevler: her biri bilinen bir arac dizisiyle cozulur
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"])


# ---- KURGU ajan: her adimda bir arac secer ya da biter
def gorme(d):
    return max(0.34, 0.80 - 0.09 * (d - 1))       # dogru araci secme payi, derinlikle duser


# ---- KURGU kod yurutme araci: yazmac makinesi ve bildirilen kaynak siniri.
# Kosan kod savunma tarafindadir: sayar , sinirlar , durdurur.
SINIR = {"gevsek": {"adim": 512, "hucre": 16, "cikti": 16},
         "orta":   {"adim": 64,  "hucre": 4,  "cikti": 8},
         "siki":   {"adim": 24,  "hucre": 4,  "cikti": 2}}
GOREV_BUTCE = 96                      # gorev basina toplam yurutme adimi


def deg(x, h):
    return h[x] if isinstance(x, str) else x


def yurut(program, sinir):
    """Sinir asildiginda calisma durur ve asilan sinif bildirilir."""
    hucre, cikti, ip, adim = {}, [], 0, 0
    while ip < len(program):
        adim += 1
        if adim > sinir["adim"]:
            return {"hata": "sinir asildi", "sinif": "adim", "harcanan": adim}
        k = program[ip]
        if k[0] == "ata":
            hucre[k[1]] = deg(k[2], hucre)
        elif k[0] == "topla":
            hucre[k[1]] += deg(k[2], hucre)
        elif k[0] == "carp":
            hucre[k[1]] *= deg(k[2], hucre)
        elif k[0] == "azalt":
            hucre[k[1]] -= 1
        elif k[0] == "yaz":
            cikti.append(hucre[k[1]])
        elif k[0] == "dallan" and hucre[k[1]] > 0:
            ip = k[2]
            continue
        if len(hucre) > sinir["hucre"]:
            return {"hata": "sinir asildi", "sinif": "hucre", "harcanan": adim}
        if len(cikti) > sinir["cikti"]:
            return {"hata": "sinir asildi", "sinif": "cikti", "harcanan": adim}
        ip += 1
    return {"tutar": cikti[-1] if cikti else None, "harcanan": adim,
            "hucre": len(hucre), "cikti": len(cikti)}


def derle(stil, m3, tip):
    """Ajanin yazdigi KURGU program; ucu de ayni tutari hesaplar."""
    d, kalan, alt = [], m3, 0
    for ust, birim in BASAMAK:                   # tarife basamaklarina ayir
        pay = min(kalan, ust - alt)
        if pay <= 0:
            break
        d.append((pay, birim))
        kalan, alt = kalan - pay, ust
    if kalan > 0:
        d.append((kalan, USTU))
    p = [("ata", "t", 0)]
    for i, (adet, birim) in enumerate(d):
        if stil == "kisa":
            p += [("ata", "x", adet), ("carp", "x", birim), ("topla", "t", "x")]
        elif stil == "birim":
            p += [("ata", "s", adet), ("topla", "t", birim), ("azalt", "s"),
                  ("dallan", "s", len(p) + 1)]
        else:                                    # acik yazim : basamak basina hucre ve cikti
            p += [("ata", f"d{i}", adet), ("carp", f"d{i}", birim),
                  ("topla", "t", f"d{i}"), ("yaz", f"d{i}")]
    if tip == "isyeri":
        p.append(("carp", "t", ISYERI_KAT))
    return p + [("yaz", "t")]


AYAR = ["gevsek"]                                # sanal alanin bildirdigi sinir


def t_kod(m3=None, tip="mesken", program=None, **_):
    if m3 is None or program is None:
        return {"hata": "eksik alan"}
    return yurut(program, SINIR[AYAR[0]])


# Tarife hesabi sabit bir arac degil , ajanin yazdigi bir programdir; arac sayisi bes kalir.
ARAC["kod_yurut"] = {"islev": t_kod, "alan": ("m3", "tip", "program"), "yan_etki": False}
ARAC_ADI = list(ARAC)
for g in GOREV:
    g["dizi"] = tuple("kod_yurut" if a == "tarife" else a for a in g["dizi"])
STIL, STIL_PAY = ("kisa", "birim", "acik"), [0.40, 0.30, 0.30]

for stil in STIL:
    p = derle(stil, 55, "isyeri")
    s = yurut(p, SINIR["gevsek"])
    d = "  ".join(f"{a} -> {yurut(p, SINIR[a]).get('sinif', 'tamam')}"
                  for a in ("orta", "siki"))
    print(f"{stil:<6} komut {len(p):>2}  adim {s['harcanan']:>3}  hucre {s['hucre']}"
          f"  cikti {s['cikti']}  tutar {s['tutar']}  |  {d}")
```

```
kisa   komut 15  adim  15  hucre 2  cikti 1  tutar 1100  |  orta -> tamam  siki -> tamam
birim  komut 19  adim 172  hucre 2  cikti 1  tutar 1100  |  orta -> adim  siki -> adim
acik   komut 19  adim  19  hucre 5  cikti 5  tutar 1100  |  orta -> hucre  siki -> cikti
```

Üç satır aynı tutarı, 1100'ü, üç ayrı kaynak profiliyle hesaplıyor. Komut sayıları neredeyse eşit —
15, 19, 19 — ve **hiçbir sınır bu sayıdan okunamaz**. Birim birim toplayan biçim **172 adım**
harcar, çünkü komutlarının dördü bir geri dallanmadır; açık yazım biçimi on dokuz adımda biter ama
**beş hücre** açar ve **beş çıktı** üretir. Kaynak sınırı bu yüzden tekil değildir: ikinci biçimi
adım sınırı, üçüncüsünü orta ayarda hücre, sıkı ayarda çıktı sınırı durdurur.

#### Sınırın Durdurduğu ve Durduramadığı

Aynı 24 görev üç sınır ayarında ve iki tohumda koşturulur; sınırı aşan çağrı **kuru çalışmayla**
saptanır, etkin sınır ise gerçekten durdurur.

```python
def ajan_kod(g, en_cok_adim=8, tohum=TOHUM):
    """Ortak ajan dongusu; tek fark tarife yerine ajanin yazdigi programin
    sanal alanda yurutulmesi ve her yurutmenin kaydedilmesi."""
    r = uretec(tohum ^ izno(g["ad"]))
    stil = STIL[ayrik(r(), STIL_PAY)]              # gorev basina tek yazim bicimi
    alan = {"abone": g["abone"], "anahtar": g["istek"], "sutun": "bedel"}
    kod_iz, adim, dizi, k = [], 0, g["dizi"], 0
    while adim < en_cok_adim:
        adim += 1
        hedef = dizi[k] if k < len(dizi) else None
        if hedef is None:
            if r() < 0.88:
                break
            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 sec == "kayit_yaz":
            alan["deger"] = alan.get("sonuc")
        prog = None
        if sec == "kod_yurut" and "m3" in alan:
            prog = alan["program"] = derle(stil, alan["m3"], alan.get("tip", "mesken"))
        s = cagir_arac(sec, alan)
        if prog is not None:
            kod_iz.append((prog, s))
        if "hata" in s:
            continue
        alan.update({kk: vv for kk, vv in s.items() if kk != "hata"})
        if sec == "tuketim":
            alan["sonuc"] = s["m3"]
        if sec == "kod_yurut":
            alan["sonuc"] = s["tutar"]
        if sec == "belge_ara":
            alan["sonuc"] = s["kod"]
        if sec == hedef:
            k += 1
    return {"adim": adim, "kod_iz": kod_iz, "yanit": alan.get("sonuc")}


def olc(ayar, tohum=TOHUM):
    """Sinir denetimi: kuru calisma gercek tuketimi verir , etkin sinir durdurur."""
    AYAR[0], lim, sinif = ayar, SINIR[ayar], {}
    ok = kod = asan = durdurulan = bosa = butce = ok_kod = n_kod = 0
    for g in GOREV:
        s = ajan_kod(g, tohum=tohum)
        d = s["yanit"] == dogru_yanit(g)
        ok += d
        if "kod_yurut" in g["dizi"]:
            n_kod, ok_kod = n_kod + 1, ok_kod + d
        toplam = 0
        for prog, sonuc in s["kod_iz"]:
            kuru = yurut(prog, SINIR["gevsek"])            # yalniz sayim icin
            kod += 1
            asan += (kuru["harcanan"] > lim["adim"] or kuru["hucre"] > lim["hucre"]
                     or kuru["cikti"] > lim["cikti"])
            toplam += sonuc["harcanan"]
            if "hata" in sonuc:
                durdurulan += 1
                bosa += sonuc["harcanan"]
                sinif[sonuc["sinif"]] = sinif.get(sonuc["sinif"], 0) + 1
        butce += toplam > GOREV_BUTCE
    return kod, asan, durdurulan, bosa, butce, ok / len(GOREV), ok_kod / n_kod, sinif


print("sinir    kod cagrisi  asan  durdurulan  bosa adim  butceyi asan"
      "  basarim  kod gorevi")
for ayar in SINIR:
    for tohum, e in ((TOHUM, "A"), (TOHUM + 1, "B")):
        a = olc(ayar, tohum)
        print(f"{ayar:<7}{e} {a[0]:>10} {a[1]:>5} {a[2]:>11} {a[3]:>10} {a[4]:>13}"
              f"  {a[5]:>7.4f}  {a[6]:>10.4f}")
for ayar in ("orta", "siki"):
    print(f"  {ayar:<5} durdurma siniflari :", olc(ayar)[7])
```

```
sinir    kod cagrisi  asan  durdurulan  bosa adim  butceyi asan  basarim  kod gorevi
gevsek A          9     0           0          0             3   0.8750      0.7778
gevsek B         10     0           0          0             1   1.0000      1.0000
orta   A         28    24          24       1356             4   0.7083      0.3333
orta   B         22    17          17        646             2   0.8333      0.5556
siki   A         28    24          24        552             4   0.7083      0.3333
siki   B         22    17          17        317             1   0.8333      0.5556
  orta  durdurma siniflari : {'adim': 20, 'hucre': 4}
  siki  durdurma siniflari : {'adim': 20, 'cikti': 4}
```

İkinci ve üçüncü sütun her satırda **eşittir**: bildirilen sınırı aşan çağrı sayısı ile durdurulan
çağrı sayısı aynıdır. Çağrı başına konan bir sınır, çağrı başına her ihlali yakalar; bu bir başarı
değil, tanımın gereğidir. Sayılmaya değer olan, sınırın **görmediği** yerdir.

Görmediği yer beşinci sütundadır. Gevşek ayarda hiçbir çağrı durdurulmaz, ama **üç görev** 96
adımlık bütçeyi geçer; çağrı başına 512 adıma izin veren bir sınır, 172 adımlık bir programın kaç
kez çalıştırıldığını saymaz. Orta ve sıkı ayarda durdurulan çağrılar tekrarlandığı için bütçeyi
aşan görev **dörde** çıkar. İkisi aynı şeyi söyler: **çağrı başına sınır görev toplamını görmez**.

Durdurmanın bedeli son sütundadır. Gevşek ayarda kod görevlerinde başarım **0,7778** ve **1,0000**,
orta ayarda **0,3333** ve **0,5556**. Düşüş iki koşumda da 0,4445'e yakındır ve gevşek ayardaki iki
koşum farkının (0,2222) iki katıdır, yani **ölçülmüştür**. Mekanizma dolaylıdır: durdurulan bir
çağrı görevi düşürmez, **adım bütçesini tüketir**. Yazım biçimi görev başına sabit olduğu için aynı
program yeniden yazılır ve bütçe biter; kod çağrısının 9'dan 28'e çıkması budur.

Orta ile sıkı satırlarını ayıran tek sütun boşa harcanan adımdır: **1356'ya karşı 552**. Durdurulan
çağrı ve başarım aynıyken sıkı sınır atılan işin beşte üçünü geri kazanıyor; kazanç isabette
değil kaynaktadır.

#### Özet

- Aynı tutarı veren üç program 15, 172 ve 19 adım harcar, 2 ile 5 hücre tutar, 1 ile 5 çıktı
  üretir; kaynak sınırı bu yüzden tekil değil çoğuldur.
- Her satırda aşan çağrı sayısı durdurulan çağrı sayısına eşittir: çağrı başına sınır, çağrı başına
  her ihlali yakalar. Görmediği yer görev toplamıdır: gevşek ayarda hiçbir çağrı durdurulmadan üç
  görev 96 adımlık bütçeyi aşar, sınırlı ayarlarda dört görev aşar.
- Durdurma görevi doğrudan düşürmez, adım bütçesini tüketerek düşürür: kod görevlerinde başarım
  0,7778 ve 1,0000'den 0,3333 ve 0,5556'ya iner, kod çağrısı 9'dan 28'e çıkar. Sıkı sınır orta
  sınırla aynı başarımı verir ve boşa harcanan adımı 1356'dan 552'ye indirir.

#### Sonraki Adım

Burada sınırlanan şey **tüketimdi**: bir çağrının kaç adım süreceği, kaç hücre tutacağı, kaç çıktı
üreteceği. Kaynak sınırı bir dördüncü soruya hiç dokunmaz: aracın **neye erişebildiğine**. Sanal
alandaki programın dışarısı yoktu; ajanın dokunduğu öbür yüzeyler böyle değildir. Bir dosya ağacı
ya da bir kabuk, yürütmeyi hiç uzatmadan geri alınamaz bir iş yapabilir. Sonraki ders aracın
**bildirdiği yetki yüzeyini** sayar.

### Dosya Sistemi ve Terminal Erişimi

Source: https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/dosya-sistemi-ve-terminal-erisimi

Ajanın dosya ve komut yüzeyinde yetki sınırlandırmasının sayılması: yirmi dört kurgu yüzey görevi üç yetki kümesinde koşturulur ve tamamlanan görev ile durdurulan çağrı yan yana basılır. Tam yetkide 0,9583 görev tamamlanıyor ve kapı hiçbir çağrıyı durdurmuyor; salt okuma yetkisi 71 çağrı durduruyor ama tamamlanan görevi 0,5000'e düşürüyor; izin listeli küme 20 çağrı durdurup 0,8333'te kalıyor ve durdurmalarının tamamı tek bir görev türünde toplanıyor. Salt okumada doğru yanıt 0,6250 iken tamamlanan görev 0,5000'dir: üç görevde yanıt doğru üretildi ama yazma hiç gerçekleşmedi. Durdurulan çağrı adımı da tüketir; adım 78'den 129'a çıkar. İki koşumda toplamlar aynı kalsa da görev düzeyinde iki görev yer değiştirir, bu yüzden ölçüm bandı 0,0833'tür ve tam yetki ile izin listesi arasındaki 0,1250 fark bandın üstündedir.

Bir önceki ders kod yürütme aracını bir kaynak sınırına bağladı ve sınırın kaç çağrıyı
durdurduğunu saydı. Orada durduran şey **miktardı**: bellek, süre, çıktı uzunluğu. Ajanın
dokunduğu yüzey ise bununla bitmiyor: aynı döngü dosya okur, dosya yazar ve işletim komutu
çalıştırır. Bu üç eylemde durduran şey miktar değil **yetkidir**.

Ders o yetkiyi tek kavramda toplar: **yetki yüzeyi (permission surface)**, bir aracın bildirdiği
yetenek ile dokunduğu yol ya da komuttur. En az yetki ilkesi Siber Güvenliğe Giriş kursunda
kuruldu, dosya izinleri ve sahiplik Linux'a Giriş kursunda ölçüldü; iki yordam da tekrarlanmaz.
Sayılan tek şey şudur: üç yetki kümesinde ajan kaç görevi bitiriyor, kapı kaç çağrıyı durduruyor
ve durdurma kime fatura ediliyor.

- **AB31.** Kod bir **benzeticidir**; gerçek bir dosya sistemi ya da çalışma zamanı çağrılmaz.
  Ortam, araçlar ve görevler **kurgudur**, tohum **20260218**.
- **AB32.** Ajan döngüsü kursun ortak tanımıdır: doğru aracı seçme payı derinlikle düşer, iş
  bitince ajan 0,88 payla durur.
- **AB33. Yetki yüzeyi aracın bildirdiği alandır:** her araç bir **yetenek** (okuma, yazma,
  çalıştırma) bildirir; dokunduğu yol ya da komut çağrının içinden okunur. Kurgu ağaç beş yol,
  kurgu terminal yüzeyi dört komut taşır.
- **AB34. Kapı yürütücünün önünde durur.** Geçmeyen çağrı araca ulaşmaz, döngüye araç hatası
  olarak döner ve adımı tüketir. Ölçülen üç küme **tam**, **salt okuma**, **izin listeli**.
- **AB35. Görev tamamlandı demek için iki koşul aranır:** yanıt doğru olmalı ve dizinin yan etkili
  adımı yürümüş olmalı. Yalnız yanıta bakmak yazmayan bir görevi bitmiş sayar.
- **AB36. Taban çizgisi tam yetki kümesidir** — kapının hiçbir şeyi durdurmadığı döngü. Bedel
  **durdurulan çağrı, adım ve düşen görev**.

#### Ortak Döngü ve Yüzeyin Bildirimi

Ortam, beş temel araç ve ajan döngüsü kurs boyunca aynıdır.

```python
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


# ---- KURGU ortam: olcum aginin kayitlari
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)


# ---- KURGU gorevler: her biri bilinen bir arac dizisiyle cozulur
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"])


# ---- KURGU ajan: her adimda bir arac secer ya da biter
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("ortak ortam:", len(ABONE), "abone ,", len(BELGE), "belge ,",
      len(ARAC), "arac ,", len(GOREV), "gorev")
```

```
ortak ortam: 60 abone , 5 belge , 5 arac , 24 gorev
```

#### Üç Yetki Kümesi ve Kapı

Yüzey üç araçla açılır: `dosya_oku`, `dosya_yaz`, `komut_calistir`; ortak kümedeki `kayit_yaz` da
yazma yeteneği bildirir. Kapı bu bildirimi yetki kümesiyle karşılaştırır ve **yalnız durdurur**;
kodun hiçbir yerinde sınırı aşan bir yol yoktur, koşan her satır denetim tarafındadır. Yirmi dört
kurgu yüzey görevi altı türe dağılır, üçü yan etki taşır.

```python
# ---- KURGU dosya agaci ve terminal yuzeyi (bu derse ozgu)
AGAC = {"veri/endeks.csv": 60, "veri/sonuc.csv": 0, "belge/tarife.txt": 5,
        "ayar/esik.cfg": 2, "gunluk/istek.log": 41}
KOMUT = {"endeks_dokum": 60, "gunluk_kuyruk": 12, "arsiv_temizle": 7, "sayac_sifirla": 0}
YOL_IZ = {"endek": "veri/endeks.csv", "sonuc": "veri/sonuc.csv", "tarif": "belge/tarife.txt",
          "esik": "ayar/esik.cfg", "gunlu": "gunluk/istek.log"}
KOMUT_IZ = {"dokum": "endeks_dokum", "kuyru": "gunluk_kuyruk",
            "temiz": "arsiv_temizle", "sifir": "sayac_sifirla"}


def yol_coz(a, kip):
    """Okuma ilk, yazma son eslesmeyi alir."""
    e = [YOL_IZ[t] for t in belirtecle(a) if t in YOL_IZ]
    return (e[0] if kip == "oku" else e[-1]) if e else "gunluk/istek.log"


def komut_coz(a):
    return next((KOMUT_IZ[t] for t in belirtecle(a) if t in KOMUT_IZ), None)


def t_dosya_oku(anahtar="", abone=None, **_):
    yol = yol_coz(anahtar, "oku")
    if yol == "veri/endeks.csv":
        a = ABONE.get(abone)
        return {"hata": "eksik alan"} if a is None else {
            "onceki": a["onceki"], "simdiki": a["simdiki"], "tip": a["tip"]}
    if yol == "belge/tarife.txt":
        return {"sonuc": "TR01"}
    return {"sonuc": USTU if yol == "ayar/esik.cfg" else AGAC[yol]}


def t_dosya_yaz(anahtar="", sonuc=None, **_):
    return {"hata": "eksik alan"} if sonuc is None else {"yazildi": yol_coz(anahtar, "yaz")}


def t_komut(anahtar="", **_):
    k = komut_coz(anahtar)
    return {"hata": "komut yok"} if k is None else {"sonuc": KOMUT[k]}


ARAC.update({"dosya_oku": {"islev": t_dosya_oku, "alan": ("anahtar", "abone"),
                           "yan_etki": False},
             "dosya_yaz": {"islev": t_dosya_yaz, "alan": ("anahtar", "sonuc"),
                           "yan_etki": True},
             "komut_calistir": {"islev": t_komut, "alan": ("anahtar",), "yan_etki": True}})
ARAC_ADI = list(ARAC)
YETENEK = {"dosya_oku": "oku", "dosya_yaz": "yaz", "kayit_yaz": "yaz",
           "komut_calistir": "calistir"}
YETKI = {"tam": {"oku": ("",), "yaz": ("",), "komut": None},
         "salt okuma": {"oku": ("",), "yaz": (), "komut": ()},
         "izin listeli": {"oku": ("veri/", "belge/", "ayar/"), "yaz": ("veri/sonuc",),
                          "komut": ("endeks_dokum", "gunluk_kuyruk")}}


def izin_ver(ad, durum, y):
    """Aracin bildirdigi yuzeyi verilen yetki kumesiyle karsilastirir."""
    g = YETENEK.get(ad)
    if g is None:
        return True
    if g == "calistir":
        return y["komut"] is None or komut_coz(durum.get("anahtar", "")) in y["komut"]
    if ad == "kayit_yaz":
        return bool(y[g])
    return any(yol_coz(durum.get("anahtar", ""), g).startswith(o) for o in y[g])


AKTIF, DUR = [YETKI["tam"]], {"sayi": 0}
_yurut = cagir_arac


def cagir_arac(ad, durum):
    """Kapi yurutucunun onundedir; gecmeyen cagri araca ulasmaz."""
    if izin_ver(ad, durum, AKTIF[0]):
        return _yurut(ad, durum)
    DUR["sayi"] += 1
    return {"hata": "yetki yok"}


# ---- KURGU yuzey gorevleri: 24 gorev, alti tur
TUR = [("endeks", "{a} abonesinin endeks satirini oku ve tuketimi hesapla",
        ("dosya_oku", "tuketim")),
       ("belge", "tarife basamaklarini belgeden oku", ("dosya_oku",)),
       ("esik", "ustu birim ucret esik degerini ayardan oku", ("dosya_oku",)),
       ("yazma", "{a} abonesinin endeks farkini hesapla ve sonuc dosyasina yaz",
        ("dosya_oku", "tuketim", "dosya_yaz")),
       ("dokum", "endeks dokumunu calistir", ("komut_calistir",)),
       ("temizlik", "arsiv temizligini calistir", ("komut_calistir",))]
YUZEY = []
for i in range(24):
    r = uretec(TOHUM + 6421 * i)
    ab = f"A{1 + int(r() * 60):03d}"
    ad, metin, dizi = TUR[ayrik(r(), [0.20, 0.12, 0.14, 0.18, 0.18, 0.18])]
    YUZEY.append({"ad": f"Y{i + 1:02d}", "tur": ad, "abone": ab,
                  "istek": metin.format(a=ab), "dizi": dizi})


def yuzey_dogru(g):
    a = ABONE[g["abone"]]
    return {"endeks": a["simdiki"] - a["onceki"], "yazma": a["simdiki"] - a["onceki"],
            "belge": "TR01", "esik": USTU, "dokum": KOMUT["endeks_dokum"],
            "temizlik": KOMUT["arsiv_temizle"]}[g["tur"]]


def kosum(yetki, tohum=TOHUM):
    """Gorev bitti demek icin yanit dogru olmali ve yan etkili adim yurumus olmali."""
    AKTIF[0] = YETKI[yetki]
    CAGRI[0] = DUR["sayi"] = 0
    c = {"dogru": [], "bitti": [], "adim": 0, "pay": {}}
    for g in YUZEY:
        onceki = DUR["sayi"]
        s = ajan(g, tohum=tohum)
        d = s["yanit"] == yuzey_dogru(g)
        c["dogru"].append(d)
        c["bitti"].append(d and all(any(x == t and not h for x, h in s["iz"])
                                    for t in g["dizi"] if ARAC[t]["yan_etki"]))
        c["adim"] += s["adim"]
        c["pay"][g["tur"]] = c["pay"].get(g["tur"], 0) + DUR["sayi"] - onceki
    c.update(cagri=CAGRI[0], dur=DUR["sayi"])
    return c


print("yuzey gorev turleri:", {t[0]: sum(g["tur"] == t[0] for g in YUZEY) for t in TUR})
print("yetki kumesi   dogru A  dogru B  tamamlanan  yurutulen  durdurulan  adim  oynak")
tablo = {}
for y in YETKI:
    a, b = kosum(y), kosum(y, TOHUM + 1)
    tablo[y] = a
    print(f"{y:<14} {sum(a['dogru']) / 24:>7.4f}  {sum(b['dogru']) / 24:>7.4f}"
          f"  {sum(a['bitti']) / 24:>10.4f}  {a['cagri']:>9}  {a['dur']:>10}"
          f"  {a['adim']:>4}  {sum(x != z for x, z in zip(a['bitti'], b['bitti'])):>5}")
print("\ndurdurulan cagrinin gorev turune dagilimi")
for y in ("salt okuma", "izin listeli"):
    print(f"  {y:<13}", {k: v for k, v in tablo[y]["pay"].items() if v})
```

```
yuzey gorev turleri: {'endeks': 6, 'belge': 3, 'esik': 3, 'yazma': 5, 'dokum': 4, 'temizlik': 3}
yetki kumesi   dogru A  dogru B  tamamlanan  yurutulen  durdurulan  adim  oynak
tam             0.9583   0.9583      0.9583         54           0    78      2
salt okuma      0.6250   0.6250      0.5000         46          71   129      0
izin listeli    0.8333   0.8333      0.8333         51          20    92      2

durdurulan cagrinin gorev turune dagilimi
  salt okuma    {'yazma': 22, 'temizlik': 19, 'dokum': 30}
  izin listeli  {'temizlik': 20}
```

#### Durdurmanın Kime Fatura Edildiği

Taban çizgisi ilk satırdır: kapı hiçbir çağrıyı durdurmuyor, görevlerin **0,9583**'ü bitiyor.
Yetkiyi okumaya indirmek durdurulanı **71**'e çıkarıyor ama tamamlananı **0,5000**'e düşürüyor;
izin listesi **20** çağrı durdurup **0,8333**'te kalıyor.

Asıl bilgi son iki satırdadır. Salt okumanın 71 durdurması üç türe yayılıyor: `yazma` 22,
`dokum` 30, `temizlik` 19. İzin listesinin 20 durdurmasının ise **tamamı** `temizlik` türündedir.
En az yetki ilkesinin ölçülmüş hâli budur: yetkiyi yeteneğe göre kesmek üç görev türünü birden
düşürürken, izin listesi maliyetin tamamını **kapatmak istediği** türe yıkıyor. Tam yetkiden farkı
yalnız **0,1250**, yani üç `temizlik` görevidir.

Bu farkın ölçülüp ölçülmediğini son sütun söyler. İki koşumun toplamları üç satırda da aynı, ama
toplamın sabit olması koşum değişkenliğinin sıfır olduğu anlamına gelmez: **oynak** sütunu tam
yetkide ve izin listesinde **ikişer** görevin sonucunu değiştirdiğini, birbirlerini götürdükleri
için toplamın oynamadığını söylüyor. Ölçüm bandı $2/24 = 0{,}0833$'tür; 0,1250 bandın üstündedir ve
**ölçülmüştür**, salt okumanın 0,4583'lük düşüşü fazlasıyla üstündedir.

Geriye iki okuma kalıyor. **Doğru yanıt ile tamamlanan görev salt okumada ayrışıyor**: 0,6250
doğru, 0,5000 tamamlanmış. Aradaki üç görevde ajan doğru sayıyı üretti ama **yazma çağrısı kapıda
durduruldu**; yalnız yanıta bakan bir ölçüt üçünü de bitmiş sayardı. Adım sütunu durdurmanın
ücretsiz olmadığını söylüyor: kapıdan dönen hata döngüye araç hatası gibi görünür, ajan adımı
yeniden dener. Salt okuma 78 yerine 129 adım, yani **%65 fazla** harcayıp daha az iş bitiriyor.

#### Özet

- Yetki yüzeyi, bir aracın bildirdiği yetenek ile dokunduğu yol ya da komuttur; kapı bu bildirimi
  yetki kümesiyle karşılaştırır ve yalnız durdurur.
- Tam yetkide 24 görevin 0,9583'ü bitiyor ve hiçbir çağrı durmuyor; salt okuma 71 çağrı durdurup
  tamamlananı 0,5000'e indiriyor; izin listesi 20 durdurup 0,8333'te kalıyor.
- İzin listesinin 20 durdurmasının tamamı tek görev türünde, salt okumanınki üç türe yayılıyor;
  en az yetki ilkesinin ölçülebilir farkı budur.
- Doğru yanıt tamamlanmış görev demek değildir: salt okumada üç görevde yanıt doğruydu ama yazma
  durduruldu, ölçüt 0,6250 yerine 0,5000 okur.
- Durdurma bedava değildir: salt okuma 129 adımla tam yetkinin 78 adımından %65 fazla harcar; iki
  koşumda ikişer görev yer değiştirdiği için ölçüm bandı 0,0833'tür.

#### Sonraki Adım

Bu derste ajanın dokunduğu her yüzey **bizimdi**: dosya ağacı da, komut kümesi de, kapı da ölçüm
ağının içindeydi. Bir çağrıyı durdurabilmek, kimin neye yetkili olduğunu bildiğimiz için mümkündü.
Ajan dışarıdan bilgi getirdiğinde bu zemin kayar: getirilen metin bizim değildir, bir izin kümesiyle
denetlenemez ve duruma girdikten sonra bütün adımlara taşınır. Sonraki ders dış bilgiyi bir **kaynak
sınıflandırması** olarak ele alır ve iki şeyi sayar: sınıf karışımı doğru yanıt payını nasıl
değiştiriyor ve yanlış bir kaynak kaç adım taşınıyor.

### Web Arama ve Gezinme

Source: https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/web-arama-ve-gezinme

Dış bilgiye erişimin bir kaynak sınıflandırması olarak sayılması: on sekiz kurgu kaynak birincil, ikincil ve denetimsiz diye sınıflanır ve yirmi dört dış bilgi görevi beş kurulumda koşturulur. Dış bilgisiz döngü 0,2083 doğru yanıt verir; yalnız birincil kaynak 0,7500'e çıkarır; ikincil sınıfı da karışıma katmak 0,3750'ye düşürür ve on görevi kirletir; üç sınıf birlikte kirli görevi on beşe çıkarır. Aynı havuzda seçimi sınıfa göre sıralamak doğru yanıtı 0,7500'e geri getirir, kirli görevi beşe indirir ve çağrı sayısını değiştirmez. Yanlış kod duruma girdikten sonra ortalama 3,40 ile 4,00 adım taşınır ve kirli görevlerin tamamında kayıt yazma adımına ulaşır. Ölçüm bandı 0,0833'tür, bu yüzden 0,3750'lik düşüş de 0,3750'lik toparlanma da ölçülmüştür.

Bir önceki ders ajanın dokunduğu yüzeyi bir yetki kapısına bağladı: hangi yolun okunacağı, hangi
komutun çalışacağı bizim bildirdiğimiz bir kümeydi. Dış bilgi bu zemini kaldırır. Ajan bir arama
aracıyla kayıt bulup bir gezinme aracıyla içeriğini okuduğunda gelen metin **bizim değildir**; ne
bir izin listesine sığar ne bir şemaya.

Dışarıdan gelen bilgiye tek bir soru sorulabilir: **bu kaydı kim tuttu.** Ders o soruyu bir
**kaynak sınıflandırmasına** çevirir ve iki şey sayar: karışım doğru yanıt payını nasıl oynatıyor
ve yanlış bir kayıt duruma girdikten sonra kaç adım taşınıyor.

- **AB37.** Kod bir **benzeticidir**; gerçek bir arama ya da gezinme aracı çağrılmaz. Kaynaklar,
  iddialar ve sorular **kurgudur**, tohum **20260218**.
- **AB38. Kaynaklar yalnız sınıfıyla anılır.** Hiçbir site, alan adı, kurum ya da ülke adı
  yazılmaz; bir kaynak kurgu kayıt numarası ve sınıfından ibarettir.
- **AB39. Üç sınıf vardır:** **birincil** (işletmenin kendi kaydı), **ikincil** (türetilmiş
  derleme), **denetimsiz** (açık katkı). Bildirilen güvenilirlik payları 0,95, 0,70 ve 0,30'dur ve
  kurgudur; ölçülen şey payın değeri değil karışımın sonucudur.
- **AB40.** On sekiz kurgu kaynağın her biri tek bir konuyu kapsar ve o konuda ya doğru kaydı taşır
  ya taşımaz; taşımayan kaynak **başka bir konunun kodunu** verir.
- **AB41.** Yirmi dört görev altı konuya dağılır; iki konu iç belge dağarcığında vardır, dördü
  **yalnız dışarıdadır**.
- **AB42. Görev dizisi üç adımdır:** ara, getir, kayda yaz. Yanlış kod yanıtta kalmaz, **yan etkili
  adıma da ulaşabilir**.
- **AB43. Taban çizgisi dış bilgisiz döngüdür** — ajan yalnız iç belge dağarcığına bakar. Bedel
  **araç çağrısı, adım, kirli görev** ve yanlış kodun **taşındığı adım** olarak yazılır.

#### Ortak Döngü

Ortam, beş temel araç ve ajan döngüsü kurs boyunca aynıdır.

```python
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


# ---- KURGU ortam: olcum aginin kayitlari
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)


# ---- KURGU gorevler: her biri bilinen bir arac dizisiyle cozulur
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"])


# ---- KURGU ajan: her adimda bir arac secer ya da biter
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("ortak ortam:", len(ABONE), "abone ,", len(BELGE), "belge ,",
      len(ARAC), "arac ,", len(GOREV), "gorev")
```

```
ortak ortam: 60 abone , 5 belge , 5 arac , 24 gorev
```

#### Kaynak Sınıfı, Karışım ve Seçim

Yüzeye iki araç eklenir: `web_ara` konuya uyan kayıtları getirir, `sayfa_getir` seçilen kaydın
taşıdığı kodu okur. Arama aracının verdiği tek tasarım kararı şudur: **karışımda hangi sınıflar var
ve aday listesinden hangisi seçilir.** Seçim bir kaydı doğrulamaz, yalnız sınıfına bakar.

```python
# ---- KURGU dis bilgi yuzeyi: kaynaklar sinifiyla anilir, hicbir yer adi yoktur
KOD = ("SY01", "TR01", "BR01", "BR02", "BR03", "BR04")
SORU = {"SY01": "sayac donusunde endeks farki nasil hesaplanir",
        "TR01": "tarife basamaklari hangi metrekup degerlerinde degisir",
        "BR01": "bolge dagitim katsayisi kac",
        "BR02": "sayac degisim araligi kac yil",
        "BR03": "itiraz suresi kac gun",
        "BR04": "gecikme orani nasil hesaplanir"}
KONU_IZ = {"endek": "SY01", "basam": "TR01", "katsa": "BR01",
           "degis": "BR02", "itira": "BR03", "gecik": "BR04"}
SINIF = ("birincil", "ikincil", "denetimsiz")
GUVEN = {"birincil": 0.95, "ikincil": 0.70, "denetimsiz": 0.30}
KAYNAK, GETIR = {}, []
for i in range(18):
    r = uretec(TOHUM + 2593 * i)
    s = SINIF[ayrik(r(), [0.28, 0.34, 0.38])]
    KAYNAK[f"K{i + 1:02d}"] = {"sinif": s, "kapsam": KOD[int(r() * 6)],
                               "dogru": r() < GUVEN[s]}
KARISIM, SIRALI = [set(SINIF)], [False]


def konu_coz(a):
    return next((KONU_IZ[t] for t in belirtecle(a) if t in KONU_IZ), None)


def t_web_ara(anahtar="", **_):
    kod = konu_coz(anahtar)
    aday = [k for k, v in KAYNAK.items()
            if v["kapsam"] == kod and v["sinif"] in KARISIM[0]]
    if not aday:
        return {"hata": "kaynak yok"}
    if SIRALI[0]:                       # sinif sirali secim: once birincil
        aday.sort(key=lambda k: (SINIF.index(KAYNAK[k]["sinif"]), k))
    return {"kaynak": aday[0], "bulunan": len(aday)}


def t_sayfa_getir(kaynak=None, **_):
    v = KAYNAK.get(kaynak)
    GETIR.append(None if v is None else v["dogru"])    # iz ile hizali kalir
    if v is None:
        return {"hata": "kaynak yok"}
    return {"sonuc": v["kapsam"] if v["dogru"]
            else KOD[(KOD.index(v["kapsam"]) + 3) % 6], "sinif": v["sinif"]}


ARAC.update({"web_ara": {"islev": t_web_ara, "alan": ("anahtar",), "yan_etki": False},
             "sayfa_getir": {"islev": t_sayfa_getir, "alan": ("kaynak",), "yan_etki": False}})
ARAC_ADI = list(ARAC)

DIS = []
for i in range(24):
    r = uretec(TOHUM + 4643 * i)
    kod = KOD[ayrik(r(), [0.16, 0.16, 0.18, 0.16, 0.16, 0.18])]
    DIS.append({"ad": f"W{i + 1:02d}", "kod": kod, "abone": f"A{1 + int(r() * 60):03d}",
                "istek": SORU[kod], "dizi": ("web_ara", "sayfa_getir", "kayit_yaz")})


def kosum(siniflar, sirali=False, ic=False, tohum=TOHUM):
    """ic=True: dis bilgi yok, ajan yalniz ic belge dagarcigina bakar."""
    KARISIM[0], SIRALI[0] = set(siniflar), sirali
    CAGRI[0] = 0
    dogru = adim = kirli = tasinan = kayit = 0
    for g in DIS:
        GETIR.clear()
        s = ajan(dict(g, dizi=("belge_ara", "kayit_yaz")) if ic else g, tohum=tohum)
        d = s["yanit"] == g["kod"]
        dogru, adim = dogru + d, adim + s["adim"]
        yer = [i for i, (a, _) in enumerate(s["iz"]) if a == "sayfa_getir"]
        yanlis = [yer[m] for m, ok in enumerate(GETIR) if ok is False]
        if yanlis and not d:
            kirli, tasinan = kirli + 1, tasinan + s["adim"] - yanlis[-1]
            kayit += any(a == "kayit_yaz" and not h for a, h in s["iz"])
    return dogru, kirli, tasinan / kirli if kirli else 0.0, kayit, CAGRI[0], adim


print("kaynak sinifi dagilimi:",
      {s: sum(v["sinif"] == s for v in KAYNAK.values()) for s in SINIF})
print("sinifin dogru bilgi tasiyan kaynagi:",
      {s: sum(v["sinif"] == s and v["dogru"] for v in KAYNAK.values()) for s in SINIF})
print("gorev konusu:", {k: sum(g["kod"] == k for g in DIS) for k in KOD})
print("\nkurulum                dogru A  dogru B  kirli gorev  tasinan adim"
      "  kirli kayit  arac cagrisi  adim")
for ad, sf, sr, ic in (("dis bilgi yok", (), False, True),
                       ("yalniz birincil", ("birincil",), False, False),
                       ("birincil + ikincil", SINIF[:2], False, False),
                       ("uc sinif", SINIF, False, False),
                       ("uc sinif , sinif sirali", SINIF, True, False)):
    a = kosum(sf, sr, ic)
    b = kosum(sf, sr, ic, TOHUM + 1)
    print(f"{ad:<22} {a[0] / 24:>8.4f} {b[0] / 24:>8.4f}  {a[1]:>11}"
          f"  {a[2]:>12.2f}  {a[3]:>11}  {a[4]:>12}  {a[5]:>4}")
```

```
kaynak sinifi dagilimi: {'birincil': 6, 'ikincil': 5, 'denetimsiz': 7}
sinifin dogru bilgi tasiyan kaynagi: {'birincil': 6, 'ikincil': 3, 'denetimsiz': 1}
gorev konusu: {'SY01': 3, 'TR01': 2, 'BR01': 5, 'BR02': 5, 'BR03': 4, 'BR04': 5}

kurulum                dogru A  dogru B  kirli gorev  tasinan adim  kirli kayit  arac cagrisi  adim
dis bilgi yok            0.2083   0.2083            0          0.00            0            91   110
yalniz birincil          0.7500   0.8333            0          0.00            0           121   139
birincil + ikincil       0.3750   0.4167           10          3.40           10           121   139
uc sinif                 0.3750   0.3750           15          3.60           15           103   126
uc sinif , sinif sirali   0.7500   0.7917            5          4.00            5           103   126
```

#### Karışımın Değiştirdiği Sayı

Taban satırı dış bilgisiz döngüdür: iç belge dağarcığı altı konunun ikisini kapsadığı için doğru
yanıt **0,2083**'te kalıyor. Birincil kaynakları açmak bunu **0,7500**'e çıkarıyor ve kirli görev
sıfır: birincil kaynakların altısı da doğru kaydı taşıyor. Bedel 91'den **121** çağrıya, 110'dan
**139** adıma çıkmaktır; birincil sınıf altı konunun dördünü kapsadığı için kalan ikisinde arama
`kaynak yok` döndürüyor ve ajan adımı yeniden deniyor.

Üçüncü satır kursun ikinci iddiasının örneğidir. Karışıma ikincil sınıfı katmak doğru yanıtı
**0,7500'den 0,3750'ye düşürüyor**. Mekanizma ikinci çıktı satırındadır: ikincil kaynakların beşinin
yalnız üçü doğru kaydı taşıyor ve seçim sınıfa bakmadığı için ikincil bir kayıt birincil olanın
önüne geçebiliyor. Kaynak eklemek burada **bilgi değil gürültü ekledi**.
Denetimsiz sınıfı da katmak doğru yanıtı 0,3750'de bırakıyor ama kirli görevi **10'dan 15'e**
çıkarıyor; buna karşılık çağrı 121'den **103**'e düşüyor, çünkü kapsama tamamlanınca `kaynak yok`
hatası kalmıyor. Daha ucuz ve daha kirli.

Son satır düzeltmenin nerede olduğunu gösteriyor. Havuz aynı, karışım aynı, çağrı **103** ve adım
**126** ile değişmedi; değişen tek şey aday listesinin **sınıfa göre sıralanmasıdır**. Doğru yanıt
**0,7500**'e dönüyor, kirli görev 15'ten **5**'e iniyor. Aynı isabeti yalnız birincil kaynakla da
almıştık, ama orada 121 çağrı ve 139 adım gerekiyordu. **Güvenilmez sınıfı atmak değil, sınıfları
sıralamak** hem daha doğru hem daha ucuz. Kalan beş kirli görev de sıralamanın sınırını yazıyor:
birincil kaynağı hiç olmayan iki konu, görev dağılımında tam **beş** göreve karşılık geliyor.

İki koşum sütunu hangi farkın gerçek olduğunu söyler: en büyük koşum farkı yalnız birincil
satırındaki 0,8333 ile 0,7500 arasındadır, band **0,0833**. Hem 0,3750'lik düşüş hem 0,3750'lik
toparlanma bandın çok üstündedir ve **ölçülmüştür**. Kirlenmenin ömrü kirli görev ve taşınan adım sütunlarındadır: yanlış kod
duruma girdikten sonra ortalama **3,40**, **3,60** ve **4,00** adım taşınıyor ve kirli görevlerin
**tamamı** kirli kayıt sütununda da görünüyor. Yanlış kaynak yanıtta durmuyor, `kayit_yaz` ile
kalıcı hâle geliyor.

#### Özet

- Dış bilgide denetlenebilen şey içeriğin doğruluğu değil kaydın sınıfıdır; kaynaklar birincil,
  ikincil ve denetimsiz diye sayılır ve hiçbiri adıyla anılmaz.
- Dış bilgisiz döngü 0,2083 doğru yanıt verir; yalnız birincil kaynak 0,7500'e çıkarır, bedeli
  91'den 121'e çıkan çağrıdır.
- Karışıma ikincil sınıfı katmak doğru yanıtı 0,3750'ye düşürür; kaynak eklemek bilgi değil gürültü
  ekleyebilir ve bu kursun ikinci iddiasının bir örneğidir.
- Üç sınıf birlikte kirli görevi 15'e çıkarır ama çağrıyı 103'e indirir; aynı havuzda seçimi sınıfa
  göre sıralamak doğru yanıtı 0,7500'e getirir, kirli görevi 5'e indirir, çağrıyı artırmaz.
- Yanlış kod ortalama 3,40 ile 4,00 adım taşınır ve kirli görevlerin tamamında kayıt yazma adımına
  ulaşır; ölçüm bandı 0,0833 olduğu için her iki 0,3750'lik oynama da ölçülmüştür.

#### Sonraki Adım

Bu derste yanlış bir kodun **taşındığı** adım sayıldı, ama taşıyan şeyin kendisi ölçülmedi. Ajanın
durumu her adımda büyüyor: gözlemler birikiyor, iz uzuyor ve bir sonraki çağrıya gönderilen her
turda artıyor. Bağlam penceresi Yapay Zekâ Mühendisliğine Giriş kursunda tanımlandı, konuşma
durumunun her istekte yeniden gönderilmesi Model API'leri ile Uygulama Geliştirme kursunda ölçüldü;
ikisi de tekrarlanmaz. Sonraki ders döngüye özgü olanı sorar: adım başına kaç belirteç taşınıyor,
pencereye kaç adım sığıyor ve pencere dolduğunda **hangi alan** düşüyor.

### Kısa Süreli Bellek

Source: https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/kisa-sureli-bellek

Ajan döngüsünün taşıdığı konuşma durumunun adım adım sayılması: tek bir görevde taşınan belirteç birinci adımda 79 iken yedinci adımda 255'e çıkar ve büyümenin büyük kısmı durumdan değil izden gelir (yedinci adımda durum 63, iz yükü 192 belirteç). Yirmi dört görev beş pencere değerinde koşturulur. Sınırsız pencerede doğru yanıt 0,9583, toplam 9316 belirteç, araç hatası 8. Pencere 200'de doğru yanıt 0,8750'ye iner, 150'de 0,8333'e, 120'de 0,7083'e; düşen alan sayısı 0'dan 85'e, araç hatası 8'den 50'ye çıkar. En çok düşen alanlar görevin kimlik alanlarıdır: abone 16, anahtar 16, sutun 15. Pencereyi daraltmak taşınan belirteci düşürmez, artırır: adım 97'den 112'ye çıktığı için toplam 9316'dan 12026'ya yükselir. Ölçüm bandı 0,0833'tür, bu yüzden 200 penceresindeki 0,0833'lük düşüş bulgu sayılmaz.

Bir önceki ders yanlış bir kaydın duruma girdikten sonra ortalama üç ile dört adım taşındığını
saydı. Taşıyan şeyin adı **kısa süreli bellektir** (short-term memory): ajanın o görev boyunca elinde tuttuğu alanlar
ve bu alanları üreten adımların izi. Döngü uzadıkça bu yük büyür ve bir yerde bir sınıra çarpar.

Sınırın adı Yapay Zekâ Mühendisliğine Giriş kursunda kondu; konuşma durumunun her istekte yeniden
gönderildiği ve bunun tur başına ne tuttuğu Model API'leri ile Uygulama Geliştirme kursunda
ölçüldü. Erişim hattında da aynı sınır sayılmıştı: Gömme Vektörleri ve Erişim Artırılmış Üretim
kursunda pencereye ortalama 11,3 parça sığıyordu. Üç yordam da tekrarlanmaz. Bu dersin sorduğu
tek şey döngüye özgüdür: **adım başına kaç belirteç taşınıyor, pencereye kaç adım sığıyor ve
pencere dolduğunda durumdan hangi alan düşüyor.**

- **AB44.** Kod bir **benzeticidir**; ortam, görevler ve belirteçleyici **kurgudur**, tohum
  **20260218**. Pencere değerleri de kurgudur, ölçülen şey eşiğin değeri değil daralmanın etkisidir.
- **AB45. Taşınan yük iki parçadır:** **durum yükü**, ajanın elindeki alanların belirteci;
  **adım izinin yükü**, o ana kadarki adımların döngüde tuttuğu yer. İkincisi ortak döngünün kendi
  sayımıdır ve adım başına 22 belirteç büyür; metinde kısaca **iz yükü** denir.
- **AB46. Pencere durum ile izin toplamına konur.** Toplam eşiği aşarsa duruma **ilk giren alan**
  düşer ve düşme sürer; iz yükü düşürülemez.
- **AB47. Düşen alan sessizdir.** Ajan bir alanın gittiğini görmez; eksikliği ancak o alanı isteyen
  araç `eksik alan` döndürdüğünde ortaya çıkar.
- **AB48. Sığan adım**, ilk düşme gerçekleşene kadar yapılan çağrı sayısıdır; hiç düşme olmayan
  görevde bütün çağrılar sayılır.
- **AB49. Taban çizgisi sınırsız penceredir** — sınırın hiç bağlamadığı döngü. Bedel **toplam
  belirteç, adım, araç hatası ve düşen alan** olarak yazılır.
- **AB50.** Görev kümesi kursun ortak yirmi dört görevidir; ölçüt yine son durumdaki yanıttır.

#### Ortak Döngü

Ortam, beş temel araç ve ajan döngüsü kurs boyunca aynıdır.

```python
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


# ---- KURGU ortam: olcum aginin kayitlari
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)


# ---- KURGU gorevler: her biri bilinen bir arac dizisiyle cozulur
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"])


# ---- KURGU ajan: her adimda bir arac secer ya da biter
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("ortak ortam:", len(ABONE), "abone ,", len(BELGE), "belge ,",
      len(ARAC), "arac ,", len(GOREV), "gorev")
```

```
ortak ortam: 60 abone , 5 belge , 5 arac , 24 gorev
```

#### Adım Başına Taşınan Belirteç

Kısa süreli belleğin ölçüsü tek bir sayı değil, bir **eğridir**: her adımda taşınan yük bir önceki
adımdan büyüktür. İlk blok bunu tek bir görevde adım adım basar; ikincisi aynı büyümeyi beş pencere
değerinde yirmi dört göreve yayar.

```python
# ---- Kisa sureli bellek: durum + iz, bir pencereye sigmak zorundadir
BELLEK = {"pencere": 10 ** 9, "cagri": 0, "bel": 0, "dusen": {},
          "sigan": -1, "profil": None}
_yurut = cagir_arac


def durum_belirteci(d):
    return sum(len(belirtecle(f"{k} : {v}")) for k, v in d.items())


def cagir_arac(ad, durum):
    """Pencere asilirsa duruma ilk giren alan duser; iz yuku dusurulemez."""
    b = BELLEK
    b["cagri"] += 1
    iz_yuk = 60 + 22 * (b["cagri"] - 1)
    while durum_belirteci(durum) + iz_yuk > b["pencere"] and len(durum) > 1:
        k = next(iter(durum))
        b["dusen"][k] = b["dusen"].get(k, 0) + 1
        if b["sigan"] < 0:
            b["sigan"] = b["cagri"] - 1
        del durum[k]
    d = durum_belirteci(durum)
    b["bel"] += d + iz_yuk
    if b["profil"] is not None:
        b["profil"].append((b["cagri"], ad, len(durum), d, iz_yuk))
    return _yurut(ad, durum)


def kosum(pencere, tohum=TOHUM):
    BELLEK.update(pencere=pencere, bel=0, dusen={})
    dogru = adim = hata = cagri = sigan = 0
    for g in GOREV:
        BELLEK.update(cagri=0, sigan=-1)
        s = ajan(g, tohum=tohum)
        dogru += s["yanit"] == dogru_yanit(g)
        adim, hata = adim + s["adim"], hata + s["hata"]
        cagri += BELLEK["cagri"]
        sigan += BELLEK["cagri"] if BELLEK["sigan"] < 0 else BELLEK["sigan"]
    return (dogru, hata, adim, BELLEK["bel"], BELLEK["bel"] / cagri, sigan / 24,
            sum(BELLEK["dusen"].values()), BELLEK["dusen"])


gk = next(g for g in GOREV if g["tur"] == "kayit")
BELLEK.update(pencere=10 ** 9, cagri=0, bel=0, dusen={}, sigan=-1, profil=[])
ajan(gk, tohum=TOHUM)
print(f"tek gorev ({gk['tur']}) , sinirsiz pencere: adim basina tasinan belirtec")
print("adim  arac         alan  durum  iz yuku  toplam")
for c, ad, n, d, y in BELLEK["profil"]:
    print(f"{c:>4}  {ad:<12} {n:>4}  {d:>5}  {y:>7}  {d + y:>6}")
BELLEK["profil"] = None

print("\npencere   dogru A  dogru B  adim  toplam bel  bel/cagri  sigan  hata A"
      "  hata B  dusen")
for pn in (10 ** 9, 260, 200, 150, 120):
    a, b = kosum(pn), kosum(pn, TOHUM + 1)
    print(f"{'sinirsiz' if pn > 10 ** 6 else pn:<9} {a[0] / 24:>7.4f}  {b[0] / 24:>7.4f}"
          f"  {a[2]:>4}  {a[3]:>10}  {a[4]:>9.1f}  {a[5]:>5.2f}  {a[1]:>6}  {b[1]:>6}"
          f"  {a[6]:>5}")
son = sorted(kosum(120)[7].items(), key=lambda x: (-x[1], x[0]))
print("\npencere 120 , en cok dusen alan:", son[:6])
```

```
tek gorev (kayit) , sinirsiz pencere: adim basina tasinan belirtec
adim  arac         alan  durum  iz yuku  toplam
   1  sayac_oku       3     19       60      79
   2  belge_ara       7     34       82     116
   3  belge_ara      10     54      104     158
   4  belge_ara      10     54      126     180
   5  tuketim        10     54      148     202
   6  tarife         11     57      170     227
   7  kayit_yaz      13     63      192     255

pencere   dogru A  dogru B  adim  toplam bel  bel/cagri  sigan  hata A  hata B  dusen
sinirsiz   0.9583   0.9583    97        9316      125.9   3.08       8       7      0
260        0.9583   0.9583    97        9297      125.6   3.04       9       7      3
200        0.8750   0.9167    98        9681      125.7   2.75      15      10     34
150        0.8333   0.7500   100        9926      124.1   2.42      28      35     40
120        0.7083   0.6250   112       12026      126.6   1.83      50      58     85

pencere 120 , en cok dusen alan: [('abone', 16), ('anahtar', 16), ('sutun', 15), ('onceki', 10), ('simdiki', 10), ('bolge', 5)]
```

#### Pencere Dolduğunda Düşen Alan

İlk çıktı büyümenin nereden geldiğini söyler. Durum yükü birinci adımda **19**, yedinci adımda
**63** belirteçtir; alan sayısı üçten on üçe çıkar. İz yükü ise **60**'tan **192**'ye gider ve
yedinci adımda durumun **üç katıdır**. Toplam 79'dan **255**'e çıkıyor. Büyümenin büyük kısmı
ajanın topladığı bilgiden değil, **kaç adım attığından** gelir. Bu ayrım dersin geri kalanını
belirler, çünkü pencere daraldığında düşürülebilen tek parça durumdur.

İkinci tablo sınırı bağlar. Sınırsız pencerede yirmi dört görevin **0,9583**'ü doğru, toplam
**9316** belirteç taşınıyor, hiçbir alan düşmüyor ve araç hatası **8**. Pencere 260'a indiğinde üç
alan düşüyor ve başarım kıpırdamıyor. 200'de düşen alan **34**'e, araç hatası **15**'e çıkıyor
ve doğru yanıt **0,8750**'ye iniyor. 150'de düşen 40, hata 28, doğru yanıt 0,8333. 120'de düşen
**85**, hata **50**, doğru yanıt **0,7083**. Sığan çağrı sütunu daralmayı doğrudan okur:
**3,08**'den **1,83**'e, yani en dar pencerede ortalama iki çağrı bile tamamlanmadan bir alan
gidiyor.

Hangi alanın gittiği son satırdadır ve düzenin kendisinden gelir. En çok düşenler `abone` **16**,
`anahtar` **16**, `sutun` **15** ile duruma **ilk giren** alanlardır; yani görevin kimlik bilgisi.
Ardından `onceki` ve `simdiki` onar kez düşüyor. Sonucu bunlar açıklar: `abone` gidince `sayac_oku`
abonesiz çağrılıyor, `onceki` gidince `tuketim` eksik alan döndürüyor. Araç hatasının 8'den 50'ye
çıkması tek bir mekanizmanın sayımıdır ve iki koşumda da aynı yönde ilerliyor (7, 7, 10, 35, 58).

Başarım sütunu daha dikkatli okunmalı. En büyük iki koşum farkı 0,8333 ile 0,7500 arasındadır, band
**0,0833**. Sınırsız pencere ile 200 arasındaki düşüş de **tam 0,0833**'tür: bandın altında değil
ama üstünde de değil, bu yüzden **bulgu sayılmaz**; mekanizma düşen alan ve araç hatası
sütunlarında görünür, görev başarımına etkisi ölçülmemiştir. 150'deki 0,1250 ve 120'deki 0,2500
bandın üstündedir ve **ölçülmüştür**.

Adım ile toplam belirteç sütunları dersin en ters sonucudur. Pencereyi daraltmak taşınan
belirteci **düşürmez**: çağrı
başına yük 125,9'dan 126,6'ya, yani hiç değişmiyor. Toplam ise **9316'dan 12026'ya**, %29 artıyor,
çünkü düşen alanlar araç hatası üretiyor ve döngü aynı işi bitirmek için 97 yerine **112** adım
atıyor. Kısıtın kendisi bir tasarruf düzeneği değildir: küçülttüğü parça durum yüküdür ve o zaten
yükün küçük kısmıdır; büyüyen parça olan iz yükü kısıttan hiç etkilenmez.

#### Özet

- Adım başına taşınan yük iki parçadır: durum yükü tek görevde 19'dan 63 belirtece çıkarken, iz
  yükü 60'tan 192'ye çıkar ve yedinci adımda durumun üç katıdır.
- Sınırsız pencerede doğru yanıt 0,9583, toplam 9316 belirteç, düşen alan 0, araç hatası 8.
- Pencere daraldıkça düşen alan 0'dan 85'e, araç hatası 8'den 50'ye çıkar ve sığan çağrı 3,08'den
  1,83'e iner.
- Düşen alanlar duruma ilk giren kimlik alanlarıdır (abone 16, anahtar 16, sutun 15); düşüş
  sessizdir ve ancak o alanı isteyen aracın hatasında görünür.
- Pencereyi daraltmak taşınan belirteci düşürmez: çağrı başına yük 125,9'dan 126,6'ya sabit
  kalırken adım 97'den 112'ye çıktığı için toplam 9316'dan 12026'ya yükselir.
- Ölçüm bandı 0,0833'tür; 200 penceresindeki 0,0833'lük düşüş bulgu sayılmaz, 150 ve 120'deki
  0,1250 ile 0,2500 ölçülmüştür.

#### Sonraki Adım

Bu derste bellek, tek bir görevin ömrü kadar yaşadı. Pencere doldu, alan düştü, araç hata verdi ve
döngü bitti; **bittiği anda geriye hiçbir şey kalmadı**. Bir sonraki görev aynı aboneyi, aynı belge
kodunu ve aynı tarife basamağını sıfırdan öğrenmek zorunda. Kısa süreli belleğin tuttuğu şey
görevin çalışma kümesidir ve o küme görev bitince atılır. Sonraki ders kalıcı olanı sorar: hangi
bilginin döngü dışına **yazılacağı**, yazma kararının kaç kayıt biriktirdiği ve bu kayıtların
sonraki görevlerde kaç adım kazandırdığı.

### Uzun Süreli Bellek

Source: https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/uzun-sureli-bellek

Üç yazma politikasının aynı 48 görevlik ikinci kümede ölçülmesi: her adımın gözlemini ayrı kayıt yapmak 74 kayıt biriktiriyor ve 44 adım kazandırıyor, görev başına tek yanıt kaydı yazmak 24 kayıtla 199 adım kazandırıyor ama başarımı 0,9167'den 0,5833'e düşürüyor, plana giren gözlemleri tek kayıtta birleştiren seçici politika ise yine 24 kayıtla 118 adım kazandırıyor. Kazandıran şey kayıt sayısı değil kaydın birimidir.

Kısa süreli bellek pencereyle sınırlıdır, ama asıl kayıp sınırda değil: **döngü bittiği anda o
bağlamın tamamı silinir.** Ajan sayacı okur, endeks farkını hesaplar, tarifeyi uygular ve yanıtı
verir. Aynı abone başka bir soruyla döndüğünde sayaç **yeniden** okunur.

Uzun süreli bellek (long-term memory) bu boşluğu doldurur: döngüden bağımsız yaşayan, sonraki
görevlerin okuyabildiği bir kayıt kümesi. Buradaki soru **neyin geri getirileceği** değildir — parçalama, sıralama ve
erişim isabeti Gömme Vektörleri ve Erişim Artırılmış Üretim kursunda ölçüldü, tekrarlanmaz.
Buradaki soru **neyin yazılacağıdır**: aynı geri çağırma düzeneğiyle üç yazma politikası
koşturulur, her birinde saklanan kayıt sayısı ile sonraki görevlerde kazanılan adım yan yana
basılır.

- **AB61.** Kod bir **benzeticidir**; gerçek bir model uç noktası, araç sunucusu ya da bellek
  servisi çağrılmaz. Ortam, araçlar ve görevler **kurgudur**, tohum **20260218**.
- **AB62.** Bellek bir **kayıt listesidir**; her kayıt aranabilir bir metin ve alanlarıdır.
  Gözlem ve yanıt kayıtları **abone koduyla**, belge kayıtları **soru metniyle** anahtarlanır.
- **AB63. Geri çağırma tek okumadır:** istekle ortak belirteci en çok olan kayıt döner, eşitlikte
  en son yazılan.
- **AB64.** Üç yazma politikası: **her adım** (her adımın gözlemi ayrı kayıt), **yalnız sonuç**
  (görev başına tek yanıt kaydı), **seçici** (planın adımlarına giren gözlemler **tek kayıtta**
  birleşir).
- **AB65.** Geri çağrılan alanlar başlangıç durumuna yazılır, çıktısı bilinen adım **atlanır**;
  plan tümüyle bellekten karşılanırsa döngü hiç başlamaz.
- **AB66.** Kayıtlar **24 görevlik ilk kümeden** yazılır; ölçüm **aynı abonelere sorulan 48 yeni
  soruda** yapılır. Adım bütçesi 8, taban çizgisi **belleksiz döngüdür**.

#### Döngü Bittiğinde Geriye Kalan

Aşağıdaki blok ortak tanımı kurar ve tek bir aboneye iki ayrı soru sorar.

```python
# KURGU ortam ve KURGU ajan. Gercek bir model ucu, arac sunucusu ya da bellek
# servisi cagrilmaz. Abone kayitlari, belgeler ve gorevler kurgudur, tohum 20260218.
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)}


CAGRI[0] = 0
g1 = GOREV[4]
g2 = dict(g1, ad="G05b", tur="tuketim", dizi=("sayac_oku", "tuketim"),
          istek=f"{g1['abone']} abonesinin bu donem tuketimi kac metrekup")
print("gorev kumesi:", {t: sum(g["tur"] == t for g in GOREV)
                        for t in ("bedel", "tuketim", "belge", "kayit")})
print(g1["abone"], "iki ayri soru:", [x[0] for x in ajan(g1)["iz"]],
      [x[0] for x in ajan(g2)["iz"]], "; arac cagrisi", CAGRI[0])
```

```
gorev kumesi: {'bedel': 6, 'tuketim': 8, 'belge': 7, 'kayit': 3}
A026 iki ayri soru: ['sayac_oku', 'tuketim', 'tarife'] ['sayac_oku', 'tuketim'] ; arac cagrisi 5
```

Aynı abonenin sayacı iki kez okundu. İkinci sorunun ilk iki adımı birincinin birebir tekrarıdır;
beş araç çağrısının ikisi yeni bilgi getirmiyor. Kayıp döngünün kusuru değil, **kalıcı bir yazma
yerinin olmamasıdır**.

#### Üç Yazma Politikası

Ajan döngüsünün tek farkı, başlangıç durumunun bellekten gelmesi ve çıktısı bilinen adımın
atlanmasıdır.

```python
PLAN = {"bedel": (("sayac_oku", "tuketim", "tarife"),
                  "{a} abonesinin bu donem bedeli nedir"),
        "tuketim": (("sayac_oku", "tuketim"),
                    "{a} abonesinin bu donem tuketimi kac metrekup"),
        "belge": (("belge_ara",), "sayac donusunde endeks farki nasil hesaplanir"),
        "kayit": (("sayac_oku", "tuketim", "tarife", "kayit_yaz"),
                  "{a} abonesinin bedelini hesapla ve kayda yaz")}
TUR = ("bedel", "tuketim", "belge", "kayit")
CIKTI = {"sayac_oku": ("onceki", "simdiki", "tip", "bolge"), "tuketim": ("m3",),
         "tarife": ("tutar",), "belge_ara": ("kod",)}
SONUC = {"tuketim": "m3", "tarife": "tutar", "belge_ara": "kod"}


def sonraki_kume(n=48):
    """Ayni abonelere sorulan YENI sorular; recete GOREV ile birebir ayni."""
    k = []
    for i in range(n):
        r = uretec(TOHUM + 7919 * (i + 101))
        ab = GOREV[int(r() * 24)]["abone"]
        t = TUR[ayrik(r(), [0.30, 0.22, 0.20, 0.28])]
        k.append({"ad": f"H{i + 1:02d}", "tur": t, "abone": ab,
                  "dizi": PLAN[t][0], "istek": PLAN[t][1].format(a=ab)})
    return k


def geri_cagir(bellek, istek):
    """Tek okuma: ortak belirteci en cok olan, esitlikte en son yazilan kayit."""
    b, en, kayit = set(belirtecle(istek)), 0, None
    for metin, alanlar in bellek:
        o = len(b & set(belirtecle(metin)))
        if o >= en and o > 0:
            en, kayit = o, alanlar
    return kayit


def ajan_b(g, bellek, tohum=TOHUM, en_cok_adim=8):
    """Ortak dongunun tek farki: baslangic durumu bellekten gelir ve
    ciktisi zaten bilinen adim atlanir."""
    r = uretec(tohum ^ izno(g["ad"]))
    kayit = geri_cagir(bellek, g["istek"])
    c = {"adim": 0, "okundu": kayit is not None, "alan": len(kayit or {}),
         "kayit": [], "yolda": {}}
    alan = {"abone": g["abone"], "anahtar": g["istek"], "sutun": "bedel"}
    alan.update(kayit or {})
    if "sonuc" in alan:
        return dict(c, yanit=alan["sonuc"])
    adim, k, dizi = 0, 0, g["dizi"]
    while k < len(dizi) and dizi[k] in CIKTI and all(x in alan for x in CIKTI[dizi[k]]):
        alan["sonuc"] = alan.get(SONUC.get(dizi[k]), alan.get("sonuc"))
        k += 1
    if k == len(dizi):
        return dict(c, yanit=alan.get("sonuc"))
    while adim < en_cok_adim:
        adim += 1
        hedef = dizi[k] if k < len(dizi) else None
        if hedef is None:
            if r() < 0.88:
                break
            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 sec == "kayit_yaz":
            alan["deger"] = alan.get("sonuc")
        s = cagir_arac(sec, alan)
        gozlem = {x: s[x] for x in CIKTI.get(sec, ()) if x in s}
        c["kayit"].append(gozlem)
        if "hata" in s:
            continue
        alan.update({kk: vv for kk, vv in s.items() if kk != "hata"})
        if sec in SONUC:
            alan["sonuc"] = s[SONUC[sec]]
        if sec == hedef:
            c["yolda"].update(gozlem)
            k += 1
    return dict(c, adim=adim, yanit=alan.get("sonuc"))


def kosum(politika, tohum=TOHUM, n=48):
    """Once ilk kume kosulur ve politikaya gore yazilir, sonra yeni kume."""
    bellek = []
    for g in GOREV:
        s = ajan_b(g, [], tohum)
        ah = g["istek"] if g["tur"] == "belge" else g["abone"]
        yaz = (s["kayit"] if politika == "her adim" else [s["yolda"]]
               if politika == "secici" else [{"sonuc": s["yanit"]}]
               if politika == "yalniz sonuc" else [])
        bellek += [(ah + " " + " ".join(f"{x} {v}" for x, v in a.items()), a)
                   for a in yaz if None not in a.values()]
    CAGRI[0] = 0
    adim = dogru = okuma = isabet = alan = 0
    for g in sonraki_kume(n):
        s = ajan_b(g, bellek, tohum)
        d = s["yanit"] == dogru_yanit(g)
        adim, dogru, okuma = adim + s["adim"], dogru + d, okuma + s["okundu"]
        alan, isabet = alan + s["alan"], isabet + (d and s["okundu"])
    return (len(bellek), okuma, alan, isabet / max(okuma, 1), adim, CAGRI[0], dogru / n)


POL = ("belleksiz", "her adim", "yalniz sonuc", "secici")
print("politika       kayit okuma alan  isabet  adim cagri basarim | 2. kosum")
for p in POL:
    a, b = kosum(p), kosum(p, TOHUM + 1)
    print(f"{p:<14} {a[0]:>5} {a[1]:>5} {a[2]:>4} {a[3]:>7.4f} {a[4]:>5} {a[5]:>5}"
          f"  {a[6]:>6.4f} | {b[4]:>4} adim  {b[6]:.4f}")
```

```
politika       kayit okuma alan  isabet  adim cagri basarim | 2. kosum
belleksiz          0     0    0  0.0000   212   166  0.9167 |  222 adim  0.8958
her adim          74    47   99  0.8936   168   132  0.8958 |  176 adim  0.8542
yalniz sonuc      24    45   45  0.5556    13    10  0.5833 |   14 adim  0.6250
secici            24    47  166  0.8511    94    74  0.8542 |   96 adim  0.8958
```

Taban çizgisi 48 görevi 212 adım ve 166 araç çağrısıyla bitiriyor, başarım 0,9167. Üç politika bu
satırın üstüne kurulur.

**Yalnız sonuç** en çarpıcı satırdır. 24 kayıtla 212 adımı **13'e** indiriyor, yani 199 adım
kazandırıyor; araç çağrısı 166'dan 10'a düşüyor. Ama başarım **0,5833'e** iniyor. Nedeni şudur:
kayıt, abone koduyla anahtarlanmış bir **yanıttır**, oysa yanıt bir soruya aittir. Abonenin tüketim
yanıtı saklanır, sonraki görev aynı abonenin **bedelini** sorar; değer doğru, soru başkadır.

**Her adım** politikası 74 kayıt biriktiriyor, üç katı ve yalnızca 44 adım kazandırıyor (212'den
168'e). Sebep okuma sütununda görünür: 47 okumanın getirdiği toplam alan **99**, okuma başına iki
alan. Her adımın gözlemi ayrı bir kayıt olduğu için tek okuma **tek adımın** çıktısını getiriyor.
**Seçici** politika aynı 24 kayıtla 118 adım kazandırıyor (212'den 94'e) ve 47 okumada **166 alan**
getiriyor; plana giren gözlemler tek kayıtta birleştiği için bir okuma bütün ön koşulları taşıyor.

#### Kayıt Sayısı Değil Kaydın Birimi

Tablonun iki sıralaması çakışmıyor. Kayıt sayısına göre en cömert politika her adımdır, kazanılan
adıma göre yalnız sonuçtur; seçici ise her adımın üçte biri kadar kayıtla onun **2,68 katı** adım
kazandırıyor. Belirleyici olan kaç kayıt yazıldığı değil, bir kaydın **hangi birimi**
kapsadığıdır: bir adım mı, bir görev mi, bir varlığın bütün alanları mı.

Hangi farkın bulgu sayılacağını ikinci koşum söylüyor. Taban çizgisi 0,9167 ve 0,8958 veriyor, iki
koşum farkı **0,0417**. Yalnız sonucun kaybı iki koşumda 0,3334 ve 0,2708, ikisi de bandın çok
üstünde ve **ölçülmüştür**. Seçicinin kaybı ise bir koşumda 0,0625, ötekinde **0,0000**; bandın
etrafında salınıyor ve **ölçülmemiş sayılır** — 118 adımlık kazancın bir başarım bedeli olduğu bu
kurulumda gösterilemedi.

Kural sadedir: belleğe **soruya bağlı bir yanıt** yazmak, o yanıtı soru değiştiğinde yanlış hâle
getirir. Yazılması gereken şey sorudan bağımsız **gözlemdir** — sayaç okuması, abone tipi, belge
kodu. Yanıtın her görevde yeniden üretilmesi, yanlış yanıttan ucuzdur.

#### Özet

- Döngü bittiğinde kısa süreli bellek silinir; aynı abonenin sayacı sonraki soruda yeniden okunur.
- Ölçülen şey erişim değil **yazma kararıdır**: aynı geri çağırma düzeneğiyle üç politika üç ayrı
  sonuç veriyor.
- Yalnız sonuç 24 kayıtla 199 adım kazandırıyor ama başarımı 0,9167'den 0,5833'e düşürüyor; kayıp
  iki koşum farkının sekiz katıdır ve ölçülmüştür.
- Her adım 74 kayıtla 44, seçici 24 kayıtla 118 adım kazandırıyor; okuma başına gelen alan 99'a
  karşı 166'dır.
- Kayıt sayısı değil kaydın birimi belirleyicidir; soruya bağlı yanıt yerine gözlem yazmak yanlış
  yanıtı ortadan kaldırır.

#### Sonraki Adım

Bu ders belleği görevler **arasında** ele aldı. Görev **içinde** sorun tersine döner: bağlam
silinmiyor, büyüyor ve pencere dolduğunda bir şeyin atılması gerekiyor. Atma kararı yazma
kararının aynasıdır. Sonraki ders sıkıştırmanın iki yolunu ölçer: gözlemleri özetlemek ve yalnız
ilgili olanı tutmak. Sıkıştırma oranı arttıkça taşınan belirteç düşer, ama düşen her alan bir
sonraki adımın girdisi olabilir. Ölçülecek olan bu ikisidir: kazanılan belirteç ve **atılan alanın
gerektiği görev sayısı**.

### Bağlam Sıkıştırma

Source: https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/baglam-sikistirma

Aynı 24 görevde iki sıkıştırma politikasının süpürülmesi: taşınan durum da ödendiğinde taban 21913 belirteç ve 0,9583 başarım veriyor. Seçici hatırlama tutulan alanı üçe indirdiğinde belirteç 16569'a düşüyor ve başarım hiç değişmiyor, ama atılan alanın gerektiği görev sayısı 0'dan 8'e çıkıyor. Özetleme aynı bütçede 0,7917'ye iniyor ve en sıkı ayarda belirteci 33566'ya, yani tabanın 1,53 katına çıkarıyor. Sıkıştırmanın işi kötüleştirdiği nokta budur.

Önceki ders belleği görevler arasında ele aldı: neyin yazıldığı sonraki görevlerde kazanılan adımı
belirliyordu. Görev içinde sorun tersine döner. Bağlam silinmez, **büyür**: her gözlem duruma
eklenir ve sonraki her adımda yeniden taşınır. Ortak döngünün belirteç sayımı bunun yalnız bir
yarısını görüyor — adım izini sayıyor, taşınan durumun kendisini saymıyor.

Bu ders önce o eksik kalemi ekler, sonra **bağlam sıkıştırmanın (context compaction)** iki
politikasını süpürür: **özetleme**, en eski alanları düşürüp yerlerine tek bir özet girdisi koyar;
**seçici hatırlama (selective recall)**, kalan adımların bildirdiği alanları tutar. Ölçülecek olan
iki sayının yan yana durmasıdır: sıkıştırma oranı arttıkça taşınan belirteç düşer, ama **atılan
alanın gerektiği görev sayısı** büyür. Bu ders kursun ikinci iddiasının bir örneğidir; en sıkı
ayarda sıkıştırma belirteci tabanın **üstüne** çıkarıyor.

- **AB67.** Kod bir **benzeticidir**; gerçek bir model uç noktası ya da araç sunucusu çağrılmaz.
  Ortam, araçlar ve görevler **kurgudur**, tohum **20260218**.
- **AB68.** Taşınan durum bir belirteç kalemidir: her alan, adı ve biçimi için 12 belirteç, değeri
  için kendi belirteç sayısı kadar ödenir ve bu bedel **her adımda yeniden** ödenir.
- **AB69. Sıkıştırma oranı `tut` ayarıyla verilir:** durumda en çok `tut` alan taşınır, fazlası
  düşürülür. Sıkıştırma her adımın sonunda uygulanır.
- **AB70. Özetleme** en eski alanları düşürür ve yerlerine tek bir özet girdisi koyar; girdi bir
  alan yeri kaplar ama kullanılabilir değer taşımaz.
- **AB71. Seçici hatırlama** aynı `tut` bütçesinde, planın kalan adımlarının **bildirdiği** alanları
  tutar; artan yeri en yeni alanlar doldurur.
- **AB72.** Bir araç, daha önce düşürülmüş bir alanı istediğinde **eksik alan** hatası döner. Bu
  hatanın en az bir kez görüldüğü görev, **atılan alanın gerektiği görev** sayılır.
- **AB73.** Görev kümesi 24, adım bütçesi 8, taban çizgisi **sıkıştırmasız döngüdür**.

#### Taşınan Durumun Bedeli

Aşağıdaki blok ortak tanımı kurar ve taban çizgisini basar.

```python
# KURGU ortam ve KURGU ajan. Gercek bir model ucu, arac sunucusu ya da bellek
# servisi cagrilmaz. Abone kayitlari, belgeler ve gorevler kurgudur, tohum 20260218.
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)}

CAGRI[0] = 0
t = [ajan(g) for g in GOREV]
print("taban: adim", sum(x["adim"] for x in t), ", cagri", CAGRI[0], ", iz belirteci",
      sum(x["belirtec"] for x in t), ", hata", sum(x["hata"] for x in t), ", basarim",
      round(sum(x["yanit"] == dogru_yanit(g) for x, g in zip(t, GOREV)) / 24, 4))
```

```
taban: adim 97 , cagri 74 , iz belirteci 11978 , hata 8 , basarim 0.9583
```

Ortak döngü 24 görevi 97 adımda bitiriyor ve 11978 belirteç sayıyor. Bu sayı **yalnız izi**
kapsıyor: her adımda o ana kadarki araç adları ve gözlem başlıkları. Oysa modele giden istem
durumu da taşır — okunan endeksler, abone tipi, bulunan belge metni. Her alan her adımda yeniden
ödenir ve durum büyüdükçe bu kalem izden hızlı büyür.

#### Sıkıştırma Oranını Süpürmek

Aşağıdaki blok bedeli tamamlar ve iki politikayı dört ayarda süpürür.

```python
AD_BEDEL = 12                       # tasinan alanin ad ve bicim bedeli
SONUC = {"tuketim": "m3", "tarife": "tutar", "belge_ara": "kod"}


def durum_belirteci(alan):
    return sum(AD_BEDEL + len(belirtecle(v)) for v in alan.values())


def sikistir(alan, kalan, tut, politika):
    """tut alandan fazlasi dusurulur. ozet en eskiyi dusurup yerine tek bir
    ozet girdisi koyar; secici, kalan adimlarin bildirdigi alanlari tutar."""
    if len(alan) <= tut:
        return alan, ()
    anahtar = list(alan)
    if politika == "secici":
        gerek = {a for ad in kalan for a in ARAC[ad]["alan"]} | {"sonuc"}
        anahtar.sort(key=lambda k: k in gerek)
    atilan = tuple(anahtar[:len(alan) - tut])
    yeni = {k: v for k, v in alan.items() if k not in atilan}
    if politika == "ozet":
        yeni["ozet"] = f"{len(atilan)} alan dusuruldu"
    return yeni, atilan


def ajan_s(g, tut=99, politika="ozet", tohum=TOHUM, en_cok_adim=8):
    """Ortak dongu, her adimin sonunda baglam sikistirmasiyla."""
    r = uretec(tohum ^ izno(g["ad"]))
    alan = {"abone": g["abone"], "anahtar": g["istek"], "sutun": "bedel"}
    iz, adim, bel, hata, atilan, geri = [], 0, 0, 0, set(), 0
    dizi, k = g["dizi"], 0
    while adim < en_cok_adim:
        adim += 1
        bel += 60 + 22 * len(iz) + durum_belirteci(alan)
        hedef = dizi[k] if k < len(dizi) else None
        if hedef is None:
            if r() < 0.88:
                break
            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 sec == "kayit_yaz":
            alan["deger"] = alan.get("sonuc")
        eksik = [a for a in ARAC[sec]["alan"] if a not in alan and a in atilan]
        s = cagir_arac(sec, alan)
        iz.append(sec)
        bel += 30
        if "hata" in s:
            hata, geri = hata + 1, geri + bool(eksik)
            continue
        alan.update({kk: vv for kk, vv in s.items() if kk != "hata"})
        if sec in SONUC:
            alan["sonuc"] = s[SONUC[sec]]
        if sec == hedef:
            k += 1
        alan, at = sikistir(alan, dizi[k:], tut, politika)
        atilan |= set(at)
    return {"adim": adim, "belirtec": bel, "hata": hata, "geri": geri,
            "yanit": alan.get("sonuc")}


def kosum(tut, politika, tohum=TOHUM):
    CAGRI[0] = 0
    ad = bel = hata = geri = dogru = 0
    for g in GOREV:
        s = ajan_s(g, tut, politika, tohum)
        ad, bel, hata = ad + s["adim"], bel + s["belirtec"], hata + s["hata"]
        geri, dogru = geri + (s["geri"] > 0), dogru + (s["yanit"] == dogru_yanit(g))
    return ad, CAGRI[0], bel, hata, geri, dogru / len(GOREV)


DUZEN = [(99, "sikistirmasiz")] + [(t, p) for p in ("ozet", "secici")
                                   for t in (6, 5, 4, 3)]
print("tut politika        adim cagri belirtec hata gerekti basarim | 2. kosum")
for tut, pol in DUZEN:
    a = kosum(tut, "ozet" if tut == 99 else pol)
    b = kosum(tut, "ozet" if tut == 99 else pol, TOHUM + 1)
    print(f"{tut if tut != 99 else '--':>3} {pol:<14} {a[0]:>4} {a[1]:>5} {a[2]:>8}"
          f" {a[3]:>4} {a[4]:>6}  {a[5]:>6.4f} | {b[2]:>6} {b[5]:.4f}")
```

```
tut politika        adim cagri belirtec hata gerekti basarim | 2. kosum
 -- sikistirmasiz    97    74    21913    8      0  0.9583 |  21998 0.9583
  6 ozet            102    82    21281   29      5  0.9167 |  23749 0.9167
  5 ozet             98    77    19027   24      6  0.9583 |  21458 0.9583
  4 ozet             98    77    18086   24      6  0.7917 |  20420 0.7500
  3 ozet            156   149    33566  125     18  0.2917 |  34523 0.2500
  6 secici           97    74    19023   15      3  0.9583 |  19233 1.0000
  5 secici           97    74    18004   18      5  0.9583 |  18184 1.0000
  4 secici           98    77    17475   24      6  0.9583 |  18846 1.0000
  3 secici           98    77    16569   24      8  0.9583 |  17861 1.0000
```

Durum kalemi eklenince taban 11978'den **21913 belirtece** çıkıyor: taşınan durum, izin
kendisinden fazla tutuyor. Sıkıştırılacak yer buradadır.

Seçici satırlar beklendiği gibi ilerliyor. `tut` altıdan üçe inerken belirteç 19023'ten
**16569'a**, yani tabana göre **0,2439** oranında düşüyor. Bedeli iki sütunda görünüyor: araç
hatası sıkıştırmasız satırdaki 8'den 24'e çıkıyor ve **atılan alanın gerektiği görev** sayısı
0'dan **8'e**, yani yirmi dört görevin üçte birine tırmanıyor. Başarım ise ilk koşumda 0,9583'te
sabit, ikinci koşumda 1,0000. Düşen alanlar bir sonraki adımı bozuyor, ama ajan aynı aracı yeniden çağırarak alanı geri
kazanıyor; bedel adım değil hata olarak ödeniyor.

Özet satırları aynı yönde başlayıp kopuyor. `tut` dörtte belirteç 18086 ile seçicinin biraz
üstünde, ama başarım **0,7917** — ikinci koşumda 0,7500. `tut` üçte tablo tersine dönüyor:
belirteç **33566**, yani sıkıştırmasız tabanın **1,53 katı**; adım 97'den 156'ya, araç çağrısı
74'ten 149'a, araç hatası 8'den **125'e** çıkıyor ve başarım 0,2917'ye iniyor. Sıkıştırma burada
bir tasarruf değil, bir çoğaltıcıdır: düşürülen alan bir hataya, hata yeni bir adıma, yeni adım
yeniden taşınan bir bağlama yol açıyor.

#### Aynı Bütçe, Başka İçerik

Tablonun en çok şey söyleyen yeri `tut` dört satırlarıdır. İki politika **aynı sayıda alan**
taşıyor; belirteç farkı 611, yani yüzde dörtten az. Başarım farkı ise 0,7917'ye karşı 0,9583 ve
ikinci koşumda 0,7500'e karşı 1,0000. Koşumlar arasındaki en büyük fark 0,0417 iken bu açıklık
bandın dört ve altı katı, yani **ölçülmüştür**. Bağlamın **boyutu** değil, **hangi alanların
kaldığı** belirliyor.

Bu, Model API'leri ile Uygulama Geliştirme kursunda ölçülen bulgunun bellek tarafındaki
karşılığıdır: orada bir düzenleme görevinde beklenen kabul, bütün dosyalar bağlama konduğunda
0,6921, yalnız ilgili dosyalar seçildiğinde **0,8233** çıkmıştı. Oradaki seçim dosyalar
üzerindeydi, buradaki seçim ajanın kendi gözlemleri üzerindedir; sonuç aynı yöne bakıyor. İlgisiz
bağlam yalnız pahalı değildir, ilgili olanın yerini de kaplar.

#### Özet

- Taşınan durum her adımda yeniden ödenir: iz belirteci 11978 iken durumla birlikte taban 21913
  belirtece çıkıyor.
- Seçici hatırlama `tut` üçte belirteci 16569'a, tabana göre 0,2439 oranında indiriyor ve başarımı
  değiştirmiyor.
- Aynı süpürmede atılan alanın gerektiği görev sayısı 0'dan 8'e, araç hatası 8'den 24'e çıkıyor:
  kazanılan belirtecin bedeli burada yazılıdır.
- Aynı `tut` bütçesinde özetleme 0,7917, seçici hatırlama 0,9583 veriyor; fark iki koşum farkının
  dört ve altı katıdır.
- `tut` üçte özetleme belirteci 33566'ya, tabanın 1,53 katına çıkarıyor; sıkıştırma bu noktada
  tasarruf değil çoğaltıcıdır.

#### Sonraki Adım

Bu iki derste bellek iki yönden ele alındı: görevler arasında neyin yazılacağı, görev içinde neyin
taşınacağı. İkisinde de kayıtların **doğru** olduğu varsayıldı. Oysa bir sayaç yeniden okunur, bir
tarife değişir, bir belge yenilenir; uzun süreli bellekteki kayıt yerinde durur ve **sessizce
yanlışa döner**. Bunu bir alan düşürmesi çözmez, çünkü sorun yerin darlığı değil kaydın yaşıdır.
Sonraki ders eskitme eşiğini süpürür ve iki sayıyı yan yana koyar: eşik sıkılaştıkça bayat kayıt
düşer, ama hâlâ geçerli olduğu hâlde atılan kayıtların payı büyür.

### Bellek Eskitme

Source: https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/bellek-eskitme

Eskitme eşiğinin yedi ayarda süpürülmesi: eşiksiz bellekte okumaların 0,4528'i bayat ve başarım 0,6389. Eşik sıkıldıkça bayat pay 0,0000'a iniyor, başarım 0,9306'ya çıkıyor, ama düşürülen kayıtların hâlâ geçerli olan payı 0,0000'dan 0,9701'e tırmanıyor ve adım 138'den 315'e çıkıyor. Eşiği sekizin altına sıkmanın kazandırdığı başarım iki koşum farkının kendisi kadar kalıyor, yani ölçülmemiş sayılır.

Önceki iki ders belleği iki yönden ele aldı: görevler arasında neyin yazılacağı, görev içinde neyin
taşınacağı. İkisinde de kayıtların **doğru** olduğu varsayıldı. Oysa ölçüm ağı hareket eder: bir
sayaç yeni dönemde yeniden okunur, endeks farkı başka bir sayıya döner, bedel değişir. Bellekteki
kayıt yerinde durur ve hiçbir hata vermeden **yanlışa döner**.

Bunun çaresi bir alan düşürmesi değildir; sorun yerin darlığı değil kaydın **yaşıdır**. **Eskitme
(memory expiry)** kayda bir yaş sınırı koyar: sınırı geçen kayıt düşürülür. Bu ders o sınırı
süpürür ve iki sayıyı yan yana basar. Eşik sıkılaştıkça **bayat kayıt** payı düşer, ama düşürülen
kayıtların **hâlâ geçerli olan** payı büyür — ikisi aynı ayarla zıt yönlere gider.

- **AB74.** Kod bir **benzeticidir**; gerçek bir bellek servisi ya da araç sunucusu çağrılmaz.
  Ortam, araçlar ve görevler **kurgudur**, tohum **20260218**.
- **AB75. Ortam hareket eder:** her altı görevde bir, on iki abone **yeni bir dönem okuması** alır
  (önceki endeks şimdikinin yerini alır). Belge dağarcığı değişmez.
- **AB76.** Bellek seçici politikayla yazılır: plana giren gözlemler tek kayıtta birleşir, kayıt
  **abone koduyla** (belge kayıtları soru metniyle) anahtarlanır ve **yazıldığı görev sırasını**
  taşır.
- **AB77. Eskitme eşiği**, kaydın taşıyabileceği en büyük yaştır; yaşı eşikten büyük kayıt her
  görevin başında düşürülür.
- **AB78. Bayat kayıt**, alanları ortamın o andaki değeriyle örtüşmeyen kayıttır; **bayat pay**
  okuma başına sayılır.
- **AB79. Kayıp kayıt**, eşiğin düşürdüğü ama düşürüldüğü anda **hâlâ geçerli** olan kayıttır;
  **kayıp pay** düşürülen kayıt başına sayılır.
- **AB80.** Akış 72 görev, adım bütçesi 8; taban çizgisi **eşiksiz bellektir**.

#### Kayıt Yerinde Durur, Değer Değişir

Aşağıdaki blok ortak tanımı kurar ve tek bir abonede bayatlamayı gösterir.

```python
# KURGU ortam ve KURGU ajan. Gercek bir model ucu, arac sunucusu ya da bellek
# servisi cagrilmaz. Abone kayitlari, belgeler ve gorevler kurgudur, tohum 20260218.
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)}

ab = "A026"
eski = dict(ABONE[ab])
kayit = t_sayac_oku(abone=ab)
ABONE[ab]["onceki"] = ABONE[ab]["simdiki"]
ABONE[ab]["simdiki"] += 31                    # yeni donem okumasi
print("bellekteki kayit:", kayit["onceki"], kayit["simdiki"], "-> tuketim",
      t_tuketim(onceki=kayit["onceki"], simdiki=kayit["simdiki"])["m3"])
print("ortamdaki deger :", ABONE[ab]["onceki"], ABONE[ab]["simdiki"], "-> tuketim",
      t_tuketim(onceki=ABONE[ab]["onceki"], simdiki=ABONE[ab]["simdiki"])["m3"])
ABONE[ab].update(eski)
```

```
bellekteki kayit: 88422 88477 -> tuketim 55
ortamdaki deger : 88477 88508 -> tuketim 31
```

Kayıt değişmedi, ortam değişti. Bellekten dönen endeks çifti 55 metreküp veriyor, sayacın o andaki
değeri 31. Ajan hata almaz, araç hata döndürmez, döngü kısalır ve yanıt yanlıştır: bayat bir kayıt
doğru kayıttan **ayırt edilemez**.

#### Eşiği Sıkmak

Aşağıdaki blok akışı, dönem yenilemelerini ve eşik süpürmesini kurar.

```python
# ---- Eskitme: kaydin yasi ile gecerliligi
PLAN = {"bedel": (("sayac_oku", "tuketim", "tarife"),
                  "{a} abonesinin bu donem bedeli nedir"),
        "tuketim": (("sayac_oku", "tuketim"),
                    "{a} abonesinin bu donem tuketimi kac metrekup"),
        "belge": (("belge_ara",), "sayac donusunde endeks farki nasil hesaplanir"),
        "kayit": (("sayac_oku", "tuketim", "tarife", "kayit_yaz"),
                  "{a} abonesinin bedelini hesapla ve kayda yaz")}
CIKTI = {"sayac_oku": ("onceki", "simdiki", "tip", "bolge"), "tuketim": ("m3",),
         "tarife": ("tutar",), "belge_ara": ("kod",)}
SONUC = {"tuketim": "m3", "tarife": "tutar", "belge_ara": "kod"}
BASLANGIC = {k: dict(v) for k, v in ABONE.items()}


def akis(n=72):
    """Ilk kumenin abonelerine sorulan n soruluk gorev akisi."""
    k = []
    for i in range(n):
        r = uretec(TOHUM + 7919 * (i + 101))
        a = GOREV[int(r() * 24)]["abone"]
        t = list(PLAN)[ayrik(r(), [0.30, 0.22, 0.20, 0.28])]
        k.append({"ad": f"H{i + 1:02d}", "tur": t, "abone": a,
                  "dizi": PLAN[t][0], "istek": PLAN[t][1].format(a=a)})
    return k


def yeni_donem(i):
    """Her alti gorevde bir, on iki abone yeni bir donem okumasi alir."""
    r = uretec(TOHUM + 61 * i)
    for _ in range(12):
        a = ABONE[f"A{1 + int(r() * 60):03d}"]
        a["onceki"] = a["simdiki"]
        a["simdiki"] = a["onceki"] + int(r() * 70)


def gecerli(kyt):
    """Kaydin alanlari ortamin o andaki degerleriyle hala ortusuyor mu."""
    if kyt["abone"] is None:
        return True
    a = ABONE[kyt["abone"]]
    f = a["simdiki"] - a["onceki"]
    m3 = f + 100000 if f < 0 else f
    su = dict(a, m3=m3, tutar=_bedel(m3, a["tip"]))
    return all(su.get(x) == v for x, v in kyt["alan"].items())


def ajan_e(g, kyt, tohum=TOHUM, en_cok_adim=8):
    """Ortak dongu; baslangic durumu bellekteki kayittan gelir."""
    r = uretec(tohum ^ izno(g["ad"]))
    alan = {"abone": g["abone"], "anahtar": g["istek"], "sutun": "bedel"}
    alan.update(kyt["alan"] if kyt else {})
    adim, k, dizi, yolda = 0, 0, g["dizi"], {}
    while k < len(dizi) and dizi[k] in CIKTI and all(x in alan for x in CIKTI[dizi[k]]):
        alan["sonuc"] = alan.get(SONUC.get(dizi[k]), alan.get("sonuc"))
        k += 1
    if k == len(dizi):
        return {"adim": 0, "yanit": alan.get("sonuc"), "yolda": {}}
    while adim < en_cok_adim:
        adim += 1
        hedef = dizi[k] if k < len(dizi) else None
        if hedef is None:
            if r() < 0.88:
                break
            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 sec == "kayit_yaz":
            alan["deger"] = alan.get("sonuc")
        s = cagir_arac(sec, alan)
        if "hata" in s:
            continue
        alan.update({x: v for x, v in s.items() if x != "hata"})
        if sec in SONUC:
            alan["sonuc"] = s[SONUC[sec]]
        if sec == hedef:
            yolda.update({x: s[x] for x in CIKTI.get(sec, ()) if x in s})
            k += 1
    return {"adim": adim, "yanit": alan.get("sonuc"), "yolda": yolda}


def kosum(esik, tohum=TOHUM, n=72):
    """Eskitme esigi: yasi esikten buyuk kayit dusurulur."""
    for x, v in BASLANGIC.items():
        ABONE[x].update(v)
    CAGRI[0] = 0
    bellek = {}
    s = dict.fromkeys(("okuma", "bayat", "atilan", "kayip", "adim", "dogru"), 0)
    for i, g in enumerate(akis(n)):
        if i and i % 6 == 0:
            yeni_donem(i)
        for a in [x for x, k in bellek.items() if i - k["zaman"] > esik]:
            s["atilan"] += 1
            s["kayip"] += gecerli(bellek.pop(a))
        ah = g["istek"] if g["tur"] == "belge" else g["abone"]
        kyt = bellek.get(ah)
        if kyt:
            s["okuma"] += 1
            s["bayat"] += not gecerli(kyt)
        c = ajan_e(g, kyt, tohum)
        s["adim"] += c["adim"]
        s["dogru"] += c["yanit"] == dogru_yanit(g)
        if c["yolda"]:
            bellek[ah] = {"alan": c["yolda"], "zaman": i,
                          "abone": None if g["tur"] == "belge" else g["abone"]}
    return (s["okuma"], s["bayat"] / max(s["okuma"], 1), s["atilan"],
            s["kayip"] / max(s["atilan"], 1), s["adim"], CAGRI[0], s["dogru"] / n)


print("esik  okuma   bayat  atilan   kayip  adim cagri basarim | 2. kosum")
for e in (10 ** 6, 32, 24, 16, 8, 4, 1):
    a, b = kosum(e), kosum(e, TOHUM + 1)
    print(f"{e if e < 100 else '--':>4} {a[0]:>6} {a[1]:>7.4f} {a[2]:>7} {a[3]:>7.4f}"
          f" {a[4]:>5} {a[5]:>5} {a[6]:>7.4f} | {b[6]:.4f}")
```

```
esik  okuma   bayat  atilan   kayip  adim cagri basarim | 2. kosum
  --     53  0.4528       0  0.0000   138   108  0.6389 | 0.5972
  32     42  0.3333      18  0.3333   176   139  0.7778 | 0.7361
  24     36  0.2222      23  0.4783   201   158  0.8194 | 0.7778
  16     31  0.0968      32  0.6250   220   171  0.8750 | 0.8611
   8     23  0.0870      41  0.7805   246   193  0.8889 | 0.8611
   4     13  0.0000      55  0.8909   283   221  0.9028 | 0.8750
   1      3  0.0000      67  0.9701   315   246  0.9306 | 0.8889
```

Eşiksiz satır belleğin en verimli ve en yanlış hâlidir: 53 okuma, 138 adım, ama okumaların
**0,4528'i bayat** ve başarım **0,6389** — görevlerin üçte birinden fazlasında kullanıcıya geçen
dönemin sayısı söyleniyor.

Eşik sıkıldıkça iki sütun zıt yönlere gidiyor. Bayat pay 0,4528'den 0,3333'e, 0,2222'ye, 0,0968'e
ve **0,0000'a** iniyor, başarım 0,6389'dan **0,9306'ya** çıkıyor. Aynı satırlarda kayıp pay
0,0000'dan 0,6250'ye ve **0,9701'e** tırmanıyor: eşik bire indiğinde düşürülen 67 kaydın 65'i
düşürüldüğü anda **hâlâ geçerliydi**. Bedel adım olarak da yazılıyor — 138 adım 315'e, araç
çağrısı 108'den 246'ya çıkıyor.

#### Yaş, Geçerliliğin Vekilidir

Kayıp pay sütunu eskitmenin ne olduğunu tek başına anlatıyor. Eşik, bir kaydın **geçerli olup
olmadığını** sormaz; **kaç görev önce yazıldığını** sorar. Yaş, geçerliliğin bir vekilidir ve bu
akışta zayıf bir vekildir: her altı görevde yalnız on iki abone yenileniyor, yani bayatlama bir
olasılıktır, yaşın belirlediği bir kesinlik değil.

Nerede durulacağını iki koşum farkı söylüyor. Eşiksiz satır iki koşumda 0,6389 ve 0,5972 veriyor;
bant **0,0417**. Eşiği sekize sıkmanın kazancı 0,2500, bandın altı katı — **ölçülmüştür**. Sekizden
bire sıkmanın kazancı ise 0,8889 ile 0,9306 arasında, **tam bandın kendisi kadar**; ikinci koşumda
aynı kazanç 0,0278'e iniyor. Bu fark bulgu olarak **ilan edilemez**: eşiği sekizin altına indirmek
69 adım ve 53 araç çağrısı daha ödetiyor, karşılığında **ölçülmemiş** bir kazanç veriyor. Ölçmek
isteyen görev sayısını büyütmeli ya da yenileme sıklığını bir ayar olarak süpürmelidir.

Bayat ve kayıp payları iki koşumda birebir aynı çıkıyor: bu iki sayı araç seçimi çekilişine değil,
yenileme takvimi ile eşiğin ilişkisine bağlıdır. Eskitme eşiği verinin değişme sıklığından bağımsız
seçilemez.

#### Özet

- Bellekteki kayıt hata vermeden yanlışa döner ve doğru kayıttan ayırt edilemez.
- Eşiksiz bellekte okumaların 0,4528'i bayat ve başarım 0,6389; eşik bire indiğinde bayat pay
  0,0000, başarım 0,9306.
- Aynı süpürmede düşürülen kayıtların hâlâ geçerli olan payı 0,0000'dan 0,9701'e çıkıyor ve adım
  138'den 315'e tırmanıyor.
- Eşik, kaydın geçerliliğini değil yaşını sorar; yaş bir vekildir ve bu yenileme takviminde bayat
  payı sıfırlamanın bedeli geçerli kayıtların neredeyse tamamını atmaktır.
- Eşiği sekizin altına sıkmanın kazandırdığı başarım iki koşum farkının kendisi kadardır ve
  ölçülmemiş sayılır; sekizden bire inmek 69 adım daha ödetiyor.

#### Sonraki Adım

Bu konu ajanı donattı: araç tanımı, yürütme, kod çalıştırma, dosya ve terminal yüzeyi, dış bilgi,
kısa ve uzun süreli bellek, sıkıştırma ve eskitme. Bir önceki konuyla birlikte on altı derste bir
ajan kuruldu, nerede duracağı yazıldı, denetim noktaları kondu ve eline araçlar verildi. Her derste
bir düzeneğin sayısı ve bedeli basıldı — ama her biri yalnız o dersin düzeneğine bakan bir ölçüyle
okundu. Bütününe bakan soru hiç sorulmadı: bu ajanın **çalıştığını nereden biliyoruz**. Doğru yanıt
veren bir koşumun doğru **yolu** izleyip izlemediği, bir aracın tek başına sınandığında görünmeyen
kusurunun akış içinde çıkıp çıkmadığı ve üretimde koşan bir döngü hakkında hangi kaydın
konuşabildiği ele alınmadı. Sonraki konu bu soruyla açılır.

## Ajan Kalitesi

Kurulmuş bir ajanın çalıştığının nereden bilindiğinin ölçülmesi: doğru dizi ile doğru yanıtın dört durumlu tablosu ve yanlış diziyle üretilen doğru yanıtın ayrı sayılması, doğru yanıt başına adım olarak okunan adım verimliliği, beş araca konan kusurların araç düzeyi sınamayla yakalanan payı ve hiç görülemeyen kusur sınıfı, uçtan uca akış sınamasının araç düzeyinin kaçırdığından yakaladığı ile iki katmanın da yakalayamadığı kalıntı, yalnız sonuç, araç adları ve tam gözlem iz düzeylerinde başarısız bir görevin nedeninin bulunabildiği pay ile saklanan belirteç, adım başına maliyet ile doğru yanıt başına maliyetin sıralamayı ayırdığı nokta, durdur, aynı aracı yeniden dene ve başka araca geç kurtarma politikalarının başarım, adım ve araç hatası olarak karşılaştırılması.

### Görev Başarımı ve Adım Verimliliğinin Ayrı Ölçülmesi

Source: https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/ajan-degerlendirmesi

Tek bir başarım oranının dört ayrı durumu üst üste yığdığının adım üzerinde gösterilmesi: yirmi dört görevlik kurgu kümede araçsız tek adımlı taban 0,2917 çıkıyor ve bu oranın tamamı yanlış dizi ile doğru yanıt kutusunda, çünkü yedi belge görevinin hepsi hiçbir araç çağrılmadan yanıtlanıyor. Dar bütçede döngü 0,8750, kurs bütçesinde 0,9583 veriyor; kurs bütçesinde tek başarısızlık doğru dizi ile yanlış yanıt kutusunda ve dizisi eksiksiz tamamlanmış bir görevin arada kalan fazla bir çağrıyla bozulmasından geliyor. Doğru yanıt başına adım tabanda 3,4286, kurs bütçesinde 4,2174 çıkıyor, yani verimlilik tek başına okunduğunda hiçbir aracı çağırmayan tabanı kazandırıyor; araç gerektiren on yedi görevde taban sıfır doğru yanıt veriyor. İki koşum farkı tabanda 0,0000, dar bütçede 0,0417.

On altı derste bir ajan kuruldu ve donatıldı: döngü yazıldı, nerede duracağı belirlendi, yan
etkili çağrılar onaya bağlandı, araçlar tanımlandı, yürütüldü ve yetkileri sınırlandı, belleği
pencereye sığdırıldı, kalıcı kayıtları yazıldı, sıkıştırıldı ve eskitildi. Bir soru hiç sorulmadı:
bu ajanın çalıştığını nereden biliyoruz.

Bir ajan koşumundan geriye genellikle tek bir oran kalır: kaç görev doğru yanıtlandı. Bu dersin
iddiası, o oranın **dört ayrı durumu** üst üste yığdığı ve yığının içinden ajanın işi **yaparak mı
yoksa yapmadan mı** doğru bulduğunun okunamadığıdır. Aynı ayrım M28/K04 Gömme Vektörleri ve Erişim
Artırılmış Üretim kursunda doğru parça ile doğru yanıt üzerinde kurulmuştu; yordamı orada kaldı ve
burada tekrarlanmaz. Buraya taşınan yalnız **ayrımın kendisidir**, bu kez **adım** üzerinde.

- **KL1.** Abone kayıtları, belge dağarcığı, beş araç, yirmi dört görev ve ajan döngüsü
  **kurgudur** ve ders içinde üretilir. Gerçek bir model uç noktası ya da araç sunucusu
  **çağrılmaz**; araç seçimi derinliğe bağlı tek bir çekilişten gelir.
- **KL2.** **Taban çizgisi tek adımlı, araçsız çağrıdır**; benzetici yalnız istek metnini görür.
- **KL3.** **Dizi ölçüsü**: adım izinde görevin beklenen araç dizisi **sırasıyla ve hatasız**
  geçiyorsa dizi doğrudur; araya giren fazla çağrı diziyi bozmaz.
- **KL4.** **Yanıt ölçüsü**: döngünün bıraktığı sonuç doğru yanıta birebir eşitse yanıt doğrudur.
- **KL5.** **Adım verimliliği** doğru yanıt başına adımdır; bedelin öbür birimleri **araç çağrısı
  ve belirteçtir**, süre yazılmaz.
- **KL6.** Her ölçü **iki koşumda** basılır (tohum 20260218 ve 20260219). **İki koşum farkının
  altında kalan fark ölçülmemiş sayılır.**

#### Kurgu Ortam, Beş Araç ve Ajan Döngüsü

Beş araç sayaç okur, tüketim hesaplar, tarife uygular, belge arar ve kayıt yazar; sonuncusu yan
etkilidir. Her görev bilinen bir dizi ile çözülür.

```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)}


TUR = ("bedel", "tuketim", "belge", "kayit")
sayim = {t: sum(1 for g in GOREV if g["tur"] == t) for t in TUR}
print(f"kurgu ortam {len(ABONE)} abone , {len(BELGE)} belge , {len(ARAC)} arac")
print("gorev", len(GOREV), ",", " , ".join(f"{k} {v}" for k, v in sayim.items()))
```

```
kurgu ortam 60 abone , 5 belge , 5 arac
gorev 24 , bedel 6 , tuketim 8 , belge 7 , kayit 3
```

#### Dizi ve Yanıt: İki Ayrı Sütun

Birinci sütun yanıta hiç bakmaz: adım izinde beklenen dizi sırasıyla ve hatasız geçti mi. İkinci
sütun ize hiç bakmaz. İki sütun kesiştiğinde her görev dört kutudan birine düşer.

```python
def aracsiz(g, tohum=TOHUM):
    """Taban cizgisi: tek adimli, araci olmayan cagri. Hicbir arac cagrilmaz;
    benzetici yalniz istek metnini gorur ve kod onekinden tahmin yurutur."""
    r = uretec(tohum ^ izno("tek" + g["ad"]))
    onek = {"sayac": "SY", "tarif": "TR", "ariza": "AR", "kesin": "KS"}
    b = set(belirtecle(g["istek"]))
    for k, p in onek.items():
        if k in b:
            aday = [c for c in BELGE if c.startswith(p)]
            return aday[int(r() * len(aday))]
    return 10 * int(r() * 20)


def dizi_dogru(g, iz):
    """Adim izinde beklenen arac dizisi sirasiyla ve hatasiz geciyor mu."""
    k = 0
    for ad, hatali in iz:
        if k < len(g["dizi"]) and ad == g["dizi"][k] and not hatali:
            k += 1
    return k == len(g["dizi"])


def dort_durum(gorevler, butce, tohum=TOHUM):
    """Butce sifirsa taban cizgisi, degilse dongu kosulur."""
    d = {"dd": 0, "dy": 0, "yd": 0, "yy": 0, "adim": 0, "bel": 0, "cagri": 0}
    c0 = CAGRI[0]
    for g in gorevler:
        s = ajan(g, en_cok_adim=butce, tohum=tohum) if butce else None
        iz, yanit, adim, bel = ((s["iz"], s["yanit"], s["adim"], s["belirtec"]) if s
                                else ([], aracsiz(g, tohum), 1, 60))
        dd, dy = dizi_dogru(g, iz), yanit == dogru_yanit(g)
        d["dd" if dd and dy else "dy" if dd else "yd" if dy else "yy"] += 1
        d["adim"] += adim
        d["bel"] += bel
    d["cagri"] = CAGRI[0] - c0
    return d


KURULUM = (("aracsiz", 0), ("butce 4", 4), ("butce 8", 8))
print("kurulum   d+d  d+y  y+d  y+y  basarim  2.kosum     fark  adim  cagri  belirtec  adim/dogru")
for ad, b in KURULUM:
    d, e = dort_durum(GOREV, b), dort_durum(GOREV, b, 20260219)
    n, m = (d["dd"] + d["yd"]) / 24, (e["dd"] + e["yd"]) / 24
    print(f"{ad:<9} {d['dd']:>3} {d['dy']:>4} {d['yd']:>4} {d['yy']:>4} {n:>8.4f} {m:>8.4f}"
          f" {m - n:>+8.4f} {d['adim']:>5} {d['cagri']:>6} {d['bel']:>9}"
          f" {d['adim'] / max(n * 24, 1):>11.4f}")

print("\ngorev turu dokumu (d+d / d+y / y+d / y+y)")
print(f"{'tur':<9} {'gorev':>6} {'aracsiz':>16} {'butce 8':>16}")
for tur in TUR:
    gs = [g for g in GOREV if g["tur"] == tur]
    sat = [" / ".join(str(dort_durum(gs, b)[k]) for k in ("dd", "dy", "yd", "yy"))
           for b in (0, 8)]
    print(f"{tur:<9} {len(gs):>6} {sat[0]:>16} {sat[1]:>16}")

ag = [g for g in GOREV if g["tur"] != "belge"]
print(f"\narac gerektiren {len(ag)} gorev , dogru yanit ve adim")
for ad, b in KURULUM:
    d = dort_durum(ag, b)
    print(f"{ad:<9} dogru {d['dd'] + d['yd']:>3} / {len(ag)}   adim {d['adim']:>4}")
```

```
kurulum   d+d  d+y  y+d  y+y  basarim  2.kosum     fark  adim  cagri  belirtec  adim/dogru
aracsiz     0    0    7   17   0.2917   0.2917  +0.0000    24      0      1440      3.4286
butce 4    20    0    1    3   0.8750   0.8333  -0.0417    84     64      9402      4.0000
butce 8    23    1    0    0   0.9583   0.9583  +0.0000    97     74     11978      4.2174

gorev turu dokumu (d+d / d+y / y+d / y+y)
tur        gorev          aracsiz          butce 8
bedel          6    0 / 0 / 0 / 6    6 / 0 / 0 / 0
tuketim        8    0 / 0 / 0 / 8    8 / 0 / 0 / 0
belge          7    0 / 0 / 7 / 0    7 / 0 / 0 / 0
kayit          3    0 / 0 / 0 / 3    2 / 1 / 0 / 0

arac gerektiren 17 gorev , dogru yanit ve adim
aracsiz   dogru   0 / 17   adim   17
butce 4   dogru  14 / 17   adim   64
butce 8   dogru  16 / 17   adim   77
```

#### Dört Bileşimin Okunması

İlk satır tabanı verir. Araçsız tek adım **0,2917** yapıyor ve bu oranın **tamamı üçüncü
kutudadır**: yanlış dizi, doğru yanıt. Çağrı sütunu sıfırdır; yedi belge görevinin hepsi doğru
gelmiştir, çünkü bu görevlerin isteği aynı metindir ve içindeki sayaç sözcüğü belge kodunun
önekini ele verir. Tek sayı ajanın görevlerin dörtte birinden fazlasını çözdüğünü söyler; dört
durum tablosu aynı satırda **hiçbir işin yapılmadığını** söyler.

Asıl gösterim son satırdadır. Sekiz adımlık bütçede tek başarısızlık **ikinci kutudadır**: doğru
dizi, yanlış yanıt. Bir kayıt görevinde beklenen dört araç sırasıyla ve hatasız geçti, ama tarife
ile kayıt arasında fazladan bir belge araması yapıldı ve o çağrı durumdaki sonucu bir belge koduyla
değiştirdi. Ajan doğru işi doğru sırayla yaptı, sonra kendi sonucunu bozdu. Tablo burada **hangi
düzeltmenin gerektiğini** yazar, çünkü araç seçimi kusuru ile durum bozulması kusuru ayrı yerlerde
onarılır.

Dar bütçe satırı üçüncü kutunun döngüde de dolabildiğini gösterir. Bir kayıt görevinde doğru tutar
bulundu, ama bütçe kayıt yazımına yetmedi: dizi eksik, yanıt doğru. Tek sayı bu görevi başarı
sayar; oysa yan etkili adım hiç gerçekleşmemiştir. Tür dökümü tabanın yedi doğrusunun tamamını
belge türünde, sekizlik bütçenin tek başarısızlığını kayıt türünde toplar.

Hangi farkın sayılabileceğini iki koşum söyler. Dar bütçede fark **0,0417**, yani tek bir görev;
tabanda ve sekizlik bütçede **0,0000**. İki bütçe arasındaki 0,0833'lük yükseliş bandın iki
katıdır ve sayılabilir; kutu geçişlerinin tek tek büyüklüğü **ölçülmemiş sayılır**.

#### Doğru Yanıt Başına Adım

İkinci ölçü bedeli başarıma bağlar. Sekizlik bütçede ajan 97 adım harcayıp 23 doğru yanıt
üretiyor: adım başına **4,2174**. Dar bütçede 84 adım ve 21 doğru yanıt, yani **4,0000**.

Tuzak ilk satırdadır. Araçsız taban 24 adımda 7 doğru yanıt veriyor ve adım verimliliği **3,4286**
çıkıyor, yani üç kurulumun en iyisi. Hiçbir araç çağırmayan, hiçbir hesap yapmayan kurulum
verimlilik ölçüsünde kazanıyor, çünkü bu ölçü paydaya yalnız doğru yanıtları koyar ve **hangi
görevlerin çözülebildiğini** hiç sormaz.

Son tablo paydayı düzeltir. Yalnız araç gerektiren on yedi görev sayıldığında taban **0 / 17**
veriyor ve verimlilik bu alt kümede tanımsız kalıyor; dar bütçe 14, sekizlik bütçe 16 doğru yanıt
veriyor. Ölçü bu yüzden tek başına okunmaz: **hangi görev kümesinde ve hangi başarım düzeyinde**
ölçüldüğü yazılmadan, bir ajanı ucuzlatmanın en kestirme yolu çözebildiği görevleri azaltmaktır.

#### Özet

- Tek bir başarım oranı dört durumu üst üste yığar: doğru dizi ile doğru yanıt, doğru dizi ile
  yanlış yanıt, yanlış dizi ile doğru yanıt, yanlış dizi ile yanlış yanıt.
- Araçsız taban 0,2917 çıkar ve bu oranın tamamı **yanlış dizi ile doğru yanıt** kutusundadır;
  ajan hiçbir araç çağırmamıştır.
- Sekiz adımlık bütçede başarım 0,9583'tür ve tek başarısızlık **doğru dizi ile yanlış yanıt**
  kutusundadır: dizi eksiksiz tamamlanmış, araya giren fazla bir çağrı sonucu bozmuştur.
- Doğru yanıt başına adım tabanda 3,4286, sekizlik bütçede 4,2174'tür; ölçü tek başına okunduğunda
  tabanı kazandırır, çünkü araç gerektiren on yedi görevde taban sıfır doğru verir.
- İki koşum farkı tabanda ve sekizlik bütçede 0,0000, dar bütçede 0,0417'dir; tek görevlik kutu
  geçişleri bu bandın altında kalır.

#### Sonraki Adım

Bu ders bir ajan koşumunun sayısını ikiye ayırdı: işin yapılıp yapılmadığı ve yanıtın doğru olup
olmadığı. İkisi de bir şeyi varsayar: araçların kendisi doğrudur. Araçlardan biri yanlış hesap
yapsaydı, eksik alanı sessizce yutsaydı ya da tanımı işlevine uymasaydı, tablo yine dolar ve hangi
aracın bozuk olduğunu söylemezdi. Bir sonraki ders her aracı döngüden ayırıp tek tek sınar: beş
araca konan kusurların kaçı bu sınamayla yakalanıyor ve **hangi kusur sınıfı bu katmandan hiç
görünmüyor**.

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

Source: https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/arac-duzeyi-testler

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.

### Senaryo Temelli Uçtan Uca Doğrulama

Source: https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/uctan-uca-akis-testleri

İki sınama katmanının yakaladığı kusur kümelerinin yan yana sayılması: yirmi dört senaryoluk uçtan uca akış sınaması, araç düzeyi sınamanın hiç göremediği dört tanım kusurunun üçünü yakalıyor ve toplamda on bir kusurun beşini düşürüyor. Araç düzeyi katman yediyi yakalıyor, iki katmanın kesişimi iki, birleşimi on bir kusurda on yani 0,9091. Kaçırmanın iki ayrı nedeni ateşleme sayısıyla ayrılıyor: D2, D4 ve D11 döngü boyunca bir kez bile ateşlemiyor; D5, D6 ve D7 ateşliyor ama senaryo sonucu değişmiyor. Kalıntı D11, işlevi doğru olduğu hâlde tanımında kullanılmayan bir alan bildiren kusurdur ve hiçbir koşum onu göremez. Kusursuz tabanda düşen senaryo iki koşumda G23 ve G15.

Bir önceki ders araç düzeyi sınamanın sınırını çizdi: on bir kusurun yedisi yakalandı, dört tanım
kusurunun hiçbiri görünmedi. Görünmemeleri onları zararsız yapmaz; işyeri abonelerinin bedeli
yanlış hesaplanıyor. Bu kusurlar ancak araçlar **birbirine bağlıyken** ortaya çıkar.

Bu ders ikinci katmanı kurar. Uçtan uca akış sınaması aracı değil **senaryoyu** koşturur ve
sonunda hem diziyi hem yanıtı denetler. Ölçülen şey katman değil, **iki katmanın kusur kümeleri
arasındaki ilişkidir**.

- **KL12.** Kusur kümesi bir önceki dersle **aynıdır**; kusurlar tablonun bir kopyasına konur.
  **Araç düzeyi sütunu o dersin ölçümüdür**, sınama kümesi burada tekrarlanmaz.
- **KL13.** **Uçtan uca akış sınaması**: yirmi dört senaryo sekiz adımlık bütçeyle koşulur.
  Senaryo ancak **dizi de yanıt da doğruysa** geçer.
- **KL14.** Kusursuz tabloda tabanı **24'te 23'tür**. Bir kusur, düşen senaryo kümesi bu tabandan
  **iki koşumda da** farklıysa akış katmanınca yakalanmış sayılır.
- **KL15.** **Ateşleme sayısı**: her çağrıda aynı girdi kusursuz tabloya da verilir ve iki sonuç
  ayrıldığında sayaç artar. **Sıfır ateşleme**, kusurun döngüde bir kez bile görünmediğidir.
- **KL16.** Tohumlar 20260218 ve 20260219; iki koşum farkının altındaki fark **ölçülmemiş
  sayılır**.

#### Kurgu Ortam ve Ajan Döngüsü

Her senaryonun bilinen bir araç dizisi ve bilinen bir doğru yanıtı vardır; sınamanın kâhini bu
ikilidir.

```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(f"kurgu ortam {len(ABONE)} abone , {len(ARAC)} arac , {len(GOREV)} senaryo")
```

```
kurgu ortam 60 abone , 5 arac , 24 senaryo
```

#### İki Katman Yan Yana

Akış sınamasının gördüğü tek şey döngünün ürettiği dizi ile yanıttır. Ateşleme sayacı yanına bir
sütun koyar: kusur o koşumda kaç çağrıda kusursuz tablodan ayrıldı. Araç düzeyi sütunu bir önceki
dersten girdi olarak gelir.

```python
KUSUR = {
    "D1": ("sayac_oku", "deger",
           lambda abone=None, **_: ({"hata": "abone yok"} if abone not in ABONE
                                    else dict(ABONE[abone], simdiki=ABONE[abone]["onceki"]))),
    "D2": ("tuketim", "deger",
           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",
           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",
           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",
           lambda onceki=None, simdiki=None, **_: t_tuketim(onceki or 0, simdiki or 0)),
    "D6": ("tarife", "sozlesme",
           lambda m3=None, tip="mesken", **_: t_tarife(m3 or 0, tip)),
    "D7": ("kayit_yaz", "sozlesme",
           lambda abone=None, sutun=None, deger=None, **_: {
               "yazildi": True, "abone": abone, "sutun": sutun, "deger": deger}),
    "D8": ("tarife", "tanim", ("m3",)),                  # tip alanini bildirmiyor
    "D9": ("kayit_yaz", "tanim", ("abone", "sutun")),     # deger alanini bildirmiyor
    "D10": ("belge_ara", "tanim", ("abone",)),            # anahtar yerine abone
    "D11": ("sayac_oku", "tanim", ("abone", "sutun")),    # islevin kullanmadigi alan
}
TEMIZ = {a: dict(v) for a, v in ARAC.items()}


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, p = KUSUR[kod][0], KUSUR[kod][2]
    t[ad]["alan" if isinstance(p, tuple) else "islev"] = p
    return t


# Bir onceki dersin katmani girdidir: on bir durumluk sinama kumesi orada
# kuruldu ve dusurdugu kusurlar sunlardi. Hesap burada tekrarlanmaz.
ARAC_DUZEYI = {"D1", "D2", "D3", "D4", "D5", "D6", "D7"}

ATES = [0]
_cagir = cagir_arac


def cagir_arac(ad, durum):
    """Cagriyi yururlukteki tabloyla kurar, ayni girdiyi kusursuz tabloya da
    verir ve iki sonuc ayrildiginda kusurun atesledigini sayar."""
    b = _cagir(ad, durum)
    kw = {a: durum[a] for a in TEMIZ[ad]["alan"] if a in durum}
    ATES[0] += b != TEMIZ[ad]["islev"](**kw)
    return b


def dizi_dogru(g, iz):
    k = 0
    for ad, hatali in iz:
        if k < len(g["dizi"]) and ad == g["dizi"][k] and not hatali:
            k += 1
    return k == len(g["dizi"])


def akis_sinamasi(tablo, tohum=TOHUM):
    """Uctan uca senaryo: dizi de yanit da dogruysa gecer."""
    global ARAC
    asil, ARAC, ATES[0] = ARAC, tablo, 0
    d = []
    for g in GOREV:
        s = ajan(g, en_cok_adim=8, tohum=tohum)
        if not (dizi_dogru(g, s["iz"]) and s["yanit"] == dogru_yanit(g)):
            d.append(g["ad"])
    ARAC = asil
    return d, ATES[0]


taban = [akis_sinamasi(TEMIZ, t) for t in (TOHUM, 20260219)]
print(f"akis sinamasi {len(GOREV)} senaryo , kusursuz tabloda dusen senaryo "
      f"{taban[0][0]} ve {taban[1][0]}")
print("\nkod   arac        sinif      arac-duzeyi  akis-1  akis-2  ates-1  ates-2")
AD, AK = set(), set()
for kod, (ad, sinif, p) in KUSUR.items():
    t, ar = kusurlu(kod), kod in ARAC_DUZEYI
    (d1, a1), (d2, a2) = akis_sinamasi(t, TOHUM), akis_sinamasi(t, 20260219)
    if ar:
        AD.add(kod)
    if d1 != taban[0][0] and d2 != taban[1][0]:
        AK.add(kod)
    print(f"{kod:<5} {ad:<11} {sinif:<9} {'+' if ar else '-':>11} {len(d1):>7} {len(d2):>7}"
          f" {a1:>7} {a2:>7}")

n = len(KUSUR)
print()
for et, s in (("ikisi birden", AD & AK), ("yalniz akis", AK - AD),
              ("yalniz arac duzeyi", AD - AK), ("kalinti", set(KUSUR) - AD - AK)):
    print(f"{et:<19} {' '.join(k for k in KUSUR if k in s)}")
print(f"arac duzeyi {len(AD)}/{n} = {len(AD) / n:.4f} , akis {len(AK)}/{n} = "
      f"{len(AK) / n:.4f} , birlesim {len(AD | AK)}/{n} = {len(AD | AK) / n:.4f}")
```

```
akis sinamasi 24 senaryo , kusursuz tabloda dusen senaryo ['G23'] ve ['G15']

kod   arac        sinif      arac-duzeyi  akis-1  akis-2  ates-1  ates-2
D1    sayac_oku   deger               +      17      17      18      20
D2    tuketim     deger               +       1       1       0       0
D3    tarife      deger               +       7       7       9       8
D4    belge_ara   deger               +       1       1       0       0
D5    tuketim     sozlesme            +       1       1       2       1
D6    tarife      sozlesme            +       1       1       4       5
D7    kayit_yaz   sozlesme            +       1       1       2       1
D8    tarife      tanim               -       5       6       6       5
D9    kayit_yaz   tanim               -       3       3       7      14
D10   belge_ara   tanim               -       7       8      44      49
D11   sayac_oku   tanim               -       1       1       0       0

ikisi birden        D1 D3
yalniz akis         D8 D9 D10
yalniz arac duzeyi  D2 D4 D5 D6 D7
kalinti             D11
arac duzeyi 7/11 = 0.6364 , akis 5/11 = 0.4545 , birlesim 10/11 = 0.9091
```

#### Kesişmeyen İki Küme

Araç düzeyi katman **7/11**, akış katmanı **5/11** yakalıyor. Kesişim yalnız **iki kusurdur**:
`D1` ve `D3`. `D1` her sayaç okumasında ateşliyor ve yirmi dört senaryonun **on yedisini**
düşürüyor; `D3` yalnız üst dilimi aşan tüketimlerde ateşliyor ve **yedisini**.

Asıl bilgi kesişimin dışındadır. Akış katmanı **`D8`, `D9`, `D10`** kusurlarını, yani araç
düzeyinin hiç göremediği dört tanım kusurunun üçünü yakalıyor. Neden mekanizmadadır: akış sınaması
aracı işlevinden değil, **döngünün kurduğu çağrıdan** görür ve o çağrı bildirilen alan listesinden
geçer. `D10` en yüksek ateşlemeyi veriyor: **44** ve **49**. Ters yönde **`D2`, `D4`, `D5`, `D6`,
`D7`** yalnız araç düzeyinde görünüyor. Birleşim **10/11 = 0,9091**; hiçbir katman tek başına
buraya yaklaşmıyor.

#### Ateşlemeyen Kusur ve Kalıntı

Akışın kaçırdığı kusurların **iki ayrı nedeni** vardır ve ateşleme sütunu ikisini ayırır. `D2` ve
`D4` iki koşumda da **sıfır** ateşliyor: sayaç dönüşü dalına bu görev kümesinde hiç girilmiyor ve
belge sorgusunda ilk örtüşen belge zaten en yüksek örtüşen belge. Kusur bir kez bile ortaya
çıkmadı; akışın onu bulma şansı yoktu. `D5`, `D6` ve `D7` ise ateşliyor — sırasıyla 2, 4 ve 2
çağrıda — ama düşen senaryo kümesi değişmiyor: kusur koşuldu, sonucu bozdu ve **senaryo yine de
geçti**. İlki bir kapsam eksiği, ikincisi bir gözlem eksiğidir.

Ateşleme sayısı bir büyüklük olarak okunmaz. `D9` ilk koşumda 7, ikincide 14 ateşliyor; `D1` 18 ve
20. Okunan şey **sıfır olup olmadığıdır**.

Kalıntı tek kusurdur ve adı `D11`. `sayac_oku` işlevi doğrudur, ama tanımı işlevin hiç kullanmadığı
bir alan bildirir. Araç düzeyi sınama göremez, çünkü işlev doğru çalışır. Akış sınaması göremez,
çünkü fazladan bildirilen alan çağrıya girer ve sessizce yutulur: ateşleme iki koşumda da
**sıfır**. Kusur davranışta değil **sözleşmenin kendisindedir** ve onu okuyacak olan, aracı
çağıracak bir başkasıdır.

#### Özet

- Akış sınaması senaryoyu koşturur ve yalnız dizi ile yanıtı görür; kusursuz tabanda düşen
  senaryo iki koşumda G23 ve G15'tir.
- Akış katmanı 11 kusurun 5'ini, araç düzeyi katman 7'sini yakalar; kesişim yalnız `D1` ve `D3`,
  birleşim 10/11 = **0,9091**.
- Akış katmanı, araç düzeyinin göremediği dört tanım kusurunun üçünü yakalar, çünkü çağrıyı
  bildirilen alan listesinden geçirir.
- Kaçırmanın nedeni ikiye ayrılır: `D2`, `D4` ve `D11` hiç ateşlemez; `D5`, `D6` ve `D7` ateşler
  ama senaryo sonucunu değiştirmez.
- Kalıntı `D11`'dir: işlev doğru, tanım fazladan bir alan bildiriyor; ateşleme sıfır olduğu için
  hiçbir koşum bu kusuru gösteremez.

#### Sonraki Adım

İki katman birlikte on bir kusurun onunu buldu, ama ikisinin ortak bir yanı var: kusuru **biz
koyduk** ve nerede olduğunu baştan biliyorduk. Sınama, ajanın sınandığı koşumları görür; üretimde
koşan bir döngü hakkında hiçbir şey söylemez. Orada düşen bir senaryo yoktur, yalnız bir kullanıcı
ve yanlış bir yanıt vardır. Bir sonraki ders koşumun geriye ne bıraktığını ölçer: üç iz düzeyinde
başarısız bir görevin nedeninin bulunabildiği pay ve saklanan belirteç.

### İzleme ve Günlükleme

Source: https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/izleme-ve-gunlukleme

Üretimde koşan bir ajan döngüsünün adım izinin neyi geri okuttuğunun ölçülmesi: yirmi dört görevin on koşumundan oluşan iki yüz kırk koşumluk akışta on beş görev yanlış bitiyor ve nedenleri on iki yanlış seçim ile üç dolaşmadan oluşuyor. Yalnız sonucu saklayan iz bu nedenlerin 0,0000'ini, araç adlarını sayan iz 0,4000'ini, tam gözlemi saklayan iz 1,0000'ini veriyor; saklanan belirteç 2738'den 5954'e ve 13623'e çıkıyor, bulunan neden başına belirteç ise 992,3'ten 908,2'ye iniyor. Altmış bir araç hatasının 0,8689'u doğru biten koşumlarda oluşuyor, yani araç hatası sayacı bir neden değil bir belirtidir.

Bir önceki iki ders ajanı sınadı. Araç düzeyi sınama beş araca konan kusurları tek tek yokladı,
uçtan uca akış sınaması onun kaçırdıklarını yakaladı ve ikisinin de göremediği bir kalıntı kaldı.
Her ikisinin de ortak bir kısıtı var: **sınanan koşumu biz kurduk**. Hangi görevin çalıştığını,
hangi aracın bozulduğunu ve doğru yanıtın ne olduğunu önceden biliyorduk.

Üretimde koşan bir döngüde bunların hiçbiri elimizde değildir. Görev bir aboneden gelir, ajan
kendi adımlarını seçer, sonuç bir kayda yazılır ve geriye yalnız **sakladığımız kadarı** kalır. Bu
dersin ölçtüğü şey ajanın kendisi değil, **izin geri okuttuğudur**: bir görev yanlış bittiğinde,
sakladığımız kayıt onun **neden** yanlış bittiğini söyleyebiliyor mu. Gözlemlenebilirliğin genel
yordamı Gözlemlenebilirlik ve Güvenilirlik kursunda ve Gözlemlenebilirlik ve İşletim kursunda
kuruldu; burada tekrarlanmaz. Ölçülen tek şey **adım izinin taşıdığı
bilgi ile bedeli** arasındaki alışveriştir.

- **KL31.** Ortam, araçlar, görev kümesi ve ajan döngüsü **kurgudur** ve aşağıda ders içinde
  tanımlanır; gerçek bir model uç noktası, araç sunucusu ya da çalışma zamanı çağrılmaz. Tohum
  **20260218**, ikinci koşum **20260219**.
- **KL32. Üretim akışı**, aynı yirmi dört görevin **on kez** koşmasıdır. Model belirsiz bir
  bileşen olduğu için her koşumda ajanın çekilişi ayrıdır; toplam **240 koşum** olur. Adım bütçesi
  **6**, yani `ajan-mimarisi/04`'ün süpürmesinde en iyi çıkan değerdir.
- **KL33. Üç iz düzeyi** şunları saklar. **Yalnız sonuç**: görev adı, son yanıt ve döngünün durup
  durmadığı. **Araç adları**: bunlara ek olarak hangi aracın kaç kez çağrıldığı — **sıra
  saklanmaz**, çünkü bu düzey bir sayaçtır. **Tam gözlem**: bunlara ek olarak çağrıların **sırası
  ve her çağrının döndürdüğü gözlemin tamamı**.
- **KL34. Neden**, izdeki **ilk sapmadır** ve tam gözlemden okunur: plan bitmeden plan dışı bir
  araç çağrıldıysa **yanlış seçim**, plan bittikten sonra çağrı sürdüyse **dolaşma**, plandaki araç
  sırasında hata döndürdüyse **araç hatası**, hiç sapma yokken plan bitmediyse **bütçe**.
- **KL35.** Bir iz düzeyi bir nedeni ancak **sakladığı alanlar onu belirliyorsa** verir; belirsiz
  kaldığı yerde neden **bulunamadı** sayılır ve yanlış tahmin doğru sayılmaz.
- **KL36.** Saklanan belirteç, kaydın ortak belirteçleyiciden geçirilmesiyle sayılır. Bu kursta iz
  için kullanılan terim **adım izidir**.

#### Üretim Akışı ve Ortak Tanım

Aşağıdaki blok kursun ortak tanımını kurar: ölçüm ağının kurgu abone kayıtları, kurgu belge
dağarcığı, beş araç, yirmi dört görev ve ajan döngüsü. Araç seçimi derinlikle bozulur, iş bitince
ajan 0,88 payla durur, şema dışı alanlar süzülür ve eksik alan aracın kendi hatasıdır.

```python
# KURGUDUR. Olcum aginin abone kayitlari, belge dagarcigi, bes arac ve yirmi dort
# gorev ders icinde tanimlanir; 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)}
```

#### İzin Geri Okuttuğu

Üç düzeyin her biri için aynı akış koşar, kayıtlar kurulur ve başarısız görevlerin nedeni o
düzeyin sakladığı alanlardan çıkarılmaya çalışılır. Tam gözlem düzeyini kurmak için araç çağrısı
sarmalanır: dönen sözlüğün tamamı günlüğe düşer.

```python
# Uretim akisi: ayni gorev kumesi on kez kosar, ajanin cekilisi her kosumda ayridir.
BUTCE, KOSUM = 6, 10
GUNLUK = []
_ham_cagir = cagir_arac


def cagir_arac(ad, durum):                 # tam gozlem duzeyi: donen alanlar da saklanir
    s = _ham_cagir(ad, durum)
    GUNLUK.append((ad, dict(s)))
    return s


def neden(g, iz):
    """Tam gozlemden okunan neden: izdeki ilk sapma."""
    k = 0
    for ad, gz in iz:
        if k >= len(g["dizi"]):
            return "dolasma"
        if ad != g["dizi"][k]:
            return "yanlis secim"
        if "hata" in gz:
            return "arac hatasi"
        k += 1
    return "butce" if k < len(g["dizi"]) else "bilinmiyor"


def tani_ad(g, say):
    """Ad duzeyi yalniz sayac tutar: sira da gozlem de yok."""
    if not [a for a in say if a not in g["dizi"]]:
        return None
    if [a for a in g["dizi"] if say.get(a, 0) == 0]:
        return "yanlis secim"
    if all(say[a] == g["dizi"].count(a) for a in set(g["dizi"])):
        return "dolasma"
    return None


ozet = {}
for taban in (20260218, 20260219):
    bulunan, saklanan, basarisiz, dagilim, hata, hata_dogru = [0, 0, 0], [0, 0, 0], 0, {}, 0, 0
    for d in range(KOSUM):
        for g in GOREV:
            GUNLUK.clear()
            s = ajan(g, en_cok_adim=BUTCE, tohum=taban + 7 * d)
            iz = list(GUNLUK)
            say = {}
            for a, _ in iz:
                say[a] = say.get(a, 0) + 1
            k1 = {"ad": g["ad"], "yanit": s["yanit"], "bitti": s["bitti"]}
            kayit = (k1, dict(k1, sayac=say), dict(k1, iz=iz))
            for i in range(3):
                saklanan[i] += len(belirtecle(kayit[i]))
            hata += s["hata"]
            if s["yanit"] == dogru_yanit(g):
                hata_dogru += s["hata"]
                continue
            basarisiz += 1
            gercek = neden(g, iz)
            dagilim[gercek] = dagilim.get(gercek, 0) + 1
            bulunan[1] += tani_ad(g, say) == gercek
            bulunan[2] += neden(g, iz) == gercek
    ozet[taban] = (bulunan, saklanan, basarisiz)
    n = " , ".join(f"{k} {v}" for k, v in sorted(dagilim.items()))
    print(f"tohum {taban} , {KOSUM * len(GOREV)} kosum , basarisiz {basarisiz} , neden: {n}")
    print(f"  arac hatasi {hata} , bunun dogru biten kosumdaki payi {hata_dogru / hata:.4f}")
print("\niz duzeyi     neden bulunan pay   saklanan belirtec   bulunan neden basina")
for i, ad in enumerate(("yalniz sonuc", "arac adlari", "tam gozlem")):
    a, b = ozet[20260218], ozet[20260219]
    ba = f"{a[0][i] / a[2]:.4f} {b[0][i] / b[2]:.4f}"
    bel = f"{a[1][i]:>6} {b[1][i]:>6}"
    bas = " ".join(f"{x[1][i] / x[0][i]:>7.1f}" if x[0][i] else "      -" for x in (a, b))
    print(f"{ad:<14} {ba}      {bel}      {bas}")
```

```
tohum 20260218 , 240 kosum , basarisiz 15 , neden: dolasma 3 , yanlis secim 12
  arac hatasi 61 , bunun dogru biten kosumdaki payi 0.8689
tohum 20260219 , 240 kosum , basarisiz 11 , neden: dolasma 3 , yanlis secim 8
  arac hatasi 66 , bunun dogru biten kosumdaki payi 0.9242

iz duzeyi     neden bulunan pay   saklanan belirtec   bulunan neden basina
yalniz sonuc   0.0000 0.0000        2738   2739            -       -
arac adlari    0.4000 0.2727        5954   6043        992.3  2014.3
tam gozlem     1.0000 1.0000       13623  14486        908.2  1316.9
```

Yalnız sonucu saklayan iz **hiçbir** nedeni vermiyor. Bu düzey başarısızlığı **sayabilir**: on beş
görevin yanlış bittiğini bilir. Neden sorulduğunda elinde yalnız yanıt ile durma bayrağı vardır ve
bu iki alan, aynı yanlış yanıtı üreten iki ayrı yolu birbirinden ayırmaz. Bir gösterge tablosu bu
düzeyle kurulabilir; bir arıza bu düzeyle kapatılamaz.

Araç adlarını sayan iz nedenlerin **0,4000'ini** veriyor. Bu düzeyin bildiği şey hangi aracın kaç
kez çağrıldığıdır; sıra yoktur. Sayaçta plan dışı bir araç görünüyorsa ve plandaki araçlardan biri
**hiç** çağrılmamışsa plan tamamlanmamıştır, yani sapma yanlış seçimdir. Plan dışı araç var ama
plandaki araçların hepsi tam birer kez çağrılmışsa plan bitmiş ve fazladan çağrı sonradan gelmiştir,
yani dolaşmadır. Bu iki durumun dışında sayaç iki senaryoyu **ayırt edemez**: aynı sayaç hem plan
ortasında yapılmış bir yanlış seçimden hem de plan bittikten sonraki bir dolaşmadan çıkabilir. O
koşumlarda neden bulunamadı sayılır.

Tam gözlem düzeyi nedenlerin **hepsini** veriyor, çünkü sıra ile birlikte her çağrının döndürdüğü
gözlem de saklanmıştır ve plan işaretçisi birebir yeniden yürütülebilir. Bedeli, saklanan
belirtecin 2738'den **13623'e** çıkmasıdır — beş katı.

Son sütun sıralamayı değiştiriyor. **Bulunan neden başına belirteç** okunduğunda araç adları düzeyi
992,3, tam gözlem düzeyi **908,2**'dir. Toplamda beş kat pahalı olan düzey, işe yarayan çıktı
birimi başına **daha ucuzdur**, çünkü ucuz düzeyin sakladığı belirtecin çoğu hiçbir soruyu
kapatmıyor. İkinci koşumda aynı okuma daha da keskindir: 2014,3'e karşı 1316,9.

#### Araç Hatası Bir Neden Değil, Belirtidir

Akışta **61 araç hatası** var ve bunların **0,8689'u doğru biten koşumlarda** oluşuyor. Araç hatası
sayacına konan bir uyarı altmış bir kez çalar, elli üçü sorunsuz biten bir görev içindir. Ters
yönde de eksiktir: nedenler tablosunda **araç hatası sınıfı hiç görünmez**. Mekanizma bunu
açıklıyor. Plandaki araç sırası geldiğinde girdisi zaten durumda hazırdır ve hata döndürmez; hata
ancak **yanlış seçilmiş** bir araç şemasının istediği alanı bulamadığında çıkar. Yani araç hatası
her zaman bir yanlış seçimin **ardından** gelir ve o seçim düzeltilmeden hata sayısına bakmak
insanı aracı düzeltmeye götürür.

İki koşum bandı bu okumanın sınırını da çiziyor. Araç adları düzeyi bir koşumda 0,4000, öbüründe
0,2727 veriyor; aradaki **0,1273** ölçüm bandıdır. Bu düzeyin tam gözlemden **0,6000** geride
olması banda göre büyüktür ve bir bulgudur. Ama iki koşum arasında bu düzeyin kendi değerinin ne
kadar oynadığı, "araç adları nedenlerin şu kadarını verir" biçimindeki bir iddianın **tek başına
ölçülmemiş** olduğunu söyler; ölçülen şey düzeyler arasındaki sıradır.

#### Özet

- Üretim akışı yirmi dört görevin on koşumudur; 240 koşumun 15'i yanlış biter ve nedenleri 12
  yanlış seçim ile 3 dolaşmadır.
- Yalnız sonuç düzeyi nedenlerin 0,0000'ini, araç adları düzeyi 0,4000'ini, tam gözlem düzeyi
  1,0000'ini verir.
- Bedel 2738, 5954 ve 13623 belirteçtir; bulunan neden başına okunduğunda sıralama tersine döner
  ve tam gözlem 908,2 ile araç adlarının 992,3'ünün altına iner.
- 61 araç hatasının 0,8689'u doğru biten koşumlardadır ve nedenler tablosunda araç hatası sınıfı
  hiç görünmez: hata sayacı bir belirtidir, neden değildir.
- Araç adları düzeyinin iki koşum arasındaki farkı 0,1273'tür; bu düzeyin tek başına değeri
  ölçülmemiş, düzeyler arasındaki sıra ölçülmüş sayılır.

#### Sonraki Adım

İz, bir görevin neden yanlış bittiğini geri okutuyor ve bunun bir belirteç bedeli var. Ama bu
bedel, döngünün ödettiği bedelin yalnız bir kalemidir. Ajan her adımda bir çağrı yapar, her çağrı
belirteç harcar ve adım sayısı görevden göreve değişir. Sonraki ders bu harcamayı iki ayrı
paydayla okur: **adım başına** ve **doğru yanıt başına**. İki sıralamanın aynı olmadığı bir nokta
vardır ve o noktada hangi düzeneğin ucuz sayılacağı paydanın seçimine bağlıdır.

### Maliyet ve Gecikme Denetimi

Source: https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/maliyet-ve-gecikme-denetimi

Bir ajan döngüsünün harcamasının iki ayrı paydayla okunması: tek adımlı araçsız çağrı adım başına 90,00 maliyet birimiyle en ucuzudur ama başarımı 0,0000 olduğu için doğru yanıt başına maliyeti tanımsızdır. Büyük ve küçük model sınıfı üç adım bütçesiyle koşturulduğunda küçük sınıf adım başına 79,25 ile büyük sınıfın 118,30'unun altında kalırken doğru yanıt başına 488,0 ile onun 453,5'inin üstüne çıkıyor. İki sıralamanın ayrıştığı bant birim fiyat oranıyla verilir: adım başına eşitlenme 0,8957, doğru yanıt başına eşitlenme 0,5576, arada kalan her fiyat oranında iki ölçüt ters yanıt verir. Gecikme sınıf olarak yazılır ve en uzun koşumdan okunur; ham süre ölçülmez.

Bir önceki ders izin bedelini belirteçle ödedi: tam gözlem düzeyi 13623 belirteç sakladı ve
karşılığında başarısız görevlerin nedenini geri okuttu. O bedel, döngünün ödettiği harcamanın
yalnız bir kalemidir. Asıl harcama döngünün kendisindedir: her adım bir istem taşır, taşınan istem
önceki gözlemlerle büyür ve adım sayısı görevden göreve değişir.

Bu dersin sorusu şudur: bir düzeneğin pahalı olup olmadığını **neye bölerek** söylüyoruz. Model
API'leri ile Uygulama Geliştirme kursundaki maliyet dersi belirteç muhasebesini istek başına
kurmuş ve paydanın değişmesiyle sıralamanın değiştiğini orada da göstermişti; o yordam burada
tekrarlanmaz. Burada payda **adım** ile **doğru yanıttır**, bölünen şey tek bir çağrının değil
**bütün döngünün** harcamasıdır ve karşılaştırılan şeyler adım bütçesi ile model sınıfıdır.

- **KL41.** Ortam, araçlar, görev kümesi ve ajan döngüsü **kurgudur**; gerçek bir model uç noktası
  ya da araç sunucusu çağrılmaz. Tohum **20260218**, ikinci koşum **20260219**.
- **KL42. Taban çizgisi tek adımlı, araçsız çağrıdır**: ajan hiçbir araç çağırmaz, tek adımda yanıt
  verir. Belirteci döngünün ilk adımının belirtecidir (istem 60, yanıt 30). Hiçbir gözlem duruma
  girmediği için başarımı **0,0000**'dır.
- **KL43. Model sınıfı** doğru araç seçme payını ölçekler. **Büyük sınıfın** çarpanı 1,00'dir, yani
  ortak tanımın kendisidir; **küçük sınıfın** çarpanı **0,60**'tır ve pay 0,20 ile 0,95 arasına
  kırpılır. Sınıflar tür adıyla anılır; hiçbir model, sürüm ya da sağlayıcı adı geçmez.
- **KL44. Maliyet birimi** belirteç başına ödenen göreli bedeldir. Büyük sınıfta 1,00 alınır;
  küçük sınıfın birim fiyatı bir **parametredir** ve tabloda **0,60** ile basılır.
- **KL45. Gecikme sınıf olarak yazılır ve en uzun koşumun adım sayısından okunur**: 1 adım **tek
  çağrı**, 2 ile 4 adım **kısa zincir**, 5 ile 8 adım **uzun zincir**, dokuz ve üstü **çok uzun
  zincir**. Hiçbir yerde ham süre ölçülmez; sınıfı belirleyen şey en kötü durumdaki zincirin
  uzunluğudur.
- **KL46. Eşitlenme oranı**, küçük sınıfın birim fiyatının hangi değerinde iki sınıfın ilgili
  ölçütte eşitlendiğidir. Seçilen fiyattan bağımsız türetilir ve tablonun kendisinden okunur.

#### Altı Düzenek, Tek Görev Kümesi

Aşağıdaki blok kursun ortak tanımını kurar: ölçüm ağının kurgu abone kayıtları, kurgu belge
dağarcığı, beş araç, yirmi dört görev ve ajan döngüsü.

```python
# KURGUDUR. Olcum aginin abone kayitlari, belge dagarcigi, bes arac ve yirmi dort
# gorev ders icinde tanimlanir; 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)}
```

Model sınıfı ortak tanıma tek bir yerden dokunur: doğru araç seçme payını ölçekleyen bir çarpan.
Çarpan 1,00 iken taban birebir korunur, yani büyük sınıf ortak tanımın kendisidir.

```python
# Model sinifi dogru arac secme payini olcekler; buyuk sinif ortak tanimin kendisidir.
CARPAN = [1.00]
_taban_gorme = gorme


def gorme(d):
    return max(0.20, min(0.95, _taban_gorme(d) * CARPAN[0]))


SINIF = {"buyuk": 1.00, "kucuk": 0.60}     # dogru arac secme payi carpani
FIYAT = {"buyuk": 1.00, "kucuk": 0.60}     # belirtec basina maliyet birimi
ARACSIZ_BEL = 60 + 30                      # tek adimli, aracsiz cagrinin belirteci


def gecikme(u):
    if u <= 1:
        return "tek cagri"
    return "kisa zincir" if u <= 4 else ("uzun zincir" if u <= 8 else "cok uzun zincir")


def kosum(sn, b, tohum):
    CARPAN[0] = SINIF[sn]
    dogru, adim, bel, uzun = 0, 0, 0, 0
    for g in GOREV:
        s = ajan(g, en_cok_adim=b, tohum=tohum)
        adim += s["adim"]
        bel += s["belirtec"]
        uzun = max(uzun, s["adim"])
        dogru += s["yanit"] == dogru_yanit(g)
    return dogru, adim, bel, uzun


for tohum in (20260218, 20260219):
    n = len(GOREV)
    print(f"tohum {tohum}")
    print("duzenek        basarim  adim  belirtec  maliyet  adim basina  dogru basina  gecikme")
    print(f"{'araci yok':<14} {0.0:>8.4f} {n:>5} {n * ARACSIZ_BEL:>9} {n * ARACSIZ_BEL:>8.1f}"
          f" {ARACSIZ_BEL:>12.2f} {'tanimsiz':>13}  {gecikme(1)}")
    veri = {}
    for sn in ("buyuk", "kucuk"):
        for b in (4, 6, 12):
            dogru, adim, bel, uzun = kosum(sn, b, tohum)
            veri[(sn, b)] = (dogru, adim, bel)
            m = bel * FIYAT[sn]
            print(f"{sn + ' butce ' + str(b):<14} {dogru / n:>8.4f} {adim:>5} {bel:>9} {m:>8.1f}"
                  f" {m / adim:>12.2f} {m / dogru:>13.1f}  {gecikme(uzun)}")
    print("butce  esitlenme orani adim basina  dogru yanit basina")
    for b in (4, 6, 12):
        db, ab, bb = veri[("buyuk", b)]
        dk, ak, bk = veri[("kucuk", b)]
        print(f"{b:>5} {(bb / ab) / (bk / ak):>28.4f} {(bb / db) / (bk / dk):>20.4f}")
```

```
tohum 20260218
duzenek        basarim  adim  belirtec  maliyet  adim basina  dogru basina  gecikme
araci yok        0.0000    24      2160   2160.0        90.00      tanimsiz  tek cagri
buyuk butce 4    0.8750    84      9402   9402.0       111.93         447.7  kisa zincir
buyuk butce 6    1.0000    92     10884  10884.0       118.30         453.5  uzun zincir
buyuk butce 12   0.9583    98     12214  12214.0       124.63         531.0  cok uzun zincir
kucuk butce 4    0.5417    90     10624   6374.4        70.83         490.3  kisa zincir
kucuk butce 6    0.7917   117     15454   9272.4        79.25         488.0  uzun zincir
kucuk butce 12   1.0000   142     21762  13057.2        91.95         544.0  cok uzun zincir
butce  esitlenme orani adim basina  dogru yanit basina
    4                       0.9482               0.5478
    6                       0.8957               0.5576
   12                       0.8132               0.5857
tohum 20260219
duzenek        basarim  adim  belirtec  maliyet  adim basina  dogru basina  gecikme
araci yok        0.0000    24      2160   2160.0        90.00      tanimsiz  tek cagri
buyuk butce 4    0.8333    83      9366   9366.0       112.84         468.3  kisa zincir
buyuk butce 6    0.9583    96     11610  11610.0       120.94         504.8  uzun zincir
buyuk butce 12   0.9583    98     11994  11994.0       122.39         521.5  uzun zincir
kucuk butce 4    0.3750    89     10550   6330.0        71.12         703.3  kisa zincir
kucuk butce 6    0.7917   121     16396   9837.6        81.30         517.8  uzun zincir
kucuk butce 12   0.8750   140     20526  12315.6        87.97         586.5  cok uzun zincir
butce  esitlenme orani adim basina  dogru yanit basina
    4                       0.9519               0.3995
    6                       0.8925               0.5850
   12                       0.8348               0.5335
```

#### Ucuz Olan Neye Göre Ucuz

İlk satır soruyu tek başına kuruyor. Araçsız çağrı **adım başına 90,00** ile büyük sınıfın bütün
düzeneklerinden ucuzdur; 111,93, 118,30 ve 124,63'ün hepsinin altındadır. Doğru yanıt başına
maliyeti ise **tanımsızdır**, çünkü payda sıfırdır. Adım başına maliyet sıralamasının en tepesinde
hiçbir işi bitirmeyen düzenek durur. Bu, bir ölçüt hatası değil, paydanın ne saydığının sonucudur:
adım başına maliyet **adımın ucuzluğunu** ölçer, işin bitip bitmediğini ölçmez.

Aynı sınıf içinde bütçeyi büyütmek adım başına maliyeti düzenli olarak yükseltiyor: büyük sınıfta
111,93, 118,30, 124,63. Sebep döngünün belirteç muhasebesindedir; her adım kendinden önceki
gözlemleri de taşır, bu yüzden geç adımlar erken adımlardan pahalıdır. Doğru yanıt başına maliyet
ise 447,7, 453,5 ve 531,0'dir. İlk iki değer arasındaki 5,8'lik fark, aynı satırın iki koşum
arasındaki farkından (447,7'ye karşı 468,3, yani 20,6) küçüktür; bütçe 4 ile bütçe 6 arasındaki
sıralama bu ölçütte **ölçülmemiş sayılır**. Bütçe 12'nin 531,0'i ise banda göre büyüktür ve
gerçek bir kötüleşmedir.

#### İki Sıralamanın Ayrıştığı Bant

Asıl ayrışma sınıflar arasındadır. Birim fiyat 0,60 alındığında küçük sınıf bütçe 6'da adım başına
**79,25** ile büyük sınıfın **118,30**'unun belirgin altındadır. Aynı iki satır doğru yanıt başına
okunduğunda sıra ters döner: **488,0**'e karşı **453,5**. Küçük sınıf ucuz adımlar üretir ama daha
çok adım harcar (117'ye karşı 92) ve daha az görev bitirir (0,7917'ye karşı 1,0000). İkinci koşum
aynı yönü verir: 81,30'a karşı 120,94 ve 517,8'e karşı 504,8.

Bu ayrışma seçilen fiyata bağlı görünebilir, ama bağlı olmadığı tablonun son bölümünden okunur.
Bütçe 6'da adım başına eşitlenme oranı **0,8957**, doğru yanıt başına eşitlenme oranı
**0,5576**'dır. Küçük sınıfın birim fiyatı 0,5576'nın altındaysa iki ölçüt de küçük sınıfı ucuz
sayar; 0,8957'nin üstündeyse iki ölçüt de büyük sınıfı ucuz sayar. **Arada kalan her fiyat
oranında iki ölçüt ters yanıt verir** ve hangisinin ucuz olduğu sorusunun tek bir doğru cevabı
yoktur. İkinci koşum bandı 0,5850 ile 0,8925 arasına koyuyor; iki koşumun üst kenarı 0,0032 ile
örtüşüyor, alt kenarı 0,0274 oynuyor. Bant bu yüzden yaklaşık olarak **0,56 ile 0,89** arasıdır.
Bütçe 4'ün alt kenarı ise 0,5478 ile 0,3995 arasında oynuyor; o satırın bandı **ölçülmemiş
sayılır**.

Gecikme sütunu ayrı bir uyarı taşıyor. Sınıf en uzun koşumdan okunur ve büyük sınıf bütçe 12 bir
koşumda **çok uzun zincir**, öbüründe **uzun zincir** çıkıyor. Ortalama adım sayısı iki koşumda
neredeyse aynıdır; oynayan şey tek bir görevin en kötü durumudur. Bir gecikme bütçesi en kötü
duruma göre konuyorsa, aynı düzeneğin sınıfı iki koşum arasında değişebilir ve bu değişim bir
iyileşme ya da kötüleşme diye okunamaz.

#### Özet

- Tek adımlı araçsız çağrı adım başına 90,00 ile en ucuz düzenektir ve doğru yanıt başına maliyeti
  tanımsızdır; adım başına ölçüt işin bitip bitmediğini saymaz.
- Büyük sınıfta bütçe büyüdükçe adım başına maliyet 111,93'ten 124,63'e çıkar, çünkü geç adımlar
  önceki gözlemleri de taşır.
- Birim fiyat 0,60'ta küçük sınıf adım başına 79,25 ile ucuz, doğru yanıt başına 488,0 ile
  pahalıdır; büyük sınıfın karşılıkları 118,30 ve 453,5'tir.
- Eşitlenme oranları iki ölçütün ayrıştığı bandı verir: bütçe 6'da adım başına 0,8957, doğru yanıt
  başına 0,5576; arada kalan her fiyatta iki ölçüt ters yanıt verir.
- Gecikme en uzun koşumdan okunan bir sınıftır ve aynı düzenek iki koşumda iki ayrı sınıfa
  düşebilir; ham süre hiçbir yerde ölçülmez.

#### Sonraki Adım

Maliyet tablosunun bütün satırlarında bir sütun sessiz kaldı: araç hatası. Döngü bir araç hata
döndürdüğünde ne yapacağını bu derste hiç sormadık; ortak tanımın döngüsü hatayı görüp aynı hedefe
yeniden yöneliyordu ve bunu bir **politika** olarak yazmamıştık. Sonraki ders bu boşluğu ölçer: ilk
hatada durmak, aynı aracı yeniden denemek ve başarısız aracı bir daha seçmemek üç ayrı politikadır
ve üçü başarımı, adımı ve araç hatasını ayrı yönlere çeker. O ders aynı zamanda kursun kapanışıdır.

### Hata Kurtarma

Source: https://academia.sh/tr/kurslar/yapay-zeka-ajanlari/hata-kurtarma

Başarısız bir araç çağrısının döngüye ne yaptığının üç kurtarma politikasıyla ölçülmesi: iki yüz kırk koşumluk üretim akışında ilk hatada durmak 0,7500 görev başarımı, 727 adım, 53 araç hatası ve 81996 belirteç veriyor; aynı aracı yeniden denemek başarımı 0,9375'e çıkarıyor ve adımı 900'e, hatayı 61'e, belirteci 106352'ye taşıyor. Başarısız aracı bırakıp başkasına geçmek beklenenin tersini yapıyor: araç hatası 64'e, adım 907'ye çıkıyor ve başarım 0,8917'ye düşüyor. Durdurmanın kazandırdığı adım verimliliği iki koşum bandının içinde kalıyor. Ders M28/K05 kursunun kapanışıdır ve yirmi iki dersin düzeneksiz ile düzenekli sayılarını tek tabloda toplar.

Bir önceki dersin maliyet tablosunda bir sütun baştan sona sessiz kaldı: araç hatası. Altı
düzenekte de hata oluştu, hiçbirinde ne yapılacağı yazılmadı. Ortak tanımın döngüsü hata
gördüğünde aynı hedefe yeniden yöneliyor; bu bir davranıştır ama politika değildir, çünkü
seçilmemiştir. Yeniden deneme, geri çekilme ve devre kesici yordamı Dayanıklılık ve Güvenilirlik
kursunda ölçüldü; tekrarlanmaz. Ölçülen tek şey **başarısız aracın döngüye ne yaptığıdır**.

- **KL51.** Ortam, araçlar, görevler ve döngü **kurgudur**. Üretim akışı bir önceki dersinkiyle
  aynıdır: yirmi dört görev **on kez** koşar, bütçe **6**, toplam **240 koşum**. Tohum
  **20260218** ve **20260219**.
- **KL52. Durdur**: ilk hatada döngü biter, taban budur. **Yinele**: hedef değişmez, bir sonraki
  adımda aynı hedefe yeniden yönelinir. **Geç**: hata döndüren araç koşumun geri kalanında
  **seçilemez** olur; çekiliş onu verirse listedeki bir sonraki araç kullanılır.
- **KL53.** Kümede aynı işi gören ikinci araç yoktur; "başka araca geçmek" başarısız aracı
  **bırakmak** demektir ve plan onu gerektiriyorsa tamamlanamaz.

#### Üç Politika, Tek Akış

Blok ortak tanımı kurar: kurgu ortam, beş araç, yirmi dört görev ve döngü.

```python
# KURGUDUR. Olcum aginin abone kayitlari, belge dagarcigi, bes arac ve yirmi dort
# gorev ders icinde tanimlanir; 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)}
```

Politika yalnız hata dalını değiştirir.

```python
# Kurtarma politikasi ortak dongunun yalniz hata dalini degistirir.
BUTCE, KOSUM = 6, 10
SONUC = {"tuketim": "m3", "tarife": "tutar", "belge_ara": "kod"}   # sonucu yazan araclar


def ajan_k(g, politika, en_cok_adim=BUTCE, tohum=TOHUM):
    r = uretec(tohum ^ izno(g["ad"]))
    alan = {"abone": g["abone"], "anahtar": g["istek"], "sutun": "bedel"}
    iz, adim, bel, hata, yasak = [], 0, 0, 0, set()
    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:
            if r() < 0.88:
                break
            sec = ARAC_ADI[int(r() * len(ARAC_ADI))]
        else:
            sec = hedef if r() < gorme(k + 1) else ARAC_ADI[int(r() * len(ARAC_ADI))]
        while sec in yasak and len(yasak) < len(ARAC_ADI):
            sec = ARAC_ADI[(ARAC_ADI.index(sec) + 1) % len(ARAC_ADI)]
        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 politika == "durdur":
                break
            if politika == "gec":
                yasak.add(sec)
            continue
        alan.update({kk: vv for kk, vv in s.items() if kk != "hata"})
        if sec in SONUC:
            alan["sonuc"] = s[SONUC[sec]]
        if sec == hedef:
            k += 1
    return {"adim": adim, "belirtec": bel, "hata": hata, "yanit": alan.get("sonuc")}


ALAN = ("adim", "belirtec", "hata", "yanit")
print("yinele ortak ajanla ayni mi:",
      all(ajan_k(g, "yinele") == {a: ajan(g, BUTCE, TOHUM)[a] for a in ALAN} for g in GOREV))
print("\ntohum     politika  basarim  adim  hata  belirtec  dogru yanit basina adim")
for taban in (20260218, 20260219):
    for p in ("durdur", "yinele", "gec"):
        dogru, adim, bel, hata = 0, 0, 0, 0
        for d in range(KOSUM):
            for g in GOREV:
                s = ajan_k(g, p, BUTCE, taban + 7 * d)
                adim += s["adim"]
                bel += s["belirtec"]
                hata += s["hata"]
                dogru += s["yanit"] == dogru_yanit(g)
        n = KOSUM * len(GOREV)
        print(f"{taban}  {p:<8} {dogru / n:>8.4f} {adim:>5} {hata:>5} {bel:>9}"
              f" {adim / dogru:>23.2f}")
```

```
yinele ortak ajanla ayni mi: True

tohum     politika  basarim  adim  hata  belirtec  dogru yanit basina adim
20260218  durdur     0.7500   727    53     81996                    4.04
20260218  yinele     0.9375   900    61    106352                    4.00
20260218  gec        0.8917   907    64    108030                    4.24
20260219  durdur     0.7667   768    54     89180                    4.17
20260219  yinele     0.9542   944    66    114430                    4.12
20260219  gec        0.8625   954    73    116776                    4.61
```

#### Politikanın Kattığı ve Ödettiği

Çıktının ilk satırı bir doğrulamadır: kurs boyunca kullandığımız döngüde yazılmamış bir politika
vardı, o da **yinele**'ydi.

**Durdur** bütün bedel sütunlarında en ucuzudur: 727 adım, 53 araç hatası, 81996 belirteç. Başarımı
yinelenin 0,9375'inin **0,1875** altındadır; iki koşum farkı 0,0167 olduğundan düşüş bandın on
katıdır. Doğru yanıt başına okunduğunda tasarruf kayboluyor: durdur 4,04, yinele 4,00 adım ve fark
durdurun kendi koşum farkının (4,04'e karşı 4,17) içindedir. Durmak adım **harcamasını** düşürür,
**verimini** değiştirmez.

**Geç**, kursun ikinci iddiasının örneğidir: sezgi hatanın azalmasını bekler, ölçüm tersini verir.
Araç hatası 61'den **64**'e, ikinci koşumda 66'dan **73**'e çıkıyor; adım 907'ye yükseliyor,
başarım **0,8917**'ye düşüyor. Mekanizma iki yönlüdür: yasaklanan araç çoğu zaman zaten yanlış
seçilmiştir ve yerine geleni de yanlıştır, yani hata başka bir ada kayar; plandaki bir araç
yasaklanırsa plan biter. Kayıp iki koşumda 0,0458 ve 0,0917'dir; en geniş band 0,0292
olduğundan ikisi de üstünde, ama ilki bandın bir buçuk katı.

#### Özet

- Kurtarma politikası döngünün hata dalıdır; yazılmasa da bir politika koşar, burada koşan
  yineledir.
- Durdur en ucuzudur (727 adım, 53 hata, 81996 belirteç), başarımı 0,1875 düşürür, adım
  verimliliğini iyileştirmez.
- Başarısız aracı bırakmak araç hatasını 64'e çıkarır ve başarımı 0,8917'ye düşürür.

#### Kurs Kapanışı

| Ders | Düzeneksiz döngü | Düzenekli döngü | Bedel |
|---|---|---|---|
| `ajan-mimarisi/01` — başarım | araçsız 0,0417 | döngü 0,9583 (**+0,9166**) | adım 24'ten 97'ye |
| `ajan-mimarisi/02` — doğru seçim payı | derinlik 1: 0,8000 | derinlik 4: 0,5000 (**−0,3000**) | adım 2,86'dan 7,67'ye |
| `ajan-mimarisi/03` — başarım | tek döngü 0,9583 | planlayıcı–yürütücü 0,8750 (**−0,0833**) | adım 97'den 118'e |
| `ajan-mimarisi/04` — başarım | bütçe 2: 0,3750 | bütçe 6: 1,0000 (**+0,6250**) | belirteç 4758'den 10884'e |
| `ajan-mimarisi/05` — başarım (24) | tursuz 21/24 | öz eleştiri 23/24 (**+2**) | adım 84'ten 152'ye |
| `ajan-mimarisi/06` — başarım (24) | tek ajan 23/24 | iki ajan 23/24 (**0**) | adım 97'den 140'a |
| `ajan-mimarisi/07` — yanlış kayıt | onay kapalı 3 | yan etkide onay 1 (**−2**) | 7 onay noktası |
| `araclar-ve-bellek/01` — geçerli çağrı oranı | yalnız ad 0,2803 | şema 0,8919 (**+0,6116**) | araç hatası 113'ten 8'e |
| `araclar-ve-bellek/02` — gözlem belirteci | ham 770 | özet 401 (**−369**) | başarım aynı: 0,9583 |
| `araclar-ve-bellek/03` — kod görevi başarımı | gevşek sınır 0,7778 | orta sınır 0,3333 (**−0,4445**) | 24 durdurulan çağrı |
| `araclar-ve-bellek/04` — tamamlanan görev | tam yetki 0,9583 | izin listesi 0,8333 (**−0,1250**) | 20 durdurulan çağrı |
| `araclar-ve-bellek/05` — doğru yanıt | dış bilgisiz 0,2083 | birincil kaynak 0,7500 (**+0,5417**) | çağrı 91'den 121'e |
| `araclar-ve-bellek/06` — doğru yanıt | sınırsız pencere 0,9583 | pencere 120: 0,7083 (**−0,2500**) | belirteç 9316'dan 12026'ya |
| `araclar-ve-bellek/07` — kazanılan adım (48) | belleksiz 0 | seçici yazma 118 (**+118**) | 24 kayıt |
| `araclar-ve-bellek/08` — toplam belirteç | sıkıştırmasız 21913 | seçici 16569 (**−5344**) | alanı atılan görev 8 |
| `araclar-ve-bellek/09` — bayat okuma payı | eşiksiz 0,4528 | eşik 1: 0,0000 (**−0,4528**) | atılan geçerli kayıt 0,9701 |
| `ajan-kalitesi/01` — başarım | araçsız taban 0,2917 | kurs bütçesi 0,9583 (**+0,6666**) | yanıt başına 3,4286'dan 4,2174'e |
| `ajan-kalitesi/02` — yakalanan kusur (11) | sınamasız 0 | araç düzeyi 7 (**+7**) | tanım kusurlarında 0,0000 |
| `ajan-kalitesi/03` — yakalanan kusur (11) | araç düzeyi 7 | iki katman 10 (**+3**) | kalıntı `D11` |
| `ajan-kalitesi/04` — nedeni bulunan pay | yalnız sonuç 0,0000 | tam gözlem 1,0000 (**+1,0000**) | belirteç 2738'den 13623'e |
| `ajan-kalitesi/05` — adım başına maliyet | büyük sınıf 118,30 | küçük sınıf 79,25 (**−39,05**) | yanıt başına 453,5'ten 488,0'e |
| `ajan-kalitesi/06` — başarım (240 koşum) | durdur 0,7500 | yeniden dene 0,9375 (**+0,1875**) | adım 727'den 900'e |

Kursun kuralı tabloda satır satır yazılıdır: **bir ajanın sayısı tek bir yanıtın değil bütün
döngünün sayısıdır ve durma koşulu yazılmayan ajan ölçülmemiş sayılır.** Düzeneksiz sütunundaki
sayılar kötü bir model yüzünden düşük değildir; döngünün nerede duracağı, aracın kendini nasıl
tanıttığı, neyin hatırlanacağı ve bir adım bozulunca ne olacağı yazılmadığı için düşük çıkar.

**İkinci iddia da aynı tablodan okunur: eklenen adım işi kötüleştirebilir.** Adıyla yazan satırlar
şunlardır: `araclar-ve-bellek/02` (kırpma 0,9583'ü 0,2917'ye indirir, belirteci 15945'e
**çıkarır**), `/05` (ikincil sınıf 0,7500'ü 0,3750'ye indirir), `/06` (daraltma belirteci 12026'ya
çıkarır), `/07` (yalnız sonuç 0,9167'yi 0,5833'e indirir), `/08` (en sıkı özetleme tabanın 1,53
katını harcar), `ajan-mimarisi/06` (ikinci ajan kazançsız), `/07` (kapsamı yaymak soruyu 74'e
çıkarır), `ajan-kalitesi/05` (küçük sınıf yanıt başına pahalı) ve bu ders (`geç` hatayı 64'e
çıkarır). Dokuz yerde eklenen adım, eklendiği için ödetti.

**Ölçüm bandının altında kalan farklar da adıyla yazılır ve bulgu sayılmazlar:**
`ajan-mimarisi/01`'de tabanın tek isabeti, `/02`'de komşu derinliklerin 0,0917 ve 0,0160'ı,
`/03`'te bölmenin 0,0833'ü, `/04`'te altıdan sekize 0,0417'lik düşüş, `/05`'te tur farkları,
`araclar-ve-bellek/06`'da 200 penceresindeki 0,0833, `/09`'da eşiği sekizin altına sıkmanın
kazancı ve bu derste durdurun adım verimliliği. Hiçbiri sıfır, hiçbiri ölçülmüş değildir.

Yirmi iki derste ajan kuruldu, donatıldı ve ölçüldü. Bir şey hiç sorulmadı: **her araç ders içinde
elle tanımlandı.** `sayac_oku`'nun adı, açıklaması ve şeması bu kursta yazıldı ve yalnız buradaki
ajan gördü. Aynı aracın başka bir ajana, başka bir ekibe ya da başka bir uygulamaya **nasıl
verileceği**, tanımın kimin elinde duracağı ve arayüz değiştiğinde kimin kırılacağı sorulmadı.
Sonraki kurs araç tanımını bir ders içi sözlükten **paylaşılan bir yüzeye** dönüştürür ve
bedelini sayar.

