跳到內容

進階★★★★★9 分鐘閱讀

從 LLM 拿結構化輸出:tool use、JSON mode、schema

讓模型吐有效 JSON 的三條路、各自什麼時候贏,以及 production 上會嚇到你的失敗模式。

登入以收藏

2026 年大半的 production LLM 應用要模型吐特定形狀的 JSON,不是自由文字。從履歷抽欄位、把客戶意圖分到 12 類之一、生成有命名步驟的行動計畫。三年前你只能對自由文字跑 regex 然後祈禱,現在有三個真實選項,各有取捨。

三條路

  1. JSON mode。 跟模型說「輸出有效 JSON」,讓它自己想形狀。便宜、低額外負擔、沒 schema 強制。
  2. Tool use / function calling。 定義一個工具,給它的參數 JSON schema。即使工具是虛構的,模型被強制吐出符合 schema 的參數(配合 strict mode)。
  3. 約束解碼 / strict 結構化輸出。 廠商在 sampling 時強制 schema 合規 —— 不合 schema 的 token 直接 mask 掉,模型字面意義不能吐出無效 JSON。

2026 年三種在主要廠商都有,但功能名跟限制不太一樣。

JSON mode:最簡單的工具

response = client.chat.completions.create(
    model="gpt-5",
    response_format={"type": "json_object"},
    messages=[
        {"role": "system", "content": "You output a JSON object with fields name, email, role."},
        {"role": "user", "content": "..."},
    ],
)

模型吐某個 JSON 物件,你 parse 它。system prompt 把 schema 講清楚通常會中。

強項: 最便宜、普遍支援、好設定。

弱項:

  • Schema 強制是 best-effort,模型可能漏欄位或加多餘的。
  • 沒型別保證。「age」可能回 "forty" 不是 40
  • 你還是要在下游寫 Pydantic / zod 驗證。

Schema 簡單、成本要省、下游能驗證 —— 用 JSON mode。

Tool use:schema 強制 JSON

定義一個其實不是工具的工具,只是用來約束輸出:

tools = [{
    "type": "function",
    "function": {
        "name": "submit_extraction",
        "description": "提交抽取出的欄位。",
        "strict": True,
        "parameters": {
            "type": "object",
            "properties": {
                "name": {"type": "string"},
                "email": {"type": "string", "format": "email"},
                "age": {"type": "integer", "minimum": 0, "maximum": 120},
                "role": {"type": "string", "enum": ["engineer", "designer", "manager", "other"]}
            },
            "required": ["name", "email", "role"],
            "additionalProperties": False
        }
    }
}]

response = client.chat.completions.create(
    model="gpt-5",
    tools=tools,
    tool_choice={"type": "function", "function": {"name": "submit_extraction"}},
    messages=[...],
)
args = json.loads(response.choices[0].message.tool_calls[0].function.arguments)

強項:

  • strict: True,OpenAI 保證輸出符合 schema。沒無效 JSON、沒缺欄位、沒型別錯。Anthropic 跟 Gemini 有類似 strict mode。
  • enum 限制特定值。role 永遠不會回 "chief vibemaster"
  • Schema 是模型輸入的一部分 —— 比「system prompt 說輸出 X」遵守得更好。

弱項:

  • Strict 加延遲(廠商每次 request 把 schema 編譯成約束解碼狀態機),通常慢 10-20%。
  • Strict 有微妙限制:root 不能 oneOf、所有欄位必須 required、遞迴 schema 受限。讀廠商文件。
  • 強迫 tool-call 回應形狀,你要去 args 挖出來。

2026 年 production 抽取的對的預設值。

約束解碼(strict 結構化輸出)

OpenAI 的 response_format={"type": "json_schema", "strict": true, ...} 跟 Anthropic / Gemini 對應的版本,是 tool-use-as-extraction 的乾淨版:

response = client.chat.completions.create(
    model="gpt-5",
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "extraction",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "role": {"type": "string", "enum": ["engineer", "designer", "manager"]}
                },
                "required": ["name", "role"],
                "additionalProperties": False
            }
        }
    },
    messages=[...],
)
result = json.loads(response.choices[0].message.content)

