iT邦幫忙

2026 iThome 鐵人賽

DAY 12
0
Build on Google AI

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

Day 12|一張不夠,那就一次來五張:讓 Gemini 回傳 Learning Card Array

  • 分享至 

  • xImage
  •  

昨天把 Learning Card 的 Schema 拆開來看,終於比較清楚 properties、description、required 分別在做什麼。

目前我們已經可以用 Structured Output 定義「一張」Learning Card:

Learning Card
├── emoji
├── title
├── content
├── question
└── answer

但從 Day 05 開始,我原本的需求其實一直都是:

一次產生 5 張學習卡。

所以今天要解決的問題很單純:

一張 Learning Card 是一個 Object,那很多張 Learning Cards 要怎麼表示?

答案就是——Array。


從一個 Object 變成一組 Cards

原本一張 Learning Card 的 JSON 大概長這樣:

{
  "emoji": "🐢",
  "title": "海龜",
  "content": "...",
  "question": "...",
  "answer": "..."
}

如果今天要一次取得多張卡片,就需要一個 Array 來裝這些 Learning Card Object。

最後我採用的資料結構是:

Response Object
│
└── cards: Array
      │
      ├── Learning Card Object
      ├── Learning Card Object
      ├── Learning Card Object
      ├── Learning Card Object
      └── Learning Card Object

也就是讓 Response 最外層維持 Object,再新增一個 cards 欄位。

而 cards 本身是一個 Array,裡面的每一筆資料都是一張 Learning Card。


定義 Learning Card Array Schema

這次的 Schema 就比昨天多了一層:

{
  "type": "object",
  "properties": {
    "cards": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "emoji": {
            "type": "string",
            "description": "與學習主題相關的一個 Emoji"
          },
          "title": {
            "type": "string",
            "description": "簡短的學習卡標題"
          },
          "content": {
            "type": "string",
            "description": "使用適合學習對象理解的詞彙,只介紹一個主要知識點"
          },
          "question": {
            "type": "string",
            "description": "根據 content 提出一個可以直接回答的問題"
          },
          "answer": {
            "type": "string",
            "description": "question 的簡短參考答案"
          }
        },
        "required": [
          "emoji",
          "title",
          "content",
          "question",
          "answer"
        ],
        "propertyOrdering": [
          "emoji",
          "title",
          "content",
          "question",
          "answer"
        ]
      }
    }
  },
  "required": [
    "cards"
  ],
  "propertyOrdering": [
    "cards"
  ]
}

看起來突然變長很多,但其實真正新增的核心只有:

"cards": {
  "type": "array",
  "items": {
    "type": "object"
  }
}

這裡我主要理解成:

cards
↓
是一個 Array

items
↓
定義 Array 裡的每一筆資料

每一個 item
↓
都是一個 Learning Card Object

所以昨天定義好的 Learning Card 並沒有消失,只是今天把它放進 Array 裡。

Day12 code editor cards object

Day12 visual editor cards object

「5 張」交給 Prompt,「長什麼樣子」交給 Schema

Schema 準備完成之後,Prompt 也可以繼續專注在「我要產生什麼內容」。

這次使用:

請製作一組適合 3~4 歲幼兒學習的「海洋動物學習卡」。

學習目標:
讓孩子認識常見的海洋動物,並了解每種動物一個簡單的特徵。

請產生 5 張學習卡,分別介紹:
海龜、鯨魚、章魚、螃蟹、小丑魚。

內容請使用繁體中文,
並使用適合 3~4 歲幼兒理解的詞彙。

這裡我沒有再告訴 Gemini:

請回傳 JSON
每張要有 emoji
每張要有 title
每張要有 content
...

因為這些已經交給 Schema 處理。

目前兩邊的工作變得更清楚:

Prompt
↓
3~4 歲幼兒
海洋動物
產生 5 張
指定 5 種動物
使用繁體中文

Schema
↓
回傳 cards
cards 是 Array
每個 item 是 Object
每張 Card 有固定的五個欄位

又再次回到前幾天整理出的概念:

Prompt 負責描述學習任務,Schema 負責定義資料結構。

Day12 prompt user 5 cards

Gemini 真的一次回傳五張了

這次執行後,Gemini 回傳:

