Блог AI / Structured Output

Насколько хорош JSON у Claude Fable 5.1? Structured Output и план проверки API

Claude Fable 5.1 shipped on 2026-09-01. The API model name is claude-fable-5-1. The docs mention both output_config.format and strict: true on tools—both promise structured JSON, with different guarantees.

Claude Fable 5.1 makes Structured Output a GA Messages API feature: attach a JSON Schema and the final answer is constrained to those fields, types, and required keys. For API work the question is no longer “will it emit JSON?” but “how far does this contract hold on Claude vs OpenAI vs Gemini?”Structured Output is no longer a beta header.

This article covers:

Keep this in mind: Fable 5.1 is good enough for production contracts, but API-level conformance is not business correctness. Forced tools 400; minimum / minLength never reach the decoder. Below: specs → limits → vendor compare → test plan.

What is Claude Fable 5.1

Claude Fable 5.1 is Anthropic’s flagship for long-horizon agents, multistep research, and document/spreadsheet work. The model ID on the Claude API, Amazon Bedrock, Google Cloud, and Microsoft Foundry is claude-fable-5-1. Knowledge cutoff June 2026, 1M context, 128K max synchronous output.

Official guidance: start most workloads on Claude Opus 5. Reach for Fable 5.1 when Opus 5 at higher effort still fails evals, or the job is a long-running agent. Pricing matches that role: $10 / $50 per MTok—twice Opus 5.

Spec Claude Fable 5.1 Claude Opus 5 Claude Sonnet 5
Context / max output 1M / 128K 1M / 128K 1M / 128K
Price ($/MTok) $10 / $50 $5 / $25 $2 / $10
Thinking Adaptive, always on Adaptive Adaptive
Default effort high high high

Official docs: Claude Fable 5.1, Structured outputs.

JSON / Structured Output limits

Fable 5.1 exposes two complementary paths—use either, or both in one request:

Versus “please return JSON” in the prompt, the difference is decoding: the schema constrains tokens instead of hoping json.loads works after the fact.

Capability Fable 5.1 behavior What it means for APIs
Valid JSON Guaranteed on the JSON outputs path Fewer regex patches and second-pass LLM parses
Fields / types / required Conforms to the schema you send Safe to feed validators and business code
Numeric / length constraints Unsupported—request returns 400 Keep minimum and minLength in your app
Force a specific tool tool_choice any/tool returns 400 Use auto + strict, or switch to JSON outputs

For the shared Structured Output model see 2026 AI Structured Output explained. This piece only covers Claude-specific gaps and how to measure them.

How to use output_config.format

In 2026 the beta field output_format moved to output_config.format. New code should not send the structured-outputs-2025-11-13 header. Python SDK 1.0+ raises TypeError if you pass the old field to messages.create().

Claude Fable 5.1 — output_config.format
import anthropic

client = anthropic.Anthropic()
response = client.messages.create(
    model="claude-fable-5-1",
    max_tokens=1024,
    messages=[
        {
            "role": "user",
            "content": "Extract ticket fields from: billing failed for acct-9921, user wants a refund.",
        }
    ],
    output_config={
        "format": {
            "type": "json_schema",
            "schema": {
                "type": "object",
                "properties": {
                    "category": {
                        "type": "string",
                        "enum": ["billing", "technical", "account", "other"],
                    },
                    "priority": {
                        "type": "string",
                        "enum": ["low", "medium", "high"],
                    },
                    "account_id": {"type": "string"},
                    "summary": {"type": "string"},
                },
                "required": ["category", "priority", "account_id", "summary"],
                "additionalProperties": False,
            },
        }
    },
)
print(next(block.text for block in response.content if block.type == "text"))

Objects must set additionalProperties: false; list every property in required. Miss one and the request may 400, or extra keys will not appear.

With Pydantic, client.messages.parse() still accepts output_format as a convenience name, rewrites it to output_config.format, adds additionalProperties: false, and strips unsupported constraints.

Strict tool use: do not force tools

Thinking is always on for Fable 5.1. A forced tool call would skip it and dump reasoning into arguments. That is why tool_choice: {"type": "any"} and {"type": "tool"} return 400 invalid_request_error. For schema-valid tool args, keep auto and turn on strict.

Strict tool use + tool_choice auto
{
  "model": "claude-fable-5-1",
  "max_tokens": 1024,
  "tool_choice": { "type": "auto" },
  "tools": [
    {
      "name": "create_ticket",
      "description": "Create a support ticket from extracted fields.",
      "strict": true,
      "input_schema": {
        "type": "object",
        "properties": {
          "category": {
            "type": "string",
            "enum": ["billing", "technical", "account", "other"]
          },
          "priority": {
            "type": "string",
            "enum": ["low", "medium", "high"]
          },
          "summary": { "type": "string" }
        },
        "required": ["category", "priority", "summary"],
        "additionalProperties": false
      }
    }
  ],
  "messages": [
    { "role": "user", "content": "Use create_ticket for this refund request." }
  ]
}

To make the model call a tool, say so in the prompt—e.g. “Use create_ticket for this refund.” Schema also shows up as MCP inputSchema / outputSchema; see Why AI Agents need JSON Schema.

vs OpenAI / Gemini

The same business schema mounts on three different fields. For a multi-model router, keep one schema and three thin adapters.

Item Claude Fable 5.1 OpenAI Gemini
Request field output_config.format response_format.json_schema responseSchema
Type flag json_schema json_schema + strict application/json
Extra fields additionalProperties: false required Same requirement in Strict mode Recommended; errors are not identical
Tool contract tools[].strict + input_schema tools[].parameters / strict function calling + responseSchema

OpenAI-compatible APIs (e.g. DeepSeek V4-Pro) follow OpenAI field names—do not paste Claude’s output_config as-is.

