# 例句圖生成功能產品需求規格書 (PRD)

## 1. 功能概述

### 1.1 產品目標
為 DramaLing 詞彙學習平台開發智能例句圖生成功能，通過視覺化例句提升用戶學習效果和記憶保留率。

### 1.2 核心價值主張
- **視覺記憶增強**：圖像結合文字，提高記憶效果 40-60%
- **學習體驗優化**：直觀的視覺內容降低理解門檻
- **個性化學習**：根據用戶程度和偏好生成適配內容
- **成本效益平衡**：智能緩存和批量處理控制 AI 生成成本

### 1.3 目標用戶
- **主要用戶**：英語學習者（A1-C2 所有等級）
- **使用場景**：詞卡複習、新詞學習、例句理解
- **預期效果**：提升學習效率 30%，延長學習時間 25%

## 2. 系統架構設計

### 2.1 雙環境架構策略

#### 2.1.1 開發環境 (Development)
```
本地圖片儲存架構
├── wwwroot/images/examples/     # 靜態圖片存放
├── LocalImageStorageService     # 本地檔案管理
├── 快速測試與迭代             # 零雲端成本
└── 完整功能驗證               # 生產前測試
```

#### 2.1.2 生產環境 (Production)
```
雲端圖片儲存架構
├── AWS S3 / Azure Blob         # 雲端圖片儲存
├── CDN 加速分發               # 全球訪問優化
├── 自動備份與容災             # 數據安全保障
└── 彈性擴展與監控             # 效能管理
```

### 2.2 兩階段 AI 圖片生成架構

#### 2.2.1 第一階段：圖片描述生成 (Gemini AI)
```
詞卡內容 → Gemini API → 優化的圖片描述 prompt
    ↓            ↓              ↓
 詞彙+例句   智能分析      詳細視覺描述
 語境資訊   風格調整      生成參數優化
```

- **Gemini 提示詞生成**：基於現有 GeminiAIProvider 服務
- **語境分析**：結合詞彙難度、學習者程度、例句內容
- **風格優化**：根據 CEFR 等級調整描述複雜度和視覺風格
- **成本效益**：重用現有 Gemini 整合，每次調用約 $0.001

#### 2.2.2 第二階段：圖片生成 (Replicate API)
```
圖片描述 prompt → Replicate API → 高品質例句圖片
       ↓              ↓                ↓
   優化提示詞      圖片生成模型        品質檢測
   參數配置       (FLUX/SD XL)        內容審核
```

- **Replicate 圖片生成**：使用 FLUX 或 Stable Diffusion XL 模型
- **多模型支援**：支援不同風格和品質需求
- **批量處理佇列**：非同步處理大量生成請求
- **品質檢測**：自動過濾不當或低品質內容

#### 2.2.3 圖片生成引擎整合
- **流程編排**：協調兩階段生成流程
- **錯誤處理**：單階段失敗時的重試和降級策略
- **狀態管理**：實時追蹤生成進度和狀態更新
- **成本優化**：智能調度和資源管理

#### 2.2.4 智能快取系統
- **雙階段快取**：Gemini 描述快取 + Replicate 圖片快取
- **語意快取**：類似例句共享相同圖片描述和最終圖片
- **多層快取策略**：記憶體 → 資料庫 → 檔案系統
- **快取失效機制**：基於使用頻率和時間的清理
- **預生成策略**：熱門詞彙預先生成描述和圖片

#### 2.2.5 儲存抽象層
```csharp
public interface IImageStorageService
{
    Task<string> SaveImageAsync(Stream imageStream, string fileName);
    Task<string> GetImageUrlAsync(string imagePath);
    Task<bool> DeleteImageAsync(string imagePath);
    Task<StorageInfo> GetStorageInfoAsync();
}

// 實現類別
- LocalImageStorageService    // 開發環境
- CloudImageStorageService    // 生產環境
- HybridImageStorageService   // 混合策略
```

## 3. 資料庫設計

### 3.1 核心數據表結構

#### 3.1.1 例句圖片表 (example_images)
```sql
CREATE TABLE example_images (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    relative_path VARCHAR(500) NOT NULL,      -- 圖片相對路徑
    alt_text VARCHAR(200),                    -- 圖片描述文字

    -- 兩階段生成相關欄位
    gemini_prompt TEXT,                       -- Gemini 生成描述的原始提示詞
    gemini_description TEXT,                  -- Gemini 生成的圖片描述
    replicate_prompt TEXT,                    -- 最終傳給 Replicate 的優化提示詞
    replicate_model VARCHAR(100),             -- 使用的 Replicate 模型名稱
    replicate_version VARCHAR(100),           -- 模型版本號

    -- 生成成本追蹤
    gemini_cost DECIMAL(10,6),                -- Gemini API 成本
    replicate_cost DECIMAL(10,6),             -- Replicate API 成本
    total_generation_cost DECIMAL(10,6),      -- 總生成成本

    -- 原有欄位
    file_size INTEGER,                        -- 檔案大小 (bytes)
    image_width INTEGER,                      -- 圖片寬度
    image_height INTEGER,                     -- 圖片高度
    content_hash VARCHAR(64) UNIQUE,          -- 內容雜湊值 (防重複)
    quality_score DECIMAL(3,2),               -- 品質評分 (0.00-1.00)
    moderation_status VARCHAR(20) DEFAULT 'pending', -- 審核狀態
    moderation_notes TEXT,                    -- 審核備註
    access_count INTEGER DEFAULT 0,           -- 存取次數統計
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```

