昨天 Day 24,我暫時停下開發新功能,實際檢查 Gemini 產生的 15 張 Learning Cards。
結果發現,Structured Output 雖然可以確保每張卡都有固定格式:
emoji
title
content
question
answer
卻不能保證內容一定適合直接拿來當教材。
實際 Review 後,我看到了幾種問題:
Focus
→ 一張卡可能放進太多概念
Factual Accuracy
→ 知識內容可能不夠精確
Oversimplification
→ 為了容易理解而簡化過頭
所以昨天最後得到一個很重要的結論:
AI Output 是 Draft,而不是 Ground Truth。
但這也帶出下一個問題。
如果 Gemini 產生 5 張 Learning Cards,其中 4 張都沒問題,只有 1 張需要調整,我真的需要把整組重新產生嗎?
今天決定把 Human Review 再往前推一步:讓使用者可以直接編輯 AI 產生的 Learning Card。
目前 App 的主要資料流程已經是:
Form Data
↓
Prompt Template
↓
Gemini API
↓
Structured Output
↓
LearningCard[]
↓
React State
↓
UI
Day 23 又加入 Regenerate,所以不喜歡整組內容時,可以使用原本的條件重新產生。
但今天要處理的是另一種情況:
5 張 Learning Cards
↓
Human Review
↓
其中 1 張需要修改
↓
只修改這一張
因此今天的目標很單純:
AI Generated Draft
↓
Human Review
↓
Edit Single Card
↓
Save
↓
Update LearningCard[]
↓
React State
↓
UI
而且這次要做的是 Human Edit。
不是再請 AI 幫我修改。
這次繼續使用 Gemini 3.8 Flash 協助修改 App。
我把 Scope 限制得很明確,只要求加入:
emoji / title / content / question / answer
同時也明確要求不要加入:
AI Rewrite
新增 / 刪除 Card
Firebase
LocalStorage
歷史版本
Undo / Redo
Approve / Reject
自動儲存
另外,前面 Day 16~21 已經逐漸確立一件事:
Gemini Generated Fields
├─ emoji
├─ title
├─ content
├─ question
└─ answer
App Managed Field
└─ id
所以這次也要求 id 不提供編輯。
Human Edit 可以修改教材內容,但不應該破壞 App 自己管理資料所需要的識別資訊。

這次 Gemini 3.8 Flash 執行:
Ran for 188s
Edited 1 file
src/LearningCardApp.tsx
Built

只修改了一個檔案,沒有去動:
Prompt Template
Structured Output Schema
LearningCard type
Gemini API
修改完成後,原本單純顯示 Learning Card 的畫面多了一個「編輯」按鈕。
流程變成:
Display Mode
↓
點擊「編輯」
↓
Edit Mode
↓
取消 / 儲存修改
進入 Edit Mode 後,可以看到:
Emoji
Title
Content
Question
Answer
五個欄位。
而 id 沒有出現在編輯介面。
這也符合目前 App 的資料分工。


這也是今天實作後,我覺得很有意思的地方。
如果使用者只是覺得:
「這個標題太長,我想改短一點。」
其實完全沒有必要走:
Human
↓
Prompt
↓
Gemini API
↓
Structured Output
↓
重新產生
直接:
Human
↓
Edit
↓
React State
反而更簡單。
生成式 AI 可以負責快速建立第一版 Draft,但最後的內容控制權還是在使用者手上。
所以今天雖然是在做 AI App,卻完全沒有增加新的 AI API 功能。
接著開始實際測試。
這次設定:
學習對象:7~8 歲兒童
學習主題:太陽系
卡片數量:5

Gemini 成功產生:
1. 🌌 認識太陽系
2. ☀️ 亮亮的太陽
3. 🪐 八大行星
4. 🌍 我們住的地球
5. 🌕 地球的好朋友月亮