API development test plan

“How good is the JSON?” is not one happy-path call. Use one schema and one case set against Claude / OpenAI / Gemini, then record four pass rates.

  1. 1
    Freeze a cross-vendor schema

    Stay in the shared subset: object, string, integer, enum, required, additionalProperties: false. Skip minimum and recursive $ref or you are testing vendor limits, not the model.

  2. 2
    Collect 4–8 real texts

    Cover enum edges, missing fields, mixed intent, and long descriptions. Label expected category / priority up front—do not relabel after the run.

  3. 3
    Score four conformance checks

    All required keys present, no extras, enum in the set, types match. All four must pass. Track json.loads failures separately—they should be near zero on Structured Output.

  4. 4
    Test the breaking changes alone

    Send tool_choice: any to Fable 5.1 and expect 400. Send a schema with minimum / minLength and expect 400. These are upgrade regressions.

  5. 5
    Measure latency and tokens, not just accuracy

    Default effort is high and thinking is always on, so the same schema is often slower and costlier than Sonnet 5 or Gemini Flash. Log P50 latency and output tokens before you put Fable on the hot path.

Minimal scoring helper
CASES = [
    "billing failed for acct-9921, user wants a refund",
    "cannot login after password reset, error 403",
    "please upgrade our workspace to Enterprise",
    "the app crashes when opening a 12MB JSON file",
]

EXPECTED_KEYS = {"category", "priority", "account_id", "summary"}

def score(payload: dict) -> dict:
    keys = set(payload)
    return {
        "has_required": EXPECTED_KEYS <= keys,
        "no_extra": keys <= EXPECTED_KEYS,
        "enum_ok": payload.get("category") in {"billing", "technical", "account", "other"},
        "types_ok": all(isinstance(payload.get(k), str) for k in EXPECTED_KEYS),
    }

Paste the three raw JSON payloads into JSONNote Diff—field drift shows up faster than in logs. If pass rate drops below 95%, check for unsupported keywords first, then prompt vs enum conflicts.

Schema limits: what the API rejects

JSON outputs and Strict tool use share one JSON Schema subset. Unsupported features 400 immediately; the model will not “try its best.”

Category Supported Not supported
Basics / numbers type / properties / required / enum / const minimum / maximum / multipleOf
String formats date-time / email / uri / uuid minLength / maxLength
Arrays minItems = 0 or 1 Larger minItems / maxItems
$ref Local $ref / $def External $ref, recursive schemas

Python / TypeScript SDKs strip unsupported constraints into descriptions. Do not rely on that silent rewrite—eval and prod should send the same trimmed schema.

Validation workflow: API to business layer

Three layers: API JSON outputs or Strict tool → business json.loads + JSON Schema validate → monitor violation rate. Fable 5.1 does not replace the last two: empty content, truncation, and enum casing still happen.

Python validation example
import json
from jsonschema import validate, ValidationError

SCHEMA = {
    "type": "object",
    "properties": {
        "category": {"type": "string", "enum": ["billing", "technical", "account", "other"]},
        "priority": {"type": "string", "enum": ["low", "medium", "high"]},
        "account_id": {"type": "string"},
        "summary": {"type": "string"},
    },
    "required": ["category", "priority", "account_id", "summary"],
    "additionalProperties": False,
}

def parse_llm_json(raw: str) -> dict:
    try:
        data = json.loads(raw)
    except json.JSONDecodeError as e:
        raise ValueError(f"Invalid JSON: {e}") from e
    try:
        validate(instance=data, schema=SCHEMA)
    except ValidationError as e:
        raise ValueError(f"Schema mismatch: {e.message}") from e
    return data

Use the same SCHEMA for the request and the check. During development, paste the raw model text and schema into JSONNote—faster than print() in a terminal.

Debug model JSON with JSONNote

The slow part of a cross-vendor eval is comparing three payloads. JSONNote runs locally in the browser; keys and samples never upload:

  1. 1
    Validate schema vs output

    Paste Fable / OpenAI / Gemini JSON plus one schema into JSON Schema to see missing fields, wrong types, and extra keys.

  2. 2
    Format the raw response

    Use JSON Formatter to confirm valid JSON and rule out log-escaping false positives.

  3. 3
    Diff the three calls

    Use JSON Diff to watch field names and enums drift—keep that as a regression baseline.

FAQ

Is Claude Fable 5.1 JSON output reliable?

On the output_config.format path, valid syntax and field conformance are API guarantees. Business meaning, numeric ranges, and string length are not—the decoder never sees those constraints.

Can I still use tool_choice: any?

No. Fable 5.1 / Mythos 5.1 return 400 for any and tool. Switch to auto + strict, or put the final shape on output_config.format. Name the tool in the prompt when it must run.

When to pick Fable vs OpenAI vs Gemini?

For long-horizon agents and hard extraction, Fable 5.1’s schema constraints are enough—but it is slow and expensive. Routine CRUD structured output should start on Sonnet 5 or Gemini. Multi-model routers should share the common schema subset.

Do I still validate after Structured Output?

Yes. API constraints do not replace try/except + validate. Empty bodies, truncated streams, and enum casing can still slip through.

Does the old output_format field still work?

REST accepts it for a transition window, but Python SDK 1.0+ messages.create() raises TypeError. New code should use output_config.format; the parse() helper may still take the convenience name output_format.

Итог

Claude Fable 5.1 JSON output in three lines:

Structured Output is GA; forced tools are gone; unsupported schema keywords 400.

Do not stop at one happy-path demo. Freeze a shared schema, score conformance, encode the breaking changes as regressions, and diff the three vendor payloads in JSONNote—closer to production than another “please return JSON” prompt.

← Назад к блогу