#### 3.1.2 詞卡圖片關聯表 (flashcard_example_images)
```sql
CREATE TABLE flashcard_example_images (
    flashcard_id UUID REFERENCES flashcards(id) ON DELETE CASCADE,
    example_image_id UUID REFERENCES example_images(id) ON DELETE CASCADE,
    display_order INTEGER DEFAULT 1,         -- 顯示順序
    is_primary BOOLEAN DEFAULT false,        -- 是否為主要圖片
    context_relevance DECIMAL(3,2),          -- 語境相關度評分
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (flashcard_id, example_image_id)
);
```

#### 3.1.3 圖片生成請求表 (image_generation_requests)
```sql
CREATE TABLE image_generation_requests (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID REFERENCES user_profiles(id) ON DELETE CASCADE,
    flashcard_id UUID REFERENCES flashcards(id) ON DELETE CASCADE,

    -- 兩階段狀態追蹤
    overall_status VARCHAR(20) DEFAULT 'pending', -- 總狀態: pending/description_generating/image_generating/completed/failed
    gemini_status VARCHAR(20) DEFAULT 'pending',  -- Gemini 階段: pending/processing/completed/failed
    replicate_status VARCHAR(20) DEFAULT 'pending', -- Replicate 階段: pending/processing/completed/failed

    -- 請求內容
    original_request TEXT NOT NULL,          -- 原始請求內容 (詞卡資訊)
    gemini_prompt TEXT,                      -- 傳給 Gemini 的提示詞
    generated_description TEXT,              -- Gemini 生成的圖片描述
    final_replicate_prompt TEXT,             -- 最終傳給 Replicate 的提示詞

    -- 結果和錯誤
    generated_image_id UUID REFERENCES example_images(id),
    gemini_error_message TEXT,               -- Gemini 階段錯誤訊息
    replicate_error_message TEXT,            -- Replicate 階段錯誤訊息

    -- 效能追蹤
    gemini_processing_time_ms INTEGER,       -- Gemini 處理時間
    replicate_processing_time_ms INTEGER,    -- Replicate 處理時間
    total_processing_time_ms INTEGER,        -- 總處理時間

    -- 成本追蹤
    gemini_cost DECIMAL(10,6),               -- Gemini API 成本
    replicate_cost DECIMAL(10,6),            -- Replicate API 成本
    total_cost DECIMAL(10,6),                -- 總成本

    -- 時間戳記
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    gemini_started_at TIMESTAMP,
    gemini_completed_at TIMESTAMP,
    replicate_started_at TIMESTAMP,
    replicate_completed_at TIMESTAMP,
    completed_at TIMESTAMP
);
```

### 3.2 索引優化策略
```sql
-- 查詢效能索引
CREATE INDEX idx_example_images_content_hash ON example_images(content_hash);
CREATE INDEX idx_example_images_access_count ON example_images(access_count DESC);
CREATE INDEX idx_flashcard_images_primary ON flashcard_example_images(flashcard_id, is_primary);
CREATE INDEX idx_generation_requests_status ON image_generation_requests(generation_status, created_at);
```

## 4. API 設計規範

### 4.1 核心 API 端點

#### 4.1.1 生成例句圖片 (兩階段流程)
```http
POST /api/flashcards/{flashcardId}/generate-example-image
Authorization: Bearer {token}
Content-Type: application/json

Request Body:
{
  "style": "cartoon|realistic|minimal",     // 圖片風格偏好
  "priority": "normal|high|low",           // 生成優先級
  "dimensions": {                          // 圖片尺寸要求
    "width": 512,
    "height": 512
  },
  "generationOptions": {                   // 生成選項
    "useGeminiCache": true,                // 是否使用 Gemini 描述快取
    "useImageCache": true,                 // 是否使用圖片快取
    "maxRetries": 3                        // 最大重試次數
  },
  "replicateModel": "flux-1-dev|stable-diffusion-xl", // Replicate 模型選擇
  "additionalContext": {                   // 額外語境資訊
    "learnerLevel": "B1",
    "scenario": "business|daily|academic",
    "visualPreferences": ["colorful", "simple", "realistic"]
  }
}

Response:
{
  "success": true,
  "data": {
    "requestId": "uuid",
    "overallStatus": "pending",             // pending/description_generating/image_generating/completed/failed
    "currentStage": "description_generation", // 當前執行階段
    "estimatedTimeMinutes": {
      "gemini": 0.5,                       // Gemini 描述生成預估時間
      "replicate": 2,                      // Replicate 圖片生成預估時間
      "total": 2.5
    },
    "costEstimate": {
      "gemini": 0.001,                     // Gemini 成本預估
      "replicate": 0.05,                   // Replicate 成本預估
      "total": 0.051
    },
    "queuePosition": 3                     // 佇列中的位置
  }
}
```

