refactor: 簡化API設計,移除statistics計算和userLevel參數
- 移除後端statistics計算邏輯,改由前端處理 - 移除userLevel參數,簡化API接口 - 清理DTO模型中的多餘欄位 (Tags, IsIdiom, UserLevel) - 更新AI模型名稱為gemini-1.5-flash - 新增完整的AI Prompt設計規格 - 建立AI驅動產品後端技術架構指南 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
8568d5e500
commit
96bb9e920e
234
AI生成功能後端API規格.md
234
AI生成功能後端API規格.md
|
|
@ -5,7 +5,7 @@
|
||||||
- **文件名稱**: AI生成功能後端API規格
|
- **文件名稱**: AI生成功能後端API規格
|
||||||
- **版本**: v1.0
|
- **版本**: v1.0
|
||||||
- **建立日期**: 2025-01-25
|
- **建立日期**: 2025-01-25
|
||||||
- **最後更新**: 2025-01-25
|
- **最後更新**: 2025-01-25 (修正API規格)
|
||||||
- **負責團隊**: DramaLing後端開發團隊
|
- **負責團隊**: DramaLing後端開發團隊
|
||||||
- **對應前端**: `/app/generate/page.tsx`
|
- **對應前端**: `/app/generate/page.tsx`
|
||||||
|
|
||||||
|
|
@ -57,7 +57,7 @@ External Services:
|
||||||
語言: C# / .NET 8
|
語言: C# / .NET 8
|
||||||
框架: ASP.NET Core Web API
|
框架: ASP.NET Core Web API
|
||||||
資料庫: PostgreSQL + Redis (緩存)
|
資料庫: PostgreSQL + Redis (緩存)
|
||||||
AI服務: Google Gemini API
|
AI服務: Google Gemini 1.5 Flash API (包含結構化Prompt設計)
|
||||||
部署: Docker + Kubernetes
|
部署: Docker + Kubernetes
|
||||||
監控: Application Insights
|
監控: Application Insights
|
||||||
```
|
```
|
||||||
|
|
@ -79,7 +79,6 @@ Authorization: Bearer {token}
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"inputText": "She just join the team, so let's cut her some slack until she get used to the workflow.",
|
"inputText": "She just join the team, so let's cut her some slack until she get used to the workflow.",
|
||||||
"userLevel": "A2",
|
|
||||||
"analysisMode": "full",
|
"analysisMode": "full",
|
||||||
"options": {
|
"options": {
|
||||||
"includeGrammarCheck": true,
|
"includeGrammarCheck": true,
|
||||||
|
|
@ -95,7 +94,6 @@ Authorization: Bearer {token}
|
||||||
| 參數 | 類型 | 必需 | 說明 |
|
| 參數 | 類型 | 必需 | 說明 |
|
||||||
|------|------|------|------|
|
|------|------|------|------|
|
||||||
| inputText | string | 是 | 待分析的英文句子 (最多300字) |
|
| inputText | string | 是 | 待分析的英文句子 (最多300字) |
|
||||||
| userLevel | string | 是 | 用戶CEFR等級 (A1-C2) |
|
|
||||||
| analysisMode | string | 否 | 分析模式: "basic"\|"full" (預設: "full") |
|
| analysisMode | string | 否 | 分析模式: "basic"\|"full" (預設: "full") |
|
||||||
| options | object | 否 | 分析選項配置 |
|
| options | object | 否 | 分析選項配置 |
|
||||||
|
|
||||||
|
|
@ -142,7 +140,6 @@ Authorization: Bearer {token}
|
||||||
"synonyms": ["her"],
|
"synonyms": ["her"],
|
||||||
"example": "She is a teacher.",
|
"example": "She is a teacher.",
|
||||||
"exampleTranslation": "她是一名老師。",
|
"exampleTranslation": "她是一名老師。",
|
||||||
"tags": ["basic", "pronoun"]
|
|
||||||
},
|
},
|
||||||
"just": {
|
"just": {
|
||||||
"word": "just",
|
"word": "just",
|
||||||
|
|
@ -155,7 +152,6 @@ Authorization: Bearer {token}
|
||||||
"synonyms": ["recently", "only", "merely"],
|
"synonyms": ["recently", "only", "merely"],
|
||||||
"example": "I just arrived.",
|
"example": "I just arrived.",
|
||||||
"exampleTranslation": "我剛到。",
|
"exampleTranslation": "我剛到。",
|
||||||
"tags": ["time", "adverb"]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"idioms": [
|
"idioms": [
|
||||||
|
|
@ -175,20 +171,10 @@ Authorization: Bearer {token}
|
||||||
"exampleTranslation": "對他寬容一點,他是新來的。"
|
"exampleTranslation": "對他寬容一點,他是新來的。"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"statistics": {
|
|
||||||
"totalWords": 16,
|
|
||||||
"uniqueWords": 15,
|
|
||||||
"simpleWords": 8,
|
|
||||||
"moderateWords": 4,
|
|
||||||
"difficultWords": 3,
|
|
||||||
"idioms": 1,
|
|
||||||
"averageDifficulty": "A2"
|
|
||||||
},
|
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"analysisModel": "gemini-pro",
|
"analysisModel": "gemini-1.5-flash",
|
||||||
"analysisVersion": "1.0",
|
"analysisVersion": "2.0",
|
||||||
"processingDate": "2025-01-25T10:30:00Z",
|
"processingDate": "2025-01-25T10:30:00Z",
|
||||||
"userLevel": "A2"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -228,7 +214,6 @@ interface VocabularyAnalysis {
|
||||||
synonyms: string[] // 同義詞
|
synonyms: string[] // 同義詞
|
||||||
example?: string // 例句
|
example?: string // 例句
|
||||||
exampleTranslation?: string // 例句翻譯
|
exampleTranslation?: string // 例句翻譯
|
||||||
tags: string[] // 標籤分類
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -250,6 +235,48 @@ interface GrammarError {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### **IdiomDto 模型**
|
||||||
|
```typescript
|
||||||
|
interface IdiomDto {
|
||||||
|
idiom: string // 慣用語本身
|
||||||
|
translation: string // 中文翻譯
|
||||||
|
definition: string // 英文定義
|
||||||
|
pronunciation: string // 發音 (IPA)
|
||||||
|
difficultyLevel: CEFRLevel // CEFR等級
|
||||||
|
frequency: FrequencyLevel // 使用頻率
|
||||||
|
synonyms: string[] // 同義詞或相似表達
|
||||||
|
example?: string // 例句
|
||||||
|
exampleTranslation?: string // 例句翻譯
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### **AnalysisMetadata 模型**
|
||||||
|
```typescript
|
||||||
|
interface AnalysisMetadata {
|
||||||
|
analysisModel: string // AI模型名稱
|
||||||
|
analysisVersion: string // 分析版本
|
||||||
|
processingDate: string // 處理時間 (ISO 8601)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### **ApiErrorResponse 模型**
|
||||||
|
```typescript
|
||||||
|
interface ApiErrorResponse {
|
||||||
|
success: boolean // 固定為 false
|
||||||
|
error: ApiError // 錯誤詳情
|
||||||
|
timestamp: string // 錯誤時間 (ISO 8601)
|
||||||
|
requestId: string // 請求ID
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ApiError {
|
||||||
|
code: string // 錯誤代碼
|
||||||
|
message: string // 錯誤訊息
|
||||||
|
details?: object // 錯誤詳情
|
||||||
|
suggestions: string[] // 建議解決方案
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### **枚舉定義**
|
### **枚舉定義**
|
||||||
```typescript
|
```typescript
|
||||||
type CEFRLevel = "A1" | "A2" | "B1" | "B2" | "C1" | "C2"
|
type CEFRLevel = "A1" | "A2" | "B1" | "B2" | "C1" | "C2"
|
||||||
|
|
@ -259,6 +286,151 @@ type AnalysisMode = "basic" | "full"
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 🤖 **AI Prompt設計規格**
|
||||||
|
|
||||||
|
### **Prompt架構設計**
|
||||||
|
|
||||||
|
#### **核心Prompt模板**
|
||||||
|
```text
|
||||||
|
You are an English learning assistant. Analyze this sentence for a learner and return ONLY a valid JSON response.
|
||||||
|
|
||||||
|
**Input Sentence**: "{inputText}"
|
||||||
|
|
||||||
|
**Required JSON Structure:**
|
||||||
|
{
|
||||||
|
"sentenceTranslation": "Traditional Chinese translation of the entire sentence",
|
||||||
|
"hasGrammarErrors": true/false,
|
||||||
|
"grammarCorrections": [
|
||||||
|
{
|
||||||
|
"original": "incorrect text",
|
||||||
|
"corrected": "correct text",
|
||||||
|
"type": "error type (tense/subject-verb/preposition/word-order)",
|
||||||
|
"explanation": "brief explanation in Traditional Chinese"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"vocabularyAnalysis": {
|
||||||
|
"word1": {
|
||||||
|
"word": "the word",
|
||||||
|
"translation": "Traditional Chinese translation",
|
||||||
|
"definition": "English definition",
|
||||||
|
"partOfSpeech": "noun/verb/adjective/etc",
|
||||||
|
"pronunciation": "/phonetic/",
|
||||||
|
"difficultyLevel": "A1/A2/B1/B2/C1/C2",
|
||||||
|
"frequency": "high/medium/low",
|
||||||
|
"synonyms": ["synonym1", "synonym2"],
|
||||||
|
"example": "example sentence",
|
||||||
|
"exampleTranslation": "Traditional Chinese example translation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idioms": [
|
||||||
|
{
|
||||||
|
"idiom": "idiomatic expression",
|
||||||
|
"translation": "Traditional Chinese meaning",
|
||||||
|
"definition": "English explanation",
|
||||||
|
"pronunciation": "/phonetic notation/",
|
||||||
|
"difficultyLevel": "A1/A2/B1/B2/C1/C2",
|
||||||
|
"frequency": "high/medium/low",
|
||||||
|
"synonyms": ["synonym1", "synonym2"],
|
||||||
|
"example": "usage example",
|
||||||
|
"exampleTranslation": "Traditional Chinese example"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Prompt指導原則**
|
||||||
|
|
||||||
|
#### **分析指導方針**
|
||||||
|
```yaml
|
||||||
|
語法檢查:
|
||||||
|
- 檢測範圍: 時態錯誤、主謂一致、介詞使用、詞序問題
|
||||||
|
- 修正原則: 提供自然且正確的英語表達
|
||||||
|
- 解釋語言: 使用繁體中文(台灣標準)
|
||||||
|
|
||||||
|
詞彙分析:
|
||||||
|
- 包含範圍: 所有有意義的詞彙(排除冠詞 a, an, the)
|
||||||
|
- CEFR標準: 準確分配A1-C2等級
|
||||||
|
- 發音標記: 使用國際音標(IPA)
|
||||||
|
- 例句品質: 提供實用且常見的例句
|
||||||
|
|
||||||
|
慣用語識別:
|
||||||
|
- 識別目標: 慣用語、片語動詞、固定搭配
|
||||||
|
- 分類原則: 獨立於vocabularyAnalysis處理
|
||||||
|
- 解釋詳度: 提供文化背景和使用場景
|
||||||
|
|
||||||
|
翻譯品質:
|
||||||
|
- 語言標準: 繁體中文(台灣用法)
|
||||||
|
- 自然程度: 符合中文表達習慣
|
||||||
|
- 準確性: 保持原文語義完整
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Prompt優化策略**
|
||||||
|
|
||||||
|
#### **結構化輸出控制**
|
||||||
|
```text
|
||||||
|
**IMPORTANT**: Return ONLY the JSON object, no additional text or explanation.
|
||||||
|
|
||||||
|
**Quality Assurance:**
|
||||||
|
1. Ensure all JSON keys are exactly as specified
|
||||||
|
2. Use Traditional Chinese for all translations
|
||||||
|
3. Assign accurate CEFR levels based on standard guidelines
|
||||||
|
4. Separate idioms from regular vocabulary
|
||||||
|
5. Provide practical examples that learners can use
|
||||||
|
```
|
||||||
|
|
||||||
|
#### **錯誤處理Prompt**
|
||||||
|
```text
|
||||||
|
**Error Handling Guidelines:**
|
||||||
|
- If unable to analyze: Return basic structure with explanatory messages
|
||||||
|
- If safety filtered: Provide alternative analysis approach
|
||||||
|
- If ambiguous input: Make reasonable assumptions and proceed
|
||||||
|
- Always return valid JSON regardless of input complexity
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Prompt版本管理**
|
||||||
|
|
||||||
|
#### **版本演進記錄**
|
||||||
|
```yaml
|
||||||
|
v1.0 (2025-01-20):
|
||||||
|
- 基礎prompt結構
|
||||||
|
- 簡單詞彙分析
|
||||||
|
|
||||||
|
v1.5 (2025-01-22):
|
||||||
|
- 新增慣用語識別
|
||||||
|
- 優化語法檢查
|
||||||
|
|
||||||
|
v2.0 (2025-01-25):
|
||||||
|
- 結構化JSON輸出
|
||||||
|
- 完整資料模型支援
|
||||||
|
- 繁體中文本地化
|
||||||
|
- 錯誤處理機制
|
||||||
|
```
|
||||||
|
|
||||||
|
#### **A/B測試配置**
|
||||||
|
```csharp
|
||||||
|
// Prompt變體測試
|
||||||
|
public enum PromptVariant
|
||||||
|
{
|
||||||
|
Standard, // 標準prompt
|
||||||
|
DetailedExamples, // 強調例句品質
|
||||||
|
StrictGrammar, // 加強語法檢查
|
||||||
|
CulturalContext // 增加文化背景
|
||||||
|
}
|
||||||
|
|
||||||
|
// 動態Prompt選擇
|
||||||
|
private string GetPromptByVariant(PromptVariant variant, string inputText)
|
||||||
|
{
|
||||||
|
return variant switch
|
||||||
|
{
|
||||||
|
PromptVariant.Standard => BuildStandardPrompt(inputText),
|
||||||
|
PromptVariant.DetailedExamples => BuildDetailedPrompt(inputText),
|
||||||
|
_ => BuildStandardPrompt(inputText)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 🔒 **認證與授權**
|
## 🔒 **認證與授權**
|
||||||
|
|
||||||
### **API認證**
|
### **API認證**
|
||||||
|
|
@ -343,7 +515,7 @@ Premium用戶:
|
||||||
"responseTime": 2340,
|
"responseTime": 2340,
|
||||||
"inputLength": 89,
|
"inputLength": 89,
|
||||||
"analysisMode": "full",
|
"analysisMode": "full",
|
||||||
"aiModel": "gpt-4",
|
"aiModel": "gemini-1.5-flash",
|
||||||
"processingSteps": {
|
"processingSteps": {
|
||||||
"grammarCheck": 450,
|
"grammarCheck": 450,
|
||||||
"vocabularyAnalysis": 1200,
|
"vocabularyAnalysis": 1200,
|
||||||
|
|
@ -409,18 +581,14 @@ Premium用戶:
|
||||||
測試目的: 驗證完整分析功能
|
測試目的: 驗證完整分析功能
|
||||||
輸入數據:
|
輸入數據:
|
||||||
inputText: "She just join the team, so let's cut her some slack until she get used to the workflow."
|
inputText: "She just join the team, so let's cut her some slack until she get used to the workflow."
|
||||||
userLevel: "A2"
|
|
||||||
analysisMode: "full"
|
analysisMode: "full"
|
||||||
|
|
||||||
預期結果:
|
預期結果:
|
||||||
statusCode: 200
|
statusCode: 200
|
||||||
grammarCorrection.hasErrors: true
|
grammarCorrection.hasErrors: true
|
||||||
grammarCorrection.corrections.length: 2
|
grammarCorrection.corrections.length: 2
|
||||||
vocabularyAnalysis keys: 17 (16個詞 + 1個慣用語)
|
vocabularyAnalysis keys: 16 (不含慣用語)
|
||||||
statistics.simpleWords: 8
|
idioms.length: 1
|
||||||
statistics.moderateWords: 4
|
|
||||||
statistics.difficultWords: 3
|
|
||||||
statistics.phrases: 1
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### **TC-002: 輸入驗證測試**
|
#### **TC-002: 輸入驗證測試**
|
||||||
|
|
@ -623,7 +791,17 @@ AI升級:
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**文件版本**: v1.0
|
**文件版本**: v1.1 (更新AI模型和模型定義)
|
||||||
**API版本**: v1
|
**API版本**: v1
|
||||||
**最後更新**: 2025-01-25
|
**最後更新**: 2025-01-25
|
||||||
**下次審查**: 2025-02-25
|
**下次審查**: 2025-02-25
|
||||||
|
|
||||||
|
**更新記錄**:
|
||||||
|
- v1.1: 修正AI模型名稱為Gemini 1.5 Flash
|
||||||
|
- v1.1: 移除VocabularyAnalysis中tags欄位
|
||||||
|
- v1.1: 新增IdiomDto和ApiErrorResponse模型定義
|
||||||
|
- v1.1: 移除userLevel參數(簡化API)
|
||||||
|
- v1.1: 統一回應格式範例
|
||||||
|
- v1.1: 新增完整的AI Prompt設計規格和版本管理
|
||||||
|
- v1.1: 同步更新後端DTO模型實現(移除Tags、IsIdiom、UserLevel欄位)
|
||||||
|
- v1.1: 移除statistics計算邏輯(前端自行處理統計)
|
||||||
|
|
@ -498,7 +498,6 @@ API通信: Fetch API
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"inputText": "英文句子",
|
"inputText": "英文句子",
|
||||||
"userLevel": "A2",
|
|
||||||
"analysisMode": "full"
|
"analysisMode": "full"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -508,9 +507,12 @@ API通信: Fetch API
|
||||||
{
|
{
|
||||||
"success": true,
|
"success": true,
|
||||||
"data": {
|
"data": {
|
||||||
"WordAnalysis": "詞彙分析對象",
|
"vocabularyAnalysis": "詞彙分析字典對象",
|
||||||
"SentenceMeaning": "句子翻譯",
|
"sentenceMeaning": "句子翻譯字串",
|
||||||
"GrammarCorrection": "語法修正資料"
|
"grammarCorrection": "語法修正資料對象",
|
||||||
|
"idioms": "慣用語陣列",
|
||||||
|
"statistics": "統計資料對象",
|
||||||
|
"metadata": "分析元資料對象"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -537,7 +539,7 @@ interface WordAnalysisRequired {
|
||||||
partOfSpeech: string // 必需:詞性
|
partOfSpeech: string // 必需:詞性
|
||||||
pronunciation: string // 必需:發音
|
pronunciation: string // 必需:發音
|
||||||
difficultyLevel: string // 必需:CEFR等級
|
difficultyLevel: string // 必需:CEFR等級
|
||||||
isIdiom: boolean // 必需:是否為慣用語
|
frequency: string // 必需:使用頻率
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -727,7 +729,13 @@ interface WordAnalysisOptional {
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**文件版本**: v1.0
|
**文件版本**: v1.1 (配合後端API規格更新)
|
||||||
**產品負責人**: DramaLing產品團隊
|
**產品負責人**: DramaLing產品團隊
|
||||||
**最後更新**: 2025-09-21
|
**最後更新**: 2025-01-25
|
||||||
**下次審查**: 2025-10-21
|
**下次審查**: 2025-02-25
|
||||||
|
|
||||||
|
**更新記錄**:
|
||||||
|
- v1.1: 移除API請求中的userLevel參數要求
|
||||||
|
- v1.1: 更新API回應格式為實際實現的格式
|
||||||
|
- v1.1: 修正資料模型,移除isIdiom欄位,新增frequency欄位
|
||||||
|
- v1.1: 保留前端個人化分類邏輯,改為從localStorage獲取用戶等級
|
||||||
|
|
@ -53,7 +53,7 @@ public class AIController : ControllerBase
|
||||||
// 直接執行 AI 分析,不使用快取
|
// 直接執行 AI 分析,不使用快取
|
||||||
var options = request.Options ?? new AnalysisOptions();
|
var options = request.Options ?? new AnalysisOptions();
|
||||||
var analysisData = await _geminiService.AnalyzeSentenceAsync(
|
var analysisData = await _geminiService.AnalyzeSentenceAsync(
|
||||||
request.InputText, request.UserLevel, options);
|
request.InputText, options);
|
||||||
|
|
||||||
stopwatch.Stop();
|
stopwatch.Stop();
|
||||||
analysisData.Metadata.ProcessingDate = DateTime.UtcNow;
|
analysisData.Metadata.ProcessingDate = DateTime.UtcNow;
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,6 @@ public class SentenceAnalysisRequest
|
||||||
[StringLength(300, MinimumLength = 1, ErrorMessage = "輸入文本長度必須在1-300字符之間")]
|
[StringLength(300, MinimumLength = 1, ErrorMessage = "輸入文本長度必須在1-300字符之間")]
|
||||||
public string InputText { get; set; } = string.Empty;
|
public string InputText { get; set; } = string.Empty;
|
||||||
|
|
||||||
[Required]
|
|
||||||
[RegularExpression("^(A1|A2|B1|B2|C1|C2)$", ErrorMessage = "無效的CEFR等級")]
|
|
||||||
public string UserLevel { get; set; } = "A2";
|
|
||||||
|
|
||||||
public string AnalysisMode { get; set; } = "full";
|
public string AnalysisMode { get; set; } = "full";
|
||||||
|
|
||||||
|
|
@ -42,7 +39,6 @@ public class SentenceAnalysisData
|
||||||
public string SentenceMeaning { get; set; } = string.Empty;
|
public string SentenceMeaning { get; set; } = string.Empty;
|
||||||
public Dictionary<string, VocabularyAnalysisDto> VocabularyAnalysis { get; set; } = new();
|
public Dictionary<string, VocabularyAnalysisDto> VocabularyAnalysis { get; set; } = new();
|
||||||
public List<IdiomDto> Idioms { get; set; } = new();
|
public List<IdiomDto> Idioms { get; set; } = new();
|
||||||
public AnalysisStatistics Statistics { get; set; } = new();
|
|
||||||
public AnalysisMetadata Metadata { get; set; } = new();
|
public AnalysisMetadata Metadata { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,12 +73,10 @@ public class VocabularyAnalysisDto
|
||||||
public string PartOfSpeech { get; set; } = string.Empty;
|
public string PartOfSpeech { get; set; } = string.Empty;
|
||||||
public string Pronunciation { get; set; } = string.Empty;
|
public string Pronunciation { get; set; } = string.Empty;
|
||||||
public string DifficultyLevel { get; set; } = string.Empty;
|
public string DifficultyLevel { get; set; } = string.Empty;
|
||||||
public bool IsIdiom { get; set; }
|
|
||||||
public string Frequency { get; set; } = string.Empty;
|
public string Frequency { get; set; } = string.Empty;
|
||||||
public List<string> Synonyms { get; set; } = new();
|
public List<string> Synonyms { get; set; } = new();
|
||||||
public string? Example { get; set; }
|
public string? Example { get; set; }
|
||||||
public string? ExampleTranslation { get; set; }
|
public string? ExampleTranslation { get; set; }
|
||||||
public List<string> Tags { get; set; } = new();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class IdiomDto
|
public class IdiomDto
|
||||||
|
|
@ -98,23 +92,12 @@ public class IdiomDto
|
||||||
public string? ExampleTranslation { get; set; }
|
public string? ExampleTranslation { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AnalysisStatistics
|
|
||||||
{
|
|
||||||
public int TotalWords { get; set; }
|
|
||||||
public int UniqueWords { get; set; }
|
|
||||||
public int SimpleWords { get; set; }
|
|
||||||
public int ModerateWords { get; set; }
|
|
||||||
public int DifficultWords { get; set; }
|
|
||||||
public int Idioms { get; set; }
|
|
||||||
public string AverageDifficulty { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AnalysisMetadata
|
public class AnalysisMetadata
|
||||||
{
|
{
|
||||||
public string AnalysisModel { get; set; } = "gpt-4";
|
public string AnalysisModel { get; set; } = "gemini-1.5-flash";
|
||||||
public string AnalysisVersion { get; set; } = "1.0";
|
public string AnalysisVersion { get; set; } = "2.0";
|
||||||
public DateTime ProcessingDate { get; set; } = DateTime.UtcNow;
|
public DateTime ProcessingDate { get; set; } = DateTime.UtcNow;
|
||||||
public string UserLevel { get; set; } = string.Empty;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ApiErrorResponse
|
public class ApiErrorResponse
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ namespace DramaLing.Api.Services;
|
||||||
|
|
||||||
public interface IGeminiService
|
public interface IGeminiService
|
||||||
{
|
{
|
||||||
Task<SentenceAnalysisData> AnalyzeSentenceAsync(string inputText, string userLevel, AnalysisOptions options);
|
Task<SentenceAnalysisData> AnalyzeSentenceAsync(string inputText, AnalysisOptions options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GeminiService : IGeminiService
|
public class GeminiService : IGeminiService
|
||||||
|
|
@ -31,20 +31,19 @@ public class GeminiService : IGeminiService
|
||||||
_httpClient.DefaultRequestHeaders.Add("User-Agent", "DramaLing/1.0");
|
_httpClient.DefaultRequestHeaders.Add("User-Agent", "DramaLing/1.0");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SentenceAnalysisData> AnalyzeSentenceAsync(string inputText, string userLevel, AnalysisOptions options)
|
public async Task<SentenceAnalysisData> AnalyzeSentenceAsync(string inputText, AnalysisOptions options)
|
||||||
{
|
{
|
||||||
var startTime = DateTime.UtcNow;
|
var startTime = DateTime.UtcNow;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Starting sentence analysis for text: {Text}, UserLevel: {UserLevel}",
|
_logger.LogInformation("Starting sentence analysis for text: {Text}",
|
||||||
inputText.Substring(0, Math.Min(50, inputText.Length)), userLevel);
|
inputText.Substring(0, Math.Min(50, inputText.Length)));
|
||||||
|
|
||||||
// 符合產品需求規格的結構化 prompt
|
// 符合產品需求規格的結構化 prompt
|
||||||
var prompt = $@"You are an English learning assistant. Analyze this sentence for a {userLevel} CEFR level learner and return ONLY a valid JSON response.
|
var prompt = $@"You are an English learning assistant. Analyze this sentence and return ONLY a valid JSON response.
|
||||||
|
|
||||||
**Input Sentence**: ""{inputText}""
|
**Input Sentence**: ""{inputText}""
|
||||||
**Learner Level**: {userLevel}
|
|
||||||
|
|
||||||
**Required JSON Structure:**
|
**Required JSON Structure:**
|
||||||
{{
|
{{
|
||||||
|
|
@ -113,7 +112,7 @@ public class GeminiService : IGeminiService
|
||||||
}
|
}
|
||||||
|
|
||||||
// 直接使用 AI 的回應創建分析數據
|
// 直接使用 AI 的回應創建分析數據
|
||||||
var analysisData = CreateAnalysisFromAIResponse(inputText, userLevel, aiResponse);
|
var analysisData = CreateAnalysisFromAIResponse(inputText, aiResponse);
|
||||||
|
|
||||||
var processingTime = (DateTime.UtcNow - startTime).TotalSeconds;
|
var processingTime = (DateTime.UtcNow - startTime).TotalSeconds;
|
||||||
_logger.LogInformation("Sentence analysis completed in {ProcessingTime}s", processingTime);
|
_logger.LogInformation("Sentence analysis completed in {ProcessingTime}s", processingTime);
|
||||||
|
|
@ -127,7 +126,7 @@ public class GeminiService : IGeminiService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SentenceAnalysisData CreateAnalysisFromAIResponse(string inputText, string userLevel, string aiResponse)
|
private SentenceAnalysisData CreateAnalysisFromAIResponse(string inputText, string aiResponse)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Creating analysis from AI response: {ResponsePreview}...",
|
_logger.LogInformation("Creating analysis from AI response: {ResponsePreview}...",
|
||||||
aiResponse.Substring(0, Math.Min(100, aiResponse.Length)));
|
aiResponse.Substring(0, Math.Min(100, aiResponse.Length)));
|
||||||
|
|
@ -166,15 +165,12 @@ public class GeminiService : IGeminiService
|
||||||
GrammarCorrection = ConvertGrammarCorrection(aiAnalysis),
|
GrammarCorrection = ConvertGrammarCorrection(aiAnalysis),
|
||||||
Metadata = new AnalysisMetadata
|
Metadata = new AnalysisMetadata
|
||||||
{
|
{
|
||||||
UserLevel = userLevel,
|
|
||||||
ProcessingDate = DateTime.UtcNow,
|
ProcessingDate = DateTime.UtcNow,
|
||||||
AnalysisModel = "gemini-1.5-flash",
|
AnalysisModel = "gemini-1.5-flash",
|
||||||
AnalysisVersion = "2.0"
|
AnalysisVersion = "2.0"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 計算統計
|
|
||||||
analysisData.Statistics = CalculateStatistics(analysisData.VocabularyAnalysis, analysisData.Idioms, userLevel);
|
|
||||||
|
|
||||||
return analysisData;
|
return analysisData;
|
||||||
}
|
}
|
||||||
|
|
@ -182,7 +178,7 @@ public class GeminiService : IGeminiService
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Failed to parse AI response as JSON: {Response}", aiResponse);
|
_logger.LogError(ex, "Failed to parse AI response as JSON: {Response}", aiResponse);
|
||||||
// 回退到舊的處理方式
|
// 回退到舊的處理方式
|
||||||
return CreateFallbackAnalysis(inputText, userLevel, aiResponse);
|
return CreateFallbackAnalysis(inputText, aiResponse);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -205,7 +201,6 @@ public class GeminiService : IGeminiService
|
||||||
Synonyms = aiWord.Synonyms ?? new List<string>(),
|
Synonyms = aiWord.Synonyms ?? new List<string>(),
|
||||||
Example = aiWord.Example,
|
Example = aiWord.Example,
|
||||||
ExampleTranslation = aiWord.ExampleTranslation,
|
ExampleTranslation = aiWord.ExampleTranslation,
|
||||||
Tags = new List<string> { "ai-analyzed", "gemini" }
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -260,7 +255,7 @@ public class GeminiService : IGeminiService
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private SentenceAnalysisData CreateFallbackAnalysis(string inputText, string userLevel, string aiResponse)
|
private SentenceAnalysisData CreateFallbackAnalysis(string inputText, string aiResponse)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Using fallback analysis due to JSON parsing failure");
|
_logger.LogWarning("Using fallback analysis due to JSON parsing failure");
|
||||||
|
|
||||||
|
|
@ -271,12 +266,10 @@ public class GeminiService : IGeminiService
|
||||||
VocabularyAnalysis = CreateBasicVocabularyFromText(inputText, aiResponse),
|
VocabularyAnalysis = CreateBasicVocabularyFromText(inputText, aiResponse),
|
||||||
Metadata = new AnalysisMetadata
|
Metadata = new AnalysisMetadata
|
||||||
{
|
{
|
||||||
UserLevel = userLevel,
|
|
||||||
ProcessingDate = DateTime.UtcNow,
|
ProcessingDate = DateTime.UtcNow,
|
||||||
AnalysisModel = "gemini-1.5-flash-fallback",
|
AnalysisModel = "gemini-1.5-flash-fallback",
|
||||||
AnalysisVersion = "1.0"
|
AnalysisVersion = "2.0"
|
||||||
},
|
},
|
||||||
Statistics = new AnalysisStatistics()
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -303,7 +296,6 @@ public class GeminiService : IGeminiService
|
||||||
Synonyms = new List<string>(),
|
Synonyms = new List<string>(),
|
||||||
Example = null,
|
Example = null,
|
||||||
ExampleTranslation = null,
|
ExampleTranslation = null,
|
||||||
Tags = new List<string> { "fallback-analysis" }
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -335,21 +327,6 @@ public class GeminiService : IGeminiService
|
||||||
return "B2";
|
return "B2";
|
||||||
}
|
}
|
||||||
|
|
||||||
private AnalysisStatistics CalculateStatistics(Dictionary<string, VocabularyAnalysisDto> vocabulary, List<IdiomDto> idioms, string userLevel)
|
|
||||||
{
|
|
||||||
var stats = new AnalysisStatistics
|
|
||||||
{
|
|
||||||
TotalWords = vocabulary.Count,
|
|
||||||
UniqueWords = vocabulary.Count,
|
|
||||||
SimpleWords = vocabulary.Count(kvp => kvp.Value.DifficultyLevel == "A1"),
|
|
||||||
ModerateWords = vocabulary.Count(kvp => kvp.Value.DifficultyLevel == "A2"),
|
|
||||||
DifficultWords = vocabulary.Count(kvp => !new[] { "A1", "A2" }.Contains(kvp.Value.DifficultyLevel)),
|
|
||||||
Idioms = idioms.Count, // 基於實際 idioms 陣列計算
|
|
||||||
AverageDifficulty = userLevel
|
|
||||||
};
|
|
||||||
|
|
||||||
return stats;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<string> CallGeminiAPI(string prompt)
|
private async Task<string> CallGeminiAPI(string prompt)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue