Laravel Uygulamalarında AI Entegrasyonu: Adım Adım Rehber
12 yılı aşkın bir süre boyunca PHP uygulamaları geliştirdikten sonra, nihayetinde bir üretim Laravel paneline AI destekli özellikler ekledim. Bu özellik, ham analiz verilerinden otomatik rapor özetleri oluşturmaktır. Yine de, PHP odaklı kaynakların bu konuda ne kadar az olduğunu görmek beni şaşırttı. Çoğu LLM (Büyük Dil Modeli) eğitimi, Python yazdığınızı varsayıyor.
- Ne Oluşturacağız?
- Adım 1: API Anahtarlarınızı Edinin
- Adım 2: İki API’yi Anlayın (95% Benzer)
- Adım 3: Sözleşmeyi ve Sürücüleri Oluşturun
- Adım 4: Sürücüyü Bir Servis Sağlayıcıda Bağlayın
- Adım 5: Gerçek Bir Özellik Geliştirin
- Adım 6: Üretim Güçlendirmesi (Eğitimleri Atlayan Kısım)
- Önbellekleme Yapın
- Uzun Görevleri Kuyruğa Alın
- Başarısızlıkları Zarifçe Yönetme
- Maliyetlerinizi Kontrol Altında Tutun
- Resmi SDK’ler Neden Olmalı?
- Kapanış
Bu yazıda, Claude API’sini ve OpenAI API’sini bir Laravel uygulamasına entegre etmeyi ele alacağım. Uygulamayı gerçekten üretime gönderebileceğiniz, temiz bir mimari ile yapacağız.
Ne Oluşturacağız?
Ham verileri alacak ve insan okur yazarlığına uygun bir özet döndürecek olan bir ReportSummaryService inşa edeceğiz. Ayrıca, konfigürasyon değişikliği ile Claude ve OpenAI arasındaki geçişleri yapabileceğiniz bir sürücü (driver) yapısı kuracağız.
Adım 1: API Anahtarlarınızı Edinin
Bu anahtarları .env dosyanıza ekleyin:
AI_PROVIDER=claude
ANTHROPIC_API_KEY=sk-ant-xxxxx
ANTHROPIC_MODEL=claude-sonnet-4-6
OPENAI_API_KEY=sk-xxxxx
OPENAI_MODEL=gpt-5-mini
⚠️ API anahtarlarını hardcode etmeyin. Onları komit etmeyin. Eğer bir anahtarı Git’e push ettiyseniz, derhal değiştirin.
Şimdi bunları config/services.php dosyasına kaydedin — bu, Laravel yöntemidir ve config() fonksiyonunu her yerde kullanarak konfigürasyon önbellekleme avantajından faydalanabilirsiniz:
'anthropic' => [
'key' => env('ANTHROPIC_API_KEY'),
'model' => env('ANTHROPIC_MODEL', 'claude-sonnet-4-6'),
],
'openai' => [
'key' => env('OPENAI_API_KEY'),
'model' => env('OPENAI_MODEL', 'gpt-5-mini'),
],
'ai' => [
'provider' => env('AI_PROVIDER', 'claude'),
],
Adım 2: İki API’yi Anlayın (95% Benzer)
Her iki API de basit REST API’lerdir. JSON gönderirsiniz, JSON alırsınız.
Claude (Mesajlar API’si):
POST https://api.anthropic.com/v1/messages
Headers:
x-api-key: YOUR_KEY
anthropic-version: 2023-06-01
content-type: application/json
OpenAI (Sohbet Tamamlamaları API’si):
POST https://api.openai.com/v1/chat/completions
Headers:
Authorization: Bearer YOUR_KEY
content-type: application/json
En Önemli Farklılıklar:
| Claude | OpenAI |
|---|---|
| Auth header: x-api-key | Authorization: Bearer |
| Extra header: anthropic-version required | – |
| max_tokens: Required | Optional |
| System prompt: Top-level system field | A message with role: system |
| Response text: content[0].text | choices[0].message.content |
Adım 3: Sözleşmeyi ve Sürücüleri Oluşturun
HTTP isteklerini controller’lar arasında dağıtmak yerine, bir sözleşme tanımlayalım:
namespace App\Services\AI;
interface AiClientInterface
{
public function complete(string $systemPrompt, string $userMessage, int $maxTokens = 1024): string;
}
Claude Sürücüsü
Laravel’in HTTP istemcisi burada harika bir temizlik sağlıyor — cURL boilerplate yok:
namespace App\Services\AI;
use Illuminate\Support\Facades\Http;
use RuntimeException;
class ClaudeClient implements AiClientInterface
{
public function complete(string $systemPrompt, string $userMessage, int $maxTokens = 1024): string
{
$response = Http::withHeaders([
'x-api-key' => config('services.anthropic.key'),
'anthropic-version' => '2023-06-01',
])
->timeout(60)
->retry(2, 500, function ($exception) {
return $exception->response?->status() >= 429;
})
->post('https://api.anthropic.com/v1/messages', [
'model' => config('services.anthropic.model'),
'max_tokens' => $maxTokens,
'system' => $systemPrompt,
'messages' => [
['role' => 'user', 'content' => $userMessage],
],
]);
if ($response->failed()) {
throw new RuntimeException('Claude API error: ' . $response->json('error.message', 'Unknown error'));
}
return $response->json('content.0.text', '');
}
}
OpenAI Sürücüsü
namespace App\Services\AI;
use Illuminate\Support\Facades\Http;
use RuntimeException;
class OpenAiClient implements AiClientInterface
{
public function complete(string $systemPrompt, string $userMessage, int $maxTokens = 1024): string
{
$response = Http::withToken(config('services.openai.key'))
->timeout(60)
->retry(2, 500, function ($exception) {
return $exception->response?->status() >= 429;
})
->post('https://api.openai.com/v1/chat/completions', [
'model' => config('services.openai.model'),
'max_completion_tokens' => $maxTokens,
'messages' => [
['role' => 'system', 'content' => $systemPrompt],
['role' => 'user', 'content' => $userMessage],
],
]);
if ($response->failed()) {
throw new RuntimeException('OpenAI API error: ' . $response->json('error.message', 'Unknown error'));
}
return $response->json('choices.0.message.content', '');
}
}
Adım 4: Sürücüyü Bir Servis Sağlayıcıda Bağlayın
Artık büyümenin sırlarını açıkladık — bir konfigürasyon değeri, uygulamanızın hangi sağlayıcıyı kullandığını belirler:
use App\Services\AI\AiClientInterface;
use App\Services\AI\ClaudeClient;
use App\Services\AI\OpenAiClient;
public function register(): void
{
$this->app->bind(AiClientInterface::class, function () {
return match (config('services.ai.provider')) {
'openai' => new OpenAiClient(),
default => new ClaudeClient(),
};
});
}
Tek bir satır değiştirerek sağlayıcıları değiştirebilirsiniz. Eğer bir sağlayıcıda bir kesinti veya fiyat değişikliği olursa, sadece bir anahtarı değiştirirsiniz. Bu soyutlama, gerçek bir değer sunar.
Adım 5: Gerçek Bir Özellik Geliştirin
Kendi işlerimden bir gerçek kullanım durumu: Günlük analiz verilerini yönetime okunabilir bir rapor olarak özetleme.
namespace App\Services;
use App\Services\AI\AiClientInterface;
class ReportSummaryService
{
public function __construct(private AiClientInterface $ai) {}
public function summarize(array $reportData): string
{
$systemPrompt = 'Özetine ihtiyacım var'; // Örneğin bir sistem yönlendirmesi yazmalısınız.
return $this->ai->complete(
systemPrompt: $systemPrompt,
userMessage: 'Bu veriyi özetleyin: ' . json_encode($reportData),
maxTokens: 500
);
}
}
Controller
Controller ince olmalıdır:
namespace App\Http\Controllers;
use App\Services\ReportSummaryService;
use Illuminate\Http\JsonResponse;
class ReportController extends Controller
{
public function summary(ReportSummaryService $summaryService): JsonResponse
{
$data = [
'total_visitors' => 12480,
'conversion_rate' => '3.2%',
'top_store' => 'Hyderabad Main Branch',
'change_vs_last_week' => '+14%',
];
return response()->json([
'summary' => $summaryService->summarize($data),
]);
}
}
Laravel’in konteyneri her şeyi enjekte eder. Controller’ın, özetin Claude veya OpenAI tarafından yazıldığını bilmesine gerek yoktur.
Adım 6: Üretim Güçlendirmesi (Eğitimleri Atlayan Kısım)
Önbellekleme Yapın
LLM çağrıları yavaş (1–10 saniye) ve maliyetlidir. Eğer girdi değişmediyse, API’yi tekrar çağırmayın:
use Illuminate\Support\Facades\Cache;
public function summarize(array $reportData): string
{
$cacheKey = 'report-summary:' . md5(json_encode($reportData));
return Cache::remember($cacheKey, now()->addHours(6), function () use ($reportData) {
return $this->ai->complete(/* ... */);
});
}
Benim durumumda bu, API maliyetlerini yaklaşık %70 oranında düşürdü — çoğu kullanıcı bir raporu günde birkaç kez talep ediyor.
Uzun Görevleri Kuyruğa Alın
Kullanıcının HTTP isteğini LLM için 10 saniye bekletmeyin. Kuyruktan bir iş gönderin, sonucu kaydedin ve hazır olduğunda bildirim yapın:
GenerateReportSummary::dispatch($reportId);
Başarısızlıkları Zarifçe Yönetme
LLM API’leri bazen başarısız olacaktır — oran sınırlamaları (rate limits), aşırı yüklü sunucular, zaman aşımları. Sürücülerimizdeki retry() çağrıları geçici hataları yönetir, ancak her zaman özelliği sarmalayarak uygulamanızın zarif bir şekilde bozulmasını sağlamalısınız:
try {
$summary = $summaryService->summarize($data);
} catch (RuntimeException $e) {
Log::warning('AI summary failed', ['error' => $e->getMessage()]);
$summary = null; // Kullanıcı, ham veri tablosunu görsün
}
Maliyetlerinizi Kontrol Altında Tutun
max_tokens değerini dikkatlice belirleyin. Bu, istek başına çıktı maliyetinizi sınırlar. Basit görevler için daha küçük modeller kullanın. Özetleme, sınıflandırma ve çıkarım genelde lider modelleri gerektirmiyor — Claude Haiku veya mini seviyedeki OpenAI modeli genellikle 10 kat daha ucuz ve yeterince iyidir. Gönderim yapmadan önce her iki sağlayıcının panellerinde harcama limitleri belirleyin.
Resmi SDK’ler Neden Olmalı?
Yukarıdaki her şey, olan biteni tam olarak görmenizi sağlamak için Laravel’in HTTP istemcisini kullanıyor. Daha büyük projeler için düşünün:
composer require anthropic-ai/sdk guzzlehttp/guzzle(PHP 8.1+ gerektirir) — streaming, pagination ve tekrarları sizin için yönetir.openai-php/laravel— Laravel’e özgü popüler bir topluluk paketi.
Ancak, buradaki gibi yalın versiyonu bir kez yazmayı yine de tavsiye ederim. Asıl API’yi anlamak, SDK sorunlarını daha sonrasında hata ayıklamak için çok daha kolay olacaktır.
Kapanış
Tüm resim:
- Keys in .env, referenced via config/services.php
- An AiClientInterface contract with a driver per provider
- Container binding so one environment variable switches providers
- Thin controllers, logic in services
- Cache + queues + graceful failure for production
Hiçbiri egzotik değil — zaten kullandığınız temiz Laravel mimarisi, yeni bir API türüne uygulandı. Gerçek sonuç: AI özellikleri eklemek için Python öğrenmeniz gerekmiyor. PHP becerileriniz doğrudan uygulanabilir.
Gelecek yazıda, Laravel ve Sunucu Tarafı Olayları (Server-Sent Events) kullanarak tarayıcıya akış yanıtları göndermeyi inceleyeceğim. Bunu kaçırmamak için takipte kalın.
Kaynak: Orijinal Makale


