Yapay Zekâ Entegrasyonu (Claude API)

API anahtarı sunucuda kalır, her yanıtın jetonu ve maliyeti hesaplanır. Claude Messages API'sine SDK'sız cURL isteği, geçici/kalıcı hata ayrımı, üstel geri çekilme ve düşünme özeti — oturum girişli bir panelin içinde.

PHP 8 PDO MySQL Bootstrap 5 Ajax Oturum Girişi
Seviye
İleri
Dosya
84
Kod satırı
~20.626
Proje boyutu
2.2 MB
Veritabanı
cy_ai
Lisans
MIT
İnceleme
29
Beğeni
0
Yayın

Ekran Görüntüleri 6 görsel

Bu Örnek Ne Yapıyor?

  • API anahtarı yalnızca sunucuda; tarayıcıya hiç gönderilmez
  • Mesaj başına giriş/çıkış jetonu ve hesaplanmış maliyet kaydı
  • Sohbet toplamları mesaj toplamlarıyla birebir tutarlı
  • Giriş jetonunun neden her mesajda arttığı somut olarak görünür
  • Geçici (429/529/5xx) ve kalıcı (401/400) hata ayrımı
  • Üstel geri çekilme + jitter ile en fazla üç yeniden deneme
  • stop_reason ve refusal alanları ayrı ele alınır
  • Modelin düşünme özeti saklanır ve details ile katlanarak gösterilir
  • SDK yok: istek cURL ile atılır, yanıt elle ayrıştırılır

Nerede İşe Yarar?

  • Projesine yapay zekâ özelliği ekleyip maliyeti kontrol altında tutmak isteyenler
  • API anahtarını nereye koyacağını soran geliştiriciler
  • Jeton muhasebesini ve maliyet hesabını öğrenmek isteyenler
  • SDK kurmadan, saf PHP ile Messages API'sine bağlanmak isteyenler

Gereksinimler

  • PHP 8.0+ · MySQL 5.7+ / MariaDB 10.3+ · pdo_mysql · mbstring · curl · Anthropic API anahtarı · Apache mod_rewrite

Nasıl Kurulur?

  1. Depoyu klonlayın veya ZIP olarak indirip web kökünüze açın
  2. database.sql dosyasını içe aktarın (veritabanını kendisi oluşturur)
  3. .env.example dosyasını .env adıyla kopyalayın
  4. .env içine ANTHROPIC_API_KEY değerinizi yazın (console.anthropic.com)
  5. Tarayıcıdan açıp [email protected] / Admin1234 ile giriş yapın

Veritabanı şeması projedeki database.sql dosyasında.

Nasıl Çalışıyor?

Çılgın Yazılım

Yapay Zekâ Entegrasyonu (Gemini · Groq · Claude)

PHP 8 · PDO · MySQL · Ücretsiz API Anahtarıyla Çalışır · Jeton ve Maliyet Takibi · Değiştirilebilir Sağlayıcı · Çılgın Yazılım Tasarım Kalıbı

Ücretsiz bir anahtarla çalışır. Anahtar sunucuda kalır, her yanıtın maliyeti hesaplanır, sohbet geçmişi veritabanında durur.

PHP
MySQL
Gemini
Sağlayıcı
Composer
License

🇹🇷 Türkçe · 🇬🇧 English

▶ Canlı Demo · Kaynak Kütüphanesi · cilginyazilim.com


Canlı Demo

Kurulum yok, kayıt yok, indirme yok — tarayıcınızdan 3 saniyede deneyin.

Canlı Demoyu Aç
Kaynak Kodu İncele
ZIP İndir

Yapay zekâ entegrasyonu canlı demo önizlemesi

▲ Görsele tıklayarak demoyu açabilirsiniz

Demo hesapları

RolE-postaParola
Yönetici[email protected]Admin1234
Kullanıcı[email protected]Demo1234

Demoda 60 saniyede neleri deneyebilirsiniz?

#Şunu deneyinPerde arkasında ne oluyor?
1Sohbetler sayfasını açın ve dört sayaca bakınSohbet · mesaj · toplam jeton · tahmini maliyet. Maliyet, model fiyat tablosundan hesaplanır; sabit bir sayı değildir
2"PDO ile hazır ifadeler" sohbetini Üç soru-cevap. Kullanıcı mesajları sağda mavi, yanıtlar solda; kod örnekleri girintisi korunarak basılır
3Her yanıtın altındaki jeton satırına bakın412 giriş · 386 çıkış jetonu · $0,011710. Bu rakam uydurma değil: giriş/1M × 5$ + çıkış/1M × 25$
4Aynı sohbette giriş jetonlarının arttığını izleyin: 412 → 890 → 1.503Model durum tutmaz; her istekte sohbetin tamamı yeniden gönderilir. Uzun sohbetlerin pahalılaşmasının sebebi tam olarak budur
5Sohbet başlığındaki rozete bakın: 3.977 jeton · $0,04333Sohbet toplamı, mesaj toplamlarına birebir eşittir. Sayfa kendi içinde çelişmez
6"N+1 sorgu problemi" sohbetini açıp "düşünme özetini göster"e basınModelin yanıtı kurmadan önceki muhakemesi. ` ile açılır — JavaScript gerekmez
7Kontrol panelindeki API Kurulumu kartına bakınAnahtarın tanımlı olup olmadığı, model, efor seviyesi ve uç nokta tek bakışta
8Aynı kartta "İstek sunucudan atılır" notunu okuyunAnahtar tarayıcıya asla gönderilmez. Anahtarı JavaScript'e koyup doğrudan API'ye istek atmak, onu sayfayı açan herkese vermektir
9Bir sohbeti Sile basınMesajlar ON DELETE CASCADE` ile birlikte gider; yetim satır kalmaz. Yalnızca kendi sohbetinizi silebilirsiniz
10Telefonunuzdan açınBalonlar genişler, sayaçlar alt alta dizilir; sayfa gövdesinde yatay kaydırma yoktur
Demoda API anahtarı yoktur — bu yüzden yeni bir yanıt üretilemez. Ekrandaki üç sohbet, arayüzü dolu hâliyle göstermek için elle yazılmış örneklerdir; jeton ve maliyet sayıları gerçek fiyat tablosuyla hesaplanmıştır. Kendi anahtarınızı .env dosyasına ekleyince gerçek yanıtlar gelmeye başlar.