#### 4.1.2 獲取圖片生成狀態 (兩階段狀態追蹤)
```http
GET /api/image-generation/requests/{requestId}/status
Authorization: Bearer {token}

Response (進行中):
{
  "success": true,
  "data": {
    "requestId": "uuid",
    "overallStatus": "image_generating",
    "stages": {
      "gemini": {
        "status": "completed",
        "startedAt": "2025-09-24T10:28:00Z",
        "completedAt": "2025-09-24T10:28:15Z",
        "processingTimeMs": 15000,
        "cost": 0.0012,
        "generatedDescription": "A professional business meeting scene with diverse people sitting around a modern conference table. One person is gesturing while presenting an idea, with other colleagues listening attentively..."
      },
      "replicate": {
        "status": "processing",
        "startedAt": "2025-09-24T10:28:20Z",
        "model": "flux-1-dev",
        "estimatedCompletionTime": "2025-09-24T10:30:30Z",
        "progress": "65%"
      }
    },
    "totalProcessingTimeMs": 95000,
    "estimatedRemainingTimeMs": 45000
  }
}

Response (完成):
{
  "success": true,
  "data": {
    "requestId": "uuid",
    "overallStatus": "completed",
    "stages": {
      "gemini": {
        "status": "completed",
        "processingTimeMs": 15000,
        "cost": 0.0012,
        "generatedDescription": "A professional business meeting scene..."
      },
      "replicate": {
        "status": "completed",
        "processingTimeMs": 125000,
        "cost": 0.048,
        "model": "flux-1-dev",
        "modelVersion": "dev-v1.2"
      }
    },
    "result": {
      "imageUrl": "https://cdn.dramaling.com/images/examples/uuid.png",
      "imageId": "uuid",
      "qualityScore": 0.95,
      "dimensions": { "width": 512, "height": 512 },
      "fileSize": 245760
    },
    "totalCost": 0.0492,
    "totalProcessingTimeMs": 140000,
    "completedAt": "2025-09-24T10:30:25Z"
  }
}

Response (失敗):
{
  "success": true,
  "data": {
    "requestId": "uuid",
    "overallStatus": "failed",
    "failedStage": "replicate",
    "stages": {
      "gemini": {
        "status": "completed",
        "cost": 0.0012
      },
      "replicate": {
        "status": "failed",
        "error": "Content policy violation: Generated image contains inappropriate content",
        "retryCount": 3
      }
    },
    "totalCost": 0.0012,
    "canRetry": true,
    "suggestedAction": "modify_prompt"
  }
}
```

#### 4.1.3 批量管理例句圖片
```http
GET /api/admin/example-images
Authorization: Bearer {adminToken}
Query Parameters:
- status: pending|approved|rejected
- provider: dalle3|midjourney
- qualityScore: 0.8+ (minimum score)
- dateFrom: 2025-09-01
- dateTo: 2025-09-30
- page: 1
- pageSize: 50

Response:
{
  "success": true,
  "data": {
    "images": [...],
    "pagination": {
      "currentPage": 1,
      "totalPages": 10,
      "totalCount": 487
    },
    "statistics": {
      "pendingCount": 23,
      "approvedCount": 450,
      "rejectedCount": 14,
      "averageQualityScore": 0.89
    }
  }
}
```

## 5. 用戶體驗設計

### 5.1 互動流程設計

#### 5.1.1 主要使用流程
```
1. 用戶在詞卡頁面點擊「生成例句圖」
   ↓
2. 系統檢查現有圖片：
   - 有圖片 → 直接顯示
   - 無圖片 → 進入生成流程
   ↓
3. 圖片生成流程：
   - 顯示生成中動畫 (預估 1-3 分鐘)
   - 提供取消選項
   - 實時更新進度狀態
   ↓
4. 生成完成：
   - 自動刷新顯示新圖片
   - 提供「重新生成」選項
   - 收集用戶滿意度回饋
```

#### 5.1.2 錯誤處理與回退機制
```
生成失敗處理：
├── 網路錯誤 → 自動重試 3 次
├── AI 服務限制 → 提示稍後再試
├── 內容不當 → 顯示預設圖片
└── 積分不足 → 引導購買或升級
```

### 5.2 視覺設計規範

#### 5.2.1 圖片容器設計
```css
.example-image-container {
  width: 100%;
  max-width: 400px;
  aspect-ratio: 4/3;
  border-radius: 12px;
  overflow: hidden;
  border: 2px solid var(--border-color);
  background: linear-gradient(135deg, #f5f5f5, #e8e8e8);
}

.loading-state {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  height: 100%;
  background: var(--loading-bg);
}

.generate-button {
  background: linear-gradient(135deg, #667eea, #764ba2);
  color: white;
  border: none;
  border-radius: 8px;
  padding: 12px 24px;
  cursor: pointer;
  transition: transform 0.2s, box-shadow 0.2s;
}
```

#### 5.2.2 載入動畫設計
- **初始載入**：脈衝效果，顯示 "正在生成圖片..."
- **處理中**：進度條，顯示預估剩餘時間
- **完成**：淡入效果，圖片平滑顯示

## 6. 技術實現細節

### 6.1 兩階段 AI 圖片生成整合