跟 strict tool use 一樣的保證,但 response 是普通 content(不是 tool call)。概念上不需要「函式」包裝就用這個更乾淨。

自架(vLLM、llama.cpp)透過 Outlines、llguidance、XGrammar 等函式庫實作,在 sampling 時 mask 無效 token。一樣保證,不打廠商 API。

開 strict 之後實際發生什麼

模型字面意義不能吐無效 JSON。decoder 每個 token step,看所有 20 萬個 vocabulary token,檢查哪些能讓輸出在已 emit 的 token 跟 schema 下保持有效,只允許從有效 token 採樣。

這代表:

  • schema 寫 integer,你永遠不會拿到 "age": "forty"
  • 永遠不會缺 required 欄位。
  • 永遠不會 JSON 語法錯。
  • 還是可能拿到語意錯的資料 —— "age": 999 是有效 integer,只是錯。

驗證是結構性的,不是事實性的。你還是要 sanity check 範圍、商業規則、內容品質。

production 上的五個驚喜

  1. Strict 對複雜巢狀 schema 失敗。 有些 schema 廠商編譯不了(深巢狀、遞迴、太多 enum 值)。你會看到 400 錯誤。要嘛簡化,要嘛 fall back 到沒 strict 的 tool use。
  2. 延遲負擔是真的。 簡單抽取從 200ms 變 280ms。批次抽取就重要了。
  3. 模型偶爾會拒答。 「我無法從輸入判斷 X」—— 它必須符合 schema,但 schema 要求模型推不出的欄位,可能吐 placeholder 或呼叫失敗。加 "unknown" enum 值或讓選填欄位真的選填。
  4. 跨廠商 schema 漂移。 OpenAI、Anthropic、Gemini 的 schema 方言略不同。OpenAI 過的 schema Anthropic 可能失敗。換廠商要測。
  5. 長 enum 的成本。 200 個 enum 值的 role 欄位讓 schema 膨脹加延遲。選項多就考慮自由 string + 下游驗證,或拆成 category + subcategory。

怎麼挑

  • 一次性原型、schema 簡單、會下游驗證: JSON mode。出貨最快。
  • Production 抽取、不能壞: strict tool use 或 strict response_format json_schema。schema 是你的合約。
  • Schema 巨大或複雜(很多 enum、巢狀、遞迴): 沒 strict 的 tool use,下游用 Pydantic / zod 驗。99% 遵守而且避免廠商編譯錯。
  • 自架想要保證: Outlines 或 XGrammar 加 vLLM。Strict 沒每呼叫成本。
  • 階層任務(計畫 + 步驟 + 每步驟參數): Tool use,每個「工具」代表一個步驟類型。模型吐一連串 tool call。

函式庫輔助

2026 年熱門 wrapper:

  • Pydantic + Instructor(Python)。 instructor.from_openai(client) 給你 client.chat.completions.create(response_model=MyModel, ...),回 typed Pydantic 物件。藏掉 strict mode 接線。
  • zod-to-json-schema(TypeScript)。 zod schema 轉 JSON schema,送 API,response 過 zod 解。雙向型別安全。
  • Outlines(Python)。 本地模型或想完全控制約束演算法時。

大部分團隊:Instructor(Python)或 zod(TypeScript)疊在 strict API 上。除非你在做奇怪的事,別自己寫驗證層。

什麼時候不要用結構化輸出

結構化輸出對抽取、分類、計畫生成很棒,對這些不好:

  • 長篇自然語言輸出。 別硬塞文章到 {"text": "..."} schema,直接用純文字。
  • 創意寫作。 Schema 約束降低創意,可量測微小;在乎品質就跳過。
  • 使用者可見 UI 的 streaming。 Strict 結構化輸出 streaming 不順。聊天 UI 純文字比較友善。

下一步

  • OpenAI 結構化輸出文件。
  • Anthropic tool use 文件。
  • Outlines 函式庫(開源結構化生成)。
  • Efficient Guided Generation for Large Language Models(Willard & Louf, 2023)。
  • 查這些詞:JSON Schema、Pydantic、Instructor library、XGrammar。

最後更新: 2026-04-29

We use cookies

Anonymous analytics help us improve the site. You can opt out anytime. Learn more