Demo alanı hakkında bilinmesi gerekenler

KonuDurum
Verilerdatabase.sql içindeki 51 kullanıcı + 3 örnek sohbet + 14 mesaj. Gerçek kişi verisi yoktur.
API anahtarıDemoda yok. Sohbet gönderme kapalıdır; var olan sohbetler okunabilir.
Örnek sohbetlerGerçek API çağrısı değil, elle yazılmış örneklerdir. Jeton ve maliyet sayıları fiyat tablosuyla tutarlıdır.
SıfırlamaDemo veritabanı düzenli aralıklarla başlangıç hâline döner.
APP_DEBUGCanlıda kendiliğinden false — sunucu adından türetilir.
BağımlılıkSıfır. SDK yok, Composer yok, npm yok, CDN yok. İstek cURL ile atılır.

Bu Proje Nedir?

Bir yapay zekâ API'sini projeye bağlamak, ilk bakışta tek bir curl çağrısıdır. Gerçekte dört soru hemen ardından gelir ve dördü de hafife alınırsa pahalıya patlar:

  • Anahtar nerede duracak? JavaScript'e koyarsanız, sayfayı açan herkes onu görür ve sizin hesabınıza istek atabilir.
  • Bu ay ne kadar harcadım? Fatura ayın sonunda gelir; o zamana kadar hiçbir fikriniz yoktur.
  • Neden ikinci soru birinciden pahalı? Çünkü model durum tutmaz: her istekte sohbetin tamamı yeniden gönderilir ve yeniden ücretlendirilir.
  • API hata verdiğinde ne olacak? 429 ve 529 geçicidir, tekrar denenmelidir; 401 kalıcıdır, denemenin anlamı yoktur. İkisini ayırt etmeyen kod ya boşuna uğraşır ya da erken pes eder.

Bu proje dördünü de ele alan bir entegrasyon katmanı kuruyor. Anahtar .env içinde durur ve yalnızca sunucuda kullanılır. Her yanıtın giriş/çıkış jetonu ve hesaplanmış maliyeti kaydedilir. Sohbet geçmişi veritabanındadır. Hata yönetimi geçici ile kalıcıyı ayırır ve geçici olanları üstel geri çekilmeyle yeniden dener.

Modelin "düşünme" özeti de saklanır ve arayüzde istenirse açılır — yanıtın nasıl kurulduğunu görmek, çıktıyı değerlendirmenin en pratik yoludur.

Sağlayıcı sınıfları hiçbir SDK kullanmaz; istek cURL ile atılır, yanıt elle ayrıştırılır.

Ve en önemlisi: denemek için para gerekmiyor. Varsayılan sağlayıcı Google Gemini'dir ve ücretsiz katmanı vardır — aistudio.google.com/apikey adresinden kredi kartı vermeden, birkaç saniyede anahtar alırsınız.

Kimler için uygun?

  • Projesine yapay zekâ özelliği ekleyecek, ama maliyeti kontrol altında tutmak isteyenler
  • API anahtarını nereye koyacağını soran herkes
  • Jeton muhasebesinin nasıl yapıldığını öğrenmek isteyenler
  • SDK kurmadan, saf PHP ile bir yapay zekâ API'sine bağlanmak isteyenler
  • Sağlayıcıya kilitlenmeden, tek ayarla Gemini/Groq/Claude arasında geçiş yapmak isteyenler
  • Bootstrap 5 üzerine kurulu, tekrar kullanılabilir bir panel kalıbı arayanlar

Bu proje, Çılgın Yazılım Kütüphanesi altında yayınlanan açıklamalı, üretime hazır örneklerden biridir.


İçindekiler


Ekran Görüntüleri

Sohbetler

Dört sayaç jeton muhasebesinin özetidir: sohbet · mesaj · toplam jeton · tahmini maliyet. Maliyet sabit bir sayı değil, model fiyat tablosundan hesaplanır (giriş/1M × 5$ + çıkış/1M × 25$). Her sohbet satırı kendi jeton ve maliyet rozetini taşır.

Sohbet listesi: sohbet, mesaj, jeton ve tahmini maliyet sayaçları

Sohbet detayı

Kullanıcı mesajları sağda, yanıtlar solda; kod örnekleri girintisi korunarak basılır. Her yanıtın altındaki satır o mesajın giriş/çıkış jetonunu ve maliyetini verir. Giriş jetonlarının mesaj mesaj artışı (412 → 890 → 1.503) burada gözle görülür: model durum tutmaz, her istekte sohbetin tamamı yeniden gönderilir ve yeniden ücretlendirilir. Modelin düşünme özeti `` ile açılır — JavaScript gerekmez.

Sohbet detayı: mesaj balonları, mesaj başına jeton ve maliyet satırı, açılabilir düşünme özeti

Kontrol paneli

API Kurulumu kartı anahtarın tanımlı olup olmadığını, modeli, efor seviyesini ve uç noktayı tek bakışta verir. Kartın altındaki not, isteğin sunucudan atıldığını söyler: anahtar tarayıcıya asla gönderilmez.

Kontrol paneli: sayaç şeridi ve API kurulum kartı

Giriş ekranı

