昨天第一次使用 Structured Output,成功讓 Gemini 不再回傳一整段需要自己拆解的文字,而是按照設定好的 Schema 回傳 JSON。
最後拿到的資料像這樣:
{
"emoji": "🐢",
"title": "認識海龜",
"content": "海龜住在美麗的大海裡……",
"question": "海龜背上背著什麼呢?",
"answer": "硬硬的大殼。"
}
看到 JSON 出現的瞬間,Day 09 那個還在研究 --- 和冒號怎麼切的自己,好像突然輕鬆了不少(笑)。
不過昨天主要只是先確認:
Structured Output 真的可以讓 Gemini 按照指定的結構回傳資料。
今天決定再回頭看看這份 Schema。
裡面的 properties、description、required,到底分別在做什麼?
Day 10 已經把一張 Learning Card 定義成五個欄位:
emoji
title
content
question
answer
今天稍微調整每個欄位的 description,讓用途更明確:
{
"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"
]
}

一開始看到這一大段確實有點多,不過拆開之後其實沒有想像中複雜。
可以先簡單理解成:
| 設定 | 用途 |
|---|---|
type |
定義資料型別 |
properties |
定義這個 Object 有哪些欄位 |
description |
說明這個欄位應該放什麼內容 |
required |
定義哪些欄位是必要的 |
propertyOrdering |
定義欄位輸出的順序 |
所以 Schema 不只是告訴 Gemini:
我要 JSON。
而是進一步把「這份 JSON 應該長什麼樣子」定義清楚。
Day 10 的 content 原本寫得比較簡單:
"description": "適合學習對象理解的簡短介紹"
今天改成:
"description": "使用適合學習對象理解的詞彙,只介紹一個主要知識點"
question 也明確設定成:
"description": "根據 content 提出一個可以直接回答的問題"
使用和昨天相同的 Prompt 再 Run 一次,Gemini 回傳:
{
"emoji": "🐢",
"title": "海龜",
"content": "海龜生活在美麗的大海裡,背上有一個又圓又硬的大殼,就像背著自己的小房子一樣保護自己喔!",
"question": "海龜的背上有什麼呢?",
"answer": "又圓又硬的大殼。"
}

這次 Content 主要聚焦在海龜的硬殼,而 Question 也可以直接從 Content 找到答案:
Content
↓
海龜背上有又圓又硬的大殼
Question
↓
海龜的背上有什麼呢?
Answer
↓
又圓又硬的大殼
至少這次的結果符合設定的 description。
不過只 Run 一次,還不能直接說:
description 寫得越詳細,Gemini 產生的內容就一定越好。
對我來說,這次比較重要的發現是:
除了 Prompt 之外,Schema 本身也可以進一步描述每個欄位的用途。
接著想測試另一個設定:
"required"
原本五個欄位全部都是 required:
"required": [
"emoji",
"title",
"content",
"question",
"answer"
]
這次刻意把 emoji 拿掉:
"required": [
"title",
"content",
"question",
"answer"
]

但 properties 裡的 emoji 還是保留:
"emoji": {
"type": "string",
"description": "與學習主題相關的一個 Emoji"
}
原本有點好奇:
既然 emoji 不是 required,這次 🐢 會不會就不見了?
結果 Run 下去:
{
"emoji": "🐢",
"title": "認識海龜",
"content": "海龜住在美麗的大海裡。牠的背上背著一個又圓又硬的大殼,就像隨身帶著堅固的小房子一樣喔!",
"question": "海龜背上背著什麼呢?",
"answer": "硬硬的大殼。"
}
🐢 還是在。
這次實驗剛好讓我更容易理解 properties 和 required 的差別。
雖然把 emoji 從 required 移除了,但它仍然存在於 properties。
也就是:
properties 有 emoji
↓
Schema 裡有定義這個欄位
required 沒有 emoji
↓
這個欄位不是必要欄位
所以:
Optional ≠ 不會出現。
比較準確的理解是:
這個欄位「不一定要有」。
而不是:
這個欄位「不可以出現」。
這一次 Gemini 還是選擇回傳了 emoji。
當然,單靠這一次實驗,也不能反過來說 Optional 欄位每次都一定會出現。
如果應用程式真的要求每一張 Learning Card 都一定要有 Emoji,那最直接的方式還是把它放在 required。
做到這裡,我對昨天那句:
Prompt 管內容,Schema 管結構。
又多了一點理解。
現在可以再拆得更清楚:
Prompt
↓
描述這次希望 AI 完成什麼學習任務
Schema
↓
定義程式預期收到什麼資料
description
↓
說明每個欄位負責什麼
required
↓
決定哪些欄位是必要的
也就是說,Schema 不只是為了讓 JSON 看起來整齊。
它開始有點像是 AI 和應用程式之間的一份資料規格。
AI 負責產生內容,但應用程式可以先定義:
我要用什麼結構接收這些內容。
今天沒有增加新的 Learning Card 功能,而是把昨天建立的 Schema 再拆開看了一次。
目前一張 Learning Card 已經可以明確定義成:
Learning Card
├── emoji string
├── title string
├── content string
├── question string
└── answer string
而且也開始理解:
properties
→ 有哪些欄位
description
→ 欄位要放什麼
required
→ 哪些欄位一定要有
到這裡,「一張 Learning Card」的資料結構算是慢慢成形了。
但還有一個很明顯的問題。
我們從 Day 05 開始的需求明明都是:
請產生 5 張學習卡。
結果這兩天為了研究 Structured Output,都只產生了一張。
如果一張 Learning Card 是一個 object:
{
"emoji": "🐢",
"title": "海龜"
}
那五張 Learning Cards 又該怎麼定義?
看來明天要讓這份 Schema 再長大一點了。
Day 12|一張不夠,那就一次來五張:讓 Gemini 回傳 Learning Card Array