Sertaç Yıldırım saha notları

Ana sayfa → Teknik

Agent Orchestrator: Zeki Tek Agent Değil, Yönlendirilmiş Akış

Salı 02:40. Gece mutabakat işini agent’a verdik: “dün bankadan gelen dosya ile bizim kayıtlar arasındaki farkı bul, sebebini araştır, sabaha özet bırak.” Sabah gelen özet üç satırdı ve yanlıştı. Log’a baktım: 17 tur, 9 dakika 12 saniye, aynı tool’a arka arkaya 4 çağrı. Model kötü değildi. İş kötü tarif edilmişti: ona bir görev değil, boş bir oda verdik.

Özet
  • Serbest agent, akışı da modele havale etmektir. Model her turda “şimdi ne yapsam” sorusunu yeniden cevaplar. 17 turluk koşuda 9 tur işi ilerletmedi; sadece durumu yeniden anlamaya çalıştı.
  • Orchestrator akışı yazar, modeli boşluklara koyar. Aynı işi 4 adıma böldük: veri çek (kod) → eşleştir (kod) → eşleşmeyenleri sınıflandır (model) → özet yaz (model). 17 tur → 2 tur, 9 dakika → 38 saniye.
  • Karar testi üç soru. Girdi sonlu mu? Kuralı bir cümlede yazabiliyor musun? Yanlışın bedeli geri alınabilir mi? İlk ikisi evet ise karar koda aittir; model orada sadece gecikme ve belirsizlik ekler.
  • Üç desen yeter. Zincir (sıra belli), yönlendirici (giriş tipi belirsiz), planlayıcı–işçi (adım sayısı girdiye bağlı). Dördüncüsünü icat etmeden önce üçünü dene.
  • Dört fren birlikte çalışır. Tur sınırı, token bütçesi, adım zaman aşımı, tekrar koruması. Biri eksikse diğer üçü de bir geceyi kurtarmıyor.
  • Akış yazılı değilse koşular karşılaştırılamaz. Aynı soruya iki farklı yol izleyen sistemde “dün neden yavaştı” sorusunun cevabı yoktur. Orchestrator’ın asıl ürünü hız değil, tekrarlanabilirliktir.

Agent döngüsü ne yapıyor?

Şirket içi agent yazısında üç parça vardı: karar veren model, çalıştıran MCP server, ikisini yöneten agent. Agent’ın işi bir döngüydü:

Agent loop, en yalın hâli
mesajlar = [system_prompt, kullanici_sorusu]
tur = 0
while tur < TUR_SINIRI:
    cevap = model.sor(mesajlar, tools)        # model karar verir
    if cevap.tool_calls:
        for cagri in cevap.tool_calls:
            sonuc = mcp.calistir(cagri)       # MCP calistirir
            mesajlar.append(sonuc)            # agent biriktirir
        tur += 1
        continue
    return cevap.metin                        # duz metin geldi: is bitti

Bu döngü rapor sorularında çalıştı: “bugün kaç dolar depozit oldu?” tek tool, tek tur, bitti. Sonra ondan yapmasını istedik. Mutabakat işi tek tool değil: dosya oku, kayıt çek, eşleştir, farkı sınıflandır, özet yaz. Beş adım. Döngüye tekrar baktığımda şunu fark ettim: adımların sırası hiçbir yerde yazmıyor. Sırayı her turda model seçiyor, üstelik önceki turları hatırlayarak değil, önüne konan mesaj yığınını baştan okuyarak.

Serbest agent’ta “sırayla şunları yap” demek, sırayı her turda yeniden zar atarak seçtirmektir.

Sahadan: 17 turluk gece

O gecenin log’unu tur tur çıkardım. 11 tool’u olan bir agent, tek bir istekle: “dünün mutabakat farkını bul, sebebini araştır, özet bırak.”

TurModel ne yaptıDeğerlendirme
1–2banka_dosyasi_oku, islem_listesiDoğru başlangıç
3islem_listesi yeniden, bu kez farklı tarih aralığıylaİlk aralık dünü kapsamıyordu; model fark etti, düzeltti. Değerli tur
4–7Eşleştirmeyi kendi kafasında yapmaya çalıştı; 2.300 satırı metin olarak taşıdıBoşa tur. Bu iş JOIN, model işi değil
8bakiye_getirİlgisiz. Mutabakat farkıyla ilgisi yok
9–12islem_detay 4 kez, 4 farklı referanslaİkisi aynı referans. Tekrar koruması olmadığı için ikisi de çalıştı
13–15Özet yazmaya başladı, yarıda kesip yeniden veri çektiContext dolmuştu; ilk turlar kaymıştı (ayrı yazının konusu)
16–17Üç satırlık özetRakamlar 4–7. turlardaki yarım eşleştirmeden geldi: yanlış

