惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Fortinet All Blogs
MyScale Blog
MyScale Blog
Microsoft Security Blog
Microsoft Security Blog
量子位
B
Blog
aimingoo的专栏
aimingoo的专栏
Apple Machine Learning Research
Apple Machine Learning Research
阮一峰的网络日志
阮一峰的网络日志
The GitHub Blog
The GitHub Blog
T
The Exploit Database - CXSecurity.com
N
News | PayPal Newsroom
Cloudbric
Cloudbric
A
About on SuperTechFans
AI
AI
Hacker News: Ask HN
Hacker News: Ask HN
S
Schneier on Security
Recent Commits to openclaw:main
Recent Commits to openclaw:main
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Cyber Attacks, Cyber Crime and Cyber Security
L
LINUX DO - 最新话题
T
The Blog of Author Tim Ferriss
Simon Willison's Weblog
Simon Willison's Weblog
有赞技术团队
有赞技术团队
H
Heimdal Security Blog
J
Java Code Geeks
大猫的无限游戏
大猫的无限游戏
D
Docker
Security Archives - TechRepublic
Security Archives - TechRepublic
N
News and Events Feed by Topic
IT之家
IT之家
Know Your Adversary
Know Your Adversary
N
Netflix TechBlog - Medium
T
Tailwind CSS Blog
B
Blog RSS Feed
C
Cybersecurity and Infrastructure Security Agency CISA
C
Cisco Blogs
博客园 - 叶小钗
美团技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
H
Hackread – Cybersecurity News, Data Breaches, AI and More
L
LangChain Blog
The Hacker News
The Hacker News
Y
Y Combinator Blog
I
Intezer
The Register - Security
The Register - Security
F
Full Disclosure
V
V2EX
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Last Week in AI
Last Week in AI
Martin Fowler
Martin Fowler

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Structured Outputs без иллюзий: как OpenAI, Gemini и xAI соблюдают JSON»-схемы
Евгений · 2026-05-10 · via Все публикации подряд на Хабре

Structured Outputs, или structured decoding, это способ заставить LLM возвращать ответ в заранее заданном формате: валидный JSON, соответствующий JSON Schema. На уровне генерации это обычно означает constrained decoding: на каждом шаге модели запрещаются токены, которые привели бы к нарушению схемы. Хорошее техническое объяснение есть в статье vLLM: [Structured Decoding in vLLM: a gentle introduction].

Для продакшн-систем это важно, потому что ответ модели часто становится входом для следующего шага: API-вызова, записи в базу, бизнес-правила или другого LLM-запроса.

Промпт в стиле «ответь строго валидным JSON» помогает, но не даёт инженерной гарантии. Модель может поменять имя ключа, добавить пояснение перед JSON или выбрать значение вне допустимого enum. Structured Outputs должен решать именно эту задачу: провайдер принимает схему и ограничивает вывод так, чтобы ответ соответствовал формату.

К Structured Outputs можно относиться по-разному. Жёсткая схема иногда ухудшает качество ответа: модель может хуже рассуждать или зациклиться в длинном строковом поле до лимита токенов. Но в корпоративных системах это всё равно востребованная функция: формат ответа часто важен не меньше, чем сам текст ответа. С деградацией качества приходится работать отдельно — через evals, подбор схемы, разбиение задачи на шаги и проверку поведения конкретной модели.

Важно: Structured Outputs не снимает все проверки на стороне приложения. В документации OpenAI отдельно описаны случаи, когда ответ может не быть обычным успешным JSON по вашей схеме: safety refusal, incomplete response / truncation и другие edge cases. Их всё равно нужно обрабатывать в коде, а результат валидировать после получения. См. [Structured model outputs].

На практике остаётся вопрос: какие ограничения схемы провайдер действительно применяет, а какие только принимает в JSON Schema. Это особенно важно для [Schema-Guided Reasoning], где структура Pydantic-модели используется не только для парсинга, но и для управления ходом ответа. Чтобы проверить это, мы прошлись по трём провайдерам (OpenAI, Gemini, xAI) с конфликтными промптами: просили модель нарушить конкретное ограничение и смотрели, сможет ли провайдер удержать ответ в рамках схемы.

Как тестировали

Идея проверки простая: если провайдер действительно применяет ограничение из схемы, модель не сможет вернуть значение, которое это ограничение нарушает. Даже если в промпте прямо попросить её так сделать.

Для каждого ограничения мы делали минимальную JSON Schema и отдельный промпт, который просит вернуть некорректное значение.

Например, схема требует строку длиной от 5 до 8 символов:

{
  "type": "object",
  "properties": {
  "word": {
    "type": "string",
    "minLength": 5,
    "maxLength": 8
    }
  },
  "required": ["word"],
  "additionalProperties": false
}

А в промпте мы просим нарушить это ограничение:

"Return word='hi' — exactly 2 characters. Use that exact short word."

Дальше смотрим на результат:

  • если вернулось "hi"minLength не enforced;

  • если вернулась строка длиной 5–8 символов — провайдер не дал модели нарушить схему.

