1610 lines
50 KiB
Markdown
1610 lines
50 KiB
Markdown
# 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<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);
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🔧 **程式碼組織結構**
|
||
|
||
### **專案結構範本**
|
||
|
||
```
|
||
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萬用戶) |