Demo hesapları tek tıkla doldurulur. Giriş denemeleri hız sınırına tabidir; art arda başarısız denemeden sonra hesap geçici olarak kilitlenir.

Giriş ekranı: demo hesapları tek tıkla doldurulur

Koyu tema

Tema tarayıcıda değil kullanıcı hesabında saklanır; başka bir cihazdan girdiğinizde de aynı gelir. Sohbet balonlarının zemin ve metin renkleri koyu temada ayrıca ölçülüdür.

Koyu tema görünümü

Mobil görünüm

390px genişlikte balonlar genişler, sayaçlar alt alta dizilir ve alt navigasyon devreye girer. Sayfa gövdesinde yatay kaydırma yoktur.

390px genişlikte mobil görünüm

Bir isteğin yolculuğu

 TARAYICI                          SUNUCU (PHP)                    SAĞLAYICI API
 ────────                          ────────────                    ─────────────
 Kullanıcı mesajı yazar
    │  POST /api/chat/send
    │  (CSRF jetonu ile)
    ▼
                          ai_messages'a "user" satırı
                                   │
                                   │  Ai::fromEnv()->send()
                                   │  (gemini · groq · claude …)
                                   │
                                   │  1) SOHBETİN TAMAMINI topla
                                   │     (model durum tutmaz)
                                   │
                                   │  2) x-goog-api-key: <ANAHTAR> ◄─ ANAHTAR
                                   │     (Claude'da x-api-key)        BURADA KALIR
                                   │                                  tarayıcıya
                                   │  3) POST …:generateContent ──────► GİTMEZ
                                   │                                     │
                                   │                                     ▼
                                   │  ◄────────────────────── 200 / 429 / 529 / 4xx
                                   │
                                   │  4) 429 · 529 · 5xx  → GEÇİCİ
                                   │     üstel geri çekilme + jitter
                                   │     en fazla 3 kez yeniden dene
                                   │
                                   │     401 · 400        → KALICI
                                   │     denemeden vazgeç, açıkla
                                   │
                                   │  5) Yanıtı ayrıştır:
                                   │     text · thinking · stop_reason
                                   │     refusal · usage
                                   │
                                   │  6) MALİYET HESAPLA
                                   │     giriş/1M×5$ + çıkış/1M×25$
                                   ▼
                          ai_messages'a "assistant" satırı
                          (metin · düşünme · jetonlar · maliyet)
                                   │
                          ai_conversations toplamları güncellenir
                                   │
    ◄──────────────────────── JSON yanıt
    ▼
 Balon çizilir, jeton satırı basılır

Kritik Kararlar

1. API anahtarı asla tarayıcıya gitmez

İnternetteki örneklerin şaşırtıcı bir kısmı anahtarı JavaScript'e koyar ve fetch ile doğrudan API'ye gider. Bu, anahtarı sayfayı açan herkese vermektir: F12 → Sources yeterlidir. Anahtarınızla başkaları istek atar, faturayı siz ödersiniz.

Bu projede istek her zaman sunucudan atılır:

Tarayıcı → (kendi sunucunuz) → Sağlayıcı API

Anahtar .env dosyasındadır, .gitignore içindedir ve PHP dışına hiç çıkmaz. Tarayıcı yalnızca kendi sunucunuzla konuşur — CSRF korumalı, oturum gerektiren bir uç nokta üzerinden.

2. Her mesajın jetonu ve maliyeti kaydedilir

Fatura ayın sonunda gelir. O zamana kadar hangi özelliğin ne kadar harcadığını bilmiyorsanız, maliyeti yönetemezsiniz.

ai_messages: input_tokens · output_tokens · cost_usd
ai_conversations: total_tokens · total_cost

Sayılar API'nin usage alanından gelir — tahmin değil, ölçümdür. Maliyet ise fiyat tablosundan hesaplanır.

Sohbet satırındaki toplam, mesajların toplamına eşittir; iki yerde tutulan bir sayının tutarsız kalması, güvenilmez bir gösterge demektir.

3. Giriş jetonu neden her mesajda artıyor?

Çünkü model durum tutmaz. "Önceki mesajımı hatırla" diye bir şey yoktur; her istekte sohbetin tamamını yeniden gönderirsiniz ve tamamı yeniden ücretlendirilir.

Örnek sohbette bunu somut görürsünüz: 412 → 890 → 1.503. Üçüncü soru, birincinin dört katı giriş jetonu tüketir — soru aynı uzunlukta olsa bile.

Bu, uzun sohbetlerin neden pahalılaştığını açıklar. Pratik sonuç: geçmişi sınırsız göndermeyin. Uzun sohbetlerde eski mesajları özetleyip özeti göndermek yaygın ve etkili bir kalıptır.

4. Geçici hata ile kalıcı hata ayrılır

$retryable = $status === 429 || $status === 529 || $status >= 500;
KodAnlamıNe yapılır
429Hız sınırıGeçici — geri çekilmeyle yeniden dene
529Servis yoğunGeçici — yeniden dene
5xxSunucu hatasıGeçici — yeniden dene
401Anahtar geçersizKalıcı — denemenin anlamı yok
400İstek hatalıKalıcı — istek düzeltilmeli

Ayrım yapmayan kod iki yönden de kaybeder: 401 için üç kez deneyip zaman kaybeder, 429 için hiç denemeyip kullanıcıya gereksiz hata gösterir.

Yeniden denemeler üstel geri çekilme + jitter ile yapılır. Jitter (rastgele sapma) önemlidir: aynı anda sınıra çarpan yüz istemci aynı anda tekrar denerse sınıra yine birlikte çarparlar.

5. stop_reason ve refusal göz ardı edilmez

