API Token Sayısını Azaltarak OpenAI Maliyetini Düşürme
OpenAI API kullanmaya başladığınızda her şey güzel görünür. Birkaç istek gönderirsiniz, harika sonuçlar alırsınız, “bu iş çok kolay” dersiniz. Sonra ay sonu fatura gelir ve gerçeklikle yüzleşirsiniz. Token maliyetleri, özellikle production ortamında yüksek hacimli kullanımda, ciddi bir gider kalemi haline gelebilir. Bu yazıda token sayısını nasıl azaltabileceğinizi, hem sistem yöneticisi bakış açısıyla hem de pratik kod örnekleriyle ele alacağız.
Token Nedir ve Neden Önemlidir
OpenAI API’si metni işlerken karakterleri değil, token adı verilen parçaları kullanır. Kabaca olarak her 4 karakter 1 token’a karşılık gelir, ama bu dil ve içeriğe göre değişir. Türkçe gibi eklemeli dillerde bu oran bazen daha az verimli çalışır.
API maliyeti hem gönderdiğiniz (input) hem de aldığınız (output) token sayısına göre hesaplanır. GPT-4o ile çalışıyorsanız input token’lar daha ucuz, output token’lar daha pahalıdır. Bu asimetri, stratejinizi şekillendirmeniz için önemli bir ipucu verir: model çok şey üretmesin, siz az ama öz gönderin.
Aşağıdaki Python örneği ile mevcut bir isteğin kaç token tükettiğini ölçebilirsiniz:
pip install tiktoken
python3 << 'EOF'
import tiktoken
def token_say(metin, model="gpt-4o"):
enc = tiktoken.encoding_for_model(model)
return len(enc.encode(metin))
sistem_prompt = """Sen bir müşteri hizmetleri asistanısın.
Kullanıcıların sorularını kibarca, profesyonelce ve Türkçe olarak yanıtlıyorsun.
Her zaman nazik ol, kullanıcıyı dinle ve onlara yardımcı olmaya çalış.
Eğer bir soruya cevap veremiyorsan, bunu açıkça belirt."""
kullanici_mesaji = "Siparişim nerede?"
print(f"Sistem prompt token: {token_say(sistem_prompt)}")
print(f"Kullanıcı mesajı token: {token_say(kullanici_mesaji)}")
print(f"Toplam input token: {token_say(sistem_prompt + kullanici_mesaji)}")
EOF
Bu basit ölçüm alışkanlığını edinmek, optimizasyonun ilk adımıdır. Neyi ölçemiyorsanız, onu yönetemezsiniz.
Sistem Prompt Optimizasyonu
Sistem promptları çoğu zaman göz ardı edilen en büyük token israf kaynağıdır. Her API çağrısında sistem promptu yeniden gönderilir. Eğer 500 token’lık bir sistem promptunuz varsa ve günde 10.000 istek yapıyorsanız, sadece sistem promptu için 5 milyon token harcıyorsunuz demektir.
Gereksiz Kelimelerden Kurtulun
python3 << 'EOF'
import tiktoken
enc = tiktoken.encoding_for_model("gpt-4o")
# Kötü örnek - şişirilmiş sistem promptu
kotu_prompt = """
Sen son derece deneyimli, son teknoloji yapay zeka destekli bir asistansın.
Görevin kullanıcılara her konuda yardımcı olmak ve onların tüm sorularını
mümkün olan en kapsamlı ve detaylı şekilde yanıtlamaktır. Her zaman kibar,
anlayışlı ve profesyonel bir dil kullanmalısın. Kullanıcı deneyimini en üst
seviyeye çıkarmak için elinden gelenin en iyisini yapmalısın.
"""
# İyi örnek - özlü sistem promptu
iyi_prompt = """Yardımcı bir asistansın. Kısa, net ve doğru yanıt ver."""
print(f"Kötü prompt: {len(enc.encode(kotu_prompt))} token")
print(f"İyi prompt: {len(enc.encode(iyi_prompt))} token")
print(f"Tasarruf: {len(enc.encode(kotu_prompt)) - len(enc.encode(iyi_prompt))} token/istek")
EOF
Aradaki fark küçük görünebilir ama aylık binlerce isteği olan bir sistemde bu fark ciddi paraya dönüşür.
Few-Shot Örnek Sayısını Optimize Edin
Few-shot örnekler modeli yönlendirmek için kullanılır ama aşırıya kaçıldığında token maliyetini katlar. Genellikle 2-3 iyi örnek, 10 ortalama örnekten daha etkilidir.
python3 << 'EOF'
import tiktoken
enc = tiktoken.encoding_for_model("gpt-4o")
# Her örnek yaklaşık bu kadar token tutar
ornek_sablonu = """
Kullanıcı: Ürünüm bozuk geldi ne yapmalıyım?
Asistan: Özür dileriz. Sipariş numaranızı paylaşır mısınız?
"""
# 10 örnek mi, 3 örnek mi?
on_ornek = ornek_sablonu * 10
uc_ornek = ornek_sablonu * 3
print(f"10 örnek: {len(enc.encode(on_ornek))} token")
print(f"3 örnek: {len(enc.encode(uc_ornek))} token")
print(f"Günlük 10.000 istekte tasarruf: {(len(enc.encode(on_ornek)) - len(enc.encode(uc_ornek))) * 10000:,} token")
EOF
Konuşma Geçmişi Yönetimi
Chat tabanlı uygulamalarda konuşma geçmişinin tamamını her seferinde göndermek, en yaygın token israf nedenlerinden biridir. Kullanıcı 20 mesaj yazmışsa, 21. mesajda önceki 20 mesajın tamamını gönderiyorsunuz demektir.
Sliding Window Yaklaşımı
python3 << 'EOF'
from openai import OpenAI
import tiktoken
client = OpenAI()
enc = tiktoken.encoding_for_model("gpt-4o")
def mesaj_token_say(mesajlar):
toplam = 0
for mesaj in mesajlar:
toplam += len(enc.encode(mesaj.get("content", "")))
toplam += 4 # Her mesaj için overhead
return toplam
def gecmis_kirp(mesajlar, max_token=2000, sistem_mesaj=None):
"""
Konuşma geçmişini token limitine göre kırpar.
Her zaman en son mesajları korur.
"""
if sistem_mesaj:
sistem_token = len(enc.encode(sistem_mesaj["content"]))
max_token -= sistem_token
kirpilmis = []
toplam_token = 0
# Mesajları sondan başa doğru işle
for mesaj in reversed(mesajlar):
mesaj_token = len(enc.encode(mesaj["content"])) + 4
if toplam_token + mesaj_token <= max_token:
kirpilmis.insert(0, mesaj)
toplam_token += mesaj_token
else:
break
return kirpilmis
# Örnek kullanım
konusma_gecmisi = [
{"role": "user", "content": "Merhaba, Python öğrenmek istiyorum"},
{"role": "assistant", "content": "Harika bir seçim! Python ile başlamak için..."},
{"role": "user", "content": "Değişkenlerden bahseder misin?"},
{"role": "assistant", "content": "Değişkenler Python'da veri depolamak için kullanılır..."},
{"role": "user", "content": "Döngüler nasıl çalışır?"},
]
kirpilmis = gecmis_kirp(konusma_gecmisi, max_token=500)
print(f"Orijinal mesaj sayısı: {len(konusma_gecmisi)}")
print(f"Kırpılmış mesaj sayısı: {len(kirpilmis)}")
print(f"Token tasarrufu: {mesaj_token_say(konusma_gecmisi) - mesaj_token_say(kirpilmis)}")
EOF
Konuşma Özeti Yaklaşımı
Sliding window yerine eski mesajları özetletmek, bağlamı kaybetmeden token tasarrufu sağlar:
python3 << 'EOF'
from openai import OpenAI
client = OpenAI()
def konusmayi_ozetle(eski_mesajlar):
"""Eski konuşma geçmişini özetler"""
ozet_istegi = [
{
"role": "system",
"content": "Aşağıdaki konuşmayı 2-3 cümleyle özetle. Sadece özeti yaz."
},
{
"role": "user",
"content": "n".join([
f"{m['role']}: {m['content']}"
for m in eski_mesajlar
])
}
]
yanit = client.chat.completions.create(
model="gpt-4o-mini", # Özetleme için mini model yeterli
messages=ozet_istegi,
max_tokens=150
)
return yanit.choices[0].message.content
# Kullanım senaryosu
gecmis = [
{"role": "user", "content": "Sunucumda disk alanı doldu"},
{"role": "assistant", "content": "df -h komutu ile disk kullanımını kontrol edebilirsiniz..."},
{"role": "user", "content": "Hangi dizinler çok yer kaplıyor?"},
{"role": "assistant", "content": "du -sh /* komutu ile dizin boyutlarını görebilirsiniz..."},
]
ozet = konusmayi_ozetle(gecmis)
print(f"Özet: {ozet}")
print("nYeni konuşma bu özetle devam eder, tam geçmiş yerine")
EOF
Model Seçimi ve Akıllı Yönlendirme
Her görev için en güçlü modeli kullanmak, en pahalı yaklaşımdır. GPT-4o ile basit bir metin sınıflandırması yapmak, Ferrari ile market alışverişine gitmek gibidir.
python3 << 'EOF'
from openai import OpenAI
client = OpenAI()
def akilli_model_sec(gorev_tipi, karmasiklik_skoru):
"""
Görev tipine ve karmaşıklığa göre model seçer.
karmasiklik_skoru: 1-10 arası
"""
if gorev_tipi in ["siniflandirma", "ozet", "basit_soru"] or karmasiklik_skoru <= 3:
return "gpt-4o-mini"
elif karmasiklik_skoru <= 7:
return "gpt-4o"
else:
return "gpt-4o" # Maksimum karmaşıklık
def hizli_siniflandir(kullanici_mesaji):
"""Kullanıcı mesajını kategorize et - mini model yeterli"""
yanit = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{
"role": "system",
"content": "Mesajı kategorize et: teknik/fatura/genel. Sadece kategoriyi yaz."
},
{"role": "user", "content": kullanici_mesaji}
],
max_tokens=10 # Kategori için 10 token fazlasıyla yeterli
)
return yanit.choices[0].message.content.strip()
# Test
test_mesajlari = [
"Siparişim nerede?",
"Kubernetes cluster'ımda pod'lar crash oluyor",
"Faturamda hata var"
]
for mesaj in test_mesajlari:
kategori = hizli_siniflandir(mesaj)
print(f"Mesaj: {mesaj[:30]}... -> Kategori: {kategori}")
EOF
max_tokens Parametresini Aktif Kullanın
Çoğu developer max_tokens parametresini ayarlamayı unutur. Model gereksiz yere uzun yanıtlar üretebilir. Bu parametreyi görev tipine göre sıkı tutmak ciddi tasarruf sağlar.
python3 << 'EOF'
from openai import OpenAI
client = OpenAI()
# Görev bazlı max_tokens değerleri
GOREV_TOKEN_LIMITLERI = {
"siniflandirma": 20,
"evet_hayir": 5,
"kisa_ozet": 150,
"kod_aciklama": 300,
"detayli_analiz": 800,
"uzun_icerik": 2000
}
def kontrollü_istek(prompt, gorev_tipi, model="gpt-4o-mini"):
max_tok = GOREV_TOKEN_LIMITLERI.get(gorev_tipi, 500)
yanit = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
max_tokens=max_tok,
temperature=0 # Deterministik yanıt, israfı önler
)
kullanilan = yanit.usage.total_tokens
print(f"Görev: {gorev_tipi}, Limit: {max_tok}, Kullanılan: {kullanilan}")
return yanit.choices[0].message.content
# Test senaryoları
kontrollü_istek("Bu metin olumlu mu yoksa olumsuz mu? 'Ürün harika geldi'", "siniflandirma")
kontrollü_istek("Python'da liste nedir?", "kisa_ozet")
EOF
Önbellekleme ile Tekrarlayan İstekleri Engelleme
Production sistemlerde aynı ya da benzer sorgular tekrar tekrar gönderilir. Bir Redis tabanlı önbellek kurarak identik sorgular için API’ye hiç gitmeyebilirsiniz.
pip install redis openai
python3 << 'EOF'
import hashlib
import json
import redis
from openai import OpenAI
client = OpenAI()
cache = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)
CACHE_TTL = 3600 # 1 saat
def prompt_hash_olustur(messages, model):
"""İstek için benzersiz hash üretir"""
icerik = json.dumps({"messages": messages, "model": model}, sort_keys=True)
return hashlib.sha256(icerik.encode()).hexdigest()
def cacheli_istek(messages, model="gpt-4o-mini", max_tokens=500):
cache_key = f"openai:{prompt_hash_olustur(messages, model)}"
# Önce cache'e bak
cached = cache.get(cache_key)
if cached:
print("Cache hit! API çağrısı yapılmadı.")
return json.loads(cached)
# Cache'de yoksa API'ye git
print("Cache miss. API çağrısı yapılıyor...")
yanit = client.chat.completions.create(
model=model,
messages=messages,
max_tokens=max_tokens
)
sonuc = {
"content": yanit.choices[0].message.content,
"tokens": yanit.usage.total_tokens
}
# Sonucu cache'e yaz
cache.setex(cache_key, CACHE_TTL, json.dumps(sonuc))
return sonuc
# Aynı istek iki kez gönderilirse ikincisi cache'den gelir
mesajlar = [{"role": "user", "content": "Python'da decorator ne işe yarar?"}]
sonuc1 = cacheli_istek(mesajlar)
sonuc2 = cacheli_istek(mesajlar) # Bu cache'den gelir
print(f"nYanıt: {sonuc1['content'][:100]}...")
EOF
Prompt Sıkıştırma Teknikleri
Uzun dökümanlarla çalışırken tüm içeriği göndermek yerine sadece ilgili bölümleri göndermek büyük tasarruf sağlar.
python3 << 'EOF'
from openai import OpenAI
import tiktoken
client = OpenAI()
enc = tiktoken.encoding_for_model("gpt-4o")
def ilgili_parcalari_sec(dokuman, soru, parca_boyutu=200, top_k=3):
"""
Basit keyword-based relevance scoring.
Production'da embedding similarity kullanılması önerilir.
"""
kelimeler = soru.lower().split()
parcalar = []
# Dökümanı parçalara böl
satirlar = dokuman.split('n')
parca = []
for satir in satirlar:
parca.append(satir)
if len(' '.join(parca).split()) >= parca_boyutu:
parcalar.append(' '.join(parca))
parca = []
if parca:
parcalar.append(' '.join(parca))
# Her parçayı skorla
skorlu_parcalar = []
for p in parcalar:
skor = sum(1 for k in kelimeler if k in p.lower())
skorlu_parcalar.append((skor, p))
# En ilgili parçaları döndür
skorlu_parcalar.sort(reverse=True)
return [p for _, p in skorlu_parcalar[:top_k]]
# Örnek
uzun_dokuman = """
Linux sistem yönetimi birçok farklı konuyu kapsar...
Disk yönetimi için fdisk, parted araçları kullanılır...
Network konfigürasyonu için ip, ifconfig komutları mevcuttur...
Log analizi için journalctl, grep, awk sık kullanılır...
Process yönetimi ps, top, htop araçlarıyla yapılır...
Güvenlik duvarı yönetimi iptables veya firewalld ile sağlanır...
"""
soru = "disk yönetimi nasıl yapılır"
ilgili = ilgili_parcalari_sec(uzun_dokuman, soru)
tam_token = len(enc.encode(uzun_dokuman))
secili_token = sum(len(enc.encode(p)) for p in ilgili)
print(f"Tam döküman token: {tam_token}")
print(f"Seçili parçalar token: {secili_token}")
print(f"Tasarruf oranı: %{((tam_token - secili_token) / tam_token * 100):.1f}")
EOF
Maliyet İzleme Sistemi Kurma
Optimizasyon yapıyorsunuz ama etkisini ölçmüyorsanız, ne kadar tasarruf ettiğinizi bilemezsiniz. Basit bir maliyet izleme sistemi kurmak şarttır.
python3 << 'EOF'
from openai import OpenAI
from datetime import datetime
import json
client = OpenAI()
# GPT-4o fiyatlandırması (1M token başına USD)
FIYATLAR = {
"gpt-4o": {"input": 2.50, "output": 10.00},
"gpt-4o-mini": {"input": 0.15, "output": 0.60},
}
maliyet_log = []
def maliyet_hesapla(model, input_token, output_token):
fiyat = FIYATLAR.get(model, FIYATLAR["gpt-4o"])
input_maliyet = (input_token / 1_000_000) * fiyat["input"]
output_maliyet = (output_token / 1_000_000) * fiyat["output"]
return input_maliyet + output_maliyet
def izlenen_istek(messages, model="gpt-4o-mini", **kwargs):
yanit = client.chat.completions.create(
model=model,
messages=messages,
**kwargs
)
input_tok = yanit.usage.prompt_tokens
output_tok = yanit.usage.completion_tokens
maliyet = maliyet_hesapla(model, input_tok, output_tok)
kayit = {
"zaman": datetime.now().isoformat(),
"model": model,
"input_token": input_tok,
"output_token": output_tok,
"maliyet_usd": round(maliyet, 6)
}
maliyet_log.append(kayit)
print(f"[{model}] Input: {input_tok}, Output: {output_tok}, Maliyet: ${maliyet:.6f}")
return yanit
# Test istekleri
izlenen_istek(
[{"role": "user", "content": "2+2 kaçtır?"}],
model="gpt-4o-mini",
max_tokens=10
)
izlenen_istek(
[{"role": "user", "content": "Kubernetes nedir?"}],
model="gpt-4o-mini",
max_tokens=100
)
# Özet rapor
toplam = sum(k["maliyet_usd"] for k in maliyet_log)
toplam_token = sum(k["input_token"] + k["output_token"] for k in maliyet_log)
print(f"nToplam istek: {len(maliyet_log)}")
print(f"Toplam token: {toplam_token}")
print(f"Toplam maliyet: ${toplam:.6f}")
print(f"Aylık tahmin (1000 istek/gün): ${toplam/len(maliyet_log)*30000:.2f}")
EOF
Gerçek Dünya Senaryosu: Müşteri Destek Botu Optimizasyonu
Bir e-ticaret şirketinin müşteri destek botunu optimize ettiğimizi düşünelim. Günde 5.000 istek, ortalama 800 token/istek ile başlıyorlar.
Önceki durum:
- Günlük token: 5.000 x 800 = 4.000.000 token
- GPT-4o ile aylık maliyet: yaklaşık 300 USD
Uygulanan optimizasyonlar:
- Sistem promptu sadeleştirme: 400 token tasarruf/istek
- Sliding window konuşma geçmişi: 150 token tasarruf/istek
- GPT-4o-mini’ye geçiş (uygun sorgular için): %90 maliyet düşüşü o isteklerde
- Redis cache (tekrarlayan sorular için %30 cache hit oranı): %30 istek azalması
Sonraki durum:
- Günlük efektif token: 3.500 x 250 = 875.000 token
- Karma model stratejisiyle aylık maliyet: yaklaşık 45 USD
- Tasarruf: %85
Sonuç
OpenAI maliyetlerini düşürmek için tek bir sihirli çözüm yoktur. Bu bir sistem düşünce biçimidir; her katmanda küçük tasarruflar birleşerek büyük fark yaratır.
Öncelik sırasıyla şu adımları uygulayın:
- Ölçün: tiktoken ile her isteğin kaç token harcadığını bilin
- Sistem promptunu sadeleştirin: Her kelimeyi sorgulayın, gereksizi atın
- max_tokens kullanın: Görev bazlı sıkı limitler koyun
- Model seçimini akıllandırın: Basit görevler için mini model yeterli
- Konuşma geçmişini yönetin: Tümünü gönderme, akıllıca kırp veya özetle
- Cache kurun: Tekrarlayan istekler için Redis veya benzeri bir çözüm kullanın
- İzleyin ve raporlayın: Maliyet trendlerini takip edin, anomalileri erken yakalayın
Son olarak şunu söylemek gerekir: Bu optimizasyonlar sadece maliyet meselesi değil. Daha az token demek, daha hızlı yanıt süresi demektir. Daha hızlı yanıt süresi, daha iyi kullanıcı deneyimi demektir. Yani bu çalışmayı yapmak, hem cebinize hem kullanıcılarınıza iyilik etmektir.
