Most developers struggle to get LLMs to return clean, parseable JSON. Here's the pattern that works every time in production.
Why LLMs Fight Structured Output
By default, LLMs are chat models. They produce conversational text. Asking them for JSON is asking them to behave like APIs — and they'll resist if you don't guide them properly.
The naive approach: "Return JSON" in your prompt. This works maybe 60% of the time. Not good enough.
The Pattern That Works: Constraint + Example + Validation Loop
Step 1: System prompt constraint
You are a JSON API. You respond ONLY with valid JSON matching this schema.
Never include explanation, markdown, or anything outside the JSON object.
Schema: {"field": "type", ...}
The key phrase: "respond ONLY with valid JSON." This primes the model to suppress its conversational instincts.
Step 2: Few-shot example
Show, don't tell:
// Request: "What's the weather in London?"
// Response:
{"city": "London", "temp_c": 14, "condition": "partly_cloudy", "source": "weather_api"}
The model sees the exact format it should produce.
Step 3: Use the JSON mode parameter
Every major LLM API has a response_format or equivalent. This is not a suggestion — it's a structural constraint at the model level.
For OpenAI: response_format={"type": "json_object"}
For Anthropic: response_format={"type": "json_schema", "json_schema": {...}}
Step 4: Validation + retry loop
Even with all the above, bad inputs produce malformed output. Always validate:
def get_structured(prompt, schema):
raw = llm_call(prompt)
try:
data = json.loads(raw)
validate(data, schema)
return data
except (json.JSONDecodeError, ValidationError):
return llm_call(prompt, strict=True)
Typically 2 iterations catches 95%+ of failures.
Real Results
After implementing this pattern across 12 production LLM integrations:
- JSON parse success rate: 60% to 97%
- Median response time: +200ms (retry loop)
- Production incidents from malformed output: eliminated
Building production LLM systems? These patterns come from real deployments, not tutorials.
Top comments (0)