Bir yanıt "başarılı" dönebilir ama tamamlanmamış olabilir:

  • stop_reason = "max_tokens" → yanıt cümlenin ortasında kesildi. Kullanıcıya eksik bir metni tam gibi göstermek yanlıştır.
  • refusal dolu → model bilerek yanıtlamadı. Bu bir hata değildir ve "sunucu hatası" diye gösterilmemelidir.

İkisi de ayrı ele alınır ve arayüzde ayrı biçimde gösterilir.

AI_MAX_TOKENS varsayılanı bu yüzden 16.000'dir. Düşük tutmak tasarruf gibi görünür ama kesilen yanıtı baştan sormak zorunda kalırsınız — yani iki kez ödersiniz.

6. "Düşünme" özeti saklanır, ama gizlenerek gösterilir

Modelin muhakemesi, çıktıyı değerlendirmenin en pratik yoludur: yanıt yanlışsa nerede saptığını görürsünüz.

Ama her zaman ekranda durması gerekmez; `` etiketiyle katlanır. JavaScript gerekmez, erişilebilirlik kendiliğinden doğrudur.

7. Metin pre-wrap ile basılır, nl2br() ile değil

Model yanıtları kod örneği içerir ve kodun girintisi anlamlıdır.

.cy-msg__bubble { white-space: pre-wrap; }

pre-wrap hem satır sonlarını hem girintileri korur, uzun satırları da kırar. Üstüne nl2br() eklemek her satır sonunu iki kez uygular ve paragraf araları iki katına çıkar — kod içeren yanıtlarda ekran dağılır.

Aynı sebeple balonun ` etiketiyle içeriği arasında boşluk bırakılmaz: pre-wrap`, şablonun kendi girintisini de metin sayar ve her balonun ilk satırı sağa kaymış görünürdü.

8. Sohbet silinince mesajlar da gider

CONSTRAINT fk_msg_conv FOREIGN KEY (conversation_id)
    REFERENCES ai_conversations (id) ON DELETE CASCADE

Uygulama kodunda "önce mesajları sil, sonra sohbeti sil" yazılsaydı, o iki adımın arasında bir hata oluştuğunda yetim mesajlar kalırdı. Kısıtı veritabanına koymak bunu imkânsız kılar.


Neler Var?

API katmanı

  • Messages API'sine cURL ile istek — SDK yok
  • Anahtar yalnızca sunucuda
  • Üstel geri çekilme + jitter, en fazla 3 deneme
  • Geçici (429/529/5xx) ve kalıcı hata ayrımı
  • stop_reason ve refusal ayrı ele alınır
  • Model ve efor seviyesi .env'den
  • Yapılandırılabilir max_tokens

Muhasebe

  • Mesaj başına giriş/çıkış jetonu
  • Mesaj başına maliyet (fiyat tablosundan)
  • Sohbet başına toplam jeton ve maliyet
  • Panelde dört sayaç

Sohbet arayüzü

  • Sohbet listesi, jeton ve maliyet sütunlu
  • Kullanıcı/model balonları, kod girintisi korunur
  • Katlanabilir "düşünme özeti" (``)
  • Mesaj altında jeton ve maliyet satırı
  • Sohbet silme (yalnızca kendi sohbeti)
  • Anahtar yoksa açıklayıcı uyarı

Ortak altyapı

  • Oturum girişi, "beni hatırla", hız sınırı, CSRF
  • CSP (script-src 'self'), X-Frame-Options: DENY
  • Açık / koyu tema, hesaba kayıtlı
  • Mobilde alt navigasyon, yatay kaydırma yok
  • Kullanıcılar sayfasında canlı filtre (JS kapalıysa da çalışır)

Maliyet nasıl hesaplanıyor?

Fiyatlar milyon jeton başınadır ve her sağlayıcının kendi PRICING tablosundadır. Ücretsiz katmanda gerçek maliyetiniz sıfırdır; rakam yine de gösterilir, çünkü asıl soru "bugün ne ödedim?" değil, "bu uygulama yayına çıkarsa ne öderim?" sorusudur.

GeminiProvider::PRICING — ücretli katman:

ModelGirişÇıkış
gemini-2.5-flash-lite0,10 $0,40 $
gemini-2.5-flash0,30 $2,50 $
gemini-3.8-flash0,75 $3,75 $

ClaudeProvider::PRICING:

ModelGiriş (1M jeton)Çıkış (1M jeton)
claude-opus-55,00 $25,00 $
claude-sonnet-52,00 $10,00 $
claude-haiku-4-51,00 $5,00 $

public static function estimateCost(string $model, array $usage): float
{
    $rates = self::PRICING[$model] ?? null;
    if ($rates === null) { return 0.0; }

    return ($usage['input_tokens']  / 1000000) * $rates['input']
         + ($usage['output_tokens'] / 1000000) * $rates['output'];
}

Örnek: 412 giriş + 386 çıkış jetonu →

412 / 1.000.000 × 5  = 0,00206
386 / 1.000.000 × 25 = 0,00965
                       ─────────
                       0,01171 $

Demodaki sohbetlerde bu hesabı satır satır doğrulayabilirsiniz.

Bu bir tahmindir, fatura değildir. Fiyatlar koda gömülüdür ve değişebilir; ayrıca ön belleğe yazılan ve ön bellekten okunan jetonlar farklı oranlarla ücretlendirilir — burada yalnızca normal giriş ve çıkış hesaba katılır. Yine de büyüklük mertebesini görmek çok değerlidir: "bu sohbet 0,04 dolar" bilgisi, model veya efor seviyesi değiştirme kararını somut hâle getirir.

Tanımlı olmayan bir model adı için 0.0 döner — yanlış bir rakam göstermektense hiç göstermemek yeğdir.


Hata yönetimi

Sağlayıcının send() metodu her durumda anlaşılır bir sonuç döndürür; ham bir istisna arayüze sızmaz.

