National Central University
Uedu Main Site
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try an AI Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

3

Local AI

--

Uedu Code

--

中央大學 AQI 80 24°C PM2.5 21
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 09

結構化輸出

JSON 模式、Schema 與驗證,讓程式接得住模型的回答
Interaction實作 25 minute(s)

Learning Objectives

  • 說明純文字回答接到程式時會出什麼問題
  • 使用 JSON 模式與 JSON Schema(strict),並說明兩者保證什麼
  • 用 json 模組解析回答,並自己寫欄位驗證
  • 處理解析失敗與被截斷的 JSON:重試一次,仍失敗就回報

程式要的是欄位

給人看的回答是文字就夠了。但如果要把公告的日期寫進行事曆、地點顯示在地圖、是否必到拿來發提醒,程式要的是欄位。

這一課的順序是:先看不做任何設定時會怎樣,再用 JSON 模式,再用 Schema,最後處理它們都擋不住的情況。回應是 2026-11-03 錄下來的真實輸出,公告是虛構的。

逐步看:只在提示詞裡說要 JSON

json.loads 把 JSON 字串變成 Python 物件,失敗時丟出 JSONDecodeError。兩種提示詞各跑三次。第二種多了一句順便說明,看看差別。

提示詞裡說要 JSON,只有大致可靠

