在前面的系列文章中,我們學習了如何使用 Anthropic Messages API 進行對話,也掌握了 Tool Use 讓 Claude 呼叫外部工具的能力。但在實際開發中,你一定遇過這樣的困境:Claude 回傳的 JSON 格式不穩定、缺少必要欄位、甚至回傳了無法解析的文字。Structured Output(結構化輸出)正是 Anthropic 提供的解決方案——它能保證 Claude 的回應完全符合你定義的 JSON Schema,徹底消除解析錯誤。
為什麼需要 Structured Output?
在沒有結構化輸出的情況下,即使你在 Prompt 中明確要求 Claude 回傳 JSON 格式,仍然可能遭遇以下問題:
- JSON 語法錯誤:回傳的內容夾帶了說明文字,導致
JSON.parse()失敗 - 欄位缺失:必要欄位有時出現、有時不出現,後續程式邏輯崩潰
- 型別不一致:預期數字卻收到字串,或陣列變成物件
- 需要重試機制:為了處理格式問題,不得不加入重試邏輯,增加延遲和成本
Structured Output 透過受限解碼(Constrained Decoding)技術,在生成階段就強制 Claude 的輸出遵循指定的 Schema。這意味著:回應永遠是合法的 JSON、欄位型別保證正確、不需要重試機制。
兩種結構化輸出方式
Anthropic 提供了兩種互補的結構化輸出功能,可以獨立使用或在同一個請求中組合使用:
| 功能 | 參數 | 用途 | 說明 |
|---|---|---|---|
| JSON Mode | output_config.format | 控制 Claude 的回應格式 | 讓 Claude 的最終回應符合你定義的 JSON Schema |
| Strict Tool Use | strict: true | 保證工具輸入參數的格式 | 確保 Claude 呼叫工具時傳入的參數符合 Schema 驗證 |
JSON Mode:output_config.format
JSON Mode 是最常用的結構化輸出方式,它讓 Claude 的回應直接以符合你指定 Schema 的 JSON 格式輸出。適用場景包括:資料擷取、分類任務、格式化 API 回應、產生結構化報告等。
基本用法
使用 JSON Mode 的核心是在 API 請求中加入 output_config.format 參數,指定 type: "json_schema" 並附上你的 Schema 定義:
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=1024,
messages=[
{
"role": "user",
"content": "從這封信中擷取重要資訊:王小明 (wang@example.com) 對企業方案有興趣,想安排下週二下午兩點的展示會議。",
}
],
output_config={
"format": {
"type": "json_schema",
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"email": {"type": "string"},
"plan_interest": {"type": "string"},
"demo_requested": {"type": "boolean"},
},
"required": ["name", "email", "plan_interest", "demo_requested"],
"additionalProperties": False,
},
}
},
)
print(response.content[0].text)
回應會是合法的 JSON,直接在 response.content[0].text 中取得:
{
"name": "王小明",
"email": "wang@example.com",
"plan_interest": "企業方案",
"demo_requested": true
}
使用 SDK 輔助工具
各語言的 SDK 提供了更方便的方式定義 Schema,不需要手寫 JSON Schema。以下是 Python(Pydantic)和 TypeScript(Zod)的範例:
from pydantic import BaseModel
from anthropic import Anthropic
class ContactInfo(BaseModel):
name: str
email: str
plan_interest: str
demo_requested: bool
client = Anthropic()
response = client.messages.parse(
model="claude-sonnet-4-5-20250514",
max_tokens=1024,
messages=[
{
"role": "user",
"content": "從這封信中擷取重要資訊:王小明...",
}
],
output_format=ContactInfo,
)
# 直接取得型別安全的 Python 物件
contact = response.parsed_output
print(contact.name, contact.email)
import Anthropic from "@anthropic-ai/sdk";
import { z } from "zod";
import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";
const ContactInfoSchema = z.object({
name: z.string(),
email: z.string(),
plan_interest: z.string(),
demo_requested: z.boolean(),
});
const client = new Anthropic();
const response = await client.messages.parse({
model: "claude-sonnet-4-5-20250514",
max_tokens: 1024,
messages: [
{
role: "user",
content: "從這封信中擷取重要資訊:王小明...",
},
],
output_config: { format: zodOutputFormat(ContactInfoSchema) },
});
// 自動解析並驗證,取得型別安全的物件
console.log(response.parsed_output);
Strict Tool Use:保證工具參數格式
除了控制 Claude 的回應格式,你也可以在工具定義中加入 strict: true,確保 Claude 呼叫工具時傳入的參數一定符合 Schema。這對於建構可靠的 AI Agent 工作流程至關重要。
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=1024,
messages=[
{"role": "user", "content": "幫我查詢台北到東京的機票"}
],
tools=[
{
"name": "search_flights",
"description": "搜尋可用航班",
"strict": True, # 啟用嚴格模式
"input_schema": {
"type": "object",
"properties": {
"origin": {
"type": "string",
"description": "出發城市"
},
"destination": {
"type": "string",
"description": "目的地城市"
},
"date": {
"type": "string",
"format": "date",
"description": "出發日期 (YYYY-MM-DD)"
}
},
"required": ["origin", "destination", "date"],
"additionalProperties": False
}
}
]
)
啟用 strict: true 後,Claude 生成的工具呼叫參數保證符合你定義的 Schema,不會出現缺少必要欄位或型別錯誤的情況。
組合使用 JSON Mode + Strict Tool Use
你可以在同一個請求中同時使用 JSON Mode 和 Strict Tool Use,讓 Claude 的工具呼叫參數和最終回應都受到 Schema 約束:
response = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=1024,
messages=[
{"role": "user", "content": "幫我規劃巴黎之旅,5月15日出發"}
],
# JSON Mode:控制最終回應格式
output_config={
"format": {
"type": "json_schema",
"schema": {
"type": "object",
"properties": {
"summary": {"type": "string"},
"next_steps": {
"type": "array",
"items": {"type": "string"}
}
},
"required": ["summary", "next_steps"],
"additionalProperties": False
}
}
},
# Strict Tool Use:保證工具參數格式
tools=[
{
"name": "search_flights",
"description": "搜尋航班",
"strict": True,
"input_schema": {
"type": "object",
"properties": {
"destination": {"type": "string"},
"date": {"type": "string", "format": "date"}
},
"required": ["destination", "date"],
"additionalProperties": False
}
}
]
)
JSON Schema 定義規則
結構化輸出使用標準 JSON Schema 格式來定義期望的輸出結構。以下是撰寫 Schema 時需要注意的規則:
支援的 Schema 功能
| 功能 | 說明 |
|---|---|
| 基本型別 | object、array、string、integer、number、boolean、null |
enum | 支援字串、數字、布林值、null(不支援複雜型別) |
const | 常數值 |
anyOf / allOf | 聯合型別與交集型別(allOf 搭配 $ref 不支援) |
$ref / $def | 內部引用定義(不支援外部 $ref) |
required | 必填欄位 |
additionalProperties | 必須設為 false |
| 字串格式 | date-time、date、time、email、uri、uuid、ipv4、ipv6 等 |
pattern | 支援基本正規表達式(不支援 lookahead/lookbehind) |
minItems | 僅支援值為 0 或 1 |
不支援的功能
- 遞迴 Schema:不支援自我引用的結構
- 數值限制:
minimum、maximum、multipleOf等不支援 - 字串長度限制:
minLength、maxLength不支援 - 陣列進階限制:
minItems超過 1、maxItems不支援 - 外部引用:
$ref指向外部 URL 不支援 - additionalProperties:不能設為
true或指定 Schema
好消息是,Python 和 TypeScript SDK 會自動轉換不支援的 Schema 特性——移除不支援的限制,並將條件資訊加入欄位描述中,讓 Claude 仍能理解你的意圖。例如 Pydantic 中的 minimum: 100 會被轉換為描述文字「Must be at least 100」。
Schema 複雜度限制
結構化輸出會將 JSON Schema 編譯成文法(Grammar),複雜的 Schema 會產生較大的文法。API 有以下明確限制:
| 限制項目 | 上限值 | 說明 |
|---|---|---|
| Strict 工具數量 | 20 | 每次請求最多 20 個 strict: true 的工具 |
| 可選參數數量 | 24 | 所有 strict Schema 中未列入 required 的參數總和 |
| 聯合型別參數 | 16 | 使用 anyOf 或型別陣列的參數總和 |
| 編譯逾時 | 180 秒 | Schema 編譯的最大時間限制 |
驗證與錯誤處理
雖然結構化輸出在絕大多數情況下保證 Schema 合規,但有兩種特殊情境需要處理:
安全拒絕(Refusal)
當 Claude 基於安全原因拒絕回應時,回傳的 stop_reason 會是 "refusal"。此時輸出可能不符合 Schema,因為拒絕訊息優先於 Schema 限制。你需要在程式中檢查 stop_reason:
response = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=1024,
messages=[{"role": "user", "content": "..."}],
output_config={...}
)
# 檢查是否被拒絕
if response.stop_reason == "refusal":
print("Claude 基於安全原因拒絕了此請求")
elif response.stop_reason == "end_turn":
# 正常回應,可以安全解析 JSON
import json
data = json.loads(response.content[0].text)
print(data)
Token 上限截斷
當回應因為達到 max_tokens 限制而被截斷時,stop_reason 會是 "max_tokens"。被截斷的 JSON 可能不完整且無法解析。解決方式是增加 max_tokens 值後重試。
完整錯誤處理模式
import json
import anthropic
client = anthropic.Anthropic()
def get_structured_response(prompt, schema):
"""安全地取得結構化輸出"""
try:
response = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=4096,
messages=[{"role": "user", "content": prompt}],
output_config={
"format": {
"type": "json_schema",
"schema": schema
}
}
)
# 檢查停止原因
if response.stop_reason == "refusal":
return {"error": "request_refused", "message": "Claude 拒絕處理此請求"}
if response.stop_reason == "max_tokens":
return {"error": "truncated", "message": "回應被截斷,請增加 max_tokens"}
# 正常解析
return json.loads(response.content[0].text)
except anthropic.APIError as e:
return {"error": "api_error", "message": str(e)}
實際應用範例
以下是三個常見的結構化輸出應用場景,展示如何在實際專案中活用這項功能。
範例一:發票資料擷取
從非結構化文字中擷取結構化資料,是 Structured Output 最經典的應用。以下範例展示如何從發票文字中擷取關鍵欄位:
from pydantic import BaseModel
from typing import List
from anthropic import Anthropic
class LineItem(BaseModel):
description: str
quantity: int
unit_price: float
class Invoice(BaseModel):
invoice_number: str
date: str
total_amount: float
line_items: List[LineItem]
customer_name: str
client = Anthropic()
invoice_text = """
發票編號:INV-2026-0042
日期:2026年5月15日
客戶:台灣科技股份有限公司
品項:
1. 雲端伺服器租賃 x 12個月 - NT$8,000/月
2. SSL 憑證 x 1 - NT$3,500
3. 技術支援方案 x 1 - NT$15,000
總計:NT$114,500
"""
response = client.messages.parse(
model="claude-sonnet-4-5-20250514",
max_tokens=4096,
output_format=Invoice,
messages=[
{"role": "user", "content": f"從以下發票中擷取結構化資料:\n{invoice_text}"}
],
)
invoice = response.parsed_output
print(f"發票號碼:{invoice.invoice_number}")
print(f"客戶:{invoice.customer_name}")
print(f"總金額:{invoice.total_amount}")
for item in invoice.line_items:
print(f" - {item.description}: {item.quantity} x {item.unit_price}")
範例二:客戶回饋分類
使用結構化輸出進行文字分類,可以同時取得分類標籤、信心分數和情感分析結果:
from pydantic import BaseModel
from typing import List
from anthropic import Anthropic
class FeedbackClassification(BaseModel):
category: str # 產品、服務、物流、價格...
confidence: float # 0.0 ~ 1.0
tags: List[str] # 相關標籤
sentiment: str # positive / negative / neutral
summary: str # 一句話摘要
client = Anthropic()
feedbacks = [
"產品品質很好,但物流速度太慢了,等了快兩週才收到",
"客服態度非常好,幫我快速解決了退款問題",
"價格比競品貴了不少,希望能有更多折扣方案",
]
for feedback in feedbacks:
response = client.messages.parse(
model="claude-sonnet-4-5-20250514",
max_tokens=1024,
output_format=FeedbackClassification,
messages=[
{"role": "user", "content": f"分類以下客戶回饋:{feedback}"}
],
)
result = response.parsed_output
print(f"類別:{result.category} | 情感:{result.sentiment}")
print(f"信心度:{result.confidence} | 標籤:{', '.join(result.tags)}")
print(f"摘要:{result.summary}")
print("---")
範例三:結構化分析報告
生成包含多層巢狀結構的分析報告,適合用於自動化報表系統:
from pydantic import BaseModel
from typing import List, Optional
from anthropic import Anthropic
class Metric(BaseModel):
name: str
value: float
unit: str
trend: str # up / down / stable
class Recommendation(BaseModel):
priority: str # high / medium / low
action: str
expected_impact: str
class AnalysisReport(BaseModel):
title: str
executive_summary: str
key_metrics: List[Metric]
strengths: List[str]
weaknesses: List[str]
recommendations: List[Recommendation]
risk_level: str # low / medium / high
client = Anthropic()
response = client.messages.parse(
model="claude-sonnet-4-5-20250514",
max_tokens=4096,
output_format=AnalysisReport,
messages=[
{
"role": "user",
"content": """分析以下業務數據並產生報告:
Q1 營收:NT$2,500萬(年增 15%)
客戶留存率:87%(下降 3%)
新客戶數:156(年增 22%)
平均客單價:NT$12,000(持平)
客訴率:2.1%(上升 0.5%)"""
}
],
)
report = response.parsed_output
print(f"報告:{report.title}")
print(f"摘要:{report.executive_summary}")
print(f"風險等級:{report.risk_level}")
Prompt Engineering 搭配結構化輸出
雖然結構化輸出保證了格式的正確性,但回應的內容品質仍然取決於你的 Prompt。以下是幾個搭配使用的技巧:
- 在 Schema 中加入描述:善用 JSON Schema 的
description欄位,告訴 Claude 每個欄位期望的內容。例如"confidence": {"type": "number", "description": "介於 0 到 1 之間的信心分數"} - 使用 enum 限制值域:對於有限選項的欄位,使用
enum限制可選值。例如"sentiment": {"type": "string", "enum": ["positive", "negative", "neutral"]} - 在 Prompt 中提供範例:即使有 Schema 限制,在 Prompt 中附上一個期望輸出的範例,仍能顯著提升回應品質
- 善用 System Prompt:在 system 訊息中設定角色和任務背景,讓 Claude 更準確地理解你需要的資訊
- 分層設計 Schema:對於複雜輸出,使用巢狀物件和陣列來組織結構,而不是把所有欄位放在最外層
效能考量與最佳實踐
使用結構化輸出時,有幾個效能面向需要注意:
文法編譯與快取
- 首次請求延遲:第一次使用特定 Schema 時,需要額外時間編譯文法
- 自動快取:編譯後的文法會快取 24 小時,後續請求速度更快
- 快取失效:修改 Schema 結構或工具集合會使快取失效(僅修改
name或description不會)
Token 成本
使用結構化輸出時,Claude 會自動收到一個額外的系統提示來說明輸出格式,這意味著你的輸入 Token 數會略微增加。此外,修改 output_config.format 會使 Prompt Cache 失效。
降低複雜度的策略
- 只對關鍵工具啟用 strict:不需要每個工具都開
strict: true,Claude 本身就有不錯的 Schema 遵循能力 - 減少可選參數:盡量將參數設為
required,每個可選參數都會大幅增加文法的狀態空間 - 簡化巢狀結構:深層巢狀加上可選欄位會使複雜度倍增,盡量扁平化
- 分拆請求:如果工具數量太多,考慮拆成多個請求或使用 Sub-Agent 架構
支援的模型與平台
結構化輸出目前已在以下模型和平台上正式可用(GA):
| 模型 | 平台支援 |
|---|---|
| Claude Opus 4.6 | Anthropic API、Amazon Bedrock |
| Claude Sonnet 4.6 | Anthropic API、Amazon Bedrock |
| Claude Sonnet 4.5 | Anthropic API、Amazon Bedrock |
| Claude Opus 4.5 | Anthropic API、Amazon Bedrock |
| Claude Haiku 4.5 | Anthropic API、Amazon Bedrock |
在 Microsoft Foundry 上目前仍為 Beta 階段。
功能相容性
結構化輸出可以與大部分 API 功能搭配使用,但有部分例外需要注意:
| 功能 | 相容性 | 說明 |
|---|---|---|
| Batch Processing | ✅ 支援 | 可大量處理結構化輸出,享有 50% 折扣 |
| Streaming | ✅ 支援 | 可以串流接收結構化輸出 |
| Token Counting | ✅ 支援 | 可計算 Token 數量,不觸發編譯 |
| JSON + Strict Tool Use | ✅ 支援 | 可在同一請求中同時使用 |
| Citations | ❌ 不相容 | 引用功能與嚴格 JSON Schema 衝突 |
| Message Prefilling | ❌ 不相容 | 不可與 JSON Mode 同時使用 |
延伸閱讀
- Anthropic 官方文件:Structured Outputs
- Anthropic 官方文件:Tool Use Overview
- Anthropic 官方文件:Increase Output Consistency
- Anthropic Cookbook:Extracting Structured JSON
- JSON Schema 官方指南
總結
Structured Output 是 Anthropic API 中非常實用的功能,它從根本上解決了 LLM 輸出格式不穩定的問題。透過 output_config.format(JSON Mode)和 strict: true(Strict Tool Use)兩種方式,你可以確保 Claude 的每一次回應都是合法、可解析、符合 Schema 的結構化資料。
在實際應用中,建議從簡單的 Schema 開始,善用 SDK 提供的 Pydantic / Zod 輔助工具,並搭配良好的 Prompt Engineering 來提升內容品質。隨著你對功能的熟悉,可以逐步處理更複雜的巢狀結構和多工具場景。
下一篇文章中,我們將探討更多 Anthropic API 的進階功能。敬請期待!