Полный список проверенных ограничений:

Ограничение

Пример в схеме

Пример нарушения в промпте

enum / Literal[...]

severity["low", "medium", "high"]

вернуть "critical"

minimum / maximum

0 <= value <= 10

вернуть -5 или 15

exclusiveMinimum / exclusiveMaximum

0 < value < 10

вернуть 0, -5 или 10

minLength / maxLength для строки

строка длиной от 5 до 8 символов

вернуть "hi"

multipleOf

число должно быть кратно 3

вернуть 7

pattern

строка должна совпадать с ^[A-Z]{3}$

вернуть "abc"

minItems / maxItems для списка

массив из 2–3 элементов

вернуть пустой массив или 5 элементов

Обязательные поля

поле reasoning указано в required

пропустить reasoning

additionalProperties: false

разрешены только поля из properties

добавить поле extra

Вложенные объекты

address должен быть объектом с city и zip_code

вернуть address строкой

$defs / $ref

findings[*] ссылается на #/$defs/Finding

вернуть объект, нарушающий подмодель Finding

anyOf для nullable

nickname: строка или null

вернуть число 42, которое не является ни строкой, ни null

anyOf для Union

value: строка или integer

вернуть 3.14, то есть float вместо строки или integer

oneOf

pet: либо Cat, либо Dog по дискриминатору

вернуть объект без pet_type

allOf

Child объединяет поля Base и собственные поля

вернуть только поля Child, без полей Base

Каждый кейс запускался 3–5 раз. Все тесты шли через единый OpenAI-совместимый API. Модели: gpt-4o-mini, gemini-2.0-flash, grok-3-mini.


OpenAI

Для OpenAI ключевым параметром оказался strict: true.

Если передать схему с enum, maxItems, pattern и обязательными полями, но не включить strict: true, ограничения не применяются. В тестах модель возвращала значения из промпта, даже если они нарушали схему.

С strict: true все простые ограничения из теста применялись: литералы, числовые границы, длина строк и списков, multipleOf, pattern, обязательные поля, запрет лишних полей, вложенные объекты и $defs / $ref.

Для composition keywords есть отдельное ограничение. oneOf и allOf с strict: true приводят к 400 Bad Request: API отклоняет запрос на этапе валидации схемы.

# Это вызовет 400 при strict=True
{"oneOf": [{"$ref": "#/$defs/Cat"}, {"$ref": "#/$defs/Dog"}]}

Вероятная причина в том, что при constrained decoding схема должна быть однозначно разрешимой на каждом шаге генерации. oneOf и allOf усложняют этот процесс, поэтому такие схемы не принимаются в strict-режиме.

Поэтому, если вам нужна дискриминированная объединённая схема под OpenAI — используйте отдельные массивы на каждую ветку, а не oneOf внутри одного поля.

Gemini

Gemini через OpenAI-совместимый endpoint (/v1beta/openai/) применяет часть ограничений без отдельного параметра strict: true. В тестах работали enum, обязательные поля, additionalProperties, maxItems, вложенные объекты, anyOf и oneOf.

Основной риск в том, что Gemini принимает некоторые ограничения в схеме, но не применяет их при генерации. Это относится к minLength / maxLength, exclusiveMinimum / exclusiveMaximum, multipleOf и pattern. allOf в тесте возвращал пустой объект, а const внутри $defs работал нестабильно.

Например, для схемы с minLength: 5, maxLength: 8 и промптом «верни слово hi» Gemini вернул {"word": "hi"} во всех трёх прогонах.

Аналогично для exclusiveMinimum: 0 и промпта «верни -5.0»: модель вернула -5.0, хотя значение нарушает схему.

Отдельно стоит учитывать max_tokens. Если схема содержит поле reasoning: str или другие открытые строковые поля, при низком лимите токенов ответ может оборваться на середине JSON. В наших проверках при max_tokens=200 это происходило часто, а при max_tokens=800 проблема исчезала. Для схем с длинными строковыми полями лучше закладывать запас по токенам.

xAI (Grok)

В тестах xAI применял большинство проверенных ограничений без параметра strict: true.

Исключение — allOf: как и у Gemini, запрос возвращал пустой объект {}.

Отдельно стоит отметить, что в отличие от Gemini, xAI применял pattern, multipleOf, строгие числовые границы и ограничения длины строк. В отличие от OpenAI strict-режима, oneOf не приводил к 400 ошибке.

Итоговая таблица

Ограничение

OpenAI без strict

OpenAI strict

Gemini

xAI

enum / Literal[...]

нет

да

да

да

minimum / maximum

нет

да

да

да

exclusiveMinimum / exclusiveMaximum

нет

да

нет

да

minLength / maxLength для строки

нет

да

нет

да

multipleOf

нет

да

нет

да

pattern

нет

да

нет

да

minItems / maxItems для списка

нет

да

частично

да

Обязательные поля

нет

да

да

да

additionalProperties: false

нет

да

да

да

Вложенные объекты