DurumKullanıcı ne görür
Anahtar tanımsız"API anahtarı tanımlı değil" + .env satırı
401"Anahtar geçersiz" — yeniden denenmez
429 (3 denemeden sonra)"Hız sınırına takıldınız ve yeniden denemeler de yetmedi."
529 / 5xx"Servis geçici olarak yoğun"
stop_reason = max_tokensYanıt gösterilir + kesildiği belirtilir
refusal doluModelin yanıtlamama gerekçesi, hata olarak değil
Ağ hatası / zaman aşımıGeri çekilmeyle yeniden denenir, sonra açıklanır

Güvenlik: Neyi, Nasıl Kapattık?

AçıkTipik hatalı kodBu projede
API anahtarı sızıntısıAnahtarı JavaScript'e koyup tarayıcıdan istek atmakİstek yalnızca sunucudan; anahtar .env içinde, .gitignore'da
Anahtarın depoya gitmesiconfig.php içine yazmak.env dosyası depoya gönderilmez; .env.example şablondur
Başkasının sohbetini okuma (IDOR)WHERE id = :idSorguya user_id de katılır; kullanıcı yalnızca kendi sohbetini görür ve siler
Yetim kayıtUygulama kodunda iki adımlı silmeON DELETE CASCADE — veritabanı garanti eder
Sonsuz maliyetmax_tokens sınırsız / geçmiş sınırsızAI_MAX_TOKENS ayarlanabilir; jeton ve maliyet her mesajda kaydedilir
Boşuna yeniden denemeHer hatada 3 kez denemek401/400 kalıcı sayılır, denenmez
Eşzamanlı yeniden deneme fırtınasıSabit aralıklı tekrarÜstel geri çekilme + jitter
XSSecho $message['content']Sunucuda e(); ayrıca CSP script-src 'self'
CSRFGizli alan yokHer POST'ta jeton; hash_equals()
SQL enjeksiyonu"... WHERE id = $id"Tüm sorgular hazır ifade; ATTR_EMULATE_PREPARES = false
Hata sızıntısıİstisna mesajını ekrana basmakAPP_DEBUG ortamdan türetilir; canlıda ayrıntı gösterilmez
Bozuk UTF-8'de sessiz JSON kaybıjson_encode($v)JSON_INVALID_UTF8_SUBSTITUTE

Kurulum

Gereksinimler

PHP8.0 veya üzeri · curl eklentisi zorunlu
MySQL / MariaDB5.7+ / 10.3+
Web sunucusuApache (mod_rewrite) veya Nginx
Bir API anahtarıÜcretsiz: aistudio.google.com/apikey (Gemini) veya console.groq.com/keys (Groq)

Adımlar

git clone https://github.com/CilginYazilim/ai-integration-system.git
cd ai-integration-system

mysql -u root -p < database.sql
cp .env.example .env        # Windows: copy .env.example .env

.env dosyasına ücretsiz anahtarınızı ekleyin:

AI_PROVIDER=gemini
GEMINI_API_KEY=••••••••
Anahtarı aistudio.google.com/apikey adresinden
alırsınız: Google hesabıyla girin, Create API key, kopyalayın. Kredi kartı
istenmez, faturalandırma açmanız gerekmez.

Açın: http://localhost/ai-integration-system/ · Giriş: [email protected] / Admin1234

Anahtar olmadan da açılır. Var olan sohbetleri okuyabilir, arayüzü inceleyebilirsiniz; yalnızca yeni yanıt üretilemez. Panel bunu açıkça söyler.

Yapılandırma

APP_DEBUG=true          # silerseniz: yerelde açık, canlıda kapalı
APP_URL=
APP_PRETTY_URLS=true

DB_HOST=127.0.0.1
DB_PORT=3306
DB_NAME=cy_ai
DB_USER=root
DB_PASS=

# --- YAPAY ZEKÂ SAĞLAYICISI ---
AI_PROVIDER=gemini
GEMINI_API_KEY=
••••••••
AI_MAX_TOKENS=••••••••
AyarNe yapar
AI_PROVIDERgemini · groq · openai-compatible · claude. Varsayılan gemini
AI_MODELKullanılacak model. Boş bırakırsanız sağlayıcının varsayılanı kullanılır
AI_MAX_TOKENSYanıtın en fazla kaç jeton olacağı. Düşük tutmayın: sınıra çarpan yanıt cümlenin ortasında kesilir ve baştan sormanız gerekir
AI_BASE_URLYalnızca openai-compatible için: hangi servise bağlanılacağı
AI_THINKINGGemini'de düşünme özeti istensin mi (varsayılan kapalı)
AI_EFFORTClaude'da düşünme derinliği. Yükseltmek daha iyi yanıt verir ama hem süreyi hem maliyeti artırır

Sağlayıcı seçmek

Uygulama sağlayıcıyı bilmez. App\Core\Ai\Ai::fromEnv() ayara bakıp doğru
sınıfı üretir; denetleyici ve arayüz aynı kalır. Sağlayıcı değiştirmek üç satırdır:

Sağlayıcı.envÜcretsiz katmanAnahtar
Google Gemini (varsayılan)

AI_PROVIDER=gemini
GEMINI_API_KEY=…
AI_MODEL=gemini-2.5-flash | var | aistudio.google.com/apikey |
| Groq | AI_PROVIDER=groq
GROQ_API_KEY=…
AI_MODEL=llama-3.3-70b-versatile | var | console.groq.com/keys |
| xAI (Grok) · OpenRouter · Ollama | AI_PROVIDER=openai-compatible
AI_API_KEY=…
AI_BASE_URL=https://api.x.ai/v1 | sağlayıcıya göre | sağlayıcının paneli |
| Anthropic Claude | AI_PROVIDER=claude
ANTHROPIC_API_KEY=…
AI_MODEL=claude-sonnet-5 | yok (ücretli) | console.anthropic.com |

