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

Ana sayfa → Teknik

CrewAI: Hazır Ekip Değil, Ödünç Alınan Varsayımlar

Perşembe raporu tek cümleyle bitiyordu: “Bu hafta regülasyon riski taşıyan şikâyet yok.” Pazartesi sabah uyum ekibi aradı. 120 şikâyetin birinde müşteri açıkça “SPK’ya şikâyet edeceğim” yazmıştı. Üç agent’lı ekibimizde o cümleyi görmesi gereken bir agent vardı. O cümle ona hiç ulaşmamıştı.

Özet
  • CrewAI sana bir ekip değil, bir dizi karar veriyor. Bağlamın nasıl taşınacağı, prompt’un nasıl kurulacağı, kaç kez deneneceği. Kararları okumadan kullanırsan varsayılanları seçmiş olursun.
  • Prototipte gerçekten hızlı. İlk çalışan sürüm bir günde, 60 satırda çıktı. Bu hız gerçek ve değerli.
  • “Ekip” bir benzetme. Gerçek ekipte uyum uzmanı ham şikâyeti okur. Sıralı crew’da bir önceki agent’ın özetini okur.
  • Maliyet agent’lar arasındaki konuşmada kayboluyor. 186 bin token’ın %58’i her çağrıda yeniden gönderilen metindi: rol, hikâye, format talimatı, önceki çıktı.
  • İzlemeyi framework’ün altına kur. Toplam kullanım yetmiyor; her LLM çağrısını agent ve görev etiketiyle kaydet.
  • Aynı işi düz kodla yaptık. Görev başı token 186 bin → 71 bin, süre 14 dk → 4 dk. Yazması üç gün sürdü, CrewAI bir gün.

Model: dört kavram, bir sözleşme

CrewAI’ın modeli sade. Agent bir rol, bir hedef ve bir arka plan hikâyesiyle tanımlanıyor. Task bir açıklama, beklenen çıktı ve onu yapacak agent. Crew agent’ları ve görevleri bir araya getiriyor. Process görevlerin nasıl ilerleyeceğini söylüyor: sıralı ya da bir yönetici agent’ın dağıttığı hiyerarşik düzen.

İşimiz basitti: haftalık müşteri şikâyetlerini oku, kategorilere ayır, regülasyon riski taşıyanları işaretle, yöneticiye bir sayfalık rapor yaz. Üç rol kendiliğinden çıktı:

Ilk surum: uc agent, uc gorev
from crewai import Agent, Task, Crew, Process

analist = Agent(
    role="Sikayet analisti",
    goal="Haftalik sikayetleri kategorilere ayirmak",
    backstory="Araci kurumun musteri deneyimi ekibinde calisiyorsun.",
    allow_delegation=False,   # varsayilana birakma, acikca yaz
)
uyum = Agent(
    role="Uyum uzmani",
    goal="Regulasyon riski tasiyan sikayetleri isaretlemek",
    backstory="Sermaye piyasasi mevzuatini iyi biliyorsun.",
    allow_delegation=False,
)
yazar = Agent(
    role="Rapor yazari",
    goal="Yoneticiye bir sayfalik ozet yazmak",
    backstory="Kisa ve net yazarsin.",
    allow_delegation=False,
)

siniflama = Task(
    description="Su sikayetleri kategorilere ayir: {sikayetler}",
    expected_output="Kategori basina sayi ve kisa aciklama",
    agent=analist,
)
risk = Task(
    description="Regulasyon riski tasiyan sikayetleri bul",
    expected_output="Risk tasiyan sikayetlerin listesi ve gerekcesi",
    agent=uyum,
    context=[siniflama],      # DIKKAT: ham sikayet degil, analistin CIKTISI gelir
)
rapor = Task(
    description="Haftalik raporu yaz",
    expected_output="Bir sayfalik rapor",
    agent=yazar,
)

ekip = Crew(
    agents=[analist, uyum, yazar],
    tasks=[siniflama, risk, rapor],
    process=Process.sequential,
)
sonuc = ekip.kickoff(inputs={"sikayetler": sikayet_metni})

Kodu okuyan biri hatayı hemen görüyor: {sikayetler} yalnızca ilk görevin açıklamasında var. Uyum agent’ı şikâyetleri değil, analistin yazdığı özeti görüyor. Ben bunu yazarken görmedim, çünkü kafamda bir ekip vardı ve ekipte herkes aynı klasöre bakar.