Toplam: 17 tur, 9 dakika 12 saniye, 11 tool çağrısı, ikisi birebir aynı. 9 tur işi ilerletmedi. Ertesi gece aynı istekle tekrar çalıştırdım: bu kez 12 tur ve başka bir yol. Cevap yine yanlıştı ama başka bir sebepten. Beni asıl rahatsız eden bu oldu: iki koşu karşılaştırılamıyordu. Düzeltecek bir şey yoktu, çünkü düzeltilecek bir akış yoktu.

Kararı kim vermeli: model mi, kod mu?

Akışı yazmaya oturduğumda her adım için tek soru sordum: bu kararı model mi vermeli? Soru zamanla üç maddelik bir teste dönüştü.

SoruEvet iseHayır ise
Girdi sonlu ve tanımlı mı? (tarih, tutar, enum)KodModel adayı
Kuralı bir cümlede yazabiliyor musun?KodModel adayı
Yanlış kararın bedeli geri alınabilir mi?Model çalışabilirModel + insan onayı

Mutabakat işinde bu test şunu verdi:

  • “Dün” hangi tarih aralığı? Sonlu, kuralı tek cümle: iş günü 00:00–23:59, Europe/Istanbul. Kod. O gecenin 3. turu bu yüzden vardı; kodda olsa hiç olmayacaktı.
  • 2.300 kayıt ile 2.287 kaydı eşleştir. Referans numarası, tutar, tarih. Kod. Bir JOIN ve bir tolerans eşiği. Modelin 4 turu buna gitti.
  • Eşleşmeyen 13 kaydın sebebi ne? Serbest metinli açıklama alanı var: “MT103 ref hatalı”, “müşteri iadesi”, “komisyon farkı”. Kuralı tek cümlede yazamıyorum; bugün 13 satır, yarın başka bir ifade. Model.
  • Sabah bırakılacak özet. Doğal dil, sabit kalıp yok. Model.
  • Fark 5.000 $’ı geçerse gece nöbetçisini ara. Eşik karşılaştırması. Kod — ve bedeli geri alınamaz olduğu için sınırı da kod tutar, model değil.
Modelin işi, kuralı yazılamayan yeri doldurmaktır. Kuralı yazılabilen yerde model sadece pahalı bir zardır.

Üç desen

1. Zincir: sıra belli

Adımlar ve sıraları sabit; her adım bir öncekinin çıktısını alır. Mutabakat işi bu. Model iki adımda var, diğerlerinde yok.

Zincir
# akis.py - sira kodda yazili, modelde degil
def gece_mutabakati(tarih):
    dosya  = banka_dosyasi_oku(tarih)              # kod
    kayit  = islem_listesi(tarih)                  # kod
    fark   = eslestir(dosya, kayit, tolerans=0.01) # kod: JOIN
    if not fark:
        return "Fark yok."
    etiket = model_sinifla(fark)                   # model: 13 satir, serbest metin
    if fark.toplam > 5000:
        nobetci_ara(fark)                          # kod: esik karari
    return model_ozet(fark, etiket)                # model: dogal dil

Buradaki model_sinifla ve model_ozet birer agent değil, birer çağrı: döngü yok, tool yok, girdi sınırlı. Beş adımlık işin üçü zaten kod; kalan ikisi de tek turluk.

2. Yönlendirici: giriş tipi belirsiz

Chat ekranına gelen sorunun ne olduğu belli değil: rapor sorusu mu, mutabakat isteği mi, KYC sorgusu mu? Burada model bir kere karar verir — hangi akışa gideceğine — sonra akış devralır.

  • Yönlendirici tek turdur ve çıktısı kapalı bir kümedendir: rapor, mutabakat, kyc, bilinmiyor.
  • “Bilinmiyor” gerçek bir seçenek olmalı. Yoksa model en yakın kutuya zorla atar; bizde ilk hafta 3 soru yanlış akışa gitti, üçü de kapalı küme dar olduğu için.
  • Yönlendirici yanlış seçerse bedel bir adımdır, tüm koşu değil: akış “bu veri bende yok” deyip geri döner.

