昨天 Day 22,我回頭補強並測試 Gemini API 的 Loading 與 Error Handling。
目前 Learning Card 已經可以:
Form Data
→ Prompt Template
→ Gemini API
→ Structured Output
→ LearningCard[]
→ React State
→ UI
API 發生錯誤時,也不會直接把原本的 Learning Cards 清空。
不過實際使用後又出現一個很自然的需求:
Gemini 成功產生了,但如果我不喜歡這一組內容呢?
難道要重新回到表單,再選一次年齡、輸入主題、選擇卡片數量嗎?
所以今天要加入一個很簡單,但更接近實際產品使用情境的功能:
重新產生 Learning Card。
今天的目標不是修改 Prompt,也不是要求 Gemini 一定產生完全不同的內容。
而是讓 App 記住上一次成功生成的條件:
ageGroup
topic
cardCount
使用者只要按下「重新產生」,就可以:
目前 Learning Cards
↓
重新產生
↓
Previous Generate Options
↓
Gemini API
↓
New LearningCard[]
↓
React State
↓
更新 UI
也就是把原本一次性的 Generate,變成可以反覆操作的功能。
這次一樣使用 Google AI Studio 的 Gemini 3.8 Flash。
我要求它保留目前已經完成的:
LearningCard Type只加入「重新產生」功能,不提前做編輯卡片、歷史紀錄、Firebase 等其他功能。
Gemini 執行結果:
Gemini 3.8 Flash
Ran for 193s
Edited 1 file
src/LearningCardApp.tsx
Built
這次只修改 LearningCardApp.tsx。



這次很重要的一個改動是 lastGenerateParams。
當 Gemini 成功產生 Learning Cards 後,除了更新 cards,App 也會記住這次使用的:
ageGroup
topic
cardCount
概念上變成:
Generate Success
↓
LearningCard[]
↓
React State
同時保存
↓
lastGenerateParams
這樣使用者按下「重新產生」時,就不需要再回到 Form 重新輸入一次。
第一組測試我使用:
學習對象:5~6 歲幼兒
學習主題:交通工具
卡片數量:3

第一次正常產生後得到:
Generation #1
🚗 小汽車
🚌 公車
✈️ 飛機



整條流程跟前幾天一樣:
Submit
→ Loading
→ Gemini Structured Response
→ response.cards
→ LearningCard[]
→ React State
接下來不修改任何條件,直接按下:
重新產生學習卡
Console 顯示:
Regenerate
Previous Generate Options:
{
"ageGroup": "5~6 歲幼兒",
"topic": "交通工具",
"cardCount": 3
}
Loading
這表示 App 的確記住了上一輪成功產生時的條件。

重新產生後,得到:
Generation #2
🚗 小汽車
🚌 大巴士
🚂 火車
兩次結果放在一起:
| 第一次產生 | 重新產生 | |
|---|---|---|
| Card 1 | 🚗 小汽車 | 🚗 小汽車 |
| Card 2 | 🚌 公車 | 🚌 大巴士 |
| Card 3 | ✈️ 飛機 | 🚂 火車 |
可以看到結果不是「全部換掉」,也不是「完全一樣」。
例如「小汽車」兩次都有出現,但 Content 已經不同;第三張則從「飛機」變成「火車」。
這次我沒有修改 Prompt,也沒有特別要求:
不要跟上一組內容重複
只是把相同的學習條件再次送給 Gemini。
所以「重新產生」比較準確的理解是:
使用相同條件,再進行一次新的生成。
它不代表下一次一定會得到完全不同的內容。
重新產生後,Gemini 回傳的資料仍然維持:
{
"cards": [
{
"emoji": "🚗",
"title": "小汽車",
"content": "...",
"question": "...",
"answer": "..."
}
]
}
接著前端一樣走:
Gemini Structured Response
↓
response.cards
↓
LearningCard[]
↓
React State
而 id 仍然是 Frontend 自己補上。
第一次產生的 ID:
gemini-card-1791365860452-1
重新產生後則變成:
gemini-card-1791366431917-1
所以 Day 21 建立的 Data Contract 沒有因為 Regenerate 被改掉:
Gemini 負責教材內容,App 負責自己的 UI 資料。
這次測試還剛好遇到一個很真實的情況。
重新產生途中,Backend 出現:
503
This model is currently experiencing high demand.
而且可以看到 Server 嘗試其他候選模型。
不過最後前端仍然取得新的 Structured Response,Learning Cards 也成功更新。
由於這次 Console 裡不同模型的 Log 有些交錯,我先不判斷最後究竟是哪個候選模型完成回應。
但至少再次證明 Day 22 遇到的問題不是假設:
AI API 本來就可能遇到暫時不可用的情況。
也因此 Loading、Error Handling,以及不要在 Request 還沒成功前清掉舊資料,都很重要。
一開始我以為 Regenerate 大概就是:
按按鈕
→ 再呼叫一次 Gemini
但真的放進 App 後,其實還要考慮原本畫面的 State。
例如使用者可能正在:
交通工具 3 / 3
甚至已經把答案展開。
新的 Learning Cards 成功回來後,畫面應該重新從第一張開始,而不是保留上一組卡片的位置。
因此成功重新產生後,除了:
setCards(newCards)
還需要處理:
currentIndex → 0
showAnswer → false
讓新的一組 Learning Cards 從乾淨的狀態重新開始。
現在 Learning Card 已經有兩條生成路徑。
第一次產生:
Form Data
↓
Generate
↓
Gemini API
↓
Structured Output
↓
LearningCard[]
↓
React State
重新產生:
lastGenerateParams
↓
Regenerate
↓
Gemini API
↓
Structured Output
↓
New LearningCard[]
↓
Replace React State
而且重新產生期間仍然保留原本的 Cards,等新的 Response 成功後才更新。
從使用者角度來看,只是多了一個「重新產生」按鈕;但從資料流程來看,App 已經開始需要記住前一次 AI 操作的 Context。
Day 23 最有意思的地方,是我原本以為:
「重新產生」=「再呼叫一次 API」。
實際做完才發現,還需要考慮:
上一次的生成條件
Loading
舊資料是否保留
新的 LearningCard[]
Frontend ID
React State
目前卡片 Index
答案顯示狀態
而這次測試也看到另一個生成式 AI 很典型的特性:
相同 Prompt、相同條件,再產生一次,不代表結果一定完全不同。
這也是為什麼「重新產生」比較像是再給 Gemini 一次生成機會,而不是保證得到完全不同的答案。
現在使用者已經可以:
產生 Learning Cards
↓
不滿意
↓
重新產生
但這又帶出下一個更重要的問題。
Gemini 產生的教材內容,真的可以直接拿來給孩子使用嗎?
前面我們一直在確認:
但還沒有真正回頭檢查:
AI 產生的教材內容本身,到底好不好?
明天開始把注意力從「程式能不能跑」,拉回「教材內容能不能用」。