Neden varsayılan ücretsiz bir sağlayıcı? Bu bir öğrenme örneğidir ve önündeki en
pahalı engel kredi kartıydı: kodu okumak isteyen çoğu kişi çalışır hâlini hiç
görmeden ayrılıyordu.

Yeni bir sağlayıcı eklemek HttpProvider sınıfını genişletip dört soruyu
cevaplamaktır: nereye (endpoint), hangi başlıkla (headers), hangi gövdeyle
(payload), gelen yanıt ortak biçime nasıl çevrilir (normalize). Yeniden deneme,
geri çekilme ve cURL ayarları zaten ortak katmandadır.


Dosya Yapısı

ai-integration-system/
│
├── index.php                     Ön denetleyici — TEK giriş noktası
├── database.sql                  Şema + 51 kullanıcı + 3 sohbet + 14 mesaj
├── .env.example
│
├── app/
│   ├── Core/
│   │   ├── Ai/                   ★ SAĞLAYICI KATMANI
│   │   │   ├── Ai.php                Fabrika — AI_PROVIDER'a bakar
│   │   │   ├── Provider.php          Arayüz + ortak yanıt sözleşmesi
│   │   │   ├── HttpProvider.php      cURL · yeniden deneme · geri çekilme
│   │   │   ├── GeminiProvider.php    Varsayılan — ücretsiz katman
│   │   │   ├── OpenAiCompatibleProvider.php  Groq · xAI · OpenRouter · Ollama
│   │   │   └── ClaudeProvider.php    Anthropic Messages API
│   │   ├── Auth.php · Session.php · Csrf.php · RateLimiter.php
│   │   ├── Database.php          PDO (EMULATE_PREPARES = false)
│   │   ├── Env.php               .env okuyucu + isLocalHost()
│   │   └── ...
│   │
│   ├── Http/Controllers/
│   │   ├── ChatController.php    Sohbet listesi, detay, silme
│   │   ├── Api/ChatApiController.php   Mesaj gönderme (AJAX)
│   │   └── Auth · Dashboard · User
│   │
│   ├── Repositories/ConversationRepository.php
│   └── Support/helpers.php
│
├── views/
│   ├── chat/index.php            Sohbet listesi + sayaçlar
│   ├── chat/show.php             ★ Balonlar · düşünme özeti · jeton satırı
│   └── ...
│
├── assets/
│   ├── css/  cilginyazilim.css (marka) · admin.css · feature.css
│   └── js/   chat.js · app.js · login.js · users.js
│
├── config/config.php
├── routes/web.php
└── docs/screenshots/

Veritabanı Şeması

ai_conversations

SütunTipİşi
idINT UNSIGNEDBirincil anahtar
user_idINT UNSIGNEDSohbet kimin (ON DELETE CASCADE)
titleVARCHAR(150)Listede görünen ad
total_tokensINT UNSIGNEDGiriş + çıkış toplamı
total_costDECIMAL(12,8)Toplam maliyet (USD)
created_at · updated_atDATETIMEAçılış ve son mesaj anı

ai_messages

SütunTipİşi
idBIGINT UNSIGNEDBirincil anahtar
conversation_idINT UNSIGNEDHangi sohbet (ON DELETE CASCADE)
roleENUM('user','assistant')Kim yazdı
contentMEDIUMTEXTMesaj metni
thinkingMEDIUMTEXT NULLModelin muhakemesi (varsa)
input_tokens · output_tokensINT UNSIGNEDAPI'nin usage alanından
cost_usdDECIMAL(12,8)Bu mesajın maliyeti
created_atDATETIMEYazılma anı
KararNeden
DECIMAL(12,8), FLOAT değilPara hiçbir zaman kayan noktalı sayıyla tutulmaz; FLOAT toplamları sessizce kaydırır. Sekiz ondalık, tek bir mesajın kesirli sentini taşır
Toplamlar sohbet satırında da varListe sayfası her satır için mesaj tablosunu toplasaydı N+1 sorgu doğardı
thinking ayrı sütunYanıt metniyle karıştırılmamalı; ayrıca isteğe bağlı gösterilir
role ENUMİki değer vardır ve üçüncüsü bir hatadır; veritabanı bunu kendisi engeller
Kullanıcı mesajında jeton 0Ücretlendirme, o mesajı da içeren API çağrısının yanıt satırında toplanır; iki yerde saymak toplamı bozardı
ON DELETE CASCADESohbet silinince mesajlar da gider; yetim satır imkânsız olur

SSS

API anahtarını JavaScript'e koysam ne olur?

Sayfayı açan herkes onu görür. F12 → Sources yeterlidir; ağ sekmesinde istek başlıklarında da durur.

Sonuç: anahtarınızla başkaları istek atar, faturayı siz ödersiniz. Anahtarı iptal edip yenisini üretmekten başka çareniz kalmaz.

Kural basit: API anahtarı sunucudan çıkmaz. Tarayıcı yalnızca kendi sunucunuzla konuşur.

Maliyeti nasıl düşürürüm?

Dört kaldıraç var, etkileri sırasıyla:

  1. Sağlayıcı. En büyük fark burada: Gemini ve Groq'un ücretsiz katmanları vardır. Bir yan projede fatura hiç başlamayabilir.
  2. Model. Aynı ailede bile fark büyüktür: gemini-2.5-flash-lite çıkışta gemini-2.5-flash'in altıda biri, claude-haiku-4-5 ise claude-opus-5'in beşte biri fiyattadır. Sınıflandırma, özetleme, biçimlendirme gibi işlerde aradaki kalite farkı çoğu zaman fark edilmez.
  3. Gönderdiğiniz geçmiş. Giriş jetonu her mesajda birikir. Uzun sohbetlerde eski mesajları özetleyip özeti gönderin.
  4. Efor seviyesi. Claude'da AI_EFFORT düşürmek düşünme jetonlarını azaltır.

