AI 工作實務
FastAPI 入門實作:用報價 API 看懂型別驗證、錯誤回應與非同步邊界
FastAPI 能把 Python 函式整理成有明確輸入、輸出與文件的 API,但不會自動完成業務授權或讓模型推論加速。這份教學建立一個可在本機重跑的報價檢查服務,用金額邊界、非法請求與回應欄位測試,驗證介面是否符合約定。

FastAPI 能把 Python 函式整理成有明確輸入、輸出與文件的 API,但不會自動完成業務授權或讓模型推論加速。這份教學建立一個可在本機重跑的報價檢查服務,用金額邊界、非法請求與回應欄位測試,驗證介面是否符合約定。
專題編輯:Derek Chou|AI 技術與架構編輯 · 文件核對 2026.11.09

先把「模型會回答」改成可交接的介面
資料團隊在 Notebook 呼叫模型成功,與另一套系統能穩定使用它,是不同的成果。接手者至少需要知道必填欄位、資料型別、失敗狀態與輸出格式。如果 API 有時回傳文字、有時回傳物件,或把「需要人工覆核」混成「已核准」,前端即使收到 HTTP 200,也可能做出錯誤動作。
FastAPI 可依 Pydantic 模型讀取與驗證請求,並生成 OpenAPI 規格。本文把練習範圍限定在介面契約:建立 POST /quotes/review,接收合成客戶代碼、幣別與金額,回傳是否超過教學門檻。沒有呼叫 AI 模型、查詢真實客戶、儲存報價或執行核准。這樣可以先驗證最容易被忽略的交接條件,再決定是否接上外部系統。
這個範例究竟保證什麼?
| 欄位或行為 | 本次約定 | 驗收方式 |
|---|---|---|
customer_id | 去除首尾空白後,長度為 1 至 40 字元 | 空白或過長回傳 422 |
currency | 必填,僅接受 TWD | USD 或缺值回傳 422 |
amount | 正值,整數最多 10 位、小數最多 2 位(不計尾端零) | 零、負數、超出精度或範圍回傳 422 |
| 教學門檻 | 大於 100,000 才標示需覆核 | 分別測試 99,999.99、100,000.00、100,000.01 |
| 回應 | 只有 review_required 與 reason | 內部診斷欄位不能出現在 JSON |
金額以 Decimal 表達,請求範例使用字串傳遞十進位數值,避免先經用戶端浮點計算而失去精度。max_digits=12 搭配 decimal_places=2,在本例限制整數最多 10 位、小數最多 2 位,計算小數位時不計尾端零:"10.000" 可接受,"10.001" 則不合格。這是教學契約,不是宣稱所有新台幣業務都需要相同精度。幣別換算、稅額、四捨五入與真正授權門檻,都需要另外定義。
review_required=false 僅表示沒有超過這條示範規則;程式故意回傳「未代表核准」,避免接手者把檢查結果當成實際批准。
步驟一:建立隔離環境並確認版本
本次於 2026 年 11 月 9 日,在 macOS 13.7.8、Python 3.12.14 的獨立虛擬環境執行。使用 FastAPI 0.162.2、Pydantic 2.14.3、Starlette 1.10.0、Uvicorn 0.54.0、HTTPX 0.28.1 與 pytest 9.2.0。這是本次測試組合,不是所有平台或未來版本的相容性保證。
先下載並解壓 FastAPI 教學專案,在包含 main.py 的資料夾執行:
python3.12 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements-lock.txt
python -V
檔案包括 main.py、test_main.py、精確版本清單及 README。上面是 macOS/Linux 的啟用方式;Windows 可用 .venv\Scripts\Activate.ps1,但本輪未執行 Windows 驗證。若安裝出現版本或網路錯誤,先確認啟用的是這個 .venv,不要改動共用 Python 或忽略安裝失敗。
步驟二:用資料模型界定輸入與輸出
main.py 的核心如下;可直接在範例檔中對照與修改。
from decimal import Decimal
from typing import Literal
from fastapi import FastAPI
from pydantic import BaseModel, ConfigDict, Field
app = FastAPI(title="Quote Review Lab", version="1.0.0")
class QuoteInput(BaseModel):
model_config = ConfigDict(str_strip_whitespace=True, extra="forbid")
customer_id: str = Field(min_length=1, max_length=40)
currency: Literal["TWD"]
amount: Decimal = Field(gt=0, max_digits=12, decimal_places=2)
class QuoteResult(BaseModel):
review_required: bool
reason: str
@app.get("/health")
def health():
return {"status": "ok"}
@app.post("/quotes/review", response_model=QuoteResult)
def review_quote(payload: QuoteInput):
needs_review = payload.amount > Decimal("100000.00")
return {
"review_required": needs_review,
"reason": "超過教學門檻,須人工覆核" if needs_review else "未超過教學門檻,未代表核准",
"internal_rule": "LAB_ONLY_100000_TWD",
}
extra="forbid" 會拒絕契約外欄位,適合本次要及早發現拼字錯誤的練習;正式 API 是否允許未知欄位,則是版本相容性選擇。response_model 依對外模型驗證與過濾輸出,因此刻意放入的 internal_rule 不會傳給呼叫者。這不表示敏感資料可以任意塞進回應:日誌、例外訊息與其他端點仍須另外審查。
步驟三:先測邊界,再啟動伺服器
執行:
python -m pytest -q
範例測試直接透過 FastAPI 的 TestClient 執行 ASGI 應用,不需要連外。它不只確認正常請求有 200,也測試以下容易造成介面誤判的情況:
- 三個門檻值是否依大於關係分類,而不是不小心改成大於等於。
- 零、負數、扣除尾端零後仍超過兩位小數、11 位整數與
NaN是否被拒絕。 "10.000"是否可接受,以及"9999999999.99"是否仍在 10 位整數與 2 位小數的合法邊界內。- 空白客戶代碼、過長代碼、錯誤或缺少幣別、多餘欄位是否回傳 422。
- 正常回應是否只有兩個公開欄位,OpenAPI 是否列出三個必填欄位。
本次結果為 16 項測試通過。測試環境另出現 Starlette 對 HTTPX TestClient 的棄用提示;它沒有造成測試失敗,紀錄已保留。未來調整測試用戶端或升級依賴時,仍應重跑這些案例,不能只移除警告文字就當成相容。
想知道測試是否真的能擋錯,可以在自己的練習副本把門檻比較改為 >= 再執行;100000.00 這個案例應失敗。還原後必須恢復全部通過。這是讀者可做的故障練習,不能把未執行的改動當成本文已測結果。
步驟四:在本機呼叫並檢視文件
啟動只綁定本機的伺服器:
python -m uvicorn main:app --host 127.0.0.1 --port 8000
開啟 http://127.0.0.1:8000/docs 可檢視互動文件;/openapi.json 是機器可讀的契約。在另一個終端機送出:
curl -i http://127.0.0.1:8000/quotes/review \
-H 'Content-Type: application/json' \
-d '{"customer_id":"SYNTHETIC-001","currency":"TWD","amount":"100000.01"}'
預期為 HTTP 200,review_required 是 true,且沒有 internal_rule。把金額改成 "0",應得到 HTTP 422 與欄位驗證錯誤。注意 422 是這個請求不符合模型的訊號,不代表框架當機;應修正輸入,而不是把驗證關掉。
本輪自動驗證涵蓋 TestClient 的請求、回應與文件路徑;上面的 Uvicorn/curl 是可重跑的本機呼叫方式,不能據此推論已完成對外部署、瀏覽器互動或壓力測試。練習結束以 Ctrl+C 停止伺服器。
步驟五:決定同步、非同步與長任務的界線
本例只做短時間的條件比較,所以使用一般 def,目的是讓新手先看懂契約。FastAPI 會把它呼叫的一般同步路由函式放到執行緒池處理;但你在 async def 裡直接呼叫的普通工具函式,不會因此自動搬到背景。若其中執行阻塞式 SDK 或耗時運算,就可能拖住事件迴圈。
接上模型時,先查 SDK 是否提供真正的非同步 I/O,再安排逾時、併發上限與取消。CPU 密集推論需要評估程序或獨立服務;很長的文件解析則可考慮工作佇列,回傳任務 ID,讓用戶查詢狀態。這些是後續架構選擇,本文沒有做模型延遲、排隊容量或效能排名的實驗。
重試也不能只靠「失敗就再送」。若端點可能新增訂單或扣款,先設計冪等識別與重複請求處理;否則網路逾時可能只是回應遺失,服務端其實已經完成動作。本例沒有副作用,仍不能當成所有 API 都可安全重試的證明。
常見失敗要對應哪個修正?
| 現象 | 優先確認 | 恢復方式 |
|---|---|---|
No module named fastapi | 終端機是否使用正確 .venv | 啟用環境,再以 python -m pip 安裝 |
Could not import module main | 執行位置是否包含 main.py | 切回範例根目錄 |
| 8000 埠無法啟動 | 是否已有自己的練習服務 | 停止原服務或改用其他本機埠,並同步改請求網址 |
| 正常業務資料回傳 422 | 契約是否只接受 TWD、兩位小數及正值 | 檢查 detail,由 API 設計者決定是否修改契約 |
| 所有測試通過卻無法給同事用 | 是否只有本機介面測試 | 另驗證認證、授權、部署網路及實際整合 |
從教學 API 到服務交付,還差哪些證據?
這個專案沒有登入、資料庫、配額或機密管理,因此只應使用合成資料並保留在本機。/health 也只表示程式能回覆,沒有證明模型、資料庫或下游服務已就緒。正式化時需要依真實用途補上使用者與租戶權限、請求大小限制、稽核日誌、監控告警及回復流程。
交接時,最有用的成果是讓另一位工程師用相同版本重跑範例,說清楚 200、422 與業務核准之間的差別,並能指出尚未實作的能力。框架解決的是介面開發的一部分;讓資料、決策與責任對得起來,仍要靠具體測試與流程設計。
官方資料與素材說明
- FastAPI 請求資料模型:請求驗證及 OpenAPI。
- FastAPI 回應模型:回應驗證與欄位過濾。
- FastAPI 同步與非同步:路由函式、執行緒池與工具函式的差異。
- Pydantic 標準型別:Decimal 的精度約束與序列化。
本文程式、測試與情境為教學用途,資料均為合成資料。封面沿用提供之原創概念圖,不是產品操作截圖;本文未宣稱真實企業核准成效或對外部署結果。
內容提供:TARAISG。本文由 AI 協助整理與查核;專題編輯為天地人 AI 編輯計畫的策展角色,未宣稱經具名人工或專家審核。
本文由 TARAISG(TIANDIREN AI SOLUTION GROUP)使用 AI 模型產生正文、整理來源並設計實務工具。方法依據與限制列於文末;案例為明確標示的教學假設。未有具名紀錄時,不宣稱經人工或專家審核。
查看內容製作與更正說明