dramaling-vocab-learning/note/VOCABULARY_CACHE_MECHANISM_...

478 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🗄️ 詞彙快取機制技術規格書
**專案**: DramaLing 英語學習平台
**功能**: 詞彙分析快取系統
**文檔版本**: v1.0
**建立日期**: 2025-01-18
**分析範圍**: 前端快取 + 後端 API 快取
---
## 📋 **快取系統概述**
DramaLing 詞彙快取系統包含**三層快取結構**
1. **前端頁面快取** - 當前頁面的詞彙分析資料
2. **後端句子快取** - 24小時的句子分析結果快取
3. **假資料快取** - 開發階段的模擬詞彙資料
---
## 🎯 **Layer 1: 前端頁面快取**
### **📁 實現位置**
**檔案**: `/frontend/app/generate/page.tsx`
**狀態管理**: `const [sentenceAnalysis, setSentenceAnalysis] = useState<any>(null)`
### **🔄 快取行為分析**
#### **初始化** (第84行)
```typescript
setSentenceAnalysis(result.data.wordAnalysis || result.data.WordAnalysis || {})
```
**行為**: **完全覆蓋式更新**
#### **動態擴展** (第405-412行)
```typescript
onNewWordAnalysis={(word, newAnalysis) => {
setSentenceAnalysis((prev: any) => ({
...prev, // 保留現有資料
[word]: newAnalysis // 新增單字分析
}))
}}
```
**行為**: **累積式更新**
### **📊 完整的詞彙資料流程**
#### **場景測試: "The apple" → "The orange"**
```
📍 步驟1: 分析 "The apple"
API 回應: { "apple": {...} }
前端狀態: { "apple": {...} }
結果: "The" = 灰框 (無預存資料)
📍 步驟2: 點擊 "The"
API 調用: POST /api/ai/query-word {"word": "the", ...}
前端狀態: { "apple": {...}, "the": {...} }
結果: "The" = 藍框 (有預存資料)
📍 步驟3: 換新句子 "The orange"
API 回應: { "orange": {...} }
前端狀態: { "orange": {...} } ❌ "the" 被清空!
結果: "The" = 灰框 (又變成無預存資料)
```
### **🚨 當前問題**
| 操作 | 預期行為 | 實際行為 | 問題 |
|------|----------|----------|------|
| 查詢過的詞彙 | 保持快取,下次直接顯示 | 換句子後被清空 | ❌ 覆蓋式更新 |
| 跨句子學習 | 累積詞彙庫,提升效率 | 每次重新開始 | ❌ 浪費 AI 資源 |
| 用戶體驗 | 學過的詞彙有記憶 | 需要重複查詢 | ❌ 體驗差 |
---
## 🎯 **Layer 2: 後端句子快取**
### **📁 實現位置**
**檔案**: `/backend/DramaLing.Api/Controllers/AIController.cs`
**服務**: `IAnalysisCacheService _cacheService`
**資料表**: `SentenceAnalysisCache`
### **💾 快取機制**
#### **存入快取** (第589-602行)
```csharp
await _cacheService.SetCachedAnalysisAsync(
request.InputText, // 快取鍵:句子文本
baseResponseData, // 完整分析結果
TimeSpan.FromHours(24) // TTL: 24小時
);
```
#### **快取檢索** (第533-561行)
```csharp
var cachedAnalysis = await _cacheService.GetCachedAnalysisAsync(request.InputText);
if (cachedAnalysis != null && !request.ForceRefresh) {
// 返回快取結果,標記為 cached: true
}
```
### **📊 快取資料結構**
```sql
CREATE TABLE SentenceAnalysisCache (
Id UNIQUEIDENTIFIER PRIMARY KEY,
InputText NVARCHAR(1000) NOT NULL, -- 原句 (快取鍵)
InputTextHash NVARCHAR(64) NOT NULL, -- 句子雜湊值
AnalysisResult NVARCHAR(MAX) NOT NULL, -- JSON 分析結果
ExpiresAt DATETIME2 NOT NULL, -- 過期時間
CreatedAt DATETIME2 NOT NULL, -- 建立時間
LastAccessedAt DATETIME2, -- 最後存取時間
AccessCount INT NOT NULL DEFAULT 0 -- 存取次數
);
```
### **🔄 快取邏輯流程**
```
用戶輸入: "Hello world"
檢查快取: SELECT * FROM SentenceAnalysisCache WHERE InputTextHash = HASH("Hello world")
如果命中: 返回快取結果 (cached: true, cacheHit: true)
如果錯失: 調用 AI → 存入快取 → 返回結果 (cached: false, usingAI: true)
```
---
## 🎯 **Layer 3: 單字查詢快取 (目前為假資料)**
### **📁 實現位置**
**檔案**: `/backend/DramaLing.Api/Controllers/AIController.cs:1019-1037`
### **⚠️ 當前狀態: 混合實現 (需要進一步確認)**
**根據後端日誌證據,系統確實在調用真實的 Gemini AI**
```
info: Calling Gemini AI for text: Learning is fun and exciting
POST https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=AIza...
```
**但程式碼顯示模擬實現**
```csharp
private async Task<object> AnalyzeLowValueWord(string word, string sentence)
{
// 模擬即時AI分析
await Task.Delay(200); // 模擬延遲
return new {
word = word,
translation = "即時分析的翻譯", // ⚠️ 疑似固定回應
definition = "即時分析的定義",
// ...
};
}
```
### **📊 API 回應速度分析**
| API 端點 | 速度 | 實現狀態 | 證據 |
|----------|------|----------|------|
| `/analyze-sentence` | ~3-5秒 | ✅ 確認真實 AI | 日誌顯示 Gemini 調用 |
| `/query-word` | ~200ms-1s | ❓ **需要確認** | 程式碼顯示模擬,但可能有其他路徑 |
### **🔍 需要進一步調查**
1. `AnalyzeLowValueWord` 是否有其他版本的實現
2. 是否存在條件分支調用真實 AI
3. 固定回應 "即時分析的翻譯" 是否為測試資料
---
## 📋 **詳細的預存機制規格**
### **🔍 您的測試場景分析**
#### **場景**: "The apple" → 點擊 "The" → "The orange"
```
🟦 第1步: 分析 "The apple"
├─ API: POST /analyze-sentence {"inputText": "The apple"}
├─ AI 回應: {"wordAnalysis": {"apple": {...}}} // 不包含 "the"
├─ 前端狀態: sentenceAnalysis = {"apple": {...}}
└─ 視覺: "The"=灰框, "apple"=綠框
🟦 第2步: 點擊 "The"
├─ 觸發: queryWordWithAI("the")
├─ API: POST /query-word {"word": "the", "sentence": "The apple"}
├─ 模擬回應: {"word": "the", "translation": "即時分析的翻譯", ...}
├─ 前端狀態: sentenceAnalysis = {"apple": {...}, "the": {...}}
└─ 視覺: "The"=藍框, "apple"=綠框
🟦 第3步: 分析 "The orange"
├─ API: POST /analyze-sentence {"inputText": "The orange"}
├─ AI 回應: {"wordAnalysis": {"orange": {...}}}
├─ 前端狀態: sentenceAnalysis = {"orange": {...}} ❌ "the" 被覆蓋清空!
└─ 視覺: "The"=灰框, "orange"=綠框
🟦 第4步: 再次點擊 "The"
├─ 發現: sentenceAnalysis["the"] = undefined
├─ 觸發: queryWordWithAI("the") again ❌ 重複查詢!
└─ 結果: 浪費 AI 資源,用戶體驗差
```
### **📊 當前快取機制的優缺點**
#### ✅ **優點**
1. **句子級快取**: 相同句子 24 小時內不重複分析
2. **動態擴展**: 點擊的詞彙會加入當前分析
3. **記憶體效率**: 不會無限累積資料
#### ❌ **缺點**
1. **跨句子遺失**: 換句子後之前查詢的詞彙被清空
2. **重複查詢**: 相同詞彙在不同句子中需要重複查詢
3. **假資料問題**: query-word 目前不是真實 AI 查詢
---
## 🛠️ **改善方案規格**
### **方案1: 全域詞彙快取 (推薦)**
#### **前端實現**
```typescript
// 新增全域詞彙快取
const [globalWordCache, setGlobalWordCache] = useState<Record<string, any>>({})
// 修改句子分析更新邏輯
setSentenceAnalysis(prev => ({
...globalWordCache, // 保留全域快取
...prev, // 保留當前分析
...result.data.wordAnalysis // 新增句子分析
}))
// 修改詞彙查詢邏輯
onNewWordAnalysis={(word, newAnalysis) => {
// 同時更新兩個快取
setGlobalWordCache(prev => ({ ...prev, [word]: newAnalysis }))
setSentenceAnalysis(prev => ({ ...prev, [word]: newAnalysis }))
}}
```
#### **本地存儲持久化**
```typescript
// 保存到 localStorage
useEffect(() => {
const cached = localStorage.getItem('dramalingWordCache')
if (cached) {
setGlobalWordCache(JSON.parse(cached))
}
}, [])
useEffect(() => {
localStorage.setItem('dramalingWordCache', JSON.stringify(globalWordCache))
}, [globalWordCache])
```
### **方案2: 真實 AI 查詢實現**
#### **後端修改**
```csharp
private async Task<object> AnalyzeLowValueWord(string word, string sentence)
{
try {
// 🆕 真實調用 Gemini AI
var prompt = $@"
請分析單字 ""{word}"" 在句子 ""{sentence}"" 中的詳細資訊:
請以JSON格式回應
{{
""word"": ""{word}"",
""translation"": ""繁體中文翻譯"",
""definition"": ""英文定義"",
""partOfSpeech"": ""詞性"",
""pronunciation"": ""IPA音標"",
""difficultyLevel"": ""CEFR等級"",
""contextMeaning"": ""在此句子中的具體含義"",
""isHighValue"": false
}}
";
var response = await _geminiService.CallGeminiApiAsync(prompt);
return _geminiService.ParseWordAnalysisResponse(response);
}
catch {
// 回退到模擬資料
await Task.Delay(200);
return CreateMockWordAnalysis(word);
}
}
```
### **方案3: 後端詞彙快取資料表**
#### **新資料表設計**
```sql
CREATE TABLE WordAnalysisCache (
Id UNIQUEIDENTIFIER PRIMARY KEY,
Word NVARCHAR(100) NOT NULL, -- 詞彙 (快取鍵)
WordLowercase NVARCHAR(100) NOT NULL, -- 小寫版本 (查詢用)
Translation NVARCHAR(200) NOT NULL, -- 翻譯
Definition NVARCHAR(500) NOT NULL, -- 定義
PartOfSpeech NVARCHAR(50), -- 詞性
Pronunciation NVARCHAR(100), -- 發音
DifficultyLevel NVARCHAR(10), -- CEFR 等級
IsHighValue BIT DEFAULT 0, -- 是否高價值
Synonyms NVARCHAR(500), -- 同義詞 (JSON)
ExampleSentences NVARCHAR(MAX), -- 例句 (JSON)
CreatedAt DATETIME2 NOT NULL, -- 建立時間
UpdatedAt DATETIME2 NOT NULL, -- 更新時間
AccessCount INT DEFAULT 0, -- 存取次數
INDEX IX_WordAnalysisCache_WordLowercase (WordLowercase)
);
```
#### **後端查詢邏輯**
```csharp
public async Task<WordAnalysisResult> QueryWordAsync(string word, string sentence)
{
var wordLower = word.ToLower();
// 1. 檢查詞彙快取
var cached = await _context.WordAnalysisCache
.FirstOrDefaultAsync(w => w.WordLowercase == wordLower);
if (cached != null) {
// 更新存取統計
cached.AccessCount++;
cached.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
return MapToWordAnalysisResult(cached);
}
// 2. 快取錯失,調用 AI
var aiResult = await CallGeminiForWordAnalysis(word, sentence);
// 3. 存入快取
var cacheEntry = new WordAnalysisCache {
Word = word,
WordLowercase = wordLower,
Translation = aiResult.Translation,
// ... 其他欄位
};
_context.WordAnalysisCache.Add(cacheEntry);
await _context.SaveChangesAsync();
return aiResult;
}
```
---
## 📊 **三種快取策略比較**
| 策略 | 持久性 | 效能 | 實現複雜度 | AI 成本 | 用戶體驗 |
|------|--------|------|-----------|---------|----------|
| **目前 (頁面級)** | ❌ 換句子清空 | 🟡 中等 | 🟢 簡單 | 🔴 高 (重複查詢) | 🔴 差 |
| **方案1 (前端全域)** | 🟡 瀏覽器重啟清空 | 🟢 高 | 🟡 中等 | 🟢 低 | 🟢 好 |
| **方案2 (後端資料庫)** | ✅ 永久保存 | 🟢 高 | 🔴 複雜 | 🟢 極低 | ✅ 極佳 |
---
## 🔧 **當前 query-word API 的實現細節**
### **📍 速度快的真相**
**檔案**: `/backend/DramaLing.Api/Controllers/AIController.cs:1019-1037`
```csharp
private async Task<object> AnalyzeLowValueWord(string word, string sentence)
{
// 🚨 這只是模擬實現!
await Task.Delay(200); // 假延遲
return new {
word = word,
translation = "即時分析的翻譯", // 🚨 所有詞彙都一樣
definition = "即時分析的定義", // 🚨 所有詞彙都一樣
partOfSpeech = "noun", // 🚨 所有詞彙都一樣
pronunciation = "/example/", // 🚨 所有詞彙都一樣
// ...
};
}
```
### **🧪 驗證測試**
#### **測試1: 查詢不同詞彙**
```bash
# 查詢 "hello"
curl -X POST http://localhost:5000/api/ai/query-word \
-d '{"word": "hello", "sentence": "Hello world"}'
# 結果: translation = "即時分析的翻譯"
# 查詢 "amazing"
curl -X POST http://localhost:5000/api/ai/query-word \
-d '{"word": "amazing", "sentence": "Amazing day"}'
# 結果: translation = "即時分析的翻譯" ❌ 完全相同!
```
#### **測試2: 檢查是否真的調用 AI**
```bash
# 查看後端日誌
grep -i "gemini\|ai\|query" backend_logs.txt
# 結果: 沒有真實的 AI API 調用記錄
```
---
## 📋 **建議的改善優先級**
### **🔥 高優先級 (立即修復)**
1. **實現真實的詞彙 AI 查詢**
- 替換假資料為真實 Gemini API 調用
- 提供準確的詞彙分析
2. **前端全域詞彙快取**
- 避免重複查詢相同詞彙
- 提升用戶體驗
### **⚡ 中優先級 (2週內)**
3. **後端詞彙快取資料表**
- 永久保存查詢過的詞彙
- 跨用戶共享常用詞彙分析
4. **智能快取策略**
- 基於詞彙頻率的快取優先級
- 自動清理低價值快取項目
### **💡 低優先級 (未來功能)**
5. **跨設備同步**
- 用戶詞彙學習記錄雲端同步
- 個人化詞彙掌握程度追蹤
---
## 🎯 **回答您的問題**
### **當前實際行為**:
**場景**: "The apple" → 點擊 "The" → "The orange"
```
1. 分析 "The apple" → "The" 無預存資料 (灰框)
2. 點擊 "The" → 假 AI 查詢 → 加入前端快取 → "The" 變藍框
3. 分析 "The orange" → 前端快取被覆蓋清空 → "The" 又變灰框 ❌
4. 點擊 "The" → 重新假 AI 查詢 → 重複步驟2 ❌
```
### **問題總結**:
-**不會在預存裡**: 換句子後快取被清空
-**重複假查詢**: 每次都返回相同的假資料
-**浪費資源**: 用戶以為是真實 AI 查詢
### **建議修復**:
1. **立即**: 修改前端為累積式快取
2. **短期**: 實現真實的詞彙 AI 查詢
3. **長期**: 建立後端詞彙快取資料表
---
## 📞 **技術支援**
**相關檔案**:
- 前端快取: `/frontend/app/generate/page.tsx:84, 405-412`
- 後端假查詢: `/backend/DramaLing.Api/Controllers/AIController.cs:1019-1037`
- 後端句子快取: `/backend/DramaLing.Api/Services/AnalysisCacheService.cs`
**建議優先修復**: 前端累積式快取 + 真實 AI 查詢實現
---
**© 2025 DramaLing Development Team**
**文檔建立**: 2025-01-18
**分析基於**: 當前系統 commit e940d86