iT邦幫忙

2026 iThome 鐵人賽

DAY 16
0
Build on Google AI

《30天打造喵語日誌:Gemini API × Vibe Coding 實戰》系列 第 16

Day 16:【Gemini API】前端正式串接 Google Gen AI SDK:告別假資料,賦予貓咪對話靈魂!

  • 分享至 

  • xImage
  •  

從靜態 Mock 到智慧對話的關鍵跨越

在昨天的半程小結中,我們完整檢視了《喵語日誌》的前端畫面與互動元件。雖然聊天室能收發文字與圖片,但先前為了驗證介面流程,送出訊息後都是透過定時器吐出寫死的罐頭字串,貓咪並無法真正理解使用者的喜怒哀樂。

在第 6 天時,我們曾在初版專案中成功驗證了 Gemini API 的結構化輸出概念。而今天我們的核心任務,就是將這顆強大的 AI 核心正式移植並整合進全新的聊天室模組中。我們將透過 Google 官方最新的 @google/genai SDK 與 gemini-3-flash-preview 模型,讓貓咪根據使用者的即時心情與文字,給予專屬、溫暖且充滿靈性的動態回覆。

核心重構:升級 API 服務與動態 Prompt 組裝

要讓 AI 能針對當下的聊天情境做出共鳴,我們需要將使用者點選的 5 種心情標籤(Rough、Low、Okay、Good、Great)化為 Prompt 的一部分。

我們在 src/services/geminiService.ts 中封裝了 analyzeDiary 函式。除了設定溫柔貼心的貓咪陪伴者 System Instruction 之外,更要求模型必須嚴格遵循 responseSchema 規範,輸出包含心情指數(mood_score)、生活場景標籤(lifestyle_label)以及陪伴文字(cat_response)的結構化 JSON。

// src/services/geminiService.ts
import { GoogleGenAI, Type } from '@google/genai';
import type { CatDiaryResponse } from '../types/diary';

const apiKey = import.meta.env.VITE_GEMINI_API_KEY;
const ai = new GoogleGenAI({ apiKey: apiKey || '' });

export async function analyzeDiary(
  userText: string,
  moodKey?: string | null
): Promise<CatDiaryResponse> {
  const systemInstruction = `你是一隻名為「喵喵」的貼心寵物貓兼生活陪伴者。
- 說話口氣可愛、傲嬌又溫暖,每句話結尾請自然帶上「喵~」、「🐾」等貓咪口吻。
- 必須仔細體察使用者的文字與當前心情標籤,給予專屬的共鳴與鼓勵(50~80字以內)。
- 嚴格以 JSON 格式回傳分析結果。`;

  const moodContext = moodKey ? `【使用者選擇的心情標籤:${moodKey}】\n` : '';
  const prompt = `${moodContext}使用者說:「${userText}」`;

  try {
    const response = await ai.models.generateContent({
      model: 'gemini-3-flash-preview',
      contents: prompt,
      config: {
        systemInstruction,
        responseMimeType: 'application/json',
        responseSchema: {
          type: Type.OBJECT,
          properties: {
            mood_score: {
              type: Type.INTEGER,
              description: '1 到 10 的情緒指數',
            },
            lifestyle_label: {
              type: Type.STRING,
              description: '生活場景標籤,例如:美食日常、深夜加班、悠閒午後',
            },
            cat_response: {
              type: Type.STRING,
              description: '帶有貓咪動作描述與溫暖語氣的回應',
            },
          },
          required: ['mood_score', 'lifestyle_label', 'cat_response'],
        },
      },
    });

    const jsonText = response.text;
    if (!jsonText) throw new Error('未取得 Gemini 回應');

    return JSON.parse(jsonText) as CatDiaryResponse;
  } catch (error) {
    console.error('Gemini API 呼叫失敗:', error);
    return {
      mood_score: 5,
      lifestyle_label: '日常陪伴',
      cat_response: '喵嗚…連線好像有點不穩,本喵先去抓個沙發,等一下再跟我說一次好嗎?🐾',
    };
  }
}

透過這種設計,前端既能取得貓咪的口語化回覆,同時還能獲取標準化的生活標籤與情緒分數,為後續寫入雲端資料庫做好最完美的準備。

聊天室串接:非同步 Request 與 Typing Indicator 連動