3. Planlayıcı–işçi: adım sayısı girdiye bağlı

“Geçen ay reddedilen tüm çekimleri sebep sebep incele” dediğinde kaç adım gerektiğini önceden bilemezsin: 4 sebep de olabilir, 40 da. Burada model bir plan üretir, kod planı çalıştırır.

  • Plan bir veri yapısıdır, serbest metin değil: adım listesi, her adımda tool adı ve argümanlar.
  • Kod planı doğrular: tool var mı, argümanlar tipinde mi, adım sayısı sınırın altında mı (bizde 12). Geçersiz plan çalıştırılmaz, modele bir kez geri verilir.
  • İşçiler planı çalıştırır; paralel çalıştırmanın ne zaman kazandırdığı ayrı yazının konusu.
  • Plan koşu başında kaydedilir. Koşu yarıda kalırsa kaldığı adımdan devam eder, en baştan başlamaz.
DesenNe zamanModelin rolüRisk
ZincirSıra sabit1–2 adımda, tek turYok denecek kadar az
YönlendiriciGiriş tipi belirsizTek seçim, kapalı kümeYanlış kutu; bedeli bir adım
Planlayıcı–işçiAdım sayısı girdiye bağlıPlan üretir, çalıştırmazUzayan plan, artan maliyet
Serbest agentKeşif, tek seferlik analizHer turda akışı seçerTekrarlanamaz koşu, sınırsız maliyet

Son satırı silmedim, çünkü serbest agent yanlış bir şey değil. Yanlış olan onu gece işinde kullanmak. Keşif yaparken — “bu veride ne var?” — başımda dururken serbest agent iyi çalışıyor. Her gece 02:40’ta kimse başında değil.

Dört fren

Yönlendirilmiş akış da bir yerde modele soruyor; model de yanlış cevaplayabiliyor. Frenler bu yüzden akışın kendisinde durur, model prompt’unda değil.

Frenler, kod tarafında
ADIM_TUR_SINIRI   = 3        # bir adim icinde model kac kez tool isteyebilir
KOSU_TUR_SINIRI   = 8        # tum kosuda toplam tur
KOSU_TOKEN_BUTCE  = 120000   # asilirsa kosu durur
ADIM_TIMEOUT_SN   = 45       # tek adim icin

# tekrar korumasi: ayni tool + ayni argumanlar
#   2. cagri -> onbellekten don, tur sayma
#   3. cagri -> adimi hata ile bitir

Dördü birlikte çalışır, çünkü her biri başka bir çöküşü yakalar:

  • Tur sınırı döngüyü keser. Tek başına yetmez: 8 tur içinde 400 bin token yakabilirsin.
  • Token bütçesi maliyeti keser. Tek başına yetmez: 3 turda 2 dakika bekleyen bir tool zinciri bütçeyi aşmadan geceyi yer.
  • Adım zaman aşımı asılı kalmayı keser. Tek başına yetmez: hızlı ve yanlış dönen döngüyü görmez.
  • Tekrar koruması aynı yere üçüncü kez gitmeyi keser. O gece 9–12. turlarda iki birebir aynı çağrı vardı; bu fren onları tek çağrıya indirirdi.

Fren devreye girdiğinde koşu sessizce bitmez. Kaydedilen üç şey: o ana kadarki kısmi sonuç, hangi frenin yakaladığı, son adım ve argümanları. Sabah gelen mesaj “beceremedim” değil, “3. adımda tur sınırına takıldım, elimde şu 13 kayıt var” oluyor.

Sınırsız agent diye bir şey yoktur; sınırı sen koymazsan sınır faturadan ya da sabah gelen yanlış rapordan gelir.

Sahadan: aynı iş, dört adım

Akışı yazdıktan sonraki hâli. Aynı gece işi, aynı model, aynı tool’lar:

AdımKimSüreNot
1. Dosya + kayıt çekKod6 snTarih aralığı kodda; tartışma yok
2. EşleştirKod0,4 sn2.300 × 2.287, tolerans 0,01; 13 eşleşmeyen
3. SınıflandırModel21 sn13 kayıt, tek tur, kapalı etiket kümesi (5 etiket + diger)
4. Özet yazModel11 snGirdi: 13 kayıt + etiketler. Ham 2.300 satır modele hiç girmedi
Toplam38 sn2 model turu; 9 dk 12 sn → 38 sn

