iT邦幫忙

2026 iThome 鐵人賽

DAY 26
0
Build on Google AI

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

Day 26|不只海洋動物:把 Learning Card 擴充成不同學習主題

  • 分享至 

  • xImage
  •  

昨天 Day 25,我替 Learning Card App 加入了 Human Edit,讓使用者可以直接修改 Gemini 產生的教材。

原本以為接下來只要繼續測試不同主題,就能確認這套 Learning Card Generator 是否具有擴充性。

結果在測試過程中,意外發現一個之前一直沒有處理的問題。

這次我使用 Gemini 產生:

學習對象:7~8 歲兒童
學習主題:太陽系
卡片數量:5

Gemini 成功回傳五張 Learning Cards,畫面也正常顯示「太陽系 1 / 5」。

但仔細看上方的 ThemeSelector,目前選取的竟然還是「海洋探險」。

Day26 new theme cards solar no show

更奇怪的是,當我切換到「森林陸地」,再切回「海洋探險」後,剛才產生的太陽系 Learning Cards 就消失了。

Day26 修改前的卡片覆蓋測試

這才發現:

Gemini 已經可以產生不同主題,但 App 目前只能顯示新主題,還不能真正管理新主題。

Day26測試舊太陽系卡片主題會被覆蓋

所以今天決定先不增加新的 AI 功能,而是整理目前的 Theme State,讓預設主題和 Gemini 動態產生的主題可以一起管理。


今天想完成什麼?

目前 App 已經支援:

  • Gemini Generate
  • Structured Output
  • Regenerate
  • Human Edit
  • ThemeSelector

但這些功能之間還存在一個問題。

原本 ThemeSelector 使用的是 App 內建的預設主題資料,例如:

ThemeSelector
├─ 海洋探險
├─ 森林陸地
├─ 車車探險
├─ 水果樂園
└─ 恐龍世界

而 Gemini 動態產生的 Learning Cards,則是直接更新目前顯示的 React State。

因此目前比較像:

Preset Themes
      ↓
ThemeSelector
      ↓
LearningCard[]

Gemini Generate
      ↓
LearningCard[]
      ↓
React State

兩套資料來源沒有真正整合。

今天希望調整成:

Preset Themes
       +
Generated Themes
       ↓
Unified Theme State
       ↓
ThemeSelector
       ↓
Selected Theme
       ↓
LearningCard[]
       ↓
React UI

讓 Gemini 產生的新主題,也能正式成為 App 裡可以切換的 Theme。

這次依然維持 MVP 原則,先使用 React State 管理資料,不加入 Firebase 或 LocalStorage。


Test Before|先確認目前的問題

在修改程式之前,我先重新測試一次原本的行為。

測試條件:

{
  "ageGroup": "7~8 歲兒童",
  "topic": "太陽系",
  "cardCount": 5
}

Gemini API 使用 gemini-2.5-flash,並透過 Structured Output 回傳資料。

Console Log 顯示:

Submit
↓
Loading
↓
Gemini API
↓
Success
↓
Gemini Structured Response
↓
response.cards
↓
LearningCard[]
↓
React State

這次成功產生五張 Learning Cards:

# Emoji Title
1 🪐 認識太陽系
2 ☀️ 熱情的太陽
3 🌍 美麗的地球
4 🌕 地球的好朋友
5 🚀 其他的行星

前端也正常替每張 Card 補上 id。

所以可以確認,Gemini API、Structured Output 與 LearningCard[] 的轉換流程都正常。

問題不在 Gemini,而是在 App 的 State Management。

問題一:ThemeSelector 沒有新主題

畫面明明顯示:

✨ 太陽系 1 / 5

但上方 ThemeSelector 仍然選取:

🐳 海洋探險

而且選單裡完全沒有「太陽系」。

也就是說,目前顯示的 Learning Cards 與 ThemeSelector 的選取狀態並不一致。

問題二:切換主題後資料消失

接著操作:

Gemini 產生「太陽系」
↓
顯示太陽系 1 / 5
↓
點擊「森林陸地」
↓
顯示森林陸地預設 Cards
↓
點擊「海洋探險」
↓
顯示海洋探險預設 Cards

