dramaling-vocab-learning/docs/05_deployment/AI驅動產品後端技術架構指南.md

2293 lines
92 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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) │
└─────────────────────────────────────────────────────────────┘
```
---
## 🔄 **程式碼運作流程圖**
### **AI 句子分析完整流程**
```
🌐 前端用戶請求
┌─────────────────────────────────────────────────────────────┐
│ 📱 Next.js Frontend (http://localhost:3000) │
│ │
│ generatePage.tsx: │
│ └─ handleAnalyzeSentence() │
│ ├─ 驗證輸入長度 (MAX_MANUAL_INPUT_LENGTH = 300) │
│ ├─ 構建請求體 { inputText, options } │
│ └─ fetch('http://localhost:5008/api/ai/analyze-sentence') │
└─────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 🚀 .NET 8 Backend API (http://localhost:5008) │
│ │
│ 1⃣ SecurityMiddleware (未啟用) │
│ ├─ 速率限制檢查 │
│ ├─ 惡意輸入檢測 │
│ └─ 安全標頭添加 │
│ │
│ 2⃣ ErrorHandlingMiddleware │
│ ├─ 全局異常捕獲 │
│ ├─ 結構化錯誤回應 │
│ └─ 錯誤日誌記錄 │
│ │
│ 3⃣ AIController.AnalyzeSentence() │
│ ├─ 請求驗證 (ModelState.IsValid) │
│ ├─ 生成 requestId = Guid.NewGuid() │
│ ├─ 開始計時 (Stopwatch.StartNew()) │
│ └─ 調用 _geminiService.AnalyzeSentenceAsync() │
└─────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 🤖 GeminiService 層 │
│ │
│ GeminiService.AnalyzeSentenceAsync(): │
│ ├─ 1. 構建結構化 Prompt (lines 42-95) │
│ │ ├─ 包含完整 JSON Schema 定義 │
│ │ ├─ 指定分析指南 (語法檢查、詞彙分析、CEFR等級) │
│ │ └─ 明確要求只返回 JSON │
│ │ │
│ ├─ 2. CallGeminiAPI() (lines 330-418) │
│ │ ├─ 構建請求體 (contents, generationConfig) │
│ │ ├─ HTTP POST 到 Gemini API │
│ │ ├─ 解析 JSON 回應結構 │
│ │ └─ 提取 candidates[0].content.parts[0].text │
│ │ │
│ └─ 3. CreateAnalysisFromAIResponse() (lines 128-182) │
│ ├─ 清理 AI 回應 (移除 ```json 標記) │
│ ├─ JSON 反序列化為 AiAnalysisResponse │
│ ├─ 轉換為 SentenceAnalysisData DTO │
│ └─ 錯誤時使用 CreateFallbackAnalysis() │
└─────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 🌍 Google Gemini API │
│ │
│ Endpoint: generativelanguage.googleapis.com │
│ Model: gemini-1.5-flash │
│ ├─ 接收結構化 Prompt │
│ ├─ 執行 AI 推理 (3-5秒) │
│ ├─ 返回 JSON 格式分析結果 │
│ └─ 包含: 翻譯、語法檢查、詞彙分析、慣用語 │
└─────────────────────────┬───────────────────────────────────┘
▼ (回應路徑)
┌─────────────────────────────────────────────────────────────┐
│ 📤 回應處理流程 │
│ │
│ 1⃣ GeminiService 回應處理: │
│ ├─ 解析 AI JSON 回應 │
│ ├─ 轉換資料格式 (DTO 映射) │
│ ├─ 設定 Metadata (處理時間、模型版本) │
│ └─ 返回 SentenceAnalysisData │
│ │
│ 2⃣ AIController 回應包裝: │
│ ├─ 停止計時器 │
│ ├─ 記錄性能日誌 │
│ ├─ 包裝為 SentenceAnalysisResponse │
│ └─ 返回 HTTP 200 OK │
│ │
│ 3⃣ 前端處理回應: │
│ ├─ 檢查 response.ok 和 result.success │
│ ├─ 設定狀態 setSentenceAnalysis(apiData) │
│ ├─ 處理語法修正 setGrammarCorrection() │
│ ├─ 渲染 ClickableTextV2 組件 │
│ └─ 顯示翻譯和慣用語 │
└─────────────────────────────────────────────────────────────┘
```
### **優化後的流程 (目標架構)**
```
🌐 前端用戶請求
┌─────────────────────────────────────────────────────────────┐
│ 📱 Next.js Frontend + 性能優化 │
│ │
│ 使用 /frontend/lib/performance/: │
│ ├─ cachedApiCall() - 本地快取檢查 │
│ ├─ debounce() - 防止重複請求 │
│ ├─ PerformanceMonitor - 性能追蹤 │
│ └─ 快取命中 = 直接返回,無需 API 調用 │
└─────────────────────────┬───────────────────────────────────┘
│ (快取未命中)
┌─────────────────────────────────────────────────────────────┐
│ 🛡️ 安全與路由層 │
│ │
│ 1⃣ SecurityMiddleware: │
│ ├─ CheckRateLimit() - 檢查請求頻率 │
│ ├─ ValidateInputSafety() - 惡意模式檢測 │
│ ├─ 請求大小驗證 │
│ └─ 添加安全標頭 │
│ │
│ 2⃣ 路由到 /api/v2/ai/analyze-sentence │
│ └─ OptimizedAIController.AnalyzeSentenceOptimized() │
└─────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 🚀 優化的控制器層 │
│ │
│ OptimizedAIController: │
│ ├─ 1. 生成快取鍵 GenerateCacheKey() │
│ │ └─ SHA256(inputText + options) → "analysis:abc123" │
│ │ │
│ ├─ 2. 快取檢查 _cacheService.GetAsync<T>(cacheKey) │
│ │ ├─ L1: Memory Cache 檢查 (< 1ms) │
│ │ ├─ L2: Distributed Cache 檢查 (Redis, < 10ms) │
│ │ └─ 快取命中 → 直接返回結果 + FromCache: true │
│ │ │
│ └─ 3. 快取未命中 → 調用 AI 服務 │
│ └─ _aiProviderManager.AnalyzeSentenceAsync() │
└─────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 🎯 AI 提供商管理層 │
│ │
│ AIProviderManager: │
│ ├─ 1. GetBestProviderAsync(ProviderSelectionStrategy) │
│ │ ├─ Performance: 選擇響應時間最快的 │
│ │ ├─ Cost: 選擇成本最低的 │
│ │ ├─ Reliability: 選擇成功率最高的 │
│ │ └─ LoadBalance: 隨機負載均衡 │
│ │ │
│ ├─ 2. 健康檢查 CheckHealthAsync() │
│ │ └─ 過濾不可用的提供商 │
│ │ │
│ ├─ 3. 故障轉移機制 │
│ │ ├─ 主提供商失敗 → 自動切換備用提供商 │
│ │ ├─ 記錄失敗統計 │
│ │ └─ 更新提供商健康狀態 │
│ │ │
│ └─ 4. 調用選定的 IAIProvider.AnalyzeSentenceAsync() │
└─────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 🤖 具體 AI 提供商 (GeminiAIProvider) │
│ │
│ 實施 IAIProvider 介面: │
│ ├─ ProviderName: "Google Gemini" │
│ ├─ IsAvailable: 檢查 API Key 配置 │
│ ├─ CostPerRequest: 0.001m │
│ └─ AverageResponseTimeMs: 統計平均響應時間 │
│ │
│ AnalyzeSentenceAsync() 執行流程: │
│ ├─ 1. BuildAnalysisPrompt() - 構建結構化提示詞 │
│ ├─ 2. CallGeminiAPIAsync() - HTTP 調用 │
│ │ ├─ 請求統計 _stats.TotalRequests++ │
│ │ ├─ 計時開始 Stopwatch.StartNew() │
│ │ ├─ HTTP POST 到 Gemini API │
│ │ └─ ExtractTextFromResponse() 解析回應 │
│ │ │
│ ├─ 3. ParseAIResponse() - 解析和轉換 │
│ │ ├─ CleanAIResponse() - 清理格式 │
│ │ ├─ JsonSerializer.Deserialize() │
│ │ ├─ ConvertVocabularyAnalysis() │
│ │ ├─ ConvertIdioms() │
│ │ └─ ConvertGrammarCorrection() │
│ │ │
│ └─ 4. 統計記錄 RecordSuccessfulRequest() │
│ ├─ 更新成功率統計 │
│ ├─ 計算平均響應時間 │
│ └─ 記錄成本追蹤 │
└─────────────────────────┬───────────────────────────────────┘
▼ (成功回應)
┌─────────────────────────────────────────────────────────────┐
│ 💾 快取與回應處理 │
│ │
│ HybridCacheService.SetAsync(): │
│ ├─ 1. 計算智能過期時間 │
│ │ ├─ analysis: → 2小時 │
│ │ ├─ user: → 30分鐘 │
│ │ └─ stats: → 5分鐘 │
│ │ │
│ ├─ 2. 多層快取存儲 │
│ │ ├─ L1: MemoryCache.Set() (< 1ms) │
│ │ └─ L2: DistributedCache.SetAsync() (< 10ms) │
│ │ │
│ └─ 3. 控制器回應格式化 │
│ ├─ 包裝 SentenceAnalysisResponse │
│ ├─ 添加處理時間和 FromCache 標記 │
│ └─ HTTP 200 OK 返回 │
└─────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 🎨 前端 UI 渲染 │
│ │
│ response 處理流程: │
│ ├─ 1. 解析 API 回應 │
│ │ ├─ setSentenceAnalysis(analysisData) │
│ │ ├─ setSentenceMeaning(翻譯) │
│ │ └─ setGrammarCorrection(語法修正) │
│ │ │
│ ├─ 2. 計算詞彙統計 useMemo() │
│ │ ├─ simpleCount (使用者程度以下) │
│ │ ├─ moderateCount (適合學習) │
│ │ ├─ difficultCount (具挑戰性) │
│ │ └─ idiomCount (慣用語數量) │
│ │ │
│ ├─ 3. 渲染 ClickableTextV2 組件 │
│ │ ├─ 每個詞彙點擊顯示詳細資訊 │
│ │ ├─ handleSaveWord() 保存到詞卡 │
│ │ └─ 顏色編碼顯示難度級別 │
│ │ │
│ └─ 4. 顯示統計卡片和翻譯 │
│ ├─ 四色卡片統計 (簡單/適中/困難/慣用語) │
│ ├─ 中文翻譯展示 │
│ └─ 慣用語點擊彈窗 │
└─────────────────────────────────────────────────────────────┘
```
### **Repository Pattern 數據流程**
```
🔍 數據查詢請求
┌─────────────────────────────────────────────────────────────┐
│ 🎯 業務服務層 (未來實作) │
│ │
│ FlashcardService (規劃中): │
│ ├─ GetUserFlashcards(userId) │
│ ├─ CreateFlashcard(cardData) │
│ ├─ UpdateMasteryLevel(flashcardId, level) │
│ └─ GetStudyRecommendations(userId) │
└─────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 📁 Repository 抽象層 │
│ │
│ IFlashcardRepository: │
│ ├─ GetFlashcardsByUserIdAsync(Guid userId) │
│ ├─ GetDueFlashcardsAsync(userId, dueDate) │
│ ├─ GetFlashcardsByDifficultyAsync(userId, level) │
│ ├─ SearchFlashcardsAsync(userId, searchTerm) │
│ ├─ GetTotalFlashcardsCountAsync(userId) │
│ └─ BulkUpdateMasteryLevelAsync(flashcardIds, level) │
│ │
│ 實作選擇: │
│ ├─ SimpleFlashcardRepository (目前使用) │
│ └─ FlashcardRepository (完整功能版本) │
└─────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 🗃️ BaseRepository<T> 泛型實作 │
│ │
│ 查詢優化策略: │
│ ├─ AsNoTracking() - 只讀查詢,提升 40-60% 性能 │
│ ├─ 投影查詢 Select() - 只查詢需要的欄位 │
│ ├─ 分頁查詢 GetPagedAsync() - 大數據集處理 │
│ ├─ 批次操作 AddRangeAsync() - 減少資料庫往返 │
│ └─ 異常處理和日誌記錄 │
│ │
│ 核心方法: │
│ ├─ GetByIdAsync(id) │
│ ├─ FindAsync(Expression<Func<T, bool>>) │
│ ├─ FirstOrDefaultAsync(predicate) │
│ ├─ AddAsync(entity) / UpdateAsync(entity) │
│ └─ SaveChangesAsync() - 工作單元模式 │
└─────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 🗄️ Entity Framework Core + SQLite │
│ │
│ DramaLingDbContext: │
│ ├─ 實體映射 (User, Flashcard, CardSet, StudySession...) │
│ ├─ 關係配置 (一對多、多對多) │
│ ├─ 索引優化 (複合索引、唯一索引) │
│ ├─ 資料庫遷移管理 │
│ └─ 連接池管理 │
│ │
│ 性能優化: │
│ ├─ 索引策略 HasIndex() │
│ ├─ 查詢編譯快取 │
│ ├─ 批次操作支援 │
│ └─ 連接字串優化 │
└─────────────────────────────────────────────────────────────┘
```
### **AI 提供商選擇流程**
```
📞 AI 分析請求
┌─────────────────────────────────────────────────────────────┐
│ 🎛️ AIProviderManager.GetBestProviderAsync() │
│ │
│ 選擇策略決策樹: │
│ │
│ ProviderSelectionStrategy 參數 │
│ │ │
│ ├─ Performance ──────┐ │
│ ├─ Cost ─────────────┼─► 提供商評估邏輯 │
│ ├─ Reliability ──────┤ │
│ ├─ LoadBalance ──────┤ │
│ └─ Primary ──────────┘ │
│ │
│ 評估流程: │
│ ├─ 1. GetAvailableProvidersAsync() │
│ │ ├─ 檢查 provider.IsAvailable │
│ │ ├─ 執行 provider.CheckHealthAsync() │
│ │ └─ 過濾不健康的提供商 │
│ │ │
│ ├─ 2. 根據策略排序 │
│ │ ├─ Performance: OrderBy(ResponseTime) │
│ │ ├─ Cost: OrderBy(CostPerRequest) │
│ │ ├─ Reliability: OrderByDescending(SuccessRate) │
│ │ └─ LoadBalance: Random.Next() │
│ │ │
│ └─ 3. 返回最佳提供商 │
│ └─ 如果主提供商失敗,自動故障轉移到備用提供商 │
└─────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 🤖 具體 AI 提供商執行 │
│ │
│ GeminiAIProvider (目前唯一提供商): │
│ ├─ 統計追蹤 AIProviderStats │
│ │ ├─ TotalRequests++ │
│ │ ├─ 計時統計 │
│ │ └─ 成本追蹤 │
│ │ │
│ ├─ 健康檢查能力 │
│ │ ├─ CheckHealthAsync() - 測試 API 連通性 │
│ │ ├─ GetStatsAsync() - 返回使用統計 │
│ │ └─ 響應時間監控 │
│ │ │
│ └─ 實際 AI 調用 │
│ ├─ BuildAnalysisPrompt() - 同現有邏輯 │
│ ├─ CallGeminiAPIAsync() - HTTP 調用 │
│ ├─ ParseAIResponse() - 結果解析 │
│ └─ 異常處理和降級策略 │
└─────────────────────────┬───────────────────────────────────┘
▼ (成功回應)
┌─────────────────────────────────────────────────────────────┐
│ 💾 快取存儲 + 監控 │
│ │
│ 1⃣ HybridCacheService.SetAsync(): │
│ ├─ 智能過期時間計算 │
│ ├─ 並行寫入多層快取 │
│ └─ 統計更新 (HitCount, TotalKeys) │
│ │
│ 2⃣ PerformanceMonitor 記錄: │
│ ├─ API 調用時間 │
│ ├─ 快取命中率 │
│ ├─ 記憶體使用量 │
│ └─ 提供商性能統計 │
│ │
│ 3⃣ HealthCheckService 監控: │
│ ├─ 資料庫連接性 │
│ ├─ AI 服務可用性 │
│ ├─ 快取服務狀態 │
│ └─ 系統資源使用 │
└─────────────────────────────────────────────────────────────┘
```
### **數據流向圖**
```
📊 數據流向和依賴關係
🌐 HTTP Request 📱 Frontend React Components
│ │
▼ ▼
🛡️ Security Middleware 🧩 ClickableTextV2
│ │
▼ ▼
📡 Controller Layer 💾 Local Cache
│ │
▼ ▼
🤖 AI Provider Manager 📈 Performance Monitor
│ │
▼ ▼
🔧 Service Layer 🎯 User Interaction
│ │
▼ ▼
💾 Cache Service 💡 Learning Analytics
│ │
▼ ▼
📁 Repository Layer 📊 Statistics Display
│ │
▼ ▼
🗄️ Database (SQLite) 🎨 UI Rendering
依賴注入流程:
Program.cs
├─ AddRepositoryServices() → IRepository<T>, IFlashcardRepository
├─ AddCachingServices() → ICacheService, MemoryCache
├─ AddAIServices() → IAIProvider, IAIProviderManager
├─ AddBusinessServices() → IAuthService, IGeminiService
├─ AddAuthenticationServices() → JWT Bearer Token
└─ AddApiDocumentationServices() → Swagger/OpenAPI
```
### **快取策略流程圖**
```
🔍 快取查詢流程 (HybridCacheService)
GetAsync<T>(key) 調用
┌─ L1: Memory Cache Check ────┐
│ _memoryCache.TryGetValue() │ ✅ Hit (< 1ms)
│ │ │
│ ❌ Miss │ ▼
│ │ │ Return cached data
│ ▼ │ + Log cache hit
└─ L2: Distributed Cache ─────┘ + Update stats
│ _distributedCache.GetAsync()
│ ✅ Hit (< 10ms)
❌ Miss │
│ ▼
▼ Deserialize data
Return null │
+ Log cache miss ▼
+ Update stats Set to L1 cache
Return data
SetAsync<T>(key, value) 調用
┌─ 計算智能過期時間 ────────────────────────────────┐
│ CalculateSmartExpiry(): │
│ ├─ "analysis:*" → 2 hours │
│ ├─ "user:*" → 30 minutes │
│ ├─ "flashcard:*" → 15 minutes │
│ ├─ "stats:*" → 5 minutes │
│ └─ default → 10 minutes │
└─────────────────┬────────────────────────────────┘
┌─ 並行設定多層快取 ──────────────────────────────────┐
│ │
│ Task 1: Memory Cache │
│ ├─ 限制最大時間 (30分鐘) │
│ ├─ _memoryCache.Set(key, value, expiry) │
│ └─ 立即可用 (< 1ms) │
│ │
│ Task 2: Distributed Cache (如果可用) │
│ ├─ SerializeToBytes(value) │
│ ├─ DistributedCacheEntryOptions 設定 │
│ ├─ _distributedCache.SetAsync() │
│ └─ 跨實例共享 (< 10ms) │
│ │
│ Task.WhenAll() 等待完成 │
└─────────────────────────────────────────────────┘
```
### **分層職責定義**
#### **1. Controllers Layer (控制器層)**
**職責**: 處理HTTP請求、參數驗證、回應格式化
```csharp
[ApiController]
[Route("api/[controller]")]
public class AIController : ControllerBase
{
private readonly IAIAnalysisService _aiService;
private readonly ILogger<AIController> _logger;
// ✅ 好的實踐單一職責只處理HTTP層邏輯
[HttpPost("analyze")]
public async Task<ActionResult<AnalysisResponse>> 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<AnalysisResult> AnalyzeAsync(AnalysisRequest request);
}
public class AIAnalysisService : IAIAnalysisService
{
private readonly IGeminiService _geminiService;
private readonly ICacheService _cacheService;
private readonly IAnalysisRepository _repository;
// ✅ 好的實踐:依賴注入,介面隔離
public async Task<AnalysisResult> 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<AnalysisResult?> GetCachedAnalysisAsync(string inputHash);
Task SaveAnalysisAsync(AnalysisResult result);
Task<IEnumerable<AnalysisStats>> GetUsageStatsAsync(DateTime from, DateTime to);
}
public class AnalysisRepository : IAnalysisRepository
{
private readonly DramaLingDbContext _context;
// ✅ 好的實踐Repository模式查詢優化
public async Task<AnalysisResult?> 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<AIResponse> AnalyzeAsync(AIRequest request);
bool IsAvailable { get; }
decimal CostPerRequest { get; }
}
public class GeminiProvider : IAIProvider
{
public string ProviderName => "Google Gemini";
public async Task<AIResponse> AnalyzeAsync(AIRequest request)
{
// Gemini特定實現
}
}
public class OpenAIProvider : IAIProvider
{
public string ProviderName => "OpenAI GPT";
public async Task<AIResponse> AnalyzeAsync(AIRequest request)
{
// OpenAI特定實現
}
}
// AI提供商選擇器
public class AIProviderSelector
{
private readonly IEnumerable<IAIProvider> _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<AIRequest> _requestQueue = new();
private readonly Timer _batchTimer;
public async Task<AIResponse> 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<T?> GetAsync<T>(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<T>(distributedResult);
_memoryCache.Set(key, result, TimeSpan.FromMinutes(5));
return result;
}
// L3: 資料庫快取 (持久化)
var dbResult = await GetFromDatabaseAsync<T>(key);
if (dbResult != null)
{
await SetMultiLevelCacheAsync(key, dbResult);
return dbResult;
}
return null;
}
// 智能過期策略
public async Task SetAsync<T>(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);
}
}
```
---
## 🤖 **AI Prompt 工程標準**
### **Prompt 設計原則**
#### **核心設計理念**
```yaml
明確性原則:
- 使用清晰、明確的指令語言
- 避免模糊或可多重解釋的表達
- 明確定義每個輸出欄位的用途和格式
一致性原則:
- 統一的 JSON Schema 定義
- 標準化的欄位命名規範
- 一致的資料類型和格式要求
穩定性原則:
- 避免讓 AI 自由發揮導致格式不穩定
- 提供完整的結構範例
- 明確禁止不期望的行為
效率性原則:
- 簡潔的 prompt 減少 token 消耗
- 平衡詳細程度與處理速度
- 優先處理核心業務需求
```
### **標準 JSON Schema 定義**
#### **句子分析回應格式**
```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"title": "SentenceAnalysisResponse",
"description": "AI 句子分析的標準回應格式",
"required": ["sentenceTranslation", "hasGrammarErrors", "grammarCorrections", "vocabularyAnalysis", "idioms"],
"properties": {
"sentenceTranslation": {
"type": "string",
"description": "整句的繁體中文翻譯,純文字格式",
"minLength": 1,
"maxLength": 500,
"examples": ["我是一個學生", "你好嗎", "給他一點空間吧"]
},
"hasGrammarErrors": {
"type": "boolean",
"description": "句子是否包含語法錯誤"
},
"grammarCorrections": {
"type": "array",
"description": "語法修正建議陣列",
"items": {
"type": "object",
"required": ["original", "corrected", "type", "explanation"],
"properties": {
"original": {"type": "string", "description": "錯誤的原文"},
"corrected": {"type": "string", "description": "修正後的文字"},
"type": {"type": "string", "enum": ["tense", "subject-verb", "preposition", "word-order"]},
"explanation": {"type": "string", "description": "繁體中文解釋"}
}
}
},
"vocabularyAnalysis": {
"type": "object",
"description": "詞彙分析物件,每個詞彙一個條目",
"patternProperties": {
"^word\\d+$": {
"type": "object",
"required": ["word", "translation", "definition", "partOfSpeech", "pronunciation", "difficultyLevel", "frequency"],
"properties": {
"word": {"type": "string", "description": "詞彙本身"},
"translation": {"type": "string", "description": "繁體中文翻譯"},
"definition": {"type": "string", "description": "英文定義"},
"partOfSpeech": {"type": "string", "enum": ["noun", "verb", "adjective", "adverb", "pronoun", "preposition", "conjunction", "article", "determiner"]},
"pronunciation": {"type": "string", "pattern": "^/.*/$", "description": "IPA 音標格式"},
"difficultyLevel": {"type": "string", "enum": ["A1", "A2", "B1", "B2", "C1", "C2"]},
"frequency": {"type": "string", "enum": ["high", "medium", "low"], "description": "使用頻率"},
"synonyms": {"type": "array", "items": {"type": "string"}},
"example": {"type": "string", "description": "例句"},
"exampleTranslation": {"type": "string", "description": "例句繁體中文翻譯"}
}
}
}
},
"idioms": {
"type": "array",
"description": "慣用語分析陣列",
"items": {
"type": "object",
"required": ["idiom", "translation", "definition", "pronunciation", "difficultyLevel", "frequency"],
"properties": {
"idiom": {"type": "string", "description": "慣用語表達"},
"translation": {"type": "string", "description": "繁體中文意思"},
"definition": {"type": "string", "description": "英文解釋"},
"pronunciation": {"type": "string", "pattern": "^/.*/$"},
"difficultyLevel": {"type": "string", "enum": ["A1", "A2", "B1", "B2", "C1", "C2"]},
"frequency": {"type": "string", "enum": ["high", "medium", "low"]},
"synonyms": {"type": "array", "items": {"type": "string"}},
"example": {"type": "string"},
"exampleTranslation": {"type": "string"}
}
}
}
}
}
```
### **標準 Prompt 模板**
#### **句子分析 Prompt 範例**
```
You are an English learning assistant. Analyze this sentence and return ONLY a valid JSON response.
**Input Sentence**: "{inputText}"
**Required JSON Structure:**
{
"sentenceTranslation": "Traditional Chinese translation of the entire sentence",
"hasGrammarErrors": true/false,
"grammarCorrections": [],
"vocabularyAnalysis": {
"word1": {
"word": "the word",
"translation": "Traditional Chinese translation",
"definition": "English definition",
"partOfSpeech": "noun/verb/adjective/etc",
"pronunciation": "/phonetic/",
"difficultyLevel": "A1/A2/B1/B2/C1/C2",
"frequency": "high/medium/low",
"synonyms": ["synonym1", "synonym2"],
"example": "example sentence",
"exampleTranslation": "Traditional Chinese example translation"
}
},
"idioms": [
{
"idiom": "idiomatic expression",
"translation": "Traditional Chinese meaning",
"definition": "English explanation",
"pronunciation": "/phonetic notation/",
"difficultyLevel": "A1/A2/B1/B2/C1/C2",
"frequency": "high/medium/low",
"synonyms": ["synonym1", "synonym2"],
"example": "usage example",
"exampleTranslation": "Traditional Chinese example"
}
]
}
**Analysis Guidelines:**
1. **Grammar Check**: Detect tense errors, subject-verb agreement, preposition usage, word order
2. **Vocabulary Analysis**: Analyze EVERY SINGLE WORD in the sentence including articles (a, an, the), pronouns (I, you, he, she, it), prepositions (in, on, at), conjunctions (and, but, or), and every other word without exception
3. **CEFR Levels**: Assign accurate A1-C2 levels for each word
4. **Idioms**: Identify any idiomatic expressions or phrasal verbs
5. **Translations**: Use Traditional Chinese (Taiwan standard)
**IMPORTANT**: Return ONLY the JSON object, no additional text or explanation.
```
### **Prompt 設計最佳實踐**
#### **Do's and Don'ts**
```yaml
✅ 應該做的:
明確性:
- 使用具體的範例而非抽象描述
- 明確定義每個欄位的預期格式
- 提供完整的結構範例
穩定性:
- 明確禁止不期望的行為
- 確保所有欄位格式與範例完全一致
- 定期測試和驗證 prompt 效果
❌ 不應該做的:
模糊性:
- 避免使用 "適當的"、"合理的" 等模糊詞彙
- 不要讓 AI 自由決定輸出格式
- 避免複雜的嵌套指令結構
不一致性:
- 避免在不同 prompt 中使用不同的欄位名稱
- 不要改變基礎數據結構定義
```
---
## 🔧 **程式碼組織結構**
### **專案結構範本**
```
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<IAnalysisService, AnalysisService>();
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<IFlashcardService, FlashcardService>();
}
// AI服務註冊
private static void RegisterAIServices(WebApplicationBuilder builder)
{
// ✅ 工廠模式支援多AI提供商
builder.Services.AddSingleton<IAIProviderFactory, AIProviderFactory>();
builder.Services.AddScoped<IGeminiService, GeminiService>();
builder.Services.AddScoped<IPromptBuilder, PromptBuilder>();
// ✅ 配置模式:強型別配置
builder.Services.Configure<GeminiOptions>(
builder.Configuration.GetSection("Gemini"));
// ✅ HttpClient工廠連接池管理
builder.Services.AddHttpClient<IGeminiService, GeminiService>(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<IEnumerable<AnalysisDto>> 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<Analysis> analyses)
{
_context.Analyses.AddRange(analyses); // 批次新增
await _context.SaveChangesAsync(); // 單次提交
}
// ✅ 連接分離:讀寫分離
public async Task<AnalysisStats> 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<AIRequest, AIResponse> _batchProcessor;
private readonly ICircuitBreaker _circuitBreaker;
// ✅ 請求合併減少AI API調用次數
public async Task<AnalysisResult> 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<AIResponse> 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<T?> GetAsync<T>(string key) where T : class
{
var cacheStrategy = DetermineCacheStrategy<T>();
return cacheStrategy switch
{
CacheStrategy.Fast => await GetFromL1Async<T>(key),
CacheStrategy.Distributed => await GetFromL2Async<T>(key),
CacheStrategy.Persistent => await GetFromL3Async<T>(key),
_ => await GetFromMultiLevelAsync<T>(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<GlobalExceptionHandlingMiddleware> _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<AIResponse> CallWithRetryAsync(AIRequest request)
{
var retryPolicy = Policy
.Handle<AIServiceException>()
.Or<HttpRequestException>()
.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<string, object>? additionalData = null)
{
using var scope = logger.BeginScope(new Dictionary<string, object>
{
["Operation"] = operation,
["UserId"] = userId,
["Success"] = success,
["Timestamp"] = DateTime.UtcNow,
["AdditionalData"] = additionalData ?? new Dictionary<string, object>()
});
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<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context, CancellationToken cancellationToken = default)
{
var checks = new List<(string Name, Task<bool> 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<SecurityMiddleware> _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<string, object>
{
["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<bool> ValidateInputSafetyAsync(HttpContext context)
{
// 檢查惡意模式
var suspiciousPatterns = new[]
{
@"<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/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<string> 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<AnalysisAggregate> StartAnalysisAsync(string inputText);
Task CompleteAnalysisAsync(AnalysisId id, AnalysisResult result);
}
```
#### **事件驅動架構**
```csharp
public class EventDrivenAIService
{
private readonly IEventBus _eventBus;
private readonly IAIProvider _aiProvider;
// ✅ 事件驅動:解耦業務流程
public async Task<AnalysisResult> 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<AnalysisCompletedEvent>,
IEventHandler<AnalysisFailedEvent>
{
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<AIServiceOptions>
{
public ValidateOptionsResult Validate(string name, AIServiceOptions options)
{
var failures = new List<string>();
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<AIServiceOptions>(
builder.Configuration.GetSection(AIServiceOptions.SectionName));
builder.Services.AddSingleton<IValidateOptions<AIServiceOptions>, 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<IGeminiService> _mockGeminiService;
private readonly Mock<ICacheService> _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<AnalysisResult>(It.IsAny<string>()))
.ReturnsAsync(cachedResult);
// Act
var result = await _service.AnalyzeAsync(request);
// Assert
Assert.That(result, Is.EqualTo(cachedResult));
_mockGeminiService.Verify(g => g.AnalyzeAsync(It.IsAny<string>()), Times.Never);
}
// ✅ 錯誤情境測試
[Test]
public async Task AnalyzeAsync_WhenAIServiceFails_ThrowsAIServiceException()
{
// Arrange
_mockCacheService.Setup(c => c.GetAsync<AnalysisResult>(It.IsAny<string>()))
.ReturnsAsync((AnalysisResult?)null);
_mockGeminiService.Setup(g => g.AnalyzeAsync(It.IsAny<string>()))
.ThrowsAsync(new HttpRequestException("API unavailable"));
var request = new AnalysisRequest { InputText = "test sentence" };
// Act & Assert
Assert.ThrowsAsync<AIServiceException>(() => _service.AnalyzeAsync(request));
}
}
```
#### **整合測試 (20%)**
```csharp
public class AIControllerIntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly WebApplicationFactory<Program> _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<AnalysisResponse>();
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<AnalysisResponse>();
// 驗證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<string, object>("provider", result.Provider),
new KeyValuePair<string, object>("processing_time", result.ProcessingTime)
});
// 詞彙複雜度分布
_metrics.Histogram("vocabulary.difficulty.distribution",
result.VocabularyAnalysis.Values.Select(v => GetDifficultyScore(v.DifficultyLevel)));
// 用戶參與度
_metrics.Increment("user.engagement", new[]
{
new KeyValuePair<string, object>("feature", "ai_analysis"),
new KeyValuePair<string, object>("session_id", result.SessionId)
});
}
// ✅ 異常指標:快速發現問題
public void RecordErrorMetrics(Exception exception, string operation)
{
_metrics.Increment("errors.total", new[]
{
new KeyValuePair<string, object>("operation", operation),
new KeyValuePair<string, object>("error_type", exception.GetType().Name),
new KeyValuePair<string, object>("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<string, object>("method", method),
new KeyValuePair<string, object>("path", path),
new KeyValuePair<string, object>("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<AnalysisResult> 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<GrammarCheckResult> 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<string, object> Context { get; }
protected DramaLingException(string errorCode, string message,
Dictionary<string, object>? context = null) : base(message)
{
ErrorCode = errorCode;
Context = context ?? new Dictionary<string, object>();
}
}
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<AnalysisResult> 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<string, IAIProvider> _providers;
private readonly IConfiguration _configuration;
// ✅ 版本控制支援AI模型A/B測試
public async Task<AnalysisResult> 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<bool> 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<string, object> CustomSettings { get; set; }
}
public class MultiTenantAIService : IAIAnalysisService
{
private readonly ITenantResolver _tenantResolver;
private readonly IServiceProvider _serviceProvider;
// ✅ 租戶隔離:每個租戶獨立配置
public async Task<AnalysisResult> AnalyzeAsync(AnalysisRequest request)
{
var tenant = await _tenantResolver.GetCurrentTenantAsync();
// 使用租戶特定的AI配置
var aiProvider = _serviceProvider.GetKeyedService<IAIProvider>(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萬用戶)