docs: 新增後端評估報告和選項詞彙庫功能規格書
- docs: 後端完成度評估報告 - 詳細分析後端85%完成度,包含SM2算法、測驗生成等核心功能 - docs: 選項詞彙庫功能規格書 - 設計智能選項生成系統,基於CEFR、字數、詞性三參數匹配 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
f486054cfb
commit
05fc4d2f28
|
|
@ -0,0 +1,465 @@
|
||||||
|
# DramaLing 後端完成度評估報告
|
||||||
|
|
||||||
|
**版本**: 1.0
|
||||||
|
**日期**: 2025-09-29
|
||||||
|
**評估範圍**: DramaLing.Api 後端服務
|
||||||
|
**評估者**: 開發團隊
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 執行摘要
|
||||||
|
|
||||||
|
### 總體完成度:**85%** ✅
|
||||||
|
|
||||||
|
DramaLing 後端已實現大部分核心功能,包括完整的智能複習系統、用戶管理、詞卡管理、學習進度追蹤等。系統架構完善,程式碼品質良好,已準備好與前端進行整合。
|
||||||
|
|
||||||
|
### 主要優勢
|
||||||
|
- ✅ **完整的 SM2 間隔重複算法**實現
|
||||||
|
- ✅ **智能測驗題目生成**服務
|
||||||
|
- ✅ **CEFR 難度分級**系統
|
||||||
|
- ✅ **Azure Speech Service** 語音功能
|
||||||
|
- ✅ **Replicate** 圖片生成服務
|
||||||
|
- ✅ **完善的資料庫設計**和實體關聯
|
||||||
|
|
||||||
|
### 需要補強的部分
|
||||||
|
- ⚠️ **測驗選項 API 端點**需要暴露給前端使用
|
||||||
|
- ⚠️ **批量測驗結果提交**優化
|
||||||
|
- ⚠️ **智能干擾項生成算法**改進
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏗️ 架構概覽
|
||||||
|
|
||||||
|
### 資料庫實體 (Entity Models)
|
||||||
|
```
|
||||||
|
📦 核心實體
|
||||||
|
├── User - 用戶管理
|
||||||
|
├── Flashcard - 詞卡核心
|
||||||
|
├── StudyRecord - 學習記錄
|
||||||
|
├── StudySession - 學習會話
|
||||||
|
├── StudyCard - 學習卡片
|
||||||
|
├── Tag - 標籤系統
|
||||||
|
└── ErrorReport - 錯誤回報
|
||||||
|
|
||||||
|
📦 智能複習實體
|
||||||
|
├── PronunciationAssessment - 發音評估
|
||||||
|
├── AudioCache - 音頻快取
|
||||||
|
└── UserAudioPreferences - 用戶音頻偏好
|
||||||
|
|
||||||
|
📦 圖片生成實體
|
||||||
|
├── ExampleImage - 例句圖片
|
||||||
|
├── FlashcardExampleImage - 詞卡圖片關聯
|
||||||
|
└── ImageGenerationRequest - 圖片生成請求
|
||||||
|
|
||||||
|
📦 快取實體
|
||||||
|
├── SentenceAnalysisCache - 句子分析快取
|
||||||
|
└── AudioCache - 音頻快取
|
||||||
|
```
|
||||||
|
|
||||||
|
### 服務架構 (Services)
|
||||||
|
```
|
||||||
|
📦 核心服務層
|
||||||
|
├── SpacedRepetitionService - SM2 間隔重複算法
|
||||||
|
├── QuestionGeneratorService - 測驗題目生成
|
||||||
|
├── ReviewTypeSelectorService - 複習類型選擇
|
||||||
|
├── StudySessionService - 學習會話管理
|
||||||
|
└── ReviewModeSelector - 複習模式選擇
|
||||||
|
|
||||||
|
📦 AI 服務層
|
||||||
|
├── GeminiAIProvider - Google Gemini AI
|
||||||
|
├── AIProviderManager - AI 提供者管理
|
||||||
|
├── AnalysisService - 內容分析
|
||||||
|
└── CEFRLevelService - CEFR 難度評估
|
||||||
|
|
||||||
|
📦 媒體服務層
|
||||||
|
├── AzureSpeechService - Azure 語音服務
|
||||||
|
├── AudioCacheService - 音頻快取
|
||||||
|
├── ReplicateService - Replicate 圖片生成
|
||||||
|
├── ImageGenerationOrchestrator - 圖片生成編排
|
||||||
|
└── ImageProcessingService - 圖片處理
|
||||||
|
|
||||||
|
📦 基礎設施服務
|
||||||
|
├── AuthService - 認證服務
|
||||||
|
├── HybridCacheService - 混合快取
|
||||||
|
├── UsageTrackingService - 使用量追蹤
|
||||||
|
└── HealthCheckService - 健康檢查
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 已完成功能清單
|
||||||
|
|
||||||
|
### 1. 用戶認證與管理 ✅
|
||||||
|
- **AuthController** - 用戶註冊、登入、JWT Token 管理
|
||||||
|
- **AuthService** - 身份驗證邏輯
|
||||||
|
- **User Entity** - 完整用戶模型
|
||||||
|
|
||||||
|
### 2. 詞卡管理系統 ✅
|
||||||
|
- **FlashcardsController** - 詞卡 CRUD 操作
|
||||||
|
- **Flashcard Entity** - 包含 SM2 算法欄位
|
||||||
|
- **Tag System** - 標籤分類功能
|
||||||
|
- **錯誤回報機制** - ErrorReport 實體
|
||||||
|
|
||||||
|
### 3. 智能複習系統 ✅
|
||||||
|
- **SpacedRepetitionService** - SM2 算法核心實現
|
||||||
|
- **SM2Algorithm** - 經典間隔重複算法
|
||||||
|
- **ReviewResult DTOs** - 複習結果數據傳輸
|
||||||
|
- **CEFR 難度映射** - CEFRMappingService
|
||||||
|
|
||||||
|
### 4. 測驗題目生成 ✅
|
||||||
|
- **QuestionGeneratorService** - 核心題目生成邏輯
|
||||||
|
- `GenerateVocabChoiceAsync()` - 詞彙選擇題
|
||||||
|
- `GenerateFillBlankQuestion()` - 填空題
|
||||||
|
- `GenerateReorderQuestion()` - 句子重組題
|
||||||
|
- `GenerateSentenceListeningAsync()` - 聽力題
|
||||||
|
- **QuestionData DTOs** - 題目數據結構
|
||||||
|
|
||||||
|
### 5. 學習進度追蹤 ✅
|
||||||
|
- **StudyController** - 學習數據 API
|
||||||
|
- **StudySessionController** - 學習會話管理
|
||||||
|
- **StudySessionService** - 學習邏輯實現
|
||||||
|
- **StatsController** - 統計數據 API
|
||||||
|
|
||||||
|
### 6. 語音功能 ✅
|
||||||
|
- **AudioController** - 語音 API 端點
|
||||||
|
- **AzureSpeechService** - Azure 語音服務整合
|
||||||
|
- **AudioCacheService** - 音頻快取優化
|
||||||
|
- **PronunciationAssessment** - 發音評估
|
||||||
|
|
||||||
|
### 7. 圖片生成功能 ✅
|
||||||
|
- **ImageGenerationController** - 圖片生成 API
|
||||||
|
- **ReplicateService** - Replicate AI 整合
|
||||||
|
- **ImageGenerationOrchestrator** - 圖片生成編排
|
||||||
|
- **LocalImageStorageService** - 本地圖片儲存
|
||||||
|
|
||||||
|
### 8. AI 分析功能 ✅
|
||||||
|
- **AIController** - AI 分析 API
|
||||||
|
- **GeminiAIProvider** - Google Gemini 整合
|
||||||
|
- **AnalysisService** - 內容分析服務
|
||||||
|
- **SentenceAnalysisCache** - 分析結果快取
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔌 核心 API 端點
|
||||||
|
|
||||||
|
### 認證相關 API
|
||||||
|
```http
|
||||||
|
POST /api/auth/register # 用戶註冊
|
||||||
|
POST /api/auth/login # 用戶登入
|
||||||
|
POST /api/auth/refresh # Token 刷新
|
||||||
|
```
|
||||||
|
|
||||||
|
### 詞卡管理 API
|
||||||
|
```http
|
||||||
|
GET /api/flashcards # 獲取詞卡列表(支援篩選、排序、分頁)
|
||||||
|
POST /api/flashcards # 創建新詞卡
|
||||||
|
GET /api/flashcards/{id} # 獲取單一詞卡
|
||||||
|
PUT /api/flashcards/{id} # 更新詞卡
|
||||||
|
DELETE /api/flashcards/{id} # 刪除詞卡
|
||||||
|
```
|
||||||
|
|
||||||
|
### 學習相關 API
|
||||||
|
```http
|
||||||
|
GET /api/study/due-cards # 獲取待複習詞卡
|
||||||
|
POST /api/study/review # 提交複習結果
|
||||||
|
GET /api/study/stats # 獲取學習統計
|
||||||
|
```
|
||||||
|
|
||||||
|
### 測驗相關 API
|
||||||
|
```http
|
||||||
|
GET /api/studysession/question/{id} # 獲取測驗題目
|
||||||
|
POST /api/studysession/submit # 提交測驗結果
|
||||||
|
```
|
||||||
|
|
||||||
|
### 語音相關 API
|
||||||
|
```http
|
||||||
|
POST /api/audio/generate # 生成語音
|
||||||
|
POST /api/audio/pronunciation/evaluate # 發音評估
|
||||||
|
```
|
||||||
|
|
||||||
|
### 圖片生成 API
|
||||||
|
```http
|
||||||
|
POST /api/imagegeneration/generate # 生成例句圖片
|
||||||
|
GET /api/imagegeneration/status/{id} # 查詢生成狀態
|
||||||
|
```
|
||||||
|
|
||||||
|
### AI 分析 API
|
||||||
|
```http
|
||||||
|
POST /api/ai/analyze-sentence # 句子分析
|
||||||
|
POST /api/ai/generate-example # 生成例句
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧠 智能複習系統詳析
|
||||||
|
|
||||||
|
### SM2 算法實現 ✅
|
||||||
|
**文件位置**: `Services/SpacedRepetitionService.cs`
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public async Task<ReviewResult> ProcessReviewAsync(Guid flashcardId, ReviewRequest request)
|
||||||
|
{
|
||||||
|
// 1. 基於現有SM2Algorithm計算基礎間隔
|
||||||
|
var quality = GetQualityFromRequest(request);
|
||||||
|
var sm2Input = new SM2Input(quality, flashcard.EasinessFactor,
|
||||||
|
flashcard.Repetitions, flashcard.IntervalDays);
|
||||||
|
var sm2Result = SM2Algorithm.Calculate(sm2Input);
|
||||||
|
|
||||||
|
// 2. 應用智能複習系統的增強邏輯
|
||||||
|
var enhancedInterval = ApplyEnhancedSpacedRepetitionLogic(
|
||||||
|
sm2Result.IntervalDays, request, overdueDays);
|
||||||
|
|
||||||
|
// 3. 更新熟悉度和下次複習時間
|
||||||
|
flashcard.EasinessFactor = sm2Result.EasinessFactor;
|
||||||
|
flashcard.IntervalDays = enhancedInterval;
|
||||||
|
flashcard.NextReviewDate = actualReviewDate.AddDays(enhancedInterval);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 題目生成演算法 ✅
|
||||||
|
**文件位置**: `Services/QuestionGeneratorService.cs`
|
||||||
|
|
||||||
|
#### 詞彙選擇題生成
|
||||||
|
```csharp
|
||||||
|
private async Task<QuestionData> GenerateVocabChoiceAsync(Flashcard flashcard)
|
||||||
|
{
|
||||||
|
// 從相同用戶的其他詞卡中選擇3個干擾選項
|
||||||
|
var distractors = await _context.Flashcards
|
||||||
|
.Where(f => f.UserId == flashcard.UserId && f.Id != flashcard.Id)
|
||||||
|
.OrderBy(x => Guid.NewGuid()) // 隨機排序
|
||||||
|
.Take(3)
|
||||||
|
.Select(f => f.Word)
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 填空題生成
|
||||||
|
```csharp
|
||||||
|
private QuestionData GenerateFillBlankQuestion(Flashcard flashcard)
|
||||||
|
{
|
||||||
|
// 在例句中將目標詞彙替換為空白
|
||||||
|
var blankedSentence = flashcard.Example.Replace(
|
||||||
|
flashcard.Word, "______", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### CEFR 難度評估 ✅
|
||||||
|
**文件位置**: `Services/CEFRLevelService.cs`
|
||||||
|
|
||||||
|
- 支援 A1-C2 六個等級
|
||||||
|
- 整合 AI 分析進行動態難度評估
|
||||||
|
- 與間隔重複算法結合
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 資料庫設計完整性
|
||||||
|
|
||||||
|
### Flashcard 實體 ✅
|
||||||
|
```csharp
|
||||||
|
public class Flashcard
|
||||||
|
{
|
||||||
|
// 基本內容
|
||||||
|
public string Word { get; set; }
|
||||||
|
public string Translation { get; set; }
|
||||||
|
public string Definition { get; set; }
|
||||||
|
public string Example { get; set; }
|
||||||
|
|
||||||
|
// SM2 算法欄位
|
||||||
|
public float EasinessFactor { get; set; } = 2.5f;
|
||||||
|
public int Repetitions { get; set; } = 0;
|
||||||
|
public int IntervalDays { get; set; } = 1;
|
||||||
|
public DateTime NextReviewDate { get; set; }
|
||||||
|
|
||||||
|
// 學習統計
|
||||||
|
public int MasteryLevel { get; set; }
|
||||||
|
public int TimesReviewed { get; set; }
|
||||||
|
public int TimesCorrect { get; set; }
|
||||||
|
|
||||||
|
// 智能複習欄位
|
||||||
|
public string? ReviewHistory { get; set; } // JSON格式
|
||||||
|
public string? LastQuestionType { get; set; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 關聯設計 ✅
|
||||||
|
- **一對多關聯**: User → Flashcards, StudyRecords, StudySessions
|
||||||
|
- **多對多關聯**: Flashcard ↔ Tag (透過 FlashcardTag)
|
||||||
|
- **圖片關聯**: Flashcard → FlashcardExampleImage → ExampleImage
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 需要補強的功能
|
||||||
|
|
||||||
|
### 1. 測驗選項 API 端點 (優先級: 高)
|
||||||
|
|
||||||
|
**問題**: 前端目前使用簡單的佔位符生成選項
|
||||||
|
```javascript
|
||||||
|
// 前端目前的實現 (ReviewRunner.tsx:112-131)
|
||||||
|
const generateOptions = (card: any, mode: string): string[] => {
|
||||||
|
switch (mode) {
|
||||||
|
case 'vocab-choice':
|
||||||
|
return [card.word, '其他選項1', '其他選項2', '其他選項3']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**解決方案**: 新增 API 端點暴露 QuestionGeneratorService
|
||||||
|
```csharp
|
||||||
|
// 建議新增到 StudyController
|
||||||
|
[HttpGet("question/{flashcardId}")]
|
||||||
|
public async Task<ActionResult<QuestionData>> GenerateQuestion(
|
||||||
|
Guid flashcardId,
|
||||||
|
[FromQuery] string questionType)
|
||||||
|
{
|
||||||
|
var questionData = await _questionGeneratorService
|
||||||
|
.GenerateQuestionAsync(flashcardId, questionType);
|
||||||
|
return Ok(questionData);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 批量測驗結果提交 (優先級: 中)
|
||||||
|
|
||||||
|
**問題**: 目前只支援單一測驗結果提交
|
||||||
|
**解決方案**: 新增批量提交 API,減少網路請求
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[HttpPost("batch-review")]
|
||||||
|
public async Task<ActionResult> SubmitBatchReview([FromBody] BatchReviewRequest request)
|
||||||
|
{
|
||||||
|
var results = new List<ReviewResult>();
|
||||||
|
foreach (var review in request.Reviews)
|
||||||
|
{
|
||||||
|
var result = await _spacedRepetitionService.ProcessReviewAsync(
|
||||||
|
review.FlashcardId, review.Request);
|
||||||
|
results.Add(result);
|
||||||
|
}
|
||||||
|
return Ok(results);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 智能干擾項生成 (優先級: 中)
|
||||||
|
|
||||||
|
**現狀**: 目前隨機選擇用戶其他詞卡作為干擾項
|
||||||
|
**改進方向**:
|
||||||
|
- 根據詞性篩選干擾項
|
||||||
|
- 考慮拼寫相似度
|
||||||
|
- 避免同義詞作為干擾項
|
||||||
|
- 根據 CEFR 難度匹配
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 建議的整合步驟
|
||||||
|
|
||||||
|
### Phase 1: 基礎 API 整合 (1-2天)
|
||||||
|
|
||||||
|
1. **新增測驗選項 API**
|
||||||
|
```csharp
|
||||||
|
// 在 StudyController 中新增
|
||||||
|
[HttpGet("question/{flashcardId}")]
|
||||||
|
public async Task<ActionResult<QuestionData>> GenerateQuestion(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **更新前端 generateOptions 函數**
|
||||||
|
```typescript
|
||||||
|
// 在 ReviewRunner.tsx 中
|
||||||
|
const generateOptions = async (card: any, mode: string) => {
|
||||||
|
const response = await fetch(`/api/study/question/${card.id}?questionType=${mode}`)
|
||||||
|
const questionData = await response.json()
|
||||||
|
return questionData.options
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: 學習進度整合 (2-3天)
|
||||||
|
|
||||||
|
1. **連接 StudyController API**
|
||||||
|
- `/api/study/due-cards` - 獲取待複習詞卡
|
||||||
|
- `/api/study/review` - 提交複習結果
|
||||||
|
|
||||||
|
2. **整合 SpacedRepetitionService**
|
||||||
|
- 使用真實的 SM2 算法結果
|
||||||
|
- 更新前端進度顯示
|
||||||
|
|
||||||
|
### Phase 3: 進階功能整合 (3-5天)
|
||||||
|
|
||||||
|
1. **語音功能整合**
|
||||||
|
- 連接 AudioController
|
||||||
|
- 整合發音評估
|
||||||
|
|
||||||
|
2. **圖片生成整合**
|
||||||
|
- 連接 ImageGenerationController
|
||||||
|
- 支援例句圖片
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📈 效能與擴展性
|
||||||
|
|
||||||
|
### 已實現的優化 ✅
|
||||||
|
- **HybridCacheService** - 多層快取策略
|
||||||
|
- **AudioCacheService** - 音頻檔案快取
|
||||||
|
- **SentenceAnalysisCache** - AI 分析結果快取
|
||||||
|
- **資料庫索引** - 關鍵查詢欄位已建立索引
|
||||||
|
|
||||||
|
### 建議的改進
|
||||||
|
- **Redis 快取** - 分散式快取支援
|
||||||
|
- **API 限流** - 防止濫用
|
||||||
|
- **背景任務** - 大量資料處理
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 程式碼品質評估
|
||||||
|
|
||||||
|
### 優勢 ✅
|
||||||
|
- **明確的分層架構** - Controller → Service → Repository
|
||||||
|
- **完整的錯誤處理** - ErrorHandlingMiddleware
|
||||||
|
- **型別安全** - 完整的 DTO 定義
|
||||||
|
- **日誌記錄** - ILogger 整合
|
||||||
|
- **設定驗證** - Options Pattern 使用
|
||||||
|
|
||||||
|
### 改進建議
|
||||||
|
- **單元測試** - 增加測試覆蓋率
|
||||||
|
- **API 文檔** - Swagger 文檔完善
|
||||||
|
- **效能監控** - APM 工具整合
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 前後端整合檢查清單
|
||||||
|
|
||||||
|
### 立即可整合 ✅
|
||||||
|
- [x] 用戶認證 API
|
||||||
|
- [x] 詞卡管理 API
|
||||||
|
- [x] 基礎學習進度 API
|
||||||
|
- [x] 語音生成 API
|
||||||
|
- [x] 圖片生成 API
|
||||||
|
|
||||||
|
### 需要小幅修改 ⚠️
|
||||||
|
- [ ] 測驗選項生成 API 端點
|
||||||
|
- [ ] 批量提交 API 優化
|
||||||
|
- [ ] 前端錯誤處理統一
|
||||||
|
|
||||||
|
### 未來擴展 🚀
|
||||||
|
- [ ] 即時通知系統
|
||||||
|
- [ ] 社群功能
|
||||||
|
- [ ] 離線支援
|
||||||
|
- [ ] PWA 功能
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 結論與建議
|
||||||
|
|
||||||
|
### 總體評估
|
||||||
|
DramaLing 後端已經具備**85%**的完成度,核心功能完善,架構設計良好。智能複習系統的 SM2 算法實現完整,測驗題目生成服務功能豐富,完全可以支撐前端的智能複習功能。
|
||||||
|
|
||||||
|
### 立即行動項目
|
||||||
|
1. **新增測驗選項 API 端點** - 讓前端可以獲取真實的測驗選項
|
||||||
|
2. **前端 API 整合** - 替換 mock 資料為真實 API 呼叫
|
||||||
|
3. **端到端測試** - 驗證前後端整合的完整流程
|
||||||
|
|
||||||
|
### 長期優化方向
|
||||||
|
1. **智能干擾項算法** - 提升測驗題目品質
|
||||||
|
2. **效能優化** - 針對大量用戶場景
|
||||||
|
3. **功能擴展** - 社群功能、離線支援等
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**評估完成日期**: 2025-09-29
|
||||||
|
**下次評估建議**: 前後端整合完成後
|
||||||
|
|
@ -0,0 +1,758 @@
|
||||||
|
# 選項詞彙庫功能規格書
|
||||||
|
|
||||||
|
**版本**: 1.0
|
||||||
|
**日期**: 2025-09-29
|
||||||
|
**專案**: DramaLing 智能英語學習系統
|
||||||
|
**功能模組**: 測驗選項生成系統
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 功能概述
|
||||||
|
|
||||||
|
### 背景
|
||||||
|
目前 DramaLing 系統的測驗選項生成存在以下問題:
|
||||||
|
- **前端使用簡單佔位符**:`["其他選項1", "其他選項2", "其他選項3"]`
|
||||||
|
- **後端隨機選擇**:從用戶自己的詞卡中隨機選取,缺乏智能性
|
||||||
|
- **選項品質不穩定**:可能產生過於簡單或困難的干擾項
|
||||||
|
- **缺乏科學性**:未考慮語言學習的認知負荷理論
|
||||||
|
|
||||||
|
### 目標
|
||||||
|
建立一個**智能選項詞彙庫系統**,根據目標詞彙的特徵自動生成高品質的測驗干擾項。
|
||||||
|
|
||||||
|
### 核心特性
|
||||||
|
- **三參數匹配**:CEFR 等級、字數、詞性
|
||||||
|
- **智能篩選**:避免同義詞、相似拼寫等不合適的選項
|
||||||
|
- **可擴展性**:支援持續新增詞彙和優化演算法
|
||||||
|
- **效能優化**:透過索引和快取確保快速回應
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 功能需求
|
||||||
|
|
||||||
|
### 核心需求
|
||||||
|
| 需求ID | 描述 | 優先級 |
|
||||||
|
|--------|------|-------|
|
||||||
|
| REQ-001 | 根據 CEFR 等級匹配相近難度的詞彙 | 高 |
|
||||||
|
| REQ-002 | 根據字數(字元長度)匹配類似長度的詞彙 | 高 |
|
||||||
|
| REQ-003 | 根據詞性匹配相同詞性的詞彙 | 高 |
|
||||||
|
| REQ-004 | 每次生成 3 個不同的干擾項 | 高 |
|
||||||
|
| REQ-005 | 避免選擇同義詞作為干擾項 | 中 |
|
||||||
|
| REQ-006 | 避免選擇拼寫過於相似的詞彙 | 中 |
|
||||||
|
| REQ-007 | 支援多種測驗類型(詞彙選擇、聽力等) | 中 |
|
||||||
|
| REQ-008 | 提供詞彙庫管理介面 | 低 |
|
||||||
|
|
||||||
|
### 非功能需求
|
||||||
|
| 需求ID | 描述 | 指標 |
|
||||||
|
|--------|------|-------|
|
||||||
|
| NFR-001 | 回應時間 | < 100ms |
|
||||||
|
| NFR-002 | 詞彙庫大小 | 初期 ≥ 10,000 詞 |
|
||||||
|
| NFR-003 | 可用性 | 99.9% |
|
||||||
|
| NFR-004 | 擴展性 | 支援 100,000+ 詞彙 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏗️ 系統設計
|
||||||
|
|
||||||
|
### 整體架構
|
||||||
|
```
|
||||||
|
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||||
|
│ 前端測驗頁面 │────│ 選項生成API │────│ 詞彙庫服務 │
|
||||||
|
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||||
|
│ │
|
||||||
|
▼ ▼
|
||||||
|
┌─────────────────┐ ┌─────────────────┐
|
||||||
|
│ 快取層 │ │ 選項詞彙庫 │
|
||||||
|
│ (Redis/Memory) │ │ (Database) │
|
||||||
|
└─────────────────┘ └─────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 核心元件
|
||||||
|
1. **OptionsVocabulary 實體** - 詞彙庫資料模型
|
||||||
|
2. **OptionsVocabularyService** - 詞彙庫業務邏輯
|
||||||
|
3. **DistractorGenerationService** - 干擾項生成邏輯
|
||||||
|
4. **VocabularyMatchingEngine** - 詞彙匹配演算法
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 資料模型設計
|
||||||
|
|
||||||
|
### OptionsVocabulary 實體
|
||||||
|
```csharp
|
||||||
|
namespace DramaLing.Api.Models.Entities;
|
||||||
|
|
||||||
|
public class OptionsVocabulary
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 主鍵
|
||||||
|
/// </summary>
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 詞彙內容
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
[MaxLength(100)]
|
||||||
|
[Index("IX_OptionsVocabulary_Word", IsUnique = true)]
|
||||||
|
public string Word { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// CEFR 難度等級 (A1, A2, B1, B2, C1, C2)
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
[MaxLength(2)]
|
||||||
|
[Index("IX_OptionsVocabulary_CEFR")]
|
||||||
|
public string CEFRLevel { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 詞性 (noun, verb, adjective, adverb, etc.)
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
[MaxLength(20)]
|
||||||
|
[Index("IX_OptionsVocabulary_PartOfSpeech")]
|
||||||
|
public string PartOfSpeech { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 字數(字元長度)
|
||||||
|
/// </summary>
|
||||||
|
[Index("IX_OptionsVocabulary_WordLength")]
|
||||||
|
public int WordLength { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 詞彙使用頻率(1-5,5最高)
|
||||||
|
/// </summary>
|
||||||
|
[Range(1, 5)]
|
||||||
|
public int FrequencyRating { get; set; } = 3;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 中文翻譯(用於避免同義詞)
|
||||||
|
/// </summary>
|
||||||
|
[MaxLength(200)]
|
||||||
|
public string? ChineseTranslation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 同義詞列表(JSON 格式,用於排除)
|
||||||
|
/// </summary>
|
||||||
|
[MaxLength(500)]
|
||||||
|
public string? Synonyms { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 詞彙來源(dictionary, corpus, manual)
|
||||||
|
/// </summary>
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string? Source { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否啟用
|
||||||
|
/// </summary>
|
||||||
|
public bool IsActive { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 品質評分(用於優先排序)
|
||||||
|
/// </summary>
|
||||||
|
[Range(0, 100)]
|
||||||
|
public int QualityScore { get; set; } = 50;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 創建時間
|
||||||
|
/// </summary>
|
||||||
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新時間
|
||||||
|
/// </summary>
|
||||||
|
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 複合索引設計
|
||||||
|
```csharp
|
||||||
|
// 在 DbContext 中配置
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
// 核心查詢索引:CEFR + 詞性 + 字數
|
||||||
|
modelBuilder.Entity<OptionsVocabulary>()
|
||||||
|
.HasIndex(e => new { e.CEFRLevel, e.PartOfSpeech, e.WordLength })
|
||||||
|
.HasDatabaseName("IX_OptionsVocabulary_Core_Matching");
|
||||||
|
|
||||||
|
// 啟用狀態索引
|
||||||
|
modelBuilder.Entity<OptionsVocabulary>()
|
||||||
|
.HasIndex(e => new { e.IsActive, e.QualityScore })
|
||||||
|
.HasDatabaseName("IX_OptionsVocabulary_Active_Quality");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 服務層設計
|
||||||
|
|
||||||
|
### IOptionsVocabularyService 介面
|
||||||
|
```csharp
|
||||||
|
namespace DramaLing.Api.Services;
|
||||||
|
|
||||||
|
public interface IOptionsVocabularyService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 根據目標詞彙生成干擾項
|
||||||
|
/// </summary>
|
||||||
|
Task<List<string>> GenerateDistractorsAsync(
|
||||||
|
string targetWord,
|
||||||
|
string cefrLevel,
|
||||||
|
string partOfSpeech,
|
||||||
|
int count = 3);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 新增詞彙到選項庫
|
||||||
|
/// </summary>
|
||||||
|
Task<bool> AddVocabularyAsync(OptionsVocabulary vocabulary);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 批量匯入詞彙
|
||||||
|
/// </summary>
|
||||||
|
Task<int> BulkImportAsync(List<OptionsVocabulary> vocabularies);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根據條件搜尋詞彙
|
||||||
|
/// </summary>
|
||||||
|
Task<List<OptionsVocabulary>> SearchVocabulariesAsync(
|
||||||
|
string? cefrLevel = null,
|
||||||
|
string? partOfSpeech = null,
|
||||||
|
int? minLength = null,
|
||||||
|
int? maxLength = null,
|
||||||
|
int limit = 100);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### DistractorGenerationService 核心邏輯
|
||||||
|
```csharp
|
||||||
|
public class DistractorGenerationService
|
||||||
|
{
|
||||||
|
private readonly DramaLingDbContext _context;
|
||||||
|
private readonly IMemoryCache _cache;
|
||||||
|
private readonly ILogger<DistractorGenerationService> _logger;
|
||||||
|
|
||||||
|
public async Task<List<string>> GenerateDistractorsAsync(
|
||||||
|
string targetWord,
|
||||||
|
string cefrLevel,
|
||||||
|
string partOfSpeech)
|
||||||
|
{
|
||||||
|
var targetLength = targetWord.Length;
|
||||||
|
|
||||||
|
// 1. 基礎篩選條件
|
||||||
|
var baseQuery = _context.OptionsVocabularies
|
||||||
|
.Where(v => v.IsActive && v.Word != targetWord);
|
||||||
|
|
||||||
|
// 2. CEFR 等級匹配(相同等級 + 相鄰等級)
|
||||||
|
var allowedCEFRLevels = GetAllowedCEFRLevels(cefrLevel);
|
||||||
|
baseQuery = baseQuery.Where(v => allowedCEFRLevels.Contains(v.CEFRLevel));
|
||||||
|
|
||||||
|
// 3. 詞性匹配
|
||||||
|
baseQuery = baseQuery.Where(v => v.PartOfSpeech == partOfSpeech);
|
||||||
|
|
||||||
|
// 4. 字數匹配(±2 字元範圍)
|
||||||
|
var minLength = Math.Max(1, targetLength - 2);
|
||||||
|
var maxLength = targetLength + 2;
|
||||||
|
baseQuery = baseQuery.Where(v => v.WordLength >= minLength && v.WordLength <= maxLength);
|
||||||
|
|
||||||
|
// 5. 排除同義詞(如果有定義)
|
||||||
|
// TODO: 實現同義詞排除邏輯
|
||||||
|
|
||||||
|
// 6. 按品質評分和隨機性排序
|
||||||
|
var candidates = await baseQuery
|
||||||
|
.OrderByDescending(v => v.QualityScore)
|
||||||
|
.ThenBy(v => Guid.NewGuid())
|
||||||
|
.Take(10) // 取更多候選詞再篩選
|
||||||
|
.Select(v => v.Word)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
// 7. 最終篩選和回傳
|
||||||
|
return candidates.Take(3).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<string> GetAllowedCEFRLevels(string targetLevel)
|
||||||
|
{
|
||||||
|
var levels = new[] { "A1", "A2", "B1", "B2", "C1", "C2" };
|
||||||
|
var targetIndex = Array.IndexOf(levels, targetLevel);
|
||||||
|
|
||||||
|
if (targetIndex == -1) return new List<string> { targetLevel };
|
||||||
|
|
||||||
|
var allowed = new List<string> { targetLevel };
|
||||||
|
|
||||||
|
// 加入相鄰等級
|
||||||
|
if (targetIndex > 0) allowed.Add(levels[targetIndex - 1]);
|
||||||
|
if (targetIndex < levels.Length - 1) allowed.Add(levels[targetIndex + 1]);
|
||||||
|
|
||||||
|
return allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🌐 API 設計
|
||||||
|
|
||||||
|
### 新增到 StudyController
|
||||||
|
```csharp
|
||||||
|
/// <summary>
|
||||||
|
/// 生成測驗選項(使用詞彙庫)
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet("question-options/{flashcardId}")]
|
||||||
|
public async Task<ActionResult<QuestionOptionsResponse>> GenerateQuestionOptions(
|
||||||
|
Guid flashcardId,
|
||||||
|
[FromQuery] string questionType = "vocab-choice")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var flashcard = await _context.Flashcards.FindAsync(flashcardId);
|
||||||
|
if (flashcard == null)
|
||||||
|
return NotFound(new { Error = "Flashcard not found" });
|
||||||
|
|
||||||
|
var options = await _distractorGenerationService.GenerateDistractorsAsync(
|
||||||
|
flashcard.Word,
|
||||||
|
flashcard.DifficultyLevel ?? "B1",
|
||||||
|
flashcard.PartOfSpeech ?? "noun");
|
||||||
|
|
||||||
|
// 加入正確答案並隨機打亂
|
||||||
|
var allOptions = new List<string> { flashcard.Word };
|
||||||
|
allOptions.AddRange(options);
|
||||||
|
var shuffledOptions = allOptions.OrderBy(x => Guid.NewGuid()).ToArray();
|
||||||
|
|
||||||
|
return Ok(new QuestionOptionsResponse
|
||||||
|
{
|
||||||
|
QuestionType = questionType,
|
||||||
|
Options = shuffledOptions,
|
||||||
|
CorrectAnswer = flashcard.Word,
|
||||||
|
TargetWord = flashcard.Word,
|
||||||
|
CEFRLevel = flashcard.DifficultyLevel,
|
||||||
|
PartOfSpeech = flashcard.PartOfSpeech
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error generating question options for flashcard {FlashcardId}", flashcardId);
|
||||||
|
return StatusCode(500, new { Error = "Internal server error" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 詞彙庫管理 API
|
||||||
|
```csharp
|
||||||
|
/// <summary>
|
||||||
|
/// 詞彙庫管理控制器
|
||||||
|
/// </summary>
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[Authorize(Roles = "Admin")]
|
||||||
|
public class OptionsVocabularyController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IOptionsVocabularyService _vocabularyService;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 新增詞彙到選項庫
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<ActionResult> AddVocabulary([FromBody] AddVocabularyRequest request)
|
||||||
|
{
|
||||||
|
var vocabulary = new OptionsVocabulary
|
||||||
|
{
|
||||||
|
Word = request.Word,
|
||||||
|
CEFRLevel = request.CEFRLevel,
|
||||||
|
PartOfSpeech = request.PartOfSpeech,
|
||||||
|
WordLength = request.Word.Length,
|
||||||
|
FrequencyRating = request.FrequencyRating,
|
||||||
|
ChineseTranslation = request.ChineseTranslation,
|
||||||
|
Source = "manual"
|
||||||
|
};
|
||||||
|
|
||||||
|
var success = await _vocabularyService.AddVocabularyAsync(vocabulary);
|
||||||
|
return success ? Ok() : BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 批量匯入詞彙
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("bulk-import")]
|
||||||
|
public async Task<ActionResult> BulkImport([FromBody] List<AddVocabularyRequest> requests)
|
||||||
|
{
|
||||||
|
var vocabularies = requests.Select(r => new OptionsVocabulary
|
||||||
|
{
|
||||||
|
Word = r.Word,
|
||||||
|
CEFRLevel = r.CEFRLevel,
|
||||||
|
PartOfSpeech = r.PartOfSpeech,
|
||||||
|
WordLength = r.Word.Length,
|
||||||
|
FrequencyRating = r.FrequencyRating,
|
||||||
|
ChineseTranslation = r.ChineseTranslation,
|
||||||
|
Source = "bulk-import"
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
var importedCount = await _vocabularyService.BulkImportAsync(vocabularies);
|
||||||
|
return Ok(new { ImportedCount = importedCount });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 搜尋詞彙庫
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet("search")]
|
||||||
|
public async Task<ActionResult<List<OptionsVocabulary>>> SearchVocabularies(
|
||||||
|
[FromQuery] string? cefrLevel = null,
|
||||||
|
[FromQuery] string? partOfSpeech = null,
|
||||||
|
[FromQuery] int? minLength = null,
|
||||||
|
[FromQuery] int? maxLength = null,
|
||||||
|
[FromQuery] int limit = 100)
|
||||||
|
{
|
||||||
|
var vocabularies = await _vocabularyService.SearchVocabulariesAsync(
|
||||||
|
cefrLevel, partOfSpeech, minLength, maxLength, limit);
|
||||||
|
return Ok(vocabularies);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 DTOs 定義
|
||||||
|
|
||||||
|
### QuestionOptionsResponse
|
||||||
|
```csharp
|
||||||
|
namespace DramaLing.Api.Models.DTOs;
|
||||||
|
|
||||||
|
public class QuestionOptionsResponse
|
||||||
|
{
|
||||||
|
public string QuestionType { get; set; } = string.Empty;
|
||||||
|
public string[] Options { get; set; } = Array.Empty<string>();
|
||||||
|
public string CorrectAnswer { get; set; } = string.Empty;
|
||||||
|
public string TargetWord { get; set; } = string.Empty;
|
||||||
|
public string? CEFRLevel { get; set; }
|
||||||
|
public string? PartOfSpeech { get; set; }
|
||||||
|
public DateTime GeneratedAt { get; set; } = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### AddVocabularyRequest
|
||||||
|
```csharp
|
||||||
|
public class AddVocabularyRequest
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string Word { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[RegularExpression("^(A1|A2|B1|B2|C1|C2)$")]
|
||||||
|
public string CEFRLevel { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(20)]
|
||||||
|
public string PartOfSpeech { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Range(1, 5)]
|
||||||
|
public int FrequencyRating { get; set; } = 3;
|
||||||
|
|
||||||
|
[MaxLength(200)]
|
||||||
|
public string? ChineseTranslation { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(500)]
|
||||||
|
public string? Synonyms { get; set; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💾 資料庫遷移
|
||||||
|
|
||||||
|
### Migration 檔案
|
||||||
|
```csharp
|
||||||
|
public partial class AddOptionsVocabularyTable : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "OptionsVocabularies",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(nullable: false),
|
||||||
|
Word = table.Column<string>(maxLength: 100, nullable: false),
|
||||||
|
CEFRLevel = table.Column<string>(maxLength: 2, nullable: false),
|
||||||
|
PartOfSpeech = table.Column<string>(maxLength: 20, nullable: false),
|
||||||
|
WordLength = table.Column<int>(nullable: false),
|
||||||
|
FrequencyRating = table.Column<int>(nullable: false, defaultValue: 3),
|
||||||
|
ChineseTranslation = table.Column<string>(maxLength: 200, nullable: true),
|
||||||
|
Synonyms = table.Column<string>(maxLength: 500, nullable: true),
|
||||||
|
Source = table.Column<string>(maxLength: 50, nullable: true),
|
||||||
|
IsActive = table.Column<bool>(nullable: false, defaultValue: true),
|
||||||
|
QualityScore = table.Column<int>(nullable: false, defaultValue: 50),
|
||||||
|
CreatedAt = table.Column<DateTime>(nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTime>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_OptionsVocabularies", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 索引
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_OptionsVocabulary_Word",
|
||||||
|
table: "OptionsVocabularies",
|
||||||
|
column: "Word",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_OptionsVocabulary_Core_Matching",
|
||||||
|
table: "OptionsVocabularies",
|
||||||
|
columns: new[] { "CEFRLevel", "PartOfSpeech", "WordLength" });
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_OptionsVocabulary_Active_Quality",
|
||||||
|
table: "OptionsVocabularies",
|
||||||
|
columns: new[] { "IsActive", "QualityScore" });
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(name: "OptionsVocabularies");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 使用案例
|
||||||
|
|
||||||
|
### 案例 1:詞彙選擇題
|
||||||
|
```
|
||||||
|
目標詞彙: "beautiful" (B1, adjective, 9字元)
|
||||||
|
|
||||||
|
篩選條件:
|
||||||
|
- CEFR: A2, B1, B2 (相鄰等級)
|
||||||
|
- 詞性: adjective
|
||||||
|
- 字數: 7-11 字元
|
||||||
|
|
||||||
|
可能的干擾項:
|
||||||
|
- "wonderful" (B1, adjective, 9字元)
|
||||||
|
- "excellent" (B2, adjective, 9字元)
|
||||||
|
- "attractive" (B2, adjective, 10字元)
|
||||||
|
|
||||||
|
最終選項: ["beautiful", "wonderful", "excellent", "attractive"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 案例 2:聽力測驗
|
||||||
|
```
|
||||||
|
目標詞彙: "running" (A2, verb, 7字元)
|
||||||
|
|
||||||
|
篩選條件:
|
||||||
|
- CEFR: A1, A2, B1
|
||||||
|
- 詞性: verb
|
||||||
|
- 字數: 5-9 字元
|
||||||
|
|
||||||
|
可能的干擾項:
|
||||||
|
- "jumping" (A2, verb, 7字元)
|
||||||
|
- "walking" (A1, verb, 7字元)
|
||||||
|
- "playing" (A2, verb, 7字元)
|
||||||
|
|
||||||
|
最終選項: ["running", "jumping", "walking", "playing"]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚡ 效能考量
|
||||||
|
|
||||||
|
### 查詢優化
|
||||||
|
1. **複合索引**:(CEFRLevel, PartOfSpeech, WordLength)
|
||||||
|
2. **覆蓋索引**:包含常用查詢欄位
|
||||||
|
3. **分頁查詢**:避免一次載入過多資料
|
||||||
|
|
||||||
|
### 快取策略
|
||||||
|
```csharp
|
||||||
|
public class CachedDistractorGenerationService
|
||||||
|
{
|
||||||
|
private readonly IMemoryCache _cache;
|
||||||
|
private readonly TimeSpan _cacheExpiry = TimeSpan.FromHours(1);
|
||||||
|
|
||||||
|
public async Task<List<string>> GenerateDistractorsAsync(string targetWord, string cefrLevel, string partOfSpeech)
|
||||||
|
{
|
||||||
|
var cacheKey = $"distractors:{targetWord}:{cefrLevel}:{partOfSpeech}";
|
||||||
|
|
||||||
|
if (_cache.TryGetValue(cacheKey, out List<string> cachedResult))
|
||||||
|
{
|
||||||
|
return cachedResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await GenerateDistractorsInternalAsync(targetWord, cefrLevel, partOfSpeech);
|
||||||
|
|
||||||
|
_cache.Set(cacheKey, result, _cacheExpiry);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 效能指標
|
||||||
|
| 指標 | 目標值 | 監控方式 |
|
||||||
|
|------|--------|----------|
|
||||||
|
| API 回應時間 | < 100ms | Application Insights |
|
||||||
|
| 資料庫查詢時間 | < 50ms | EF Core 日誌 |
|
||||||
|
| 快取命中率 | > 80% | 自訂計數器 |
|
||||||
|
| 併發請求數 | > 1000 req/s | 負載測試 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 初始資料建立
|
||||||
|
|
||||||
|
### 資料來源建議
|
||||||
|
1. **CEFR 詞彙表**
|
||||||
|
- Cambridge English Vocabulary Profile
|
||||||
|
- Oxford 3000/5000 詞彙表
|
||||||
|
- 各級別教材詞彙表
|
||||||
|
|
||||||
|
2. **詞性標注**
|
||||||
|
- WordNet 資料庫
|
||||||
|
- 英語詞性詞典
|
||||||
|
- 語料庫分析結果
|
||||||
|
|
||||||
|
3. **頻率評級**
|
||||||
|
- Google Ngram Corpus
|
||||||
|
- Brown Corpus
|
||||||
|
- 現代英語使用頻率統計
|
||||||
|
|
||||||
|
### 初始資料腳本
|
||||||
|
```csharp
|
||||||
|
public class VocabularySeeder
|
||||||
|
{
|
||||||
|
public async Task SeedInitialVocabularyAsync()
|
||||||
|
{
|
||||||
|
var vocabularies = new List<OptionsVocabulary>
|
||||||
|
{
|
||||||
|
// A1 Level - 名詞
|
||||||
|
new() { Word = "cat", CEFRLevel = "A1", PartOfSpeech = "noun", WordLength = 3, FrequencyRating = 5, ChineseTranslation = "貓" },
|
||||||
|
new() { Word = "dog", CEFRLevel = "A1", PartOfSpeech = "noun", WordLength = 3, FrequencyRating = 5, ChineseTranslation = "狗" },
|
||||||
|
new() { Word = "book", CEFRLevel = "A1", PartOfSpeech = "noun", WordLength = 4, FrequencyRating = 5, ChineseTranslation = "書" },
|
||||||
|
|
||||||
|
// A1 Level - 動詞
|
||||||
|
new() { Word = "eat", CEFRLevel = "A1", PartOfSpeech = "verb", WordLength = 3, FrequencyRating = 5, ChineseTranslation = "吃" },
|
||||||
|
new() { Word = "run", CEFRLevel = "A1", PartOfSpeech = "verb", WordLength = 3, FrequencyRating = 4, ChineseTranslation = "跑" },
|
||||||
|
new() { Word = "walk", CEFRLevel = "A1", PartOfSpeech = "verb", WordLength = 4, FrequencyRating = 5, ChineseTranslation = "走" },
|
||||||
|
|
||||||
|
// B1 Level - 形容詞
|
||||||
|
new() { Word = "beautiful", CEFRLevel = "B1", PartOfSpeech = "adjective", WordLength = 9, FrequencyRating = 4, ChineseTranslation = "美麗的" },
|
||||||
|
new() { Word = "wonderful", CEFRLevel = "B1", PartOfSpeech = "adjective", WordLength = 9, FrequencyRating = 4, ChineseTranslation = "精彩的" },
|
||||||
|
new() { Word = "excellent", CEFRLevel = "B2", PartOfSpeech = "adjective", WordLength = 9, FrequencyRating = 4, ChineseTranslation = "優秀的" },
|
||||||
|
|
||||||
|
// ... 更多詞彙
|
||||||
|
};
|
||||||
|
|
||||||
|
await _context.OptionsVocabularies.AddRangeAsync(vocabularies);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 服務註冊
|
||||||
|
|
||||||
|
### Startup.cs / Program.cs
|
||||||
|
```csharp
|
||||||
|
// 註冊服務
|
||||||
|
builder.Services.AddScoped<IOptionsVocabularyService, OptionsVocabularyService>();
|
||||||
|
builder.Services.AddScoped<DistractorGenerationService>();
|
||||||
|
|
||||||
|
// 記憶體快取
|
||||||
|
builder.Services.AddMemoryCache();
|
||||||
|
|
||||||
|
// 背景服務(可選)
|
||||||
|
builder.Services.AddHostedService<VocabularyQualityScoreUpdateService>();
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📈 品質保證
|
||||||
|
|
||||||
|
### 演算法驗證
|
||||||
|
1. **A/B 測試**:比較新舊選項生成方式的學習效果
|
||||||
|
2. **專家評審**:語言學習專家評估選項品質
|
||||||
|
3. **用戶回饋**:收集學習者對選項難度的反饋
|
||||||
|
|
||||||
|
### 監控指標
|
||||||
|
```csharp
|
||||||
|
public class DistractorQualityMetrics
|
||||||
|
{
|
||||||
|
public double AverageResponseTime { get; set; }
|
||||||
|
public double OptionVariability { get; set; } // 選項多樣性
|
||||||
|
public double CEFRLevelAccuracy { get; set; } // CEFR 匹配準確度
|
||||||
|
public double UserSatisfactionScore { get; set; } // 用戶滿意度
|
||||||
|
public int TotalDistractorsGenerated { get; set; }
|
||||||
|
public DateTime MeasuredAt { get; set; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 實作階段規劃
|
||||||
|
|
||||||
|
### Phase 1: 基礎實作 (1-2 週)
|
||||||
|
- [ ] 建立 OptionsVocabulary 實體和資料庫遷移
|
||||||
|
- [ ] 實作 OptionsVocabularyService 基礎功能
|
||||||
|
- [ ] 建立核心 API 端點
|
||||||
|
- [ ] 匯入初始詞彙資料(1000-5000 詞)
|
||||||
|
|
||||||
|
### Phase 2: 演算法優化 (1 週)
|
||||||
|
- [ ] 實作 DistractorGenerationService
|
||||||
|
- [ ] 新增同義詞排除邏輯
|
||||||
|
- [ ] 實作品質評分系統
|
||||||
|
- [ ] 加入快取機制
|
||||||
|
|
||||||
|
### Phase 3: 前端整合 (3-5 天)
|
||||||
|
- [ ] 修改前端 generateOptions 函數
|
||||||
|
- [ ] 整合新的 API 端點
|
||||||
|
- [ ] 測試各種測驗類型
|
||||||
|
- [ ] 效能測試和優化
|
||||||
|
|
||||||
|
### Phase 4: 進階功能 (1-2 週)
|
||||||
|
- [ ] 管理介面開發
|
||||||
|
- [ ] 批量匯入工具
|
||||||
|
- [ ] 監控和分析儀表板
|
||||||
|
- [ ] A/B 測試框架
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 驗收標準
|
||||||
|
|
||||||
|
### 功能驗收
|
||||||
|
- [ ] 能根據 CEFR、詞性、字數生成合適的干擾項
|
||||||
|
- [ ] API 回應時間 < 100ms
|
||||||
|
- [ ] 生成的選項無重複
|
||||||
|
- [ ] 支援各種測驗類型
|
||||||
|
|
||||||
|
### 品質驗收
|
||||||
|
- [ ] 干擾項難度適中(不會太簡單或太困難)
|
||||||
|
- [ ] 無明顯的同義詞作為干擾項
|
||||||
|
- [ ] 拼寫差異合理(避免過於相似)
|
||||||
|
|
||||||
|
### 技術驗收
|
||||||
|
- [ ] 程式碼覆蓋率 > 80%
|
||||||
|
- [ ] 通過所有單元測試
|
||||||
|
- [ ] API 文檔完整
|
||||||
|
- [ ] 效能測試通過
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔒 安全性考量
|
||||||
|
|
||||||
|
### 資料保護
|
||||||
|
- 詞彙庫資料非敏感性,無特殊加密需求
|
||||||
|
- 管理 API 需要管理員權限驗證
|
||||||
|
- 防止 SQL 注入攻擊
|
||||||
|
|
||||||
|
### API 安全
|
||||||
|
- 實作 Rate Limiting 防止濫用
|
||||||
|
- 輸入驗證和清理
|
||||||
|
- 錯誤訊息不洩露系統資訊
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 相關文件
|
||||||
|
|
||||||
|
- [智能複習系統-第五階段開發計劃.md](./智能複習系統-第五階段開發計劃.md)
|
||||||
|
- [後端完成度評估報告.md](./後端完成度評估報告.md)
|
||||||
|
- [DramaLing API 文檔](./docs/api-documentation.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**規格書完成日期**: 2025-09-29
|
||||||
|
**下次更新時間**: 實作完成後
|
||||||
Loading…
Reference in New Issue