iT邦幫忙

2026 iThome 鐵人賽

DAY 29
0
Build on Google AI

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

Day 29|不只會動,還要穩定:完成智慧學習卡整合測試

  • 分享至 

  • xImage
  •  

前幾天已經分別完成:

  • AI 產生學習卡
  • 多主題管理
  • Human Edit
  • LocalStorage 自動保存
  • 列印與另存 PDF

這些功能在開發時都個別測試過,如果今天再把相同操作全部重做一次,雖然也算測試,卻不一定能找出真正影響使用體驗的問題。

因此,Day 29 我改變了測試方式。

今天不再加入新功能,也不只是依照固定清單重複操作,而是從實際使用 App 的過程中觀察:

資料雖然正確,但呈現在畫面上的結果是否真的合理?

這次就在「大挑戰」模式中,發現了一個過去沒有注意到的問題。


一、今天發現的問題

我使用 Gemini 產生「太陽系」5 張學習卡,接著切換到大挑戰模式。

其中一題是:

地球是太陽系裡的第幾顆行星?

但畫面上的選項卻是:

我們的太陽系
美麗的地球
大火球太陽

【圖 1:修改前,第幾顆行星的題目卻顯示卡片標題】
Day29 大挑戰問題.jpg

另一題是:

月亮本身會發光嗎?

選項則是:

我們的太陽系
地球的好朋友月亮
美麗的地球

【圖 2:修改前,會不會的題目仍然顯示卡片標題】
Day29 test challenge solar.jpg

雖然選項和太陽系主題有關,但它們並不能直接回答題目。

如果題目問「第幾顆行星」,使用者預期看到的應該是「第三顆」;如果題目問「會不會發光」,選項中就應該出現「會」或「不會」。

因此,今天將這個問題記錄為:

CH-01:大挑戰使用 question 顯示題目,選項卻使用 title,造成題目與選項語意不一致。


二、問題是在 Gemini 產生資料時發生的嗎?

看到題目和選項對不上時,第一個需要確認的是 Gemini 是否產生了錯誤資料。

我先查看產生卡片時的 Console。

Gemini 回傳的其中兩張卡片如下:

title:美麗的地球
question:地球是太陽系裡的第幾顆行星?
answer:第三顆
title:地球的好朋友月亮
question:月亮本身會發光嗎?
answer:不會

這表示 Gemini 已經正確產生:

  • 卡片標題
  • 學習內容
  • 題目
  • 答案

接著再查看轉換完成的 LearningCard[],裡面的 question 與 answer 仍然正確。

因此,問題並不是發生在 Gemini Structured Output,也不是發生在資料轉換階段。


三、繼續檢查 LocalStorage

為了確認重新整理後的資料是否被改壞,我也查看了 LocalStorage 中的太陽系主題。

其中第四張卡片保存的是:

{
  "title": "美麗的地球",
  "question": "地球是太陽系裡的第幾顆行星?",
  "answer": "第三顆"
}

第五張卡片保存的是:

{
  "title": "地球的好朋友月亮",
  "question": "月亮本身會發光嗎?",
  "answer": "不會"
}

LocalStorage 中的內容也沒有問題。

到這裡可以排除:

  • Gemini Prompt
  • Structured Output
  • LearningCard[] 轉換
  • Generated Theme
  • LocalStorage 保存與還原

完整追蹤結果變成:

Gemini Structured Response:正確
             ↓
LearningCard[]:正確
             ↓
AppTheme[]:正確
             ↓
LocalStorage:正確
             ↓
QuizMode 顯示選項時出現語意落差

問題範圍因此縮小到大挑戰模式的畫面邏輯。


四、檢查 QuizMode 的選項產生方式

原本的 QuizMode.tsx 會取得目前題目的卡片,再隨機加入另外兩張卡片作為干擾選項。

概念如下:

目前題目的卡片
      +
另外兩張隨機卡片
      ↓
隨機排列三張 LearningCard

這個選項產生方式本身沒有錯,因為它可以確保目前卡片一定存在於選項中。

正確答案的判斷也不是比較文字,而是比較 Card ID:

if (card.id === currentCard.id) {
  // Correct
}

真正的問題出現在選項顯示內容。

原本畫面使用:

<div className="text-xl font-black">
  {option.title}
</div>

因此,即使題目使用的是:

currentCard.question

選項顯示的卻是:

option.title

這使大挑戰實際上變成:

問題:地球是第幾顆行星?
選項:美麗的地球

