302 lines
6.7 KiB
Markdown
302 lines
6.7 KiB
Markdown
# Gemini AI API 整合文檔
|
||
|
||
## 概述
|
||
本文檔說明如何整合 Google Gemini API 來實現 AI 詞卡生成功能。
|
||
|
||
## API 設置
|
||
|
||
### 1. 取得 API Key
|
||
1. 訪問 [Google AI Studio](https://makersuite.google.com/app/apikey)
|
||
2. 點擊 "Create API Key"
|
||
3. 選擇專案或建立新專案
|
||
4. 複製生成的 API Key
|
||
|
||
### 2. 安裝 SDK
|
||
```bash
|
||
npm install @google/generative-ai
|
||
```
|
||
|
||
### 3. 環境變數配置
|
||
```env
|
||
GEMINI_API_KEY=your_api_key_here
|
||
```
|
||
|
||
## API 實作
|
||
|
||
### 基礎設置
|
||
```typescript
|
||
// lib/gemini.ts
|
||
import { GoogleGenerativeAI } from '@google/generative-ai';
|
||
|
||
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY!);
|
||
|
||
export const geminiModel = genAI.getGenerativeModel({
|
||
model: "gemini-pro"
|
||
});
|
||
```
|
||
|
||
### 詞卡生成功能
|
||
|
||
```typescript
|
||
// app/api/ai/generate-flashcard/route.ts
|
||
import { NextRequest, NextResponse } from 'next/server';
|
||
import { geminiModel } from '@/lib/gemini';
|
||
|
||
export async function POST(request: NextRequest) {
|
||
try {
|
||
const { text, type } = await request.json();
|
||
|
||
const prompt = createPrompt(text, type);
|
||
const result = await geminiModel.generateContent(prompt);
|
||
const response = await result.response;
|
||
const generatedText = response.text();
|
||
|
||
const flashcards = parseFlashcards(generatedText);
|
||
|
||
return NextResponse.json({ flashcards });
|
||
} catch (error) {
|
||
console.error('Gemini API error:', error);
|
||
return NextResponse.json(
|
||
{ error: 'Failed to generate flashcards' },
|
||
{ status: 500 }
|
||
);
|
||
}
|
||
}
|
||
```
|
||
|
||
## Prompt 工程
|
||
|
||
### 基礎 Prompt 模板
|
||
```typescript
|
||
const FLASHCARD_PROMPT = `
|
||
你是一個專業的英語教學助手,專門幫助台灣學生透過美劇學習英文。
|
||
請根據以下內容生成詞卡:
|
||
|
||
輸入文本:{input_text}
|
||
|
||
請生成5個最重要的詞彙學習卡片,每個卡片包含:
|
||
1. 英文單字或片語
|
||
2. 中文翻譯
|
||
3. 詞性和用法說明
|
||
4. 原文例句(從輸入文本中提取)
|
||
5. 額外例句(創造一個相似情境的例句)
|
||
6. 使用情境說明(什麼場合使用)
|
||
7. 難度等級(1-5,1最簡單)
|
||
|
||
請以 JSON 格式回覆,格式如下:
|
||
{
|
||
"flashcards": [
|
||
{
|
||
"word": "英文單字或片語",
|
||
"translation": "中文翻譯",
|
||
"partOfSpeech": "詞性",
|
||
"usage": "用法說明",
|
||
"originalExample": "原文例句",
|
||
"additionalExample": "額外例句",
|
||
"context": "使用情境",
|
||
"difficulty": 難度數字
|
||
}
|
||
]
|
||
}
|
||
`;
|
||
```
|
||
|
||
### 不同類型的 Prompt
|
||
|
||
#### 1. 美劇對話分析
|
||
```typescript
|
||
const DRAMA_DIALOGUE_PROMPT = `
|
||
分析以下美劇對話,提取日常對話中的重要表達:
|
||
- 重點關注俚語、慣用語、口語表達
|
||
- 解釋文化背景和使用場合
|
||
- 標註正式程度(非常口語/口語/中性/正式)
|
||
`;
|
||
```
|
||
|
||
#### 2. 主題學習
|
||
```typescript
|
||
const TOPIC_LEARNING_PROMPT = `
|
||
生成關於「{topic}」主題的詞彙卡片:
|
||
- 包含該主題最常用的詞彙
|
||
- 提供實用的搭配詞和片語
|
||
- 給出真實對話場景的例句
|
||
`;
|
||
```
|
||
|
||
#### 3. 語法重點
|
||
```typescript
|
||
const GRAMMAR_FOCUS_PROMPT = `
|
||
分析文本中的語法結構:
|
||
- 識別重要的語法模式
|
||
- 解釋語法規則和例外
|
||
- 提供類似結構的變化練習
|
||
`;
|
||
```
|
||
|
||
## 回應解析
|
||
|
||
### JSON 解析函數
|
||
```typescript
|
||
function parseFlashcards(responseText: string) {
|
||
try {
|
||
// 清理回應文本(移除可能的 markdown 標記)
|
||
const cleanedText = responseText
|
||
.replace(/```json\n?/g, '')
|
||
.replace(/```\n?/g, '')
|
||
.trim();
|
||
|
||
const parsed = JSON.parse(cleanedText);
|
||
return parsed.flashcards;
|
||
} catch (error) {
|
||
console.error('Parse error:', error);
|
||
// 備用解析邏輯
|
||
return extractFlashcardsManually(responseText);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 錯誤處理與重試
|
||
```typescript
|
||
async function generateWithRetry(prompt: string, maxRetries = 3) {
|
||
for (let i = 0; i < maxRetries; i++) {
|
||
try {
|
||
const result = await geminiModel.generateContent(prompt);
|
||
return result;
|
||
} catch (error) {
|
||
if (i === maxRetries - 1) throw error;
|
||
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## Rate Limiting 與配額管理
|
||
|
||
### 請求限制
|
||
```typescript
|
||
import { RateLimiter } from '@/lib/rate-limiter';
|
||
|
||
const limiter = new RateLimiter({
|
||
tokensPerInterval: 60, // Gemini Free: 60 QPM
|
||
interval: 'minute',
|
||
});
|
||
|
||
export async function checkRateLimit(userId: string) {
|
||
const isAllowed = await limiter.check(userId);
|
||
if (!isAllowed) {
|
||
throw new Error('Rate limit exceeded');
|
||
}
|
||
}
|
||
```
|
||
|
||
### 配額追蹤
|
||
```typescript
|
||
// 記錄用戶使用量
|
||
async function trackUsage(userId: string, tokensUsed: number) {
|
||
await supabase
|
||
.from('user_usage')
|
||
.upsert({
|
||
user_id: userId,
|
||
date: new Date().toISOString().split('T')[0],
|
||
gemini_tokens: tokensUsed,
|
||
});
|
||
}
|
||
```
|
||
|
||
## 最佳實踐
|
||
|
||
### 1. Prompt 優化
|
||
- 使用具體、清晰的指示
|
||
- 提供輸出格式範例
|
||
- 設定合適的 temperature (0.7-0.9)
|
||
|
||
### 2. 成本控制
|
||
- 限制每個請求的 token 數量
|
||
- 實施用戶配額系統
|
||
- 快取常見請求結果
|
||
|
||
### 3. 錯誤處理
|
||
- 實施重試機制
|
||
- 提供降級方案
|
||
- 記錄錯誤日誌
|
||
|
||
### 4. 安全考量
|
||
- 不在前端暴露 API Key
|
||
- 實施內容過濾
|
||
- 驗證用戶輸入
|
||
|
||
## 測試範例
|
||
|
||
### 單元測試
|
||
```typescript
|
||
describe('Gemini Integration', () => {
|
||
it('should generate flashcards from text', async () => {
|
||
const input = "I'm gonna grab a coffee, wanna come?";
|
||
const result = await generateFlashcards(input);
|
||
|
||
expect(result).toHaveLength(5);
|
||
expect(result[0]).toHaveProperty('word');
|
||
expect(result[0]).toHaveProperty('translation');
|
||
});
|
||
|
||
it('should handle rate limiting', async () => {
|
||
// 測試超過限制的請求
|
||
});
|
||
});
|
||
```
|
||
|
||
### 整合測試
|
||
```typescript
|
||
describe('API Endpoint', () => {
|
||
it('should return flashcards via API', async () => {
|
||
const response = await fetch('/api/ai/generate-flashcard', {
|
||
method: 'POST',
|
||
body: JSON.stringify({ text: 'test input' }),
|
||
});
|
||
|
||
expect(response.status).toBe(200);
|
||
const data = await response.json();
|
||
expect(data.flashcards).toBeDefined();
|
||
});
|
||
});
|
||
```
|
||
|
||
## 監控與日誌
|
||
|
||
### 關鍵指標
|
||
- API 回應時間
|
||
- 成功/失敗率
|
||
- Token 使用量
|
||
- 用戶滿意度
|
||
|
||
### 日誌記錄
|
||
```typescript
|
||
import { logger } from '@/lib/logger';
|
||
|
||
logger.info('Gemini API called', {
|
||
userId,
|
||
promptLength: prompt.length,
|
||
responseTime: Date.now() - startTime,
|
||
});
|
||
```
|
||
|
||
## 故障排除
|
||
|
||
### 常見問題
|
||
|
||
1. **API Key 無效**
|
||
- 確認環境變數設置正確
|
||
- 檢查 API Key 是否啟用
|
||
|
||
2. **Rate Limit 錯誤**
|
||
- 實施請求隊列
|
||
- 升級到付費方案
|
||
|
||
3. **回應解析失敗**
|
||
- 改進 prompt 明確性
|
||
- 實施備用解析邏輯
|
||
|
||
4. **生成品質不佳**
|
||
- 調整 temperature 參數
|
||
- 優化 prompt 內容
|
||
- 提供更多範例 |