[{"data":1,"prerenderedAt":2232},["ShallowReactive",2],{"page-\u002Fprompt-engineering\u002F07-output-formatting-and-structured-data":3},{"id":4,"title":5,"body":6,"description":2225,"extension":2226,"meta":2227,"navigation":76,"path":2228,"seo":2229,"stem":2230,"__hash__":2231},"content\u002Fprompt-engineering\u002F07-output-formatting-and-structured-data.md","07 — Output Formatting & Structured Data",{"type":7,"value":8,"toc":2210},"minimark",[9,13,18,27,31,113,222,226,711,1227,1231,1321,1324,1328,1541,1613,1617,1622,1677,1700,1704,1813,1817,1917,1921,2035,2039,2042,2076,2080,2206],[10,11,5],"h1",{"id":12},"_07-output-formatting-structured-data",[14,15,17],"h2",{"id":16},"why-structured-output-matters","Why Structured Output Matters",[19,20,21,22,26],"p",{},"When output is consumed by ",[23,24,25],"em",{},"code"," rather than a human, format stops being cosmetic and becomes a correctness requirement. A summary that's \"close enough\" is fine; a JSON response that's \"almost valid\" breaks your parser.",[14,28,30],{"id":29},"prompted-json-with-explicit-schema","Prompted JSON with Explicit Schema",[32,33,36],"code-wrapper",{"filename":34,"language":35},"prompted_json.md","markdown",[37,38,42],"pre",{"className":39,"code":40,"language":35,"meta":41,"style":41},"language-markdown shiki shiki-themes github-light github-dark","Extract the following fields from the job posting below and return them as\na JSON object with exactly these keys: \"title\" (string), \"company\" (string),\n\"salary_min\" (number or null if not stated), \"salary_max\" (number or null\nif not stated), \"remote\" (boolean), \"required_skills\" (array of strings).\n\nReturn only the JSON object. Do not include any explanation, markdown code\nfences, or additional text before or after it.\n\nJob posting:\n\"Senior Backend Engineer at Fintech Startup Co. Fully remote. $140k-$180k\nDOE. Must have 5+ years with distributed systems, Kafka, and PostgreSQL.\"\n","",[25,43,44,53,59,65,71,78,84,90,95,101,107],{"__ignoreMap":41},[45,46,49],"span",{"class":47,"line":48},"line",1,[45,50,52],{"class":51},"ssxIu","Extract the following fields from the job posting below and return them as\n",[45,54,56],{"class":47,"line":55},2,[45,57,58],{"class":51},"a JSON object with exactly these keys: \"title\" (string), \"company\" (string),\n",[45,60,62],{"class":47,"line":61},3,[45,63,64],{"class":51},"\"salary_min\" (number or null if not stated), \"salary_max\" (number or null\n",[45,66,68],{"class":47,"line":67},4,[45,69,70],{"class":51},"if not stated), \"remote\" (boolean), \"required_skills\" (array of strings).\n",[45,72,74],{"class":47,"line":73},5,[45,75,77],{"emptyLinePlaceholder":76},true,"\n",[45,79,81],{"class":47,"line":80},6,[45,82,83],{"class":51},"Return only the JSON object. Do not include any explanation, markdown code\n",[45,85,87],{"class":47,"line":86},7,[45,88,89],{"class":51},"fences, or additional text before or after it.\n",[45,91,93],{"class":47,"line":92},8,[45,94,77],{"emptyLinePlaceholder":76},[45,96,98],{"class":47,"line":97},9,[45,99,100],{"class":51},"Job posting:\n",[45,102,104],{"class":47,"line":103},10,[45,105,106],{"class":51},"\"Senior Backend Engineer at Fintech Startup Co. Fully remote. $140k-$180k\n",[45,108,110],{"class":47,"line":109},11,[45,111,112],{"class":51},"DOE. Must have 5+ years with distributed systems, Kafka, and PostgreSQL.\"\n",[32,114,117],{"filename":115,"language":116},"expected_output.json","json",[37,118,121],{"className":119,"code":120,"language":116,"meta":41,"style":41},"language-json shiki shiki-themes github-light github-dark","{\n  \"title\": \"Senior Backend Engineer\",\n  \"company\": \"Fintech Startup Co.\",\n  \"salary_min\": 140000,\n  \"salary_max\": 180000,\n  \"remote\": true,\n  \"required_skills\": [\"distributed systems\", \"Kafka\", \"PostgreSQL\"]\n}\n",[25,122,123,128,144,156,168,180,192,217],{"__ignoreMap":41},[45,124,125],{"class":47,"line":48},[45,126,127],{"class":51},"{\n",[45,129,130,134,137,141],{"class":47,"line":55},[45,131,133],{"class":132},"snvgF","  \"title\"",[45,135,136],{"class":51},": ",[45,138,140],{"class":139},"sJ6F3","\"Senior Backend Engineer\"",[45,142,143],{"class":51},",\n",[45,145,146,149,151,154],{"class":47,"line":61},[45,147,148],{"class":132},"  \"company\"",[45,150,136],{"class":51},[45,152,153],{"class":139},"\"Fintech Startup Co.\"",[45,155,143],{"class":51},[45,157,158,161,163,166],{"class":47,"line":67},[45,159,160],{"class":132},"  \"salary_min\"",[45,162,136],{"class":51},[45,164,165],{"class":132},"140000",[45,167,143],{"class":51},[45,169,170,173,175,178],{"class":47,"line":73},[45,171,172],{"class":132},"  \"salary_max\"",[45,174,136],{"class":51},[45,176,177],{"class":132},"180000",[45,179,143],{"class":51},[45,181,182,185,187,190],{"class":47,"line":80},[45,183,184],{"class":132},"  \"remote\"",[45,186,136],{"class":51},[45,188,189],{"class":132},"true",[45,191,143],{"class":51},[45,193,194,197,200,203,206,209,211,214],{"class":47,"line":86},[45,195,196],{"class":132},"  \"required_skills\"",[45,198,199],{"class":51},": [",[45,201,202],{"class":139},"\"distributed systems\"",[45,204,205],{"class":51},", ",[45,207,208],{"class":139},"\"Kafka\"",[45,210,205],{"class":51},[45,212,213],{"class":139},"\"PostgreSQL\"",[45,215,216],{"class":51},"]\n",[45,218,219],{"class":47,"line":92},[45,220,221],{"class":51},"}\n",[14,223,225],{"id":224},"prompted-vs-api-enforced-the-critical-distinction","Prompted vs. API-Enforced: The Critical Distinction",[32,227,230],{"filename":228,"language":229},"schema_enforced.py","python",[37,231,234],{"className":232,"code":233,"language":229,"meta":41,"style":41},"language-python shiki shiki-themes github-light github-dark","import json\nfrom anthropic import Anthropic\n\nclient = Anthropic()\n\n# PROMPTED JSON: you ask nicely with a schema description in prompt text.\n# No hard guarantee — model can produce invalid JSON, add prose, wrong types.\n# Reliable MOST of the time, but \"most of the time\" breaks unattended production.\n\n# API-ENFORCED JSON: the API constrains token sampling so only valid JSON\n# matching your schema CAN be generated. Guarantee by construction, not by\n# the model \"choosing\" to comply.\n\nJOB_SCHEMA = {\n    \"type\": \"object\",\n    \"properties\": {\n        \"title\": {\"type\": \"string\"},\n        \"company\": {\"type\": \"string\"},\n        \"salary_min\": {\"type\": [\"number\", \"null\"]},\n        \"salary_max\": {\"type\": [\"number\", \"null\"]},\n        \"remote\": {\"type\": \"boolean\"},\n        \"required_skills\": {\"type\": \"array\", \"items\": {\"type\": \"string\"}},\n    },\n    \"required\": [\"title\", \"company\", \"salary_min\", \"salary_max\", \"remote\", \"required_skills\"],\n    \"additionalProperties\": False,  # ← prevents the model from inventing extra keys\n}\n\nresponse = client.messages.create(\n    model=\"claude-opus-5\",\n    max_tokens=1024,\n    output_config={\"format\": {\"type\": \"json_schema\", \"schema\": JOB_SCHEMA}},\n    messages=[{\"role\": \"user\", \"content\":\n        \"Extract fields from: Senior Backend Engineer at Fintech Startup Co. \"\n        \"Fully remote. $140k-$180k DOE.\"\n    }],\n)\n\ndata = json.loads(response.content[0].text)  # ← GUARANTEED valid; no try\u002Fexcept needed\n# The exact API surface (output_config.format, response_format, etc.) differs by\n# provider and changes over time — always check current docs for your specific model.\n",[25,235,236,245,258,262,273,277,283,288,293,297,302,307,313,318,330,343,352,372,388,411,431,448,479,485,524,541,546,551,562,576,589,623,650,656,662,668,674,679,699,705],{"__ignoreMap":41},[45,237,238,242],{"class":47,"line":48},[45,239,241],{"class":240},"svdQ7","import",[45,243,244],{"class":51}," json\n",[45,246,247,250,253,255],{"class":47,"line":55},[45,248,249],{"class":240},"from",[45,251,252],{"class":51}," anthropic ",[45,254,241],{"class":240},[45,256,257],{"class":51}," Anthropic\n",[45,259,260],{"class":47,"line":61},[45,261,77],{"emptyLinePlaceholder":76},[45,263,264,267,270],{"class":47,"line":67},[45,265,266],{"class":51},"client ",[45,268,269],{"class":240},"=",[45,271,272],{"class":51}," Anthropic()\n",[45,274,275],{"class":47,"line":73},[45,276,77],{"emptyLinePlaceholder":76},[45,278,279],{"class":47,"line":80},[45,280,282],{"class":281},"sdCPZ","# PROMPTED JSON: you ask nicely with a schema description in prompt text.\n",[45,284,285],{"class":47,"line":86},[45,286,287],{"class":281},"# No hard guarantee — model can produce invalid JSON, add prose, wrong types.\n",[45,289,290],{"class":47,"line":92},[45,291,292],{"class":281},"# Reliable MOST of the time, but \"most of the time\" breaks unattended production.\n",[45,294,295],{"class":47,"line":97},[45,296,77],{"emptyLinePlaceholder":76},[45,298,299],{"class":47,"line":103},[45,300,301],{"class":281},"# API-ENFORCED JSON: the API constrains token sampling so only valid JSON\n",[45,303,304],{"class":47,"line":109},[45,305,306],{"class":281},"# matching your schema CAN be generated. Guarantee by construction, not by\n",[45,308,310],{"class":47,"line":309},12,[45,311,312],{"class":281},"# the model \"choosing\" to comply.\n",[45,314,316],{"class":47,"line":315},13,[45,317,77],{"emptyLinePlaceholder":76},[45,319,321,324,327],{"class":47,"line":320},14,[45,322,323],{"class":132},"JOB_SCHEMA",[45,325,326],{"class":240}," =",[45,328,329],{"class":51}," {\n",[45,331,333,336,338,341],{"class":47,"line":332},15,[45,334,335],{"class":139},"    \"type\"",[45,337,136],{"class":51},[45,339,340],{"class":139},"\"object\"",[45,342,143],{"class":51},[45,344,346,349],{"class":47,"line":345},16,[45,347,348],{"class":139},"    \"properties\"",[45,350,351],{"class":51},": {\n",[45,353,355,358,361,364,366,369],{"class":47,"line":354},17,[45,356,357],{"class":139},"        \"title\"",[45,359,360],{"class":51},": {",[45,362,363],{"class":139},"\"type\"",[45,365,136],{"class":51},[45,367,368],{"class":139},"\"string\"",[45,370,371],{"class":51},"},\n",[45,373,375,378,380,382,384,386],{"class":47,"line":374},18,[45,376,377],{"class":139},"        \"company\"",[45,379,360],{"class":51},[45,381,363],{"class":139},[45,383,136],{"class":51},[45,385,368],{"class":139},[45,387,371],{"class":51},[45,389,391,394,396,398,400,403,405,408],{"class":47,"line":390},19,[45,392,393],{"class":139},"        \"salary_min\"",[45,395,360],{"class":51},[45,397,363],{"class":139},[45,399,199],{"class":51},[45,401,402],{"class":139},"\"number\"",[45,404,205],{"class":51},[45,406,407],{"class":139},"\"null\"",[45,409,410],{"class":51},"]},\n",[45,412,414,417,419,421,423,425,427,429],{"class":47,"line":413},20,[45,415,416],{"class":139},"        \"salary_max\"",[45,418,360],{"class":51},[45,420,363],{"class":139},[45,422,199],{"class":51},[45,424,402],{"class":139},[45,426,205],{"class":51},[45,428,407],{"class":139},[45,430,410],{"class":51},[45,432,434,437,439,441,443,446],{"class":47,"line":433},21,[45,435,436],{"class":139},"        \"remote\"",[45,438,360],{"class":51},[45,440,363],{"class":139},[45,442,136],{"class":51},[45,444,445],{"class":139},"\"boolean\"",[45,447,371],{"class":51},[45,449,451,454,456,458,460,463,465,468,470,472,474,476],{"class":47,"line":450},22,[45,452,453],{"class":139},"        \"required_skills\"",[45,455,360],{"class":51},[45,457,363],{"class":139},[45,459,136],{"class":51},[45,461,462],{"class":139},"\"array\"",[45,464,205],{"class":51},[45,466,467],{"class":139},"\"items\"",[45,469,360],{"class":51},[45,471,363],{"class":139},[45,473,136],{"class":51},[45,475,368],{"class":139},[45,477,478],{"class":51},"}},\n",[45,480,482],{"class":47,"line":481},23,[45,483,484],{"class":51},"    },\n",[45,486,488,491,493,496,498,501,503,506,508,511,513,516,518,521],{"class":47,"line":487},24,[45,489,490],{"class":139},"    \"required\"",[45,492,199],{"class":51},[45,494,495],{"class":139},"\"title\"",[45,497,205],{"class":51},[45,499,500],{"class":139},"\"company\"",[45,502,205],{"class":51},[45,504,505],{"class":139},"\"salary_min\"",[45,507,205],{"class":51},[45,509,510],{"class":139},"\"salary_max\"",[45,512,205],{"class":51},[45,514,515],{"class":139},"\"remote\"",[45,517,205],{"class":51},[45,519,520],{"class":139},"\"required_skills\"",[45,522,523],{"class":51},"],\n",[45,525,527,530,532,535,538],{"class":47,"line":526},25,[45,528,529],{"class":139},"    \"additionalProperties\"",[45,531,136],{"class":51},[45,533,534],{"class":132},"False",[45,536,537],{"class":51},",  ",[45,539,540],{"class":281},"# ← prevents the model from inventing extra keys\n",[45,542,544],{"class":47,"line":543},26,[45,545,221],{"class":51},[45,547,549],{"class":47,"line":548},27,[45,550,77],{"emptyLinePlaceholder":76},[45,552,554,557,559],{"class":47,"line":553},28,[45,555,556],{"class":51},"response ",[45,558,269],{"class":240},[45,560,561],{"class":51}," client.messages.create(\n",[45,563,565,569,571,574],{"class":47,"line":564},29,[45,566,568],{"class":567},"sCrzJ","    model",[45,570,269],{"class":240},[45,572,573],{"class":139},"\"claude-opus-5\"",[45,575,143],{"class":51},[45,577,579,582,584,587],{"class":47,"line":578},30,[45,580,581],{"class":567},"    max_tokens",[45,583,269],{"class":240},[45,585,586],{"class":132},"1024",[45,588,143],{"class":51},[45,590,592,595,597,600,603,605,607,609,612,614,617,619,621],{"class":47,"line":591},31,[45,593,594],{"class":567},"    output_config",[45,596,269],{"class":240},[45,598,599],{"class":51},"{",[45,601,602],{"class":139},"\"format\"",[45,604,360],{"class":51},[45,606,363],{"class":139},[45,608,136],{"class":51},[45,610,611],{"class":139},"\"json_schema\"",[45,613,205],{"class":51},[45,615,616],{"class":139},"\"schema\"",[45,618,136],{"class":51},[45,620,323],{"class":132},[45,622,478],{"class":51},[45,624,626,629,631,634,637,639,642,644,647],{"class":47,"line":625},32,[45,627,628],{"class":567},"    messages",[45,630,269],{"class":240},[45,632,633],{"class":51},"[{",[45,635,636],{"class":139},"\"role\"",[45,638,136],{"class":51},[45,640,641],{"class":139},"\"user\"",[45,643,205],{"class":51},[45,645,646],{"class":139},"\"content\"",[45,648,649],{"class":51},":\n",[45,651,653],{"class":47,"line":652},33,[45,654,655],{"class":139},"        \"Extract fields from: Senior Backend Engineer at Fintech Startup Co. \"\n",[45,657,659],{"class":47,"line":658},34,[45,660,661],{"class":139},"        \"Fully remote. $140k-$180k DOE.\"\n",[45,663,665],{"class":47,"line":664},35,[45,666,667],{"class":51},"    }],\n",[45,669,671],{"class":47,"line":670},36,[45,672,673],{"class":51},")\n",[45,675,677],{"class":47,"line":676},37,[45,678,77],{"emptyLinePlaceholder":76},[45,680,682,685,687,690,693,696],{"class":47,"line":681},38,[45,683,684],{"class":51},"data ",[45,686,269],{"class":240},[45,688,689],{"class":51}," json.loads(response.content[",[45,691,692],{"class":132},"0",[45,694,695],{"class":51},"].text)  ",[45,697,698],{"class":281},"# ← GUARANTEED valid; no try\u002Fexcept needed\n",[45,700,702],{"class":47,"line":701},39,[45,703,704],{"class":281},"# The exact API surface (output_config.format, response_format, etc.) differs by\n",[45,706,708],{"class":47,"line":707},40,[45,709,710],{"class":281},"# provider and changes over time — always check current docs for your specific model.\n",[32,712,714],{"filename":713,"language":229},"prompted_vs_enforced.py",[37,715,717],{"className":232,"code":716,"language":229,"meta":41,"style":41},"# ANTI-PATTERN: relying on prompted JSON for production parsing\ndef parse_prompted_json_naive(response_text: str) -> dict:\n    \"\"\"This WILL fail in production — the model adds prose, code fences, etc.\"\"\"\n    return json.loads(response_text)  # crashes on \"Sure! Here's the JSON:\\n```json\\n{...}\\n```\"\n\n# PRODUCTION: layered defense — try clean parse, fall back to extraction, then validate\nimport re\n\ndef parse_model_json_robust(response_text: str, schema: dict) -> dict:\n    \"\"\"Extract JSON from model output with multiple fallback strategies.\"\"\"\n    # Strategy 1: direct parse (works if API-enforced or model was clean)\n    try:\n        return validate_schema(json.loads(response_text), schema)\n    except json.JSONDecodeError:\n        pass\n\n    # Strategy 2: extract from code fence\n    fence_match = re.search(r'```(?:json)?\\s*(\\{.*?\\})\\s*```', response_text, re.DOTALL)\n    if fence_match:\n        try:\n            return validate_schema(json.loads(fence_match.group(1)), schema)\n        except (json.JSONDecodeError, SchemaError):\n            pass\n\n    # Strategy 3: find first { ... } in the text\n    brace_match = re.search(r'\\{[^{}]*(?:\\{[^{}]*\\}[^{}]*)*\\}', response_text, re.DOTALL)\n    if brace_match:\n        try:\n            return validate_schema(json.loads(brace_match.group(0)), schema)\n        except (json.JSONDecodeError, SchemaError):\n            pass\n\n    raise JSONExtractionError(f\"Could not extract valid JSON from response: {response_text[:200]}\")\n\ndef validate_schema(data: dict, schema: dict) -> dict:\n    \"\"\"Lightweight schema validation — check required fields and types.\"\"\"\n    for field in schema.get(\"required\", []):\n        if field not in data:\n            raise SchemaError(f\"Missing required field: {field}\")\n    return data\n\n# BUT: if your provider offers API-enforced structured output, USE THAT INSTEAD.\n# The robust parser is a fallback for when enforcement isn't available, not a\n# replacement for it. Prompted JSON is \"most of the time\"; API-enforced is \"always.\"\n",[25,718,719,724,747,752,763,767,772,779,783,805,810,815,822,830,838,843,847,852,922,930,937,951,959,964,968,973,1035,1042,1048,1059,1065,1069,1073,1106,1110,1132,1137,1157,1173,1197,1204,1209,1215,1221],{"__ignoreMap":41},[45,720,721],{"class":47,"line":48},[45,722,723],{"class":281},"# ANTI-PATTERN: relying on prompted JSON for production parsing\n",[45,725,726,729,733,736,739,742,745],{"class":47,"line":55},[45,727,728],{"class":240},"def",[45,730,732],{"class":731},"sIsaT"," parse_prompted_json_naive",[45,734,735],{"class":51},"(response_text: ",[45,737,738],{"class":132},"str",[45,740,741],{"class":51},") -> ",[45,743,744],{"class":132},"dict",[45,746,649],{"class":51},[45,748,749],{"class":47,"line":61},[45,750,751],{"class":139},"    \"\"\"This WILL fail in production — the model adds prose, code fences, etc.\"\"\"\n",[45,753,754,757,760],{"class":47,"line":67},[45,755,756],{"class":240},"    return",[45,758,759],{"class":51}," json.loads(response_text)  ",[45,761,762],{"class":281},"# crashes on \"Sure! Here's the JSON:\\n```json\\n{...}\\n```\"\n",[45,764,765],{"class":47,"line":73},[45,766,77],{"emptyLinePlaceholder":76},[45,768,769],{"class":47,"line":80},[45,770,771],{"class":281},"# PRODUCTION: layered defense — try clean parse, fall back to extraction, then validate\n",[45,773,774,776],{"class":47,"line":86},[45,775,241],{"class":240},[45,777,778],{"class":51}," re\n",[45,780,781],{"class":47,"line":92},[45,782,77],{"emptyLinePlaceholder":76},[45,784,785,787,790,792,794,797,799,801,803],{"class":47,"line":97},[45,786,728],{"class":240},[45,788,789],{"class":731}," parse_model_json_robust",[45,791,735],{"class":51},[45,793,738],{"class":132},[45,795,796],{"class":51},", schema: ",[45,798,744],{"class":132},[45,800,741],{"class":51},[45,802,744],{"class":132},[45,804,649],{"class":51},[45,806,807],{"class":47,"line":103},[45,808,809],{"class":139},"    \"\"\"Extract JSON from model output with multiple fallback strategies.\"\"\"\n",[45,811,812],{"class":47,"line":109},[45,813,814],{"class":281},"    # Strategy 1: direct parse (works if API-enforced or model was clean)\n",[45,816,817,820],{"class":47,"line":309},[45,818,819],{"class":240},"    try",[45,821,649],{"class":51},[45,823,824,827],{"class":47,"line":315},[45,825,826],{"class":240},"        return",[45,828,829],{"class":51}," validate_schema(json.loads(response_text), schema)\n",[45,831,832,835],{"class":47,"line":320},[45,833,834],{"class":240},"    except",[45,836,837],{"class":51}," json.JSONDecodeError:\n",[45,839,840],{"class":47,"line":332},[45,841,842],{"class":240},"        pass\n",[45,844,845],{"class":47,"line":345},[45,846,77],{"emptyLinePlaceholder":76},[45,848,849],{"class":47,"line":354},[45,850,851],{"class":281},"    # Strategy 2: extract from code fence\n",[45,853,854,857,859,862,865,868,872,875,877,880,883,886,889,892,896,899,902,905,908,910,912,914,917,920],{"class":47,"line":374},[45,855,856],{"class":51},"    fence_match ",[45,858,269],{"class":240},[45,860,861],{"class":51}," re.search(",[45,863,864],{"class":240},"r",[45,866,867],{"class":139},"'",[45,869,871],{"class":870},"svAP2","```",[45,873,874],{"class":132},"(?:",[45,876,116],{"class":870},[45,878,879],{"class":132},")",[45,881,882],{"class":240},"?",[45,884,885],{"class":132},"\\s",[45,887,888],{"class":240},"*",[45,890,891],{"class":132},"(",[45,893,895],{"class":894},"snRuI","\\{",[45,897,898],{"class":132},".",[45,900,901],{"class":240},"*?",[45,903,904],{"class":894},"\\}",[45,906,907],{"class":132},")\\s",[45,909,888],{"class":240},[45,911,871],{"class":870},[45,913,867],{"class":139},[45,915,916],{"class":51},", response_text, re.",[45,918,919],{"class":132},"DOTALL",[45,921,673],{"class":51},[45,923,924,927],{"class":47,"line":390},[45,925,926],{"class":240},"    if",[45,928,929],{"class":51}," fence_match:\n",[45,931,932,935],{"class":47,"line":413},[45,933,934],{"class":240},"        try",[45,936,649],{"class":51},[45,938,939,942,945,948],{"class":47,"line":433},[45,940,941],{"class":240},"            return",[45,943,944],{"class":51}," validate_schema(json.loads(fence_match.group(",[45,946,947],{"class":132},"1",[45,949,950],{"class":51},")), schema)\n",[45,952,953,956],{"class":47,"line":450},[45,954,955],{"class":240},"        except",[45,957,958],{"class":51}," (json.JSONDecodeError, SchemaError):\n",[45,960,961],{"class":47,"line":481},[45,962,963],{"class":240},"            pass\n",[45,965,966],{"class":47,"line":487},[45,967,77],{"emptyLinePlaceholder":76},[45,969,970],{"class":47,"line":526},[45,971,972],{"class":281},"    # Strategy 3: find first { ... } in the text\n",[45,974,975,978,980,982,984,986,988,991,994,997,999,1001,1003,1005,1007,1009,1011,1013,1015,1017,1019,1021,1023,1025,1027,1029,1031,1033],{"class":47,"line":543},[45,976,977],{"class":51},"    brace_match ",[45,979,269],{"class":240},[45,981,861],{"class":51},[45,983,864],{"class":240},[45,985,867],{"class":139},[45,987,895],{"class":894},[45,989,990],{"class":132},"[",[45,992,993],{"class":240},"^",[45,995,996],{"class":132},"{}]",[45,998,888],{"class":240},[45,1000,874],{"class":132},[45,1002,895],{"class":894},[45,1004,990],{"class":132},[45,1006,993],{"class":240},[45,1008,996],{"class":132},[45,1010,888],{"class":240},[45,1012,904],{"class":894},[45,1014,990],{"class":132},[45,1016,993],{"class":240},[45,1018,996],{"class":132},[45,1020,888],{"class":240},[45,1022,879],{"class":132},[45,1024,888],{"class":240},[45,1026,904],{"class":894},[45,1028,867],{"class":139},[45,1030,916],{"class":51},[45,1032,919],{"class":132},[45,1034,673],{"class":51},[45,1036,1037,1039],{"class":47,"line":548},[45,1038,926],{"class":240},[45,1040,1041],{"class":51}," brace_match:\n",[45,1043,1044,1046],{"class":47,"line":553},[45,1045,934],{"class":240},[45,1047,649],{"class":51},[45,1049,1050,1052,1055,1057],{"class":47,"line":564},[45,1051,941],{"class":240},[45,1053,1054],{"class":51}," validate_schema(json.loads(brace_match.group(",[45,1056,692],{"class":132},[45,1058,950],{"class":51},[45,1060,1061,1063],{"class":47,"line":578},[45,1062,955],{"class":240},[45,1064,958],{"class":51},[45,1066,1067],{"class":47,"line":591},[45,1068,963],{"class":240},[45,1070,1071],{"class":47,"line":625},[45,1072,77],{"emptyLinePlaceholder":76},[45,1074,1075,1078,1081,1084,1087,1089,1092,1095,1098,1101,1104],{"class":47,"line":652},[45,1076,1077],{"class":240},"    raise",[45,1079,1080],{"class":51}," JSONExtractionError(",[45,1082,1083],{"class":240},"f",[45,1085,1086],{"class":139},"\"Could not extract valid JSON from response: ",[45,1088,599],{"class":132},[45,1090,1091],{"class":51},"response_text[:",[45,1093,1094],{"class":132},"200",[45,1096,1097],{"class":51},"]",[45,1099,1100],{"class":132},"}",[45,1102,1103],{"class":139},"\"",[45,1105,673],{"class":51},[45,1107,1108],{"class":47,"line":658},[45,1109,77],{"emptyLinePlaceholder":76},[45,1111,1112,1114,1117,1120,1122,1124,1126,1128,1130],{"class":47,"line":664},[45,1113,728],{"class":240},[45,1115,1116],{"class":731}," validate_schema",[45,1118,1119],{"class":51},"(data: ",[45,1121,744],{"class":132},[45,1123,796],{"class":51},[45,1125,744],{"class":132},[45,1127,741],{"class":51},[45,1129,744],{"class":132},[45,1131,649],{"class":51},[45,1133,1134],{"class":47,"line":670},[45,1135,1136],{"class":139},"    \"\"\"Lightweight schema validation — check required fields and types.\"\"\"\n",[45,1138,1139,1142,1145,1148,1151,1154],{"class":47,"line":676},[45,1140,1141],{"class":240},"    for",[45,1143,1144],{"class":51}," field ",[45,1146,1147],{"class":240},"in",[45,1149,1150],{"class":51}," schema.get(",[45,1152,1153],{"class":139},"\"required\"",[45,1155,1156],{"class":51},", []):\n",[45,1158,1159,1162,1164,1167,1170],{"class":47,"line":681},[45,1160,1161],{"class":240},"        if",[45,1163,1144],{"class":51},[45,1165,1166],{"class":240},"not",[45,1168,1169],{"class":240}," in",[45,1171,1172],{"class":51}," data:\n",[45,1174,1175,1178,1181,1183,1186,1188,1191,1193,1195],{"class":47,"line":701},[45,1176,1177],{"class":240},"            raise",[45,1179,1180],{"class":51}," SchemaError(",[45,1182,1083],{"class":240},[45,1184,1185],{"class":139},"\"Missing required field: ",[45,1187,599],{"class":132},[45,1189,1190],{"class":51},"field",[45,1192,1100],{"class":132},[45,1194,1103],{"class":139},[45,1196,673],{"class":51},[45,1198,1199,1201],{"class":47,"line":707},[45,1200,756],{"class":240},[45,1202,1203],{"class":51}," data\n",[45,1205,1207],{"class":47,"line":1206},41,[45,1208,77],{"emptyLinePlaceholder":76},[45,1210,1212],{"class":47,"line":1211},42,[45,1213,1214],{"class":281},"# BUT: if your provider offers API-enforced structured output, USE THAT INSTEAD.\n",[45,1216,1218],{"class":47,"line":1217},43,[45,1219,1220],{"class":281},"# The robust parser is a fallback for when enforcement isn't available, not a\n",[45,1222,1224],{"class":47,"line":1223},44,[45,1225,1226],{"class":281},"# replacement for it. Prompted JSON is \"most of the time\"; API-enforced is \"always.\"\n",[14,1228,1230],{"id":1229},"xml-output-for-mixed-structured-and-prose-content","XML Output for Mixed Structured-and-Prose Content",[32,1232,1234],{"filename":1233,"language":35},"xml_output.md",[37,1235,1237],{"className":39,"code":1236,"language":35,"meta":41,"style":41},"Analyze the following customer feedback. Respond using this exact XML\nstructure:\n\n\u003Canalysis>\n  \u003Csentiment>positive|negative|mixed\u003C\u002Fsentiment>\n  \u003Ckey_themes>\n    \u003Ctheme>...\u003C\u002Ftheme>\n    \u003C!-- one \u003Ctheme> element per distinct theme identified, at most 5 -->\n  \u003C\u002Fkey_themes>\n  \u003Crecommended_action>...\u003C\u002Frecommended_action>\n\u003C\u002Fanalysis>\n\nDo not include anything outside the \u003Canalysis> tags.\n\nFeedback: \"The app is fast and the design is beautiful, but I've lost work\ntwice now because it doesn't autosave. Please fix this before I recommend\nit to my team.\"\n",[25,1238,1239,1244,1249,1253,1258,1263,1268,1273,1278,1283,1288,1293,1297,1302,1306,1311,1316],{"__ignoreMap":41},[45,1240,1241],{"class":47,"line":48},[45,1242,1243],{"class":51},"Analyze the following customer feedback. Respond using this exact XML\n",[45,1245,1246],{"class":47,"line":55},[45,1247,1248],{"class":51},"structure:\n",[45,1250,1251],{"class":47,"line":61},[45,1252,77],{"emptyLinePlaceholder":76},[45,1254,1255],{"class":47,"line":67},[45,1256,1257],{"class":51},"\u003Canalysis>\n",[45,1259,1260],{"class":47,"line":73},[45,1261,1262],{"class":51},"  \u003Csentiment>positive|negative|mixed\u003C\u002Fsentiment>\n",[45,1264,1265],{"class":47,"line":80},[45,1266,1267],{"class":51},"  \u003Ckey_themes>\n",[45,1269,1270],{"class":47,"line":86},[45,1271,1272],{"class":51},"    \u003Ctheme>...\u003C\u002Ftheme>\n",[45,1274,1275],{"class":47,"line":92},[45,1276,1277],{"class":51},"    \u003C!-- one \u003Ctheme> element per distinct theme identified, at most 5 -->\n",[45,1279,1280],{"class":47,"line":97},[45,1281,1282],{"class":51},"  \u003C\u002Fkey_themes>\n",[45,1284,1285],{"class":47,"line":103},[45,1286,1287],{"class":51},"  \u003Crecommended_action>...\u003C\u002Frecommended_action>\n",[45,1289,1290],{"class":47,"line":109},[45,1291,1292],{"class":51},"\u003C\u002Fanalysis>\n",[45,1294,1295],{"class":47,"line":309},[45,1296,77],{"emptyLinePlaceholder":76},[45,1298,1299],{"class":47,"line":315},[45,1300,1301],{"class":51},"Do not include anything outside the \u003Canalysis> tags.\n",[45,1303,1304],{"class":47,"line":320},[45,1305,77],{"emptyLinePlaceholder":76},[45,1307,1308],{"class":47,"line":332},[45,1309,1310],{"class":51},"Feedback: \"The app is fast and the design is beautiful, but I've lost work\n",[45,1312,1313],{"class":47,"line":345},[45,1314,1315],{"class":51},"twice now because it doesn't autosave. Please fix this before I recommend\n",[45,1317,1318],{"class":47,"line":354},[45,1319,1320],{"class":51},"it to my team.\"\n",[19,1322,1323],{},"XML is often better than JSON when the structure includes variable-length lists of rich content, mixed prose and structure, or nested sections that would require awkward escaping in JSON strings.",[14,1325,1327],{"id":1326},"function-calling-tool-use-as-structured-output","Function Calling \u002F Tool Use as Structured Output",[32,1329,1331],{"filename":1330,"language":116},"tool_definition.json",[37,1332,1334],{"className":119,"code":1333,"language":116,"meta":41,"style":41},"{\n  \"name\": \"extract_job_posting\",\n  \"description\": \"Extract structured fields from a job posting.\",\n  \"input_schema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"title\": {\"type\": \"string\"},\n      \"company\": {\"type\": \"string\"},\n      \"salary_min\": {\"type\": [\"number\", \"null\"]},\n      \"salary_max\": {\"type\": [\"number\", \"null\"]},\n      \"remote\": {\"type\": \"boolean\"},\n      \"required_skills\": {\"type\": \"array\", \"items\": {\"type\": \"string\"}}\n    },\n    \"required\": [\"title\", \"company\", \"salary_min\", \"salary_max\", \"remote\", \"required_skills\"]\n  }\n}\n",[25,1335,1336,1340,1352,1364,1371,1381,1387,1402,1417,1436,1455,1470,1498,1502,1532,1537],{"__ignoreMap":41},[45,1337,1338],{"class":47,"line":48},[45,1339,127],{"class":51},[45,1341,1342,1345,1347,1350],{"class":47,"line":55},[45,1343,1344],{"class":132},"  \"name\"",[45,1346,136],{"class":51},[45,1348,1349],{"class":139},"\"extract_job_posting\"",[45,1351,143],{"class":51},[45,1353,1354,1357,1359,1362],{"class":47,"line":61},[45,1355,1356],{"class":132},"  \"description\"",[45,1358,136],{"class":51},[45,1360,1361],{"class":139},"\"Extract structured fields from a job posting.\"",[45,1363,143],{"class":51},[45,1365,1366,1369],{"class":47,"line":67},[45,1367,1368],{"class":132},"  \"input_schema\"",[45,1370,351],{"class":51},[45,1372,1373,1375,1377,1379],{"class":47,"line":73},[45,1374,335],{"class":132},[45,1376,136],{"class":51},[45,1378,340],{"class":139},[45,1380,143],{"class":51},[45,1382,1383,1385],{"class":47,"line":80},[45,1384,348],{"class":132},[45,1386,351],{"class":51},[45,1388,1389,1392,1394,1396,1398,1400],{"class":47,"line":86},[45,1390,1391],{"class":132},"      \"title\"",[45,1393,360],{"class":51},[45,1395,363],{"class":132},[45,1397,136],{"class":51},[45,1399,368],{"class":139},[45,1401,371],{"class":51},[45,1403,1404,1407,1409,1411,1413,1415],{"class":47,"line":92},[45,1405,1406],{"class":132},"      \"company\"",[45,1408,360],{"class":51},[45,1410,363],{"class":132},[45,1412,136],{"class":51},[45,1414,368],{"class":139},[45,1416,371],{"class":51},[45,1418,1419,1422,1424,1426,1428,1430,1432,1434],{"class":47,"line":97},[45,1420,1421],{"class":132},"      \"salary_min\"",[45,1423,360],{"class":51},[45,1425,363],{"class":132},[45,1427,199],{"class":51},[45,1429,402],{"class":139},[45,1431,205],{"class":51},[45,1433,407],{"class":139},[45,1435,410],{"class":51},[45,1437,1438,1441,1443,1445,1447,1449,1451,1453],{"class":47,"line":103},[45,1439,1440],{"class":132},"      \"salary_max\"",[45,1442,360],{"class":51},[45,1444,363],{"class":132},[45,1446,199],{"class":51},[45,1448,402],{"class":139},[45,1450,205],{"class":51},[45,1452,407],{"class":139},[45,1454,410],{"class":51},[45,1456,1457,1460,1462,1464,1466,1468],{"class":47,"line":109},[45,1458,1459],{"class":132},"      \"remote\"",[45,1461,360],{"class":51},[45,1463,363],{"class":132},[45,1465,136],{"class":51},[45,1467,445],{"class":139},[45,1469,371],{"class":51},[45,1471,1472,1475,1477,1479,1481,1483,1485,1487,1489,1491,1493,1495],{"class":47,"line":309},[45,1473,1474],{"class":132},"      \"required_skills\"",[45,1476,360],{"class":51},[45,1478,363],{"class":132},[45,1480,136],{"class":51},[45,1482,462],{"class":139},[45,1484,205],{"class":51},[45,1486,467],{"class":132},[45,1488,360],{"class":51},[45,1490,363],{"class":132},[45,1492,136],{"class":51},[45,1494,368],{"class":139},[45,1496,1497],{"class":51},"}}\n",[45,1499,1500],{"class":47,"line":315},[45,1501,484],{"class":51},[45,1503,1504,1506,1508,1510,1512,1514,1516,1518,1520,1522,1524,1526,1528,1530],{"class":47,"line":320},[45,1505,490],{"class":132},[45,1507,199],{"class":51},[45,1509,495],{"class":139},[45,1511,205],{"class":51},[45,1513,500],{"class":139},[45,1515,205],{"class":51},[45,1517,505],{"class":139},[45,1519,205],{"class":51},[45,1521,510],{"class":139},[45,1523,205],{"class":51},[45,1525,515],{"class":139},[45,1527,205],{"class":51},[45,1529,520],{"class":139},[45,1531,216],{"class":51},[45,1533,1534],{"class":47,"line":332},[45,1535,1536],{"class":51},"  }\n",[45,1538,1539],{"class":47,"line":345},[45,1540,221],{"class":51},[32,1542,1544],{"filename":1543,"language":229},"tool_vs_schema.py",[37,1545,1547],{"className":232,"code":1546,"language":229,"meta":41,"style":41},"# Tool use and structured-output extraction are the SAME underlying mechanism,\n# applied to two framings:\n#   - Schema-constrained extraction: \"here's a schema for the object you should return\"\n#   - Tool use: \"here's a schema for the function you should call\"\n#\n# The JSON is identical. The difference is SEMANTIC:\n#   - Extraction: the task is \"produce this data shape\"\n#   - Tool use: the task is \"decide WHETHER and HOW to invoke an external capability\"\n#\n# When the task is genuinely \"extract this data\" → use schema-constrained output.\n# When the task is \"the model needs to decide to search\u002Fquery\u002Fsend\" → use tool use,\n# because it also carries semantics (name + description) that help the model reason\n# about WHEN to invoke, not just what shape to produce.\n",[25,1548,1549,1554,1559,1564,1569,1574,1579,1584,1589,1593,1598,1603,1608],{"__ignoreMap":41},[45,1550,1551],{"class":47,"line":48},[45,1552,1553],{"class":281},"# Tool use and structured-output extraction are the SAME underlying mechanism,\n",[45,1555,1556],{"class":47,"line":55},[45,1557,1558],{"class":281},"# applied to two framings:\n",[45,1560,1561],{"class":47,"line":61},[45,1562,1563],{"class":281},"#   - Schema-constrained extraction: \"here's a schema for the object you should return\"\n",[45,1565,1566],{"class":47,"line":67},[45,1567,1568],{"class":281},"#   - Tool use: \"here's a schema for the function you should call\"\n",[45,1570,1571],{"class":47,"line":73},[45,1572,1573],{"class":281},"#\n",[45,1575,1576],{"class":47,"line":80},[45,1577,1578],{"class":281},"# The JSON is identical. The difference is SEMANTIC:\n",[45,1580,1581],{"class":47,"line":86},[45,1582,1583],{"class":281},"#   - Extraction: the task is \"produce this data shape\"\n",[45,1585,1586],{"class":47,"line":92},[45,1587,1588],{"class":281},"#   - Tool use: the task is \"decide WHETHER and HOW to invoke an external capability\"\n",[45,1590,1591],{"class":47,"line":97},[45,1592,1573],{"class":281},[45,1594,1595],{"class":47,"line":103},[45,1596,1597],{"class":281},"# When the task is genuinely \"extract this data\" → use schema-constrained output.\n",[45,1599,1600],{"class":47,"line":109},[45,1601,1602],{"class":281},"# When the task is \"the model needs to decide to search\u002Fquery\u002Fsend\" → use tool use,\n",[45,1604,1605],{"class":47,"line":309},[45,1606,1607],{"class":281},"# because it also carries semantics (name + description) that help the model reason\n",[45,1609,1610],{"class":47,"line":315},[45,1611,1612],{"class":281},"# about WHEN to invoke, not just what shape to produce.\n",[14,1614,1616],{"id":1615},"common-failure-modes","Common Failure Modes",[1618,1619,1621],"h3",{"id":1620},"the-unwanted-preamble","The Unwanted Preamble",[32,1623,1625,1667,1670],{"filename":1624,"language":35},"unwanted_preamble.md",[37,1626,1628],{"className":39,"code":1627,"language":35,"meta":41,"style":41},"\u003C!-- ANTI-PATTERN: what the model produces without explicit suppression -->\nSure! Here's the JSON object you requested:\n\n```json\n{\"title\": \"Senior Backend Engineer\", ...}\n",[25,1629,1630,1635,1640,1644,1649],{"__ignoreMap":41},[45,1631,1632],{"class":47,"line":48},[45,1633,1634],{"class":281},"\u003C!-- ANTI-PATTERN: what the model produces without explicit suppression -->\n",[45,1636,1637],{"class":47,"line":55},[45,1638,1639],{"class":51},"Sure! Here's the JSON object you requested:\n",[45,1641,1642],{"class":47,"line":61},[45,1643,77],{"emptyLinePlaceholder":76},[45,1645,1646],{"class":47,"line":67},[45,1647,1648],{"class":51},"```json\n",[45,1650,1651,1653,1655,1657,1659,1661,1665],{"class":47,"line":73},[45,1652,599],{"class":51},[45,1654,495],{"class":132},[45,1656,136],{"class":51},[45,1658,140],{"class":139},[45,1660,205],{"class":51},[45,1662,1664],{"class":1663},"sMKGj","...",[45,1666,221],{"class":51},[19,1668,1669],{},"Let me know if you need anything else!",[37,1671,1675],{"className":1672,"code":41,"language":1674},[1673],"language-text","text",[25,1676,41],{"__ignoreMap":41},[32,1678,1680],{"filename":1679,"language":35},"preamble_fix.md",[37,1681,1683],{"className":39,"code":1682,"language":35,"meta":41,"style":41},"\u003C!-- PRODUCTION: explicit suppression + API enforcement -->\nReturn only the JSON object, no other text, no code fences, no preamble,\nno postamble. Begin your response with { and end it with }.\n",[25,1684,1685,1690,1695],{"__ignoreMap":41},[45,1686,1687],{"class":47,"line":48},[45,1688,1689],{"class":281},"\u003C!-- PRODUCTION: explicit suppression + API enforcement -->\n",[45,1691,1692],{"class":47,"line":55},[45,1693,1694],{"class":51},"Return only the JSON object, no other text, no code fences, no preamble,\n",[45,1696,1697],{"class":47,"line":61},[45,1698,1699],{"class":51},"no postamble. Begin your response with { and end it with }.\n",[1618,1701,1703],{"id":1702},"overly-rigid-format-causing-truncation","Overly Rigid Format Causing Truncation",[32,1705,1707],{"filename":1706,"language":229},"truncation_anti_pattern.py",[37,1708,1710],{"className":232,"code":1709,"language":229,"meta":41,"style":41},"# ANTI-PATTERN: a format spec so verbose it consumes the output budget\nBAD_FORMAT_SPEC = \"\"\"\nRespond with a JSON object containing exactly 15 keys: title, subtitle,\nintroduction (minimum 200 words), background (minimum 300 words), methodology\n(minimum 250 words), findings (minimum 400 words, must include at least 3\nnumbered sub-points each with its own citation), implications (minimum 200 words)...\n[continues for 15 total sections with individually specified minimum lengths]\n\"\"\"\n\n# The format spec itself is so demanding that the actual CONTENT gets cut short\n# to fit the mandated structure — especially with a tight max_tokens limit.\n# The model spends its budget complying with the format, not producing substance.\n\n# PRODUCTION: bound the format complexity to the output budget\nGOOD_FORMAT_SPEC = \"\"\"\nRespond with a JSON object: {\"summary\": string, \"key_points\": array of\nstrings (max 5), \"recommendation\": string}. Keep \"summary\" under 100 words.\n\"\"\"\n# Simple enough that the model can produce real content within the budget.\n",[25,1711,1712,1717,1727,1732,1737,1742,1747,1752,1757,1761,1766,1771,1776,1780,1785,1794,1799,1804,1808],{"__ignoreMap":41},[45,1713,1714],{"class":47,"line":48},[45,1715,1716],{"class":281},"# ANTI-PATTERN: a format spec so verbose it consumes the output budget\n",[45,1718,1719,1722,1724],{"class":47,"line":55},[45,1720,1721],{"class":132},"BAD_FORMAT_SPEC",[45,1723,326],{"class":240},[45,1725,1726],{"class":139}," \"\"\"\n",[45,1728,1729],{"class":47,"line":61},[45,1730,1731],{"class":139},"Respond with a JSON object containing exactly 15 keys: title, subtitle,\n",[45,1733,1734],{"class":47,"line":67},[45,1735,1736],{"class":139},"introduction (minimum 200 words), background (minimum 300 words), methodology\n",[45,1738,1739],{"class":47,"line":73},[45,1740,1741],{"class":139},"(minimum 250 words), findings (minimum 400 words, must include at least 3\n",[45,1743,1744],{"class":47,"line":80},[45,1745,1746],{"class":139},"numbered sub-points each with its own citation), implications (minimum 200 words)...\n",[45,1748,1749],{"class":47,"line":86},[45,1750,1751],{"class":139},"[continues for 15 total sections with individually specified minimum lengths]\n",[45,1753,1754],{"class":47,"line":92},[45,1755,1756],{"class":139},"\"\"\"\n",[45,1758,1759],{"class":47,"line":97},[45,1760,77],{"emptyLinePlaceholder":76},[45,1762,1763],{"class":47,"line":103},[45,1764,1765],{"class":281},"# The format spec itself is so demanding that the actual CONTENT gets cut short\n",[45,1767,1768],{"class":47,"line":109},[45,1769,1770],{"class":281},"# to fit the mandated structure — especially with a tight max_tokens limit.\n",[45,1772,1773],{"class":47,"line":309},[45,1774,1775],{"class":281},"# The model spends its budget complying with the format, not producing substance.\n",[45,1777,1778],{"class":47,"line":315},[45,1779,77],{"emptyLinePlaceholder":76},[45,1781,1782],{"class":47,"line":320},[45,1783,1784],{"class":281},"# PRODUCTION: bound the format complexity to the output budget\n",[45,1786,1787,1790,1792],{"class":47,"line":332},[45,1788,1789],{"class":132},"GOOD_FORMAT_SPEC",[45,1791,326],{"class":240},[45,1793,1726],{"class":139},[45,1795,1796],{"class":47,"line":345},[45,1797,1798],{"class":139},"Respond with a JSON object: {\"summary\": string, \"key_points\": array of\n",[45,1800,1801],{"class":47,"line":354},[45,1802,1803],{"class":139},"strings (max 5), \"recommendation\": string}. Keep \"summary\" under 100 words.\n",[45,1805,1806],{"class":47,"line":374},[45,1807,1756],{"class":139},[45,1809,1810],{"class":47,"line":390},[45,1811,1812],{"class":281},"# Simple enough that the model can produce real content within the budget.\n",[14,1814,1816],{"id":1815},"tips-tricks","💡 Tips & Tricks",[32,1818,1820],{"filename":1819,"language":229},"tips.py",[37,1821,1823],{"className":232,"code":1822,"language":229,"meta":41,"style":41},"# [Performance] Prefer API-enforced structured output over prompted JSON\n# whenever available. The enforced-shape guarantee transfers better across\n# model swaps (Chapter 16) and eliminates the entire class of \"model added\n# prose around my JSON\" failures.\n\n# [Idiom] For human-readable structured output, Markdown is the right tool.\n# Specify exact heading level, exact labels, exact ordering:\n# \"## [Database name]\\n**Best for:** one sentence\\n**Watch out for:** one sentence\"\n\n# [Debug] If JSON parsing fails in production, log the RAW response text before\n# attempting to parse. \"The model returned invalid JSON\" is less useful than\n# \"the model returned 'Sure! Here's the JSON:\\n```json\\n{...}\\n```' — add\n# 'return only the JSON object, no code fences' to the prompt.\"\n\n# [Idiom] When mixing reasoning and structured output, separate them:\n# \u003Creasoning> ...free-form reasoning... \u003C\u002Freasoning>\n# \u003Canswer> ...JSON only... \u003C\u002Fanswer>\n# Then parse only the \u003Canswer> block. See Chapter 5 for the two-call pipeline\n# alternative, which cleanly avoids mixing the two concerns in one response.\n",[25,1824,1825,1830,1835,1840,1845,1849,1854,1859,1864,1868,1873,1878,1883,1888,1892,1897,1902,1907,1912],{"__ignoreMap":41},[45,1826,1827],{"class":47,"line":48},[45,1828,1829],{"class":281},"# [Performance] Prefer API-enforced structured output over prompted JSON\n",[45,1831,1832],{"class":47,"line":55},[45,1833,1834],{"class":281},"# whenever available. The enforced-shape guarantee transfers better across\n",[45,1836,1837],{"class":47,"line":61},[45,1838,1839],{"class":281},"# model swaps (Chapter 16) and eliminates the entire class of \"model added\n",[45,1841,1842],{"class":47,"line":67},[45,1843,1844],{"class":281},"# prose around my JSON\" failures.\n",[45,1846,1847],{"class":47,"line":73},[45,1848,77],{"emptyLinePlaceholder":76},[45,1850,1851],{"class":47,"line":80},[45,1852,1853],{"class":281},"# [Idiom] For human-readable structured output, Markdown is the right tool.\n",[45,1855,1856],{"class":47,"line":86},[45,1857,1858],{"class":281},"# Specify exact heading level, exact labels, exact ordering:\n",[45,1860,1861],{"class":47,"line":92},[45,1862,1863],{"class":281},"# \"## [Database name]\\n**Best for:** one sentence\\n**Watch out for:** one sentence\"\n",[45,1865,1866],{"class":47,"line":97},[45,1867,77],{"emptyLinePlaceholder":76},[45,1869,1870],{"class":47,"line":103},[45,1871,1872],{"class":281},"# [Debug] If JSON parsing fails in production, log the RAW response text before\n",[45,1874,1875],{"class":47,"line":109},[45,1876,1877],{"class":281},"# attempting to parse. \"The model returned invalid JSON\" is less useful than\n",[45,1879,1880],{"class":47,"line":309},[45,1881,1882],{"class":281},"# \"the model returned 'Sure! Here's the JSON:\\n```json\\n{...}\\n```' — add\n",[45,1884,1885],{"class":47,"line":315},[45,1886,1887],{"class":281},"# 'return only the JSON object, no code fences' to the prompt.\"\n",[45,1889,1890],{"class":47,"line":320},[45,1891,77],{"emptyLinePlaceholder":76},[45,1893,1894],{"class":47,"line":332},[45,1895,1896],{"class":281},"# [Idiom] When mixing reasoning and structured output, separate them:\n",[45,1898,1899],{"class":47,"line":345},[45,1900,1901],{"class":281},"# \u003Creasoning> ...free-form reasoning... \u003C\u002Freasoning>\n",[45,1903,1904],{"class":47,"line":354},[45,1905,1906],{"class":281},"# \u003Canswer> ...JSON only... \u003C\u002Fanswer>\n",[45,1908,1909],{"class":47,"line":374},[45,1910,1911],{"class":281},"# Then parse only the \u003Canswer> block. See Chapter 5 for the two-call pipeline\n",[45,1913,1914],{"class":47,"line":390},[45,1915,1916],{"class":281},"# alternative, which cleanly avoids mixing the two concerns in one response.\n",[14,1918,1920],{"id":1919},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[32,1922,1924],{"filename":1923,"language":229},"edge_cases.py",[37,1925,1927],{"className":232,"code":1926,"language":229,"meta":41,"style":41},"# [Gotcha] API-enforced JSON guarantees SYNTACTIC validity but not SEMANTIC\n# correctness. The JSON will parse, but the model can still put the wrong value\n# in a field — salary_min as a string of digits, or \"remote\": \"yes\" instead of\n# true if the schema allows strings. The schema enforces shape, not truth.\n\n# [Gotcha] \"Additional properties: false\" in a JSON schema prevents extra keys,\n# but doesn't prevent the model from putting the RIGHT key with the WRONG value.\n# Always validate semantic content, not just structural validity.\n\n# [Gotcha] Schema-constrained output can still truncate at max_tokens. A valid\n# JSON object that's cut off mid-generation is invalid JSON. Ensure max_tokens\n# is large enough for the LARGEST expected valid response, not just the average.\n\n# [Gotcha] JSON-mode guarantees differ across providers. Some enforce schema\n# validation; others only guarantee JSON syntax validity (valid JSON, but\n# any structure); others are best-effort. Check what \"structured output\" actually\n# means for YOUR provider before relying on it for production parsing.\n\n# [Safety] Function-calling \u002F tool-use output containing untrusted external content\n# is a direct injection vector (Chapter 18). A tool result that looks like a\n# function call instruction can manipulate the model — validate tool calls against\n# your registered tool list before execution.\n",[25,1928,1929,1934,1939,1944,1949,1953,1958,1963,1968,1972,1977,1982,1987,1991,1996,2001,2006,2011,2015,2020,2025,2030],{"__ignoreMap":41},[45,1930,1931],{"class":47,"line":48},[45,1932,1933],{"class":281},"# [Gotcha] API-enforced JSON guarantees SYNTACTIC validity but not SEMANTIC\n",[45,1935,1936],{"class":47,"line":55},[45,1937,1938],{"class":281},"# correctness. The JSON will parse, but the model can still put the wrong value\n",[45,1940,1941],{"class":47,"line":61},[45,1942,1943],{"class":281},"# in a field — salary_min as a string of digits, or \"remote\": \"yes\" instead of\n",[45,1945,1946],{"class":47,"line":67},[45,1947,1948],{"class":281},"# true if the schema allows strings. The schema enforces shape, not truth.\n",[45,1950,1951],{"class":47,"line":73},[45,1952,77],{"emptyLinePlaceholder":76},[45,1954,1955],{"class":47,"line":80},[45,1956,1957],{"class":281},"# [Gotcha] \"Additional properties: false\" in a JSON schema prevents extra keys,\n",[45,1959,1960],{"class":47,"line":86},[45,1961,1962],{"class":281},"# but doesn't prevent the model from putting the RIGHT key with the WRONG value.\n",[45,1964,1965],{"class":47,"line":92},[45,1966,1967],{"class":281},"# Always validate semantic content, not just structural validity.\n",[45,1969,1970],{"class":47,"line":97},[45,1971,77],{"emptyLinePlaceholder":76},[45,1973,1974],{"class":47,"line":103},[45,1975,1976],{"class":281},"# [Gotcha] Schema-constrained output can still truncate at max_tokens. A valid\n",[45,1978,1979],{"class":47,"line":109},[45,1980,1981],{"class":281},"# JSON object that's cut off mid-generation is invalid JSON. Ensure max_tokens\n",[45,1983,1984],{"class":47,"line":309},[45,1985,1986],{"class":281},"# is large enough for the LARGEST expected valid response, not just the average.\n",[45,1988,1989],{"class":47,"line":315},[45,1990,77],{"emptyLinePlaceholder":76},[45,1992,1993],{"class":47,"line":320},[45,1994,1995],{"class":281},"# [Gotcha] JSON-mode guarantees differ across providers. Some enforce schema\n",[45,1997,1998],{"class":47,"line":332},[45,1999,2000],{"class":281},"# validation; others only guarantee JSON syntax validity (valid JSON, but\n",[45,2002,2003],{"class":47,"line":345},[45,2004,2005],{"class":281},"# any structure); others are best-effort. Check what \"structured output\" actually\n",[45,2007,2008],{"class":47,"line":354},[45,2009,2010],{"class":281},"# means for YOUR provider before relying on it for production parsing.\n",[45,2012,2013],{"class":47,"line":374},[45,2014,77],{"emptyLinePlaceholder":76},[45,2016,2017],{"class":47,"line":390},[45,2018,2019],{"class":281},"# [Safety] Function-calling \u002F tool-use output containing untrusted external content\n",[45,2021,2022],{"class":47,"line":413},[45,2023,2024],{"class":281},"# is a direct injection vector (Chapter 18). A tool result that looks like a\n",[45,2026,2027],{"class":47,"line":433},[45,2028,2029],{"class":281},"# function call instruction can manipulate the model — validate tool calls against\n",[45,2031,2032],{"class":47,"line":450},[45,2033,2034],{"class":281},"# your registered tool list before execution.\n",[14,2036,2038],{"id":2037},"spot-the-bug","🧠 Spot the Bug",[19,2040,2041],{},"A team extracts structured data using prompted JSON (no API enforcement). The prompt says \"return a JSON object.\" In production, ~2% of responses fail to parse. Investigation shows the model sometimes wraps the JSON in a code fence or adds a one-line preamble like \"Here are the extracted fields:\". What's the fix?",[2043,2044,2045,2049,2056,2073],"details",{},[2046,2047,2048],"summary",{},"Answer",[19,2050,2051,2052,2055],{},"Prompted JSON has no hard guarantee — the model ",[23,2053,2054],{},"usually"," complies but can add prose, code fences, or conversational framing any time. The 2% failure rate is the exact failure mode API-enforced structured output exists to eliminate. Two fixes, in order of preference:",[2057,2058,2059,2067],"ol",{},[2060,2061,2062,2066],"li",{},[2063,2064,2065],"strong",{},"Use the provider's native structured-output feature"," (schema-constrained generation). The output is valid JSON by construction — the 2% failure rate drops to 0% because the API constrains which tokens can be generated.",[2060,2068,2069,2070,2072],{},"If API enforcement isn't available for your model, add an explicit suppression instruction (\"Return only the JSON object, no code fences, no preamble, no explanation. Begin with { and end with }.\") AND implement a robust parser with fallback extraction strategies (see ",[25,2071,713],{},").",[19,2074,2075],{},"The deeper lesson: \"most of the time\" is not good enough for unattended production parsing. Always prefer enforced guarantees over prompted requests when the feature is available.",[14,2077,2079],{"id":2078},"key-takeaways","Key Takeaways",[32,2081,2083],{"filename":2082,"language":229},"key_takeaways.py",[37,2084,2086],{"className":232,"code":2085,"language":229,"meta":41,"style":41},"\"\"\"\nOutput formatting & structured data — reliability for downstream code.\n\"\"\"\n\n# 1. The moment output is consumed by CODE, format is a correctness requirement,\n#    not a cosmetic preference. \"Almost valid JSON\" breaks your parser.\n\n# 2. Prompted JSON = \"most of the time.\" API-enforced JSON = \"always.\"\n#    Prefer API-level schema-constrained output for anything parsed programmatically.\n#    prompted: \"return a JSON object with these keys...\" → ~98% reliable\n#    enforced:  output_config={\"format\": {\"type\": \"json_schema\", ...}} → 100% valid\n\n# 3. Tool use \u002F function calling IS structured output, with semantic framing.\n#    Extraction: \"produce this data shape\" → schema-constrained output\n#    Tool use: \"decide whether\u002Fhow to invoke a capability\" → tool definition\n#    Same JSON schema; different purpose. Match the mechanism to the task.\n\n# 4. XML is better than JSON for mixed prose-and-structure, variable-length\n#    content, and nested sections needing no escaping. Tag boundaries are\n#    visually obvious even in malformed output, aiding partial recovery.\n\n# 5. Common failure modes: unwanted preamble (fix with explicit suppression +\n#    API enforcement), truncation from rigid format specs (fix by bounding\n#    format complexity to the output budget), and semantic-vs-syntactic errors\n#    (API enforces valid JSON; it doesn't enforce correct VALUES — validate both).\n",[25,2087,2088,2092,2097,2101,2105,2110,2115,2119,2124,2129,2134,2139,2143,2148,2153,2158,2163,2167,2172,2177,2182,2186,2191,2196,2201],{"__ignoreMap":41},[45,2089,2090],{"class":47,"line":48},[45,2091,1756],{"class":139},[45,2093,2094],{"class":47,"line":55},[45,2095,2096],{"class":139},"Output formatting & structured data — reliability for downstream code.\n",[45,2098,2099],{"class":47,"line":61},[45,2100,1756],{"class":139},[45,2102,2103],{"class":47,"line":67},[45,2104,77],{"emptyLinePlaceholder":76},[45,2106,2107],{"class":47,"line":73},[45,2108,2109],{"class":281},"# 1. The moment output is consumed by CODE, format is a correctness requirement,\n",[45,2111,2112],{"class":47,"line":80},[45,2113,2114],{"class":281},"#    not a cosmetic preference. \"Almost valid JSON\" breaks your parser.\n",[45,2116,2117],{"class":47,"line":86},[45,2118,77],{"emptyLinePlaceholder":76},[45,2120,2121],{"class":47,"line":92},[45,2122,2123],{"class":281},"# 2. Prompted JSON = \"most of the time.\" API-enforced JSON = \"always.\"\n",[45,2125,2126],{"class":47,"line":97},[45,2127,2128],{"class":281},"#    Prefer API-level schema-constrained output for anything parsed programmatically.\n",[45,2130,2131],{"class":47,"line":103},[45,2132,2133],{"class":281},"#    prompted: \"return a JSON object with these keys...\" → ~98% reliable\n",[45,2135,2136],{"class":47,"line":109},[45,2137,2138],{"class":281},"#    enforced:  output_config={\"format\": {\"type\": \"json_schema\", ...}} → 100% valid\n",[45,2140,2141],{"class":47,"line":309},[45,2142,77],{"emptyLinePlaceholder":76},[45,2144,2145],{"class":47,"line":315},[45,2146,2147],{"class":281},"# 3. Tool use \u002F function calling IS structured output, with semantic framing.\n",[45,2149,2150],{"class":47,"line":320},[45,2151,2152],{"class":281},"#    Extraction: \"produce this data shape\" → schema-constrained output\n",[45,2154,2155],{"class":47,"line":332},[45,2156,2157],{"class":281},"#    Tool use: \"decide whether\u002Fhow to invoke a capability\" → tool definition\n",[45,2159,2160],{"class":47,"line":345},[45,2161,2162],{"class":281},"#    Same JSON schema; different purpose. Match the mechanism to the task.\n",[45,2164,2165],{"class":47,"line":354},[45,2166,77],{"emptyLinePlaceholder":76},[45,2168,2169],{"class":47,"line":374},[45,2170,2171],{"class":281},"# 4. XML is better than JSON for mixed prose-and-structure, variable-length\n",[45,2173,2174],{"class":47,"line":390},[45,2175,2176],{"class":281},"#    content, and nested sections needing no escaping. Tag boundaries are\n",[45,2178,2179],{"class":47,"line":413},[45,2180,2181],{"class":281},"#    visually obvious even in malformed output, aiding partial recovery.\n",[45,2183,2184],{"class":47,"line":433},[45,2185,77],{"emptyLinePlaceholder":76},[45,2187,2188],{"class":47,"line":450},[45,2189,2190],{"class":281},"# 5. Common failure modes: unwanted preamble (fix with explicit suppression +\n",[45,2192,2193],{"class":47,"line":481},[45,2194,2195],{"class":281},"#    API enforcement), truncation from rigid format specs (fix by bounding\n",[45,2197,2198],{"class":47,"line":487},[45,2199,2200],{"class":281},"#    format complexity to the output budget), and semantic-vs-syntactic errors\n",[45,2202,2203],{"class":47,"line":526},[45,2204,2205],{"class":281},"#    (API enforces valid JSON; it doesn't enforce correct VALUES — validate both).\n",[2207,2208,2209],"style",{},"html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .svAP2, html code.shiki .svAP2{--shiki-default:#032F62;--shiki-github-dark:#DBEDFF}html pre.shiki code .snRuI, html code.shiki .snRuI{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-github-dark:#85E89D;--shiki-github-dark-font-weight:bold}html pre.shiki code .sMKGj, html code.shiki .sMKGj{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-github-dark:#FDAEB7;--shiki-github-dark-font-style:italic}",{"title":41,"searchDepth":55,"depth":55,"links":2211},[2212,2213,2214,2215,2216,2217,2221,2222,2223,2224],{"id":16,"depth":55,"text":17},{"id":29,"depth":55,"text":30},{"id":224,"depth":55,"text":225},{"id":1229,"depth":55,"text":1230},{"id":1326,"depth":55,"text":1327},{"id":1615,"depth":55,"text":1616,"children":2218},[2219,2220],{"id":1620,"depth":61,"text":1621},{"id":1702,"depth":61,"text":1703},{"id":1815,"depth":55,"text":1816},{"id":1919,"depth":55,"text":1920},{"id":2037,"depth":55,"text":2038},{"id":2078,"depth":55,"text":2079},"Reliable JSON, XML, and structured output for downstream automation — prompted vs. API-enforced schemas, function calling as structured output, common failure modes, and production parsing patterns. Code-first reference for mid-to-senior engineers.","md",{},"\u002Fprompt-engineering\u002F07-output-formatting-and-structured-data",{"title":5,"description":2225},"prompt-engineering\u002F07-output-formatting-and-structured-data","3QxuU-4-Wwg3ivnMjpJEhF4UsEfmGbmmodNp5kQl0LA",1789924650846]