#### 6.1.1 第一階段：Gemini 圖片描述生成服務
```csharp
public class GeminiImageDescriptionService : IGeminiImageDescriptionService
{
    private readonly GeminiAIProvider _geminiProvider;
    private readonly ILogger<GeminiImageDescriptionService> _logger;

    public async Task<ImageDescriptionResult> GenerateDescriptionAsync(Flashcard flashcard, GenerationOptions options)
    {
        var prompt = BuildGeminiPrompt(flashcard, options);

        try
        {
            var geminiResponse = await _geminiProvider.CallGeminiAPIAsync(prompt);
            var description = ExtractImageDescription(geminiResponse);

            return new ImageDescriptionResult
            {
                Success = true,
                Description = description,
                OptimizedPrompt = OptimizeForReplicate(description, options),
                Cost = CalculateGeminiCost(prompt),
                ProcessingTimeMs = stopwatch.ElapsedMilliseconds
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Gemini description generation failed for flashcard {FlashcardId}", flashcard.Id);
            return new ImageDescriptionResult { Success = false, Error = ex.Message };
        }
    }

    private string BuildGeminiPrompt(Flashcard flashcard, GenerationOptions options)
    {
        return $@"# 總覽
你是一位專業插畫設計師兼職英文老師，專門為英語學習教材製作插畫圖卡，用來幫助學生理解英文例句的意思。

# 詞卡資訊
- 詞彙：{flashcard.Word}
- 中文翻譯：{flashcard.Translation}
- 詞性：{flashcard.PartOfSpeech}
- 例句：{flashcard.Example}
- 難度等級：{flashcard.DifficultyLevel}

# SOP
1. 根據上述英文例句，請撰寫一段圖像描述提示詞，用於提供圖片生成AI作為生成圖片的提示詞
2. 請將下方「風格指南」的所有要求加入提示詞中
3. 並於圖片提示詞最後加上：「Absolutely no visible text, characters, letters, numbers, symbols, handwriting, labels, or any form of writing anywhere in the image — including on signs, books, clothing, screens, or backgrounds.」

# 圖片提示詞規範

## 情境清楚
1. 角色描述具體清楚
   - 明確指出圖中有哪些人物，包含性別、年齡、外觀特徵或服裝
   - 如有兩人以上，需說明他們彼此的關係或互動狀態（如：母女、朋友、陌生人等）

2. 動作明確具象
   - 說明主角正在做的動作，須是能被具體畫出來的動作（如：喝咖啡、講電話、跑步）
   - 若動作帶有情緒（如：生氣地講電話、緊張地看著別人），請加入情緒描述以利傳達語意
   - 人物比例正常、表情自然、生動但不誇張

3. 場景明確具體
   - 指出事件發生的地點（如：公園、教室、咖啡廳、城市街道）
   - 可補充時間（如：早上、傍晚）與天氣（如：下雨、晴天），幫助構圖更清楚

4. 物品明確具體
   - 若例句中包含物品（如：書、手機、餐點、雨傘等），必須清楚描述物品的種類、外觀特徵、位置與用途
   - 避免模糊詞（如 ""some stuff""、""a thing""），應具體指出是什麼物品
   - 若物品為主題核心，請描述其使用情境或與人物的互動方式
   - 若出現多個物品，需明確指示其關係與空間位置
   - 所有物品須為日常生活中常見物件，避免使用過於抽象或符號化的圖像

5. 語意需與原句一致
   - 提示詞必須忠實呈現英文句子的核心意思
   - 若英文句含有抽象概念或隱喻，請轉化為對應的具象場景

6. 避免過於抽象或象徵性符號
   - 圖片必須用生活中常見的情境、物體或角色表現，避免使用抽象圖形來傳達語意
   - 圖片中不要出現任何文字

## 風格指南
- 風格類型：扁平插畫（Flat Illustration）
- 線條特徵：無描邊線條（outline-less）
- 色調：暖色調、柔和、低飽和
- 人物樣式：簡化卡通人物，表情自然，不誇張
- 背景構成：圖形簡化（如樹、草地），使用色塊區分層次
- 整體氛圍：溫馨、平靜、適合教育情境
- 技術風格：無紋理、無漸層、無光影寫實感

請根據以上規範，為這個英文例句生成圖片描述提示詞，並確保完全符合風格指南要求。";
    }

    private string OptimizeForReplicate(string description, GenerationOptions options)
    {
        // Gemini 已經包含完整的風格指南，這裡只需要確保符合 Ideogram 模型要求
        var optimizedPrompt = description;

        // 確保包含扁平插畫風格要求
        if (!optimizedPrompt.Contains("flat illustration"))
        {
            optimizedPrompt += ". Style guide: flat illustration style, outline-less shapes, warm and soft color tones, low saturation, cartoon-style characters with natural expressions, simplified background with color blocks, cozy and educational atmosphere, no texture, no gradients, no photorealism, no fantasy elements.";
        }

        // 強制加入禁止文字的規則
        if (!optimizedPrompt.Contains("Absolutely no visible text"))
        {
            optimizedPrompt += " Absolutely no visible text, characters, letters, numbers, symbols, handwriting, labels, or any form of writing anywhere in the image — including on signs, books, clothing, screens, or backgrounds.";
        }

        return optimizedPrompt;
    }
}
```