Hızlı prototip: gerçekten hızlı

Önce hakkını vereyim. 14 Temmuz Salı sabah başladım, akşam ilk rapor uyum ekibinin önündeydi. Rolleri yazmak işi düşünmeme de yardım etti: “uyum uzmanı ne arar?” sorusunu agent tanımlarken sorduk ve cevabı uyum ekibinden aldık. Düz kodla aynı gün bu noktaya gelemezdim; hangi adıma kaç çağrı gerektiğini, çıktıyı nasıl parse edeceğimi düşünmem gerekirdi.

İki hafta boyunca, 16 ve 23 Temmuz’da, rapor düzgün geldi. Yöneticiler memnundu. 30 Temmuz raporu da düzgün görünüyordu.

Framework sana hız vermiyor; kararlarını ödünç veriyor. Ödünç alınan karar, canlıda faiziyle geri ödenir.

Sahadan: kaybolan cümle

3 Ağustos Pazartesi uyum ekibi aradıktan sonra şikâyeti bulduk. Müşteri, para çekme talebinin üç gün beklemesinden şikâyet ediyordu ve son cümlesi “bu hafta içinde çözülmezse SPK’ya şikâyet edeceğim” idi. Analist agent onu doğru sınıflamıştı: “para çekme gecikmesi, müşteri memnuniyetsiz”. Doğru, ama eksik. Özetin içinde SPK kelimesi yoktu.

Uyum agent’ı elindeki metne göre doğru karar verdi: gecikme şikâyetleri risk değildir. Hatayı yapan agent yoktu. Hata veri akışındaydı ve veri akışını ben değil, sıralı process’in varsayılanı belirlemişti. Bulmamız bir buçuk gün sürdü, çünkü önce prompt’ta sorun aradık: uyum agent’ının hikâyesini uzattık, “dikkatli ol” ekledik. Hiçbiri işe yaramadı; agent’ın görmediği bir cümleyi dikkatle okumasını istiyorduk.

Nerede önüne geçiyor?

1. Bağlam taşıma

Sıralı process’te bir görev bir öncekinin çıktısını bağlam olarak alıyor. Bu, her adımda bilginin bir kez sıkıştırılması demek. Üç adımlık zincirde ham veri ilk agent’ta kalıyor; sonrakiler özetin özetini görüyor. Ham veriyi her göreve açıkça koymak mümkün, ama o zaman token iki katına çıkıyor ve “ekip” modelinin sunduğu kolaylık kalmıyor.

2. Gizli yeniden deneme

Bir agent bir görevde tek çağrı yapmıyor: düşünüyor, gerekirse tool çağırıyor, sonucu okuyor, tekrar düşünüyor. Çıktı beklenen biçime uymazsa yeniden deniyor. Bunun bir iterasyon sınırı (max_iter) var; değerini sen yazmadıysan varsayılanı seçmişsin demektir. Bir koşumuzda uyum agent’ı biçimi tutturamadı, koşu 38 yerine 52 çağrı ve 241 bin token’a çıktı. Bunu yalnızca koşunun sonundaki toplamdan fark ettik.

Bizim tool’larımız salt okumaydı. Yazan bir tool olsaydı — bilet açan, müşteriye mesaj atan — gizli yeniden deneme çift bilet demekti. Bu, at-least-once yazısındaki problemin aynısı; yeniden deneyen bir sistemin yan etkisi olan her adımı idempotent olmak zorunda.

3. Agent’lar arası konuşmada kaybolan token

Her LLM çağrısında agent’ın rolü, hedefi, hikâyesi, format talimatları ve önceki görevin çıktısı yeniden gönderiliyor. Tek çağrıda küçük; 38 çağrıda büyük. Ortalama bir koşunun 186 bin token’ının 108 bini, yani %58’i, işin kendisi değil bu tekrar eden metindi. Şikâyet metinlerinin kendisi toplamda 30 bin token civarı.

4. Hata ayıklama

