← Home ← Codex ← DEBT ← Engine
Browse by Category
+ added · updated 7d
← Back to glossary

Structured Output from LLMs (JSON Mode)

AI / ML PHP 8.0+ Intermediate
debt(d7/e3/b3/t7)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). The detection_hints.tools list is empty, and there are no standard linters or SAST tools that catch missing schema validation after json_decode(). The bug — assuming valid JSON means correct structure — is silent at parse time and only surfaces when downstream code tries to access an absent key or wrong-typed value, typically discovered during testing or in production when an LLM response varies.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix says to use tool/function calling instead of JSON mode when possible, and common_mistakes point to adding isset() checks, a DTO, and retry logic. These changes are contained within the LLM integration layer — one component — but involve a few coordinated additions (schema validation, retry wrapper, DTO mapping) rather than a single one-line patch.

b3 Burden Structural debt — long-term weight of choosing wrong

Closest to 'localised tax' (b3). The choice applies to web and cli contexts but only within LLM integration code. The structural overhead — validation, retry logic, schema enforcement — is paid by the component that calls the LLM; the rest of the codebase is largely unaffected. It does impose an ongoing maintenance cost as LLM APIs evolve, but it doesn't shape the broader architecture.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The misconception field explicitly states the canonical wrong belief: 'JSON mode guarantees the exact structure you need.' Developers familiar with schema-enforcing formats (XML Schema, OpenAPI, database constraints) naturally expect 'structured output mode' to mean structural correctness, but it only guarantees syntactic JSON validity. This contradicts how analogous validation concepts work in other parts of the stack, making it a serious and well-documented gotcha that misleads competent developers on first encounter.

About DEBT scoring →

Also Known As

JSON mode structured output LLM function calling tool use JSON LLM JSON response

TL;DR

Instructing an LLM to return valid JSON rather than prose — either via a system prompt schema, a JSON mode API flag, or a tool-use response format — so the output can be reliably parsed and used programmatically.

Explanation

Raw LLM text output is unreliable for programmatic consumption — the model may add prose around the JSON, use single quotes, or include trailing commas. Modern LLM APIs address this differently: Anthropic Claude supports tool use where the tool parameters enforce JSON schema; OpenAI has response_format: {type: 'json_object'} and structured outputs with JSON schema enforcement. For PHP applications, the most reliable approach is either tool/function calling (the model fills in typed parameters) or including a JSON schema example in the system prompt combined with JSON_THROW_ON_ERROR parsing with a retry on failure. Always validate the returned structure against your expected schema — even JSON mode does not guarantee your specific fields are present.

Common Misconception

JSON mode guarantees the exact structure you need. JSON mode guarantees valid JSON syntax — it does not guarantee your specific keys are present or that values have the right types. Always validate with a schema check after decoding.

Why It Matters

PHP applications that use LLMs for classification, extraction, or data transformation need reliable structured output. Parsing free-form prose with regex is brittle. JSON mode or tool use gives you a machine-readable response you can json_decode() directly, dramatically simplifying the integration code.

Common Mistakes

  • Not validating the returned structure — json_decode() succeeding does not mean your expected keys are present; always check isset() or use a DTO.
  • Using JSON mode for creative or open-ended tasks — JSON mode constrains the model significantly; use it only for extraction and classification tasks.
  • Not implementing retry logic — LLMs occasionally produce invalid JSON even in JSON mode; catch JsonException and retry once with a stronger prompt.
  • Nesting too deep — deeply nested JSON schemas increase hallucination risk; flatten your schema to 1–2 levels when possible.

Code Examples

✗ Vulnerable
<?php
// ❌ Asking for JSON in prose — fragile, often fails
$response = $client->messages->create([
    'model'    => 'claude-sonnet-4-20250514',
    'messages' => [[
        'role'    => 'user',
        'content' => 'Extract name and email from this text: ' . $text
                   . ' Return as JSON.'
    ]]
]);

// Model might return: "Here's the JSON: {"name": ...}" — breaks json_decode()
$data = json_decode($response->content[0]->text, true);
// $data might be null if response includes prose
✓ Fixed
<?php
// ✅ Tool use — schema enforced at API level
$response = $client->messages->create([
    'model' => 'claude-sonnet-4-20250514',
    'tools' => [[
        'name'        => 'extract_contact',
        'description' => 'Extract contact information from text',
        'input_schema' => [
            'type'       => 'object',
            'properties' => [
                'name'  => ['type' => 'string'],
                'email' => ['type' => 'string', 'format' => 'email'],
            ],
            'required' => ['name', 'email'],
        ],
    ]],
    'tool_choice' => ['type' => 'tool', 'name' => 'extract_contact'],
    'messages'    => [['role' => 'user', 'content' => $text]],
]);

$toolUse = collect($response->content)->firstWhere('type', 'tool_use');
$data    = $toolUse->input; // Already a PHP array, schema-validated

// ✅ System prompt approach as fallback
$system = 'Respond ONLY with valid JSON matching this schema: '
        . '{"name": string, "email": string}. No prose, no markdown.';
// Then validate after: if (!isset($data['name'], $data['email'])) retry();

Added 23 Mar 2026
Edited 5 Apr 2026
Views 116
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 2 pings F 1 ping S 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 2 pings F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S 2 pings S 0 pings M
No pings yet today
Amazonbot 1 SEMrush 1
Amazonbot 10 ChatGPT 8 Google 8 PetalBot 8 Ahrefs 7 SEMrush 7 Scrapy 5 Perplexity 3 Bing 3 Applebot 3 Meta AI 2 Majestic 2 Claude 1 Twitter/X 1 Brave Search 1 Baidu 1
crawler 67 crawler_json 3
DEV INTEL Tools & Severity
⚙ Fix effort: Medium
⚡ Quick Fix
Use tool/function calling instead of JSON mode when possible — tool parameters enforce a schema at the API level, not just syntactically valid JSON.
📦 Applies To
PHP 8.0+ web cli


✓ schema.org compliant