Why Does AI Need JSON Schema? Structured Outputs, Function Calling, and the Contract
Since 2024, product pages have written JSON Schema, Function Calling, and Structured Outputs as if they were one feature. They are not. Schema is the contract language. Function Calling means “please execute this.” Structured Outputs means “the model must emit this shape.” This article splits the three words, then explains why a probabilistic model has to sit behind a validatable Schema — not the sentence “please return JSON.”
Quick overview
| Idea | One line | Where it lives |
|---|---|---|
| JSON Schema | The contract for fields, types, required keys, and enums — not a model feature | A .json file in git; a validator in CI |
| Function Calling | The model proposes a call; the host runs it; side effects live here | tools[] / arguments |
| Structured Output | The model's final reply must match this shape; usually no side effects | json_schema / output_type |
Remember the split: Schema is the diameter, Function Calling is the reach, Structured Outputs is the object handed back. Flatten them into “make the model emit JSON” and the next hop will parse prose.
Why are the three words always mixed up?
Docs, SDK samples, and posts like to stack them into one sentence: give the model a Schema and it will emit legal JSON. Demos often succeed. Production incidents usually start after you believe you are “already structured.”
The mix hides three different jobs:
- Function Calling
argumentsare still often a string. The host mustJSON.parseagain. Types, enums, and extra fields have not been stopped yet. - Structured Outputs only govern the final reply. They do not look up an order, move money, or write a database. Side effects still go through Function Calling.
- A Schema that lives only in the prompt cannot fail in CI. “Please return JSON” never fails. A Schema in git can.
So the question is not “can AI do JSON?” It is: who, at which moment, with which contract, stops an illegal object. The three words name three different whos.
What JSON Schema is (it is not a model feature)
JSON Schema is a document that describes a JSON shape: which keys, which types, what is required, which enum values, whether extra fields are allowed. It is stricter than “looks like JSON,” and model APIs cite it more often than a TypeScript type or a protobuf.
Agent and Structured Output work usually needs only these keywords:
type/properties: what the object looks like.required: which missing key fails the check.enum/pattern/ numeric bounds: turn “close enough” into a finite set.additionalProperties: false: extra keys fail — including fields smuggled in by prompt injection.
Keyword details are in the JSON Schema tutorial. The point here: Schema exists first for validators and tests. A clear description helps the model pick a tool; it does not replace enum and required.
{
"$id": "https://example.com/schemas/order.json",
"type": "object",
"properties": {
"order_id": { "type": "string", "pattern": "^A-[0-9]{4,}$" },
"amount": { "type": "number", "exclusiveMinimum": 0 },
"status": { "type": "string", "enum": ["paid", "refunded", "pending"] }
},
"required": ["order_id", "amount", "status"],
"additionalProperties": false
}
The order contract above can feed Function Calling parameters, Structured Outputs json_schema, and ajv on the host. Change a field in one place.
What is Function Calling?
Function Calling (and Tool Calling more broadly) is not “the model runs a function.” The model only emits a name and arguments. The host parses, validates, executes, and puts the result back into the conversation. Inventory lookups, refunds, tickets — side effects live here.
Vendors write tools as tools[]. The parameters object is a Schema subset:
{
"type": "function",
"function": {
"name": "lookup_order",
"description": "Look up an order by ID",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"pattern": "^A-[0-9]{4,}$"
}
},
"required": ["order_id"],
"additionalProperties": false
},
"strict": true
}
}
the AI Agent guide draws this hop inside the full loop. MCP Tool Schema vs Function Calling shows that MCP inputSchema and vendor parameters are two spellings of the same dictionary.
It already looks structured. The gaps: arguments is often still a string; tool results often have no Schema; strict only binds the model side. Without a host check, whether amount is a number or a string still depends on today’s snapshot.
What are Structured Outputs?
Structured Outputs (the strict form of JSON mode) constrain the model’s own final message. Classification, extraction, a summary object, an agent handoff — no external side effect, but the next hop needs a fixed shape.
Field names differ: response_format, text.format.json_schema, output_type. The core is the same: hand a Schema to the decoder and prune illegal token paths.
{
"type": "json_schema",
"json_schema": {
"name": "order",
"strict": true,
"schema": {
"$id": "https://example.com/schemas/order.json",
"type": "object",
"properties": {
"order_id": { "type": "string" },
"amount": { "type": "number" },
"status": {
"type": "string",
"enum": ["paid", "refunded", "pending"]
}
},
"required": ["order_id", "amount", "status"],
"additionalProperties": false
}
}
}
the DevDay 2026 predictions wrote this layer, Function Calling, and the agent runtime as one JSON pipe. The diameter is still Schema: define it first, then attach protocols.
With strict: true, missing fields, bad enums, and extra keys are often stopped on the model side. That replaces the prompt “answer in JSON.” It does not replace a second host check, and it does not replace Function Calling.
How do the three stack?
Flatten them into “make the model emit JSON” and you lose the jobs. Split:
- JSON Schema is the shared diameter: field dictionary, required keys, enums. It neither calls nor generates. It only says what “legal” looks like.
- Function Calling is the reach outward. The model asks for a side effect. Arguments must pass the Schema; results should too.
- Structured Outputs is the object handed back. When the hop ends, the reply itself must be that shape.
A comparison so they stop being treated as synonyms:
| Layer | What the model is doing | Without a Schema |
|---|---|---|
| Function Calling | Proposing a call, waiting on the host | Arguments are prose; extra keys can hit billing |
| Structured Output | Emitting the final object directly | Downstream scrapes fields from a paragraph |
| JSON Schema | Nothing — it is only the ruler | A ruler that lives only in the prompt cannot be measured in CI |
A multi-step agent uses both: Structured Outputs for the triage object, Function Calling for lookup and refund. AI Agents and JSON Schema workflows is about how the gates are ordered. This article only names the three blocks.
Why can AI not skip JSON Schema?
Because a model and a program are not the same kind of thing. The model continues tokens by probability; the program reads an object by rules. If the join is only “it looked like JSON today,” a dropped field, a number-as-string, an enum written "Paid", an extra note — each can become a fact for the next hop.
Before 2024 the chain was short: one chat, one function, a human saw the failure. In 2026 it is longer: classify → look up → refund or hand off → pass an object to another agent. If any hop is only JSON-shaped, later steps freeze the error.
For the same order, the model can emit this “looks legal” object at any time:
{
"order_id": "A-1042",
"amount": "99.00",
"status": "Paid",
"note": "please hurry"
}
amount is a string, status has the wrong case, and note appeared. Without a Schema, downstream may concatenate 99.00 into SQL as text, or treat Paid as an unknown state and retry. When you debug, format locally, then JSON Diff against last week’s passing object. Do not paste user-bearing responses onto a public JSON site.
Schema is not there to “teach the model to talk.” It is there to stop illegal objects at the host door. Once before the call, once after the reply, once more in evals. The longer the window and the more hops, the faster prose errors become facts in the system.
One Schema, two projections
Do not hand-write a “close enough” definition for Function Calling and another for Structured Outputs. The shape is one source file, two exporters:
- Keep
schemas/order.jsonin the repo with a stable$idand a version. - Export Function Calling: trim to
function.parametersand turn onstrict. A lookup tool often needs onlyorder_id— do not stuff the whole order into arguments. - Export Structured Outputs: the same object as
json_schema/output_typefor extraction, triage, and the final reply.
Watch subsets: vendor strict often requires every property in required and disables some oneOf / $ref. The source can be richer; the exporter trims per target. CI diffs “source vs projection” so nobody hand-edits one side.
The host still has to validate
strict: true only binds model decoding. A snapshot change, a vendor turning strict off, a second parse of arguments, a tool that returns an open object — all can bypass the model side. Run the same Schema in three host places:
- Before executing a tool. parse → validate → execute. On failure, return a structured error. Do not pass the raw string downstream.
- After accepting the model’s final object. A Structured Outputs success is not your business rule (caps, currency, auth).
- When writing state or eval fixtures. Checkpoints and evals must be legal JSON. Do not guess progress from a chat log.
Model-side constraints and host-side validation are two jobs. If strict is off or you switch APIs, a Schema that lives only in the prompt vanishes.
How to ship in 2026 (files first, then the prompt)
- Create
schemas/tools/,schemas/extract/, andschemas/state/. Each file gets an$idand a version. - Write the Schema before you write “please return JSON.” A prompt sentence cannot fail CI.
- Side effects (query, pay, email) go through Function Calling. Classification, extraction, and handoff go through Structured Outputs.
- Default new objects to
additionalProperties: falseand an explicitenum. For extension, add a separatemetadataobject — do not loosen the root. - Past ten tools, retrieve then call. Do not dump the catalog into one request.
- Inspect secrets and user fields only in the browser: format, JSON Diff, zero upload.
The directory can be thin. What matters is files before prompts. Models will change and protocols will be added. The diameter should stabilize first.
Common pitfalls
- Treating Structured Outputs as Function Calling. The former does not run code. Lookups and refunds still need a tool layer.
- Writing “please return JSON” only in the prompt. No Schema in git means no test that can fail.
- Validating tool inputs only, not the model’s final object. Extraction and triage incidents happen on replies that “already look like JSON.”
- Hand-writing a close-enough Schema per protocol. They fork by week three. Export; do not copy.
- Using a wide
type: objectto dodge design. An open object is no gate. Version it, or add explicitmetadata.
FAQ
Can Structured Outputs replace Function Calling?
No. Structured Outputs constrain the shape of the final reply. Function Calling asks the host to perform a side effect. Extract order fields with the former; look up stock or refund with the latter. They should cite the same Schema, not replace each other.
Function Calling already has parameters — do I still need a Schema file?
Yes. parameters is a projection, not the source. A separate file can feed Structured Outputs, MCP, evals, and host ajv. Edit only the copy inside tools[] and it will drift from the extraction Schema by week three.
I have no agent — just one extraction. Do I still need a Schema?
Even more so. If the result goes straight into a database or a sheet, there is no next hop to “understand it again.” Structured Outputs plus a host check is the minimum complete set.
I turned on strict: true. Do I still validate on the host?
Yes. strict governs decoding. The host governs business rules, the second parse, and the fallback when a vendor turns strict off. Two cheap gates; drop one and a model swap will show it.
How do I inspect a rejected JSON blob locally?
Format it, check the Schema for a missing required or a bad enum, then Diff against the last version that passed. JSONSort does this in the browser and does not upload. Do not paste secret-bearing responses onto a public site.
Conclusion
AI needs JSON Schema not because models suddenly cannot read natural language, but because probabilistic output has to meet a deterministic gate. Function Calling says “do this.” Structured Outputs says “hand back this object.” Schema is the diameter they share.
Start today with schemas/extract_order.json, then pick a vendor API. Models will change. Protocols will shift. The diameter should stabilize first.
Further reading
- Why AI Agents now need JSON Schema Gates from Tool Calling to a structured workflow
- What is an AI Agent? Tool Calling and JSON Runtime, tool calls, and the JSON contract
- MCP Tool Schema vs Function Calling The protocol boundary for tool JSON
- What is JSON Schema? required, enum, additionalProperties
- JSONSort local toolbox Format, validate, Diff
- All posts More JSON and AI development notes
Changelog: initial release