İleri
uygulama-iskeleti/php-baslangic-sablonu
PHP Başlangıç Şablonu (CY PHP Starter)
- PHP 8.1
- PDO
- MySQL
- MariaDB
- +4
309 inceleme 150 canlı açılış 6 beğeni 2 yorum
Uygulama İskeleti İleri
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.
Veritabanı şeması projedeki database.sql dosyasında.
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:
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?
Bu proje, Çılgın Yazılım Kütüphanesi altında yayınlanan açıklamalı, üretime hazır örneklerden biridir.
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
İ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.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.
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_costusage 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.
Çü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.
$retryable = $status === 429 || $status === 529 || $status >= 500;
| Kod | Anlamı | Ne yapılır |
|---|---|---|
429 | Hız sınırı | Geçici — geri çekilmeyle yeniden dene |
529 | Servis yoğun | Geçici — yeniden dene |
5xx | Sunucu hatası | Geçici — yeniden dene |
401 | Anahtar geçersiz | Kalı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.
stop_reason ve refusal göz ardı edilmezBir 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.
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; <details> etiketiyle katlanır. JavaScript gerekmez, erişilebilirlik kendiliğinden doğrudur.
pre-wrap ile basılır, nl2br() ile değilModel 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<div>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ü.
CONSTRAINT fk_msg_conv FOREIGN KEY (conversation_id)
REFERENCES ai_conversations (id) ON DELETE CASCADEAPI katmanı
cURL ile istek — SDK yok429/529/5xx) ve kalıcı hata ayrımıstop_reason ve refusal ayrı ele alınır.env'denmax_tokensMuhasebe
Sohbet arayüzü
<details>)Ortak altyapı
script-src 'self'), X-Frame-Options: DENYFiyatlar 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:
| Model | Giriş | Çıkış |
|---|---|---|
gemini-2.5-flash-lite | 0,10 $ | 0,40 $ |
gemini-2.5-flash | 0,30 $ | 2,50 $ |
gemini-3.8-flash | 0,75 $ | 3,75 $ |
ClaudeProvider::PRICING:
| Model | Giriş (1M jeton) | Çıkış (1M jeton) |
|---|---|---|
claude-opus-5 | 5,00 $ | 25,00 $ |
claude-sonnet-5 | 2,00 $ | 10,00 $ |
claude-haiku-4-5 | 1,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'];
}412 / 1.000.000 × 5 = 0,00206
386 / 1.000.000 × 25 = 0,00965
─────────
0,01171 $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.
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.
| Durum | Kullanı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_tokens | Yanıt gösterilir + kesildiği belirtilir |
refusal dolu | Modelin yanıtlamama gerekçesi, hata olarak değil |
| Ağ hatası / zaman aşımı | Geri çekilmeyle yeniden denenir, sonra açıklanır |
| Açık | Tipik hatalı kod | Bu 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 gitmesi | config.php içine yazmak | .env dosyası depoya gönderilmez; .env.example şablondur |
| Başkasının sohbetini okuma (IDOR) | WHERE id = :id | Sorguya user_id de katılır; kullanıcı yalnızca kendi sohbetini görür ve siler |
| Yetim kayıt | Uygulama kodunda iki adımlı silme | ON DELETE CASCADE — veritabanı garanti eder |
| Sonsuz maliyet | max_tokens sınırsız / geçmiş sınırsız | AI_MAX_TOKENS ayarlanabilir; jeton ve maliyet her mesajda kaydedilir |
| Boşuna yeniden deneme | Her hatada 3 kez denemek | 401/400 kalıcı sayılır, denenmez |
| Eşzamanlı yeniden deneme fırtınası | Sabit aralıklı tekrar | Üstel geri çekilme + jitter |
| XSS | echo $message['content'] | Sunucuda e(); ayrıca CSP script-src 'self' |
| CSRF | Gizli alan yok | Her 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 basmak | APP_DEBUG ortamdan türetilir; canlıda ayrıntı gösterilmez |
| Bozuk UTF-8'de sessiz JSON kaybı | json_encode($v) | JSON_INVALID_UTF8_SUBSTITUTE |
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=••••••••
| Ayar | Ne yapar |
|---|---|
AI_PROVIDER | gemini · groq · openai-compatible · claude. Varsayılan gemini |
AI_MODEL | Kullanılacak model. Boş bırakırsanız sağlayıcının varsayılanı kullanılır |
AI_MAX_TOKENS | Yanı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_URL | Yalnızca openai-compatible için: hangi servise bağlanılacağı |
AI_THINKING | Gemini'de düşünme özeti istensin mi (varsayılan kapalı) |
AI_EFFORT | Claude'da düşünme derinliği. Yükseltmek daha iyi yanıt verir ama hem süreyi hem maliyeti artırır |
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 katman | Anahtar |
|---|---|---|---|
| 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.
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/
ai_conversations| Sütun | Tip | İşi |
|---|---|---|
id | INT UNSIGNED | Birincil anahtar |
user_id | INT UNSIGNED | Sohbet kimin (ON DELETE CASCADE) |
title | VARCHAR(150) | Listede görünen ad |
total_tokens | INT UNSIGNED | Giriş + çıkış toplamı |
total_cost | DECIMAL(12,8) | Toplam maliyet (USD) |
created_at · updated_at | DATETIME | Açılış ve son mesaj anı |
ai_messages| Sütun | Tip | İşi |
|---|---|---|
id | BIGINT UNSIGNED | Birincil anahtar |
conversation_id | INT UNSIGNED | Hangi sohbet (ON DELETE CASCADE) |
role | ENUM('user','assistant') | Kim yazdı |
content | MEDIUMTEXT | Mesaj metni |
thinking | MEDIUMTEXT NULL | Modelin muhakemesi (varsa) |
input_tokens · output_tokens | INT UNSIGNED | API'nin usage alanından |
cost_usd | DECIMAL(12,8) | Bu mesajın maliyeti |
created_at | DATETIME | Yazılma anı |
| Karar | Neden |
|---|---|
DECIMAL(12,8), FLOAT değil | Para 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 var | Liste sayfası her satır için mesaj tablosunu toplasaydı N+1 sorgu doğardı |
thinking ayrı sütun | Yanı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 CASCADE | Sohbet silinince mesajlar da gider; yetim satır imkânsız olur |
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:
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.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.
.env içinde APP_DEBUG=false (veya satırı tümüyle silin).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ı?| Belirti | Sebep | Çözüm |
|---|---|---|
| "API anahtarı tanımlı değil" | .env boş veya okunmuyor | Seçili sağlayıcının anahtar satırını kontrol edin (GEMINI_API_KEY, GROQ_API_KEY, ANTHROPIC_API_KEY) |
401 geliyor | Anahtar geçersiz veya iptal edilmiş | Konsoldan yeni anahtar üretin |
429 sürekli | Hız sınırı | İstekleri yavaşlatın veya kuyruğa alın |
| Yanıt kesiliyor | max_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 yok | Gerekirse 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 bozuk | Bağlantı karakter kümesi | charset=utf8mb4 olduğunu doğrulayın |
| Tüm adresler 404 | mod_rewrite kapalı | Açın veya APP_PRETTY_URLS=false yapın |
Hata bildirimi ve öneriler için issue açabilirsiniz.
MIT — ticari projelerinizde de özgürce kullanabilirsiniz.
İncelemek istediğiniz dosyayı ağaçtan seçin.
Güvenlik gereği kaynak dosyalardaki parola, API anahtarı ve benzeri gizli değerler gösterilmeden önce maskelenir (••••••••).
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.
Bu örneğin daha gelişmişi projenizde mi lazım?
İhtiyacınızı anlatın; bu yapıyı sizin sisteminize uyarlayalım ya da size özel geliştirelim.
Henüz yorum yok. Takıldığınız bir yer varsa ilk soruyu siz sorun.
Yorumunuz onaylandıktan sonra yayınlanır. Teknik sorularınıza ekibimiz yanıt verir.
İleri
uygulama-iskeleti/php-baslangic-sablonu
309 inceleme 150 canlı açılış 6 beğeni 2 yorum
Orta
uygulama-iskeleti/php-scheduler-cron
178 inceleme 50 canlı açılış
Orta
uygulama-iskeleti/php-multi-language-i18n
150 inceleme 48 canlı açılış