程式仍然可以透過 Card ID 判定「美麗的地球」是目前題目的卡片,但從使用者角度來看,選項並沒有回答問題。

這不是 Gemini 資料錯誤,而是資料欄位在畫面上的使用方式不一致。


五、交給 Gemini 的修正目標

確認問題後,我沒有要求 Gemini 重寫整個大挑戰,也沒有修改 Gemini Prompt 或資料結構。

這次只要求最小必要修改:

請檢查目前 React + TypeScript Learning Card App 的大挑戰模式,
只修正題目使用 question、選項卻使用 title 的問題。

【修改目標】

1. 題目繼續顯示 currentCard.question。
2. 選項文字改成顯示 option.answer。
3. 保留目前「正確卡片加兩張隨機干擾卡片」的方式。
4. 保留以 card.id === currentCard.id 判定正確答案。
5. 將「小朋友找找看」改成「選出正確答案」。
6. 答對回饋改用 card.answer,不再使用 card.title。
7. 答錯回饋不要再顯示卡片 title。
8. 保留 Emoji、音效、語音、動畫與下一題流程。

【限制】

- 優先只修改 src/components/QuizMode.tsx
- 不修改 Gemini Prompt
- 不修改 Structured Output Schema
- 不修改 LearningCard 與 AppTheme 型別
- 不修改 LocalStorage
- 不修改星星計算方式
- 不修改探索卡
- 不修改 Generate、Regenerate、Human Edit 與列印功能
- 採用最小必要修改

這次的重點不是增加更多功能,而是修正資料已經正確、畫面卻使用錯誤欄位的問題。


六、Gemini 實際修改內容

Gemini 執行約 113 秒,修改了一個檔案:

src/components/QuizMode.tsx

沒有修改其他元件、資料型別或 LocalStorage。

1. 選項由 title 改成 answer

原本:

<div className="text-xl font-black">
  {option.title}
</div>

修改為:

<div className="text-xl font-black leading-snug">
  {option.answer}
</div>

現在大挑戰的資料對應方式變成:

題目:currentCard.question
選項:option.answer

例如:

地球是太陽系裡的第幾顆行星?

第三顆
會
不會

雖然錯誤選項仍然來自其他卡片的答案,但至少每一個選項現在都是「回答內容」,而不是卡片名稱。

2. 移除選項中的 pinyin

原本選項顯示卡片標題時,也會顯示對應的 pinyin。

改為答案選項後,這些拼音不一定和答案文字相符,因此 Gemini 一併移除了選項中的 pinyin,避免再次出現資料語意不一致。

Emoji 則繼續保留,維持原本適合幼兒辨識的視覺設計。

3. 修改答對回饋

原本答對後可能顯示:

太棒了!就是美麗的地球!

或:

好厲害!找到了美麗的地球!

修改後改用實際答案:

`太棒了!答案就是「${card.answer}」!🎉`

以及:

`好厲害!選對了「${card.answer}」!🎈`

這樣題目、選項與回饋會使用同一個答案內容。

4. 修改答錯回饋

原本答錯時會顯示使用者點選的卡片名稱:

這是大火球太陽喔!再摸摸看哪一個是呢?

但選項已經改成答案後,繼續提到卡片標題會令人困惑。

因此修改成較通用的提示:

不對喔!再想想看哪一個才是正確答案呢?

5. 修改操作提示

題目上方原本顯示:

小朋友找找看

修改為:

選出正確答案

讓使用者一進入大挑戰,就能理解這是一個選擇正確答案的問答模式。


七、為什麼保留 Card ID 判斷?

雖然畫面改為顯示 answer,但 Gemini 沒有將正確答案判斷改成字串比較。

正確判斷仍然使用:

card.id === currentCard.id

這樣做有幾個好處:

  • 不依賴選項所在位置。
  • 選項重新洗牌後仍能正確判斷。
  • 不會因為答案前後有空白而判斷失敗。
  • 不需要額外修改 LearningCard 型別。
  • 保留目前穩定的關卡流程。

畫面負責顯示 answer,程式則使用 Card ID 確認選到的是不是目前題目的卡片。

使用者看到:answer
程式判斷:card.id

兩者各自負責不同工作。


八、修改後測試

完成修改後,我再次使用相同的「太陽系」主題測試大挑戰。

測試一:會不會的題目

題目:

太陽會發光發熱嗎?

修改後的選項包括:

會
第三顆
不會

選擇「會」後,畫面正確顯示答對回饋。