#### 6.1.2 第二階段：Replicate 圖片生成服務
```csharp
public class ReplicateImageGenerationService : IReplicateImageGenerationService
{
    private readonly HttpClient _httpClient;
    private readonly ReplicateOptions _options;
    private readonly ILogger<ReplicateImageGenerationService> _logger;

    public async Task<ImageGenerationResult> GenerateImageAsync(string prompt, ReplicateModel model, GenerationOptions options)
    {
        var requestPayload = BuildReplicateRequest(prompt, model, options);

        try
        {
            // 啟動 Replicate 預測
            var predictionResponse = await StartPredictionAsync(requestPayload);

            // 輪詢檢查生成狀態
            var result = await WaitForCompletionAsync(predictionResponse.Id, options.TimeoutMinutes);

            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Replicate image generation failed");
            return new ImageGenerationResult { Success = false, Error = ex.Message };
        }
    }

    private object BuildReplicateRequest(string prompt, ReplicateModel model, GenerationOptions options)
    {
        return model.Name switch
        {
            "ideogram-v2a-turbo" => new
            {
                version = "c169dbd9a03b7bd35c3b05aa91e83bc4ad23ee2a4b8f93f2b6cbdda4f466de4a",
                input = new
                {
                    prompt = prompt,
                    width = options.Width ?? 512,
                    height = options.Height ?? 512,
                    magic_prompt_option = "Auto",  // Ideogram 特有參數
                    style_type = "General",       // 適合教育用途的一般風格
                    aspect_ratio = "ASPECT_1_1",  // 1:1 比例適合詞卡
                    model = "V_2_TURBO",          // 使用 Turbo 版本
                    seed = options.Seed ?? Random.Shared.Next()
                }
            },
            "flux-1-dev" => new
            {
                input = new
                {
                    prompt = prompt,
                    width = options.Width ?? 512,
                    height = options.Height ?? 512,
                    num_outputs = 1,
                    guidance_scale = 3.5,
                    num_inference_steps = 28,
                    seed = options.Seed ?? Random.Shared.Next()
                }
            },
            "stable-diffusion-xl" => new
            {
                input = new
                {
                    prompt = prompt,
                    width = options.Width ?? 512,
                    height = options.Height ?? 512,
                    num_outputs = 1,
                    scheduler = "K_EULER_ANCESTRAL",
                    num_inference_steps = 25,
                    guidance_scale = 7.5,
                    seed = options.Seed ?? Random.Shared.Next()
                }
            },
            _ => throw new NotSupportedException($"Model {model.Name} not supported")
        };
    }

    private async Task<ReplicatePrediction> StartPredictionAsync(object requestPayload)
    {
        var json = JsonSerializer.Serialize(requestPayload);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        // 使用 Ideogram V2 Turbo 的 API 端點
        var response = await _httpClient.PostAsync(
            "https://api.replicate.com/v1/models/ideogram-ai/ideogram-v2a-turbo/predictions",
            content);

        response.EnsureSuccessStatusCode();

        var responseJson = await response.Content.ReadAsStringAsync();
        return JsonSerializer.Deserialize<ReplicatePrediction>(responseJson);
    }

    private async Task<ImageGenerationResult> WaitForCompletionAsync(string predictionId, int timeoutMinutes)
    {
        var timeout = TimeSpan.FromMinutes(timeoutMinutes);
        var pollInterval = TimeSpan.FromSeconds(2);
        var startTime = DateTime.UtcNow;

        while (DateTime.UtcNow - startTime < timeout)
        {
            var status = await GetPredictionStatusAsync(predictionId);

            switch (status.Status)
            {
                case "succeeded":
                    return new ImageGenerationResult
                    {
                        Success = true,
                        ImageUrl = status.Output?.FirstOrDefault()?.ToString(),
                        ProcessingTimeMs = (int)(DateTime.UtcNow - startTime).TotalMilliseconds,
                        Cost = CalculateReplicateCost(status.Metrics),
                        ModelVersion = status.Version
                    };

                case "failed":
                    return new ImageGenerationResult
                    {
                        Success = false,
                        Error = status.Error?.ToString() ?? "Generation failed",
                        ProcessingTimeMs = (int)(DateTime.UtcNow - startTime).TotalMilliseconds
                    };

                case "processing":
                    await Task.Delay(pollInterval);
                    break;
            }
        }

        return new ImageGenerationResult
        {
            Success = false,
            Error = "Generation timeout exceeded"
        };
    }
}
```

#### 6.1.3 兩階段流程編排服務
```csharp
public class ImageGenerationOrchestrator : IImageGenerationOrchestrator
{
    private readonly IGeminiImageDescriptionService _geminiService;
    private readonly IReplicateImageGenerationService _replicateService;
    private readonly IImageGenerationRepository _repository;

    public async Task<GenerationRequestResult> StartGenerationAsync(Guid flashcardId, GenerationRequest request)
    {
        var generationRequest = await _repository.CreateRequestAsync(flashcardId, request);

        // 後台執行兩階段生成
        _ = Task.Run(() => ExecuteGenerationPipelineAsync(generationRequest));

        return new GenerationRequestResult
        {
            RequestId = generationRequest.Id,
            Status = "pending",
            EstimatedTimeMinutes = 3
        };
    }

    private async Task ExecuteGenerationPipelineAsync(ImageGenerationRequest request)
    {
        try
        {
            // 第一階段：生成圖片描述
            await _repository.UpdateStatusAsync(request.Id, "description_generating");

            var descriptionResult = await _geminiService.GenerateDescriptionAsync(
                request.Flashcard,
                request.Options
            );

            if (!descriptionResult.Success)
            {
                await _repository.MarkAsFailedAsync(request.Id, "gemini", descriptionResult.Error);
                return;
            }

            await _repository.UpdateGeminiResultAsync(request.Id, descriptionResult);

            // 第二階段：生成圖片
            await _repository.UpdateStatusAsync(request.Id, "image_generating");

            var imageResult = await _replicateService.GenerateImageAsync(
                descriptionResult.OptimizedPrompt,
                request.Options.ReplicateModel,
                request.Options
            );

            if (!imageResult.Success)
            {
                await _repository.MarkAsFailedAsync(request.Id, "replicate", imageResult.Error);
                return;
            }

            // 儲存最終結果
            var savedImage = await SaveGeneratedImageAsync(imageResult);
            await _repository.CompleteRequestAsync(request.Id, savedImage.Id);

        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Generation pipeline failed for request {RequestId}", request.Id);
            await _repository.MarkAsFailedAsync(request.Id, "system", ex.Message);
        }
    }
}
```