在完成服務端封裝後,我們回到 ChatContainer.tsx 改造送出事件。

當使用者按下發送或 Enter 鍵時,前端會先將文字氣泡推入對話串,並將 isLoading 設為 true 啟動跳動的打字動畫(Typing Indicator)。接著透過非同步 await analyzeDiary() 發送請求,在收到回傳的 JSON 後,精準提取 result.cat_response 並渲染為貓咪回覆氣泡。

// src/components/ChatContainer.tsx 發送邏輯重構
const handleSend = async () => {
  const trimmed = input.trim();
  if ((!trimmed && !selectedImage) || isLoading) return;

  const currentMood = mood;
  const userMessage: Message = {
    id: Date.now(),
    role: "user",
    text: trimmed,
    image: selectedImage || undefined,
  };

  // 1. 立即將使用者訊息推入畫面
  setMessages((prev) => [...prev, userMessage]);
  setInput("");
  setSelectedImage(null);
  setIsLoading(true);

  try {
    // 2. 向 Gemini 請求真實即時回覆
    const result = await analyzeDiary(trimmed || "(分享了一張照片)", currentMood);
    
    // 3. 渲染貓咪回覆氣泡
    const catReply: Message = {
      id: Date.now() + 1,
      role: "cat",
      text: result.cat_response,
    };
    setMessages((prev) => [...prev, catReply]);
  } catch (error) {
    console.error(error);
  } finally {
    setIsLoading(false);
  }
};

真實動態對話成果驗收

為了驗證模型在不同情緒與生活場景下的回覆表現,我們進行了兩組對照測試:

https://ithelp.ithome.com.tw/upload/images/20260816/20178708ocnH6TZw74.png

▲ 情境一:開心美食分享(心情 Great)

在選擇「Great」並傳送「今天吃了好吃的義大利麵~」時,貓咪回傳了傲嬌的回應:「看到你吃得這麼開心,喵喵也跟著高興起來了喔!雖然我不能吃義大利麵,但看著你滿足的樣子,就勉為其難讓你多摸兩下吧。既然心情這麼好,下次也要記得買罐罐犒賞我喵~🐾」。

https://ithelp.ithome.com.tw/upload/images/20260816/20178708sOdrXj2n9I.png

▲ 情境二:疲憊家務日常(心情 Low)

在選擇「Low」並傳送「今天在家大掃除,好累喔...」時,貓咪則給予溫柔安撫:「哎呀,看你忙得滿頭大汗,家裡確實變乾淨了,但也要照顧好自己嘛!快過來沙發坐下,喵喵勉為其難讓你摸摸肚皮消消疲勞喔。辛苦了,剩下的明天再說吧!喵~🐾」。

兩次回覆不僅精準抓取了生活中的場景細節,更根據心情標籤給予最適切的情感支持,正式宣告靜態假資料退役!

值得一提的是,目前畫面上雖然已能附帶圖片預覽,但 AI 是依據文字與心情標籤進行推理。如果使用者只傳送一張照片而不輸入任何文字,貓咪目前還無法親眼辨識圖片內容,這也為明天的多模態改造留下了絕佳的突破點!

這次實作的心得

將靜態畫面真正接上生成式 AI 的那一刻,是整個開發過程中成就感最豐沛的瞬間之一。

過去在前端開發中,要做出一個會理解語意的對話系統門檻極高;如今借助 Google Gen AI SDK 與結構化輸出技術,我們不僅能用極少的程式碼賦予 App 靈魂,還能同時兼顧前端型別安全與資料格式的精準度。這種讓介面與智慧模型快速融合的能力,正是現代 Web 全端開發最迷人之處。

結語

今天我們順利告別假資料,完成了文字與心情標籤的即時對話串接!明天我們將更進一步解鎖多模態能力——【多模態串接】實作圖文同時輸入!我們將把前端壓縮後的 Base64 照片直接打包送給 Gemini,讓貓咪不僅能聽懂你說的心事,還能親眼看懂你盤子裡的美食與生活照片!

明天見,喵~ 🐾


上一篇
Day 15:【第二週小結】前端靜態畫面全面完工:全功能 Demo 展示與 Vibe Coding 開發回顧
系列文
《30天打造喵語日誌:Gemini API × Vibe Coding 實戰》16
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言