{
  "cards": [
    {
      "emoji": "🐢",
      "title": "認識海龜",
      "content": "海龜住在美麗的大海裡。牠的背上背著一個又圓又硬的大殼,就像隨身帶著堅固的小房子一樣喔!",
      "question": "海龜背上背著什麼呢?",
      "answer": "硬硬的大殼。"
    },
    {
      "emoji": "🐋",
      "title": "認識鯨魚",
      "content": "鯨魚是世界上體型最大的動物,牠在水面上呼氣的時候,頭頂會噴出高高的水花喔!",
      "question": "鯨魚呼氣時,頭頂會噴出什麼呢?",
      "answer": "高高的水花。"
    },
    {
      "emoji": "🐙",
      "title": "認識章魚",
      "content": "章魚的身體軟綿綿的,牠有八隻長長的腳,遇到危險時還會噴出黑黑的墨汁逃跑喔!",
      "question": "章魚有幾隻長長的腳呢?",
      "answer": "八隻腳。"
    },
    {
      "emoji": "🦀",
      "title": "認識螃蟹",
      "content": "螃蟹有硬硬的外殼和一對像剪刀的大鉗子,而且牠們走路時都是橫著走的喔!",
      "question": "螃蟹走路是向前走還是橫著走呢?",
      "answer": "橫著走。"
    },
    {
      "emoji": "🐠",
      "title": "認識小丑魚",
      "content": "小丑魚身上有漂亮橘色和白色的條紋,最喜歡躲在軟綿綿的海葵家裡面玩捉迷藏喔!",
      "question": "小丑魚最喜歡躲在哪裡呢?",
      "answer": "軟綿綿的海葵家裡。"
    }
  ]
}

Day12 prompt json answer

這次終於不是只有一隻海龜了(笑)。

而且從資料結構來看,剛好得到:

cards
├── 🐢 海龜
├── 🐋 鯨魚
├── 🐙 章魚
├── 🦀 螃蟹
└── 🐠 小丑魚

五張卡片都在同一個 cards Array 裡,每一筆也都有:

emoji
title
content
question
answer

這正是我之後希望交給程式使用的資料形式。


Array 解決的是「多筆資料」問題

做到今天,我覺得 Structured Output 的輪廓又更清楚了一點。

Day 10 解決的是:

怎麼讓 Gemini 不要只回傳一大段文字,而是固定的 JSON?

Day 11 解決的是:

一張 Learning Card 的 Schema 到底要怎麼定義?

今天則是:

怎麼從「一張 Card」擴充成「一組 Cards」?

現在的資料結構也從:

Learning Card Object

變成:

Response Object
└── cards: Learning Card[]

這一步看起來只是多了一個 Array,但對後面的程式來說很重要。

因為之後不需要再從一大段 Response 裡猜:

「第一張卡在哪裡?第二張又從哪裡開始?」

而是可以直接取得 cards,再一張一張處理。

終於不用再跟 split("---") 奮鬥了(笑)。


Structured Output 解決結構,但內容還是 Draft

不過今天也再次提醒自己一件事。

即使 JSON 結構完全符合 Schema:

5 張 Card            ✓
固定欄位             ✓
Array 結構           ✓
Question / Answer    ✓

也不代表裡面的學習內容就一定完全正確。

例如用詞是否精確、知識點是否適合學習對象,仍然需要 Review。

所以目前整個流程依然是:

Learning Requirement
        ↓
      Gemini
        ↓
Structured Learning Cards
        ↓
      Draft
        ↓
   Human Review

Structured Output 解決的是:

「資料要怎麼回來?」

而不是:

「AI 說的內容一定是真的嗎?」

後面的文章再來處理這個問題。


Day 12 小結

今天終於從一張 Learning Card,前進到一次產生一組 Learning Cards。

目前的資料結構已經變成:

Response
│
└── cards[]
      │
      ├── emoji
      ├── title
      ├── content
      ├── question
      └── answer

Gemini 也成功按照這個結構,一次回傳海龜、鯨魚、章魚、螃蟹和小丑魚五張學習卡。

從 Day 09 還在想辦法解析文字,到現在終於有一份相對穩定、程式比較容易使用的資料格式了。

不過 JSON 有了,下一個問題也跟著出現:

Gemini 知道 Learning Card 長什麼樣子了,但我的程式知道嗎?

身為一個平常寫 TypeScript 的前端工程師,看到這份 JSON,手已經有點想開始幫它加型別了(笑)。

明天終於輪到 TypeScript 上場。

Day 13|JSON 有了,TypeScript 呢?建立 LearningCard 型別


上一篇
Day 11|Learning Card Schema 怎麼設計?把 AI 回傳格式定義清楚
系列文
今天學什麼?30 天用 Google AI 打造智慧學習卡 共 12 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言