這次選擇第 4 / 5 張:
🌍 我們住的地球
進行 Human Edit。
原本內容是:
Title:
我們住的地球
Content:
地球是我們居住的地方,它是八大行星中的一顆。
地球上有很多水和空氣,所以有生命喔!
Question:
我們住在哪顆行星上?
Answer:
地球。
點擊「編輯」後,畫面成功進入 Edit Mode。

可以修改:
Emoji
Title
Content
Question
Answer
同時提供:
取消
儲存修改
而 id 並沒有出現在畫面中。
這次先做一個最簡單的修改。
把:
我們住的地球
改成:
地球
其他 Content、Question、Answer 都保持不變。
接著按下:
儲存修改
畫面立即離開 Edit Mode。

原本:
🌍 我們住的地球
成功變成:
🌍 地球
其他內容則維持原本資料。
因此 Test A:
PASS ✅

整個流程成功走完:
LearningCard
↓
Edit Mode
↓
Human Edit
↓
Save
↓
React State 更新
↓
Display Mode
↓
顯示修改後資料
這也代表使用者不需要為了一個小修改,把另外四張正常的 Learning Cards 一起重新產生。
接下來測試另一個重要情境。
今天不是要 Replace 整組 Learning Cards,而是:
只修改目前這一張。
儲存第 4 張後,我再切換其他 Learning Cards 檢查。
結果其他卡片都保持原本內容,只有剛才編輯的 Card 被更新。
因此 Test B:
PASS ✅
目前可以理解成:
LearningCard[]
↓
找到目前 Card
↓
更新這一張
↓
其他 Cards 保持不變
↓
Updated LearningCard[]
這正是我今天需要的行為。
有 Save,當然也要測 Cancel。
這次進入 Edit Mode 後先修改內容,但不按「儲存修改」,而是直接按:
取消
結果也符合預期:
Edit Mode
↓
修改 Draft
↓
Cancel
↓
離開 Edit Mode
↓
原本 LearningCard 保持不變
尚未儲存的修改不會寫回原本的 Cards State。
因此 Test C:
PASS ✅
這也代表 Edit Mode 中正在修改的 Draft,和真正顯示中的 Learning Card 資料可以分開處理。
只有按下 Save,修改才正式生效。
今天實際測試結果:
| Test | 驗證內容 | 結果 |
|---|---|---|
| Test A | Edit → Save → UI 顯示修改內容 | ✅ PASS |
| Test B | 修改單張 Card,其他 Cards 不受影響 | ✅ PASS |
| Test C | Cancel 後不寫入修改內容 | ✅ PASS |
到這裡,基本的 Human Edit Workflow 就完成了。
Day 24 我一直在談 Human Review。
但如果 App 只能讓使用者看到:
「嗯,這張 AI 好像寫得不太對。」
然後什麼都不能做,那 Human-in-the-loop 其實還不完整。
現在流程終於變成:
AI Generated Draft
↓
Human Review
↓
Human Edit
↓
Final Learning Card
使用者不只是負責檢查 AI。
還可以真正介入 AI 的輸出,修改內容,再決定最後留下什麼。
對目前這個 Learning Card App 來說,我覺得這比再多加一個 AI Feature 更重要。
做到這裡,我也開始重新思考一件事。
既然這是一個使用 Gemini 的 App,很容易直覺地認為:
每個功能是不是都應該再交給 Gemini?
例如:
「幫我改寫這張」
「幫我修正這張」
「幫我重新產生這張」
當然都可以做。
但如果使用者只是想把:
我們住的地球
改成:
地球
普通的 React Edit Form 反而是更直接的解法。
所以目前 App 的角色逐漸變得清楚:
Gemini
↓
快速產生教材 Draft
Human
↓
Review
React Edit
↓
人工修改
React State
↓
保存目前 App 中的結果
AI 不一定需要負責整個 Workflow。
原本做到這裡,我以為今天差不多可以收工了。
結果在測試不同主題時,又發現目前 App 還有一個資料狀態問題。
這次我使用 Gemini 產生:
7~8 歲兒童
太陽系
5 張
產生完成後:
Gemini
↓
太陽系 LearningCard[]
↓
React State
↓
UI 正常顯示
Human Edit 也可以正常修改。
但是,如果這時候去點上方原本 App 就存在的 ThemeSelector,例如切換到:
森林探險
原本剛產生的「太陽系」Learning Cards 就不見了。
回頭看目前 App 的設計,其實原因很清楚。
現在同時存在兩種 Learning Card 資料來源。
一種是 App 原本就有的預設主題:
ThemeSelector
├─ 海洋動物
├─ 森林探險
├─ 交通工具
└─ ...
這些主題都有事先準備好的 Cards。
另一種則是現在透過 Gemini 動態產生的資料:
產生學習卡表單
↓
太陽系
↓
Gemini API
↓
LearningCard[]
↓
React State
問題就在這裡。
當 Gemini 產生「太陽系」後,目前的 cards State 會變成太陽系資料。
但點擊 ThemeSelector 的「森林探險」後,原本的主題切換邏輯又會載入森林探險的預設資料。
結果變成:
太陽系 LearningCard[]
↓
點擊「森林探險」
↓
載入森林探險預設 Cards
↓
setCards(...)
↓
太陽系資料被取代
而目前又沒有 Firebase、LocalStorage 或其他 Persistence。
所以這組 Gemini 動態產生的太陽系資料一旦被覆蓋,就沒有地方可以再切回去了。
這個問題其實讓我重新看了一次目前的 App。
從 Gemini 的角度:
海洋動物 ✅
交通工具 ✅
太陽系 ✅
人體器官 ✅
Prompt 本身早就可以產生不同主題。
但從 App 的角度來看,目前其實還是:
預設 Theme Data
+
目前這一次 Gemini Generated Cards
並沒有真正把「動態產生的主題」納入同一套 Theme 管理。
也就是:
Gemini 已經支援不同主題,不代表我的 App 已經真正支援不同主題。
這個問題今天先記下來,不急著順手修改。
不然好不容易控制住 Day 25 的 Scope,又會一路改到 Theme Architecture 去 XD。
Day 25 沒有修改 Prompt Template,也沒有修改 Structured Output Schema。
今天主要完成:
Display Mode
↓
Edit Mode
↓
emoji / title / content / question / answer
↓
Save / Cancel
↓
React State
三個實際測試也都通過:
Edit + Save ✅
只修改目前 Card ✅
Cancel 不寫入修改 ✅
所以目前 App 已經逐漸具備:
Generate ✅
Regenerate ✅
Human Review ✅
Human Edit ✅
Learning Card 不再只是:
Gemini 產生什麼,我就顯示什麼。
而開始變成:
AI 幫忙產生,人負責最後決定。
今天最大的收穫其實有兩個。
第一個是 Human-in-the-loop。
現在整個 Learning Card Workflow 已經變成:
Learning Requirements
↓
Gemini
↓
Structured Output
↓
LearningCard[]
↓
Human Review
↓
Human Edit
↓
Final Learning Card
人不只是站在最後檢查 AI,而是真的可以修改 AI 的結果。
第二個則是今天意外發現的 Theme 問題。
前幾天我一直在確認:
Gemini 能不能產生不同的 Learning Card?
現在答案已經很明確:可以。
但下一個問題變成:
我的 App 能不能同時管理這些不同主題的 Learning Cards?
目前答案還不算真正做到。
目前 ThemeSelector 管理的是原本寫好的預設主題,而 Gemini 動態產生的 Cards 只存在目前的 React State。
所以:
Gemini 產生「太陽系」
↓
切換「森林探險」
↓
預設資料取代 cards
↓
「太陽系」消失
明天就從這個實際遇到的問題開始。
先不碰 Firebase,也先不做永久儲存,而是想辦法讓同一次 App Session 裡:
太陽系
↓
森林探險
↓
再切回太陽系
資料還能存在。
也順便真正整理目前的 Theme 與 Learning Card 資料關係。
Day 26|不只海洋動物:把 Learning Card 擴充成不同學習主題