解鎖智能推薦:Laravel x FastAPI 微服務架構實戰指南
✨ 引言:當 PHP 遇上 AI:你的應用程式為何需要「智能推薦」?
在現今數據驅動的時代,個性化體驗已成為產品成功的關鍵。從電商的「你可能也喜歡」到新聞平台的「為你推薦」,背後都少不了推薦系統的身影。作為 PHP 工程師,我們深諳 Laravel 在 Web 開發領域的效率與優雅,但當面對機器學習模型推論這種高併發、計算密集型的挑戰時,單一語言框架往往力有未逮。
別擔心,這並不意味著你必須放棄 Laravel!本文將引導你進入一個真實的生產級推薦系統專案,它巧妙地結合了 Laravel 的業務處理能力與 FastAPI 的高性能 AI 推論。我們將透過 Docker 實現無縫整合,並建立一套全面的監控與 A/B 測試機制。這不僅能為你的 Laravel 應用注入智能,更能讓你一窺現代微服務架構在 AI 領域的應用精髓。
準備好了嗎?讓我們一同探索如何打造一個既強大又易於管理的智能推薦引擎!
🚀 專案概覽與核心亮點:一個完整且富有韌性的推薦架構
本專案提供了一個從用戶請求到模型訓練、結果監控的端到端解決方案。它不僅僅是個概念驗證,更是為實際生產環境而設計的 robust 系統。
系統核心亮點:
- 智能個性化推薦:基於真實用戶互動數據(點擊、曝光),提供精準的 Item-based 協同過濾推薦。
- 業務合規性優先:嚴格確保只推薦當前上架(Active)的商品,避免業務邏失。
- 內建 A/B 測試框架:採用 Thompson Sampling 動態分組策略,助你科學評估不同推薦演算法的真實效果。
- 全自動化模型更新:推薦模型定期自動訓練並熱載入,無需停機,確保推薦內容始終新鮮有效。
- 實時監控與智能告警:利用 Prometheus 和 Grafana 全面監控系統性能與推薦質量(如冷啟動比例、重複率、覆蓋率),實現主動式運維。
- 微服務解耦哲學:清晰劃分 Laravel(業務邏輯)與 FastAPI(AI 推論)職責,提升系統可擴展性與維護性。
- 容器化部署:所有服務透過 Docker Compose 一鍵啟動,大幅簡化環境配置與部署流程。
⚙️ 系統架構深度解析:Laravel 與 FastAPI 的協同共舞
本專案的核心是其精心設計的微服務架構,讓各組件各司其職,協同高效運作:
graph TD
A[用戶請求] -->|HTTP| B[Laravel App: API & Middleware]
B -->|A/B 測試分配| C[FastAPI: 推薦服務]
B -->|資料儲存| D[MySQL: 商品與用戶行為]
C -->|模型訓練/查詢| D
C -->|快取| E[Redis: 模型與行為快取]
F[Prometheus: 監控指標] -->|抓取 Metrics| C
F -->|資料來源| G[Grafana: 可視化儀表板]
B -->|健康檢查| F
- Laravel App (PHP):作為整個系統的前端網關與業務邏輯核心。它負責接收所有來自用戶的推薦請求,進行 A/B 測試分組,並發起對 FastAPI 推薦服務的調用。更重要的是,它會二次驗證推薦結果,確保僅返回上架商品。此外,所有用戶的關鍵行為(如曝光、點擊)也在此層被追蹤並異步記錄。
- FastAPI 推薦服務 (Python):這是推薦系統的**「智能大腦」**。它是一個高性能的獨立微服務,專門處理推薦算法的推論與模型訓練。選擇 Python 和 FastAPI 的原因顯而易見:Python 在機器學習領域擁有無與倫比的生態(Pandas, scikit-learn 等),而 FastAPI 則以其極致的性能和非同步特性,成為構建 AI 推論服務的理想選擇。
- MySQL 資料庫:系統的數據基石,負責持久化儲存用戶、商品(包含關鍵的
status
欄位)以及所有的用戶推薦互動事件。這些互動事件數據是 FastAPI 進行模型訓練的唯一真實數據來源。 - Redis 緩存與隊列:扮演著加速器和緩衝區的角色:
- 推薦模型緩存:FastAPI 訓練好的模型會被序列化後儲存於 Redis,實現快速載入與熱更新。
- Laravel 異步隊列:用戶行為事件被推送到 Redis 隊列,由 Laravel Worker 異步處理寫入數據庫,避免阻塞用戶請求,極大提升 API 響應速度。
- 最近推薦結果快取:用於計算推薦結果的重複率,提升用戶體驗。
- Prometheus & Grafana:這對黃金搭檔構成了系統的可觀測性中樞。Prometheus 負責從 FastAPI 服務周期性抓取各類性能與業務指標。Grafana 則將這些數據轉化為直觀的儀表板,並在關鍵指標異常時自動觸發告警,助你實時掌握系統健康狀況。
💻 Laravel 端實作解密:業務邏輯與 API 設計精粹
作為 PHP 開發者,讓我們聚焦 Laravel 如何優雅地肩負起業務層的重任。
1. API 接口與推薦請求流程
前端透過 Laravel 的 API 路由請求推薦列表。RecommendationController
會調用核心服務層 RecommendationService
:
// laravel-app/routes/api.php
use App\Http\Controllers\RecommendationController;
use App\Http\Middleware\AssignRecommendationGroup; // 引入 A/B 測試中間件
Route::middleware([AssignRecommendationGroup::class])->group(function () {
Route::get('/user/recommendations', [RecommendationController::class, 'getRecommendations']);
});
RecommendationService
是關鍵所在,它承擔了與 FastAPI 交互並確保數據一致性的職責:
// laravel-app/app/Services/RecommendationService.php
namespace App\Services;
use Illuminate\Support\Facades\Http;
use App\Models\Product;
use App\Models\User;
use App\Events\RecommendationInteraction;
use Illuminate\Support\Facades\Log; // 引入日誌
class RecommendationService
{
private string $fastApiUrl;
public function __construct()
{
$this->fastApiUrl = config('services.fastapi.url');
}
public function getRecommendations(User $user, string $experimentName, string $group): array
{
try {
$response = Http::timeout(3) // 為外部 API 請求設定合理超時
->get("{$this->fastApiUrl}/recommend/{$user->id}", [
'experiment_name' => $experimentName,
'group' => $group,
]);
if ($response->successful()) {
$recommendedProductIds = $response->json()['recommendations'] ?? [];
// **【核心】二道防線:再次過濾,確保僅返回上架商品**
$products = Product::whereIn('id', $recommendedProductIds)
->active() // 利用 Eloquent Local Scope 簡潔篩選
->get();
// 異步記錄曝光事件,不阻塞用戶請求
event(new RecommendationInteraction(
$user->id,
$experimentName,
$group,
'impression', // 動作類型
$products->pluck('id')->toArray(), // 實際曝光的商品 ID
['source' => 'fastapi']
));
return $products->toArray();
}
} catch (\Throwable $e) {
Log::error("FastAPI 推薦服務請求失敗: " . $e->getMessage(), ['user_id' => $user->id, 'trace' => $e->getTraceAsString()]);
}
// 【健壯性】備用策略:當 FastAPI 無法響應時,隨機推薦活躍商品
$fallbackProducts = Product::active()->inRandomOrder()->limit(10)->get();
event(new RecommendationInteraction(
$user->id,
$experimentName,
$group,
'impression',
$fallbackProducts->pluck('id')->toArray(),
['source' => 'fallback', 'error_msg' => $e->getMessage() ?? 'unknown']
));
return $fallbackProducts->toArray();
}
}
2. 嚴格的商品上下架狀態整合
確保推薦內容的業務正確性至關重要。我們在 products
表中引入了 status
欄位 (active
, inactive
, sold_out
)。
// laravel-app/database/migrations/xxxx_create_products_table.php (片段)
Schema::create('products', function (Blueprint $table) {
// ... 其他欄位
$table->enum('status', ['active', 'inactive', 'sold_out'])->default('active'); // **定義商品狀態**
$table->timestamps();
});
並為 Product
Model 定義一個 scopeActive()
查詢範圍,方便篩選活躍商品:
// laravel-app/app/Models/Product.php (片段)
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
use HasFactory;
protected $fillable = ['name', 'image_url', 'price', 'category_id', 'status'];
/**
* Scope a query to only include active products.
*/
public function scopeActive(Builder $query): Builder
{
return $query->where('status', 'active');
}
}
這不僅在 Laravel 端篩選,FastAPI 也會只讀取活躍商品進行模型訓練,形成雙重保障。
3. A/B 測試框架:Thompson Sampling 動態分組
為科學地評估不同推薦策略,我們在 Laravel 中間件 AssignRecommendationGroup
中實現了 A/B 測試的分組邏輯。
// laravel-app/app/Http/Middleware/AssignRecommendationGroup.php (精簡核心邏輯)
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Config;
class AssignRecommendationGroup
{
public function handle(Request $request, Closure $next)
{
$experimentName = Config::get('ab_test.recommendation_experiment.name', 'default_recommendation');
$isEnabled = Config::get('ab_test.recommendation_experiment.enabled', false);
if (!$isEnabled) { /* 如果 A/B 測試未啟用,預設到控制組 */ }
$user = Auth::user();
$group = null;
if ($user) {
// 已登入用戶:優先使用 DB 中已分組結果,否則進行 Thompson Sampling 分配並持久化
if (empty($user->recommendation_group)) {
$group = $this->assignGroupUsingThompsonSampling($experimentName);
$user->recommendation_group = $group;
$user->save();
} else {
$group = $user->recommendation_group;
}
} else {
// 未登入用戶(訪客):基於 Session ID 和哈希算法分組
$sessionId = session()->getId();
$group = $this->assignGroupBasedOnSession($sessionId, $experimentName);
}
$request->attributes->set('recommendation_experiment_name', $experimentName);
$request->attributes->set('recommendation_group', $group);
return $next($request);
}
/**
* 基於 Thompson Sampling 分配組別。
* Thompson Sampling 會根據各組的歷史表現(如點擊率),動態調整分配權重,
* 讓表現更好的組別獲得更多流量,更快地收斂到最優解。
*/
private function assignGroupUsingThompsonSampling(string $experimentName): string
{
$groupsConfig = Config::get('ab_test.recommendation_experiment.groups', []);
$scores = []; // 實際應用中會從數據庫獲取各組的 alpha/beta 參數
// 這裡會實作 Thompson Sampling 的核心邏輯:
// 1. 從各組的 Beta 分佈中抽取一個隨機數。
// 2. 選擇隨機數最大的組別。
// 簡化為:若無實際 Thompson Sampling 數據,則按預設權重分配。
return $this->assignGroupBasedOnWeight($groupsConfig);
}
/**
* 基於 Session ID 進行哈希分組,確保訪客在同一 Session 保持組別一致性。
*/
private function assignGroupBasedOnSession(string $sessionId, string $experimentName): string
{
$hash = crc32($sessionId . $experimentName) % 100; // 確保結果在 0-99
$groupsConfig = Config::get('ab_test.recommendation_experiment.groups', []);
return $this->assignGroupBasedOnWeight($groupsConfig, $hash);
}
private function assignGroupBasedOnWeight(array $groupsConfig, int $hash = null): string
{
$totalWeight = array_sum(array_column($groupsConfig, 'weight'));
$targetValue = ($hash !== null) ? $hash : mt_rand(0, $totalWeight * 100) / 100; // 如果是哈希分組,直接用哈希值,否則隨機
$currentThreshold = 0;
foreach ($groupsConfig as $groupName => $config) {
$currentThreshold += ($config['weight'] ?? 0);
if ($targetValue < $currentThreshold) {
return $groupName;
}
}
return 'control'; // 安全回退
}
}
這個中間件會確保每個用戶都被分配到一個特定的實驗組,並將分組信息附加到請求中,供後續服務使用。
4. 用戶行為事件追蹤:異步處理的效能優化
用戶的每一次推薦曝光和點擊都是訓練模型不可或缺的「燃料」。為了不影響 API 響應速度,我們採用 Laravel 的 Event & Listener + Queue 機制進行異步追蹤。
// laravel-app/app/Events/RecommendationInteraction.php
namespace App\Events;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class RecommendationInteraction
{
use Dispatchable, SerializesModels;
public int $userId;
public string $experimentName;
public string $group;
public string $action; // 'impression' (曝光) or 'click' (點擊)
public array $productIds; // 相關商品 ID 列表
public array $metadata; // 額外元數據
public function __construct(int $userId, string $experimentName, string $group, string $action, array $productIds, array $metadata = [])
{
$this->userId = $userId;
$this->experimentName = $experimentName;
$this->group = $group;
$this->action = $action;
$this->productIds = $productIds;
$this->metadata = $metadata;
}
}
// laravel-app/app/Listeners/LogRecommendationInteraction.php
namespace App\Listeners;
use App\Events\RecommendationInteraction;
use App\Models\RecommendationEvent; // 用於儲存事件的 Eloquent Model
use Illuminate\Contracts\Queue\ShouldQueue; // **核心:標記此監聽器為異步處理**
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Log;
class LogRecommendationInteraction implements ShouldQueue
{
use InteractsWithQueue;
public function handle(RecommendationInteraction $event)
{
try {
RecommendationEvent::create([
'user_id' => $event->userId,
'experiment_name' => $event->experimentName,
'group' => $event->group,
'action' => $event->action,
'product_ids' => json_encode($event->productIds), // 將數組存儲為 JSON 字符串
'metadata' => json_encode($event->metadata),
]);
} catch (\Exception $e) {
Log::error("無法記錄推薦互動事件: " . $e->getMessage(), ['event_data' => $event]);
}
}
}
【重要】 別忘了在 laravel-app/app/Providers/EventServiceProvider.php
中註冊這些事件與監聽器,並確保 Laravel 使用 Redis 作為隊列驅動。此外,我們的 create_project.sh
腳本還會引導你為 User
模型添加一個 Observer
,確保用戶資料被創建或更新時,A/B 測試組能正確被分配和記錄。
🐍 FastAPI 端實作解密:推薦核心與智能引擎
FastAPI 服務是整個推薦系統的智能中樞,它負責處理所有與推薦算法相關的邏輯。
1. 推薦算法核心:Item-based 協同過濾 (CF)
我們採用了經典且高效的 Item-based Collaborative Filtering。其核心原理是「喜歡 A 商品的用戶也喜歡 B 商品,則 A 與 B 相似」,從而推薦與用戶歷史行為中商品相似的新商品。
# ai-recommender-service/recommender.py (簡化核心邏輯)
import pandas as pd
from sklearn.metrics.pairwise import cosine_similarity
from typing import List, Dict, Any
import logging # 引入日誌
logger = logging.getLogger(__name__)
class Recommender:
def __init__(self):
self.item_similarity_df = pd.DataFrame() # 儲存商品相似度矩陣
self.products = {} # 儲存活躍商品 {id: product_data}
self.redis_client = None # Redis 連接實例
# ... 其他初始化
def _load_user_interactions_from_mysql(self) -> pd.DataFrame:
"""從 MySQL 加載用戶互動數據,轉換為 Pandas DataFrame。"""
# 實際實作會透過數據庫連接池從 recommendation_events 表查詢數據
# 並進行適當的數據清洗和格式轉換
logger.info("正在從 MySQL 加載用戶互動數據...")
# 假設返回 DataFrame 包含 'user_id', 'product_id', 'action'
# 轉換成 user-item 矩陣
# ...
pass
def _load_products_from_mysql(self) -> Dict[int, Any]:
"""從 MySQL 加載所有狀態為 'active' 的商品數據。"""
# 實際實作會透過數據庫連接池從 products 表查詢數據
# **關鍵:WHERE status = 'active'**
logger.info("正在從 MySQL 加載活躍商品數據...")
# 假設返回 {product_id: product_info}
pass
def train_and_save_model(self):
"""定時觸發:從數據庫加載數據、訓練模型並持久化。"""
logger.info("開始訓練推薦模型...")
try:
# 1. 更新活躍商品數據
self.products = self._load_products_from_mysql()
active_product_ids = list(self.products.keys())
if not active_product_ids:
logger.warning("無活躍商品數據,模型訓練中止。")
return
# 2. 加載用戶互動數據
user_interactions = self._load_user_interactions_from_mysql()
if user_interactions.empty:
logger.warning("無用戶互動數據,模型訓練中止。")
return
# 3. 構建用戶-商品互動矩陣
# 確保矩陣只包含活躍商品
user_item_matrix = user_interactions.pivot_table(index='user_id', columns='product_id', values='action').fillna(0)
user_item_matrix = user_item_matrix[user_item_matrix.columns.intersection(active_product_ids)]
if user_item_matrix.empty or user_item_matrix.shape[1] < 2:
logger.warning("用戶-商品矩陣不足以訓練模型,可能活躍商品少於2個或無有效互動。")
return
# 4. 計算商品相似度:核心算法
self.item_similarity_df = pd.DataFrame(cosine_similarity(user_item_matrix.T),
index=user_item_matrix.columns,
columns=user_item_matrix.columns)
# 5. 模型持久化:保存到本地文件和 Redis
# ... 序列化 self.item_similarity_df 到 .pkl 文件
# ... 將 .pkl 內容存儲到 Redis
logger.info("模型訓練並保存成功!")
except Exception as e:
logger.exception(f"模型訓練失敗: {e}")
def get_recommendations(self, user_id: int, product_count: int = 10) -> List[int]:
"""獲取指定用戶的推薦商品 ID 列表。"""
# 1. 獲取用戶最近瀏覽或點擊的商品 (從數據庫或 Redis 緩存)
# 2. 根據這些商品和 item_similarity_df 計算相似商品
# 3. **【核心】確保只從 `self.products` (活躍商品字典) 中選擇推薦結果**
# 4. 冷啟動處理:若推薦數量不足,從活躍商品中隨機補充
# 5. 確保推薦結果不重複且符合數量要求
logger.info(f"為用戶 {user_id} 生成推薦結果...")
# ...
pass
2. 模型自動訓練與熱載入:確保推薦時效性
FastAPI 服務啟動時會透過 APScheduler
定義定時任務,實現模型的自動化訓練與數據同步:
# ai-recommender-service/main.py (啟動 APScheduler)
from fastapi import FastAPI # ...
from apscheduler.schedulers.background import BackgroundScheduler
from recommender import Recommender # 引入 Recommender 類
import logging # 引入日誌
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI()
recommender_instance = Recommender()
scheduler = BackgroundScheduler()
@app.on_event("startup")
async def startup_event():
"""應用啟動時的初始化任務。"""
logger.info("FastAPI 推薦服務啟動中...")
recommender_instance.connect_to_redis() # 連接 Redis
recommender_instance.load_model() # 啟動時加載最新模型 (從 Redis 或文件)
recommender_instance.update_product_data() # 立即同步活躍商品數據
# 定義定時任務
scheduler.add_job(recommender_instance.train_and_save_model, 'interval', hours=6, id='model_retrain', replace_existing=True)
scheduler.add_job(recommender_instance.update_product_data, 'interval', hours=1, id='product_data_sync', replace_existing=True)
scheduler.start()
logger.info("APScheduler 定時任務已啟動。")
@app.on_event("shutdown")
def shutdown_event():
"""應用關閉時的清理任務。"""
scheduler.shutdown()
recommender_instance.disconnect_from_redis()
logger.info("FastAPI 推薦服務已關閉。")
# ... FastAPI 路由定義,例如 /recommend/{user_id}
這種設計確保了推薦模型能夠定期反映最新的用戶行為和商品狀態,且在模型更新時,無需中斷服務,實現無縫熱載入。
3. 冷啟動與推薦多樣性
冷啟動處理:對於沒有足夠歷史互動數據的用戶(新用戶),協同過濾模型無法有效推薦。FastAPI 服務會智能判斷,若協同過濾無法產生足夠的推薦數量,則會從當前所有活躍商品中隨機補充,確保用戶總能看到推薦結果。
推薦多樣性:目前主要透過冷啟動時的隨機補充來增加多樣性。未來可以進一步引入更複雜的多樣性算法,例如最大化邊際相關性 (MMR) 等,以平衡推薦的相關性與新穎性。
4. Prometheus 指標暴露:讓推薦服務數據化
FastAPI 服務透過 prometheus_client
庫,將內部的運行狀態和業務指標標準化暴露在 /metrics
HTTP 端點,供 Prometheus 抓取。
# ai-recommender-service/main.py (部分指標定義與使用)
from prometheus_client import generate_latest, Counter, Histogram, Gauge
# HTTP 請求指標
REQUEST_COUNT = Counter('http_requests_total', 'Total HTTP Requests', ['method', 'endpoint'])
REQUEST_LATENCY = Histogram('http_request_duration_seconds', 'HTTP Request Latency', ['method', 'endpoint'])
# 推薦業務指標 (核心!)
RECOMMENDATION_SUCCESS_TOTAL = Counter('recommendation_success_total', 'Total successful recommendations served')
RECOMMENDATION_COLD_START_TOTAL = Counter('recommendation_cold_start_total', 'Total cold start recommendations served')
RECOMMENDATION_REPETITION_RATIO = Gauge('recommendation_repetition_ratio', 'Ratio of repeated recommendations', ['user_id', 'experiment_name', 'group'])
RECOMMENDATION_CATALOG_COVERAGE_RATIO = Gauge('recommendation_catalog_coverage_ratio', 'Ratio of unique recommended products to total active products')
這些指標是後續監控和告警的基礎,也是評估推薦系統效果的量化依據。
📚 數據庫與緩存策略:速度與可靠性的黃金平衡
- MySQL (持久化):作為所有核心數據(用戶、商品、推薦事件)的最終一致性來源。它的穩定性和 ACID 特性確保了數據的完整性與可靠性。
- Redis (高性能緩存與隊列):
- 極速模型載入:序列化後的推薦模型儲存於 Redis,FastAPI 啟動時能瞬間加載,無需等待文件讀取。
- 非同步事件處理:Laravel 將用戶行為事件推送到 Redis 隊列,由後台 Worker 非同步處理,將數據庫寫入的延遲從用戶請求路徑中剝離。
- 實時重複率計算:快速讀寫特性使 Redis 成為記錄和對比用戶最近推薦結果的理想選擇。
📈 可觀測性:Prometheus & Grafana,讓你的系統「會說話」
一個好的系統不僅要能工作,更要「會說話」。我們透過 Prometheus 和 Grafana,賦予推薦系統強大的可觀測性。
- Prometheus:它會定期從 FastAPI 服務的
/metrics
端點「抓取」(scrape) 各類指標數據。- 系統層指標:HTTP 請求總數、請求延遲、服務錯誤率等,反映服務運行健康。
- 業務層指標:
recommendation_success_total
:成功響應的推薦請求總數。recommendation_cold_start_total
:冷啟動用戶的推薦請求次數。recommendation_repetition_ratio
:推薦結果重複的比例,衡量內容新鮮度。recommendation_catalog_coverage_ratio
:推薦出的商品種類覆蓋整個商品庫的比例,衡量推薦廣度。
- Grafana:作為 Prometheus 的強大可視化介面。我們可以在 Grafana 中建立客製化的儀表板 (Dashboard),將這些複雜的指標以直觀的圖表呈現。
最關鍵的是,Grafana 配置了告警規則。例如,當:
- 冷啟動比例 (
rate(recommendation_cold_start_total[5m]) / rate(http_requests_total[5m])
) 在 5 分鐘內持續超過 30% 時,觸發「WARN」警告。 - 推薦重複率 (
recommendation_repetition_ratio
) 在 10 分鐘內持續高於 80% 時,觸發「CRITICAL」告警。
一旦觸發,Grafana 會立即透過郵件、Slack 或其他通知渠道發送告警,實現真正的「主動式運維」。這意味著我們能夠在用戶察覺問題之前,就及時發現、定位並解決問題,極大降低了平均恢復時間 (MTTR),保障了用戶體驗和業務連續性。
🚀 快速啟動與親身體驗:運行你的專案!
親手運行這個專案,是你理解其精髓的最佳方式。所有服務都已容器化,啟動流程極為簡便:
- 環境準備:確保您的系統已安裝
Docker
和Docker Compose
。 - 克隆專案:
Bash
git clone https://github.com/BpsEason/recommendation-system.git cd recommendation-system
- 啟動 Docker 服務:這會啟動所有 Laravel、FastAPI、MySQL、Redis、Prometheus、Grafana 容器。
Bash
docker compose up --build -d
- 初始化 Laravel 應用 (重要步驟!)
Bash
docker compose exec laravel-app php artisan key:generate --show # 複製輸出,貼到 laravel-app/.env 的 APP_KEY= docker compose exec laravel-app php artisan migrate docker compose exec laravel-app php artisan db:seed --class=ProductSeeder docker compose exec laravel-app php artisan db:seed --class=UserSeeder
- 【關鍵手動配置】綁定 Laravel 事件監聽器
編輯
laravel-app/app/Providers/EventServiceProvider.php
,在$listen
陣列中添加以下內容: 並在PHP'App\\Events\\RecommendationInteraction' => [ 'App\\Listeners\\LogRecommendationInteraction', ],
boot()
方法中添加UserObserver
的註冊: 完成後,請儲存檔案。PHP\App\Models\User::observe(\App\Observers\UserObserver::class);
- 啟動 Laravel 隊列 Worker:確保異步事件處理正常運行。
Bash
docker compose exec laravel-app php artisan queue:work --queue=default --tries=3
現在,你的推薦系統已準備就緒!你可以訪問:
- Laravel App (主要業務 API):
http://localhost:8000
- FastAPI Recommender (推薦服務接口):
http://localhost:8001
- Prometheus (監控數據收集):
http://localhost:9090
- Grafana (可視化儀表板與告警):
http://localhost:3000
(默認用戶/密碼:admin
/admin
)
展望未來:推薦系統的無限進化之路
雖然這個專案已具備生產級應用的基礎,但推薦系統的進化永無止境。對於有志於深度探索的你,以下是一些值得思考和實踐的未來方向:
- 更前沿的推薦算法:
- 探索基於深度學習的模型(如 DSSM, DIN, YoutubeDNN, Transformer)來處理更複雜的用戶行為和物品特徵。
- 引入圖神經網絡(GNN)處理用戶-物品交互圖,挖掘更深層次的關係。
- 實時推薦:透過整合消息隊列(如 Kafka)和流處理框架(如 Flink/Spark Streaming),實現用戶行為的實時捕獲、特徵提取和模型更新,提供秒級響應的推薦。
- 多樣性與可解釋性優化:在追求推薦精準度之外,加入更多樣性指標,並探索如何讓推薦結果更具「可解釋性」(Explainable AI),增強用戶信任感。
- 完善 MLOps 流程:將模型的數據收集、訓練、評估、部署、監控整個生命週期全面自動化,實現持續集成/持續部署 (CI/CD) for ML。
- 特徵平台 (Feature Store):為了解耦特徵計算與模型訓練,建立統一的特徵儲存和服務層,加速新模型的實驗和迭代。
結語:跨越 PHP 與 AI 的鴻溝
本專案提供了一個清晰的藍圖,展示了 PHP 工程師如何利用其在 Laravel 上的深厚功底,與 Python 的 AI 生態無縫協作,共同打造強大而智能的應用程式。這不僅是技術棧的整合,更是微服務架構思維、跨語言協作、以及數據驅動決策的實際應用。
希望這篇文章能激發你的靈感,鼓勵你跳出舒適區,探索更廣闊的技術領域。智能推薦的未來充滿可能性,期待你能在這條路上創造更多精彩!
歡迎訪問我的 GitHub 倉庫:
沒有留言:
張貼留言