iT邦幫忙

2026 iThome 鐵人賽

DAY 26
0
Claude AI

從零開始 Claude Code:30 天打造 AI 任務管理 Web App系列 第 26 篇

[Day 26] 把 AI 回傳內容變成真正可用的結構化任務

  • 分享至 

  • xImage
  •  

Day 25 我已經成功做到:

輸入一個大型目標
		↓
Python 呼叫 Groq API
		↓
openai/gpt-oss-20b
		↓
回傳 5~8 個小任務

今天我要處理的問題就是:

怎麼讓 AI 回傳的內容,不只是「看得懂」,而是變成 Python 可以穩定處理的資料。

所以 Day 26 的重點,不再只是「AI 有沒有回答」,而是:

AI 回傳的格式穩不穩
Python 能不能解析
欄位是不是固定
資料不符合規則時能不能擋下來

為什麼 Day 25 的文字還不夠?

Day 25 的回傳內容大概像:

1. 確定專題題目與研究範圍:選定題目並列出主要研究目標與問題
2. 進行文獻回顧與資料蒐集:查閱相關資料並整理重要內容

人看到之後,很自然就知道:

標題:
確定專題題目與研究範圍

說明:
選定題目並列出主要研究目標與問題

但 Python 看到的其實比較接近:

"1. 確定專題題目與研究範圍:選定題目...\n2. ..."

也就是一整串字串。
如果 AI 某一次改成:

一、確定專題題目

或:

任務 1:確定專題題目

甚至多加:

以下是幫你整理的任務:

那原本靠文字切割的方式就可能出問題。
所以如果之後真的要把這些內容變成 Web App 裡的一筆一筆 Task,就不能只靠自由文字。


Day 26 改成 JSON

今天我希望 AI 回傳的格式變成:

{
  "tasks": [
    {
      "title": "確定專題題目與研究範圍",
      "description": "選定題目並整理主要研究目標"
    },
    {
      "title": "進行文獻回顧與資料蒐集",
      "description": "查閱相關資料並整理重要內容"
    }
  ]
}

這樣 Python 就能知道:

tasks
→ 任務清單

title
→ 任務名稱

description
→ 任務說明

而不是自己猜文字要怎麼切。


今天還是不接 Web App

雖然今天的資料格式已經很接近真正的 Task,但我還是不急著改 app.py。
Day 26 一樣先限制範圍。
今天不做:

  • 不接首頁
  • 不新增 Flask route
  • 不寫入 MySQL
  • 不建立 Task
  • 不修改 Task model
  • 不讓 AI 產生 status
  • 不讓 AI 產生 category
  • 不讓 AI 產生 due_date
  • 不新增 AI 資料表
  • 不新增 services/routes 分層

今天只處理:

 AI
 ↓
JSON
 ↓
Python dict / list

先把資料格式處理穩,再考慮資料庫。


先讓 Claude Code 規劃

我先進入 Plan Mode,然後把需求講清楚:

現在開始 Day 26。

Day 25 已完成:
 - Python 3.9.13
 - openai==2.47.0
 - Groq OpenAI-compatible API
 - model=openai/gpt-oss-20b
 - groq_api_test.py 已成功從命令列取得大型目標
 - AI 已能在終端機回傳 5~8 個繁體中文任務
 - API key 與基本錯誤處理已驗證

Day 26 的目標是:

把 AI 原本回傳的自由文字,
改成 Python 可以穩定處理的結構化任務資料。

今天先只需要:
1. AI 回傳 JSON。
2. 最外層格式為:
   {
     "tasks": [...]
   }
3. 每個 task 只有:
   - title
   - description
4. 任務數量維持 5~8 個。
5. Python 可以把回傳 JSON 解析成 dict / list。
6. 在終端機逐筆印出解析後的 title 和 description。
7. 驗證每個 task 都有 title、description。

希望優先使用 Groq + openai/gpt-oss-20b 支援的 Structured Outputs / JSON Schema,
不要只靠 Prompt 要求「請輸出 JSON」。

今天不要:
 - 接 Flask 首頁
 - 新增 Flask route
 - 寫入 MySQL
 - 自動建立 Task
 - 修改 Task model
 - 讓 AI 產生 status、category、due_date
 - 新增 AI 資料表
 - 新增 services/ 或 routes/ 分層
 - 修改 CLAUDE.md