#### 6.1.2 品質檢測機制
```csharp
public class ImageQualityValidator
{
    public async Task<QualityResult> ValidateAsync(Stream imageStream)
    {
        var results = await Task.WhenAll(
            CheckImageClarity(imageStream),
            CheckContentAppropriateness(imageStream),
            CheckEducationalRelevance(imageStream)
        );

        return new QualityResult
        {
            OverallScore = results.Average(r => r.Score),
            Issues = results.SelectMany(r => r.Issues).ToList(),
            Approved = results.All(r => r.Score >= 0.7)
        };
    }
}
```

### 6.2 儲存服務實現

#### 6.2.1 環境配置
```json
// appsettings.Development.json
{
  "ImageStorage": {
    "Provider": "Local",
    "Local": {
      "BasePath": "wwwroot/images/examples",
      "BaseUrl": "https://localhost:5001/images/examples",
      "MaxFileSize": 5242880,  // 5MB
      "AllowedFormats": ["png", "jpg", "webp"]
    }
  }
}

// appsettings.Production.json
{
  "ImageStorage": {
    "Provider": "AWS",
    "AWS": {
      "BucketName": "dramaling-example-images",
      "Region": "ap-northeast-1",
      "CDNDomain": "https://cdn.dramaling.com",
      "AccessKeyId": "{from-environment}",
      "SecretAccessKey": "{from-environment}"
    }
  }
}
```

#### 6.2.2 儲存服務實現
```csharp
public class ImageStorageFactory
{
    public static IImageStorageService Create(IConfiguration config, ILogger logger)
    {
        var provider = config["ImageStorage:Provider"];

        return provider.ToLower() switch
        {
            "local" => new LocalImageStorageService(config, logger),
            "aws" => new AwsImageStorageService(config, logger),
            "azure" => new AzureImageStorageService(config, logger),
            _ => throw new NotSupportedException($"Storage provider '{provider}' not supported")
        };
    }
}
```

## 7. 成本控制與優化策略

### 7.1 兩階段成本結構管理

#### 7.1.1 詳細成本分析
```
單次完整生成成本結構：
├── Gemini 描述生成: $0.001 - $0.003
│   ├── 基於輸入 token 數 (~800-1200 tokens，包含詳細規範)
│   ├── 輸出 token 數 (~300-500 tokens，詳細描述)
│   └── Gemini 1.5 Flash 定價
│
└── Replicate 圖片生成: $0.02 - $0.06
    ├── Ideogram V2 Turbo: ~$0.025/張 (主要選擇)
    ├── FLUX-1-dev: ~$0.05/張 (備選)
    ├── Stable Diffusion XL: ~$0.04/張 (備選)
    └── 基於生成時間和運算資源

總成本範圍: $0.021 - $0.063 per 圖片 (使用 Ideogram 約 $0.026)
```

#### 7.1.2 積分系統重新設計
```
積分消耗策略 (基於實際成本):
├── Gemini 階段: 0.1 積分 (約 $0.002)
├── Replicate 階段:
│   ├── Ideogram V2 Turbo: 2.5 積分 (約 $0.025) - 主要選擇
│   ├── FLUX-1-dev: 5 積分 (約 $0.05) - 高品質選項
│   ├── Stable Diffusion XL: 4 積分 (約 $0.04) - 備選
│   └── 失敗不扣 Replicate 積分
│
└── 總成本: 2.6 - 5.1 積分/張圖片 (Ideogram: 2.6 積分)

用戶等級積分分配:
├── 新用戶: 50 積分 (約 19 張 Ideogram 圖片)
├── 基礎用戶: 250 積分/月 (約 96 張 Ideogram 圖片)
├── 進階用戶: 1000 積分/月 (約 385 張 Ideogram 圖片)
└── 企業用戶: 無限制

模型選擇策略:
├── 預設使用 Ideogram V2 Turbo (性價比最佳)
├── 用戶可選擇升級到 FLUX (更高品質)
└── 根據用戶積分餘額智能推薦模型
```

#### 7.1.3 智能成本優化策略

