dramaling-vocab-learning/docs/03_development/api/gemini-integration.md

6.7 KiB
Raw Blame History

Gemini AI API 整合文檔

概述

本文檔說明如何整合 Google Gemini API 來實現 AI 詞卡生成功能。

API 設置

1. 取得 API Key

  1. 訪問 Google AI Studio
  2. 點擊 "Create API Key"
  3. 選擇專案或建立新專案
  4. 複製生成的 API Key

2. 安裝 SDK

npm install @google/generative-ai

3. 環境變數配置

GEMINI_API_KEY=your_api_key_here

API 實作

基礎設置

// 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"
});

詞卡生成功能

// 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 模板

const FLASHCARD_PROMPT = `
你是一個專業的英語教學助手,專門幫助台灣學生透過美劇學習英文。
請根據以下內容生成詞卡:

輸入文本:{input_text}

請生成5個最重要的詞彙學習卡片每個卡片包含
1. 英文單字或片語
2. 中文翻譯
3. 詞性和用法說明
4. 原文例句(從輸入文本中提取)
5. 額外例句(創造一個相似情境的例句)
6. 使用情境說明(什麼場合使用)
7. 難度等級1-51最簡單

請以 JSON 格式回覆,格式如下:
{
  "flashcards": [
    {
      "word": "英文單字或片語",
      "translation": "中文翻譯",
      "partOfSpeech": "詞性",
      "usage": "用法說明",
      "originalExample": "原文例句",
      "additionalExample": "額外例句",
      "context": "使用情境",
      "difficulty": 難度數字
    }
  ]
}
`;

不同類型的 Prompt

1. 美劇對話分析

const DRAMA_DIALOGUE_PROMPT = `
分析以下美劇對話,提取日常對話中的重要表達:
- 重點關注俚語、慣用語、口語表達
- 解釋文化背景和使用場合
- 標註正式程度(非常口語/口語/中性/正式)
`;

2. 主題學習

const TOPIC_LEARNING_PROMPT = `
生成關於「{topic}」主題的詞彙卡片:
- 包含該主題最常用的詞彙
- 提供實用的搭配詞和片語
- 給出真實對話場景的例句
`;

3. 語法重點

const GRAMMAR_FOCUS_PROMPT = `
分析文本中的語法結構:
- 識別重要的語法模式
- 解釋語法規則和例外
- 提供類似結構的變化練習
`;

回應解析

JSON 解析函數

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);
  }
}

錯誤處理與重試

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 與配額管理

請求限制

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');
  }
}

配額追蹤

// 記錄用戶使用量
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
  • 實施內容過濾
  • 驗證用戶輸入

測試範例

單元測試

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 () => {
    // 測試超過限制的請求
  });
});

整合測試

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 使用量
  • 用戶滿意度

日誌記錄

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 內容
    • 提供更多範例