微調結構化輸出:JSON Schema 合規
JSON 模式讓你得到有效的 JSON。微調讓你得到有保證的 schema 合規性:每個欄位、每種類型、每次都正確。以下是訓練模型的方法。
你的應用程式期望一個包含確切 8 個欄位的 JSON 物件,每個欄位有特定類型,其中兩個為枚舉值,以及一個帶有自身 schema 的巢狀物件陣列。你要求 GPT-4 生成它。大多數時候,你得到了你想要的。有時你得到 7 個欄位。偶爾你得到一個字串而不是預期的整數。偶爾模型會發明一個不存在的枚舉值。
在 95% 的 schema 合規率下,每 20 次 API 呼叫就有 1 次產生你的 解析器無法處理的輸出。如果你的應用程式每天進行 10,000 次結構化輸出呼叫,那就是每天 500 次失敗。每天都是。你的錯誤處理代碼變得比你的實際業務邏輯更複雜。你添加重試、備用提示、後處理修復器。系統可以工作,但很脆弱——靠膠帶和重試循環維持。
微調改變了這個方程式。一個在 500-1,000 個你確切 schema 範例上訓練的模型不會偏移、不會幻覺欄位、不會發明枚舉值。Schema 合規率從提示的 95% 提升到微調後的 99.5% 以上。在每天 10,000 次呼叫的情況下,這是 500 次失敗和 50 次之間的差異。
結構化輸出的層次
結構化輸出有一個層次體系,從最不可靠到最可靠:
第 1 級:基於提示(「請輸出 JSON」)
在提示中要求模型生成 JSON。包含範例。期望最好的結果。
- 合規率: 80-90%
- 失敗模式: 無效的 JSON(缺少引號、尾部逗號)、缺少欄位 、錯誤類型、額外欄位、Markdown 包裝
- 何時使用: 僅用於原型開發
第 2 級:JSON 模式
OpenAI 的 JSON 模式,或其他 API 中的等效設定。強制模型輸出語法有效的 JSON。
- 合規率: 95-98%(有效 JSON,但 schema 合規性有所不同)
- 失敗模式: 不符合你的 schema 的有效 JSON——缺少必填欄位、錯誤欄位名稱、類型不匹配、額外欄位
- 何時使用: 當你需要有效 JSON 但可以容忍 schema 偏移時
第 3 級:Function Calling / Structured Outputs API
OpenAI 搭配 JSON schema 的結構化輸出,或 function-calling 端點。API 在解碼層面強制執行 schema。
- 合規率: schema 結構 99% 以上
- 失敗模式: 結構正確但值錯誤——幻覺的枚舉值、語義錯誤的內容、預期有內容的地方卻是空字串
- 何時使用: 當你需要來自雲端 API 的 schema 合規性,且可以接受每 token 成本時
第 4 級:微調模型
在數百個你確切 schema 範例上訓練的模型。了解欄位名稱、類型、有效值和語義期望。
- 合規率: 99.5-99.9%
- 失敗模式: 訓練分佈之外的遠端輸入上的罕見邊緣案例
- 何時使用: 高流量的生產系統,可靠性和成本很重要
第 5 級:微調模型 + 約束解碼
帶有約束解碼的微調模型(llama.cpp grammar、Outlines 或 guidance),使無效 token 成為不可能。
- 合規率: 100% 結構合規性
- 失敗模式: 結構完美的 JSON,但值在語義上錯誤(微調後很少見)
- 何時使用: 當你需要零結構失敗且正在運行本地推論時
為何提示有上限
基於提示的結構化輸出的根本問題:模型按順序生成 token,沒有任何機制阻止它生成違反你的 schema 的 token。
當你提示 GPT-4 輸出 {"status": "approved" | "denied" | "pending"} 時,模型可能在一次呼叫中生成 "status": "approved",在下一次生成 "status": "Approved"。或者 "status": "approve"。每個都是有效的 JSON 字串——模型沒有限制說「只有這三個確切值是可接受的」。
更詳細的提示有更詳細的 schema 有幫助,但它們達到收益遞減:
- 1 行 schema 描述:約 85% 合規率
- 詳細 schema 帶範例:約 92% 合規率
- Schema + 少樣本範例 + 明確約束:約 95-97% 合規率
你無法通過提示達到 99.5%。模型的 token 生成是概率性的。無論你的提示多好,生成錯誤 token 的概率永遠不會恰好為零。
微調直接改變模型的概率分佈。在 500 個範例訓練後,其中 status 始終恰好是 "approved"、"denied" 或 "pending",模型為該欄位的任何其他值分配接近零的概率。合規性來自權重,而不是提示。
構建符合 Schema 的訓練資料集
訓練資料的品質決定了你的 schema 合規性品質。以下是如何構建產生可靠結構化輸出的資料集。
第 1 步:正式定義你的 Schema
從 JSON Schema 規 範開始:
{
"type": "object",
"required": ["id", "status", "score", "category", "findings", "metadata"],
"properties": {
"id": {"type": "string", "pattern": "^RPT-[0-9]{6}quot;},
"status": {"type": "string", "enum": ["approved", "denied", "pending", "review"]},
"score": {"type": "number", "minimum": 0, "maximum": 100},
"category": {"type": "string", "enum": ["financial", "operational", "compliance", "technical"]},
"findings": {
"type": "array",
"items": {
"type": "object",
"required": ["description", "severity", "recommendation"],
"properties": {
"description": {"type": "string"},
"severity": {"type": "string", "enum": ["low", "medium", "high", "critical"]},
"recommendation": {"type": "string"}
}
}
},
"metadata": {
"type": "object",
"required": ["analyst", "date", "version"],
"properties": {
"analyst": {"type": "string"},
"date": {"type": "string", "format": "date"},
"version": {"type": "string"}
}
}
}
}第 2 步:生成多樣化的有效範例
你需要 500-1,000 個訓練範例。每個都必須是你的 schema 的完全有效實例。生成它們有三種方法:
來自生產資料: 如果你有遵循此 schema 的現有資料(來自資料庫、來自之前的 API 輸出),將其轉換為訓練範例。這是最佳來源,因為它反映了真實世界的值分佈。
來自 GPT-4 加驗證: 使用 GPT-4 生成多樣化範例,然後根據你的 JSON Schema 驗證每個範例。丟棄任何未通過驗證的範例。在 GPT-4 95% 合規率下,你將丟棄約 1/20——對資料集生成來說可以接受。通過你的實際解析器再次檢查已驗證的範例。
程序化生成: 編寫一個腳本,隨機生成你的 schema 的有效實例。這保證了結構有效性,但可能缺乏語義連貫性。用它來補充真實範例,而不是作為唯一來源。
第 3 步:讓輸入多樣化,而不僅僅是輸出
你的訓練資料需要與符合 schema 的輸出配對的多樣化輸入。如果每個輸入都是「分析這份財務報告」,模型學到了 schema 但無法泛化到新的輸入措辭。
包含變體:
- 不同的輸入長度(一句話到三段)
- 不同的輸入格式(正式請求、隨意問題、結構化指令)
- 不同的上下文級別(完整資訊 vs. 部分資訊)
- 邊緣案例(模糊輸入、缺少資訊的輸入、應該生成空陣列的輸入)
第 4 步:明確包含邊緣案例
提示模型失敗最多的地方是邊緣案例。確保你的訓練資料覆蓋:
- 空陣列: 當
findings應該是[]因為沒有發現時 - 最小/最大值:
score: 0和score: 100——模型傾向於避免極端值 - 長字串值: 有 200 個以上字元的欄位(模型有時會截斷)
- 特殊字元: 字串欄位中的引號、換行、unicode
- Null 處理: 如果你的 schema 允許可空欄位,包含帶 null 值的範例
第 5 步:格式化為訓練配對
將每個範例結構化為 system/user/assistant 對話:
{
"messages": [
{
"role": "system",
"content": "You are a report analysis system. Analyze the provided report and output a structured assessment."
},
{
"role": "user",
"content": "Report: Q3 revenue declined 12% due to supply chain disruptions. Two compliance violations were identified in the procurement process. The technical infrastructure handled the load increase from the holiday promotion without incidents."
},
{
"role": "assistant",
"content": "{\"id\": \"RPT-004521\", \"status\": \"review\", \"score\": 62, \"category\": \"operational\", \"findings\": [{\"description\": \"Revenue decline of 12% attributed to supply chain disruptions\", \"severity\": \"high\", \"recommendation\": \"Diversify supplier base and establish backup procurement channels\"}, {\"description\": \"Two compliance violations in procurement process\", \"severity\": \"medium\", \"recommendation\": \"Audit procurement workflows and retrain staff on compliance requirements\"}], \"metadata\": {\"analyst\": \"system\", \"date\": \"2026-03-15\", \"version\": \"1.0\"}}"
}
]
}助理的回應必須是原始 JSON——無 Markdown 格式,無解釋性文字,無程式碼塊。訓練模型只輸出 JSON,僅此而已。
測量 Schema 合規性
不要依賴「看起來對」的判斷。以程式方式測量合規性:
結構合規性
通過 JSON Schema 驗證器執行每個輸出。統計通過的百分比。這能捕獲缺少的欄位、錯誤的類型、無效的枚舉值和 schema 結構違規。
語義合規性
結構有效性並不意味著內容是正確的。模型可以輸出 {"status": "approved", "score": 0}——結構上有效,但語義上可疑(得 0 分卻被批准?)。建立語義檢查 :
- 枚舉值是否與其他欄位正確關聯?
- 數值是否在現實範圍內?
- 字串值是否包含相關內容(不是 lorem ipsum 或空字串)?
- 陣列長度是否與輸入相稱?
比較指標
微調後,與你的基線進行比較:
| 指標 | 提示 GPT-4o | 微調 8B | 微調 8B + Grammar |
|---|---|---|---|
| 有效 JSON | 99.5% | 99.8% | 100% |
| Schema 合規性 | 93-96% | 99.2-99.7% | 100% |
| 語義準確率 | 90-94% | 93-97% | 93-97% |
| 平均 tokens/回應 | 350 | 280 | 280 |
| 每 1K 呼叫成本 | $2.50-$8.00 | $0(本地) | $0(本地) |
微調模型更符合 schema,使用更少 tokens(它不會用不必要的冗長填充回應),且每次呼叫零成本。
結合微調與約束解碼
對於即使 99.5% 都不夠的應用——金融交易、醫療記錄、法律文件——將微調與約束解碼結合。
約束解碼(也稱為 grammar 引導生成)限制模型的輸出 token 只能是根據 grammar 規範產生有效輸出的那些。llama.cpp 透過 GBNF grammars 支援此功能。Outlines 和 guidance 為基於 Python 的推論提供類似功能。
你的 schema 的 GBNF grammar:
root ::= "{" ws "\"id\":" ws string "," ws "\"status\":" ws status "," ws "\"score\":" ws number "," ws "\"category\":" ws category "," ws "\"findings\":" ws findings "," ws "\"metadata\":" ws metadata ws "}"
status ::= "\"approved\"" | "\"denied\"" | "\"pending\"" | "\"review\""
category ::= "\"financial\"" | "\"operational\"" | "\"compliance\"" | "\"technical\""
...
使用微調模型 + grammar,你可以得到:
- 100% 結構合規性(grammar 防止無效結構)
- 99.5% 以上語義準確率(微調教導正確值)
- 零每 token 成本(本地推論)
- 快速推論(grammar 約束實際上通過減少 token 搜索空間稍微加速生成)
grammar 處理結構。微調處理內容。兩者結合產生你的解析器可以無條件信任的輸出。
效能影響
生成結構化輸出的微調模型通常比提示模型更快,原因有兩個:
-
更短的輸出: 提示模型通常在 JSON 周圍包含解釋性文字、Markdown 格式或元評論。微調模型只輸出原始 JSON。這將輸出 tokens 減少 20-40%。
-
更自信的生成: 當模型「了解」schema 時(來自微調),它以更高的信心生成每個 token。在採樣過程中較少回溯。可測量地降低首個 token 時間和更快的 token 生成速率。
客戶支援票證分類 schema 的基準測試(8 個欄位,2 個枚舉,1 個巢狀物件):
| 設置 | 平均輸出 Tokens | 平均延遲 | 結構有效性 |
|---|---|---|---|
| GPT-4o + 提示 | 420 tokens | 1.8s | 96.2% |
| GPT-4o + structured outputs API | 310 tokens | 1.4s | 99.8% |
| 微調 8B (Ollama) | 270 tokens | 0.4s | 99.5% |
| 微調 8B + grammar | 265 tokens | 0.35s | 100% |
微調的本地模型快 4 倍,且產生更短、更可靠的輸出。
Schema 微調中的常見錯誤
錯誤 1:訓練資料不一致
如果你的 80% 訓練範例使用 "date": "2026-03-15",而 20% 使用 "date": "March 15, 2026"