Skip to main content

Structured Output

Force models to return structured data that strictly conforms to a JSON Schema, eliminating the need to parse strings or use regex extraction. Ideal for data extraction, form filling, and workflow intermediate artifacts.

When to Use

  • You need directly deserializable JSON (no tolerance for json.loads failures)
  • Downstream systems have a strict schema (database fields, API payloads)
  • Data extraction / entity recognition / classification
  • Decision output in an Agent (model must output a specific action object)

Difference from Function Calling:

Structured OutputFunction Calling
PurposeMake the final response a JSON objectHave the model call your functions
RoundsSingle requestAt least two turns (call → return result)
Typical useData extraction, field fillingAgents, external system integration

Three Implementation Approaches

Different providers take different implementation paths. Turing transparently proxies all of them via the Chat Completions / Messages protocols.

Approach A: response_format JSON Mode (earliest OpenAI version)

Guarantees the output is valid JSON, but does not enforce a specific schema. The model may return a structure that does not match expectations.

from openai import OpenAI

client = OpenAI(
api_key="your-api-key",
base_url="https://live-turing.cn.llm.tcljd.com/api/v1",
)

response = client.chat.completions.create(
model="turing/gpt-5.4-mini",
messages=[
{"role": "system", "content": "You are a JSON output assistant. Return strict JSON."},
{"role": "user", "content": "Extract: John Smith, 28 years old, engineer"},
],
response_format={"type": "json_object"},
)

data = json.loads(response.choices[0].message.content)
warning

When using json_object, you must mention "JSON" in the prompt; otherwise OpenAI will reject the request.

Forces output to strictly conform to the schema. Supported by OpenAI gpt-4o / gpt-5.x.

response = client.chat.completions.create(
model="turing/gpt-5.4-mini",
messages=[
{"role": "user", "content": "Extract: John Smith, 28 years old, engineer"},
],
response_format={
"type": "json_schema",
"json_schema": {
"name": "person_info",
"strict": True,
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"occupation": {"type": "string"}
},
"required": ["name", "age", "occupation"],
"additionalProperties": False
}
}
},
)

# Parsing is guaranteed to succeed with all fields present
data = json.loads(response.choices[0].message.content)
curl $TURING_BASE_URL/chat/completions \
-H "Authorization: Bearer $TURING_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "turing/gpt-5.4-mini",
"messages": [{"role": "user", "content": "Extract: John Smith, 28 years old, engineer"}],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "person_info",
"strict": true,
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"occupation": {"type": "string"}
},
"required": ["name", "age", "occupation"],
"additionalProperties": false
}
}
}
}'

Approach C: Gemini Native responseSchema

The Gemini family uses two fields: responseMimeType and responseSchema.

response = client.chat.completions.create(
model="turing/gemini-3.1-pro-latest",
messages=[{"role": "user", "content": "Extract: John Smith, 28 years old, engineer"}],
extra_body={
"responseMimeType": "application/json",
"responseSchema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"occupation": {"type": "string"}
},
"required": ["name", "age", "occupation"]
}
}
)

Claude: Simulated via Function Calling

Claude does not natively support response_format. The most reliable approach is to use function calling as a single-turn structured output: define a tool and force the model to call it.

from anthropic import Anthropic

client = Anthropic(
base_url="https://live-turing.cn.llm.tcljd.com/api/v1",
auth_token="your-api-key",
)

response = client.messages.create(
model="turing/claude-sonnet-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Extract: John Smith, 28 years old, engineer"}],
tools=[{
"name": "emit_person_info",
"description": "Return the extracted person information",
"input_schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"occupation": {"type": "string"}
},
"required": ["name", "age", "occupation"]
}
}],
tool_choice={"type": "tool", "name": "emit_person_info"},
)

# Retrieve structured data from the tool_use block
data = next(b.input for b in response.content if b.type == "tool_use")

Provider Comparison

ProviderJSON ModeJSON SchemaNative FieldsModel Selection
OpenAIresponse_format: {"type":"json_object"}response_format: {"type":"json_schema","json_schema":{…,"strict":true}}See Model List → OpenAI; prefer models that support JSON Schema / strict
Anthropic❌ Simulate via function calling❌ Use function calling + tool_choice: {"type":"tool",…}See Model List → Claude; prefer larger models with stable function calling
GeminiresponseMimeType: "application/json"responseSchema + responseMimeTyperesponseMimeType / responseSchemaSee Model List → Gemini; prefer models that support structured output
Qwenresponse_format: {"type":"json_object"}⚠ Model-dependentSee Model List → Alibaba; prefer primary models with better JSON stability
DeepSeek⚠ Model-dependentSee Model List → DeepSeek

Parameter Reference

Full schema documentation:


Billing Impact

  • The model must generate the entire JSON structure, so completion_tokens includes all field names and punctuation
  • The schema definition (parameters / json_schema) itself also consumes input tokens
  • OpenAI strict: true incurs a small amount of extra latency on the first call to compile the schema (subsequently cached on the model server)

FAQ

  • strict: true reports unsupported schema → OpenAI strict mode requires additionalProperties: false and does not support oneOf/anyOf at the root level; refer to the constraints listed in the OpenAI Structured Outputs Docs
  • Gemini returns JSON but field order is inconsistent → This is expected; JSON objects are inherently unordered. Access fields by key.
  • Claude calls the tool but parameter fields are empty → Add more required fields and richer description entries to input_schema
  • Smaller models produce invalid JSON → Upgrade to a larger model, or combine json_object with secondary regex validation

See also