iT邦幫忙

2026 iThome 鐵人賽

DAY 30
0
Build on Google AI

今天學什麼?30 天用 Google AI 打造智慧學習卡系列 第 30 篇

Day 30|30 天打造 AI 學習卡:成果總結、限制與下一步

  • 分享至 

  • xImage
  •  

來到第 30 天,我終於將幼兒互動認知學習卡正式發布。

正式網站:

幼兒互動認知學習卡(Toddler Learning Cards)

不過,最後一天的工作不只是按下 Publish。

在發布以前,我重新檢查了輸入資料、重新產生功能、瀏覽器 Console、API Response 與後端日誌,確認開發階段使用的除錯資料不會不必要地出現在正式環境。

今天完成的工作包括:

  1. 替產生學習卡功能加入前後端輸入限制。
  2. 修正重新產生按鈕與目前主題不一致的問題。
  3. 使用 Google AI Studio Edit Tool 微調主題按鈕版面。
  4. 移除前端 Prompt 與開發階段的 Debug Log。
  5. 最小化後端 API Response。
  6. 檢查 Network 與瀏覽器 Console。
  7. 使用 Google AI Studio Publish 正式發布 App。

一、發布前,先建立輸入資料限制

原本產生學習卡表單已經提供固定的年齡與卡片數量選項,但如果有人跳過畫面、直接呼叫 API,還是可能送出錯誤或過大的資料。

因此,我先建立前後端一致的輸入規則。

欄位 限制
ageGroup 只能是 3~4 歲、5~6 歲或 7~8 歲
topic 去除前後空白後不可為空,最多 10 個字元,不可包含換行或控制字元
cardCount 只能是整數 3、5 或 7
Request Body 最大 2 KB

前端限制:讓使用者立即知道問題

主題輸入框加入 10 個字元的輸入上限,畫面上也會顯示目前字數:

{Array.from(topic.trim()).length} / 10

送出表單以前,前端會再次檢查:

  • 主題是否為空。
  • 是否超過 10 個字元。
  • 是否包含換行或控制字元。
  • 年齡是否為允許值。
  • 卡片數量是否為 3、5 或 7。

驗證失敗時,畫面會顯示繁體中文錯誤訊息,而且不會呼叫後端 API。

圖 1:Gemini 修改產生學習卡的前後端輸入限制。
Day30 Gemini update input limit

後端限制:不能只相信瀏覽器表單

前端限制可以改善操作體驗,但仍可能被繞過,所以 /api/generate-cards 必須重新驗證一次。

例如主題驗證:

const trimmedTopic = topic.trim();

if (!trimmedTopic || Array.from(trimmedTopic).length > 10) {
  return res.status(400).json({
    error: '輸入資料不符合規定。',
  });
}

卡片數量驗證:

const VALID_CARD_COUNTS = [3, 5, 7];

if (
  typeof cardCount !== 'number' ||
  !Number.isInteger(cardCount) ||
  !VALID_CARD_COUNTS.includes(cardCount)
) {
  return res.status(400).json({
    error: '輸入資料不符合規定。',
  });
}

後端同時將 JSON Request Body 限制為 2 KB。

超過限制時,伺服器會停止處理並回傳 HTTP 413:

{
  "error": "請求內容過大。"
}

只有通過後端驗證的資料,才會進入 Prompt Builder 並呼叫 Gemini API。


二、修正重新產生按鈕的主題文字

接著,我發現一個容易讓使用者困惑的 UI 問題。

操作流程如下:

  1. 先產生「旅遊英文」學習卡。
  2. 切換到「太陽系」。
  3. 畫面上正在瀏覽太陽系卡片。
  4. 底部按鈕卻仍顯示「重新產生學習卡(旅遊英文)」。

問題原因是按鈕文字與實際執行邏輯讀取了不同資料:

  • 按鈕文字讀取上一次產生的 lastGenerateParams。
  • 實際重新產生則優先使用目前的 currentTheme。

圖 2:目前選擇太陽系,重新產生按鈕卻仍顯示旅遊英文。
Day30 UI renew card button text error

將文字與行為統一成目前主題

修改後,先取得目前主題:

const activeTopic =
  currentTheme.generationParams?.topic || currentTheme.name;

按鈕文字改為:

