Bağlam penceresi, token ve maliyet
Önkoşul:Prompt nedir, model ne "görüyor"
Kanca
Uzun bir sohbetin ortasında, model BİRDENBİRE konuşmanın başında ne konuştuğunu “unutuyormuş” gibi davranabilir. Bu bir hata değil — 1. derste gördüğümüz token dizisinin, GERÇEKTEN sınırlı bir “bağlam penceresine” sığması gerekiyor.
Sezgi
Her model, bir seferde işleyebileceği MAKSİMUM token sayısına (bağlam penceresi) sahiptir. Bu pencere DOLUNCA, en ESKİ mesajlar SESSİZCE düşer — model onları bir daha GÖRMEZ. Aşağıda öğretici amaçlı KÜÇÜK bir pencereyle (100 token), bir sohbetin adım adım nasıl dolduğunu ve neyin düştüğünü izle:
Pencerede 42 / 100 token
Şimdiye kadarki kümülatif girdi maliyeti: $0.000000(Sonnet 5, 1M girdi token = $2, Eylül 2026)
Mekanizma
Önce 1. dersteki AYNI tokenizer’ı kullanarak, GERÇEK bir sohbetin her mesajının kaç token olduğunu sayalım:
Bir sohbetteki her mesajın gerçek token sayısı
# Bir sohbet UZADIKÇA, her mesajın GERÇEK token sayısını hesaplayalım.
KONUSMA = [
("system", "Sen yardımcı bir müşteri hizmetleri asistanısın."),
("user", "Merhaba, siparişim nerede?"),
("assistant", "Merhaba! Sipariş numaranızı paylaşabilir misiniz?"),
("user", "12345"),
("assistant", "Teşekkürler, hemen kontrol ediyorum."),
("user", "Ne kadar sürer?"),
("assistant", "Birkaç dakika içinde size bilgi vereceğim."),
("user", "Tamam, bekliyorum."),
("assistant", "Siparişiniz kargoya verildi, yarın elinizde olur."),
("user", "Harika, teşekkürler!"),
]
token_sayilari = [mesaj_token_sayisi(mesaj) for _, mesaj in KONUSMA]
print(f"{'Rol':<12} {'Token':<8} Mesaj")
for (rol, mesaj), t in zip(KONUSMA, token_sayilari):
print(f"{rol:<12} {t:<8} {mesaj}")
print(f"\nToplam (10 mesaj): {sum(token_sayilari)} token.")Rol Token Mesaj
system 42 Sen yardımcı bir müşteri hizmetleri asistanısın.
user 22 Merhaba, siparişim nerede?
assistant 38 Merhaba! Sipariş numaranızı paylaşabilir misiniz?
user 6 12345
assistant 30 Teşekkürler, hemen kontrol ediyorum.
user 13 Ne kadar sürer?
assistant 38 Birkaç dakika içinde size bilgi vereceğim.
user 15 Tamam, bekliyorum.
assistant 41 Siparişiniz kargoya verildi, yarın elinizde olur.
user 19 Harika, teşekkürler!
Toplam (10 mesaj): 264 token.Şimdi bu sohbeti, KÜÇÜK bir pencereden (100 token) geçirelim:
Pencere dolunca, en eski mesajlar düşer
# Gerçek modellerin bir "bağlam penceresi" (context window) SINIRI var -- ötesi
# HATIRLANMAZ. Öğretici amaçlı KÜÇÜK bir pencere (100 token) ile simüle edelim:
# sistem mesajı SABİT kalır, en ESKİ user/assistant mesajları pencere DOLUNCA düşer.
PENCERE_LIMITI = 100
def pencereyi_guncelle(pencere, yeni_indeks, token_sayilari, konusma):
pencere.append(yeni_indeks)
while sum(token_sayilari[i] for i in pencere) > PENCERE_LIMITI:
# SİSTEM mesajı hariç, en ESKİ mesajı düşür.
dusecek = next(i for i in pencere if konusma[i][0] != "system")
pencere.remove(dusecek)
return pencere
pencere = [0] # sistem mesajı en baştan pencerede
print(f"\nBağlam penceresi limiti: {PENCERE_LIMITI} token (öğretici amaçlı küçük tutuldu)\n")
for i in range(1, len(KONUSMA)):
pencere = pencereyi_guncelle(pencere, i, token_sayilari, KONUSMA)
pencere_toplami = sum(token_sayilari[j] for j in pencere)
pencerede_olanlar = [KONUSMA[j][0] for j in sorted(pencere)]
print(f"Mesaj {i} eklendi -- pencerede {len(pencere)} mesaj, {pencere_toplami} token: {pencerede_olanlar}")
dusenler = [i for i in range(len(KONUSMA)) if i not in pencere]
print(f"\nPencereden TAMAMEN düşen mesajlar (indeks): {dusenler}")
print("Sistem mesajı HİÇBİR ZAMAN düşmedi -- ama en eski user/assistant mesajları, pencere")
print("dolunca SESSİZCE unutuluyor. Model, konuşmanın BAŞINDA ne konuşulduğunu ARTIK 'hatırlamıyor'.")
Bağlam penceresi limiti: 100 token (öğretici amaçlı küçük tutuldu)
Mesaj 1 eklendi -- pencerede 2 mesaj, 64 token: ['system', 'user']
Mesaj 2 eklendi -- pencerede 2 mesaj, 80 token: ['system', 'assistant']
Mesaj 3 eklendi -- pencerede 3 mesaj, 86 token: ['system', 'assistant', 'user']
Mesaj 4 eklendi -- pencerede 3 mesaj, 78 token: ['system', 'user', 'assistant']
Mesaj 5 eklendi -- pencerede 4 mesaj, 91 token: ['system', 'user', 'assistant', 'user']
Mesaj 6 eklendi -- pencerede 3 mesaj, 93 token: ['system', 'user', 'assistant']
Mesaj 7 eklendi -- pencerede 3 mesaj, 95 token: ['system', 'assistant', 'user']
Mesaj 8 eklendi -- pencerede 3 mesaj, 98 token: ['system', 'user', 'assistant']
Mesaj 9 eklendi -- pencerede 2 mesaj, 61 token: ['system', 'user']
Pencereden TAMAMEN düşen mesajlar (indeks): [1, 2, 3, 4, 5, 6, 7, 8]
Sistem mesajı HİÇBİR ZAMAN düşmedi -- ama en eski user/assistant mesajları, pencere
dolunca SESSİZCE unutuluyor. Model, konuşmanın BAŞINDA ne konuşulduğunu ARTIK 'hatırlamıyor'.10 mesajlık bir sohbette, sistem mesajı DIŞINDA 8 mesaj tamamen unutuldu — SADECE sistem talimatı ve en SON mesaj pencerede kaldı.
Matematik
Neden maliyet karesel büyür
Bir sohbet API’si “hafızasızdır” (stateless): HER yeni cevap için, o ana kadarki TÜM geçmiş girdi olarak YENİDEN gönderilir.
mesaj sayısı, her mesajın token sayısı, ortalama mesaj uzunluğudur. Mesaj sayısı arttıkça, toplam girdi token’ı ile DEĞİL, ile büyür — bu yüzden UZUN sohbetler, mesaj başına orantısız derecede PAHALI hâle gelir.
Kod
Bunu GERÇEK, güncel bir fiyatla hesaplayalım:
Her tur, tüm geçmişi yeniden gönderir
# GERÇEK bir sohbet API'sinde, HER yeni mesaj için TÜM önceki geçmiş TEKRAR gönderilir
# (girdi olarak) -- bu yüzden toplam maliyet, mesaj sayısıyla DOĞRUSAL değil, YAKLAŞIK
# KARESEL büyür. Fiyat: Claude Sonnet 5, Eylül 2026 itibarıyla, 1M girdi token'ı için $2
# (kaynak: finout.io, Eylül 2026 fiyatlandırma özeti).
GIRDI_FIYATI_1M = 2.0 # $ / 1.000.000 girdi token'ı (Sonnet 5, Eylül 2026)
kumulatif_girdi_token = 0
toplam_maliyet_dolar = 0.0
print(f"\n{'API çağrısı':<14} {'Bu çağrının girdisi (token)':<30} {'Kümülatif maliyet ($)':<22}")
for i in range(1, len(KONUSMA)):
if KONUSMA[i][0] != "assistant":
continue
# Bu assistant cevabını ÜRETMEK için, i'ye kadar olan TÜM mesajlar girdi olarak gönderilir.
bu_cagri_girdisi = sum(token_sayilari[:i])
kumulatif_girdi_token += bu_cagri_girdisi
maliyet = kumulatif_girdi_token / 1_000_000 * GIRDI_FIYATI_1M
print(f"{i:<14} {bu_cagri_girdisi:<30} {maliyet:<22.6f}")
print(f"\n5 asistan cevabı için TOPLAM kümülatif girdi maliyeti: ${maliyet:.6f}")
print("Bu sohbet KISA olduğu için mutlak rakam küçük -- ama örüntüye dikkat: her yeni tur,")
print("TÜM geçmişi YENİDEN gönderir. 100 turluk bir sohbette bu, mesaj başına maliyetin")
print("DOĞRUSAL değil, YAKLAŞIK KARESEL büyümesi demektir.")
API çağrısı Bu çağrının girdisi (token) Kümülatif maliyet ($)
2 64 0.000128
4 108 0.000344
6 151 0.000646
8 204 0.001054
5 asistan cevabı için TOPLAM kümülatif girdi maliyeti: $0.001054
Bu sohbet KISA olduğu için mutlak rakam küçük -- ama örüntüye dikkat: her yeni tur,
TÜM geçmişi YENİDEN gönderir. 100 turluk bir sohbette bu, mesaj başına maliyetin
DOĞRUSAL değil, YAKLAŞIK KARESEL büyümesi demektir.Nerede işe yarar
Bağlam penceresi yönetimi, pratikte GÜNLÜK bir mühendislik kararıdır:
- Uzun sohbetlerde, GEÇMİŞİ özetleyip (summarize) sıkıştırmak yaygın bir teknik — tüm geçmişi ham hâliyle taşımak yerine, önemli noktaları KISA bir özete indirger.
- Sistem mesajı genelde SABİT tutulur (bu dersteki simülasyonda olduğu gibi) — talimatların HER turda unutulmaması için.
- RAG (11. ders), bağlam penceresi sorununa KISMEN bir çözümdür — İLGİLİ bilgiyi, TÜM geçmişi taşımak yerine, İHTİYAÇ anında ARAYIP ekler.
Bu 2 hatayı yaparsın:
- Bağlam penceresinin “sonsuz hafıza” olduğunu varsaymak — HAYIR, pencere dolunca, en eski bilgi SESSİZCE (hatasız görünen ama YANLIŞ bir şekilde) kaybolur.
- Uzun bir sohbetin maliyetinin mesaj sayısıyla DOĞRUSAL arttığını sanmak — HER yeni mesaj, TÜM geçmişi yeniden gönderdiği için büyüme YAKLAŞIK KARESELDİR.
Kendini test et
1. Notebook'ta 100 token'lık pencerede, 10 mesajlık sohbetin 8 mesajı tamamen düşüyor. Bu NEDEN oluyor?
- Bir hata nedeniyle
- Bağlam penceresi SINIRLI olduğu için, YENİ bir mesaj eklendiğinde ve toplam token limiti AŞTIĞINDA, sistem mesajı HARİÇ en ESKİ mesajlar sırayla düşürülüyor -- pencere sadece SON birkaç mesajı tutabiliyor (doğru cevap)
- Sistem tüm mesajları rastgele siliyor
- Kullanıcı mesajları her zaman öncelikli olarak silinir
Neden: baglam-penceresi-doluyor bloğunda pencereyi_guncelle fonksiyonu, toplam token PENCERE_LIMITI'ni aşınca sistem mesajı HARİÇ en eski mesajı düşürüyor -- 8 mesajın (indeks 1-8) tamamen düşmesi bunun sonucu.
2. Bir sohbet API'sinde maliyetin mesaj sayısıyla YAKLAŞIK KARESEL büyümesinin nedeni nedir?
- API sağlayıcıları fiyatı kasten yükseltiyor
- API 'hafızasız' (stateless) olduğu için, HER yeni cevap üretilirken TÜM önceki geçmiş girdi olarak YENİDEN gönderilir -- bu yüzden N. mesajın maliyeti, ÖNCEKİ N-1 mesajın TAMAMINI da içerir (doğru cevap)
- Her mesaj otomatik olarak 2 katına çıkar
- Sadece uzun mesajlarda karesel büyüme görülür
Neden: maliyet-buyumesi bloğunda her API çağrısının girdisi, o ana kadarki TÜM mesajların toplamı (sum(token_sayilari[:i])) -- MathBox'taki toplam girdi formülü bu tekrarlı gönderimin NEDEN N² ile ölçeklendiğini gösteriyor.
3. Sistem mesajının bağlam penceresi dolduğunda DÜŞMEMESİ neden ÖNEMLİDİR?
- Önemli değildir, sistem mesajı da düşebilir
- Sistem mesajı modelin TEMEL talimatlarını (rol, kısıtlar, ton) taşır -- eğer o da düşerse, model konuşmanın ORTASINDA aniden farklı bir 'kişiliğe' bürünmüş gibi davranabilir (doğru cevap)
- Sistem mesajı zaten çok kısadır, bu yüzden önemi yoktur
- Sistem mesajı sadece dekoratif amaçlıdır
Neden: baglam-penceresi-doluyor bloğunda pencereyi_guncelle fonksiyonu AÇIKÇA sistem mesajını korumak için tasarlandı (dusecek = next(i for i in pencere if konusma[i][0] != 'system')) -- bu, gerçek sistemlerde YAYGIN bir tasarım kararıdır.
Özet
Özet
- Her model, sabit bir bağlam penceresi (maksimum token) sınırına sahiptir.
- Pencere dolunca, en eski mesajlar (genelde sistem mesajı HARİÇ) sessizce düşer -- model onları bir daha görmez.
- Sohbet API'leri hafızasızdır: her yeni cevap için TÜM geçmiş yeniden gönderilir.
- Bu yüzden uzun bir sohbetin toplam maliyeti, mesaj sayısıyla DOĞRUSAL değil YAKLAŞIK KARESEL büyür.
- Özetleme ve RAG (11. ders) gibi teknikler, bu pencere sınırını YÖNETMEK için kullanılır.