Все статьи

Структурированный вывод LLM API: JSON Mode, JSON Schema и реальная поддержка у каждого провайдера (2026)

Практическое руководство по structured output (response_format) для OpenAI, Anthropic, DashScope, DeepSeek и SiliconFlow. Разбираем json_object, json_schema strict mode, output_config у Anthropic, взаимодействие со streaming, ограничения глубины schema и проблемы совместимости при маршрутизации запросов между провайдерами.

· TheRouter

Получить валидный JSON от LLM — звучит просто. Пока не попробуешь сделать это через пять провайдеров. Один предлагает strict schema enforcement на уровне токенов. Другой поддерживает json_object, но не json_schema. Третий использует совершенно другое имя параметра. А когда тот же запрос проходит через gateway, параметр structured output может быть, а может и не быть корректно транслирован.

Мы маршрутизируем structured-output запросы через OpenAI, Anthropic, DashScope, DeepSeek и SiliconFlow каждый день. В этом руководстве задокументировано, что именно поддерживает каждый провайдер, где возникают несовместимости и как построить production pipeline, который получает надёжный JSON независимо от того, какой провайдер обрабатывает запрос.

OpenAI-совместимость означает, что провайдер предоставляет endpoint chat-completions, чей контракт запроса и ответа достаточно близок к API OpenAI, чтобы немодифицированный вызов OpenAI SDK работал после замены трёх значений: API key, base URL, название модели. Минимальная поверхность на практике —POST /v1/chat/completions с messages, model и потоковым ответом в форме OpenAI.

Сводная таблица

Провайдерjson_objectjson_schema (strict)Нативный параметрНужен "json" в promptStreaming + structured
OpenAIДаДа (constrained decoding)response_formatТолько json_objectДа
AnthropicНет (используйте output_config)Да (grammar-based)output_config.formatНетДа
DashScopeДаНет (только json_object)response_formatДаДа
DeepSeekДаНет (только json_object)response_formatДаДа
SiliconFlowДа (зависит от модели)Нетresponse_formatДа (рекомендуется)Да

Главный вывод: только OpenAI и Anthropic предлагают гарантированный schema structured output через constrained decoding. DashScope, DeepSeek и SiliconFlow поддерживают json_object — валидность JSON-синтаксиса гарантирована, но schema (имена полей, типы, вложенность) не контролируется на уровне token-генерации.

Как это работает под капотом

JSON Object mode (type: "json_object") сообщает модели, что нужно выдать валидный JSON. Провайдер ограничивает генерацию токенов — скобки закрываются правильно, строки в кавычках и т.д. Но модель может вернуть любую валидную JSON-структуру. Если вы ожидали {"name": string, "age": number}, можете получить {"full_name": "Alice", "years_old": 25}. Валидный JSON, неверный schema.

JSON Schema mode (type: "json_schema") использует constrained decoding с конечным автоматом, отслеживающим позицию в schema. На каждом токене модель может сгенерировать только токены, валидные в текущей позиции schema. Результат гарантированно соответствует schema — не просто валидный JSON, а именно та структура, которую вы указали.

Это различие критически важно для production: с json_object всё равно нужна валидация на уровне приложения; с json_schema провайдер берёт это на себя.

OpenAI: полный стек structured output

OpenAI предлагает наиболее полную реализацию structured output. Два режима через параметр response_format.

JSON Object Mode (legacy)

from openai import OpenAI

client = OpenAI()

response = client.chat.completions.create(
    model="gpt-5.6",
    messages=[
        {"role": "system", "content": "Извлеки детали события. Верни JSON."},
        {"role": "user", "content": "Алиса и Боб встречаются в полдень в пятницу на обед."}
    ],
    response_format={"type": "json_object"}
)

data = json.loads(response.choices[0].message.content)

В сообщениях обязательно должно быть слово "json" — иначе API вернёт ошибку. Вывод — валидный JSON, но не обязательно по вашему schema.

JSON Schema Mode (рекомендуется)

from openai import OpenAI
from pydantic import BaseModel

client = OpenAI()

class CalendarEvent(BaseModel):
    name: str
    date: str
    participants: list[str]

response = client.responses.parse(
    model="gpt-5.6",
    input=[
        {"role": "system", "content": "Извлеки информацию о событии."},
        {"role": "user", "content": "Алиса и Боб идут на научную выставку в пятницу."}
    ],
    text_format=CalendarEvent,
)

event = response.output_parsed  # CalendarEvent, schema гарантирован

Ключевые детали:

  • Constrained decoding — модель буквально не может генерировать токены, нарушающие schema
  • Поддерживаемые модели — GPT-4o и позже, включая GPT-5.6
  • Возможности schema — вложенные объекты, массивы, enum, опциональные поля, anyOf/allOf
  • Обработка отказов — при отказе модели response.refusal заполняется; output_parsed = None
  • Streaming — поддерживается; chunks собираются в schema-валидный JSON

Ограничения schema: все поля должны быть required (nullable типы для опциональных), additionalProperties должен быть false, рекурсивные schema имеют ограничение глубины.