這時候原本的太陽系 Learning Cards 已經無法再切回。

原因是 Gemini 產生的資料沒有被加入 ThemeSelector 所管理的主題集合。

當使用者切換預設主題時,原本顯示的 Cards 就被預設資料取代。

這也成為今天要修正的核心問題。


這次怎麼請 Gemini 修改?

這次一樣使用 Google AI Studio 的 Gemini 3.8 Flash 協助修改 App。

不過,跟前幾天相比,我把 Prompt 寫得更精簡。

不再解釋 Day 23、Day 25 做過什麼,而是直接告訴 Gemini:

請檢查目前 React + TypeScript Learning Card App 的程式碼,修正 ThemeSelector 與 Gemini 動態產生的 Learning Cards 之間的資料管理問題。

主要需求包含:

  1. 統一管理 Preset Themes 與 Generated Themes。
  2. Gemini 產生新主題後,自動加入 ThemeSelector。
  3. 切換主題時保留其他 Themes 的 Cards。
  4. Human Edit 只更新目前主題的 Card。
  5. Regenerate 只更新對應的 Generated Theme。
  6. 保留原本功能,不加入持久化儲存。

同時限制 Gemini 不要修改 Prompt Template、Structured Output Schema 或 Gemini API。

因為今天要處理的是前端資料管理,不是 AI 生成流程。

Day26 new prompt


Gemini 修改了什麼?

這次 Gemini 3.8 Flash 執行:

Ran for 390s

Edited 4 files

src/types.ts
src/components/ThemeSelector.tsx
src/LearningCardApp.tsx
src/components/QuizMode.tsx

Built

總共花了 390 秒,大約 6 分 30 秒。

這也是目前幾次修改中,執行時間比較長的一次。

根據 Gemini 的修改摘要,這次主要完成了三件事。

1. 建立統一的 AppTheme

原本 App 主要管理目前顯示的:

LearningCard[]

現在則新增了 AppTheme 的概念。

每個 Theme 不只是名稱,還包含:

AppTheme
├─ id
├─ name
├─ emoji
├─ source
├─ cards
└─ generationParams

其中:

  • id:辨識不同 Theme。
  • name:顯示主題名稱。
  • emoji:顯示主題圖示。
  • source:區分預設或 AI 產生。
  • cards:這個 Theme 對應的 Learning Cards。
  • generationParams:保留產生條件,供 Regenerate 使用。

這次最重要的變化,就是每個 Theme 都開始管理自己的 Cards。

2. 統一管理 Preset 與 Generated Themes

現在概念上變成:

AppTheme[]
│
├─ 海洋探險 [preset]
│   └─ LearningCard[]
│
├─ 森林陸地 [preset]
│   └─ LearningCard[]
│
├─ 車車探險 [preset]
│   └─ LearningCard[]
│
├─ 太陽系 [generated]
│   └─ LearningCard[]
│
└─ 人體器官 [generated]
    └─ LearningCard[]

Gemini 產生新主題時,不再只是替換目前顯示的 Cards,而是將它加入 Themes State。

接著透過選取的 Theme,決定畫面要顯示哪一組 Learning Cards。

3. 讓原本功能配合新的 Theme State

這次 Gemini 除了修改 ThemeSelector,也調整了 LearningCardApp.tsx 與 QuizMode.tsx。

主要是讓原本的卡片探索、大挑戰、Human Edit 與 Regenerate,能配合新的 Theme 資料結構。

Gemini 回報 Build 成功,接下來就要實際測試功能是否真的符合預期。

Day26 Gemini update files


Test A|Generated Theme 會出現在 ThemeSelector 嗎?

第一個測試沿用修改前的條件:

學習對象:7~8 歲兒童
學習主題:太陽系
卡片數量:5

Day26 TestA產生卡片7-8y solar 5cards

這次 Gemini 產生成功後,ThemeSelector 終於出現了:

太陽系

而且會自動選取新產生的主題。

Learning Card 畫面也正確顯示:

