🏗️ AI Modülü Mimarisi Analizi

📅 Tarih: 2025-11-23 03:15 | 🎯 Konu: Tenant-Aware Sistem Analizi | 👤 Talep: Multi-tenant mimarisi değerlendirmesi

📊 Yönetici Özeti

Sistem Durumu

AI modülü şu anda ÜÇ TEMEL PROBLEM ile karşı karşıyadır:

  1. Hardcoded Tenant Kontrolleri - Tenant 2/3 ID'leri 8 yerde kodda yazılı
  2. Çift Mimarı Patern - Tenant-specific ve global kurallar karışık
  3. Ölçeklenebilirlik Riski - 1000+ tenant desteklemesi imkansız

Mimarinin Güçlü Tarafları

  • Tenant-Aware Cache Service (TenantAwareCacheService)
  • Dinamik Tenant Service Yükleme (Factory Pattern)
  • Workflow Engine mimarisi (genişletilebilir)
  • Database Directive Sistemi (yapılandırılabilir)
  • Hybrid Search entegrasyonu

Acil Öncelikler

Yükseklik Problem Dosya Satırlar
KRİTİK Hardcoded [2, 3] kontrolleri AIResponseNode.php 85, 126
KRİTİK Tenant-specific kurallara hardcode AIResponseNode.php 125-170
YÜKSEK PublicAIController'da Tenant2 kontrolü PublicAIController.php ~55
YÜKSEK ShopSearchService'de hardcode ShopSearchService.php Çok

🔄 Mevcut Mimari Yapısı

1. Tenant-Aware Servisler (İyi)

Tenant2PromptService.php

1.132 satır İXTİF ÖZEL

Amaç: Tenant 2 (ixtif.com) ve Tenant 3 (ixtif.com.tr) için özel prompt kuralları

İçeriği:

  • Belirsiz istek kuralları (tonnaj, tip sorma)
  • Ürün gösterme kriterleri
  • Olumsuz kelime yasağı (maalesef, bulunmamaktadır, vb.)
  • İletişim bilgisi stratejisi
  • Fiyat ve stok politikası

Dosya Yolu:

/var/www/.../Modules/AI/app/Services/Tenant/Tenant2PromptService.php

Tenant2ProductSearchService.php

998 satır İXTİF ÖZEL

Amaç: Tenant 2 için kategori ve ürün arama logiki

İçeriği:

  • Forklift, transpalet, reach truck kategorileri (ID: 1-6)
  • Model numarası çıkarma (F4, EPL153, CPD15)
  • Fiyat-bazlı sorgular (en ucuz, en pahalı)
  • Yedek parça filtreleme
  • HybridSearch entegrasyonu

Dosya Yolu:

/var/www/.../Modules/AI/app/Services/Tenant/Tenant2ProductSearchService.php

2. Global (Tenant-Neutral) Yapılar

ProductSearchNode.php

Özellikleri:

  • Factory Pattern ile Tenant-specific servis yükleme
  • Dinamik sınıf adı: Tenant{$tenantId}ProductSearchService
  • Service yoksa fallback (MySQL, Meilisearch)
  • Generic keyword extraction (global tenants için)

Dosya Yolu:

Modules/AI/app/Services/Workflow/Nodes/ProductSearchNode.php

Kodu:

protected function getTenantSearchService(?int $tenantId) { $serviceClass = "\\Modules\\AI\\App\\Services\\Tenant\\Tenant{$tenantId}ProductSearchService"; if (class_exists($serviceClass)) { return app($serviceClass); } return null; }

3. Hardcoded Problemler (Kötü)

AIResponseNode.php - KRİTİK HARDCODE

PROBLEM Satır 85 ve 126'da hardcoded tenant kontrolü:

