187 lines
4.6 KiB
Markdown
187 lines
4.6 KiB
Markdown
# 智能填空題系統設計規格書
|
||
|
||
## 概述
|
||
|
||
將填空題的挖空邏輯從前端移至後端,建立智能的例句處理系統,支援詞彙變形識別和AI輔助挖空。
|
||
|
||
## 問題分析
|
||
|
||
### 當前問題
|
||
- 前端使用簡單正則 `\b${word}\b` 進行挖空
|
||
- 無法處理詞彙變形:`eat` vs `ate`、`go` vs `went`
|
||
- 挖空失敗時無替代方案,導致題目無法正常顯示
|
||
|
||
### 影響範圍
|
||
- 動詞變形:eat/ate、go/went、run/ran
|
||
- 名詞複數:cat/cats、child/children
|
||
- 形容詞比較級:good/better、bad/worse
|
||
- 過去分詞:break/broken、speak/spoken
|
||
|
||
## 系統架構設計
|
||
|
||
### 資料庫結構調整
|
||
|
||
#### Flashcard 實體新增欄位
|
||
```csharp
|
||
[MaxLength(1000)]
|
||
public string? FilledQuestionText { get; set; } // 挖空後的題目文字
|
||
```
|
||
|
||
#### 範例資料
|
||
```json
|
||
{
|
||
"word": "ate",
|
||
"example": "She ate an apple yesterday.",
|
||
"filledQuestionText": "She ____ an apple yesterday."
|
||
}
|
||
```
|
||
|
||
### 後端服務架構
|
||
|
||
#### 1. BlankGenerationService 服務
|
||
```csharp
|
||
public interface IBlankGenerationService
|
||
{
|
||
Task<string?> GenerateBlankQuestionAsync(string word, string example);
|
||
string? TryProgrammaticBlank(string word, string example);
|
||
Task<string?> GenerateAIBlankAsync(string word, string example);
|
||
}
|
||
```
|
||
|
||
#### 2. 智能挖空處理流程
|
||
|
||
##### Step 1: 程式碼挖空 (快速處理)
|
||
```csharp
|
||
// 1. 完全匹配
|
||
var regex1 = new Regex($@"\b{Regex.Escape(word)}\b", RegexOptions.IgnoreCase);
|
||
|
||
// 2. 常見變形規則
|
||
var variations = GetCommonVariations(word);
|
||
foreach(var variation in variations)
|
||
{
|
||
var regex2 = new Regex($@"\b{Regex.Escape(variation)}\b", RegexOptions.IgnoreCase);
|
||
// 嘗試替換
|
||
}
|
||
```
|
||
|
||
##### Step 2: AI 輔助挖空 (處理複雜變形)
|
||
```csharp
|
||
var prompt = $@"
|
||
請將以下例句中與詞彙「{word}」相關的詞挖空,用____替代:
|
||
|
||
詞彙: {word}
|
||
例句: {example}
|
||
|
||
規則:
|
||
1. 只挖空與目標詞彙相關的詞(包含變形、時態、複數等)
|
||
2. 用____替代被挖空的詞
|
||
3. 保持句子其他部分不變
|
||
4. 直接返回挖空後的句子,不要額外說明
|
||
|
||
挖空後的句子:";
|
||
|
||
await _geminiService.GenerateTextAsync(prompt);
|
||
```
|
||
|
||
#### 3. API 端點調整
|
||
|
||
##### GET /api/flashcards/due 強化邏輯
|
||
```csharp
|
||
foreach(var flashcard in dueCards)
|
||
{
|
||
if(string.IsNullOrEmpty(flashcard.FilledQuestionText))
|
||
{
|
||
var blankQuestion = await _blankGenerationService.GenerateBlankQuestionAsync(
|
||
flashcard.Word, flashcard.Example);
|
||
|
||
if(!string.IsNullOrEmpty(blankQuestion))
|
||
{
|
||
flashcard.FilledQuestionText = blankQuestion;
|
||
// 更新資料庫
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
##### 新增重新生成端點
|
||
```csharp
|
||
[HttpPost("{id}/regenerate-blank")]
|
||
public async Task<ActionResult> RegenerateBlankQuestion(Guid id)
|
||
```
|
||
|
||
### 前端架構簡化
|
||
|
||
#### SentenceFillTest 組件調整
|
||
```typescript
|
||
// 移除複雜的 renderSentenceWithInput() 邏輯
|
||
// 改為簡單的模板渲染
|
||
|
||
interface SentenceFillTestProps {
|
||
word: string;
|
||
definition: string;
|
||
example: string; // 原始例句
|
||
filledQuestionText: string; // 挖空後的題目
|
||
// ... 其他屬性
|
||
}
|
||
|
||
// 簡化的渲染邏輯
|
||
const renderFilledSentence = () => {
|
||
return filledQuestionText.replace('____',
|
||
`<input value="${fillAnswer}" ... />`
|
||
);
|
||
}
|
||
```
|
||
|
||
## 實施計劃
|
||
|
||
### Phase 1: 資料庫結構
|
||
1. 創建 Migration 添加 `FilledQuestionText` 欄位
|
||
2. 更新 Flashcard 實體模型
|
||
|
||
### Phase 2: 後端服務開發
|
||
1. 實作 `BlankGenerationService`
|
||
2. 建立常見詞彙變形對應表
|
||
3. 整合 AI 挖空功能
|
||
4. 修改 FlashcardsController
|
||
|
||
### Phase 3: 前端優化
|
||
1. 簡化 SentenceFillTest 組件
|
||
2. 更新 Props 介面
|
||
3. 測試新的渲染邏輯
|
||
|
||
### Phase 4: 測試與優化
|
||
1. 測試各種詞彙變形情況
|
||
2. 驗證 AI 挖空品質
|
||
3. 效能優化和錯誤處理
|
||
|
||
## 預期效果
|
||
|
||
### 功能提升
|
||
- ✅ 支援所有詞彙變形挖空
|
||
- ✅ AI 輔助處理複雜情況
|
||
- ✅ 一次生成,多次使用
|
||
- ✅ 統一的挖空品質
|
||
|
||
### 技術優勢
|
||
- 🚀 前端邏輯簡化
|
||
- 🎯 後端統一處理
|
||
- 💾 結果快取提升效能
|
||
- 🤖 AI 確保準確性
|
||
|
||
### 維護性
|
||
- 📦 挖空邏輯集中管理
|
||
- 🔧 易於調整和優化
|
||
- 📊 可監控挖空成功率
|
||
- 🐛 錯誤處理更完善
|
||
|
||
## 風險評估
|
||
|
||
### 技術風險
|
||
- AI 調用可能失敗 → 提供降級策略
|
||
- 資料庫遷移 → 確保現有資料相容
|
||
- 效能影響 → 批次處理優化
|
||
|
||
### 緩解措施
|
||
- 多層回退機制 (程式碼 → AI → 手動)
|
||
- 非同步處理避免阻塞
|
||
- 詳細日誌記錄便於除錯 |