**1. 階段性成本控制**
```csharp
public class CostOptimizationStrategy
{
    public async Task<bool> ShouldProceedToReplicate(DescriptionResult result, UserQuota quota)
    {
        // 檢查用戶剩餘積分是否足夠完成 Replicate 階段
        var replicateCost = CalculateReplicateCost(result.Options);

        if (quota.RemainingCredits < replicateCost)
        {
            // Gemini 階段已完成，保存描述供後續使用
            await SaveDescriptionForLater(result);
            return false;
        }

        return true;
    }
}
```

**2. 語意去重和共享**
- **Gemini 描述快取**：相似詞卡共享描述生成結果
- **最終圖片共享**：完全相同的優化提示詞重用生成結果
- **部分重用策略**：描述相似度 ≥ 85% 時提示用戶選擇重用

**3. 批量和預生成**
- **批量 Gemini 調用**：單次請求處理多個詞卡描述
- **預生成熱門詞彙**：基於學習統計預先生成高頻詞彙
- **離峰生成**：成本較低時段優先處理非急迫請求

### 7.2 兩階段快取策略優化

#### 7.2.1 雙快取架構系統
```
Gemini 描述快取層：
├── L1: 記憶體快取 (30分鐘) → 最近生成的描述
├── L2: Redis 快取 (24小時) → 常用詞彙描述
└── L3: 資料庫快取 (永久) → 所有生成的描述

最終圖片快取層：
├── L1: 記憶體快取 (1小時) → 最熱門圖片 URL
├── L2: Redis 快取 (24小時) → 常用圖片 metadata
├── L3: 資料庫快取 (永久) → 所有已生成圖片記錄
└── L4: CDN/儲存快取 (30天) → 實際圖片檔案
```

#### 7.2.2 智能快取匹配策略
```csharp
public class TwoStageCache
{
    // Gemini 描述快取匹配
    public async Task<string> GetCachedDescriptionAsync(Flashcard flashcard, GenerationOptions options)
    {
        // 1. 完全匹配：相同詞卡+選項
        var exactMatch = await _cache.GetAsync($"desc:{flashcard.Id}:{options.GetHashCode()}");
        if (exactMatch != null) return exactMatch;

        // 2. 語意匹配：相似例句和語境
        var semanticMatches = await FindSemanticMatches(flashcard.Example, 0.85);
        if (semanticMatches.Any())
        {
            return await SelectBestMatch(semanticMatches, options);
        }

        // 3. 基礎匹配：同詞不同例句
        var wordMatches = await _cache.GetAsync($"desc:word:{flashcard.Word}");
        return wordMatches;
    }

    // Replicate 圖片快取匹配
    public async Task<string> GetCachedImageAsync(string optimizedPrompt)
    {
        // 1. 完全匹配：相同的優化提示詞
        var promptHash = ComputeHash(optimizedPrompt);
        var exactImage = await _cache.GetAsync($"img:{promptHash}");
        if (exactImage != null) return exactImage;

        // 2. 相似匹配：相似提示詞 (相似度 ≥ 90%)
        var similarPrompts = await FindSimilarPrompts(optimizedPrompt, 0.9);
        if (similarPrompts.Any())
        {
            return await SelectBestImageMatch(similarPrompts);
        }

        return null;
    }
}
```

#### 7.2.3 預生成和預快取策略
- **熱門詞彙預生成**：基於學習統計，預先完成兩階段生成
- **描述預生成**：新詞彙預先生成 Gemini 描述，圖片按需生成
- **季節性內容**：節慶、時事相關詞彙的描述和圖片提前準備
- **學習路徑預測**：根據用戶學習進度預生成即將學習的詞彙圖片

## 8. 監控與分析指標

### 8.1 關鍵效能指標 (KPIs)

#### 8.1.1 生成效能指標
- **生成成功率**：目標 ≥ 95%
- **平均生成時間**：目標 ≤ 90 秒
- **圖片品質評分**：目標平均 ≥ 0.85
- **用戶滿意度**：目標 ≥ 4.2/5.0

#### 8.1.2 業務影響指標
- **學習效果提升**：使用例句圖 vs 純文字的記憶測試對比
- **用戶參與度**：有圖片詞卡的複習頻率 vs 無圖片詞卡
- **留存率影響**：使用例句圖功能用戶的留存率提升
- **付費轉換**：例句圖功能對付費訂閱的貢獻度

### 8.2 實時監控告警

#### 8.2.1 系統健康監控
```yaml
alerts:
  - name: high_generation_failure_rate
    condition: failure_rate > 0.1  # 10%失敗率告警
    duration: 5m
    action: slack_notification

  - name: slow_generation_time
    condition: avg_generation_time > 120s
    duration: 3m
    action: email_alert

  - name: storage_quota_warning
    condition: storage_usage > 0.85  # 85%容量告警
    action: admin_dashboard_alert
```

## 9. 開發里程碑與排程

### 9.1 Phase 1: 兩階段核心功能開發 (5-7 週)

#### Week 1-2: 兩階段架構基礎
- [ ] 擴展資料庫 schema (支援兩階段追蹤)
- [ ] 實現 `GeminiImageDescriptionService`
- [ ] 開發 `ReplicateImageGenerationService`
- [ ] 建立 `ImageGenerationOrchestrator` 流程編排
- [ ] 儲存抽象層實現 (本地 + 雲端)

#### Week 3-4: API 與後端服務
- [ ] 兩階段生成 API 端點開發
- [ ] 狀態追蹤與進度回報機制
- [ ] 錯誤處理與重試策略
- [ ] Replicate API 整合與輪詢機制
- [ ] 基於現有 Gemini 服務的描述生成