Источник: OpenAI Structured Outputs Guide (получено 2026-08-04)

Anthropic Claude: output_config с grammar-based enforcement

Anthropic использует другой подход. Вместо расширения response_format Claude использует отдельный параметр output_config с grammar-based constrained decoding.

import anthropic

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-opus-4-6",
    max_tokens=1024,
    messages=[
        {"role": "user", "content": "Извлеки сущности из текста: Встреча в штаб-квартире Google во вторник."}
    ],
    output_config={
        "format": "json",
        "schema": {
            "type": "object",
            "properties": {
                "entities": {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "properties": {
                            "name": {"type": "string"},
                            "type": {"type": "string", "enum": ["person", "org", "location", "date"]},
                            "confidence": {"type": "number", "minimum": 0, "maximum": 1}
                        },
                        "required": ["name", "type", "confidence"]
                    }
                }
            },
            "required": ["entities"]
        }
    }
)

Ключевые отличия от OpenAI:

  • Имя параметраoutput_config, а не response_format. Не совместим с OpenAI.
  • Grammar сбрасывается между секциями — при использовании extended thinking grammar применяется только к финальному ответу, а не к блоку рассуждений. Claude может свободно рассуждать, а затем сгенерировать structured output.
  • Нет json_object mode — Anthropic не поддерживает простой режим «просто дай валидный JSON». Либо полный schema, либо workaround через tool-use.
  • Несовместимости — citations и prefix-filling (prefill assistant message) несовместимы с output_config.
  • Поддерживаемые моделиClaude Opus 4 и Claude Sonnet 4.5 и позже.

Источник: Anthropic Structured Outputs (получено 2026-08-04)

DashScope (Qwen): json_object через OpenAI-совместимый API

DashScope поддерживает structured output через OpenAI-совместимый endpoint, но только json_object — без strict json_schema.

from openai import OpenAI

client = OpenAI(
    api_key="your-dashscope-key",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)

response = client.chat.completions.create(
    model="qwen-plus",
    messages=[
        {"role": "system", "content": "Извлеки имя и возраст пользователя. Верни JSON."},
        {"role": "user", "content": "Привет, я Alex Brown, мне 34 года."}
    ],
    response_format={"type": "json_object"}
)

data = json.loads(response.choices[0].message.content)

Ключевые детали:

  • МоделиQwen-Max, Qwen-Plus, Qwen-Flash, Qwen-Turbo, Qwen-Coder, Qwen-Long (все в non-thinking mode). Мультимодальные (Qwen-VL, Qwen-Omni) тоже поддерживаются.
  • "json" в prompt обязателен — слово "JSON" (без учёта регистра) должно быть в system или user message.
  • Thinking mode — модели в thinking mode принимают json_object без ошибки, но некоторые могут вернуть невалидный JSON.
  • Нет json_schematype: "json_schema" не поддерживается.

Источник: DashScope Structured Output (получено 2026-08-04)

DeepSeek: только json_object

DeepSeek поддерживает structured output через json_object.

from openai import OpenAI

client = OpenAI(
    api_key="your-deepseek-key",
    base_url="https://api.deepseek.com"
)

response = client.chat.completions.create(
    model="deepseek-v4-pro",
    messages=[
        {"role": "system", "content": "Разбери вопрос и ответ в JSON формат."},
        {"role": "user", "content": "Какая самая высокая гора? Эверест."}
    ],
    response_format={"type": "json_object"}
)

data = json.loads(response.choices[0].message.content)

Ключевые детали:

  • МоделиDeepSeek V4 Pro, DeepSeek V4 Flash
  • "json" в prompt — обязателен, как у OpenAI в json_object mode
  • Нет json_schema — только json_object
  • Reasoning-модели — DeepSeek-R1 и reasoning mode имеют ограниченную поддержку json_object

Источник: DeepSeek JSON Output (получено 2026-08-04)

SiliconFlow: OpenAI-совместимый json_object

SiliconFlow предоставляет json_object через OpenAI-совместимый API, но доступность зависит от модели.

from openai import OpenAI

client = OpenAI(
    api_key="your-siliconflow-key",
    base_url="https://api.siliconflow.cn/v1"
)

response = client.chat.completions.create(
    model="Qwen/Qwen3-8B",
    messages=[
        {"role": "system", "content": "Верни ответ в формате JSON."},
        {"role": "user", "content": "Назови топ-3 языка программирования."}
    ],
    response_format={"type": "json_object"}
)

Ключевые детали:

  • Зависит от модели — не все модели на SiliconFlow поддерживают response_format
  • OpenAI-совместимый — тот же параметр response_format
  • Нет json_schema — только json_object
  • "json" в prompt — рекомендуется для стабильности

Источник: SiliconFlow Chat Completions API (получено 2026-08-04)

Подводные камни при маршрутизации между провайдерами

При маршрутизации structured-output запросов через TheRouter или любой gateway возникает несколько проблем совместимости.

1. Даунгрейд json_schema

