Структурный вывод
Когда от модели нужен не текст, а готовый объект — карточка товара, результат классификации, аргументы для функции, — на POST /v1/chat/completions включается параметр response_format. Он передаётся апстриму как есть, поэтому ведёт себя так же, как в OpenAI API. Тарификация обычная, по токенам — наценки за структурный вывод нет.
С остальными параметрами response_format сочетается свободно: stream, reasoning_effort и tools можно использовать в том же запросе — например, вызвать инструмент и получить ответ функции в JSON по схеме.
json_object — просто валидный JSON
Заголовок раздела «json_object — просто валидный JSON»Режим {"type": "json_object"} обязывает модель ответить валидным JSON: без вводных фраз, без markdown-обёртки, без обрыва на середине объекта.
Требование, которое стоит соблюдать: слово «JSON» должно встречаться в самом промпте. Это правило OpenAI — часть моделей, не увидев его, отказывается отвечать или запрос падает с 400. Проще всего прямо написать: «Верни ответ в виде JSON с полями …».
import jsonfrom 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"])curl https://api.mixen.ai/v1/chat/completions \ -H "Authorization: Bearer $MIXEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.6-luna", "messages": [{ "role": "user", "content": "Извлеки из письма отправителя, получателя и сумму перевода. Верни ответ в виде JSON с полями from, to, amount (сумма числом в рублях).\n\nПисьмо: «Иван, привет! Переведи, пожалуйста, 1500 рублей Кате до пятницы. — Мария»" }], "response_format": {"type": "json_object"} }'В message.content придёт примерно такая строка:
{"from": "Мария", "to": "Катя", "amount": 1500}Обратите внимание: content остаётся строкой — Mixen и апстрим гарантируют валидный JSON в ней, но распарсить его должен клиент (json.loads). Это общее правило структурного вывода: гарантия живёт на стороне модели, разбор — в вашем коде.
Гарантируется только синтаксис. Какие поля окажутся внутри и каких они типов — решает промпт; если нужна жёсткая схема, это следующий режим.
json_schema — ответ по схеме
Заголовок раздела «json_schema — ответ по схеме»Режим json_schema строже: модель обязана построить ответ по схеме — ровно те поля, ровно тех типов. Схема передаётся внутри response_format, а флаг strict: true включает строгую генерацию.
Схему удобно описать pydantic-моделью и взять из неё готовый словарь через .model_json_schema() — тем же словарём потом валидируется ответ:
import jsonfrom openai import OpenAIfrom 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 https://api.mixen.ai/v1/chat/completions \ -H "Authorization: Bearer $MIXEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "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", "strict": true, "schema": { "type": "object", "properties": { "name": {"type": "string"}, "price": {"type": "integer"}, "tags": {"type": "array", "items": {"type": "string"}} }, "required": ["name", "price", "tags"], "additionalProperties": false } } } }'Пара замечаний к примерам:
- Схему можно собрать и руками, как в примере на 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 из аргументов вызова.
См. также: Чат и стриминг и Вызов инструментов.