3. Generate Structured JSON
Structured JSON turns model output into data that application code can parse, validate, and route. This section compares strict schema-constrained output with lighter JSON mode. Use Structured Outputs when your application needs a contract; use JSON mode when valid JSON is enough but the exact schema can remain flexible.
3.1 Define the Structured Output Schema
Define the support-ticket schema used by the next live request. The schema lists the exact fields the application expects, including category, priority, sentiment, summary, required actions, and escalation status. Inspect the request shape to see how text.format.type="json_schema", strict=true, and the JSON Schema are attached to a normal Responses request.
from __future__ import annotations
support_triage_schema = {
"type": "object",
"properties": {
"ticket_id": {"type": "string"},
"category": {"type": "string", "enum": ["delivery_delay", "return_exchange", "damaged_item", "billing", "account"]},
"priority": {"type": "string", "enum": ["low", "medium", "high", "urgent"]},
"customer_sentiment": {"type": "string"},
"summary": {"type": "string"},
"required_actions": {"type": "array", "items": {"type": "string"}, "minItems": 2},
"escalation_needed": {"type": "boolean"},
},
"required": ["ticket_id", "category", "priority", "customer_sentiment", "summary", "required_actions", "escalation_needed"],
"additionalProperties": False,
}
structured_payload = {
"model": MODEL_ID,
"input": (
"Support ticket TICKET-7429: Maya Chen says ORDER-8831 is a replacement for a damaged standing desk. "
"The replacement is two days late, the carrier scan has not moved, and she needs the desk before Monday. "
"She asks for a supervisor callback and refund options. Triage this ticket for the next support agent."
),
"text": {"format": {"type": "json_schema", "name": "support_ticket_triage", "strict": True, "schema": support_triage_schema}},
"max_output_tokens": 1024,
"store": False,
}
print_request_shape(structured_payload)
print_key_takeaway('The schema is part of the request and defines the fields the next cell validates.')| field | value |
|---|---|
| model | openai.gpt-5.4 |
| max_output_tokens | 1024 |
| store | False |
| text format | json_schema: support_ticket_triage strict=True required=7 fields |
| input | Support ticket TICKET-7429: Maya Chen says ORDER-8831 is a replacement for a damaged standing desk. The replacement is two days late, the carrier scan has not moved, and she needs the desk before Monday. She asks for a supervisor callback and refund options. T... |
3.2 Validate Schema-Constrained Output
Call the model with the schema from the previous cell, parse the returned text as JSON, and validate important fields in Python. The API request asks for schema adherence, while application-side validation still checks that the returned object is suitable for downstream routing. Inspect the parsed object and the response summary.
from __future__ import annotations
def validate_support_triage(payload: dict[str, Any]) -> dict[str, Any]:
require("ticket_id" in payload, "Missing key: ticket_id")
require(payload.get("ticket_id") == "TICKET-7429", "Ticket ID did not match expected value.")
require("required_actions" in payload, "Missing key: required_actions")
require(isinstance(payload.get("required_actions"), builtins.list), "required_actions must be a list.")
require(len(payload["required_actions"]) >= 2, "required_actions should contain at least two actions.")
return payload
print_request_shape(structured_payload)
try:
structured_response = create_response(**structured_payload)
raw_structured_text = output_text(structured_response).strip()
try:
structured_payload_result = validate_support_triage(json.loads(raw_structured_text))
record_check("Structured Outputs", "pass", structured_payload_result)
record_response("Structured ticket triage", "json", structured_payload_result)
print_labeled_json("Result", structured_payload_result)
except json.JSONDecodeError as e:
raise ValueError(f"Invalid JSON: {e}")
except Exception as parse_exc:
record_check("Structured Outputs", "warn", {"message": "Response did not match the expected schema shape.", "text_sample": raw_structured_text[:600], "error": builtins.str(parse_exc)})
print_labeled_text("Result", "The request completed, but the returned text did not match the expected schema shape.")
print_wrapped(raw_structured_text[:1200])
print_response_summary(structured_response)
print_key_takeaway('Schema-constrained output gives application code a predictable JSON object to parse and validate.')
except Exception as exc:
handle_example_error("Structured Outputs", exc)| field | value |
|---|---|
| model | openai.gpt-5.4 |
| max_output_tokens | 1024 |
| store | False |
| text format | json_schema: support_ticket_triage strict=True required=7 fields |
| input | Support ticket TICKET-7429: Maya Chen says ORDER-8831 is a replacement for a damaged standing desk. The replacement is two days late, the carrier scan has not moved, and she needs the desk before Monday. She asks for a supervisor callback and refund options. T... |
| field | value |
|---|---|
| id | resp_rk7y6aobdqx2m2fjpnllihuoyku2n55gj5o2h5jxl7lru77mwwiq |
| model | openai.gpt-5.4 |
| status | completed |
| output_item_types | ['message'] |
| input_tokens | 328 |
| cached_input_tokens | 0 |
| output_tokens | 200 |
| total_tokens | 528 |
| reasoning_output_tokens | 0 |
| service_tier | default |
3.3 Use JSON Mode
JSON mode asks the model to return a valid JSON object without enforcing a strict schema. This is useful for lightweight handoffs where you still want parsable output but can tolerate a looser contract. This cell requests a support handoff object, parses it, and checks for the expected keys.
from __future__ import annotations
json_mode_payload = {
"model": MODEL_ID,
"input": (
"Return JSON for a support chat handoff with keys customer_name, order_id, issue_summary, next_step, "
"and metrics_to_watch. Context: Maya Chen asks about delayed replacement order ORDER-8831; the carrier scan is stale. "
"metrics_to_watch should be an array."
),
"text": {"format": {"type": "json_object"}},
"max_output_tokens": 1024,
"store": False,
}
print_request_shape(json_mode_payload)
try:
json_mode_response = create_response(**json_mode_payload)
payload = json.loads(output_text(json_mode_response).strip())
require({"customer_name", "order_id", "issue_summary", "next_step", "metrics_to_watch"}.issubset(payload), "JSON mode response missed required keys.")
record_check("JSON mode", "pass", payload)
record_response("JSON support handoff", "json", payload)
print_labeled_json("Result", payload)
print_response_summary(json_mode_response)
print_key_takeaway('JSON mode is useful when valid JSON is enough and a strict schema is not required.')
except Exception as exc:
handle_example_error("JSON mode", exc)| field | value |
|---|---|
| model | openai.gpt-5.4 |
| max_output_tokens | 1024 |
| store | False |
| text format | json_object |
| input | Return JSON for a support chat handoff with keys customer_name, order_id, issue_summary, next_step, and metrics_to_watch. Context: Maya Chen asks about delayed replacement order ORDER-8831; the carrier scan is stale. metrics_to_watch should be an array. |
| field | value |
|---|---|
| id | resp_2wloqig6bh6sz2ysyiozx7oon7hogdzavqnv4pn7wcmnfkxnmlra |
| model | openai.gpt-5.4 |
| status | completed |
| output_item_types | ['message'] |
| input_tokens | 212 |
| cached_input_tokens | 0 |
| output_tokens | 122 |
| total_tokens | 334 |
| reasoning_output_tokens | 0 |
| service_tier | default |
3.4 Control Verbosity from Reasoning Effort
Verbosity controls help tune the shape of generated prose, while reasoning effort controls how much reasoning work the model spends before answering. The notebook demonstrates reasoning.effort in the SDK and model-control cells above; this cell focuses on text.verbosity by sending compact and detailed versions of the same policy topic. Inspect the side-by-side text and token summaries to compare style and usage.
from __future__ import annotations
verbosity_prompt = "Explain BrightCart's delayed-replacement policy to a new support agent."
compact_payload = {
"model": MODEL_ID,
"input": verbosity_prompt + " Reply in one sentence under 35 words.",
"text": {"verbosity": "low"},
"max_output_tokens": 1024,
"store": False,
}
detailed_payload = {
"model": MODEL_ID,
"input": verbosity_prompt + " Reply in exactly three numbered plain-text lines, each under 18 words. Do not use leading hyphens or bold text.",
"text": {"verbosity": "high"},
"max_output_tokens": 1024,
"store": False,
}
print_labeled_json("Request shape", {
"compact": redact_payload(compact_payload),
"detailed": redact_payload(detailed_payload),
})
try:
compact_response = create_response(**compact_payload)
detailed_response = create_response(**detailed_payload)
compact_guidance_text = output_text(compact_response).strip()
detailed_guidance_text = output_text(detailed_response).strip()
require(compact_guidance_text and detailed_guidance_text, "Verbosity responses did not return text.")
compact_summary = summarize_response(compact_response)
detailed_summary = summarize_response(detailed_response)
status = "pass" if compact_summary.get("status") in {None, "completed"} and detailed_summary.get("status") in {None, "completed"} else "warn"
record_check("Verbosity", status, {"compact_chars": len(compact_guidance_text), "detailed_chars": len(detailed_guidance_text)})
record_response("Compact policy guidance", "text", compact_guidance_text)
record_response("Detailed policy guidance", "text", detailed_guidance_text)
print_labeled_text("Result: compact guidance", compact_guidance_text)
print_labeled_text("Result: detailed guidance", detailed_guidance_text)
verbosity_summary = pd.DataFrame([
{"request": "compact", **compact_summary},
{"request": "detailed", **detailed_summary},
])
print_label("Response summary")
display_wrapped_table(verbosity_summary, max_col_width_px=420)
print_key_takeaway('Verbosity controls tune the answer style while the prompt still bounds the output.')
except Exception as exc:
handle_example_error("Verbosity", exc)| request | id | model | status | output_item_types | input_tokens | output_tokens | total_tokens | cached_input_tokens | reasoning_output_tokens | service_tier |
|---|---|---|---|---|---|---|---|---|---|---|
| compact | resp_m33l24gl3wl55lwqxhnrqkrf34ikc4rex4sazo2pnqpp4malnliq | openai.gpt-5.4 | completed | [message] | 180 | 34 | 214 | 0 | 0 | default |
| detailed | resp_icqbez74xlmlvw4kl3yf2qtbutfopfefayzftragcwmhu4cohnua | openai.gpt-5.4 | completed | [message] | 197 | 60 | 257 | 0 | 0 | default |