# AI驅動產品後端技術架構指南 ## 📋 **文件資訊** - **文件名稱**: AI驅動產品後端技術架構指南 - **版本**: v1.0 - **建立日期**: 2025-01-25 - **最後更新**: 2025-01-25 - **負責團隊**: DramaLing技術架構團隊 - **適用產品**: AI驅動的語言學習、內容分析、智能推薦等產品 --- ## 🎯 **架構設計原則** ### **核心設計理念** #### **高效率 (High Performance)** ```yaml 響應速度: - API響應時間 < 500ms (不含AI處理) - AI處理時間 < 5秒 - 資料庫查詢 < 100ms - 快取命中率 > 80% 並發處理: - 支援1000+併發請求 - 優雅降級機制 - 資源池化管理 - 非同步處理優先 ``` #### **好維護 (Maintainability)** ```yaml 程式碼品質: - 單一職責原則 (SRP) - 依賴注入 (DI) - 介面隔離 (ISP) - 開放封閉原則 (OCP) 文檔完整性: - API文檔自動生成 - 程式碼註釋覆蓋率 > 60% - 架構決策記錄 (ADR) - 部署流程文檔化 ``` #### **穩定性 (Stability)** ```yaml 錯誤處理: - 全局異常處理 - 優雅降級策略 - 重試機制設計 - 斷路器模式 監控告警: - 健康檢查端點 - 關鍵指標監控 - 自動告警機制 - 日誌追蹤完整 ``` --- ## 🏗️ **分層架構設計** ### **整體架構圖** ``` ┌─────────────────────────────────────────────────────────────┐ │ 🌐 API Gateway │ │ (Authentication, Rate Limiting) │ └─────────────────────┬───────────────────────────────────────┘ │ ┌─────────────────────▼───────────────────────────────────────┐ │ 📡 Controllers Layer │ │ (Request Handling, Validation) │ └─────────────────────┬───────────────────────────────────────┘ │ ┌─────────────────────▼───────────────────────────────────────┐ │ 🔧 Services Layer │ │ (Business Logic, AI Integration) │ └─────────────────────┬───────────────────────────────────────┘ │ ┌─────────────────────▼───────────────────────────────────────┐ │ 💾 Data Access Layer │ │ (Repository Pattern, EF Core) │ └─────────────────────┬───────────────────────────────────────┘ │ ┌─────────────────────▼───────────────────────────────────────┐ │ 🗄️ Data Storage Layer │ │ (Database, Cache, External APIs) │ └─────────────────────────────────────────────────────────────┘ ``` ### **分層職責定義** #### **1. Controllers Layer (控制器層)** **職責**: 處理HTTP請求、參數驗證、回應格式化 ```csharp [ApiController] [Route("api/[controller]")] public class AIController : ControllerBase { private readonly IAIAnalysisService _aiService; private readonly ILogger _logger; // ✅ 好的實踐:單一職責,只處理HTTP層邏輯 [HttpPost("analyze")] public async Task> Analyze( [FromBody] AnalysisRequest request) { // 1. 輸入驗證 if (!ModelState.IsValid) return BadRequest(ModelState); // 2. 委派給服務層 var result = await _aiService.AnalyzeAsync(request); // 3. 格式化回應 return Ok(result); } } ``` #### **2. Services Layer (服務層)** **職責**: 業務邏輯實現、AI服務整合、快取策略 ```csharp public interface IAIAnalysisService { Task AnalyzeAsync(AnalysisRequest request); } public class AIAnalysisService : IAIAnalysisService { private readonly IGeminiService _geminiService; private readonly ICacheService _cacheService; private readonly IAnalysisRepository _repository; // ✅ 好的實踐:依賴注入,介面隔離 public async Task AnalyzeAsync(AnalysisRequest request) { // 1. 快取檢查 var cached = await _cacheService.GetAsync(request.InputText); if (cached != null) return cached; // 2. AI服務調用 var aiResult = await _geminiService.AnalyzeAsync(request.InputText); // 3. 業務邏輯處理 var processedResult = ProcessAIResult(aiResult, request); // 4. 快取存儲 await _cacheService.SetAsync(request.InputText, processedResult); // 5. 持久化 await _repository.SaveAnalysisAsync(processedResult); return processedResult; } } ``` #### **3. Data Access Layer (資料存取層)** **職責**: 資料庫操作、查詢優化、事務管理 ```csharp public interface IAnalysisRepository { Task GetCachedAnalysisAsync(string inputHash); Task SaveAnalysisAsync(AnalysisResult result); Task> GetUsageStatsAsync(DateTime from, DateTime to); } public class AnalysisRepository : IAnalysisRepository { private readonly DramaLingDbContext _context; // ✅ 好的實踐:Repository模式,查詢優化 public async Task GetCachedAnalysisAsync(string inputHash) { return await _context.AnalysisCache .AsNoTracking() // 性能優化:只讀查詢 .Where(a => a.InputHash == inputHash && a.ExpiresAt > DateTime.UtcNow) .Select(a => new AnalysisResult // 投影查詢:只選需要的欄位 { Data = a.CachedData, CreatedAt = a.CreatedAt }) .FirstOrDefaultAsync(); } } ``` --- ## 🤖 **AI整合架構模式** ### **AI服務抽象層設計** #### **多AI提供商支援架構** ```csharp // ✅ 策略模式:支援多個AI提供商 public interface IAIProvider { string ProviderName { get; } Task AnalyzeAsync(AIRequest request); bool IsAvailable { get; } decimal CostPerRequest { get; } } public class GeminiProvider : IAIProvider { public string ProviderName => "Google Gemini"; public async Task AnalyzeAsync(AIRequest request) { // Gemini特定實現 } } public class OpenAIProvider : IAIProvider { public string ProviderName => "OpenAI GPT"; public async Task AnalyzeAsync(AIRequest request) { // OpenAI特定實現 } } // AI提供商選擇器 public class AIProviderSelector { private readonly IEnumerable _providers; public IAIProvider SelectBestProvider(AIRequest request) { // 基於成本、可用性、性能選擇最佳提供商 return _providers .Where(p => p.IsAvailable) .OrderBy(p => p.CostPerRequest) .First(); } } ``` ### **AI請求優化模式** #### **智能批次處理** ```csharp public class BatchAIProcessor { private readonly Queue _requestQueue = new(); private readonly Timer _batchTimer; public async Task ProcessAsync(AIRequest request) { // ✅ 批次處理:提高AI API效率 _requestQueue.Enqueue(request); if (_requestQueue.Count >= BatchSize || IsTimeoutReached()) { await ProcessBatchAsync(); } return await GetResultAsync(request.Id); } private async Task ProcessBatchAsync() { var batch = DrainQueue(); var batchPrompt = CombineRequests(batch); var response = await _aiProvider.AnalyzeAsync(batchPrompt); var results = SplitResponse(response, batch); // 分發結果給等待的請求 foreach (var result in results) { _resultStore.SetResult(result.RequestId, result); } } } ``` #### **智能快取策略** ```csharp public class IntelligentCacheService { private readonly IMemoryCache _memoryCache; private readonly IDistributedCache _distributedCache; private readonly IDatabase _database; // ✅ 多層快取:記憶體 → Redis → 資料庫 public async Task GetAsync(string key) where T : class { // L1: 記憶體快取 (最快) if (_memoryCache.TryGetValue(key, out T? memoryResult)) return memoryResult; // L2: 分散式快取 (Redis) var distributedResult = await _distributedCache.GetStringAsync(key); if (distributedResult != null) { var result = JsonSerializer.Deserialize(distributedResult); _memoryCache.Set(key, result, TimeSpan.FromMinutes(5)); return result; } // L3: 資料庫快取 (持久化) var dbResult = await GetFromDatabaseAsync(key); if (dbResult != null) { await SetMultiLevelCacheAsync(key, dbResult); return dbResult; } return null; } // 智能過期策略 public async Task SetAsync(string key, T value, TimeSpan? expiry = null) { var smartExpiry = CalculateSmartExpiry(key, value); // 同時更新多層快取 _memoryCache.Set(key, value, smartExpiry); await _distributedCache.SetStringAsync(key, JsonSerializer.Serialize(value), new DistributedCacheEntryOptions { SlidingExpiration = smartExpiry }); await SaveToDatabaseAsync(key, value, smartExpiry); } } ``` --- ## 🔧 **程式碼組織結構** ### **專案結構範本** ``` DramaLing.Api/ ├── 📁 Controllers/ # API控制器 │ ├── AIController.cs # AI分析相關端點 │ ├── AuthController.cs # 認證相關端點 │ └── BaseController.cs # 共用控制器基類 ├── 📁 Services/ # 業務服務層 │ ├── AI/ # AI相關服務 │ │ ├── IGeminiService.cs │ │ ├── GeminiService.cs │ │ ├── IPromptBuilder.cs │ │ └── PromptBuilder.cs │ ├── Cache/ # 快取服務 │ │ ├── ICacheService.cs │ │ ├── MemoryCacheService.cs │ │ └── RedisCacheService.cs │ └── Core/ # 核心業務服務 │ ├── IAnalysisService.cs │ └── AnalysisService.cs ├── 📁 Models/ # 資料模型 │ ├── Entities/ # 資料庫實體 │ ├── DTOs/ # 資料傳輸對象 │ └── Requests/ # API請求模型 ├── 📁 Data/ # 資料存取層 │ ├── DbContext.cs # EF Core上下文 │ ├── Repositories/ # Repository實現 │ └── Migrations/ # 資料庫遷移 ├── 📁 Infrastructure/ # 基礎設施 │ ├── Middleware/ # 中介軟體 │ ├── Filters/ # 過濾器 │ ├── Extensions/ # 擴展方法 │ └── Configuration/ # 配置管理 ├── 📁 Utils/ # 工具類 │ ├── Helpers/ # 輔助函數 │ ├── Constants/ # 常數定義 │ └── Validators/ # 驗證器 └── Program.cs # 應用程式入口 ``` ### **依賴注入最佳實踐** #### **服務註冊配置** ```csharp // Program.cs public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); // ✅ 分層註冊:按類型組織 RegisterControllers(builder); RegisterBusinessServices(builder); RegisterDataServices(builder); RegisterInfrastructureServices(builder); RegisterAIServices(builder); var app = builder.Build(); ConfigureMiddleware(app); app.Run(); } // 業務服務註冊 private static void RegisterBusinessServices(WebApplicationBuilder builder) { builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); } // AI服務註冊 private static void RegisterAIServices(WebApplicationBuilder builder) { // ✅ 工廠模式:支援多AI提供商 builder.Services.AddSingleton(); builder.Services.AddScoped(); builder.Services.AddScoped(); // ✅ 配置模式:強型別配置 builder.Services.Configure( builder.Configuration.GetSection("Gemini")); // ✅ HttpClient工廠:連接池管理 builder.Services.AddHttpClient(client => { client.Timeout = TimeSpan.FromSeconds(30); client.DefaultRequestHeaders.Add("User-Agent", "DramaLing/1.0"); }); } ``` --- ## 🚀 **性能優化策略** ### **資料庫性能優化** #### **Entity Framework最佳實踐** ```csharp public class OptimizedAnalysisRepository : IAnalysisRepository { private readonly DramaLingDbContext _context; // ✅ 查詢優化:AsNoTracking + 投影 public async Task> GetRecentAnalysesAsync(int userId, int count) { return await _context.Analyses .AsNoTracking() // 關閉變更追蹤:提升查詢性能 .Where(a => a.UserId == userId) .OrderByDescending(a => a.CreatedAt) .Take(count) .Select(a => new AnalysisDto // 投影查詢:只查詢需要的欄位 { Id = a.Id, InputText = a.InputText, CreatedAt = a.CreatedAt, Summary = a.Summary }) .ToListAsync(); } // ✅ 批次操作:減少資料庫往返 public async Task SaveMultipleAnalysesAsync(IEnumerable analyses) { _context.Analyses.AddRange(analyses); // 批次新增 await _context.SaveChangesAsync(); // 單次提交 } // ✅ 連接分離:讀寫分離 public async Task GetAnalysisStatsAsync(int userId) { using var readOnlyContext = CreateReadOnlyContext(); return await readOnlyContext.Analyses .Where(a => a.UserId == userId) .GroupBy(a => a.UserId) .Select(g => new AnalysisStats { TotalCount = g.Count(), LastAnalysisDate = g.Max(a => a.CreatedAt) }) .FirstOrDefaultAsync(); } } ``` ### **AI服務性能優化** #### **請求合併與批次處理** ```csharp public class OptimizedAIService : IAIAnalysisService { private readonly IBatchProcessor _batchProcessor; private readonly ICircuitBreaker _circuitBreaker; // ✅ 請求合併:減少AI API調用次數 public async Task AnalyzeAsync(AnalysisRequest request) { var aiRequest = new AIRequest { Id = Guid.NewGuid(), InputText = request.InputText, Timestamp = DateTime.UtcNow }; // 批次處理:自動合併相近時間的請求 var aiResponse = await _batchProcessor.ProcessAsync(aiRequest); return TransformToAnalysisResult(aiResponse); } // ✅ 斷路器模式:防止AI服務故障影響整體系統 public async Task CallAIWithCircuitBreakerAsync(AIRequest request) { return await _circuitBreaker.ExecuteAsync(async () => { var response = await _aiProvider.CallAsync(request); if (!response.IsSuccessful) throw new AIServiceException(response.ErrorMessage); return response; }); } } ``` ### **快取優化策略** #### **多層快取架構** ```csharp public class HybridCacheService : ICacheService { private readonly IMemoryCache _l1Cache; // L1: 程序內快取 private readonly IDistributedCache _l2Cache; // L2: Redis分散式快取 private readonly ICacheRepository _l3Cache; // L3: 資料庫快取 // ✅ 智能快取:根據數據特性選擇策略 public async Task GetAsync(string key) where T : class { var cacheStrategy = DetermineCacheStrategy(); return cacheStrategy switch { CacheStrategy.Fast => await GetFromL1Async(key), CacheStrategy.Distributed => await GetFromL2Async(key), CacheStrategy.Persistent => await GetFromL3Async(key), _ => await GetFromMultiLevelAsync(key) }; } // 智能過期策略 private TimeSpan CalculateExpiry(string key, object value) { return key switch { var k when k.StartsWith("analysis:") => TimeSpan.FromHours(24), var k when k.StartsWith("user:") => TimeSpan.FromMinutes(30), var k when k.StartsWith("stats:") => TimeSpan.FromMinutes(5), _ => TimeSpan.FromMinutes(15) }; } // ✅ 快取預熱:提前載入熱點資料 public async Task WarmupCacheAsync() { var hotData = await _repository.GetHotDataAsync(); var tasks = hotData.Select(data => SetAsync($"warmup:{data.Id}", data, TimeSpan.FromHours(1))); await Task.WhenAll(tasks); } } ``` --- ## 🛡️ **錯誤處理與穩定性** ### **全局異常處理中介軟體** ```csharp public class GlobalExceptionHandlingMiddleware { private readonly RequestDelegate _next; private readonly ILogger _logger; public async Task InvokeAsync(HttpContext context) { try { await _next(context); } catch (Exception ex) { await HandleExceptionAsync(context, ex); } } // ✅ 結構化錯誤處理:不同異常類型對應不同處理策略 private async Task HandleExceptionAsync(HttpContext context, Exception exception) { var response = exception switch { ValidationException validationEx => CreateValidationErrorResponse(validationEx), AIServiceException aiEx => CreateAIServiceErrorResponse(aiEx), DatabaseException dbEx => CreateDatabaseErrorResponse(dbEx), UnauthorizedAccessException authEx => CreateUnauthorizedResponse(authEx), _ => CreateGenericErrorResponse(exception) }; // 記錄錯誤日誌 _logger.LogError(exception, "Request failed: {Method} {Path}", context.Request.Method, context.Request.Path); // 返回結構化錯誤回應 context.Response.StatusCode = response.StatusCode; context.Response.ContentType = "application/json"; await context.Response.WriteAsync(JsonSerializer.Serialize(response)); } } ``` ### **重試與斷路器模式** ```csharp public class ResilientAIService : IAIAnalysisService { private readonly IAIProvider _primaryProvider; private readonly IAIProvider _fallbackProvider; private readonly ICircuitBreaker _circuitBreaker; // ✅ 重試機制:指數退避 public async Task CallWithRetryAsync(AIRequest request) { var retryPolicy = Policy .Handle() .Or() .WaitAndRetryAsync( retryCount: 3, sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), // 指數退避:2^n秒 onRetry: (outcome, timespan, retryCount, context) => { _logger.LogWarning("AI service retry {RetryCount} after {Delay}ms", retryCount, timespan.TotalMilliseconds); }); return await retryPolicy.ExecuteAsync(async () => { return await _circuitBreaker.ExecuteAsync(async () => { try { return await _primaryProvider.AnalyzeAsync(request); } catch (AIServiceException) { // ✅ 降級策略:使用備用提供商 _logger.LogWarning("Primary AI provider failed, using fallback"); return await _fallbackProvider.AnalyzeAsync(request); } }); }); } } ``` --- ## 📊 **監控與可觀測性** ### **結構化日誌設計** #### **日誌標準化** ```csharp public static class LoggerExtensions { // ✅ 結構化日誌:便於查詢和分析 public static void LogAIRequest(this ILogger logger, string requestId, string inputText, string provider, double processingTime) { logger.LogInformation("AI Request Completed: {RequestId} Provider: {Provider} " + "InputLength: {InputLength} ProcessingTime: {ProcessingTime}ms", requestId, provider, inputText.Length, processingTime); } public static void LogBusinessOperation(this ILogger logger, string operation, string userId, bool success, Dictionary? additionalData = null) { using var scope = logger.BeginScope(new Dictionary { ["Operation"] = operation, ["UserId"] = userId, ["Success"] = success, ["Timestamp"] = DateTime.UtcNow, ["AdditionalData"] = additionalData ?? new Dictionary() }); if (success) logger.LogInformation("Business operation completed successfully"); else logger.LogWarning("Business operation failed"); } } ``` ### **健康檢查系統** ```csharp public class AIServiceHealthCheck : IHealthCheck { private readonly IGeminiService _geminiService; private readonly ICacheService _cacheService; private readonly DramaLingDbContext _dbContext; // ✅ 全面健康檢查:AI服務、快取、資料庫 public async Task CheckHealthAsync( HealthCheckContext context, CancellationToken cancellationToken = default) { var checks = new List<(string Name, Task Check)> { ("Gemini API", CheckGeminiHealthAsync()), ("Cache Service", CheckCacheHealthAsync()), ("Database", CheckDatabaseHealthAsync()), ("Memory Usage", CheckMemoryUsageAsync()) }; var results = await Task.WhenAll(checks.Select(async check => { try { var isHealthy = await check.Check; return (check.Name, IsHealthy: isHealthy, Error: (string?)null); } catch (Exception ex) { return (check.Name, IsHealthy: false, Error: ex.Message); } })); var failedChecks = results.Where(r => !r.IsHealthy).ToList(); if (failedChecks.Any()) { var errors = string.Join(", ", failedChecks.Select(f => $"{f.Name}: {f.Error}")); return HealthCheckResult.Unhealthy($"Failed checks: {errors}"); } return HealthCheckResult.Healthy("All systems operational"); } } ``` --- ## 🔒 **安全架構設計** ### **多層安全防護** #### **API安全中介軟體** ```csharp public class SecurityMiddleware { private readonly RequestDelegate _next; private readonly ILogger _logger; public async Task InvokeAsync(HttpContext context) { // ✅ 輸入驗證:防止注入攻擊 if (!await ValidateInputSafetyAsync(context)) { context.Response.StatusCode = 400; await context.Response.WriteAsync("Invalid input detected"); return; } // ✅ 速率限制:防止濫用 if (!await CheckRateLimitAsync(context)) { context.Response.StatusCode = 429; await context.Response.WriteAsync("Rate limit exceeded"); return; } // ✅ 請求追蹤:安全稽核 var requestId = Guid.NewGuid().ToString(); context.Items["RequestId"] = requestId; using var scope = _logger.BeginScope(new Dictionary { ["RequestId"] = requestId, ["UserId"] = context.User?.Identity?.Name ?? "Anonymous", ["IPAddress"] = context.Connection.RemoteIpAddress?.ToString(), ["UserAgent"] = context.Request.Headers["User-Agent"].ToString() }); await _next(context); } private async Task ValidateInputSafetyAsync(HttpContext context) { // 檢查惡意模式 var suspiciousPatterns = new[] { @")<[^<]*)*<\/script>", // XSS @"(\bUNION\b|\bSELECT\b|\bINSERT\b|\bDELETE\b)", // SQL注入 @"(javascript:|data:|vbscript:)", // 協議注入 }; var body = await ReadRequestBodyAsync(context); return !suspiciousPatterns.Any(pattern => Regex.IsMatch(body, pattern, RegexOptions.IgnoreCase)); } } ``` ### **API金鑰管理** #### **安全配置管理** ```csharp public class SecureConfigurationService { private readonly IConfiguration _configuration; private readonly IKeyVault _keyVault; // Azure Key Vault 或 AWS Secrets Manager // ✅ 多層金鑰管理:環境變數 > Key Vault > 配置檔案 public async Task GetAPIKeyAsync(string serviceName) { // 優先級1: 環境變數(容器化部署) var envKey = Environment.GetEnvironmentVariable($"{serviceName.ToUpper()}_API_KEY"); if (!string.IsNullOrEmpty(envKey)) return envKey; // 優先級2: Key Vault(生產環境) if (_keyVault != null) { var vaultKey = await _keyVault.GetSecretAsync($"{serviceName}-api-key"); if (!string.IsNullOrEmpty(vaultKey)) return vaultKey; } // 優先級3: 配置檔案(開發環境) var configKey = _configuration[$"ApiKeys:{serviceName}"]; if (!string.IsNullOrEmpty(configKey)) return configKey; throw new InvalidOperationException($"API key for {serviceName} not found"); } // ✅ 金鑰輪換:定期更新API金鑰 public async Task RotateAPIKeyAsync(string serviceName) { var newKey = await GenerateNewKeyAsync(serviceName); await _keyVault.SetSecretAsync($"{serviceName}-api-key", newKey); // 通知相關服務更新金鑰 await NotifyKeyRotationAsync(serviceName, newKey); } } ``` --- ## 📈 **可擴展性設計** ### **微服務準備架構** #### **服務邊界定義** ```csharp // ✅ 領域驅動設計:按業務領域劃分服務 namespace DramaLing.AI.Domain { // AI分析聚合根 public class AnalysisAggregate { public AnalysisId Id { get; private set; } public string InputText { get; private set; } public AnalysisResult Result { get; private set; } public AnalysisStatus Status { get; private set; } // 業務邏輯封裝在聚合內 public void MarkAsCompleted(AnalysisResult result) { if (Status != AnalysisStatus.Processing) throw new InvalidOperationException("Analysis is not in processing state"); Result = result; Status = AnalysisStatus.Completed; // 發布領域事件 AddDomainEvent(new AnalysisCompletedEvent(Id, result)); } } } // 服務介面定義 public interface IAIAnalysisDomainService { Task StartAnalysisAsync(string inputText); Task CompleteAnalysisAsync(AnalysisId id, AnalysisResult result); } ``` #### **事件驅動架構** ```csharp public class EventDrivenAIService { private readonly IEventBus _eventBus; private readonly IAIProvider _aiProvider; // ✅ 事件驅動:解耦業務流程 public async Task ProcessAnalysisAsync(AnalysisRequest request) { // 1. 發布分析開始事件 await _eventBus.PublishAsync(new AnalysisStartedEvent { RequestId = request.Id, InputText = request.InputText, Timestamp = DateTime.UtcNow }); try { // 2. 執行AI分析 var result = await _aiProvider.AnalyzeAsync(request); // 3. 發布分析完成事件 await _eventBus.PublishAsync(new AnalysisCompletedEvent { RequestId = request.Id, Result = result, ProcessingTime = result.ProcessingTime }); return result; } catch (Exception ex) { // 4. 發布分析失敗事件 await _eventBus.PublishAsync(new AnalysisFailedEvent { RequestId = request.Id, Error = ex.Message, Timestamp = DateTime.UtcNow }); throw; } } } // 事件處理器 public class AnalysisEventHandler : IEventHandler, IEventHandler { public async Task HandleAsync(AnalysisCompletedEvent eventData) { // 更新統計資料、發送通知、清理資源等 await UpdateAnalysisStatsAsync(eventData); await NotifyUserAsync(eventData.RequestId, "分析完成"); } public async Task HandleAsync(AnalysisFailedEvent eventData) { // 錯誤恢復、告警、重試邏輯等 await LogFailureAsync(eventData); await TriggerRetryIfNecessaryAsync(eventData); } } ``` --- ## 🔧 **配置管理最佳實踐** ### **強型別配置** ```csharp // ✅ 配置類別:型別安全的配置管理 public class AIServiceOptions { public const string SectionName = "AIService"; [Required] public string GeminiApiKey { get; set; } = string.Empty; [Range(1, 300)] public int MaxInputLength { get; set; } = 300; [Range(1, 60)] public int TimeoutSeconds { get; set; } = 30; public RetryOptions Retry { get; set; } = new(); public CacheOptions Cache { get; set; } = new(); } public class RetryOptions { public int MaxAttempts { get; set; } = 3; public int BaseDelayMs { get; set; } = 1000; public bool UseExponentialBackoff { get; set; } = true; } // 配置驗證 public class AIServiceOptionsValidator : IValidateOptions { public ValidateOptionsResult Validate(string name, AIServiceOptions options) { var failures = new List(); if (string.IsNullOrWhiteSpace(options.GeminiApiKey)) failures.Add("Gemini API key is required"); if (options.TimeoutSeconds <= 0) failures.Add("Timeout must be positive"); return failures.Any() ? ValidateOptionsResult.Fail(failures) : ValidateOptionsResult.Success; } } // 註冊配置 builder.Services.Configure( builder.Configuration.GetSection(AIServiceOptions.SectionName)); builder.Services.AddSingleton, AIServiceOptionsValidator>(); ``` ### **環境特定配置** ```csharp // appsettings.Development.json { "AIService": { "GeminiApiKey": "dev-key", "TimeoutSeconds": 10, "Cache": { "EnableDistributed": false, "DefaultExpiry": "00:05:00" } }, "Logging": { "LogLevel": { "DramaLing.AI": "Debug" } } } // appsettings.Production.json { "AIService": { "TimeoutSeconds": 30, "Cache": { "EnableDistributed": true, "DefaultExpiry": "01:00:00" } }, "Logging": { "LogLevel": { "DramaLing.AI": "Information" } } } ``` --- ## 🧪 **測試架構設計** ### **測試金字塔實作** #### **單元測試 (70%)** ```csharp public class AIAnalysisServiceTests { private readonly Mock _mockGeminiService; private readonly Mock _mockCacheService; private readonly AIAnalysisService _service; // ✅ 純邏輯測試:快速、可靠、獨立 [Test] public async Task AnalyzeAsync_WithCachedResult_ReturnsCachedData() { // Arrange var request = new AnalysisRequest { InputText = "test sentence" }; var cachedResult = new AnalysisResult { /* ... */ }; _mockCacheService.Setup(c => c.GetAsync(It.IsAny())) .ReturnsAsync(cachedResult); // Act var result = await _service.AnalyzeAsync(request); // Assert Assert.That(result, Is.EqualTo(cachedResult)); _mockGeminiService.Verify(g => g.AnalyzeAsync(It.IsAny()), Times.Never); } // ✅ 錯誤情境測試 [Test] public async Task AnalyzeAsync_WhenAIServiceFails_ThrowsAIServiceException() { // Arrange _mockCacheService.Setup(c => c.GetAsync(It.IsAny())) .ReturnsAsync((AnalysisResult?)null); _mockGeminiService.Setup(g => g.AnalyzeAsync(It.IsAny())) .ThrowsAsync(new HttpRequestException("API unavailable")); var request = new AnalysisRequest { InputText = "test sentence" }; // Act & Assert Assert.ThrowsAsync(() => _service.AnalyzeAsync(request)); } } ``` #### **整合測試 (20%)** ```csharp public class AIControllerIntegrationTests : IClassFixture> { private readonly WebApplicationFactory _factory; private readonly HttpClient _client; // ✅ 真實環境測試:包含所有中介軟體和配置 [Test] public async Task AnalyzeSentence_WithValidInput_ReturnsSuccessResponse() { // Arrange var request = new AnalysisRequest { InputText = "She just join the team", AnalysisMode = "full" }; // Act var response = await _client.PostAsJsonAsync("/api/ai/analyze-sentence", request); // Assert response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync(); Assert.That(result.Success, Is.True); Assert.That(result.Data, Is.Not.Null); Assert.That(result.Data.VocabularyAnalysis, Is.Not.Empty); } } ``` #### **E2E測試 (10%)** ```csharp public class AIAnalysisE2ETests { private readonly TestServer _server; private readonly HttpClient _client; // ✅ 端到端測試:包含真實的AI API調用 [Test] [Category("E2E")] public async Task CompleteAnalysisWorkflow_WithRealAI_ProducesValidResults() { // 此測試使用真實的AI API,運行時間較長 // 適合在CI/CD流程中定期執行 var testSentences = new[] { "She just join the team, so let's cut her some slack.", "The implementation was challenging but rewarding.", "Could you please review the documentation?" }; foreach (var sentence in testSentences) { var request = new AnalysisRequest { InputText = sentence }; var response = await _client.PostAsJsonAsync("/api/ai/analyze-sentence", request); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync(); // 驗證AI分析品質 Assert.That(result.Data.VocabularyAnalysis.Count, Is.GreaterThan(0)); Assert.That(result.Data.SentenceMeaning, Is.Not.Empty); // 驗證CEFR等級分配合理性 Assert.That(result.Data.VocabularyAnalysis.Values .All(v => Enum.IsDefined(typeof(CEFRLevel), v.DifficultyLevel)), Is.True); } } } ``` --- ## 🔄 **部署與DevOps架構** ### **容器化配置** #### **Dockerfile最佳實踐** ```dockerfile # ✅ 多階段建置:減小鏡像大小 FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src # ✅ 快取優化:先複製專案檔案 COPY ["DramaLing.Api/DramaLing.Api.csproj", "DramaLing.Api/"] COPY ["DramaLing.Core/DramaLing.Core.csproj", "DramaLing.Core/"] RUN dotnet restore "DramaLing.Api/DramaLing.Api.csproj" # 複製原始碼並建置 COPY . . WORKDIR "/src/DramaLing.Api" RUN dotnet build "DramaLing.Api.csproj" -c Release -o /app/build # 發布應用程式 FROM build AS publish RUN dotnet publish "DramaLing.Api.csproj" -c Release -o /app/publish --no-restore # ✅ 執行階段:使用最小鏡像 FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final WORKDIR /app # ✅ 安全設定:非root用戶 RUN adduser --disabled-password --gecos '' appuser && chown -R appuser /app USER appuser COPY --from=publish /app/publish . # ✅ 健康檢查:容器監控 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:5000/health || exit 1 ENTRYPOINT ["dotnet", "DramaLing.Api.dll"] ``` #### **Kubernetes部署配置** ```yaml # deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: dramaling-api spec: replicas: 3 # ✅ 高可用:多實例部署 strategy: type: RollingUpdate # ✅ 零停機部署 rollingUpdate: maxSurge: 1 maxUnavailable: 0 template: spec: containers: - name: api image: dramaling/api:latest resources: # ✅ 資源限制:防止資源爭用 requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" env: # ✅ 外部化配置 - name: ASPNETCORE_ENVIRONMENT value: "Production" - name: GEMINI_API_KEY valueFrom: secretKeyRef: name: ai-api-keys key: gemini-key livenessProbe: # ✅ 自動恢復 httpGet: path: /health port: 5000 initialDelaySeconds: 30 periodSeconds: 30 readinessProbe: # ✅ 流量控制 httpGet: path: /health/ready port: 5000 initialDelaySeconds: 5 periodSeconds: 5 ``` --- ## 📊 **監控指標設計** ### **關鍵性能指標 (KPIs)** #### **業務指標監控** ```csharp public class BusinessMetricsService { private readonly IMetricsCollector _metrics; // ✅ 業務指標:追蹤產品健康度 public void RecordAnalysisMetrics(AnalysisResult result) { // AI分析成功率 _metrics.Increment("ai.analysis.completed", new[] { new KeyValuePair("provider", result.Provider), new KeyValuePair("processing_time", result.ProcessingTime) }); // 詞彙複雜度分布 _metrics.Histogram("vocabulary.difficulty.distribution", result.VocabularyAnalysis.Values.Select(v => GetDifficultyScore(v.DifficultyLevel))); // 用戶參與度 _metrics.Increment("user.engagement", new[] { new KeyValuePair("feature", "ai_analysis"), new KeyValuePair("session_id", result.SessionId) }); } // ✅ 異常指標:快速發現問題 public void RecordErrorMetrics(Exception exception, string operation) { _metrics.Increment("errors.total", new[] { new KeyValuePair("operation", operation), new KeyValuePair("error_type", exception.GetType().Name), new KeyValuePair("severity", GetErrorSeverity(exception)) }); } } ``` #### **技術指標監控** ```csharp public class TechnicalMetricsMiddleware { private readonly RequestDelegate _next; private readonly IMetricsCollector _metrics; public async Task InvokeAsync(HttpContext context) { var stopwatch = Stopwatch.StartNew(); var path = context.Request.Path.Value; var method = context.Request.Method; try { await _next(context); } finally { stopwatch.Stop(); // ✅ 請求指標:API性能監控 _metrics.Histogram("http.request.duration", stopwatch.ElapsedMilliseconds, new[] { new KeyValuePair("method", method), new KeyValuePair("path", path), new KeyValuePair("status_code", context.Response.StatusCode) }); // 記憶體使用指標 var memoryUsage = GC.GetTotalMemory(false); _metrics.Gauge("system.memory.usage", memoryUsage); // 資料庫連接池指標 _metrics.Gauge("database.connection_pool.active", GetActiveConnectionCount()); } } } ``` --- ## 📚 **程式碼品質標準** ### **編碼規範** #### **命名規範** ```csharp // ✅ 清晰的命名:表達意圖,而非實現 public class SentenceAnalysisService // 而非 AIService { // 方法命名:動詞 + 名詞,表達業務意圖 public async Task AnalyzeEnglishSentenceAsync(string sentence) { // 變數命名:有意義的業務術語 var grammarCheckResult = await CheckGrammarAsync(sentence); var vocabularyAnalysis = await AnalyzeVocabularyAsync(sentence); var idiomDetection = await DetectIdiomsAsync(sentence); return BuildAnalysisResult(grammarCheckResult, vocabularyAnalysis, idiomDetection); } // ✅ 私有方法:體現實現細節 private async Task CheckGrammarAsync(string sentence) { var prompt = _promptBuilder.BuildGrammarCheckPrompt(sentence); var aiResponse = await _aiProvider.CallAsync(prompt); return _grammarParser.Parse(aiResponse); } } ``` #### **錯誤處理規範** ```csharp // ✅ 自訂異常:明確的錯誤分類 public abstract class DramaLingException : Exception { public string ErrorCode { get; } public Dictionary Context { get; } protected DramaLingException(string errorCode, string message, Dictionary? context = null) : base(message) { ErrorCode = errorCode; Context = context ?? new Dictionary(); } } public class AIServiceException : DramaLingException { public AIServiceException(string provider, string aiErrorMessage, string? originalPrompt = null) : base("AI_SERVICE_ERROR", $"AI service '{provider}' failed: {aiErrorMessage}") { Context["Provider"] = provider; Context["AIErrorMessage"] = aiErrorMessage; if (originalPrompt != null) Context["OriginalPrompt"] = originalPrompt; } } // 使用範例 public async Task AnalyzeAsync(string inputText) { try { return await _aiProvider.AnalyzeAsync(inputText); } catch (HttpRequestException ex) when (ex.Message.Contains("timeout")) { throw new AIServiceException(_aiProvider.Name, "Request timeout", inputText); } catch (JsonException ex) { throw new AIServiceException(_aiProvider.Name, "Invalid response format", inputText); } } ``` --- ## 🔮 **未來擴展架構** ### **AI模型演進支援** #### **模型版本管理** ```csharp public class AIModelManager { private readonly Dictionary _providers; private readonly IConfiguration _configuration; // ✅ 版本控制:支援AI模型A/B測試 public async Task AnalyzeWithVersionAsync(string inputText, string? modelVersion = null) { var version = modelVersion ?? GetDefaultModelVersion(); var provider = GetProviderForVersion(version); var result = await provider.AnalyzeAsync(inputText); // 記錄模型性能 await RecordModelPerformanceAsync(version, result); return result; } // ✅ 藍綠部署:無縫模型升級 public async Task CanaryDeploymentAsync(string newModelVersion, double trafficPercentage) { var canaryProvider = GetProviderForVersion(newModelVersion); var testResults = await RunCanaryTestsAsync(canaryProvider); if (testResults.ErrorRate < 0.01 && testResults.PerformanceIndex > 0.95) { await GraduallyMigrateTrafficAsync(newModelVersion, trafficPercentage); return true; } return false; } } ``` ### **多租戶架構準備** #### **租戶隔離設計** ```csharp public class TenantContext { public string TenantId { get; set; } public string DatabaseConnection { get; set; } public AIServiceConfiguration AIConfig { get; set; } public Dictionary CustomSettings { get; set; } } public class MultiTenantAIService : IAIAnalysisService { private readonly ITenantResolver _tenantResolver; private readonly IServiceProvider _serviceProvider; // ✅ 租戶隔離:每個租戶獨立配置 public async Task AnalyzeAsync(AnalysisRequest request) { var tenant = await _tenantResolver.GetCurrentTenantAsync(); // 使用租戶特定的AI配置 var aiProvider = _serviceProvider.GetKeyedService(tenant.AIConfig.Provider); // 使用租戶特定的提示詞範本 var prompt = BuildTenantSpecificPrompt(request.InputText, tenant); return await aiProvider.AnalyzeAsync(prompt); } } ``` --- ## 📋 **實施檢查清單** ### **架構實施階段** #### **Phase 1: 基礎架構 (第1-2週)** - [ ] 建立分層架構基礎專案結構 - [ ] 實作依賴注入容器配置 - [ ] 建立基礎API控制器和中介軟體 - [ ] 設定Entity Framework和資料庫遷移 - [ ] 實作基本的健康檢查和日誌系統 #### **Phase 2: AI整合 (第3-4週)** - [ ] 實作AI提供商抽象層 - [ ] 建立智能快取系統 - [ ] 實作Prompt管理和版本控制 - [ ] 建立錯誤處理和重試機制 - [ ] 設定監控指標和告警 #### **Phase 3: 優化與安全 (第5-6週)** - [ ] 實作性能優化策略 - [ ] 建立全面的安全防護 - [ ] 完善測試套件 (單元/整合/E2E) - [ ] 設定CI/CD流程 - [ ] 建立生產環境監控 ### **品質檢查標準** #### **程式碼品質** ```yaml 覆蓋率要求: - 單元測試覆蓋率: > 80% - 業務邏輯覆蓋率: > 90% - 關鍵路徑覆蓋率: 100% 性能基準: - API回應時間: P95 < 500ms - AI處理時間: P95 < 5s - 資料庫查詢: P95 < 100ms - 記憶體使用: < 500MB per instance 安全檢查: - 輸入驗證: 100%覆蓋 - SQL注入防護: 已驗證 - XSS防護: 已驗證 - API金鑰安全: 已驗證 ``` --- ## 🎓 **最佳實踐總結** ### **核心原則** 1. **單一職責**: 每個類別、方法都有明確單一的職責 2. **依賴倒置**: 依賴抽象而非具體實現 3. **開放封閉**: 對擴展開放,對修改封閉 4. **介面隔離**: 客戶端不應依賴不需要的介面 5. **最小驚訝**: 程式碼行為符合直覺期望 ### **AI特定最佳實踐** 1. **Prompt版本化**: 將Prompt當作程式碼管理 2. **多提供商支援**: 避免供應商鎖定 3. **智能快取**: 減少昂貴的AI API調用 4. **優雅降級**: AI服務故障時的備援策略 5. **成本監控**: 追蹤和優化AI API使用成本 ### **維護性保證** 1. **文檔驅動**: 架構決策和變更都要記錄 2. **自動化測試**: 確保重構和擴展的安全性 3. **監控完整**: 從業務指標到技術指標全覆蓋 4. **配置外部化**: 所有環境特定配置都外部化 5. **日誌結構化**: 便於查詢、分析和告警 --- **文件版本**: v1.0 **技術架構**: .NET 8 + Entity Framework + AI整合 **最後更新**: 2025-01-25 **下次審查**: 2025-02-25 **參考實現**: DramaLing AI語言學習平台 **適用範圍**: 中小型AI驅動產品 (< 10萬用戶)