verbose açıkken ekrana akan metin okunabilir ama sorgulanamaz. “Uyum agent’ı üçüncü çağrısında hangi metni gördü?” sorusunun cevabı o akışın içinde bir yerde, ama bulmak için kaydırarak okuman gerekiyor. Bir buçuk günün büyük kısmı buraya gitti.

Ödünç alınan varsayımPrototipteCanlıda
Görevler arası bağlam = önceki çıktıRahat, düşünmeye gerek yokHam veri ilk adımda kalıyor
Rol + hikâye her çağrıdaTutarlı sesToken’ın yarısından fazlası tekrar
Biçim tutmazsa yeniden deneKendiliğinden düzeliyorKoşu maliyeti tahmin edilemiyor (31–52 çağrı)
Ayrıntılı ekran çıktısıİzlemesi keyifliSorgulanamayan bir metin akışı

CrewAI’ı izlemek: kim ne harcadı?

Koşu bittiğinde crew bir kullanım özeti veriyor: toplam token, toplam istek. Bu, token panosu yazısında anlattığım görev başı maliyet için yetiyor, ama “neden bu kadar” sorusuna cevap vermiyor. Kırılımı almak için izlemeyi framework’ün altına, LLM çağrısı seviyesine indirdik:

Her LLM cagrisini etiketle
# LLM istemcisini sar: framework ne yaparsa yapsin, her cagri buradan gecer
def izlenen_cagri(mesajlar, **ayar):
    bas = simdi()
    cevap = llm_istemcisi.cagir(mesajlar, **ayar)
    kaydet({
        "kosu_id"   : aktif_kosu(),         # tek haftalik rapor = tek kosu
        "agent"     : aktif_agent_rolu(),   # gorev baslarken set edilir
        "gorev"     : aktif_gorev(),
        "iterasyon" : iterasyon_sayaci(),   # ayni gorevde kacinci cagri
        "girdi_tok" : cevap.kullanim.girdi,
        "cikti_tok" : cevap.kullanim.cikti,
        "sure_ms"   : simdi() - bas,
        "girdi"     : mesajlar,             # asil kazanc: agent NE GORDU
    })
    return cevap

CrewAI görev ve adım sonlarında callback kabul ediyor; biz onları sadece aktif agent ve görevi işaretlemek için kullandık. Asıl bilgi, çağrının girdisini kaydetmekten geldi. SPK hatasını bu kayıtla yeniden oynattığımızda, uyum agent’ının gördüğü metinde “SPK” kelimesinin geçmediğini tek sorguyla gördük. Bir buçuk gün yerine iki dakika.

AgentÇağrıTokenTekrar eden metin payı
Analist1796 bin%61
Uyum1458 bin%55
Yazar732 bin%53
Toplam38186 bin%58

Aynı iş, iki kez: CrewAI ve düz kod

Hatayı bulduktan sonra aynı işi düz kodla yazdım. Agent yok, rol yok; üç adım ve adımlar arasında açık bir veri sözleşmesi:

Duz kod: uc adim, acik veri akisi
# 1) siniflama: 20'lik partiler, JSON cikti, sema dogrulamasi
kategoriler = [siniflandir(parti) for parti in partile(sikayetler, 20)]   # 6 cagri

# 2) risk: HAM sikayet metni gider, ozet degil
#    once ucuz on filtre (SPK, avukat, dava, mahkeme...), sonra model
adaylar = on_filtre(sikayetler) + riskli_kategoriler(kategoriler)
riskler = [risk_degerlendir(parti) for parti in partile(adaylar, 10)]    # ~6 cagri

# 3) rapor: tek cagri, girdisi yapisal veri
rapor = rapor_yaz(kategoriler, riskler)                                   # 1 cagri

Sonra iki sürümü uyum ekibinin elle etiketlediği dört haftalık şikâyet setinde koşturduk. Dört haftada 11 riskli şikâyet vardı:

ÖlçüCrewAIDüz kod
İlk çalışan sürüm1 gün3 gün
Kod satırı~60~210
LLM çağrısı / koşu31–52 (ort. 38)12–14 (ort. 13)
Görev başı token (120 şikâyet)~186 bin~71 bin
Süre / koşu~14 dk~4 dk
Bir hatayı bulma süresi1,5 gün (SPK, izleme öncesi)40 dk (tarih biçimi, test sırasında)
Kaçan riskli şikâyet3 / 111 / 11