Hangisinin işe yaradığını görmek için paneldeki maliyet sayacına bakın — ölçmeden optimize etmeyin.

Yanıt cümlenin ortasında kesiliyor

stop_reason = "max_tokens" demektir: yanıt AI_MAX_TOKENS sınırına çarptı.

.env içindeki değeri yükseltin. Düşük tutmak tasarruf gibi görünür ama kesilen yanıtı baştan sormak zorunda kalırsınız — yani iki kez ödersiniz. Varsayılan 16.000 çoğu iş için yeterlidir.

Çok uzun yanıtlar için akış (streaming) gerekir; bu örnekte akışsız istek kullanılmıştır.

Neden resmi SDK'yı kullanmadınız?

Bu proje Messages API'sinin nasıl çalıştığını göstermek için yazıldı: hangi başlıklar gidiyor, yanıt nasıl ayrışıyor, hangi hata kodu geçici.

Sağlayıcı sınıfları her adımı yorumlarla açıklar; ortak HTTP ve yeniden deneme katmanı HttpProvider içinde bir kez yazılmıştır. Ayrıca Composer bağımlılığı olmaması, paylaşımlı hosting'e atıp çalıştırabilmeniz demektir.

Üretimde SDK kullanmak isterseniz, artık onun ne yaptığını biliyor olacaksınız.

429 alıyorum, ne yapmalıyım?

429 hız sınırıdır ve geçicidir. Uygulama zaten üstel geri çekilmeyle en fazla 3 kez yeniden dener.

Üç deneme de yetmiyorsa istek hızınız hesabınızın sınırının üstünde demektir. Seçenekler: istekleri kuyruğa alıp yavaşça göndermek, daha küçük bir model kullanmak veya hesap limitinizi yükseltmek.

Kuyruk örneği için İş Kuyruğu ve Worker Sistemi'ne bakın.

Sohbet geçmişini sınırsız göndermek zorunda mıyım?

Hayır ve göndermemelisiniz. Model durum tutmadığı için her istekte tüm geçmiş yeniden ücretlendirilir; 50 mesajlık bir sohbette 51. soru çok pahalıya gelir.

Yaygın kalıp: son N mesajı ham gönderin, daha eskileri tek bir özet mesajına indirin. Özeti üretmek de bir API çağrısıdır ama bir kez ödenir.


Canlı Ortama Alırken

  • [ ] .env içinde APP_DEBUG=false (veya satırı tümüyle silin)
  • [ ] API anahtarı yalnızca .env içinde; depoda değil
  • [ ] .env dosyasının tarayıcıdan erişilemediğini doğrulayın (403 dönmeli)
  • [ ] AI_MODEL ve AI_MAX_TOKENS bütçenize göre ayarlanmış mı?
  • [ ] Kullanıcı başına günlük istek sınırı düşünün (bu örnekte yoktur)
  • [ ] Maliyet sayaçlarını düzenli izleyin
  • [ ] HTTPS zorunlu olsun
  • [ ] Veritabanı için root olmayan bir kullanıcı açın
  • [ ] Demo hesaplarının parolalarını değiştirin veya hesapları silin

Sorun Giderme

BelirtiSebepÇözüm
"API anahtarı tanımlı değil".env boş veya okunmuyorSeçili sağlayıcının anahtar satırını kontrol edin (GEMINI_API_KEY, GROQ_API_KEY, ANTHROPIC_API_KEY)
401 geliyorAnahtar geçersiz veya iptal edilmişKonsoldan yeni anahtar üretin
429 sürekliHız sınırıİstekleri yavaşlatın veya kuyruğa alın
Yanıt kesiliyormax_tokens sınırıAI_MAX_TOKENS değerini yükseltin
Maliyet 0,00 görünüyorÜcretsiz katmandasınız (doğru) ya da AI_MODEL fiyat tablosunda yokGerekirse sağlayıcının PRICING tablosuna modeli ekleyin
Bağlantı zaman aşımıcurl eklentisi yok veya giden bağlantı kapalıphp -m \grep curl; sunucu güvenlik duvarını kontrol edin
Türkçe karakterler bozukBağlantı karakter kümesicharset=utf8mb4 olduğunu doğrulayın
Tüm adresler 404mod_rewrite kapalıAçın veya APP_PRETTY_URLS=false yapın

Yol Haritası

  • [ ] Akış (streaming) desteği — yanıt yazılırken göstermek
  • [ ] Kullanıcı başına günlük jeton/maliyet kotası
  • [ ] Uzun sohbetlerde otomatik özetleme
  • [ ] Sistem istemi (system prompt) yönetimi
  • [ ] Araç kullanımı (tool use) örneği
  • [ ] Ön bellek (prompt caching) jetonlarının ayrı sayılması

Katkı

Hata bildirimi ve öneriler için issue açabilirsiniz.

Lisans

MIT — ticari projelerinizde de özgürce kullanabilirsiniz.