太陽系 1 / 5

不再發生:

ThemeSelector:海洋探險
Learning Cards:太陽系

這種選取狀態與實際內容不一致的問題。

Test A:PASS ✅

Day26 TestA測試新增卡片主題solar


Test B|切換主題後,太陽系還在嗎?

接下來測試最初發現的問題。

操作流程:

太陽系
↓
森林陸地
↓
太陽系

修改前,只要切換到森林陸地,原本的太陽系 Cards 就無法再切回。

但這次切換回太陽系後,原本五張 Learning Cards 仍然存在。

沒有重新呼叫 Gemini API,也不需要重新產生。

Test B:PASS ✅

這代表現在切換 Theme,已經不再等於直接覆蓋其他主題的資料。


Test C|Human Edit 的修改會不會消失?

接著確認昨天才完成的 Human Edit。

因為現在每個 Theme 都有自己的 Cards,所以編輯功能也必須更新正確的 Theme。

測試流程:

太陽系
↓
編輯其中一張 Learning Card
↓
Save
↓
切換森林陸地
↓
切回太陽系
↓
檢查修改內容

結果修改後的內容仍然保留。

Test C:PASS ✅

這代表 Human Edit 不只是修改目前畫面,而是能將修改結果保留在所屬 Theme 的資料中。

目前的資料關係也更清楚:

Selected Theme
↓
Theme.cards
↓
Edit Single Card
↓
Save
↓
更新目前 Theme
↓
其他 Themes 不受影響

Test D|如果連續產生兩個新主題呢?

原本只打算測試太陽系,但做到這裡,我又想到另一個問題。

如果 App 只能保留一個 Generated Theme,那其實還是不算真正的多主題管理。

所以我又額外新增了一個主題:

人體器官

這次操作順序是:

Generate:太陽系
↓
ThemeSelector 新增太陽系
↓
Generate:人體器官
↓
ThemeSelector 新增人體器官

結果兩個新主題都成功顯示在 ThemeSelector 最上方。

Day26 新增第二組新卡片 3-4y人體器官3張

也就是:

ThemeSelector
│
├─ 人體器官 [generated]
├─ 太陽系 [generated]
├─ 海洋探險 [preset]
├─ 森林陸地 [preset]
└─ ...

這次不只是成功新增一個動態 Theme,而是確認 App 可以同時管理多個 Generated Themes。

Test D:PASS ✅


Test E|Regenerate 會不會更新錯誤的 Theme?

最後再補一個回歸測試。

因為原本 Day 23 的 Regenerate,是針對目前顯示的 Learning Cards 重新產生。

但現在 App 已經有多個 Generated Themes。

所以 Regenerate 必須知道:

這次重新產生的結果,應該更新哪一個 Theme?

這次測試先建立:

太陽系
人體器官

接著切換回「人體器官」,再按下 Regenerate。

預期:

人體器官
↓
Regenerate
↓
Gemini API
↓
新的 LearningCard[]
↓
更新人體器官 Theme

太陽系
↓
保持原本資料

實際測試結果符合預期。

人體器官的 Cards 成功更新,而太陽系仍然保留原本內容。

Test E:PASS ✅

這也確認了 generationParams 與 Theme State 的整合,至少在這次測試情境中可以正常運作。


五個測試結果

今天的測試整理如下:

Test 驗證內容 結果
Test A Generated Theme 加入 ThemeSelector ✅ PASS
Test B 切換主題後,原本 Cards 保留 ✅ PASS
Test C Human Edit 修改後,切換主題仍保留 ✅ PASS
Test D 同時新增太陽系、人體器官兩個主題 ✅ PASS
Test E Regenerate 只更新指定 Theme ✅ PASS

這次沒有額外測試同名 Topic 再次 Generate 的情境,因此先不把它列為已驗證功能。

不過,Day 26 最主要的多主題管理問題已經解決。


這次沒有加入 Firebase 或 LocalStorage

這次還有一個刻意保留的限制。

目前所有 Generated Themes 都只存在 React State 中。

也就是:

Generate 太陽系
↓
Theme State
↓
切換森林陸地
↓
切回太陽系
↓
資料仍然存在

