對應原題(Common ‧ Agents and Tool Use)

  • What is the difference between structured output and function calling? — Asked at: Mistral AI, Apple
  • What is MCP (Model Context Protocol) and how does it differ from traditional function calling? — Asked at: Microsoft
  • How does an agent decide when to call a tool versus answer from its own knowledge?

🎯 什麼情境該想到我

當你「要讓模型輸出機器能解析的東西、或要接工具」的時候。這三個名詞常被混用,能把它們分清楚就是這題的全部分數。

⚙️ 怎麼用

★ 一、三個概念在不同的層

結構化輸出Function CallingMCP
是什麼輸出格式的約束模型決定「呼叫誰、帶什麼參數」工具怎麼被提供的協定
所在層解碼層模型 API 層整合層
終點資料執行工具的來源
問「什麼」「請用這個 schema 回答我」「你需要做什麼?」「你有哪些工具可用?」

關係:

結構化輸出  ⊂  Function Calling 的實作手段
             (函式參數本身就必須符合 schema)

MCP  ──提供──▶  工具定義  ──餵給──▶  Function Calling

★ 一句話區分:
結構化輸出的終點是「一份資料」,function calling 的終點是「一個動作」。
前者你拿到 JSON 就結束了;後者你拿到 JSON 之後還要去執行它,並把結果餵回模型(見 ReAct 與 Agent Loop)。

技術上兩者高度重疊——OpenAI 早期的 JSON mode 就是用 function calling 的機制實作的。
但設計意圖不同,用錯會讓架構變怪:用 function calling 做純資料抽取,會多出不必要的迴圈;用結構化輸出做動作分派,會缺少執行與回饋。

二、怎麼保證輸出合法:約束解碼

做法保證
Prompt 要求(「請輸出 JSON」)❌ 沒有保證。會加開場白、會少括號、長輸出時尤其容易壞
重試 + 驗證⚠️ 可用但浪費,且長尾永遠存在
★ 約束解碼(grammar-constrained decoding)✅ 語法層保證

約束解碼的機制(值得會講):

每一步生成前:
  1. 依 JSON Schema 編譯出的狀態機,算出「此刻哪些 token 是合法的」
  2. 把所有不合法 token 的 logit 設成 -inf
  3. 在剩下的合法 token 上取樣

