Перейти к содержимому
EN

Структурный вывод

Когда от модели нужен не текст, а готовый объект — карточка товара, результат классификации, аргументы для функции, — на POST /v1/chat/completions включается параметр response_format. Он передаётся апстриму как есть, поэтому ведёт себя так же, как в OpenAI API. Тарификация обычная, по токенам — наценки за структурный вывод нет.

С остальными параметрами response_format сочетается свободно: stream, reasoning_effort и tools можно использовать в том же запросе — например, вызвать инструмент и получить ответ функции в JSON по схеме.

Режим {"type": "json_object"} обязывает модель ответить валидным JSON: без вводных фраз, без markdown-обёртки, без обрыва на середине объекта.

Требование, которое стоит соблюдать: слово «JSON» должно встречаться в самом промпте. Это правило OpenAI — часть моделей, не увидев его, отказывается отвечать или запрос падает с 400. Проще всего прямо написать: «Верни ответ в виде JSON с полями …».

import json
from openai import OpenAI
client = OpenAI(
base_url="https://api.mixen.ai/v1",
api_key="mxn-...",
)
resp = client.chat.completions.create(
model="gpt-5.6-luna",
messages=[{
"role": "user",
"content": (
"Извлеки из письма отправителя, получателя и сумму перевода. "
"Верни ответ в виде JSON с полями from, to, amount (сумма числом в рублях).\n\n"
"Письмо: «Иван, привет! Переведи, пожалуйста, 1500 рублей Кате до пятницы. — Мария»"
),
}],
response_format={"type": "json_object"},
)
data = json.loads(resp.choices[0].message.content)
print(data["from"], data["to"], data["amount"])

В message.content придёт примерно такая строка:

{"from": "Мария", "to": "Катя", "amount": 1500}

Обратите внимание: content остаётся строкой — Mixen и апстрим гарантируют валидный JSON в ней, но распарсить его должен клиент (json.loads). Это общее правило структурного вывода: гарантия живёт на стороне модели, разбор — в вашем коде.

Гарантируется только синтаксис. Какие поля окажутся внутри и каких они типов — решает промпт; если нужна жёсткая схема, это следующий режим.

Режим json_schema строже: модель обязана построить ответ по схеме — ровно те поля, ровно тех типов. Схема передаётся внутри response_format, а флаг strict: true включает строгую генерацию.

Схему удобно описать pydantic-моделью и взять из неё готовый словарь через .model_json_schema() — тем же словарём потом валидируется ответ:

import json
from openai import OpenAI
from pydantic import BaseModel
client = OpenAI(
base_url="https://api.mixen.ai/v1",
api_key="mxn-...",
)
class ProductCard(BaseModel):
name: str # название товара
price: int # цена в рублях
tags: list[str] # до пяти тегов
resp = client.chat.completions.create(
model="gpt-5.6-luna",
messages=[{
"role": "user",
"content": (
"Составь карточку товара: name, price в рублях и до пяти tags.\n\n"
"Товар: беспроводные наушники с активным шумоподавлением, 7 990 ₽"
),
}],
response_format={
"type": "json_schema",
"json_schema": {
"name": "product_card",
"schema": ProductCard.model_json_schema(),
"strict": True,
},
},
)
parsed = json.loads(resp.choices[0].message.content)
card = ProductCard.model_validate(parsed)
print(card.name, card.price, card.tags)

Пара замечаний к примерам:

  • Схему можно собрать и руками, как в примере на cURL, — pydantic не обязателен. Для strict-режима OpenAI обычно требует, чтобы все поля были в required, а additionalProperties было false.
  • model_validate здесь — страховка, а не замена строгой генерации: ответ почти всегда уже соответствует схеме, но проверить на своей стороне дешевле, чем поймать сюрприз в проде.
  • Как и в первом режиме, message.content — строка. Метод parse(..., response_model=...) из OpenAI SDK полагается на то, что валидацию делает API, — у нас её делает апстрим на этапе генерации, поэтому разбор и проверку выполняет ваш код: явный json.loads плюс валидация.

json_schema со strict — возможность конкретной модели, а не часть протокола. Если модель строгие схемы не умеет, запрос вернёт 400 с ошибкой апстрима (коды и формат ошибок — в Ошибки). Два рабочих хода:

  • Фолбэк без strict. Опишите схему словами в промпте — «Верни JSON с полями name (строка), price (целое число), tags (массив строк, до пяти)» — и оставьте {"type": "json_object"}. Валидность синтаксиса гарантирована, а соответствие полей проверит та же pydantic-модель через model_validate; на несоответствии останется повторить запрос.
  • Пробный запрос. Поддерживает ли конкретная модель strict-схемы, быстрее всего проверить одним дешёвым запросом с минимальной схемой — и зафиксировать результат в конфигурации своего приложения.
  • Только /v1/chat/completions. Эндпоинты /v1/messages и /v1/responses параметр response_format не принимают.
  • message.content — строка. Гарантия формы ответа живёт в апстриме, разбор JSON — всегда на клиенте.
  • strict зависит от модели: где-то поддерживается, где-то нет. Универсального флага в каталоге нет — проверяйте пробным запросом.

Отдельно про Anthropic-протокол: на /v1/messages структурного вывода как параметра нет, и это не запрет Mixen, а устройство самого протокола. Там тот же эффект собирают через tool-use: описываете инструмент, чья input_schema совпадает с нужной схемой, принуждаете модель к вызову через tool_choice и читаете JSON из аргументов вызова.

См. также: Чат и стриминг и Вызов инструментов.