但如果重新整理網頁:

Refresh
↓
React State 重新初始化
↓
Generated Themes 消失

目前這是可以接受的。

因為今天要解決的是:

同一次 App Session 中,如何管理不同主題的 Learning Cards?

而不是:

如何永久保存使用者產生的教材?

後者才需要進一步考慮 LocalStorage、Firebase 或資料庫。

如果今天直接把 Persistence 一起加入,開發範圍就會變得太大。

所以這次先把 State Management 整理好。


今天完成的資料流程

回頭看這次修改前後的差別。

Before

Gemini Generate
↓
Structured Output
↓
LearningCard[]
↓
setCards()
↓
React UI

這個流程可以正常顯示 Gemini 產生的資料。

但它主要只管理「目前要顯示哪些 Cards」。

After

Gemini Generate
↓
Structured Output
↓
LearningCard[]
↓
新增 / 更新 Generated Theme
↓
AppTheme[]
↓
ThemeSelector
↓
selectedThemeId
↓
Selected Theme.cards
↓
React UI

現在 App 不只知道目前要顯示哪些 Cards,也開始知道:

  • 目前有哪些 Themes?
  • 哪些是 Preset Themes?
  • 哪些是 Generated Themes?
  • 每個 Theme 有哪些 Cards?
  • Human Edit 應該更新哪個 Theme?
  • Regenerate 應該重新產生哪個 Theme?

這也是今天真正完成的事情。


今天最大的收穫

原本我以為 Day 26 只要繼續測試不同的 Prompt Topic。

例如:

海洋動物
交通工具
太陽系
人體器官

但其實前面幾天就已經證明,Gemini 可以使用相同的 Prompt Template 和 Structured Output Schema,產生不同主題的 Learning Cards。

真正還沒完成的是 App 自己的資料管理。

這次才發現:

AI 能產生不同主題,不代表 App 已經具備管理不同主題的能力。

一開始使用單一 LearningCard[] 很合理。

因為當時 App 只需要顯示一組卡片。

但後來陸續加入:

Generate
Regenerate
Human Edit
ThemeSelector
Multiple Topics

原本的 State 結構就開始不夠用了。

所以這次不是為了使用新技術而重構,而是實際遇到問題後,才把資料結構從 Card 層級提升到 Theme 層級。

這也讓我覺得,MVP 開發不一定需要一開始就把所有架構設計完整。

有時候是功能逐漸增加、測試發現問題,再回頭調整資料模型。

而這次 Gemini 主要協助修改的是 React State Management,並沒有增加新的 AI 功能。

但對整個 Learning Card App 來說,反而是很重要的一次調整。


明天預告

今天完成了 Learning Card App 的多主題管理功能。

現在不論是原本的預設主題,還是 Gemini 動態產生的「太陽系」、「人體器官」,都可以透過 ThemeSelector 自由切換。

而且 Human Edit 和 Regenerate 也能正確更新對應的 Theme,不會影響其他主題的資料。

不過,目前還有一個問題:

如果重新整理網頁,Gemini 產生的 Learning Cards 就會消失。

因為目前所有 Generated Themes 都只儲存在 React State 中,重新整理後,State 就會重新初始化。

對家長或老師來說,如果花時間產生教材、修改內容,卻因為重新整理頁面而失去資料,使用上還是不太方便。

所以明天打算使用瀏覽器內建的 LocalStorage,讓 App 可以保存 Gemini 產生的學習主題與編輯後的內容。

這次不需要加入 Firebase 或資料庫,先從最簡單的本機資料保存開始。

希望完成後,即使關閉網頁再重新開啟,也能找回之前產生的 Learning Cards。

Day 27|重新整理也不怕:使用 LocalStorage 保存 AI 學習卡


上一篇
Day 25|AI 寫完,人來決定:加入 Learning Card 編輯功能
下一篇
Day 27|重新整理也不怕:使用 LocalStorage 保存 AI 學習卡
系列文
今天學什麼?30 天用 Google AI 打造智慧學習卡 共 28 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言