提示詞 三次的結果
「……給我 JSON:」 三次都解析成功
「幫我整理成 JSON,順便說明你怎麼判斷……」 三次都失敗:開頭是 ```json,後面接著說明文字

第二種的失敗還有其他問題。三次的欄位結構都不一樣,第一次是平的(課程、日期…),後兩次包在 event 裡,而且 location 變成巢狀的物件。回答也因為說明太長,碰到 150 token 的上限。

用字串處理去掉程式碼框是常見的補救,但這只處理了看到的那一種情況。比較好的做法是請 API 幫忙。

逐步看:JSON 模式

response_format={"type": "json_object"} 要求輸出一定是合法的 JSON。先看一個真實的 400 錯誤:提示詞裡沒有 JSON 這個字。

JSON 模式保證語法,不管內容

從錄音可以看到兩件事。

一定要寫 JSON 這個字。 錯誤訊息是:

'messages' must contain the word 'json' in some form, to use 'response_format' of type 'json_object'.

它只保證是合法的 JSON。 欄位名稱、型別、有沒有缺欄位都由模型決定。這次三次的欄位名稱剛好相同,是因為提示詞寫了「時間、地點、是否必到」,模型照著用了中文。但後面的範例裡,同樣是 JSON 模式,模型把 required 這個欄位填成字串 "全班都要出席",不是我們想要的 true。

要限制欄位與型別,就要提供 Schema。

逐步看:JSON Schema(strict)

Schema 是用 JSON 寫的規格:有哪些欄位、各是什麼型別。兩則公告各跑三次,第二則沒有寫地點。先想想:location 會是 null 嗎?

Schema 管形狀,不管內容對不對

層次 提示詞要求 JSON 模式 Schema(strict)
是合法的 JSON 大致 是 是
欄位名稱固定 否 否 是
型別固定 否 否 是
內容正確 否 否 否

官方指南建議,模型支援時優先用 json_schema。但上面的輸出說明它管不到內容:

  • date 永遠是字串,但「下週三 14:00」和「下週三下午兩點」寫法不同。要寫進行事曆,還要程式再處理,或在 Schema 裡加描述、限制格式。
  • 第二則公告沒有寫地點,我們希望是 null,模型卻填了「線上自學(課程網站)」。這是合法的字串,Schema 檢查不出來。內容對不對要用測試資料評估(第 20 課)。

還有一個真實的意外。 這個範例的第一版把是否必到的欄位命名為 required,而且欄位有中文描述。那一版錄到兩個奇怪的結果:地點變成 工程二館 E2-315 שה上課.,夾了其他語言的文字,而且明明全班要出席,required 卻是 false;另一次地點後面接了一句外文,再輸出一長串空白與換行,直到 150 token 上限,json.loads 解析失敗。

改名為 must_attend、拿掉描述後,這一版的 6 次就沒有再出現。我們沒有查出確切的原因,也不能保證不會再發生(這兩筆回答沒有放進範例的錄音,保留在 datasets/g09_schema_anomaly_raw.json)。重點是:用了 strict,程式仍然要處理解析失敗,也要檢查 finish_reason。 官方指南也提醒,輸出被截斷時可能不是完整的 JSON,模型拒答時會回傳拒答內容(chat 回應的 message.refusal),解析前都要先檢查。

逐步看:被截斷的 JSON,重試一次

上一課看過被截斷的文字。JSON 被截斷時更麻煩,少一個括號整段都解析不了。這裡把驗證與重試的流程寫出來。

接住模型回答的流程

不論用哪一種模式,建議都走同一個流程:

送出請求
  ↓
finish_reason 是 length? → 是:被截斷,不解析
  ↓
json.loads 成功?         → 否:不是 JSON
  ↓
欄位和型別都對?          → 否:驗證失敗
  ↓
通過,交給後面的程式

失敗時的處理:

  • 重試一次。 再送一次,或把錯誤告訴模型(「must_attend 應該是布林值」),請它改正。
  • 有次數上限。 通常一兩次就好,每次都要花錢。
  • 仍然失敗就回報。 回傳 None 或丟出例外,把原始回應記下來方便除錯。不要猜一個預設值填進去,例如把解析失敗的 must_attend 當成 false,同學就會收不到必到的提醒。

驗證可以用 Pydantic 或 jsonschema 這類套件,本課先用標準函式庫自己寫一次,下面的實作題就是這個部分。

互動練習:從課程公告抽出欄位

這個實驗開啟 JSON 模式(json_object),所以提示詞裡一定要有 JSON 這個字,否則會收到上面看過的 400 錯誤。目標是三則公告都輸出 date、location、must_attend 三個欄位,至少跑兩個版本比較。

規則檢查只看語法與欄位名稱,不會檢查內容對不對,也不檢查型別。請你自己看:沒寫地點時填了什麼?must_attend 是布林值嗎?

未登入時可以切換兩個示意版本。只寫一句的版本,三題的欄位都由模型自己決定(課程、公告內容、點名…),程式讀不到。寫清楚的版本三題都有指定的欄位,第 2 題的 location 是 null。提示詞在這裡仍然有差別,而且實務上提示詞和 Schema 會一起用。

實作:把模型的回應分類

完成 check_reply(text, finish_reason),依序檢查,回傳第一個符合的結果:

  1. finish_reason 是 length:回傳 被截斷
  2. json.loads 失敗:回傳 不是合法的 JSON
  3. 解析結果不是字典:回傳 不是物件
  4. 缺少 date、location、must_attend 其中一個(依這個順序檢查):回傳 缺少欄位 欄位名稱
  5. must_attend 不是布林值:回傳 must_attend 不是布林值
  6. 都沒問題:回傳 通過

這一題不呼叫模型,回應是寫死的字串,其中幾段改寫自這一課看過的真實回答。

你的輸出
預期輸出

小測驗:結構化輸出

1.

用 response_format={"type": "json_object"},但提示詞沒有 JSON 這個字。範例裡的 API 怎麼回應?

2.

strict 的 Schema 規定 location 是字串或 null。公告沒寫地點,模型卻填了「線上自學(課程網站)」。這代表什麼?

3.

拿到回答後,最合理的檢查順序是什麼?

4.

重試一次後,回答還是解析失敗。must_attend 應該怎麼處理?

5.

下面的程式會印出什麼?

import json
text = '{"must_attend": "true"}'
data = json.loads(text)
print(isinstance(data["must_attend"], bool))
第 1 次作答

重點整理

  • 只在提示詞要求 JSON,回答可能帶程式碼框或說明文字,加一句話就可能全部解析失敗。
  • JSON 模式保證語法(這個模型要求 messages 含 json 這個字);Schema(strict)再保證欄位與型別。
  • 兩者都不保證內容正確,也擋不了截斷與其他意外。
  • 接回答的流程:看 finish_reason → json.loads → 驗證欄位 → 通過。失敗就重試有限次,再失敗就回報,不要編造預設值。

下一課: 回到文字的意思:把文字變成向量,用數字比較兩段話有多接近。

這一課完成了!
Next lesson:Embedding 與語意相似度
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。