İki hafta boyunca her gece koştu. 14 koşunun 13’ü aynı 4 adımı izledi; biri 3. adımda zaman aşımına düştü (model sunucusu yeniden başlamıştı), kısmi sonuç kaydedildi, sabah 2 dakikada elle bitirdim. Asıl kazanç süre değil: artık “dün neden yavaştı” sorusunun cevabı var, çünkü karşılaştırılacak sabit bir akış var.

Yapılacaklar
  • Akışı kodda yaz; modele yalnızca kuralı yazılamayan adımları ver.
  • Model çıktısını kapalı kümeye zorla: etiket listesi, JSON şeması, enum.
  • Her koşuya bir kimlik ver; adımları, süreleri ve token’ı kaydet.
  • Frenleri koda koy, prompt’a değil. Dördünü birden.
  • Kısmi sonucu sakla; yarıda kalan koşu kaldığı adımdan devam etsin.
  • Geri alınamaz adımı (para, mail, silme) insan onayına bağla.
Yapılmayacaklar
  • “Sırayla şunları yap” diye prompt’a adım listesi yazmak. Sıra koda aittir.
  • 2.300 satır ham veriyi modele göndermek. Eşleştirme kod işi.
  • Tur sınırını tek fren sanmak.
  • Gece çalışan bir işi serbest agent’a bırakmak.
  • Modelin ürettiği planı doğrulamadan çalıştırmak.
  • Fren yakalayınca koşuyu sessizce bitirmek.

Ne izlemeli?

  • Koşu başına tur sayısı (p50 / p95). Dağılım genişse akış yazılı değil demektir. Bizde 2 / 3.
  • Adım başına model çağrısı. 1’in üstüne çıkan adım aslında iki adımdır; böl.
  • Fren tetiklenme oranı. %0 ise frenler gevşek, kimse fark etmeden pahalanıyorsundur; %10’u geçiyorsa akış yanlış.
  • Aynı girdiye aynı yol oranı. Tekrarlanabilirliğin tek sayısı. Bizde 13/14.
  • Koşu başına token ve süre. Görev başı maliyetin girdisi; adım kırılımıyla tut.
  • İnsana düşen koşu oranı. Artıyorsa modelden fazla şey isteniyordur; azalan bir eğri beklenir.

Kontrol listesi

Akışı yazmadan önce
  • Bu işin adımlarını kâğıda yazabiliyor muyum? Yazamıyorsam model de yazamaz.
  • Her adım için üç soruyu sordum mu: girdi sonlu mu, kural tek cümle mi, bedel geri alınabilir mi?
  • Modele giden adımların çıktısı kapalı bir kümede mi, serbest metin mi?
  • Hangi deseni kullanıyorum: zincir, yönlendirici, planlayıcı–işçi? Neden diğeri değil?
  • Yönlendiricide “bilinmiyor” seçeneği var mı?
  • Dört fren de kodda mı: tur, bütçe, zaman aşımı, tekrar?
  • Fren yakalayınca ne kaydediliyor? Sabah bakan kişi nereden devam edecek?
  • Aynı girdiyle iki koşu aynı yolu izliyor mu? İzlemiyorsa hangi adım belirsiz?
  • Geri alınamaz adım hangisi? Onu kim onaylıyor?
  • Koşu yarıda kalırsa baştan mı başlıyor, kaldığı yerden mi?

Sonuç

O gece 17 tur döndü, 9 dakika harcadı ve yanlış bir özet bıraktı. Model kötü olduğu için değil; ona görev yerine boş bir oda verdiğimiz için. “Mutabakat farkını bul” bir cümle, beş adım. Beş adımın üçünün kuralı belliydi ve biz o üçünü de modele sordurduk.

Yönlendirilmiş akışta aynı iş 38 saniye sürüyor, iki model turu içeriyor ve her gece aynı yolu izliyor. Hızlanması güzel; asıl değişen, ölçülebilir olması. Yanlış çıkarsa hangi adımda yanlış çıktığını biliyorum ve o adımı düzeltiyorum. Serbest agent’ta düzeltecek bir adım yoktu.

Akılda kalacak cümle şu: agent’ın zekâsı, akışı yazdıktan sonra başlar. Kuralını yazabildiğin her şeyi koda yaz, kalanını modele bırak; frenleri de modele değil akışa koy. Geriye kalan boşluk, modelin gerçekten iyi olduğu yerdir.