<span>
  重新產生學習卡
  {activeTopic ? `(${activeTopic})` : ''}
</span>

handleRegenerate 也使用相同的 activeTopic:

const targetParams = {
  ageGroup:
    currentTheme.generationParams?.ageGroup ||
    lastGenerateParams?.ageGroup ||
    '3~4 歲幼兒',
  topic: activeTopic.trim(),
  cardCount: validCount,
};

現在切換到哪個主題,按鈕顯示與實際產生內容就會指向同一個主題。

如果目前選擇的是 Preset Theme,重新產生後會建立對應的 Generated Theme,不會覆蓋原本的內建卡片。

圖 3:修正後,重新產生按鈕與目前主題一致。
Day30 Gemini fixed renew button text


三、使用 Edit Tool 完成版面微調

除了在對話區輸入程式修改 Prompt,Google AI Studio 也提供 Edit Tool。

我可以先在預覽畫面選取 ThemeSelector,再輸入:

一行只顯示三個主題按鈕,其餘部分不要修改。

Gemini 便會針對選取的元件調整版面,讓主題按鈕以較整齊的兩排顯示。

這種修改方式對不熟悉元件位置與 CSS 的使用者很方便,但完成後仍然要實際檢查:

  • 手機寬度是否正常。
  • 主題名稱是否被截斷。
  • Generated Themes 增加後是否還能操作。
  • Gemini 是否只修改指定範圍。

圖 4:Google AI Studio 的 Edit Tool。
Day30 Google Ai Studio edit tool
圖 5:選取 ThemeSelector 後,以自然語言調整主題按鈕排列。
Day30 gemini update theme button ui


四、為什麼只移除前端 Console 還不夠?

在開發階段,為了追蹤資料流程,我曾經在前端 Console 顯示:

  • 使用者送出的表單資料。
  • 完整 Prompt。
  • Gemini Structured Response。
  • 轉換後的 LearningCard[]。
  • React State。
  • Generate 與 Regenerate 流程狀態。

這些資料對開發很有幫助,但正式發布後,訪客只要開啟瀏覽器開發工具,就能查看前端 Console。

因此,我先移除不必要的前端 Debug Log,只保留不含 Prompt 與卡片內容的必要錯誤訊息。

不過,進一步檢查後,我發現只整理 Console 還不夠。


五、API Response 原本也會暴露 Prompt

修改以前,後端會將下列資料一起送回瀏覽器:

return res.json({
  success: true,
  prompt,
  data: structuredData,
  response: responseText,
});

即使前端刪除了所有 console.log,訪客仍可從 Network 面板看到:

  • 完整 Prompt。
  • Gemini 原始回應。
  • Structured JSON。

改成最小必要回傳

修改後,API 只回傳畫面真正需要的內容:

return res.json({
  success: true,
  data: structuredData,
});

Prompt Builder 也從前端移除,只保留在後端。

現在前端只送出:

{
  "ageGroup": "5~6 歲幼兒",
  "topic": "浩瀚太空",
  "cardCount": 5
}

伺服器驗證資料、建立 Prompt、呼叫 Gemini,再將結構化卡片送回前端。


六、收斂後端 Server Log

後端 Console 不會直接出現在訪客的瀏覽器,但它可能保存在 Cloud Run 或其他雲端平台的歷史日誌中。

原本後端會輸出:

  • 完整 Prompt。
  • Gemini 完整 Structured JSON。
  • 完整 API 錯誤物件。

修改後不再記錄完整內容,只保留操作指標,例如:

學習卡產生成功
使用模型:gemini-2.5-flash
處理時間:xxxx ms
卡片數量:5

這樣仍能確認服務是否正常,卻不會把使用者輸入與完整卡片內容留在雲端日誌。


七、實際檢查 Network 與 Console

完成修改後,我使用 Chrome DevTools 的 Network 面板重新產生學習卡。

實際 Response 為:

{
  "success": true,
  "data": {
    "cards": [
      {
        "emoji": "☀️",
        "title": "太陽亮晶晶",
        "content": "太陽是我們地球的大燈泡……",
        "question": "太陽會發光又發熱嗎?",
        "answer": "是的,太陽會發光又發熱。"
      }
    ]
  }
}