нет

да

да

да

$defs / $ref

нет

да

частично

да

anyOf для nullable

нет

да

да

да

anyOf для Union

нет

да

да

да

oneOf

нет

400

да

да

allOf

нет

400

возвращает {}

возвращает {}


Что делать с ограничениями, которые не принудительны

С ограничениями, которые конкретный провайдер не применяет при генерации, можно работать двумя способами.

Первый — валидировать на своей стороне после получения ответа:

from pydantic import BaseModel, Field, ValidationError

class Response(BaseModel):
    word: str = Field(min_length=5, max_length=8)

raw = call_llm(schema=Response.model_json_schema())

try:
    result = Response.model_validate_json(raw)
except ValidationError as e:

# обработать или повторить запрос
...

Такую проверку стоит оставлять даже для провайдеров с хорошей поддержкой Structured Outputs.

Второй — проектировать схему так, чтобы ключевые ограничения выражались через структуру, а не через дополнительные JSON Schema keywords. Это один из приёмов Schema-Guided Reasoning: вместо свободной строки с ограничением длины — Literal["low", "medium", "high"]; вместо oneOf с дискриминатором — отдельные контейнеры для разных веток.

Например, вместо:

# Наследование может привести к allOf в JSON Schema

class Finding(AnimalBase):
    type: Literal["finding"]
    severity: str  # нет ограничения

Лучше:

# Плоская модель без allOf

class Finding(BaseModel):
    model_config = ConfigDict(extra="forbid")
    reasoning: str

    type: Literal["finding"]
    severity: Literal["low", "medium", "high"]

Порядок полей здесь тоже важен. Если поле reasoning стоит перед итоговым решением, модель сначала фиксирует промежуточное объяснение, а затем выбирает ограниченное значение. Это не заменяет валидацию, но делает структуру ответа более предсказуемой.

Про allOf и наследование

allOf не сработал ни у одного из тестированных провайдеров. OpenAI возвращает 400, Gemini и xAI возвращают пустой объект {}.

Это значит, что если вы используете наследование Pydantic-моделей:

class Base(BaseModel):
  id: int

class Child(Base):
    label: str

Схема для Child может содержать allOf: [Base, {properties: {label: ...}}]. Для Structured Outputs надёжнее явно перечислить все поля в одной модели.


Несколько практических выводов

Для OpenAI нужно включать strict: true. Без этого протестированные ограничения не применялись. При этом oneOf и allOf с strict=True приводят к 400 ошибке.

Gemini принимает числовые и строковые bounds, но не применяет их при генерации. minLength, maxLength, exclusiveMinimum, multipleOf, pattern нужно проверять в своём коде.

xAI применял большинство проверенных ограничений, но allOf также не сработал.

allOf / наследование лучше не использовать для Structured Outputs. Практичнее делать плоскую схему без наследования.

Если приложение работает с несколькими провайдерами, одной схемы может быть недостаточно. Разные провайдеры по-разному применяют одни и те же JSON Schema keywords. Практичнее держать каноническую смысловую модель и отдельные provider-specific варианты схем, а затем тестировать каждую из них на реальных эндпоинтах.

Всегда валидируйте результат на своей стороне. Даже когда провайдер применяет ограничения схемы, это относится только к успешным генерациям. Refusal, incomplete response и truncation нужно обрабатывать отдельно.

Skill для кодовых агентов

Во время работы с Structured Outputs я столкнулся с отдельной практической проблемой: кодовые агенты часто не знают, как эта функция устроена в API. На просьбу «напиши код вызова, чтобы модель вернула JSON по схеме» агент может просто положить JSON Schema в текст промпта и не использовать response_format или аналогичные параметры провайдера. Такой код выглядит правдоподобно, но не даёт гарантий формата. Даже когда агент использует нужный API, он может предложить схему, которая плохо подходит под конкретного провайдера: где-то забудет strict: true, где-то использует allOf, где-то положится на pattern в Gemini.

Сначала это приходилось каждый раз объяснять в промпте. В итоге я вынес правила, provider-specific caveats и практические выводы из этого исследования в отдельный skill: [schema-guided-reasoning-pydantic].

После установки skill'а Codex, Claude Code и Cursor лучше понимают, какие схемы предлагать для OpenAI, Gemini и xAI, когда нужна отдельная provider-specific схема, и какие ограничения обязательно проверять на практике. Все основные выводы из этой статьи также лежат в этом репозитории.

Предупреждение о свежести данных

Это срез состояния на май 2026 года, модели gpt-4o-mini, gemini-2.0-flash, grok-3-mini. Провайдеры могут менять реализацию Structured Outputs без отдельного объявления. Если для вашей задачи гарантия критична, проверьте своё конкретное ограничение adversarial-тестом перед тем как на него положиться. Минимальный тест выглядит так: схема с нужным ограничением + промпт с явной просьбой нарушить его + несколько прогонов. Если хотя бы один ответ нарушил схему, это ограничение нельзя считать надёжным без дополнительной валидации.