例如已經輸出 {"age": 時,狀態機只允許數字與空白——模型在物理上無法輸出 "twenty"。

★ 它保證的是「語法合法」,不是「語意正確」。
{"age": 999, "name": "asdf"} 完全符合 schema,但完全是胡說。
schema 驗證通過 ≠ 內容正確——這是 structured output 最大的誤解,也是必考的追問。

副作用:

  • 約束會改變機率分布,可能把模型推離它原本想說的內容 → 有時反而降低內容品質
  • 它蓋過取樣參數的一部分效果(見 取樣與解碼策略)
  • 複雜 schema 的編譯與逐步查表有延遲成本

實務建議:schema 要盡量簡單扁平。深層巢狀 + 大量 enum + 遞迴定義會讓模型(和解碼器)都變差。

二點五、三家 API 怎麼開,以及失敗路徑

(補充自 https://masonailab.com/tech/structured-output/;參數與限制逐條對過三家官方文件,查證 2026-10-11。參數名會改版,實作前再看一次文件)

怎麼開拒答輸出被截斷Schema 限制(重點)
OpenAI(Responses API)text.format: {type: "json_schema", name, strict: true, schema}message content 裡出現 type: "refusal"status === "incomplete" 且 incomplete_details.reason === "max_output_tokens"只支援 JSON Schema 子集
Claudeoutput_config.format: {type: "json_schema", schema};工具參數用 strict: truestop_reason: "refusal"(仍回 200、照樣計費),輸出可能不符 schemastop_reason: "max_tokens",輸出可能不完整additionalProperties 必須是 false;不支援遞迴、minimum/maximum、minLength/maxLength;違反直接回 400
Gemini(Interactions API)response_format: {mime_type: "application/json", schema}——支援 minimum/maximum、minItems/maxItems;太大或太深的 schema 可能被拒
  • Claude 舊的 output_format 參數已移到 output_config.format,舊參數 deprecated,要用得加 beta header,否則回 400。
  • JSON Schema 的 additionalProperties 預設是允許——不明寫 false,模型(或 validator)可以合法地多吐欄位。正式資料契約要明確關掉。
  • Claude 不支援數值範圍約束,所以「金額 ≥ 0」只能放到業務驗證層,這正好說明下面那條:schema 只管形狀。

★ 成功條件不是「JSON.parse 沒丟例外」。拒答與截斷都會讓你拿到一段「不符 schema 的東西」,直接 parse 要嘛炸掉、要嘛拿到半截資料。處理順序固定是:

1. 先看結束原因(OpenAI: status / incomplete_details;Claude: stop_reason)
   ├─ 截斷 → 不 parse;加大輸出上限重試,或拆小 schema
   └─ 拒答 → 不 parse;顯示拒答或轉人工,不要硬塞進原 schema
2. parse(try/catch)
3. schema 驗證(Zod / Pydantic,型別與 schema 同一份定義產生)
4. 業務驗證(ID 存在嗎?金額合理嗎?信心不足就轉人工)

監控時把這三件事分開記:schema 驗證通過率、業務驗證通過率、任務實際正確率。只看第一個會以為一切正常。

★ 三、MCP vs Function Calling(Microsoft 那題)

最常見的錯誤答案是「MCP 是更新版的 function calling」。不是。它們在不同層,不互相取代。

        ┌──────────────────────────────────────┐
模型層  │ Function Calling                      │
        │ 模型決定:呼叫 search_docs(q="RAG")   │  ← 模型的能力
        └──────────────────────────────────────┘
                        ▲
                   工具定義從哪來?
                        │
        ┌──────────────────────────────────────┐
整合層  │ MCP                                   │
        │ 標準化「工具如何被發現、描述、呼叫」   │  ← 生態的協定
        │ 任何 MCP client ←→ 任何 MCP server     │
        └──────────────────────────────────────┘
傳統 function callingMCP
工具定義寫死在你的應用程式裡由 MCP server 動態提供
新增工具改程式碼、重新部署接一個 server 就好
複用每個應用各寫一份一個 server 所有 client 通用
傳輸無(同一個 process)stdio / HTTP,有明確的協定
能提供什麼只有函式Tools + Resources + Prompts 三種原語

面試答法:「MCP 解的是 M×N 問題——M 個 AI 應用要接 N 個資料源,原本要寫 M×N 個整合,MCP 讓它變成 M+N。
模型端仍然是用 function calling 的方式決定要呼叫什麼,MCP 只是換了工具的來源與描述方式。」

→ 深入:MCP、工具-MCP架構與資料流、工具-MCP三大伺服器原語、工具-MCP安全防護要點

★ 四、何時該呼叫工具、何時自己回答

兩種對稱的失效:

失效症狀代價
呼叫不足該查不查,直接用記憶回答幻覺。「你們的退款政策是 14 天」——它在編
過度呼叫什麼都查一遍慢、貴。「1+1 等於多少」也去搜尋

模型的判斷依據(都不太可靠):工具描述、系統提示、以及它對自己知識的信心——而 LLM 的信心校準本來就很差。所以不要只靠模型判斷。

五個控制手段(由弱到強):

#手段說明
1工具描述寫清楚「何時用 / 何時不要用」最便宜。描述是 prompt 的一部分,不是文件
2系統提示定規則★ 「任何涉及日期、價格、庫存、帳號狀態的問題,一律查詢,不得依賴記憶」
3tool_choice 參數auto / required(強制至少呼叫一個)/ none / 指定某個工具
4前置路由分類器用小模型或規則先分類,再決定給不給工具。便宜、可控、可測
5直接不給某些對話階段就不提供該工具

★ 一條好用的原則:

會變的東西永遠查;不會變的東西永遠不查。

  • 永遠查:時間、價格、庫存、使用者資料、系統狀態、公司政策、近期事件
  • 永遠不查:算術、格式轉換、文法、翻譯、一般常識推理

這條規則可以直接寫進系統提示,而且可以被評估——建一組「應該查/不該查」的測試集,量它的決策正確率。

⚠️ 注意 / 什麼時候不適用

  • 工具描述就是 prompt。寫得爛,模型就選錯工具。這是最高投報率的一行字(見 工具集設計)。

  • 約束解碼不是萬靈丹。schema 太複雜時,模型會被逼著填出語法合法但語意荒謬的值。寧可簡單 schema + 事後驗證。

  • schema 驗證只是第一關。通過之後還要驗業務規則(日期合理嗎?ID 存在嗎?金額在範圍內嗎?)。

  • 各家 API 的行為不一致:平行工具呼叫、strict mode、tool_choice 的語義、拒絕時的回應格式都不同。換供應商要重測。

  • 「模型只提出請求、執行都在你這邊」不一定成立,要看工具在哪裡跑。Anthropic 把工具分成兩種:

    • client tools:你自訂的工具,加上 bash、text_editor 這類 Anthropic 定義好 schema 的工具。模型回 tool_use,你的程式執行,再用 tool_result 回傳。
    • server tools:web_search、web_fetch、code_execution、MCP connector 等。在 Anthropic 的基礎設施上執行,回應裡是 server_tool_use 加上結果,你不用處理、也攔不到執行過程。

    OpenAI 同樣有內建工具(web search、file search 等)。所以做權限盤點時,要把「供應商代為執行的工具」另列一欄:它們能讀到什麼、會把資料送去哪裡,都不經過你的 dispatch 白名單。OpenAI 官方另外明寫:模型產出的參數不一定是合法 JSON,也可能編造 schema 沒定義的參數,執行前一律要驗證。(Anthropic Tool use overview、OpenAI Function calling guide,查證 2026-09-27;補充自 https://signals.tw/articles/what-is-tool-calling/)

  • 模型會編造工具名與參數。dispatch 端必須白名單驗證,不能相信模型給的名字(見 ReAct 與 Agent Loop)。

  • MCP server 是新的信任邊界。第三方 server 能看到你餵進去的內容,工具描述本身也可能是注入載體(見 工具-MCP安全防護要點)。

  • 結構化輸出會降低 CoT 空間。強制 JSON 會壓縮模型的思考餘地——需要推理時,先讓它自由思考,再用第二次呼叫格式化。

🧪 我實際套用的紀錄

  • 2026-09-20:從面試題庫展開。待辦:把「會變的永遠查」寫進系統提示,並建一組決策正確率測試集。

🔗 相關