if (in_array($tenantId, [2, 3])) { // Tenant2PromptService yüklenmesi $tenant2Service = new \Modules\AI\App\Services\Tenant\Tenant2PromptService(); }

Neden Problem?

  • Yeni tenant eklenirken kod değiştirilmesi gerekir
  • Tenant 4, 5, 1001 için ayrı if bloğu yazılmalı
  • 1000+ tenant ile bu imkansız
  • Her güncellemede deployment riski

AIResponseNode.php - TENANT-SPECIFIC KURALLARA HARDCODE (Satır 125-170)

Sorun: İXTİF özel kurallar (olumsuz kelime yasağı, ürün gösterme, vb.) global AI dosyasında yazılı:

// 🏭 TENANT 2 (İXTİF) ÖZEL KURALLARI if (in_array($tenantId, [2, 3])) { $ixtifRules = <<<'IXTIF' ## İXTİF ÖZEL KURALLARI: - "Olumsuz kelimeler yasak!" - "Transpalet isteyince tonnaj sor" - ... IXTIF; $systemPrompt = $ixtifRules . "\n\n" . $systemPrompt; }

Neden Hata?

  • Bu kurallar Tenant2PromptService'de olmalı
  • Global Node'de tenant-specific kurallı ASLA bulunmamalı
  • Müzik (Tenant 1001) kurallarını eklerken başka developer bunu hatırlayacak mı?

PublicAIController.php - Tenant Kontrolü

if (in_array(tenant('id'), [2, 3])) { // Özel mantık }

Sorun: Controller'da tenant kontrolleri olmamalı

ShopSearchService.php - Çok Sayıda Hardcode

Dosyada 3 yerde in_array($tenantId, [2, 3]) kontrolü

OptimizedPromptService.php - Hardcoded Tenant Kontrolleri

2 yerde in_array($tenantId, [2, 3])

🔍 Detaylı Bulgular

Hardcoded Tenant ID'leri - Tam Sayı

Dosya Adı Satırlar Hardcode Tipi İşlev
AIResponseNode.php 85, 126 in_array([2,3]) Tenant2PromptService yükleme
AIResponseNode.php 127-169 İXTİF kuralları (hardcode) Olumsuz kelimeler, ürün gösterme
PublicAIController.php ~55 in_array([2,3]) Controller seviyesinde kontrol
ShopSearchService.php Çok in_array([2,3]) Search servisi tenantı kontrol
OptimizedPromptService.php 84, +1 in_array([2,3]) Optimized prompt oluşturma
Tenant2PromptService.php ~47 return in_array(tenant('id'), [2,3]) Tenant kontrolü içinde

Geçerli Tenant-Specific Servisler

Servis Adı Satır Sayısı Tenant(lar) Amaç
Tenant2PromptService 1.132 2, 3 Prompt kuralları (iXTİF)
Tenant2ProductSearchService 998 2, 3 Ürün arama (iXTİF)
Diğer tenantlar için: BAŞKA SERVİS YOK (fallback kullanılır)

Tenant Selection / Routing Mekanizması

Workflow Akışı: 1. AIResponseNode.execute() ├─ tenantId = tenant('id') ├─ IF tenantId IN [2,3] ← HARDCODED! │ └─ Tenant2PromptService::buildPrompt() çağır └─ Diğer kurallar 2. ProductSearchNode.execute() ├─ tenantId = context['tenant_id'] ├─ getTenantSearchService(tenantId) │ └─ Service varsa: Tenant{ID}ProductSearchService yükleme │ └─ Service yoksa: Fallback (MySQL/Meilisearch) └─ Sonuçları dön 3. New Tenant Eklendiğinde (Tenant 1001 - Müzik): ADIM 1: Tenant1001PromptService.php oluştur ADIM 2: Tenant1001ProductSearchService.php oluştur ADIM 3: AIResponseNode.php'de [2,3] → [2,3,1001] şeklinde güncelle ← BUG! ADIM 4: PublicAIController.php'de güncelle ADIM 5: ShopSearchService.php'de güncelle

Default Behavior (Service Yoksa)

❓ Tasarımsal Sorular

Soru 1: Tenant 2 & 3 Neden Aynı Servisi Kullanıyor?

Cevap: ixtif.com ve ixtif.com.tr aynı işletme olduğundan aynı ürün katalogu ve kuralları kullanıyor.

Sorun: İleride farklı kural isterse, Tenant2 → Tenant2 + Tenant3 şeklinde ayrılacak mı?

Soru 2: Neden Factory Pattern Devam Etmedi?

Durum:

  • ProductSearchNode: Factory kullanıyor (iyi!) ✓
  • AIResponseNode: Hardcoded if/in_array (kötü!) ✗

Sorun: Neden tutarsız?

Soru 3: Database Directives vs Hardcode Kurallar

Mekanizmalar:

  • Database: ai_tenant_directives tablosu (panel'den düzenlenebilir)
  • Hardcode: Tenant2PromptService.php (code yazılması gerekir)

Soru: Kurallar database'e taşınabilir mi?

Soru 4: Muzik (Tenant 1001) İçin Neyi Hazırlamak Gerekir?

Gerekli Dosyalar:

  1. Tenant1001PromptService.php (müzik kuralları)
  2. Tenant1001ProductSearchService.php (şarkı arama)
  3. AIResponseNode.php güncelleme (hardcode kontrol)
  4. PublicAIController.php güncelleme
  5. Diğer kontrollü dosyaları güncelleme

Durum: Konsistent bir pattern yok!

✅ Öneriler & Çözümler

Öncelik 1: Factory Pattern'i Tüm Servislere Yaygınlaştır (MUTLAKA)

Çözüm: TenantServiceFactory

Merkezi bir factory sınıfı oluştur:

// app/Services/AI/TenantServiceFactory.php class TenantServiceFactory { public static function getPromptService(?int $tenantId = null): TenantPromptServiceInterface { $tenantId = $tenantId ?? tenant('id'); $serviceClass = "\\App\\Services\\Tenant\\Prompt\\Tenant{$tenantId}PromptService"; if (class_exists($serviceClass)) { return app($serviceClass); } return app(DefaultPromptService::class); } public static function getSearchService(?int $tenantId = null) { $tenantId = $tenantId ?? tenant('id'); $serviceClass = "\\App\\Services\\Tenant\\Search\\Tenant{$tenantId}ProductSearchService"; if (class_exists($serviceClass)) { return app($serviceClass); } return app(DefaultProductSearchService::class); } }

Avantajları:

  • Merkezi kontrol noktası
  • Default service fallback
  • Tutarlı pattern
  • 1000+ tenant için ölçeklenebilir
  • Test yapılabilir

Öncelik 2: Interface Tanımla (Contract-Based Design)

Çözüm: Tenant Service Interfaces

// app/Services/Tenant/Contracts/TenantPromptServiceInterface.php interface TenantPromptServiceInterface { public function buildPrompt(): array; public function getTenantSpecificRules(): array; public function isApplicable(): bool; } // app/Services/Tenant/Contracts/TenantSearchServiceInterface.php interface TenantSearchServiceInterface { public function search(string $query, int $limit): array; public function detectCategory(string $message): ?int; }

Faydaları:

  • Standart contract sağlar
  • Her servis aynı metodları implementer
  • Type hinting mümkün
  • Mock'lama kolay

Öncelik 3: Hardcoded Tenant Kontrollerini Temizle

Dosya: AIResponseNode.php (Satır 85-170)

Eski Kod (Hardcoded):

$tenantId = tenant('id') ?? null; if (in_array($tenantId, [2, 3])) { try { $tenant2Service = new \Modules\AI\App\Services\Tenant\Tenant2PromptService(); $tenantPrompt = implode("\n", $tenant2Service->buildPrompt()); $systemPrompt = $tenantPrompt . "\n\n" . $systemPrompt; } catch (\Exception $e) { // ... } } // İXTİF ÖZEL KURALLARI (HARDCODED!) if (in_array($tenantId, [2, 3])) { $ixtifRules = <<<'IXTIF' ... IXTIF; $systemPrompt = $ixtifRules . "\n\n" . $systemPrompt; }

Yeni Kod (Factory):

// Factory'den service al (otomatik!) $promptService = TenantServiceFactory::getPromptService(); // Tenant-specific rules'u al $tenantRules = $promptService->getTenantSpecificRules(); $systemPrompt = implode("\n", $tenantRules) . "\n\n" . $systemPrompt;

Faydaları:

  • Tenant 1001 eklenirse kod değişmez!
  • Service yoksa DefaultPromptService kullanılır
  • Tenant-specific kurallar hep serviste kalır
  • AIResponseNode saf kalır (tenant-neutral)

Öncelik 4: Database Directives Yapısını Genişlet

Struktur: AITenantDirective'ler

Tenant-specific kurallar database'e taşın:

Directive Key Tipi Örnek Değer
negative_response_handling String "positive" / "fallback"
ask_on_ambiguous_request Boolean true / false
max_questions Integer 2, 3, unlimited
product_filter_rules JSON {"spare_parts": true, "categories": [1,2,3]}

Öncelik 5: Tenant 1001 (Müzik) İçin Yol Haritası

Yapılacaklar

  1. Tenant1001PromptService.php oluştur (müzik kuralları)
  2. Tenant1001SearchService.php oluştur (şarkı/albüm arama)
  3. Factory Pattern kullanarak dynamic load (hardcode YOK!)
  4. Test et: Tenant 2 ve 1001 aynı anda çalışır mı?

🛠️ Uygulama Planı (Adım Adım)

FAZA 1: Foundation (Factory & Interfaces)

  1. TenantServiceFactory.php oluştur
  2. TenantPromptServiceInterface tanımla
  3. TenantSearchServiceInterface tanımla
  4. DefaultPromptService (fallback)
  5. DefaultSearchService (fallback)

TAHMİN 3-4 saat

FAZA 2: Mevcut Kodları Refactor

  1. AIResponseNode.php güncelle (factory kullan)
  2. ProductSearchNode.php güncelle (interface uygun hale getir)
  3. PublicAIController.php temizle
  4. Tenant2 servisleri interface implement ettir

TAHMİN 4-5 saat

FAZA 3: Testing & Validation

  1. Tenant 2 (ixtif.com) test: Prompt kuralları çalışır mı?
  2. Tenant 2 (ixtif.com.tr) test: Aynı servisi kullanır mı?
  3. Tenant 1001 (muzibu) test: Default rules kullanır mı?
  4. Unit test yazma
  5. Production deployment

TAHMİN 2-3 saat

FAZA 4: Tenant 1001 (Müzik) Hazırlanması

  1. Tenant1001PromptService.php oluştur
  2. Tenant1001SearchService.php oluştur (müzik albüm/şarkı arama)
  3. Database directives (müzik kuralları)
  4. Test & Deploy

TAHMİN 8-10 saat

Toplam Zaman: ~17-22 saat
Benefit: 1000+ tenant için ölçeklenebilir mimarı

⚠️ Risk Analizi

Risk 1: Tenant 2/3 Kuralları Kırılabilir

Senaryо: Factory'e geçiş sırasında Tenant2PromptService'den kural kaçırılır.

Azaltma:

  • Kapsamlı unit test yazma
  • Staging ortamında test etme
  • Sadece Tenant 2 ile başlama

Risk 2: Service Yükleme Başarısızlığı

Senaryо: Tenant1001PromptService.php syntax hatası var, default service karamak isteyeceği sırada da hata.

Azaltma:

  • Try-catch kodu ekle
  • Log'u kontrol et
  • Default service robust olmalı

Risk 3: Performance

Senaryо: Her request'te factory new service oluşturur.

Azaltma:

  • Service container caching
  • TenantAwareCacheService kullan

📋 Kısa Özet - Hardcoded Tenant Kontrolleri

Dosya Problem Tipi Satırlar Çözüm
AIResponseNode.php Hardcoded [2,3] 85, 126 Factory pattern
AIResponseNode.php Tenant-specific kurallar 125-170 Service'e taşı
PublicAIController.php Hardcoded [2,3] ~55 Factory pattern
ShopSearchService.php Hardcoded [2,3] ×3 Çok Factory pattern
OptimizedPromptService.php Hardcoded [2,3] 84, +1 Factory pattern
Tenant2PromptService.php İç kontrol ~47 Zaten doğru