Python 維持 3.9.13。
OpenAI SDK 維持 2.47.0。
GROQ_API_KEY 的處理方式維持 Day 25。

請先:
1. 讀取 CLAUDE.md
2. 檢查目前 groq_api_test.py
3. 確認 openai 2.47.0 搭配 Groq Chat Completions 要怎麼傳 response_format=json_schema
4. 告訴我預計修改哪些檔案
5. 說明 JSON Schema 的內容
6. 說明成功與失敗要怎麼驗證

先給我 Plan,不要修改檔案。

這次我特別要求:

先給 Plan
不要修改

因為我不想讓 Claude Code 一看到「Day 26」就自己開始改 app.py。


為什麼只讓 AI 回 title 和 description?

目前我的 Task model 有:

title
description
status
category
due_date

但這次我只讓 AI 回:

title
description

沒有讓 AI 自己決定:

status
category
due_date

原因是這些欄位不一定適合由 AI 猜。
例如使用者只輸入:

我要準備期末專題

AI 並不知道真正的截止日期。
如果硬叫它產生 due_date,很可能就會自己猜。
而新任務的狀態本來就可以由程式預設成:

todo

所以沒有必要讓 AI 多決定一次。
目前把責任切開比較清楚:

AI 負責:
title
description

程式之後負責:
status
category
due_date

使用 Structured Outputs

如果只是單純在 Prompt 裡寫:

請用 JSON 回答

通常 AI 會照做。
但「通常」還是不夠穩。
它可能回:

以下是 JSON:

再接 JSON。
也可能包成 Markdown:

   json
{
  ...
}
對人來說這沒有什麼差別,但對 Python 來說就多了很多要處理的東西。
所以這次不是只靠 Prompt。
而是改用 Groq 的:
```text
Structured Outputs

搭配:

JSON Schema

讓 API 直接限制回傳格式。


JSON Schema 是什麼?

我這次可以先把它簡單理解成:

JSON
→ 真正的資料

JSON Schema
→ 規定資料應該長什麼樣子

例如真正資料:

{
  "title": "蒐集資料",
  "description": "搜尋專題相關資料"
}

Schema 則是在規定:

title 必須存在
title 必須是字串
description 必須存在
description 必須是字串

這次我的 Schema 概念是:

{
  "type": "object",
  "properties": {
    "tasks": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          }
        },
        "required": [
          "title",
          "description"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "tasks"
  ],
  "additionalProperties": false
}

也就是要求:

最外層一定要有 tasks
tasks 一定是 array
每一筆 task 一定要有:
title
description
不能多出:
status
category
due_date
或其他欄位

strict: true

這次 response_format 使用:

type: json_schema
strict: true

意思是我要模型嚴格按照指定的 Schema 回傳。
也就是不是:

「盡量長得像這樣」

而是:

「必須符合這個資料結構」

這次真正實測之後,也確認:

openai/gpt-oss-20b
  +
Groq
  +
strict: true

可以正常工作。

沒有被 API 回 HTTP 400。


修改 groq_api_test.py

Day 26 沒有新增一堆新檔案。

只修改原本 Day 25 的:

groq_api_test.py

Day 25 的版本已經留在 Git 歷史裡,所以現在直接把這個測試腳本往下一階段改就可以。

其他地方都沒有動。

這樣可以很清楚知道 Day 26 的變化到底在哪裡。


max_completion_tokens 改成 2048

Day 25 使用的是:

max_completion_tokens=1024

Day 26 改成:

max_completion_tokens=2048

原因是這次不再只是單純文字。

輸出裡還會有:

JSON 結構
欄位名稱
title
description
5~8 筆任務
reasoning

如果剛好因為長度限制被截斷,JSON 很可能會直接變成不完整。

例如:

{
  "tasks": [
    {
      "title": "蒐集資料",
      "description":

這種內容根本不能解析。

所以這次多留一點輸出空間。

後面的實測結果也證明,實際每次大約只用了:

500~660 tokens

距離 2048 還很遠。

所以目前不用再調高。


新增 parse_tasks()

這次最重要的新功能之一是:

parse_tasks(content)

它負責把 AI 回傳的 JSON 字串轉成 Python 可以使用的資料。

第一步:

json.loads(content)

如果不是合法 JSON,就直接失敗。

例如:

這不是 JSON

會得到:

ValueError

而不是繼續往下跑。


不只 parse,還要 validate

parse_tasks() 不只是把 JSON 讀進來而已。

它還會檢查:

最外層是不是 dict
有沒有 tasks
tasks 是不是 list
任務數量是不是 5~8
每一筆是不是 dict
有沒有 title
有沒有 description
有沒有多餘欄位
title 是不是字串
description 是不是字串
title 去除空白後是不是空的
description 去除空白後是不是空的

例如如果 AI 回:

{
  "tasks": [
    {
      "title": "蒐集資料",
      "description": "搜尋相關資料",
      "status": "todo"
    }
  ]
}

就不符合今天的規則。

因為 Day 26 每筆任務只允許:

title
description

所以 Python 會把它擋下來。


為什麼 strict 了還要 Python 再驗證?

一開始我也會想到:

既然 strict: true 已經限制 Schema,為什麼 Python 還要再檢查一次?

因為這兩層負責的事情不完全一樣。

Structured Outputs 比較像是在保證:

資料的形狀

例如:

tasks 是陣列
title 是字串
description 是字串
沒有多餘欄位

但我自己的專案還有其他規則。

例如:

任務一定要 5~8 筆
title 不能是空字串
description 不能只有空白

所以可以把它想成:

Groq Structured Outputs
→ 確保資料格式

parse_tasks()
→ 確保符合這個專案自己的規則

兩層一起做,比單靠其中一層更清楚。


呼叫 Groq 測試

第一個還是使用:

venv\Scripts\python.exe groq_api_test.py "我要準備期末專題"

這次 AI 不再回傳自由文字格式。

而是先回傳符合 Schema 的 JSON,再由 Python 解析後顯示成比較好閱讀的格式。

其中一次得到 8 筆任務。

最後顯示:

共 8 筆,驗證通過

而:

finish_reason: stop

表示正常完成。


測英文輸入

接著我測:

venv\Scripts\python.exe groq_api_test.py "Plan a 5-day trip to Japan"

雖然輸入是英文,但因為 Prompt 有要求使用繁體中文,所以最後仍然得到繁體中文:

1. 確定旅行日期與行程時間表
   選定5天的旅行日期,制定每天的主要活動與交通安排。

2. 訂機票與住宿
   比對航班選項,預訂往返機票;挑選並預約適合的酒店或旅社。

3. 規劃每日行程路線圖
   依據目的地規劃每天的景點順序、餐廳與購物點。
...

最後:

共 6 筆,驗證通過

API 資訊:

model: openai/gpt-oss-20b
finish_reason: stop
tokens: prompt=272 completion=288 total=560

這也證明:

英文輸入
	 ↓
仍然可以依 Prompt 回繁體中文

測很短、很模糊的輸入

接著測:

venv\Scripts\python.exe groq_api_test.py "學英文"

這次輸入非常短。

結果還是可以正常拆成:

1. 設定學習目標
2. 挑選教材
3. 規劃學習時間表
4. 進行語言實踐
5. 評估與調整

最後:

共 5 筆,驗證通過

token:

prompt=266
completion=182
total=448

代表即使輸入資訊不多,格式還是維持正常。


測比較長、比較複雜的目標

再來測:

venv\Scripts\python.exe groq_api_test.py "我要在三個月內學會 Python 基礎、Flask、MySQL,最後做出一個可以展示的專題"

這次內容比較長,而且有很多階段。

AI 最後拆成 8 筆:

1. 學習 Python 基礎語法
2. 完成 Python 進階練習題
3. 入門 Flask 框架
4. 學習 MySQL 資料庫基礎
5. 結合 Flask 與 MySQL 建立 CRUD 應用
6. 撰寫專題需求與規劃設計文件
7. 實作並部署專題到線上平台
8. 撰寫專題說明文件與測試報告

最後:

共 8 筆,驗證通過

token:

prompt=293
completion=368
total=661

目前仍然遠低於:

2048

所以輸出長度不是問題。


這次反而發現一個適合留到 Day 27 的問題

這次比較長的測試中,有一筆:

實作並部署專題到線上平台

但我輸入的原文其實只是:

最後做出一個可以展示的專題

我沒有說:

一定要部署

AI 自己把「可以展示」延伸成:

部署到 Heroku、Render 或其他雲端平台

這個結果在格式上完全正確。

也通過:

JSON Schema
parse_tasks()
5~8 筆檢查

但內容上卻多做了一點推測。

這件事讓我看到 Day 26 和 Day 27 的差別。

Day 26 現在已經處理好:

格式穩不穩

接下來 Day 27 要開始處理:

內容準不準

也就是:

AI 有沒有自己增加使用者沒有要求的事情?
AI 有沒有過度推測?
Prompt 要怎麼限制?

這個例子剛好可以留到下一天處理。


總共做了幾次真實測試?

這次 Day 26 不只呼叫一次。

實際共跑了 6 次主要驗證:

我要準備期末專題
→ 8 筆

我要三個月內學會 Python
→ 6 筆

Plan a 5-day trip to Japan
→ 7 筆

同一目標再連跑 3 次
→ 6、7、6 筆

後面又另外測:

學英文
較長的 Python + Flask + MySQL 目標
種花小技巧

每次都是:

finish_reason: stop

沒有發生:

HTTP 400

也就是這次的:

strict: true

確實可以在 Groq + openai/gpt-oss-20b 上正常使用。


finish_reason=length 還沒有真的遇到

程式現在也處理:

finish_reason = length

如果發生,代表輸出可能被截斷。

這時候程式不會硬做:

json.loads()

而是直接顯示提示並停止。

因為被截斷的 JSON 很可能根本不是合法 JSON。

不過這次實際測試沒有真的遇到。

目前每次大約只有:

500~660 tokens

離:

2048

還很遠。

所以我沒有為了測試而故意把 token 上限調很低。

這個防呆目前先保留就好。


Day 26 完成後,流程變成什麼?

Day 25 是:

大型目標
	 ↓
Groq API
	 ↓
自由文字

Day 26 現在變成:

大型目標
	 ↓
Groq API
	 ↓
openai/gpt-oss-20b
	 ↓
JSON Schema
	 ↓
Structured Outputs
	 ↓
合法 JSON
	 ↓
json.loads()
	 ↓
parse_tasks()
	 ↓
Python list / dict

也就是 AI 回傳的內容,終於不只是拿來看的文字。

現在 Python 已經可以一筆一筆取得:

task["title"]
task["description"]

這就離真正建立 Task 又更近一步。


今天最重要的觀念

Day 25 我在確認:

AI 能不能回答?

Day 26 我在確認:

AI 的回答能不能被程式可靠地使用?

這兩件事其實差很多。

AI 回一句:

1. 蒐集資料:搜尋專題相關內容

對人很好懂。

但如果之後要存進資料庫,比較希望拿到:

{
  "title": "蒐集資料",
  "description": "搜尋專題相關內容"
}

這樣才有辦法穩定對應到:

Task.title
Task.description

所以今天真正完成的不是單純:

JSON 格式比較漂亮

而是:

AI 回傳資料
開始有固定契約

Day 26 的實際成果

目前可以整理成:

Groq Structured Outputs                 
openai/gpt-oss-20b strict: true          
JSON Schema                              
response_format=json_schema              
json.loads()                             
parse_tasks()                           
5~8 筆數量驗證                           
title / description 欄位驗證              
空字串驗證                               
多餘欄位驗證                              
14 個離線案例                             
多次真實 API 測試                          
英文輸入仍回繁體中文                      
短輸入可以處理                          
較長輸入可以處理                          
finish_reason 全部 stop                   
Day 25 API Key 錯誤處理仍正常              
沒有修改 Flask / MySQL / Task model       

目前還沒有做:

AI 結果建立 Task
寫入 MySQL
接進首頁
新增 AI route

所以專案現在停在:

AI 已經能產生
「程式看得懂的任務資料」

但還沒有真正寫進 Web App。


上一篇
[Day 25] 第一次串接 AI API:用 Groq 讓 AI 幫我拆解任務
下一篇
[Day 27] 調整 Prompt:讓 AI 不只格式正確,內容也更符合需求
系列文
從零開始 Claude Code:30 天打造 AI 任務管理 Web App 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言