В 2026 году команды, которые держат OpenAI-агентов в проде, чаще всего падают не на «модель не умеет говорить», а на старом коде, где json_object всё ещё считают структурированным выводом, или на Chat Completions без строгого Function Calling. Для новых проектов официально рекомендуют стартовать с gpt-5.6. Function Calling и Structured Outputs опираются на один и тот же constrained decoding, но вход, поведение strict по умолчанию и допустимое подмножество JSON Schema — разные.
Дата сверки — 18 августа 2026. Поля и поведение — по руководству OpenAI Function Calling и руководству Structured Outputs. Здесь нет выдуманных задержек, цен и долей успеха. Где публичная документация ничего не фиксирует, явно написано: «проверить на replay живого трафика».
Если вы переводите OpenAI-совместимый бэкенд на другую модель, структурированный вывод и цикл инструментов нужно принимать отдельно — одной сменой Base URL недостаточно. См. внутренний чек-лист миграции OpenAI API на Kimi K3.
Сначала вывод: меняются три вещи сразу
Многие репозитории всё ещё живут моделью 2024 года: в промпте «верни JSON», потом regex по code fence. В проде 2026 это рвёт парсер через нарушения схемы, отсутствующие поля и галлюцинации enum.
Менять нужно три слоя, а не имя модели побольше:
- Контракт поставки
- Объект для пользователя или downstream — через Structured Outputs (
text.formatв Responses,response_format.json_schemaв Chat Completions). - Контракт исполнения
- Когда модель вызывает ваши инструменты, аргументы должны совпадать с JSON Schema инструмента. Это Function Calling — тот же constrained decoding, что у Structured Outputs.
- Контракт совместимости
- Старый JSON Mode (
json_object) гарантирует только «похоже на JSON», не поля, типы и enum относительно схемы. В доке это предшественник Structured Outputs. Новым проектам не стоит делать его основным путём.
Ещё один продуктовый сдвиг, который легко пропустить: новый код должен идти через Responses API. Chat Completions по-прежнему доступен, но стратегия strict по умолчанию другая — Responses старается нормализовать схему в строгий режим и откатывается только при неудаче; у Chat Completions по умолчанию по-прежнему non-strict / best effort.
Модель и основной API-путь: gpt-5.6 + Responses
Structured Outputs доступны начиная с поколения GPT-4o. Для новых проектов в документации предлагают сразу gpt-5.6. Более старые снимки вроде gpt-4-turbo и раньше по-прежнему отсылают к JSON Mode, а не к полноценному строгому json_schema.
Сначала разделить два входа
| Нужный результат | Какой вход | На что смотреть в 2026 |
|---|---|---|
| Фиксированный объект пользователю / downstream | Responses: text.format; или Chat Completions: response_format: json_schema |
Включить strict: true; SDK: Pydantic / Zod + parse() |
| Модель вызывает ваши функции, читает БД, меняет состояние | function-инструмент в tools |
Схема параметров тоже strict; параллельные вызовы и мультитул-циклы — свой исполнитель |
| Слишком много инструментов, не хотите класть всё в контекст | Отложенная загрузка tool_search |
Только gpt-5.4 и новее; определения инструментов считаются input-токенами |
| Аргументы не JSON, а свободный текст или грамматика | custom tools + опциональная CFG | Для DSL и языков запросов; не втискивать в JSON Schema функции |
На стороне SDK привычка важнее: не писать схему вручную (легко забыть additionalProperties), а генерировать её из типов официальными хелперами. Python: client.responses.parse(..., text_format=YourModel); JavaScript: zodTextFormat. При рукописной схеме и strict: true нарушение ограничений даёт отклонение запроса, а не «модель выдала что попало, вы ретраите».
Сравнивая с линейкой Gemini, «совместимость с OpenAI SDK» не означает то же поведение схемы. Обновления возможностей Google: 10 новых функций Gemini 3.5 Pro. Когда одну и ту же JSON Schema копируют между провайдерами, правило additionalProperties на вложенных object часто взрывается первым.
JSON Mode, Structured Outputs, Function Calling
Самая частая путаница в инцидентах: в логах JSON, значит Structured Outputs уже включены. По официальной семантике:
| Возможность | Валидный JSON | Соответствие схеме | Типичное включение | Модели |
|---|---|---|---|---|
| JSON Mode | Да | Нет | text.format.type = json_object |
В т.ч. часть совместимых ступеней GPT-5; типично для старых снимков |
| Structured Outputs | Да | Да (поддерживаемое подмножество схемы) | json_schema + strict: true |
gpt-4o-2024-08-06 / gpt-4o-mini и новее; новые проекты — gpt-5.6 |
| Function Calling + strict | Аргументы инструмента — валидный JSON | Аргументы совпадают со схемой parameters | strict: true у инструмента |
Модели с tools; strict рекомендуют всегда |
Когда Function Calling не нужен
Если модель не должна трогать вашу систему (нет склада, тикетов, скриптов), а ответ лишь раскладывается в карточки, шаги или оценки — Structured Outputs. Как только вывод означает «выполни этот побочный эффект», нужны tools: не маскируйте аргументы функции под схему финального ответа.
Отказ — это не «битый JSON»
При safety-отказе модель не запихивают в вашу схему. Responses / Chat Completions отдают отдельное поле refusal. Слой разбора должен считать отказ гражданином первого класса: сначала refusal, потом output_parsed — пустой объект не успех.
Жёсткие правила Strict JSON Schema
После включения strict OpenAI принимает подмножество JSON Schema, а не любой документ Draft 2020-12. Три самых частых отказа на уровне запроса:
- Каждое поле из
propertiesдолжно быть в массивеrequired. - Каждый
object(включая вложенные) должен иметьadditionalProperties: false. - Корневой объект не может быть
anyOf; опциональность — через «обязательное + разрешён null», например["string", "null"].
То есть: убрать поле из required и притвориться, что оно опционально — под strict это сразу 400. Правильно: поле остаётся required, тип — nullable union, приложение трактует null как «не задано».
Типичный прод-объект «извлечь тикет» — у вложенного object тоже есть additionalProperties:
from openai import OpenAI
from pydantic import BaseModel
client = OpenAI()
class Ticket(BaseModel):
title: str
priority: str
assignee: str | None
tags: list[str]
response = client.responses.parse(
model="gpt-5.6",
input=[
{"role": "system", "content": "Извлеки поля тикета из описания пользователя."},
{"role": "user", "content": "Страница входа 500, назначить Noah, приоритет высокий, теги auth и api."},
],
text_format=Ticket,
)
ticket = response.output_parsed
print(ticket.title, ticket.priority, ticket.assignee)
Если запрос отклонён, сначала смотрите в ошибке, какого ограничения не хватает — не понижайте модель сразу. Схемы из Playground уже со strict; скопировать их в репозиторий обычно быстрее, чем переделывать старые промпты json_object.
Между вендорами проверьте ещё раз: та же схема «у каждого object false» на части совместимых шлюзов или других моделях может дать HTTP 400. Тогда нужна трансформация по провайдеру, а не три бизнес-схемы.
Function Calling 2026: strict, tool_search, custom tools
Function Calling и tool calling в документации — одно и то же: JSON Schema описывает вызываемые функции, исполнитель в приложении делает побочные эффекты. Несколько пунктов, которые в 2026 подчёркивают отдельно, сразу меняют цикл агента.
Значение strict по умолчанию не угадывать
- Рекомендуют всегда явно ставить
strict: true. - Responses: если strict опущен, сервер пытается нормализовать схему; при неудаче откат в non-strict, в ответе у tool будет
strict: false. - Chat Completions: без указания по умолчанию non-strict.
- У дообученных моделей при нескольких вызовах функций за один ход документация допускает отключение strict на этот ход.
Определения инструментов входят в контекст и тарифицируются как input-токены. Слишком длинные description и 40 инструментов сразу бьют и по счёту, и по точности выбора. Если инструментов много — откладывайте редкие через tool_search: только gpt-5.4 и новее. В цикле сначала могут появиться tool_search_call / tool_search_output, и лишь потом настоящий function_call.
Custom tools: не запихивайте DSL в JSON-объект
Function tools подходят для структурированных параметров; custom tools — для свободного текста на входе и выходе, с опциональной контекстно-свободной грамматикой (CFG). Фрагменты SQL, внутренние языки запросов, форматы с взаимоисключающими терминалами: CFG стабильнее, чем «string-поле плюс простыня промпта». Если CFG ругается на unexpected tokens, сначала ищите пересекающиеся терминалы, а не вините модель.
tools = [{
"type": "function",
"name": "get_order",
"description": "Запросить статус заказа по номеру. Вызывать только если пользователь указал конкретный номер заказа.",
"strict": True,
"parameters": {
"type": "object",
"properties": {
"order_id": {"type": "string"},
"locale": {"type": ["string", "null"]},
},
"required": ["order_id", "locale"],
"additionalProperties": False,
},
}]
Цикл исполнения не изменился: увидели finish_reason / тип item = вызов инструмента → локальная функция → вернуть результат в роли tool → новый запрос. Изменилось другое: аргументы больше не нужно ловить через json.loads наудачу. Полное сообщение assistant (включая tool_calls) всё равно нужно сохранять, иначе на втором ходе пропадут ID вызовов.
При параллельных вызовах инструментов порядок задаёт call ID
В одном ответе может быть несколько tool call. Ответ выравнивайте по call_id, не по индексу массива. В canary-логах минимум: имя инструмента, хеш аргументов, длительность, был ли strict, был ли откат схемы.
Чек-лист миграции старых проектов
Разделите приёмку «оно крутится» и «это можно отдавать». Оставьте переключатель на старый бэкенд и сначала прогоните реальный трафик в изолированной среде.
- Модель: новая цепочка явно на
gpt-5.6(или эквивалентный уровень, уже открытый на аккаунте); шлюз не должен молча маппить на старый снимок. - Вывод: заменить
json_objectнаjson_schema+strict: trueили наtext.formatв Responses. - Инструменты: у каждой function полный
requiredи вложенныйadditionalProperties: false; опциональные поля — nullable union. - Разбор: подключить
parse()и веткуrefusal; в стриминге сверить инкрементальный JSON с финальным parsed-объектом. - Поверхность инструментов: больше десятка — оценить
tool_search; сначала укоротить description, потом отложенную загрузку. - Сравнение: один и тот же набор фиксированных задач — ретраи, доля отсутствующих полей, ручная доработка между старым JSON Mode и новым путём схемы.
Критерий прохождения — не HTTP 200, а: парсер без regex-подстраховки, стабильные типы аргументов инструментов, наблюдаемые отказы, уже отработанный переключатель отката. Долгие сессии SDK, скрипты replay и вкладки браузера обрываются, когда ноутбук засыпает — отсюда облачный Mac mini ниже.
FAQ
Можно ли смешивать JSON Mode и Structured Outputs?
Не на одной цепочке. JSON Mode гарантирует только валидный JSON; Structured Outputs — схему. Вперемешку мониторинг не отличит сбой парсера от сбоя контракта. Новый код только json_schema / text.format.
Новым проектам ещё писать Chat Completions?
Если можно Responses — берите Responses. Официальные примеры, parse-хелперы и нормализация strict идут туда в первую очередь. Существующий Chat Completions можно оставить, но со явным strict и с пониманием non-strict по умолчанию.
Почему схема даёт 400, как только включаю strict?
Чаще всего: нет required, у вложенного object нет additionalProperties:false, корень — anyOf, или опциональность сделана как «нет в required». Допишите ограничение из текста ошибки; не выключайте strict, чтобы спрятать ошибку схемы, если только вы сознательно не хотите non-strict fallback.
Function Calling обязательно со strict?
Официально — всегда включать. Без strict аргументы best effort: исполнителю всё равно ловить дыры в полях и дрейф типов. Responses при опущенном strict может переписать его на сервере; в логах фиксируйте итоговое значение strict.
Когда имеет смысл tool_search?
Когда определения инструментов заметно занимают контекст или большинство инструментов в одной задаче не нужны. Нужен gpt-5.4 и новее. Перед продом прогоните двухтактную трассу «сначала поиск инструмента, потом вызов»; старый исполнитель, который знает только function_call, оборвётся.
Схема держится — всё равно проверять бизнес-значения?
Да. Constrained decoding не проверяет внешние ключи, права и идемпотентность. Легальный enum не значит, что значение осмысленно для вашего склада. Разведите логи схемы и бизнес-проверок — иначе инцидент не разберёте.
Чем gpt-5.6 отличается от более раннего GPT-5.x на структурированном выводе?
Документация ставит gpt-5.6 дефолтом для новых проектов. Совпадает ли это на вашем аккаунте, в регионе, batch и fine-tune — смотрите текущий список моделей и минимальный parse-запрос, а не алиасы из блога как маппинг шлюза.
Одна JSON Schema на Claude / Grok и OpenAI?
Диалект близок к Draft 2020-12, подмножества разные. OpenAI strict требует additionalProperties:false на каждом object; часть провайдеров отклоняет это поле на вложенном уровне. Одна бизнес-схема, слой трансформации по провайдеру.
Читать дальше
На облачном Mac mini приёмку схемы можно держать 24/7
Регрессия Function Calling и Structured Outputs — это длинный сравнительный эксперимент: два SDK, фиксированный replay-набор, песочница инструментов, стриминговый фронт — и ноутбук, который не засыпает от крышки. Unified memory на Apple Silicon удобна, чтобы параллельно крутить локальный прокси и отладку в браузере; Homebrew, Docker и SSH на macOS готовы сразу. M4 Mac mini в простое около 4 Вт — среда приёмки спокойно висит на ночь.
Если нужен Mac, который не отъедает домашний канал и доступен по SSH для replay агентов, облачный Mac mini M4 от Nuvcloud — низкофрикционный способ развести машину разработки и стенд сравнения — смотреть тарифы, чтобы canary строгого Schema не был привязан к вашему ноутбуку.