Kaynak Kod soldaki ağaçtan bir dosya seçin

  • app
    • Core
      • Ai
        • Ai.php 9.2 KB
        • ClaudeProvider.php 9.4 KB
        • GeminiProvider.php 12.1 KB
        • HttpProvider.php 8.9 KB
        • OpenAiCompatibleProvider.php 6.7 KB
        • Provider.php 3 KB
      • Auth.php 13.9 KB
      • Autoloader.php 2.2 KB
      • Config.php 1.9 KB
      • Csrf.php 2.8 KB
      • Database.php 3.5 KB
      • Env.php 4.3 KB
      • Flash.php 2.6 KB
      • Middleware.php 2.5 KB
      • Paginator.php 8.6 KB
      • RateLimiter.php 3.9 KB
      • Request.php 4.5 KB
      • Response.php 7 KB
      • Router.php 6.3 KB
      • Session.php 6.3 KB
      • Validator.php 8.8 KB
      • View.php 3.5 KB
    • Http
      • Controllers
        • Api
          • ChatApiController.php 6.5 KB
          • PreferenceApiController.php 2.5 KB
        • AuthController.php 3.2 KB
        • ChatController.php 4.3 KB
        • DashboardController.php 1.2 KB
        • UserController.php 3.1 KB
      • Controller.php 1.3 KB
    • Models
      • User.php 5.9 KB
    • Repositories
      • ActivityRepository.php 3.8 KB
      • ConversationRepository.php 9.7 KB
      • RememberTokenRepository.php 5.8 KB
      • UserRepository.php 6.9 KB
    • Support
      • helpers.php 12.3 KB
    • .htaccess 851 B
  • assets
    • css
      • admin.css 58.6 KB
      • bootstrap.min.css 227.5 KB
      • cilginyazilim.css 29.8 KB
      • feature.css 5.9 KB
    • images
      • logo.png 70.4 KB
    • js
      • app.js 20.2 KB
      • bootstrap.bundle.js 203.2 KB
      • chat.js 5.6 KB
      • jquery-3.7.0.js 278.3 KB
      • login.js 1.8 KB
      • users.js 4.7 KB
  • config
    • .htaccess 196 B
    • config.php 5 KB
    • menu.php 1.1 KB
  • docs
    • screenshots
      • 01-giris.png 378.3 KB
      • 02-kontrol-paneli.png 74 KB
      • 03-sohbetler.png 86.4 KB
      • 04-sohbet-detay.png 158 KB
      • 05-koyu-tema.png 149.9 KB
      • 06-mobil.png 58.5 KB
  • routes
    • .htaccess 197 B
    • web.php 3.4 KB
  • views
    • auth
      • login.php 7.5 KB
    • chat
      • index.php 8.7 KB
      • show.php 4.7 KB
    • dashboard
      • _feature.php 4 KB
      • index.php 4 KB
    • errors
      • 404.php 844 B
      • 500.php 565 B
    • layouts
      • admin.php 6.8 KB
      • auth.php 2.9 KB
      • plain.php 1.3 KB
    • partials
      • bottomnav.php 2.1 KB
      • head_extra.php 414 B
      • pagination.php 4.9 KB
      • sidebar.php 2.7 KB
      • topbar.php 3.5 KB
    • users
      • index.php 6.8 KB
    • .htaccess 262 B
  • .gitattributes 2.3 KB
  • .gitignore 1.2 KB
  • .htaccess 6 KB
  • CHANGELOG.md 9.3 KB
  • database.sql 27.2 KB
  • index.php 5.4 KB
  • LICENSE 1.1 KB
  • README.en.md 34.9 KB
  • README.md 36.4 KB
Dosya seçilmedi
İncelemek istediğiniz dosyayı soldaki ağaçtan seçin.

Güvenlik gereği kaynak dosyalardaki parola, API anahtarı ve benzeri gizli değerler gösterilmeden önce maskelenir (••••••••).

Sık Sorulan Sorular

Sayfayı açan herkes onu görür; F12 ile kaynak sekmesi yeterlidir. Sonuç: anahtarınızla başkaları istek atar, faturayı siz ödersiniz. Kural basittir: API anahtarı sunucudan çıkmaz, tarayıcı yalnızca kendi sunucunuzla konuşur.

Üç kaldıraç var. Model seçimi en büyüğüdür: claude-haiku-4-5 çıkışta claude-opus-5'in beşte biri fiyattadır. İkincisi gönderdiğiniz geçmiş: giriş jetonu her mesajda birikir, uzun sohbetlerde eski mesajları özetleyip özeti gönderin. Üçüncüsü efor seviyesi. Hangisinin işe yaradığını görmek için paneldeki maliyet sayacına bakın.

Çünkü model durum tutmaz. "Önceki mesajımı hatırla" diye bir şey yoktur; her istekte sohbetin tamamı yeniden gönderilir ve yeniden ücretlendirilir. Örnek sohbette bunu somut görürsünüz: 412, sonra 890, sonra 1.503 jeton.

stop_reason değeri max_tokens demektir: yanıt AI_MAX_TOKENS sınırına çarpmıştır. .env içindeki değeri yükseltin. Düşük tutmak tasarruf gibi görünür ama kesilen yanıtı baştan sormak zorunda kalırsınız, yani iki kez ödersiniz.

Bu proje Messages API'sinin nasıl çalıştığını göstermek için yazıldı: hangi başlıklar gidiyor, yanıt nasıl ayrışıyor, hangi hata kodu geçici. Ayrıca Composer bağımlılığı olmaması, paylaşımlı hosting'e atıp çalıştırabilmeniz demektir.

Demo sunucusunda API anahtarı tanımlı değildir; herkese açık bir demoda gerçek bir anahtar bulundurmak faturayı ziyaretçilere açmak olurdu. Ekrandaki üç sohbet arayüzü dolu göstermek için elle yazılmış örneklerdir; jeton ve maliyet sayıları gerçek fiyat tablosuyla hesaplanmıştır.

Yorumlar 0 konuşma

Bu kod örneğine henüz yorum yapılmamış. Takıldığınız bir yer veya merak ettiğiniz bir ayrıntı varsa ilk soruyu siz sorun.

Soru Sor veya Yorum Yaz

Yorumunuz onaylandıktan sonra yayınlanır. Teknik sorularınıza ekibimiz yanıt verir.

Yayınlanmaz; yalnızca yanıt bildirimi için kullanılır.
En az 10 karakter.

İlgili Kod Örnekleri