檢查結果:

  • HTTP Status 為 200 OK。
  • Response 只包含 success 與 data。
  • 沒有完整 Prompt。
  • 沒有原始 responseText。
  • 沒有 API Key。
  • 沒有伺服器 Stack Trace。
  • 回傳卡片數量與 cardCount 相符。
  • 瀏覽器 Console 沒有 App 輸出的 Prompt、卡片 State 或 Structured Response。

Network 中仍然可以看到 Generated Cards,這是正常的,因為瀏覽器必須取得資料才能顯示卡片。

資料最小化不是讓 Network 完全沒有資料,而是只傳送功能真正需要的內容。

圖 6:在 Network 面板確認 API Response 不再包含 Prompt。
Day30 API response remove prompt
圖 7:Console 中已沒有 App 的 Prompt、Structured Response 與 React State。
Day30 移除console

Console 畫面仍可能顯示 React DevTools 或 Chrome 本身的提示,這些不是 Learning Card App 主動輸出的資料。


八、正式 Publish

完成輸入限制、資料回傳整理與實際測試後,我在 Google AI Studio 開啟 Publish 面板,開始發布 App。

發布過程會依序進行:

  • Checking for app description
  • Reviewing features
  • Gathering your app details

圖 8:Google AI Studio 正在發布 App。
Day30 Publish

發布完成後,狀態顯示為:

Ready

Google AI Studio 同時提供正式網址:

https://toddler-learning-cards.ai.studio

圖 9:App 發布成功,狀態顯示 Ready。
Day30 Publish Google Ai

這次發布也讓我第一次完整走過:

開發
  ↓
人工測試
  ↓
輸入限制
  ↓
資料暴露檢查
  ↓
Build
  ↓
Publish
  ↓
正式網址驗證

發布成功不代表後續不需要維護,但它代表這 30 天累積的功能,已經從 AI Studio 預覽畫面,變成可以透過獨立網址開啟的實際 App。


九、Day 30 測試結果

編號 測試項目 結果
D30-01 空白主題不可送出 ✅
D30-02 10 個字元主題可以送出 ✅
D30-03 11 個字元主題被阻擋 ✅
D30-04 含換行或控制字元的主題被阻擋 ✅
D30-05 非法 ageGroup 回傳 HTTP 400 ✅
D30-06 非法 cardCount 回傳 HTTP 400 ✅
D30-07 超過 2 KB 的 Request Body 回傳 HTTP 413 ✅
D30-08 合法條件可正常產生卡片 ✅
D30-09 Network Response 不含 Prompt 與原始 Response ✅
D30-10 Console 不顯示 App 的 Prompt 與卡片 State ✅
D30-11 重新產生按鈕與目前主題一致 ✅
D30-12 專案 Build 成功 ✅
D30-13 Google AI Studio Publish 狀態為 Ready ✅
D30-14 正式網址可以開啟 ✅

十、30 天完成了哪些功能?

學習與互動功能

  • 多組 Preset Themes。
  • 上一張/下一張卡片。
  • 顯示問題與答案。
  • 語音朗讀。
  • 自動播放。
  • 大挑戰模式。
  • 星星獎勵。
  • 幼兒友善的視覺與操作方式。

Gemini 動態生成功能

  • 選擇學習年齡。
  • 自訂學習主題。
  • 選擇 3、5 或 7 張卡片。
  • Server-side Prompt Builder。
  • Structured Output。
  • Loading 與錯誤處理。
  • Generate 與 Regenerate。
  • Generated Theme 自動加入 ThemeSelector。

Human in the Loop

  • 人工檢查 AI 內容。
  • 單張卡片編輯。
  • 使用 themeId + cardId 鎖定編輯目標。
  • 避免鍵盤與自動播放造成卡片儲存錯位。

資料保存與輸出

  • Generated Themes 儲存至 LocalStorage。
  • 重新整理後自動還原。
  • LocalStorage 資料格式驗證。
  • 損壞資料安全降級。
  • 列印學習卡。
  • 另存為 PDF。

發布前整理

  • 前後端輸入驗證。
  • Request Body 大小限制。
  • 移除前端 Prompt Template。
  • 最小化 API Response。
  • 收斂 Server Log。
  • 清除正式環境不需要的 Debug Log。
  • 發布至 Google AI Studio 正式網址。