#### Week 5-6: 前端整合與用戶體驗
- [ ] 詞卡頁面新增兩階段生成功能
- [ ] 分階段載入狀態與進度顯示
- [ ] 實時狀態更新 (WebSocket/長輪詢)
- [ ] 兩階段錯誤處理與用戶回饋
- [ ] 響應式設計適配

#### Week 7: 快取與優化
- [ ] 兩階段快取機制實現
- [ ] Gemini 描述語意匹配
- [ ] Replicate 圖片去重機制
- [ ] 批量處理佇列開發
- [ ] 成本控制策略實現

### 9.2 Phase 2: 進階功能與成本優化 (3-4 週)

#### Week 8-9: 智能優化與成本控制
- [ ] 階段性積分扣款系統
- [ ] 智能提示詞優化引擎 (Gemini→Replicate)
- [ ] 相似性檢測與快取共享
- [ ] 預生成策略 (熱門詞彙描述)
- [ ] 圖片品質自動評分

#### Week 10-11: 管理功能與監控
- [ ] 兩階段成本統計與報表
- [ ] 管理後台 (Gemini 描述審核 + 圖片審核)
- [ ] 用戶積分系統整合
- [ ] 分階段監控告警 (Gemini 失敗率、Replicate 超時)
- [ ] 效能分析儀表板

### 9.3 Phase 3: 生產部署與擴展 (2-3 週)

#### Week 12-13: 生產環境部署
- [ ] Replicate API 生產環境配置
- [ ] 雲端儲存服務配置與 CDN
- [ ] 兩階段生成的容錯與降級機制
- [ ] 生產環境部署測試
- [ ] 灰度發布 (先開放描述生成，再開放圖片生成)

#### Week 14: 優化與擴展
- [ ] 用戶回饋收集與兩階段效果分析
- [ ] 成本效益分析與積分系統調優
- [ ] 多模型支援擴展 (更多 Replicate 模型)
- [ ] 文檔完善與團隊培訓

### 9.4 技術風險時程調整

#### 高風險項目緩衝時間
- **Replicate API 整合複雜度**: +1 週
- **兩階段狀態同步機制**: +0.5 週
- **成本控制策略實現**: +0.5 週
- **快取匹配算法優化**: +1 週

#### 總預估時程: **10-14 週**
- **最樂觀**: 10 週 (無重大技術障礙)
- **實際預估**: 12 週 (包含常見問題處理)
- **保守估計**: 14 週 (包含風險緩衝)

## 10. 風險評估與應對策略

### 10.1 技術風險

#### 10.1.1 AI 服務依賴風險
- **風險**：第三方 AI 服務中斷或限制
- **機率**：中等
- **影響**：高
- **應對策略**：
  - 多供應商備援 (DALL-E 3 + Midjourney + Stable Diffusion)
  - 離線 fallback 機制 (預設圖片庫)
  - 服務降級策略 (優雅處理失敗)

#### 10.1.2 儲存成本失控風險
- **風險**：圖片儲存成本超出預算
- **機率**：低
- **影響**：中等
- **應對策略**：
  - 自動清理未使用圖片機制
  - 壓縮與格式優化 (WebP)
  - 儲存層級管理 (熱/溫/冷數據分層)

### 10.2 產品風險

#### 10.2.1 用戶接受度風險
- **風險**：用戶對 AI 生成圖片品質不滿意
- **機率**：中等
- **影響**：中等
- **應對策略**：
  - 提供手動上傳選項
  - 多候選圖片讓用戶選擇
  - 持續的品質改進機制

#### 10.2.2 內容合規風險
- **風險**：AI 生成不當內容
- **機率**：低
- **影響**：高
- **應對策略**：
  - 多層內容過濾機制
  - 人工審核流程
  - 用戶舉報與快速處理機制

## 11. 成功指標與驗證方式

### 11.1 量化成功指標

#### 11.1.1 技術指標
- 圖片生成成功率 ≥ 95%
- 平均生成時間 ≤ 90 秒
- 系統可用性 ≥ 99.5%
- 圖片載入速度 ≤ 2 秒

#### 11.1.2 業務指標
- 用戶對圖片品質滿意度 ≥ 4.2/5.0
- 使用例句圖功能的詞卡複習率提升 ≥ 30%
- 用戶留存率提升 ≥ 15%
- 功能使用率 ≥ 60% (活躍用戶中)

### 11.2 驗證方式

#### 11.2.1 A/B 測試設計
- **測試組 A**：使用例句圖功能的用戶
- **對照組 B**：僅使用文字例句的用戶
- **測試指標**：學習效果、用戶參與度、留存率
- **測試週期**：4-6 週

#### 11.2.2 用戶回饋收集
- 功能滿意度問卷調查
- 用戶訪談與深度回饋
- 應用商店評分變化追蹤
- 客服反饋問題分析

---

## 文檔版本資訊

- **版本**：v1.0
- **創建日期**：2025-09-24
- **最後更新**：2025-09-24
- **負責人**：產品開發團隊
- **審核狀態**：待審核

---

*本文檔將隨著開發進展持續更新，確保與實際實現保持同步。如有疑問或建議，請聯繫產品團隊。*