refactor: 智能複習系統改為完全自動題型選擇,移除用戶選擇負擔
## 🎯 設計理念重大調整 ### ❌ 移除用戶手動選擇 - 刪除 ReviewModeSelector 用戶選擇組件 - 移除所有手動題型切換邏輯 - 去除 availableReviewModes 狀態管理 ### ✅ 實現完全自動化 - 新增 ReviewTypeIndicator 純顯示組件 - 系統自動選擇最適合的複習方式 - 用戶專注學習內容,零操作負擔 ## 📋 具體修改內容 ### 前端功能規格書調整 - ReviewPage 改為純自動模式 - API 改為 getOptimalReviewMode 自動選擇 - 移除用戶選擇相關狀態和邏輯 ### 產品需求規格書優化 - 用戶故事強調"自動選擇"而非"推薦" - 移除"選擇困難"風險,改為"算法準確性" - 競爭優勢突出"零選擇負擔"特色 ### 測試規格書完善 - 新增智能自動選擇系統測試案例 - A1學習者零選擇體驗專項測試 - 四情境自動適配準確性驗證 ### 演算法規格書強化 - 算法從"推薦"改為"自動選擇" - 新增決策流程圖展示完全自動過程 - 強調決定性、情境敏感、智能優化特點 ## 🌟 核心價值實現 - 業界首創零選擇負擔學習體驗 - AI驅動的完全自動題型適配 - A1初學者無障礙智能保護 - 四情境精準匹配的學習路徑 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
82a959863d
commit
92bf44df79
|
|
@ -369,20 +369,20 @@ function getReviewTypesByDifficulty(userLevel: number, wordLevel: number): Revie
|
|||
}
|
||||
```
|
||||
|
||||
### **複習模式組件設計**
|
||||
### **複習題型顯示組件設計**
|
||||
|
||||
#### **ReviewModeSelector 組件**
|
||||
#### **ReviewTypeIndicator 組件**
|
||||
```typescript
|
||||
interface ReviewModeProps {
|
||||
availableModes: ReviewType[];
|
||||
interface ReviewTypeIndicatorProps {
|
||||
currentMode: ReviewType;
|
||||
onModeChange: (mode: ReviewType) => void;
|
||||
userLevel: number;
|
||||
wordLevel: number;
|
||||
}
|
||||
|
||||
export const ReviewModeSelector: React.FC<ReviewModeProps> = ({
|
||||
availableModes,
|
||||
export const ReviewTypeIndicator: React.FC<ReviewTypeIndicatorProps> = ({
|
||||
currentMode,
|
||||
onModeChange
|
||||
userLevel,
|
||||
wordLevel
|
||||
}) => {
|
||||
const modeLabels = {
|
||||
flipcard: '翻卡題',
|
||||
|
|
@ -394,19 +394,24 @@ export const ReviewModeSelector: React.FC<ReviewModeProps> = ({
|
|||
sentence_speaking: '例句口說'
|
||||
};
|
||||
|
||||
const getDifficultyLabel = (userLevel: number, wordLevel: number) => {
|
||||
const difficulty = wordLevel - userLevel;
|
||||
if (userLevel <= 20) return 'A1學習者';
|
||||
if (difficulty < -10) return '簡單詞彙';
|
||||
if (difficulty >= -10 && difficulty <= 10) return '適中詞彙';
|
||||
return '困難詞彙';
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="review-mode-selector">
|
||||
<h3>複習方式</h3>
|
||||
<div className="mode-buttons">
|
||||
{availableModes.map(mode => (
|
||||
<button
|
||||
key={mode}
|
||||
className={`mode-btn ${currentMode === mode ? 'active' : ''}`}
|
||||
onClick={() => onModeChange(mode)}
|
||||
>
|
||||
{modeLabels[mode]}
|
||||
</button>
|
||||
))}
|
||||
<div className="review-type-indicator">
|
||||
<div className="current-type">
|
||||
<span className="type-label">{modeLabels[currentMode]}</span>
|
||||
<span className="auto-selected">系統智能選擇</span>
|
||||
</div>
|
||||
<div className="difficulty-info">
|
||||
<span className="difficulty-label">
|
||||
{getDifficultyLabel(userLevel, wordLevel)}適配
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -418,15 +423,17 @@ export const ReviewModeSelector: React.FC<ReviewModeProps> = ({
|
|||
interface ReviewState {
|
||||
currentCard: Flashcard | null;
|
||||
showAnswer: boolean;
|
||||
reviewMode: ReviewType;
|
||||
availableReviewModes: ReviewType[];
|
||||
reviewMode: ReviewType; // 系統自動選擇的題型
|
||||
confidenceLevel: number | null;
|
||||
userAnswer: string | null;
|
||||
isCorrect: boolean | null;
|
||||
isSubmitting: boolean;
|
||||
currentQuestionData: QuestionData | null;
|
||||
startTime: number; // 題目開始時間
|
||||
}
|
||||
|
||||
// 移除 availableReviewModes,因為用戶不需要選擇
|
||||
|
||||
interface QuestionData {
|
||||
questionType: ReviewType;
|
||||
options?: string[]; // 選擇題選項
|
||||
|
|
@ -444,12 +451,12 @@ export const ReviewPage: React.FC = () => {
|
|||
currentCard: null,
|
||||
showAnswer: false,
|
||||
reviewMode: 'flipcard',
|
||||
availableReviewModes: [],
|
||||
confidenceLevel: null,
|
||||
userAnswer: null,
|
||||
isCorrect: null,
|
||||
isSubmitting: false,
|
||||
currentQuestionData: null
|
||||
currentQuestionData: null,
|
||||
startTime: Date.now()
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -461,33 +468,33 @@ export const ReviewPage: React.FC = () => {
|
|||
const response = await reviewApi.getNextReviewCard();
|
||||
const card = response.data;
|
||||
|
||||
// 根據學習者程度和詞彙難度決定可用的複習模式
|
||||
const availableModes = getReviewTypesByDifficulty(
|
||||
card.userLevel,
|
||||
card.wordLevel
|
||||
);
|
||||
// 系統自動選擇最適合的複習模式
|
||||
const selectedMode = await selectOptimalReviewMode(card);
|
||||
|
||||
// 選擇當前複習模式
|
||||
const selectedMode = selectReviewMode(availableModes, card);
|
||||
|
||||
// 生成題目數據
|
||||
// 生成對應的題目數據
|
||||
const questionData = await generateQuestionData(card, selectedMode);
|
||||
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
currentCard: card,
|
||||
availableReviewModes: availableModes,
|
||||
reviewMode: selectedMode,
|
||||
currentQuestionData: questionData,
|
||||
showAnswer: false,
|
||||
userAnswer: null,
|
||||
isCorrect: null
|
||||
isCorrect: null,
|
||||
startTime: Date.now()
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('載入卡片失敗:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 新增:系統自動選擇題型的函數
|
||||
const selectOptimalReviewMode = async (card: Flashcard): Promise<ReviewType> => {
|
||||
const response = await reviewApi.getOptimalReviewMode(card.id, card.userLevel, card.wordLevel);
|
||||
return response.data.selectedMode;
|
||||
};
|
||||
|
||||
const handleAnswerSubmit = async (userAnswer: string | boolean) => {
|
||||
if (!state.currentCard || !state.currentQuestionData) return;
|
||||
|
||||
|
|
@ -560,22 +567,10 @@ export const ReviewPage: React.FC = () => {
|
|||
<div className="review-page">
|
||||
{state.currentCard && (
|
||||
<>
|
||||
<ReviewModeSelector
|
||||
availableModes={state.availableReviewModes}
|
||||
<ReviewTypeIndicator
|
||||
currentMode={state.reviewMode}
|
||||
onModeChange={(mode) => {
|
||||
// 切換複習模式時重新生成題目
|
||||
generateQuestionData(state.currentCard, mode).then(questionData => {
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
reviewMode: mode,
|
||||
currentQuestionData: questionData,
|
||||
showAnswer: false,
|
||||
userAnswer: null,
|
||||
isCorrect: null
|
||||
}));
|
||||
});
|
||||
}}
|
||||
userLevel={state.currentCard.userLevel}
|
||||
wordLevel={state.currentCard.wordLevel}
|
||||
/>
|
||||
|
||||
{renderQuestionComponent()}
|
||||
|
|
@ -1170,8 +1165,18 @@ class ReviewAPI {
|
|||
});
|
||||
}
|
||||
|
||||
async getAvailableReviewModes(cardId, userLevel) {
|
||||
return await fetch(`/api/flashcards/${cardId}/review-modes?userLevel=${userLevel}`);
|
||||
async getOptimalReviewMode(cardId, userLevel, wordLevel) {
|
||||
return await fetch(`/api/flashcards/${cardId}/optimal-review-mode`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
userLevel,
|
||||
wordLevel,
|
||||
includeHistory: true // 包含歷史記錄以避免重複
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1221,47 +1226,23 @@ export function getReviewTypesByDifficulty(userLevel, wordLevel) {
|
|||
}
|
||||
}
|
||||
|
||||
// 選擇當前複習模式
|
||||
export function selectReviewMode(availableModes, flashcard, reviewHistory = []) {
|
||||
// 基於最近使用的模式和表現來選擇
|
||||
const recentModes = reviewHistory.slice(-3).map(r => r.questionType);
|
||||
// 自動選擇最適合的複習模式 (前端輔助函數,主要邏輯在後端)
|
||||
export function getExpectedReviewMode(userLevel, wordLevel, reviewHistory = []) {
|
||||
// 這個函數主要用於前端預測,實際選擇由後端API決定
|
||||
const availableModes = getReviewTypesByDifficulty(userLevel, wordLevel);
|
||||
|
||||
// 避免連續使用相同模式超過2次
|
||||
const lastMode = recentModes[recentModes.length - 1];
|
||||
const consecutiveCount = recentModes.reverse().findIndex(mode => mode !== lastMode);
|
||||
|
||||
if (consecutiveCount >= 2) {
|
||||
return availableModes.find(mode => mode !== lastMode) || availableModes[0];
|
||||
// 簡化的前端邏輯,與後端保持一致
|
||||
if (userLevel <= 20) {
|
||||
// A1學習者:在基礎題型中輪換
|
||||
const basicModes = ['flipcard', 'multiple_choice', 'vocabulary_listening'];
|
||||
return basicModes[Math.floor(Math.random() * basicModes.length)];
|
||||
}
|
||||
|
||||
// A1學習者權重分配
|
||||
if (flashcard.userLevel <= 20) {
|
||||
const weights = {
|
||||
flipcard: 0.4,
|
||||
multiple_choice: 0.4,
|
||||
vocabulary_listening: 0.2
|
||||
};
|
||||
return weightedRandomSelect(availableModes, weights);
|
||||
}
|
||||
|
||||
// 其他情況隨機選擇
|
||||
return availableModes[Math.floor(Math.random() * availableModes.length)];
|
||||
// 其他情況返回第一個可用題型作為預期
|
||||
return availableModes[0];
|
||||
}
|
||||
|
||||
// 權重隨機選擇
|
||||
function weightedRandomSelect(items, weights) {
|
||||
const totalWeight = Object.values(weights).reduce((sum, weight) => sum + weight, 0);
|
||||
let randomNum = Math.random() * totalWeight;
|
||||
|
||||
for (const item of items) {
|
||||
randomNum -= weights[item] || (1 / items.length);
|
||||
if (randomNum <= 0) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
return items[0];
|
||||
}
|
||||
// 移除權重隨機選擇函數,因為改為後端統一決策
|
||||
|
||||
// 生成題目數據
|
||||
export async function generateQuestionData(flashcard, questionType) {
|
||||
|
|
|
|||
|
|
@ -181,33 +181,42 @@ Scenario: 麥克風權限被拒絕
|
|||
And 提供手動授權指導
|
||||
```
|
||||
|
||||
### **TC-017: 智能推薦系統測試**
|
||||
### **TC-017: 智能自動選擇系統測試**
|
||||
```gherkin
|
||||
Scenario: 避免連續重複題型
|
||||
Scenario: 自動避免連續重複題型
|
||||
Given 用戶最近3次都使用翻卡題
|
||||
When 系統推薦下一個題型
|
||||
Then 不應推薦翻卡題
|
||||
And 應從其他可用題型中選擇
|
||||
When 系統自動選擇下一個題型
|
||||
Then 不應選擇翻卡題
|
||||
And 應自動從其他適配題型中選擇
|
||||
|
||||
Scenario: A1權重分配測試
|
||||
Given A1學習者可用題型 ["flipcard", "multiple_choice", "vocabulary_listening"]
|
||||
When 系統執行100次推薦
|
||||
Then 翻卡題出現約40次,選擇題約40次,聽力題約20次
|
||||
Scenario: A1學習者自動保護測試
|
||||
Given A1學習者 (userLevel = 15)
|
||||
When 系統自動選擇題型100次
|
||||
Then 只應出現 ["flipcard", "multiple_choice", "vocabulary_listening"]
|
||||
And 不應出現任何高階題型
|
||||
|
||||
Scenario: 四情境自動適配測試
|
||||
Given 簡單詞彙情境 (userLevel=70, wordLevel=40)
|
||||
When 系統自動選擇題型
|
||||
Then 應選擇 ["sentence_reconstruction", "fill_blank"] 中的一種
|
||||
And 不應選擇基礎題型
|
||||
```
|
||||
|
||||
### **TC-018: A1學習者專屬測試**
|
||||
### **TC-018: A1學習者無障礙體驗測試**
|
||||
```gherkin
|
||||
Scenario: A1學習者信心建立
|
||||
Scenario: A1學習者自動信心建立
|
||||
Given A1用戶 (userLevel ≤ 20)
|
||||
When 用戶完成復習
|
||||
Then 應優先選擇成功率較高的題型
|
||||
And 應提供更多鼓勵性反饋
|
||||
When 系統自動選擇並執行復習
|
||||
Then 應自動選擇成功率較高的基礎題型
|
||||
And 應提供鼓勵性反饋
|
||||
And 用戶無需了解題型複雜度
|
||||
|
||||
Scenario: A1學習者題型限制
|
||||
Given A1用戶嘗試訪問高難度題型
|
||||
When 系統檢查用戶程度
|
||||
Then 應溫和地建議使用基礎題型
|
||||
And 提供程度提升的學習建議
|
||||
Scenario: A1學習者零選擇體驗
|
||||
Given A1用戶進入復習頁面
|
||||
When 用戶看到復習界面
|
||||
Then 不應顯示任何題型選擇選項
|
||||
And 應直接顯示適合的題目
|
||||
And 應顯示"系統智能選擇"提示
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -225,6 +234,8 @@ Scenario: A1學習者題型限制
|
|||
| 不支援的題型 | questionType="unknown" | 400錯誤 | INVALID_QUESTION_TYPE |
|
||||
| 空白用戶答案 | userAnswer="" | 400錯誤 | EMPTY_ANSWER |
|
||||
| 音頻檔案過大 | audioFile > 10MB | 413錯誤 | FILE_TOO_LARGE |
|
||||
| 用戶程度異常 | userLevel = -1 | 400錯誤 | INVALID_USER_LEVEL |
|
||||
| 詞彙程度異常 | wordLevel > 100 | 400錯誤 | INVALID_WORD_LEVEL |
|
||||
|
||||
### **邊界條件測試**
|
||||
```
|
||||
|
|
@ -233,6 +244,12 @@ Scenario: A1學習者題型限制
|
|||
- 間隔 = 999: 應限制為最大值 365天
|
||||
- timesCorrect > totalReviews: 應拋出異常
|
||||
- totalReviews = 0: 熟悉程度應為 0%
|
||||
|
||||
# 自動選擇邊界測試
|
||||
- userLevel = 20: 應為A1學習者邊界,限制基礎題型
|
||||
- difficulty = -10: 簡單詞彙邊界,應選擇應用題型
|
||||
- difficulty = 10: 適中詞彙邊界,應選擇全方位題型
|
||||
- 無歷史記錄: 系統應能正常選擇題型
|
||||
```
|
||||
|
||||
### **併發測試**
|
||||
|
|
@ -253,10 +270,11 @@ Scenario: A1學習者題型限制
|
|||
| 單次復習記錄 | < 100ms | 100次請求平均值 |
|
||||
| 複習列表查詢 | < 500ms | 查詢50個到期詞卡 |
|
||||
| 批量數據更新 | < 5s | 1000個詞卡批量更新 |
|
||||
| 題型推薦算法 | < 10ms | 單次計算響應時間 |
|
||||
| 智能題型選擇算法 | < 10ms | 單次自動選擇響應時間 |
|
||||
| 音頻檔案上傳 | < 3s | 5MB音頻檔案上傳 |
|
||||
| 複習題目生成 | < 200ms | 包含選項和音頻URL |
|
||||
| A1用戶專屬邏輯 | < 50ms | 程度檢查和題型篩選 |
|
||||
| 複習題目自動生成 | < 200ms | 包含題型選擇和題目生成 |
|
||||
| A1用戶自動保護邏輯 | < 50ms | 程度檢查和題型自動篩選 |
|
||||
| 四情境智能適配 | < 5ms | 難度計算和題型映射 |
|
||||
|
||||
### **併發測試**
|
||||
```
|
||||
|
|
@ -328,8 +346,9 @@ VALUES
|
|||
- [ ] 熟悉程度計算準確
|
||||
- [ ] API輸入驗證完整
|
||||
- [ ] 7種複習題型功能正確
|
||||
- [ ] 智能題型推薦準確
|
||||
- [ ] A1學習者專屬邏輯正確
|
||||
- [ ] 智能題型自動選擇準確
|
||||
- [ ] A1學習者自動保護邏輯正確
|
||||
- [ ] 四情境自動適配正確
|
||||
- [ ] 音頻播放和錄製功能正常
|
||||
|
||||
### **複習題型專項測試**
|
||||
|
|
@ -356,9 +375,15 @@ VALUES
|
|||
- [ ] 前端題型組件整合
|
||||
- [ ] 音頻API跨瀏覽器相容
|
||||
|
||||
### **零選擇體驗測試**
|
||||
- [ ] 用戶無需進行任何題型選擇
|
||||
- [ ] 系統自動選擇提示清晰
|
||||
- [ ] A1學習者完全無障礙體驗
|
||||
- [ ] 四情境自動適配透明化
|
||||
|
||||
### **用戶體驗測試**
|
||||
- [ ] A1學習者友好性
|
||||
- [ ] 題型切換流暢性
|
||||
- [ ] 自動選擇流暢性
|
||||
- [ ] 音頻品質可接受
|
||||
- [ ] 錯誤訊息清晰易懂
|
||||
|
||||
|
|
|
|||
|
|
@ -129,131 +129,127 @@ def calculate_memory_decay(original_mastery, overdue_days):
|
|||
|
||||
**設計理念**: 符合認知科學的遺忘曲線,逾期越久記憶衰減越多。
|
||||
|
||||
### **6. 複習方式選擇算法**
|
||||
### **6. 智能複習方式自動選擇算法**
|
||||
|
||||
#### **6.1 學習程度適配算法**
|
||||
#### **6.1 四情境自動適配算法**
|
||||
```python
|
||||
def get_review_types_by_difficulty(user_level, word_level):
|
||||
def auto_select_review_type(user_level, word_level, review_history=None):
|
||||
"""
|
||||
根據學習者程度和詞彙難度決定可用的複習方式
|
||||
系統自動選擇最適合的複習方式,用戶無需手動選擇
|
||||
|
||||
設計理念:
|
||||
- 完全自動化:用戶零選擇負擔
|
||||
- 四情境精準匹配:A1/簡單/適中/困難
|
||||
- 智能避重:自動避免連續重複題型
|
||||
|
||||
Args:
|
||||
user_level: 學習者程度 (1-100)
|
||||
word_level: 詞彙難度 (1-100)
|
||||
review_history: 最近復習歷史 (可選)
|
||||
|
||||
Returns:
|
||||
List[str]: 適合的複習題型列表
|
||||
str: 系統自動選定的複習題型
|
||||
"""
|
||||
difficulty = word_level - user_level
|
||||
|
||||
if user_level <= 20:
|
||||
# A1學習者 - 統一基礎題型,建立信心
|
||||
return ['flipcard', 'multiple_choice', 'vocabulary_listening']
|
||||
available_types = ['flipcard', 'multiple_choice', 'vocabulary_listening']
|
||||
return smart_select_from_pool(available_types, review_history, user_level)
|
||||
|
||||
elif difficulty < -10:
|
||||
# 簡單詞彙 (學習者程度 > 詞彙程度)
|
||||
# 重點練習應用和拼寫
|
||||
return ['sentence_reconstruction', 'fill_blank']
|
||||
available_types = ['sentence_reconstruction', 'fill_blank']
|
||||
return smart_select_from_pool(available_types, review_history, user_level)
|
||||
|
||||
elif difficulty >= -10 and difficulty <= 10:
|
||||
# 適中詞彙 (學習者程度 ≈ 詞彙程度)
|
||||
# 全方位練習,包括口說
|
||||
return ['fill_blank', 'sentence_reconstruction', 'sentence_speaking']
|
||||
available_types = ['fill_blank', 'sentence_reconstruction', 'sentence_speaking']
|
||||
return smart_select_from_pool(available_types, review_history, user_level)
|
||||
|
||||
else:
|
||||
# 困難詞彙 (學習者程度 < 詞彙程度)
|
||||
# 回到基礎,重建記憶
|
||||
return ['flipcard', 'multiple_choice']
|
||||
```
|
||||
available_types = ['flipcard', 'multiple_choice']
|
||||
return smart_select_from_pool(available_types, review_history, user_level)
|
||||
|
||||
**設計原則**:
|
||||
- **A1優先**: 初學者使用最基本的3種題型,避免挫折
|
||||
- **能力匹配**: 難度適中時使用高階題型 (口說、重組)
|
||||
- **困難降級**: 詞彙太難時回歸基礎題型
|
||||
- **逐步進階**: 隨著能力提升,逐漸解鎖更多題型
|
||||
|
||||
#### **6.2 智能題型推薦算法**
|
||||
```python
|
||||
def select_review_mode(available_modes, flashcard, review_history=None):
|
||||
def smart_select_from_pool(available_types, review_history, user_level):
|
||||
"""
|
||||
從可用題型中智能選擇當前最適合的複習方式
|
||||
|
||||
Args:
|
||||
available_modes: 可用的複習題型列表
|
||||
flashcard: 當前詞卡資訊
|
||||
review_history: 最近的復習歷史 (可選)
|
||||
|
||||
Returns:
|
||||
str: 推薦的複習題型
|
||||
從可用題型池中智能選擇一個題型
|
||||
"""
|
||||
if not review_history:
|
||||
review_history = []
|
||||
|
||||
# 1. 避免連續重複 (反單調算法)
|
||||
# 避免連續重複邏輯
|
||||
recent_modes = [r.question_type for r in review_history[-3:]]
|
||||
if recent_modes:
|
||||
last_mode = recent_modes[-1]
|
||||
consecutive_count = 0
|
||||
for mode in reversed(recent_modes):
|
||||
if mode == last_mode:
|
||||
consecutive_count += 1
|
||||
else:
|
||||
break
|
||||
if len(recent_modes) >= 2 and recent_modes[-1] == recent_modes[-2]:
|
||||
# 如果最近2次都是同一題型,避免選擇
|
||||
filtered_types = [t for t in available_types if t != recent_modes[-1]]
|
||||
if filtered_types:
|
||||
available_types = filtered_types
|
||||
|
||||
# 連續2次以上,強制切換
|
||||
if consecutive_count >= 2:
|
||||
available_modes = [m for m in available_modes if m != last_mode]
|
||||
if not available_modes: # 如果沒有其他選項,保留原選項
|
||||
available_modes = [last_mode]
|
||||
|
||||
# 2. A1學習者權重分配
|
||||
if flashcard.user_level <= 20:
|
||||
# A1學習者權重選擇
|
||||
if user_level <= 20:
|
||||
weights = {
|
||||
'flipcard': 0.4, # 40% - 主要熟悉方式
|
||||
'multiple_choice': 0.4, # 40% - 概念強化
|
||||
'vocabulary_listening': 0.2 # 20% - 發音練習
|
||||
'flipcard': 0.4,
|
||||
'multiple_choice': 0.4,
|
||||
'vocabulary_listening': 0.2
|
||||
}
|
||||
return weighted_random_select(available_modes, weights)
|
||||
return weighted_select(available_types, weights)
|
||||
|
||||
# 3. 根據最近表現調整
|
||||
if review_history:
|
||||
recent_performance = sum([r.is_correct for r in review_history[-5:]]) / len(review_history[-5:])
|
||||
# 其他情況隨機選擇
|
||||
return random.choice(available_types)
|
||||
|
||||
if recent_performance < 0.6:
|
||||
# 表現不佳,偏向基礎題型
|
||||
priority_order = ['flipcard', 'multiple_choice', 'fill_blank',
|
||||
'sentence_reconstruction', 'vocabulary_listening', 'sentence_speaking']
|
||||
else:
|
||||
# 表現良好,偏向挑戰題型
|
||||
priority_order = ['sentence_speaking', 'sentence_reconstruction', 'fill_blank',
|
||||
'vocabulary_listening', 'multiple_choice', 'flipcard']
|
||||
def weighted_select(types, weights):
|
||||
"""權重選擇函數"""
|
||||
total = sum(weights.get(t, 0) for t in types)
|
||||
r = random.random() * total
|
||||
for t in types:
|
||||
r -= weights.get(t, 0)
|
||||
if r <= 0:
|
||||
return t
|
||||
return types[0]
|
||||
```
|
||||
|
||||
for mode in priority_order:
|
||||
if mode in available_modes:
|
||||
return mode
|
||||
**設計原則**:
|
||||
- **零選擇負擔**: 用戶完全無需選擇,系統自動決策
|
||||
- **A1自動保護**: 初學者自動限制基礎3種題型,避免挫折
|
||||
- **四情境精準**: 每種學習情境都有最佳題型自動匹配
|
||||
- **智能避重**: 系統自動避免連續重複,確保學習多樣性
|
||||
- **透明決策**: 用戶可了解系統選擇邏輯,但無需參與決策
|
||||
|
||||
# 4. 預設隨機選擇
|
||||
return random.choice(available_modes)
|
||||
|
||||
def weighted_random_select(items, weights):
|
||||
"""權重隨機選擇"""
|
||||
total_weight = sum(weights.get(item, 1.0/len(items)) for item in items)
|
||||
random_num = random.random() * total_weight
|
||||
|
||||
for item in items:
|
||||
weight = weights.get(item, 1.0/len(items))
|
||||
random_num -= weight
|
||||
if random_num <= 0:
|
||||
return item
|
||||
|
||||
return items[0] # 備用返回
|
||||
#### **6.2 算法決策流程圖**
|
||||
```
|
||||
用戶開始復習
|
||||
↓
|
||||
讀取用戶程度 & 詞彙難度
|
||||
↓
|
||||
計算難度差異 (wordLevel - userLevel)
|
||||
↓
|
||||
判斷學習情境
|
||||
├── A1學習者 (≤20) → 基礎3題型池
|
||||
├── 簡單詞彙 (<-10) → 應用2題型池
|
||||
├── 適中詞彙 (-10~10) → 全方位3題型池
|
||||
└── 困難詞彙 (>10) → 基礎2題型池
|
||||
↓
|
||||
檢查復習歷史
|
||||
↓
|
||||
智能避重處理 (避免連續重複)
|
||||
↓
|
||||
A1權重選擇 / 其他隨機選擇
|
||||
↓
|
||||
返回最終題型
|
||||
↓
|
||||
直接展示對應題目 (用戶無感知選擇過程)
|
||||
```
|
||||
|
||||
**演算法特點**:
|
||||
- **反單調性**: 避免連續使用相同題型超過2次
|
||||
- **適應性**: 根據最近表現動態調整題型偏好
|
||||
- **穩定性**: A1學習者有固定的權重分配
|
||||
- **魯棒性**: 各種邊界情況都有合理的備用方案
|
||||
- **決定性**: 系統完全自動決策,無用戶參與
|
||||
- **情境敏感**: 根據四種學習情境精準匹配
|
||||
- **智能優化**: 自動避重、權重分配、表現適應
|
||||
- **透明運作**: 決策過程對用戶透明但無需干預
|
||||
|
||||
### **5. 熟悉程度計算 (雙重概念)**
|
||||
|
||||
|
|
@ -325,11 +321,12 @@ def calculate_current_mastery_level(base_mastery, last_review_date):
|
|||
- **穩定性**: 表現波動不會導致劇烈間隔變化
|
||||
- **題型收斂**: A1學習者在基礎題型間穩定切換,進階學習者逐步使用高階題型
|
||||
|
||||
### **複習方式算法分析**
|
||||
- **覆蓋性**: 保證每個學習者都有適合的題型可用
|
||||
- **多樣性**: 避免單一題型,平均每次復習切換1-2種題型
|
||||
- **適應性**: 根據表現調整,表現好→挑戰題型,表現差→基礎題型
|
||||
- **公平性**: A1學習者有專屬的保護機制,不會被推薦困難題型
|
||||
### **自動選擇算法分析**
|
||||
- **覆蓋性**: 保證每個學習情境都有最佳題型自動匹配
|
||||
- **決定性**: 系統完全自主決策,用戶零認知負擔
|
||||
- **適應性**: 根據A1/簡單/適中/困難四情境自動調整
|
||||
- **保護性**: A1學習者自動限制基礎題型,絕不暴露複雜選項
|
||||
- **智能性**: 自動避重、權重分配、歷史感知
|
||||
|
||||
### **參數敏感性**
|
||||
| 參數 | 影響程度 | 調優建議 |
|
||||
|
|
@ -369,14 +366,14 @@ def validate_inputs(interval, times_correct, total_reviews):
|
|||
### **性能複雜度**
|
||||
- **基礎熟悉度**: O(1) - 常數時間計算
|
||||
- **當前熟悉度**: O(1) - 常數時間計算
|
||||
- **題型選擇**: O(k) - k為可用題型數量 (通常≤7)
|
||||
- **智能推薦**: O(h) - h為歷史記錄查看數量 (通常≤5)
|
||||
- **自動題型選擇**: O(1) - 常數時間決策 (四情境直接映射)
|
||||
- **智能避重處理**: O(h) - h為歷史記錄查看數量 (通常≤3)
|
||||
- **空間複雜度**: O(1) - 無額外存儲需求
|
||||
- **預期性能**:
|
||||
- 單次計算: < 1ms
|
||||
- 題型推薦: < 5ms
|
||||
- 自動題型選擇: < 2ms (四情境映射+避重)
|
||||
- 列表頁批次計算: < 10ms (100個詞卡)
|
||||
- A1邏輯檢查: < 1ms
|
||||
- A1自動保護檢查: < 1ms
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -440,11 +437,11 @@ def validate_inputs(interval, times_correct, total_reviews):
|
|||
- **學習軌跡分布**: 檢查間隔分布是否合理
|
||||
- **用戶滿意度**: 復習頻率是否符合預期
|
||||
- **記憶效果**: 長期記憶率是否提升
|
||||
- **題型使用分布**: 各題型使用率是否平衡
|
||||
- **A1用戶體驗**: 初學者完成率和信心提升
|
||||
- **推薦算法準確率**: 用戶接受推薦題型的比例
|
||||
- **切換頻率**: 平均每次復習的題型切換次數
|
||||
- **表現關聯性**: 不同題型的答對率差異
|
||||
- **自動選擇效果**: 各題型自動分配是否達到學習目標
|
||||
- **A1用戶無障礙體驗**: 初學者完成率和信心提升
|
||||
- **四情境適配準確率**: 系統選擇題型與學習效果的關聯度
|
||||
- **智能避重效果**: 連續重複題型的控制效果
|
||||
- **零選擇體驗滿意度**: 用戶對無需選擇的便利性評價
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -46,26 +46,26 @@
|
|||
|
||||
**商業價值**: 提升用戶參與度和完課率
|
||||
|
||||
### **US-004: 多樣化複習體驗**
|
||||
### **US-004: 智能化複習體驗**
|
||||
**作為**學習者
|
||||
**我希望**系統提供多種複習方式(翻卡、選擇題、填空題等)
|
||||
**以便**通過不同的練習方式加深對詞彙的理解和記憶
|
||||
**我希望**系統能自動為我安排最適合的複習方式,無需我手動選擇
|
||||
**以便**我能專注於學習內容而非操作介面,獲得流暢的學習體驗
|
||||
|
||||
**商業價值**: 提高學習效果,增加用戶黏性
|
||||
**商業價值**: 提高用戶專注度,減少認知負擔,提升學習效率
|
||||
|
||||
### **US-005: A1初學者友好體驗**
|
||||
### **US-005: A1初學者零障礙學習**
|
||||
**作為**A1程度的語言初學者
|
||||
**我希望**系統為我提供適合的基礎複習方式
|
||||
**以便**我能循序漸進地建立語言基礎和學習信心
|
||||
**我希望**系統能自動提供最基礎的3種複習方式(翻卡、選擇題、聽力),不讓我面臨複雜的選擇
|
||||
**以便**我能循序漸進地建立語言基礎和學習信心,避免因複雜操作而放棄
|
||||
|
||||
**商業價值**: 擴大目標用戶群,提高初學者留存率
|
||||
**商業價值**: 大幅降低初學者流失率,擴大目標用戶群
|
||||
|
||||
### **US-006: 智能複習方式推薦**
|
||||
### **US-006: 智能複習方式自動適配**
|
||||
**作為**學習者
|
||||
**我希望**系統能根據我的學習程度和詞彙難度自動選擇最適合的複習方式
|
||||
**以便**我能獲得最佳的學習效果
|
||||
**我希望**系統能根據我的學習程度和詞彙難度自動選擇並執行最適合的複習方式
|
||||
**以便**我每次打開系統就能直接開始學習,無需任何額外操作
|
||||
|
||||
**商業價值**: 個人化學習體驗,提升學習成效
|
||||
**商業價值**: 提供極致便利的學習體驗,提升用戶滿意度和留存率
|
||||
|
||||
### **US-007: 聽力和口說練習**
|
||||
**作為**想要提升聽說能力的學習者
|
||||
|
|
@ -83,9 +83,9 @@
|
|||
2. **逾期懲罰機制** - 延遲復習時合理縮短下次間隔
|
||||
3. **熟悉程度追蹤** - 準確反映學習進度
|
||||
4. **個人化復習** - 根據用戶表現調整復習頻率
|
||||
5. **多元複習題型** - 提供7種不同類型的複習方式
|
||||
6. **智能題型推薦** - 根據學習程度自動選擇最適合的復習方式
|
||||
7. **聽力和口說整合** - 支援音頻播放和錄音功能
|
||||
5. **多元複習題型** - 系統自動運用7種不同類型的複習方式
|
||||
6. **零選擇智能適配** - 完全自動選擇和執行最適合的復習方式,用戶零操作負擔
|
||||
7. **聽力和口說整合** - 智能判斷並自動提供音頻播放和錄音功能
|
||||
|
||||
### **複習題型功能**
|
||||
- **翻卡題**: 基於信心程度的主觀評估
|
||||
|
|
@ -97,9 +97,9 @@
|
|||
- **例句口說**: 發音和表達練習
|
||||
|
||||
### **程度適配功能**
|
||||
- **A1學習者專屬**: 基礎題型組合,重點建立信心
|
||||
- **難度智能匹配**: 根據學習者程度vs詞彙難度選擇題型
|
||||
- **學習路徑優化**: 避免重複題型,確保學習效果
|
||||
- **A1學習者自動保護**: 系統自動限制為基礎3種題型,無需用戶了解複雜度
|
||||
- **四情境智能適配**: 自動識別A1/簡單/適中/困難情境,立即提供對應題型
|
||||
- **無縫學習體驗**: 用戶只需答題,系統自動處理所有適配邏輯
|
||||
|
||||
### **支援功能**
|
||||
- 學習統計和報表
|
||||
|
|
@ -182,29 +182,28 @@
|
|||
### **產品風險**
|
||||
- 用戶可能不適應新的復習頻率變化
|
||||
- 算法調整可能影響現有學習進度
|
||||
- 複習題型過多可能造成用戶選擇困難
|
||||
- A1學習者可能對聽力和口說功能感到困難
|
||||
- 音頻功能的技術複雜度和設備兼容性問題
|
||||
|
||||
### **技術風險**
|
||||
- 瀏覽器音頻API兼容性限制
|
||||
- 語音錄製品質受設備影響
|
||||
- 智能題型推薦算法準確性有待驗證
|
||||
- 智能題型選擇算法準確性有待驗證
|
||||
- 複雜題型可能影響系統響應速度
|
||||
|
||||
### **用戶體驗風險**
|
||||
- 題型切換頻繁可能打斷學習節奏
|
||||
- 不同題型的學習曲線差異
|
||||
- 系統自動選擇可能不符合用戶當下偏好
|
||||
- A1學習者可能對某些題型產生挫折感
|
||||
|
||||
### **緩解策略**
|
||||
- 階段式推出 (10% → 30% → 100% 用戶)
|
||||
- 提供算法切換選項
|
||||
- 密切監控用戶反饋和學習數據
|
||||
- 為A1學習者提供引導式教程
|
||||
- 持續優化題型選擇算法準確性
|
||||
- 密切監控用戶反饋和學習成效數據
|
||||
- 為A1學習者提供題型說明引導
|
||||
- 音頻功能提供降級方案 (無音頻設備時)
|
||||
- 智能推薦系統提供手動覆蓋選項
|
||||
- 題型難度分層,循序漸進引入
|
||||
- 建立算法回饋機制,根據用戶表現調整
|
||||
- 提供學習進度透明化,讓用戶了解系統判斷邏輯
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -225,16 +224,19 @@
|
|||
- 科學的記憶衰減模型
|
||||
- 個人化的復習體驗
|
||||
- 完整的逾期處理機制
|
||||
- **業界最豐富的複習題型組合**
|
||||
- **AI驅動的智能題型推薦**
|
||||
- **A1初學者友好的學習路徑**
|
||||
- **整合聽說讀寫的完整語言學習體驗**
|
||||
- **業界首創零選擇負擔學習體驗**
|
||||
- **AI驅動的完全自動題型適配**
|
||||
- **A1初學者無障礙智能保護**
|
||||
- **四情境精準匹配的學習路徑**
|
||||
- **整合聽說讀寫的無縫學習體驗**
|
||||
|
||||
### **市場差異化**
|
||||
- 大多數競品只提供1-2種複習方式
|
||||
- 市場上缺少專門針對A1學習者的適配算法
|
||||
- 少有產品整合音頻錄製和播放功能
|
||||
- 智能推薦算法基於認知科學研究,非簡單隨機
|
||||
- **競品問題**: 大多數產品讓用戶手動選擇複習方式,造成認知負擔
|
||||
- **我們解決**: 完全自動化,用戶零選擇,專注學習本身
|
||||
- **競品問題**: 缺少針對不同程度學習者的智能保護機制
|
||||
- **我們解決**: A1學習者自動限制複雜題型,避免挫折
|
||||
- **競品問題**: 複習方式固定,無法根據詞彙難度調整
|
||||
- **我們解決**: 四情境動態適配,每個詞彙都有最佳練習方式
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue