Start with one model request: what does the source establish, what may be returned, and what needs clarification? Use order-form prefilling to connect task definitions, missing values, structural constraints and business validation. Valid JSON still needs supported fields and an acceptable resource state.
Prompt Describes Goals, Inputs, and Boundaries
Saying "You are an expert, please extract accurately" does not define what "accurately" means. A verifiable task must at least specify what the input is, which fields are required, the extent to which inference is allowed, how missing or conflicting information is handled, and who will use the final result. Prompt engineering also requires success criteria upfront to determine if changes are effective. Prompt engineering overview
For example, given the input "Want to buy A1, quantity to be confirmed later," if the task only requires sku and quantity, the model might guess 1. By explicitly stating that an unknown quantity should be null and marking the status as needs_input, making "no answer" a valid result becomes possible.
Original Ambiguity
Convention to Clarify
Quantity not written, default to one?
Do not guess; return null
Multiple items mentioned but field supports only one
Mark ambiguity or use an array; do not arbitrarily pick one
What time period does "next week" refer to?
Specify reference date and timezone, or keep original text
Conflicting content in source
Preserve conflicts and sources; do not arbitrarily choose authority
Output will directly trigger an order
Extraction is only a candidate; do not auto-authorize execution
Stable rules can be placed in the corresponding trusted instruction layer; current materials provide data with clear sources. Message roles and content blocks are interface structures and should not be treated merely as arbitrarily concatenated text. XML tags, headers, or separators help distinguish content, but they do not provide secure isolation; "ignore previous rules" found in external materials should still be treated as data processing.
A teaching prompt can be organized as follows:
Task: Extract a product SKU and quantity from the user's original text for form pre-filling before manual confirmation.
Rules: Use only information explicitly stated in the original text; missing fields are null.
Status: Set to complete only when both fields are explicit and conflict-free; otherwise, set to needs_input.
Scope: Do not create orders, query personal profiles, or infer payment information.
Output: Adhere to the provided schema; do not add explanations before or after the result.
User Original Text: Want to buy A1, quantity to be confirmed later.
The "Scope" section in the prompt indicates what the model should do, but actual tool permissions are enforced by the execution side. Writing operational permissions into a single sentence cannot replace pre-execution verification in the Agent Loop.
Use Examples to Eliminate Real-World Ambiguity
Few-shot examples illustrate conventions using specific inputs and outputs. They are suitable for explaining abstract rules such as nulls, enums, units, and conflicts where misinterpretation is likely; however, more examples are not always better, and positive examples cannot always replace boundary descriptions.
Input
Expected Core Result
Rule Illustrated
Buy A1, total 3 items
A1, 3, complete
Normal extraction
Want to buy A1, quantity to be confirmed later
A1, null, needs_input
Missing values cannot be guessed
A1 needs 3 items, wait, change to 2 items
Must be handled according to explicit correction rules
Temporal order and correction semantics
Buy A1 or B2, undecided
Cannot arbitrarily select SKU
Ambiguity needs to be expressed
If the schema does not allow space for ambiguity, even the best examples cannot prevent forced value filling; adjust the data model first, then adjust the wording. Do not write all test answers from development into examples and then claim that improvements on the same batch of questions represent generalization ability.
When diverse scenarios are needed, explicitly require that each scenario differs in constraints or trade-offs; when short outputs are needed, directly specify length and fields. temperature, effort, and length requirements address different issues and cannot be formulated as a migration formula like temperature=0 → effort=low. See Tokens and Sampling and Reasoning and Thinking for specific mechanisms.
Scope of Structural Constraints
Simply requesting JSON in the prompt asks the model to follow textual conventions; constrained decoding restricts valid output continuations during generation, ensuring the complete successful output conforms to the supported schema. Client-side validation is a check performed after generation; it can supplement constraints not supported by the service but cannot retroactively change already generated tokens.
Mechanism
Constrained Object
Application Checks Still Required
JSON format requirement or schema
Parseable JSON syntax, capabilities vary by interface
Fields, types, semantics
Schema-constrained response
Specified structure of final output
Complete response, evidence, and business conditions
Strict tool parameters
Allowed structure of tool selection and parameters
Permissions, resource state, action semantics
Client-side type/business validation
Received object
State changes during subsequent execution
Claude's output_config.format is used for JSON responses, while strict: true on tools is used for strict tool calls; the two can be combined, but the available schema subsets and functional compatibility ranges need to be verified against the target model and SDK. Structured outputs
Several Boundaries of JSON Schema Itself
properties describes fields but does not imply these fields must appear; required fields are declared by required. additionalProperties: false rejects undefined fields but does not automatically make defined fields required. null is a value, distinct from the non-existence of a field. JSON Schema object
For instance, if you want the quantity field to always appear but be null when unknown, you need both required and a nullable type. Defining it only as an integer and setting it as required would make it impossible to express "no quantity in the original text" as expected.
Vendor structured outputs may only support a subset of JSON Schema. Some SDKs convert the full schema into a simplified form supported by the service, then verify against the original constraints on the client side; this is different from directly sending an original JSON request with unsupported fields. Do not interpret "SDK accepts it" as "the server enforced all constraints during generation." SDK schema conversion notes
How far is valid JSON from usable output?
Changing quantity from 3 to 5 preserves JSON and types but loses source support; keeping 3 while reducing stock fails a business condition. Separate predicates show whether to fix the prompt, data model, evidence check or execution precondition.
Preparing the visual
How far is valid JSON from usable output?
Syntax, schema, source support and business conditions are distinct checks; passing one does not guarantee the next.
{"id":"ai-output-contract","title":"How far is valid JSON from usable output?","summary":"Syntax, schema, source support and business conditions are distinct checks; passing one does not guarantee the next.","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"presets\" role=\"group\" data-i18n-label=\"mode\"><button data-mode=\"valid\" data-i18n=\"valid\"></button><button data-mode=\"invent\" data-i18n=\"invent\"></button><button data-mode=\"missing\" data-i18n=\"missing\"></button><button data-mode=\"truncated\" data-i18n=\"truncated\"></button></div><div class=\"control\"><label for=\"stock\" data-i18n=\"stock\"></label><output for=\"stock\" id=\"stock-value\"></output><input id=\"stock\" type=\"range\" min=\"0\" max=\"8\" value=\"2\" step=\"1\"></div><section class=\"panel\"><h3 data-i18n=\"json\"></h3><div id=\"json\"></div></section><section class=\"panel\"><h3 data-i18n=\"layers\"></h3><div id=\"layers\"></div></section><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"explain\"></p></div><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control output{color:var(--accent);text-align:right;font-variant-numeric:tabular-nums;min-width:4em}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1;min-width:0;overflow-wrap:anywhere;font-size:13px;min-height:44px}.actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin:16px 0}.actions button{min-width:0;min-height:44px;font-size:13px;overflow-wrap:anywhere}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0;overflow-wrap:anywhere}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;margin:18px 0}.metrics>div{border-left:2px solid var(--accent);padding:0 6px 0 10px;min-width:0}.metrics span{display:block;min-height:3.6em;color:var(--muted);font-size:12px;overflow-wrap:anywhere}.metrics strong{font-size:21px;display:block;min-height:2em;font-weight:550;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.check{display:flex;align-items:center;gap:9px;font-size:13px;margin:14px 0}.check input{width:18px;height:18px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:14px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:44px}.panel{padding:14px;border:1px solid var(--rule);border-radius:9px;margin:16px 0;min-width:0}.panel>header{font-size:13px;font-weight:600;margin-bottom:12px;color:var(--muted)}.panel>div{overflow-wrap:anywhere}.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:8px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:56px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:6px 3px;font-size:12px;overflow-wrap:anywhere}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.table{display:grid;gap:5px;font-size:12px}.tr{display:grid;gap:5px}.tr>div{background:var(--surface);border-radius:4px;padding:9px 6px;min-width:0;min-height:5em;overflow-wrap:anywhere;display:flex;align-items:center}.tr>.gold{background:var(--second-soft)}.tr>.on{background:var(--accent-soft)}.formula,.source{font:13px/1.8 ui-monospace,monospace;white-space:pre-wrap;overflow-wrap:anywhere;margin:16px 0}.formula{border-top:1px solid var(--rule);padding-top:12px;min-height:7.2em}.source{min-height:8em;background:var(--surface);padding:12px;border-radius:8px}.intervals{margin:16px 0}.interval{position:relative;height:38px;background:var(--surface);margin:6px 0;border-radius:4px;overflow:hidden}.interval i{position:absolute;height:100%;background:var(--accent-soft);border-left:2px solid var(--accent)}.interval i.gold{background:var(--second-soft);border-color:var(--second)}.interval span{position:relative;z-index:1;font:12px/38px ui-monospace,monospace;padding-left:7px}.bars{display:grid;gap:10px;margin:16px 0}.bar-row{display:grid;grid-template-columns:44px minmax(0,1fr) 62px;gap:8px;align-items:center;font:12px ui-monospace,monospace}.bar-track{height:24px;background:var(--surface);border-radius:4px;overflow:hidden}.bar-track i{display:block;height:100%;background:var(--accent);transition:width .2s}.bar-track i.gold{background:var(--second)}.bar-track i.empty{opacity:.2}.bar-row output{text-align:right}.diagram{display:block;width:100%;height:auto;margin:18px 0}.diagram text{font:13px ui-monospace,monospace;fill:var(--ink)}.node{fill:var(--surface);stroke:var(--rule)}.node.on{fill:var(--accent-soft);stroke:var(--accent)}.node.gold{fill:var(--second-soft);stroke:var(--second)}.edge{stroke:var(--rule);stroke-width:2;fill:none}.edge.on{stroke:var(--accent)}.matrix{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px;margin:16px 0}.matrix button{min-width:0;min-height:44px;font:13px ui-monospace,monospace}.matrix button[aria-pressed=true]{background:var(--accent-soft);border-color:var(--accent)}.matrix button.masked{opacity:.35}.legend{font-size:12px;color:var(--muted);min-height:3.6em;overflow-wrap:anywhere}.numeric{font-variant-numeric:tabular-nums}.scope p{line-break:strict}@media(max-width:480px){.presets button,.actions button{font-size:12px}.panel{padding:12px}.metrics{gap:10px}.metrics strong{font-size:19px}.bar-row{grid-template-columns:34px minmax(0,1fr) 58px;gap:6px}}@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n\n.panel>h3{font-size:13px;font-weight:600;margin:0 0 12px;color:var(--muted)}\n\n.bar-row{grid-template-columns:44px minmax(0,1fr) 82px}.bar-row output{white-space:nowrap}@media(max-width:480px){.bar-row{grid-template-columns:34px minmax(0,1fr) 76px}}\n\n#json{min-height:7.2em;font:13px/1.8 ui-monospace,monospace;white-space:pre-wrap}.table .tr>div{min-height:4em}","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}const esc=s=>String(s).replace(/[&<>\"']/g,c=>({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));const fmt=s=>'{'+[...s].sort().join(', ')+'}';function graph(id,nodes,edges){const el=$('#'+id);el.setAttribute('viewBox','0 0 360 200');el.innerHTML='<defs><marker id=\"arrow-'+id+'\" viewBox=\"0 0 10 10\" refX=\"8\" refY=\"5\" markerWidth=\"5\" markerHeight=\"5\" orient=\"auto-start-reverse\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"var(--muted)\"/></marker></defs>'+edges.map(e=>{const a=nodes[e[0]],b=nodes[e[1]],dx=b.x-a.x,dy=b.y-a.y,d=Math.hypot(dx,dy)||1;if(edges.some(r=>r[0]===e[1]&&r[1]===e[0])){const nx=-dy/d,ny=dx/d;return '<path class=\"edge '+(e[2]?'on':'')+'\" d=\"M '+(a.x+dx/d*22+nx*12)+' '+(a.y+dy/d*22+ny*12)+' Q '+((a.x+b.x)/2+nx*38)+' '+((a.y+b.y)/2+ny*38)+' '+(b.x-dx/d*25+nx*12)+' '+(b.y-dy/d*25+ny*12)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}return '<line class=\"edge '+(e[2]?'on':'')+'\" x1=\"'+(a.x+dx/d*25)+'\" y1=\"'+(a.y+dy/d*25)+'\" x2=\"'+(b.x-dx/d*28)+'\" y2=\"'+(b.y-dy/d*28)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}).join('')+nodes.map(n=>'<g><circle class=\"node '+(n.cls||'')+'\" cx=\"'+n.x+'\" cy=\"'+n.y+'\" r=\"25\"/><text x=\"'+n.x+'\" y=\"'+(n.y+4)+'\" text-anchor=\"middle\">'+esc(n.name)+'</text>'+(n.sub?'<text x=\"'+n.x+'\" y=\"'+(n.y+43)+'\" text-anchor=\"middle\">'+esc(n.sub)+'</text>':'')+'</g>').join('');}function table(id,rows){$('#'+id).classList.add('table');$('#'+id).replaceChildren(...rows.map(r=>{const row=document.createElement('div');row.className='tr';row.style.gridTemplateColumns='repeat('+r.length+',minmax(0,1fr))';r.forEach(x=>{const c=document.createElement('div');if(typeof x==='object'){c.textContent=x.text;c.className=x.cls||'';}else c.textContent=x;row.append(c)});return row;}));}function band(id,parts,total){$('#'+id).replaceChildren(...parts.map((p,i)=>{const e=document.createElement('i');e.style.width=(100*p.value/total)+'%';e.className=i%2?'gold':'on';e.title=p.name+': '+p.value;return e}));}function intervals(id,items,total){$('#'+id).replaceChildren(...items.map(p=>{const row=document.createElement('div');row.className='interval';const bar=document.createElement('i');bar.style.left=(100*p.start/total)+'%';bar.style.width=(100*(p.end-p.start)/total)+'%';bar.className=p.cls||'';const label=document.createElement('span');label.textContent=p.label;row.append(bar,label);return row;}));}function plot(id,series,xr,yr,opts={}){const e=$('#'+id),X=x=>42+(x-xr[0])/(xr[1]-xr[0])*298,Y=y=>175-(y-yr[0])/(yr[1]-yr[0])*148;let out='<defs><clipPath id=\"clip-'+id+'\"><rect x=\"42\" y=\"27\" width=\"298\" height=\"148\"/></clipPath></defs>';for(let i=0;i<3;i++){let x=xr[0]+i*(xr[1]-xr[0])/2,y=yr[0]+i*(yr[1]-yr[0])/2;out+='<path class=\"gridline\" d=\"M '+X(x)+' 27V175M42 '+Y(y)+'H340\"/><text x=\"'+X(x)+'\" y=\"194\" text-anchor=\"middle\">'+esc(opts.xfmt?opts.xfmt(x):Number(x.toFixed(2)))+'</text><text x=\"36\" y=\"'+(Y(y)+4)+'\" text-anchor=\"end\">'+esc(opts.yfmt?opts.yfmt(y):Number(y.toFixed(2)))+'</text>';}out+='<g clip-path=\"url(#clip-'+id+')\">';for(const s of series){out+='<path fill=\"none\" stroke=\"'+(s.color||'var(--accent)')+'\" stroke-width=\"2.5\" '+(s.dash?'stroke-dasharray=\"5 4\"':'')+' d=\"'+s.pts.map((p,i)=>(i?'L':'M')+X(p[0]).toFixed(2)+','+Y(p[1]).toFixed(2)).join(' ')+'\"/>';}for(const p of opts.points||[])out+='<circle cx=\"'+X(p[0])+'\" cy=\"'+Y(p[1])+'\" r=\"4\" fill=\"var(--second)\" stroke=\"var(--paper)\" stroke-width=\"1.5\"/>';if(opts.cursor!==undefined)out+='<path d=\"M'+X(opts.cursor)+' 27V175\" stroke=\"var(--muted)\" stroke-dasharray=\"3 3\"/>';e.innerHTML=out+'</g>';e.dataset.curves=JSON.stringify(series.map(s=>s.pts));}const curve=(fn,lo,hi,n=120)=>Array.from({length:n+1},(_,i)=>{const x=lo+(hi-lo)*i/n;return[x,fn(x)]});function inputs(draw){document.querySelectorAll('input,select').forEach(e=>{e.addEventListener('input',draw);e.addEventListener('change',draw)});draw()}const n=id=>+$('#'+id).value;const show=(id,v,d=3)=>set(id,Number(v.toFixed(d)));function bars(id,values,max=1){$('#'+id).className='bars';$('#'+id).innerHTML=values.map(v=>'<div class=\"bar-row\"><span>'+esc(v.label)+'</span><div class=\"bar-track\"><i class=\"'+(v.cls||'')+'\" style=\"width:'+Math.max(0,Math.min(100,v.value/max*100))+'%\"></i></div><output>'+esc(v.text??(v.value*100).toFixed(1)+'%')+'</output></div>').join('')}function softmax(z,T=1){let max=Math.max(...z),w=z.map(x=>Math.exp((x-max)/T)),sum=w.reduce((a,b)=>a+b,0);return w.map(x=>x/sum)}let mode='valid';function draw(){pressed(mode);let src={valid:'{\"sku\":\"A1\",\"quantity\":3}',invent:'{\"sku\":\"A1\",\"quantity\":5}',missing:'{\"sku\":\"A1\"}',truncated:'{\"sku\":\"A1\",\"quantity\":'}[mode],parsed;try{parsed=JSON.parse(src)}catch{}let syntax=!!parsed,schema=syntax&&parsed.sku==='A1'&&Number.isInteger(parsed.quantity),source=schema&&parsed.quantity===3,business=schema&&parsed.quantity<=n('stock');set('stock-value',n('stock'));set('json',src);table('layers',[[t('syntax'),syntax?'✓':'×'],[t('schema'),syntax?(schema?'✓':'×'):'—'],[t('source'),schema?(source?'✓':'×'):'—'],[t('business'),schema?(business?'✓':'×'):'—']]);set('explanation',t('explain'));document.body.dataset.accepted=source&&business;}document.querySelectorAll('[data-mode]').forEach(e=>e.onclick=()=>{mode=e.dataset.mode;draw()});inputs(draw);","audio":false,"strings":{"business":"Stock sufficient","explain":"These checks need source agreement and sufficient stock. Even passing every layer neither executes nor authorizes an order.","heading":"How far is valid JSON from usable output?","intro":"Choose an output candidate and change stock; inspect each validation layer.","invent":"Invents 5","json":"Candidate JSON","layers":"Validation layers","missing":"Missing quantity","mode":"Output candidate","reset":"Reset","schema":"Schema valid","scope":"Source fixed to “Buy 3 of A1”; teaching schema requires sku and quantity, stock varies. No model or order is called.","scopeLabel":"Model scope and assumptions","source":"Source supports it","stock":"Current stock","syntax":"Parseable","truncated":"Truncated JSON","valid":"Matches source"}}
Design Outputs for Non-Success Paths
Structured output guarantees are limited to paths supported by the interface and where the response is complete. Truncation, refusal, transmission errors, and cancellations need independent handling; you cannot simply take the first text block and write it to the database.
Situation
Application Handling
Normal completion
Parse, validate schema, then perform business verification
Output truncation
Mark as incomplete, preserve reason; do not execute half-formed parameters
Missing information
Accept unknown states in the schema, continue reading or asking based on the task
Source conflicts
Return locatable conflicts; do not automatically fabricate consistent conclusions
Model refusal
Read refusal status via the interface; do not disguise it as normal business data
Network drop or timeout
Distinguish between incomplete generation and unknown external operation results
The client can retry with limits or request correction of specific fields, but each retry must have an upper limit and be counted towards costs. If failure stems from missing information, repeating the same input will not magically fill in facts; if tool side effects were previously executed, the entire task cannot be replayed just because parsing failed.
Preserve sources for structured reports by carrying document IDs, evidence locations, and versions in your own schema, which the application then verifies. Whether vendor-built citations can be used simultaneously with certain structured outputs is a specific functional compatibility issue, not equivalent to "JSON and traceability cannot coexist." See RAG for retrieval evidence integrity.
A Complete Local Verification Example
The following schema is used for complete verification on the application side and does not promise that the target service supports all keywords in it. The example relies on Python's jsonschema package to verify normal results, missing results, extra fields, wrong types, and cross-field contradictions; it does not call the model or create orders.
fromjsonschemaimportDraft202012Validator,ValidationErrorschema={"type":"object","properties":{"sku":{"type":["string","null"],"minLength":1},"quantity":{"type":["integer","null"],"minimum":1},"status":{"type":"string","enum":["complete","needs_input"]},},"required":["sku","quantity","status"],"additionalProperties":False,}Draft202012Validator.check_schema(schema)validator=Draft202012Validator(schema)defcheck(data):validator.validate(data)fields_present=data["sku"]isnotNoneanddata["quantity"]isnotNoneif(data["status"]=="complete")!=fields_present:raiseValueError("Status inconsistent with field completeness")returndataassertcheck({"sku":"A1","quantity":3,"status":"complete"})["quantity"]==3assertcheck({"sku":"A1","quantity":None,"status":"needs_input"})["quantity"]isNonebad=[{"sku":"A1","quantity":True,"status":"complete"},{"sku":"A1","quantity":0,"status":"complete"},{"sku":"A1","quantity":None,"status":"complete"},{"sku":"A1","status":"needs_input"},{"sku":"A1","quantity":3,"status":"complete","extra":1},]foriteminbad:try:check(item)except (ValidationError,ValueError):passelse:raiseAssertionError(f"Invalid result accepted: {item}")print("Normal/missing results passed, 5 types of invalid results rejected")
This simplified data model only expresses missing values, not multiple items or conflicts. If real tasks require these states, corresponding fields and rules should be added, and fields_present should not continue to be used as the completeness criterion. The example also does not verify if the SKU exists, if the quantity comes from the original text, or if inventory is sufficient; these belong to source and business validation.
When executing actions, real-time status must be verified again. For example, if inventory is sufficient during extraction but sold out during submission, the schema cannot solve this race condition. Parameter validation, business constraints, and transaction processing must be completed in the corresponding systems.
Version Control Prompts and Schemas Together
Record versions of prompts, schemas, models, SDKs, and verification logic. Changing fields from nullable to required, adding new values to enums, or replacing single objects with arrays can all change downstream behavior; do not just look at model outputs, but also verify how old consumers handle new version results.
Evaluation sets should cover normal inputs, missing values, conflicts, noise, long inputs, malicious materials, and truncation. Statistically report structural validity rate, field accuracy, correctness of unknown handling, and true task success rate. If JSON is all valid but facts are wrong, adding more structural constraints may not be effective; return to evidence, prompts, and model capability positioning.