【圖 3:修改後,太陽題正確顯示「會」】
Day29 Gemini fixed challenge.jpg

測試二:第幾顆行星

題目:

地球是太陽系裡的第幾顆行星?

修改後的選項包括:

第三顆
會
不會

選擇「第三顆」後,按鈕變成綠色,並顯示:

好厲害!選對了「第三顆」!

【圖 4:修改後,地球題正確顯示「第三顆」】
Day29 gemini fixed challenge solar 04.jpg

測試結果

測試項目 結果
題目繼續使用 question 通過
選項改為顯示 answer 通過
正確答案存在於選項中 通過
選項仍會隨機排列 通過
Card ID 判斷正常 通過
答對回饋顯示答案 通過
答錯後可繼續選擇 通過
下一題功能正常 通過
Emoji 正常顯示 通過
語音與動畫正常 通過
TypeScript 檢查 0 錯誤
Build 成功

九、這次整合測試真正測到了什麼?

如果只確認按鈕能不能點、大挑戰能不能切換到下一題,原本的功能其實也算「可以運作」。

但從使用者角度來看:

題目問第幾顆行星
選項卻顯示卡片名稱

這並不是一個合理的問答體驗。

這次測試讓我理解:

程式沒有報錯,不代表功能設計就是正確的。

整合測試除了確認程式不會當機,也要檢查資料從來源到畫面的語意是否一致。

這次的資料追蹤包含:

Gemini Response
      ↓
LearningCard[]
      ↓
AppTheme[]
      ↓
LocalStorage
      ↓
QuizMode
      ↓
畫面選項

逐層檢查後,才能確定問題不是 AI 內容品質,也不是資料在保存時損壞,而是 QuizMode 最後選擇了不適合的顯示欄位。


十、最小修改的價值

這次 Gemini 只修改一個檔案:

src/components/QuizMode.tsx

沒有因為大挑戰顯示錯誤就重新設計:

  • Gemini Prompt
  • Structured Output Schema
  • LearningCard 型別
  • LocalStorage
  • Generate
  • Human Edit
  • 列印功能

實際需要修正的只是:

option.title
      ↓
option.answer

以及與選項語意相關的提示與回饋文字。

修改範圍越清楚,就越容易:

  • 理解改了什麼
  • 驗證修改結果
  • 避免破壞其他功能
  • 保留已經穩定的程式架構

十一、今天學到的事

1. 整合測試不等於重做所有舊測試

前面已經驗證過的功能,不需要每天完全重跑。可以從實際操作中找出跨元件或資料流程上的問題,再進行針對性測試。

2. Console 可以協助排除資料來源問題

Gemini Response 和 LearningCard[] 都正確,代表不應該急著修改 Prompt。

3. LocalStorage 不只是保存資料,也能協助除錯

檢查實際保存的 JSON,可以判斷資料是否在重新整理前後被改壞。

4. 欄位都有自己的語意

title 是卡片名稱,answer 才是問題答案。兩者都是字串,但不能因為型別相同就互相替代。

5. 使用者看到的內容也需要測試

TypeScript 和 Build 只能確認程式可以編譯,無法判斷題目與選項是否合理。

6. 找到根本原因後,應限制修改範圍

這次只修改 QuizMode.tsx,就完成了問題修正,也降低了其他功能產生回歸問題的風險。


十二、Day 29 完成成果

今天完成了:

  • 從實際操作發現大挑戰語意問題
  • 檢查 Gemini Structured Response
  • 檢查 LearningCard[]
  • 檢查 LocalStorage 實際資料
  • 排除 Prompt、資料轉換及保存問題
  • 定位 QuizMode 使用錯誤顯示欄位
  • 將選項由 title 改為 answer
  • 更新答對與答錯回饋
  • 將操作提示改為「選出正確答案」
  • 保留 Card ID 判斷與原本關卡流程
  • 完成修改後測試
  • 通過 TypeScript 與 Build

經過這次修正,大挑戰不只是程式可以運作,題目、選項及回饋也真正形成一致的問答流程。

明天將進入系列最後一天:

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

最後會整理這 30 天完成的功能、AI 協作方式、目前仍存在的限制,以及這個 Learning Card App 未來可以繼續發展的方向。


上一篇
Day 28|從 Prompt 到畫面:整理智慧學習卡完整資料流程
下一篇
Day 30|30 天打造 AI 學習卡:成果總結、限制與下一步
系列文
今天學什麼?30 天用 Google AI 打造智慧學習卡 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言