Если запрос использует response_format: {type: "json_schema", json_schema: {...}} и маршрутизируется к DashScope или DeepSeek, strict schema enforcement теряется. Gateway может понизить до json_object и вставить schema в system prompt, но это best-effort.

Смягчение: Добавьте валидацию JSON Schema на уровне приложения после каждого ответа. jsonschema (Python) или ajv (JavaScript) дают пренебрежимую латентность.

2. Трансляция параметров Anthropic

Anthropic использует output_config вместо response_format. Gateway должен транслировать параметр и учитывать, что Claude вообще не поддерживает json_object.

3. Требование "json" в prompt

OpenAI (json_object mode), DashScope и DeepSeek требуют слово "json" в сообщениях. Anthropic — нет.

Смягчение: Всегда включайте "json" в system prompt при использовании response_format. Для провайдеров, которым это не нужно, это безвредно.

4. Streaming + structured output

Все провайдеры поддерживают streaming со structured output, но поведение различается:

  • OpenAI — каждый chunk — частичный JSON-фрагмент; собранный результат schema-валиден
  • Anthropic — streaming с output_config работает; grammar constraint действует между chunks
  • DashScope / DeepSeek — streaming выдаёт частичные JSON chunks; только финальный результат гарантированно валиден

5. Ограничения глубины schema

OpenAI json_schema имеет ограничения: максимум 5 уровней для anyOf, все поля required, additionalProperties: false обязателен.

DashScope и DeepSeek не имеют ограничений schema — но и не гарантируют его соблюдение.

Чеклист для production

  1. Всегда валидируйте после генерации — даже с json_schema проверяйте ответ в коде приложения.

  2. Стратегия retry — при ошибке JSON-парсинга попробуйте упрощённый prompt или переключитесь через fallback routing.

  3. Достаточный max_tokens — если token limit достигнут посреди JSON, получится невалидный JSON.

  4. Логируйте сырой ответ — для отладки schema-несоответствий.

  5. Тестируйте на всех провайдерах — schema, работающий на OpenAI, может давать другой порядок полей на DashScope.

  6. Обрабатывайте отказы — OpenAI возвращает refusal, Anthropic — stop_reason: "end_turn" с возможно пустым контентом, DashScope/DeepSeek могут вернуть текст вместо JSON.

Интеграция с TheRouter

TheRouter маршрутизирует OpenAI-совместимые запросы через настроенных провайдеров, включая structured-output запросы. Когда запрос содержит response_format, TheRouter сохраняет параметр для провайдеров, которые его поддерживают (OpenAI, DashScope, DeepSeek, SiliconFlow). Для Anthropic трансляция следует провайдер-специфичному маппингу.

Если запрос указывает json_schema, а маршрутизированный провайдер поддерживает только json_object, schema enforcement зависит от провайдера. Мы рекомендуем валидацию на уровне приложения в любом случае.

При fallback routing structured-output параметры сохраняются между retry. Fallback-провайдер может иметь другой уровень поддержки schema.

Частые ошибки и решения

ОшибкаПровайдерПричинаРешение
messages must contain the word 'json'OpenAI, DashScope, DeepSeekjson_object без "json" в сообщенияхДобавить "Return JSON" в system prompt
Invalid response_format typeDashScope, DeepSeekjson_schema на провайдере без его поддержкиДаунгрейд до json_object + schema в prompt
output_config is incompatible with citationsAnthropicoutput_config + citationsОтключить citations или tool-use workaround
Обрезанный JSONВсеmax_tokens слишком малУвеличить max_tokens
Валидный JSON, неверный schemaDashScope, DeepSeek, SiliconFlowjson_object не контролирует schemaВалидация JSON Schema после генерации
Пустой content + stop_reasonAnthropicМодель отказалаПроверить отказ; повторить с изменённым prompt

FAQ

Можно ли использовать json_schema с reasoning-моделями?

Зависит от провайдера. OpenAI поддерживает json_schema с o-series. Anthropic output_config работает с extended thinking — grammar применяется только к финальному ответу. DashScope предупреждает, что thinking-mode модели могут вернуть невалидный JSON даже с json_object.

tool-use или response_format для structured output?

response_format (или output_config на Anthropic) — когда сам ответ модели должен быть structured JSON. tool-use — когда модель подключается к реальным функциям. Подробнее в руководстве по function calling.

Instructor или Outlines?

Instructor (Python/TypeScript) и Outlines обеспечивают structured output на уровне фреймворка. Instructor оборачивает клиент провайдера и добавляет Pydantic/Zod валидацию с автоматическими retry. Outlines использует grammar-constrained decoding для локальных моделей.

Как structured output взаимодействует с prompt caching?

На OpenAI запросы с одинаковым json_schema выигрывают от prompt caching. На Anthropic output_config не взаимодействует с cache_control. На DashScope json_object кэшируется нормально через context caching.


Последняя проверка: 4 августа 2026 года. API провайдеров меняются; проверяйте официальную документацию для актуальной информации о поддержке schema.

Модели, упомянутые в статье

Поддержка