Tabloya dürüst bir çekince: düz kod sürümünü, problemi bildikten sonra yazdım. Üç günün içinde CrewAI’dan öğrendiklerim de var. Sıfırdan başlasaydım düz kod muhtemelen daha uzun sürerdi. Hata ayıklama satırı da adil değil: iki hata farklı zorlukta ve CrewAI’ın 1,5 günü izleme kurulmadan önceydi. Tabloyu “CrewAI kötü” diye değil, “neyin bedelini nerede ödüyorsun” diye oku.

Framework’te tasarruf ettiğin satırlar kaybolmuyor; log’da, faturada ve hata ayıklama saatinde geri geliyor.
CrewAI iyi seçim
  • İşin adımları henüz belli değil, keşif yapıyorsun
  • Bir günde çalışan bir şey gösterip geri bildirim alman lazım
  • Tool’lar salt okuma, yeniden deneme zararsız
  • Koşu başı maliyet ve süre esnek
Düz koda geç
  • Adımlar artık belli ve her hafta aynı
  • Bir adımın ham veriyi görmesi şart
  • Yan etkisi olan tool var (bilet, mesaj, para)
  • Koşu maliyetinin tahmin edilebilir olması gerekiyor

Bende işe yaramayanlar

  • Hikâyeyi uzatarak düzeltmek. Uyum agent’ının backstory’sine üç paragraf mevzuat ekledim. Her çağrıda tekrar gönderildiği için koşu 186 binden 214 bin token’a çıktı; SPK cümlesi yine gelmedi, çünkü sorun agent’ın bilgisinde değil, önündeki metindeydi.
  • Hiyerarşik process’e geçmek. “Bir yönetici agent dağıtsın, belki ham veriyi doğru kişiye verir” dedik. Çağrı sayısı 38’den 61’e çıktı, sonuç değişmedi. Veri akışı problemini bir agent daha ekleyerek çözemezsin.
  • Ekran çıktısını log sanmak. İki hafta boyunca “verbose açık, görüyoruz” dedik. Görüyorduk, ama saklamıyorduk ve sorgulayamıyorduk.

Ne izlemeli?

NeNeden
LLM çağrısı / koşu (min–maks)Aralık genişse bir yerde gizli yeniden deneme var
Agent başına tokenToplam yetmez; hangi rolün pahalı olduğunu söyler
Tekrar eden metin payı%50’yi geçiyorsa işe değil, framework’e para ödüyorsun
Görev başı iterasyonBiçim tutmayan görev burada görünür
Kaçan vaka, elle etiketli setteTek gerçek kalite ölçüsü; ayda bir koştur

Kontrol listesi

Framework’ü seçmeden önce
  • Her görev ham veriyi mi görüyor, önceki agent’ın özetini mi? Bunu kodda gösterebiliyor muyum?
  • İterasyon sınırını ve delegasyonu açıkça ben mi yazdım, varsayılan mı?
  • Yan etkisi olan bir tool yeniden denenirse ne olur?
  • Her LLM çağrısının girdisini agent ve görev etiketiyle saklıyor muyum?
  • Bir koşunun token’ının ne kadarı tekrar eden metin?
  • Koşu başı çağrı sayısı ne kadar oynuyor?
  • Elle etiketlenmiş bir sette kaçan vakayı ölçtüm mü?
  • Adımlar artık belli mi? Belliyse bu iş hâlâ neden bir crew?

Sonuç

O Perşembe raporu yanlış değildi; eksikti. Her agent kendi önündeki metne göre doğru karar verdi. Eksik olan, benim yazmadığım ama framework’ün benim yerime verdiği bir karardı: uyum uzmanı neyi okuyacak?

CrewAI’ı bırakmadık. Yeni bir iş geldiğinde ilk gün hâlâ onunla başlıyoruz, çünkü adımları bulmanın en hızlı yolu bu. Ama adımlar belli olunca düz koda geçiyoruz ve o geçişi bir iyileştirme değil, işin olgunlaşması olarak görüyoruz.

Test şu: crew’undaki her agent’ın bir çağrıda tam olarak hangi metni gördüğünü söyleyebiliyor musun? Söyleyemiyorsan bir ekibin yok; varsayımlarını tanımadığın biriyle çalışıyorsun.