十一、目前的完整資料流程

使用者選擇年齡、主題、卡片數量
                ↓
前端表單驗證
                ↓
POST /api/generate-cards
                ↓
後端再次驗證輸入資料
                ↓
Server-side Prompt Builder
                ↓
Gemini Structured Output
                ↓
後端只回傳 data.cards
                ↓
轉換為 LearningCard[]
                ↓
加入或更新 AppTheme[]
                ↓
ThemeSelector 顯示 Generated Theme
                ↓
自動儲存至 LocalStorage
                ↓
探索卡/大挑戰/Human Edit/列印 PDF

這條流程也說明了,AI App 不能只有一段 Prompt。

真正可以持續使用的產品,還需要資料驗證、狀態管理、錯誤處理、儲存機制、人工檢查與清楚的使用者介面。


十二、目前仍存在的限制

1. LocalStorage 只存在目前瀏覽器

換裝置、清除瀏覽器資料或改用其他瀏覽器後,Generated Themes 不會自動同步。

開發網址與正式發布網址屬於不同來源時,兩邊的 LocalStorage 也不會共用。

2. AI 內容仍需要人工確認

Structured Output 可以固定資料格式,但不能保證所有知識內容永遠正確。家長與老師使用前仍應檢查內容。

3. 尚未加入 Rate Limit

現在已限制單次輸入資料,但仍無法阻止他人重複送出合法請求。

如果正式網址開始對外大量分享,還需要:

  • API Rate Limit。
  • Gemini API 配額上限。
  • Google Cloud 預算警示。
  • 使用量與錯誤監控。
  • 必要時加入登入或驗證機制。

4. 星星規則尚未完整說明

目前星星會儲存在 toddler_stars,但畫面沒有清楚說明取得規則,也還需要避免重複領取。

5. 大挑戰選項仍可繼續改善

目前使用其他卡片的答案作為干擾選項。雖然已修正 Question 與 Answer 對應問題,未來仍可讓 Gemini 產生更合理的錯誤選項。

6. 尚未建立完整自動化測試

目前主要依靠 Build 與人工操作測試。未來可以加入單元測試、API 測試與端對端測試。


十三、下一步可以做什麼?

如果繼續開發,我會優先處理:

  1. 建立 API Rate Limit 與預算警示。
  2. 增加正式環境監控。
  3. 改善星星獎勵規則。
  4. 為大挑戰加入專用錯誤選項。
  5. 加入刪除與重新命名 Generated Theme。
  6. 將 LocalStorage 升級成帳號與雲端同步。
  7. 增加自動化測試。
  8. 改善鍵盤操作、ARIA 與無障礙體驗。
  9. 發展教師版教材管理與批次匯出功能。

十四、30 天後,我真正學到的是什麼?

這 30 天最大的收穫,不只是完成一個能產生學習卡的 App。

我逐漸學會將模糊需求拆成可以驗證的小問題:

  • 畫面是否正確顯示?
  • State 是否更新到正確資料?
  • AI 回傳格式是否可靠?
  • 編輯時鎖定的是 Index 還是 Card ID?
  • 重新整理後資料是否仍存在?
  • 問題與答案的語意是否一致?
  • Console 與 Network 是否傳送了不必要的資料?
  • AI 說 Build 成功後,我是否真的親自操作驗證?
  • 發布成功後,正式網址是否仍能完成完整流程?

AI 可以協助產生程式、分析問題與提出修改方案,但最終仍需要人決定:

  • 需求是否合理。
  • 修改範圍是否過大。
  • 測試是否足夠。
  • 使用者看到的行為是否符合預期。
  • 哪些限制應該誠實保留。
  • 什麼時候才適合按下 Publish。

從第一張固定內容的學習卡,到現在可以自訂主題、呼叫 Gemini、人工編輯、保存資料、進行挑戰、輸出 PDF,最後發布成可以實際開啟的網站,這個 App 並不是單次 Prompt 的結果,而是 30 天持續觀察、測試與修正的累積。

30 天專題在今天告一段落,但這個 App 的下一個階段,才正要開始。


上一篇
Day 29|不只會動,還要穩定:完成智慧學習卡整合測試
系列文
今天學什麼?30 天用 Google AI 打造智慧學習卡 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言