Sometimes you don’t actually want to call a tool. You just want the model to output structured JSON with specific fields and types. The most reliable way to get that? Define a “virtual tool” and force the model to call it.
How it works
- Define a tool whose
input_schemadescribes the exact JSON structure you want. - Set
tool_choiceto force that specific tool:{"type": "tool", "name": "extract_data"}. - The model “calls” the tool, generating parameters that conform to your schema.
- You ignore the tool call mechanism and just use the
inputfield as your structured output.
The tool never gets executed. There’s no real function behind it. You’re using the tool_use machinery purely as a structured output mechanism.
Why this beats asking for JSON in the prompt
Asking the model to “respond in JSON format” in the prompt is unreliable. The model might wrap it in markdown code blocks, add explanatory text before the JSON, use slightly different field names, or occasionally produce invalid JSON.
Forced tool_use eliminates all of this. The input field is guaranteed to be valid JSON conforming to the schema you defined — correct types, required fields present, proper structure. The schema acts as a contract.
Structure is guaranteed. Values are not.
This is the critical distinction. tool_use with a schema guarantees structural correctness: valid JSON, correct types, required fields present. It does not guarantee semantic correctness: the values might be wrong, sums might not add up, dates might be out of sequence.
If your schema says amount is a number, you’ll always get a number. But it might be the wrong number. Schema enforcement and semantic validation are separate concerns. You need both for production systems.
From the API’s perspective, it’s all the same
There is no technical difference between a “real” tool call and a “virtual” tool used for structured output. Same definition format, same tool_use response, same everything. The distinction is purely in your application: do you execute something with the parameters, or just consume them as data?
One-liner: Force a virtual tool call to get guaranteed schema-compliant JSON — but remember, structural correctness doesn’t mean the values are right.