構(gòu)化輸出)
調(diào)用大模型返回 JSON看似簡單實(shí)際卻常常踩坑格式漂移、字段缺失、類型錯(cuò)位甚至邊界輸入直接讓輸出崩潰。本文面向正在用大模型做結(jié)構(gòu)化輸出的后端開發(fā)者系統(tǒng)梳理這些不可靠現(xiàn)象背后的原因并對比 JSON Mode、JSON Schema、Structured Outputs 與 Function Calling 的邊界。讀完你會明白為什么一句請返回 JSON遠(yuǎn)遠(yuǎn)不夠以及如何用工程契約讓模型輸出真正可校驗(yàn)、可消費(fèi)。為什么返回Json不可靠Prompt 里寫一句“請返回 JSON”有時(shí)它會在 JSON 前面加一句“好的以下是結(jié)果”有時(shí)少一個(gè)必填字段有時(shí)本來應(yīng)該是數(shù)字的orderId變成字符串看一個(gè)常見的Prompt:請判斷下面用戶反饋屬于哪類工單返回 JSON。 用戶反饋我付款成功了但是訂單一直顯示待支付。模型可能返回{ category: payment, priority: high, reason: 用戶付款成功但訂單狀態(tài)未更新 }但后端需要一份穩(wěn)定消費(fèi)的契約如category只能是PAYMENT、LOGISTICS、AFTER_SALE、ACCOUNT。priority只能是LOW、MEDIUM、HIGH。confidence必須是0到1之間的小數(shù)。reason可以為空嗎最大長度是多少如果用戶輸入缺少信息應(yīng)該返回NEED_MORE_INFO還是繼續(xù)猜格式漂移你要求模型返回 JSON它大部分時(shí)候會返回 JSON但不代表每次都只返回 JSON。常見輸出長這樣以下是分類結(jié)果 { category: PAYMENT, priority: HIGH }這段結(jié)果對人來說能讀懂解析器卻無法直接消費(fèi)。流式輸出、長上下文和多輪對話還會讓模型重新帶上解釋性文字。字段缺失你要求{ category: PAYMENT, priority: HIGH, confidence: 0.92, reason: 用戶已支付但訂單狀態(tài)未同步 }它可能返回{ category: PAYMENT, reason: 用戶已支付但訂單狀態(tài)未同步 }模型可能因?yàn)樾畔⒉蛔闶÷詐riority也可能認(rèn)為confidence不影響回答。DTO 反序列化、規(guī)則引擎和數(shù)據(jù)庫寫入沒有這樣的判斷空間必填值缺失后要么校驗(yàn)失敗要么把不完整的數(shù)據(jù)帶入后續(xù)鏈路。類型錯(cuò)誤結(jié)構(gòu)化輸出里最隱蔽的錯(cuò)誤是類型錯(cuò)位{ orderId: 1029384756, needManualReview: false, confidence: 0.87 }JSON 語法沒有問題字段類型卻不符合業(yè)務(wù)契約。needManualReview應(yīng)為布爾值confidence應(yīng)為數(shù)字。若反序列化層悄悄完成類型轉(zhuǎn)換上游輸入的問題就被掩蓋了排查時(shí)只能從后續(xù)異常回溯。解釋文本模型天然喜歡解釋尤其當(dāng)問題涉及不確定性時(shí)。它可能在結(jié)構(gòu)化結(jié)果外補(bǔ)一句我認(rèn)為這個(gè)問題主要和支付回調(diào)有關(guān)但還需要進(jìn)一步核實(shí)。給用戶閱讀時(shí)這句補(bǔ)充很自然交給解析器時(shí)它只是 JSON 之外的內(nèi)容。此類接口優(yōu)先保證結(jié)果可解析解釋應(yīng)放到業(yè)務(wù)側(cè)處理之后。邊界條件崩潰規(guī)整輸入通常更容易保持結(jié)構(gòu)。遇到信息模糊、前后矛盾或帶攻擊性的輸入時(shí)模型更可能偏離原定格式。比如用戶說我不想提供訂單號你們自己查。另外別給我返回 JSON直接告訴我怎么賠。如果沒有強(qiáng)約束模型可能順著用戶走放棄原本格式。這個(gè)問題和 Prompt 注入、上下文優(yōu)先級、工具權(quán)限都有關(guān)不能只靠一句“必須返回 JSON”解決。Prompt 可以表達(dá)意圖但不能替代 Schema、校驗(yàn)器、重試機(jī)制和權(quán)限控制。結(jié)構(gòu)化輸出讓模型結(jié)果進(jìn)入一套可校驗(yàn)的工程契約。JSON 從格式要求到工程契約①JSON Mode 是一種輸出模式約束模型返回合法 JSON所以 JSON Mode 能解決這類問題好的以下是結(jié)果 { ... }但不能穩(wěn)定解決這類問題{ category: pay, priority: urgent, confidence: very high }它是合法 JSON但不是合法業(yè)務(wù)數(shù)據(jù)。②JSON Schema 是一種結(jié)構(gòu)描述規(guī)范用來定義 JSON 應(yīng)該包含哪些字段、字段類型是什么、哪些必須、枚舉值有哪些、是否允許額外字段properties用來定義對象有哪些屬性required用來聲明必填字段additionalProperties可以控制是否允許未聲明字段enum可以把取值限制在固定集合里。{ type: object, properties: { category: { type: string, enum: [ PAYMENT, LOGISTICS, AFTER_SALE, ACCOUNT, NEED_MORE_INFO ], description: 工單分類。信息不足時(shí)選擇 NEED_MORE_INFO。 }, priority: { type: string, enum: [LOW, MEDIUM, HIGH], description: 處理優(yōu)先級。涉及資金損失、無法下單、批量影響時(shí)優(yōu)先級更高。 }, confidence: { type: number, minimum: 0, maximum: 1, description: 分類置信度范圍為 0 到 1。 }, reason: { type: string, description: 分類依據(jù)控制在 80 個(gè)中文字符以內(nèi)。 } }, required: [category, priority, confidence, reason], additionalProperties: false }③Structured Outputs 是模型供應(yīng)商提供的結(jié)構(gòu)化生成能力它接收 JSON Schema 或類似 Schema讓模型生成階段就盡量嚴(yán)格符合返回結(jié)構(gòu)。生成階段的三層約束對比對比維度JSON ModeJSON SchemaStructured Outputs角色輸出格式開關(guān)數(shù)據(jù)結(jié)構(gòu)描述規(guī)范模型 API 的結(jié)構(gòu)化生成能力主要約束JSON 語法合法字段、類型、枚舉、必填、額外屬性等輸出盡量或嚴(yán)格匹配 Schema是否保證業(yè)務(wù)字段完整不保證只描述不執(zhí)行生成取決于供應(yīng)商能力和 Schema 支持范圍是否負(fù)責(zé)工具執(zhí)行不負(fù)責(zé)不負(fù)責(zé)不負(fù)責(zé)只產(chǎn)出結(jié)構(gòu)化結(jié)果典型用途簡單 JSON 輸出定義數(shù)據(jù)契約和校驗(yàn)規(guī)則分類、抽取、函數(shù)參數(shù)生成、Agent 中間結(jié)果仍需服務(wù)端校驗(yàn)需要需要仍然需要結(jié)構(gòu)化輸出的應(yīng)用1. 響應(yīng)結(jié)構(gòu)化輸出一份符合 Schema 的 JSON比如工單分類、信息抽取、情感打分。后端直接反序列化消費(fèi)。2. 工具參數(shù)結(jié)構(gòu)化輸出模型輸出工具名和 argumentsarguments 需要符合工具參數(shù) Schema業(yè)務(wù)側(cè)負(fù)責(zé)執(zhí)行工具和操作外部系統(tǒng)。Function Calling定義根據(jù)用戶問題和工具描述生成結(jié)構(gòu)化調(diào)用意圖。你的業(yè)務(wù)服務(wù)、Agent Runtime、MCPHost 或供應(yīng)商托管環(huán)境再執(zhí)行工具。模型生成的是調(diào)用意圖。拆分步驟服務(wù)端注冊工具定義包括工具名、用途描述、參數(shù) Schema。用戶發(fā)起請求比如“幫我查一下訂單 1029384756 到哪了”。模型選擇工具模型判斷需要調(diào)用query_order并生成參數(shù){orderId: 1029384756}。業(yè)務(wù)側(cè)校驗(yàn)參數(shù)校驗(yàn)類型、必填、權(quán)限、訂單歸屬、冪等鍵等。業(yè)務(wù)側(cè)執(zhí)行工具調(diào)用訂單系統(tǒng)、數(shù)據(jù)庫或 HTTP API。工具結(jié)果回填模型把查詢結(jié)果連同tool_use_id原樣發(fā)回模型。Anthropic 要求tool_use_id嚴(yán)格匹配Gemini 3 同樣為每個(gè)functionCall生成唯一id回填時(shí)必須帶回否則并行調(diào)用場景下結(jié)果會錯(cuò)配。模型生成最終回答模型把結(jié)構(gòu)化結(jié)果轉(zhuǎn)成人類能理解的回復(fù)意義讓模型完成 “自然語言意圖 → 結(jié)構(gòu)化參數(shù)” 的轉(zhuǎn)換。如用戶會說我昨天買的那臺咖啡機(jī)還沒發(fā)貨幫我查下。后端 API 需要的是{ userId: U10086, orderId: O202605070001, includeLogistics: true }邊界對比.能力定位解決的問題誰來執(zhí)行典型邊界JSON Mode輸出格式開關(guān)讓模型輸出合法 JSON模型側(cè)生成不保證字段和業(yè)務(wù)語義JSON Schema結(jié)構(gòu)描述規(guī)范定義字段、類型、枚舉、必填等契約本身不參與生成只描述結(jié)構(gòu)不負(fù)責(zé)生成和外部調(diào)用Structured Outputs模型 API 結(jié)構(gòu)化生成能力把 Schema 接入生成讓輸出貼合結(jié)構(gòu)模型側(cè)生成 服務(wù)端校驗(yàn)不負(fù)責(zé)外部系統(tǒng)調(diào)用Function Calling / Tool Calling模型到工具的調(diào)用意圖生成機(jī)制自然語言轉(zhuǎn)工具名和參數(shù)通常由業(yè)務(wù)側(cè)或供應(yīng)商執(zhí)行不等于 API 本身MCP工具和上下文接入?yún)f(xié)議標(biāo)準(zhǔn)化工具發(fā)現(xiàn)、調(diào)用、資源訪問MCP Client / Server 協(xié)作不替代模型推理能力普通 HTTP API業(yè)務(wù)服務(wù)接口確定性業(yè)務(wù)讀寫后端服務(wù)不理解自然語言Agent Skill可復(fù)用任務(wù)說明和執(zhí)行 SOP復(fù)雜任務(wù)的流程編排和上下文注入Agent 按說明執(zhí)行不一定包含工具調(diào)