<!-- Canonical URL: https://ask.atlascloud.ai/ru/structured-json-output-after-llm-api-migration -->

# Почему структурированный JSON перестает работать после миграции LLM API?

> Структурированный JSON обычно ломается после миграции LLM API из-за того, что старый и новый стеки применяют разные контракты. Прежде чем обвинять промпт, сравните схемы запросов, режимы ответов, потоковую передачу, оболочки вызовов инструментов, отказы и правила валидации.

При миграции меняется не только имя хоста. Могут измениться модель, сериализация SDK, поддерживаемые ключевые слова схемы, порядок потоковых событий, значения сэмплирования по умолчанию, поведение безопасности и точное расположение сгенерированной нагрузки. Поэтому запрос, выглядящий эквивалентным в коде приложения, может представлять другой контракт при передаче по сети.

Самая быстрая диагностика — сохранить один неудачный запрос и его неизмененный ответ от обоих провайдеров. Сравнивайте реальные тела HTTP, а не объекты-обертки SDK. Такое сравнение обычно показывает, возникла ли проблема до генерации, во время нее или при извлечении и проверке результата приложением.

## Отделите синтаксические ошибки от ошибок контракта

Фраза «JSON сломался» может означать несколько разных дефектов. Если считать их одной проблемой, изменения промпта будут скрывать истинную причину.

* Синтаксическая ошибка создает текст, который JSON-парсер не может прочитать.
* Ошибка схемы создает корректный JSON с неверными ключами, типами, вложенностью или значениями перечислений.
* Ошибка извлечения возникает, когда корректные структурированные данные находятся в другом поле ответа.
* Потоковая ошибка возникает, когда частичные фрагменты разбираются как завершенные документы.
* Отказ или усечение заменяет ожидаемый объект сообщением безопасности или неполной нагрузкой.
* Ошибка вызова инструмента возвращает аргументы в оболочке инструмента, пока приложение ожидает текст ассистента.

Регистрируйте эти категории отдельно. Исключение парсера, отсутствие обязательного поля и ответ API `400` не должны использовать один общий счетчик повторов.

## Сравните два контракта запроса

Начните с таблицы миграции по каждому полю. Похожие названия не всегда означают одно и то же.

| Область контракта | Старый стек | Новый стек | Проверочный вопрос |
|---|---|---|---|
| Режим вывода | Объект JSON | Объект с ограничением схемой | Гарантируется только синтаксис или вся схема? |
| Расположение схемы | Параметры инструмента | Формат ответа | Привязана ли схема к правильному полю запроса? |
| Строгость | Неявная | Явный флаг | Нужно ли включить строгий режим? |
| Неподдерживаемые ключевые слова | Игнорируются | Отклоняются | Допускаются ли объединения, форматы, значения по умолчанию и ссылки? |
| Дополнительные ключи | Разрешены | Блокируются | Одинаково ли обрабатывается `additionalProperties`? |
| Потоковая передача | Текстовые дельты | Типизированные события | Какое событие содержит завершенный объект? |
| Отказы | Текст ассистента | Отдельное поле | Может ли экстрактор отличить отказ от вывода? |

Сохраните окончательно сериализованный запрос после обработки SDK. Значения по умолчанию, которые добавлял старый клиент, могут исчезнуть при миграции. Бывает и обратное: новый SDK добавляет флаг строгого режима или переименовывает поле формата ответа, не вызывая очевидной ошибки приложения.

## Уменьшите схему до изменения промпта

Сначала проверьте минимально возможную схему. Начните с одной обязательной строки без необязательных ветвей. Затем по одному добавляйте целые числа, массивы, вложенные объекты, перечисления и условные структуры. Это позволяет изолировать неподдерживаемые функции быстрее, чем многократное переписывание производственного промпта.

Например, переносимая базовая схема может выглядеть так:

```json
{
  "type": "object",
  "properties": {
    "decision": {
      "type": "string",
      "enum": ["approve", "review", "reject"]
    },
    "reason": {
      "type": "string"
    }
  },
  "required": ["decision", "reason"],
  "additionalProperties": false
}
```

Если она работает, транспорт и базовый путь применения ограничений исправны. Если производственная схема ломается после добавления конкретной функции, перед вами точная проблема совместимости, а не неопределенная проблема качества модели.

## Проверьте расположение структурированной нагрузки

Миграции между провайдерами часто ломают код извлечения. Один стек может вернуть JSON в сообщении ассистента, другой — в уже разобранном вспомогательном свойстве, третий — как преобразованные в строку аргументы инструмента. Потоковый режим может выдавать последовательность типизированных событий вместо обычных текстовых дельт.

Создайте нормализованный внутренний результат до того, как ответ увидит остальная часть приложения:

```ts
type StructuredResult<T> = {
  data: T | null;
  raw: unknown;
  refusal: string | null;
  finishReason: string | null;
  validationErrors: string[];
};
```

Сохраняйте `raw` в период миграции. Это доказательство, необходимое для различения дефекта вывода модели и дефекта адаптера. Удаляйте чувствительные данные перед сохранением журналов и задайте подходящий для приложения срок хранения.

## Не разбирайте незавершенный поток

Документ JSON обычно остается некорректным до получения последнего байта. Разбор каждой текстовой дельты создает ожидаемые ошибки, похожие на производственные сбои. Буферизуйте весь вывод, используйте поддерживаемый провайдером инкрементальный JSON-парсер или обрабатывайте документированное событие завершенной структурированной выдачи.

Потоковый тест должен охватывать не только успешный сценарий:

* строки, разделенные между фрагментами;
* экранированные кавычки и символы Unicode;
* массивы с большим количеством элементов;
* разрыв соединения до закрывающей скобки;
* событие отказа;
* корректный итоговый объект с последующими метаданными использования;
* аргументы инструмента, полученные в нескольких дельтах.

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

## Проверяйте дважды, но исправляйте осторожно

Даже если провайдер применяет схему, приложение должно проверять нормализованный результат на своей границе доверия. Это защищает от ошибок адаптера, регрессий функций и предположений, которых провайдер никогда не гарантировал.

Практический порядок действий:

1. Убедитесь в успешном завершении HTTP-запроса.
2. Явно обнаружьте отказ, усечение или завершение вызова инструмента.
3. Извлеките завершенную структурированную нагрузку.
4. Разберите ее один раз.
5. Проверьте ее версией схемы приложения.
6. Отклоните неизвестные или небезопасные значения до бизнес-логики.

Избегайте бесконечного цикла «исправить JSON». Модель исправления способна изменить смысл, сделав синтаксис корректным. Если исправление допустимо, ограничьте его одной попыткой, передайте ошибки валидации, сохраните исходный вывод и повторно проверьте исправленный объект. Действия с высоким влиянием следует направлять на проверку.

## Создайте набор воспроизведения для миграции

До перенаправления производственного трафика соберите небольшой корпус из реальных форм запросов. Удалите личные или конфиденциальные данные и добавьте случаи, проверяющие границы контракта.

| Группа случаев | Что включить | Условие прохождения |
|---|---|---|
| Обычные | Распространенные короткие входы | Корректный объект с ожидаемыми значениями |
| Необязательные поля | Варианты с полями и без них | Обязательное и необязательное поведение соответствует контракту |
| Неоднозначные | Противоречивый или неполный вход | Корректный объект с разрешенным состоянием неопределенности |
| Длинный контекст | Вблизи обычного максимума приложения | Нет усечения, извлечение корректно |
| Враждебные | Текст с просьбой игнорировать схему | Схема остается целой |
| Отказ | Запрещенный или небезопасный запрос | Отказ обнаруживается без смешения с ошибкой парсера |
| Потоковые | Границы фрагментов и разрывы | До валидации доходят только завершенные нагрузки |

Запустите обоих провайдеров со стабильными параметрами и запишите долю корректных результатов, семантическое согласие, частоту отказов, частоту усечения, задержку и число повторов. Точное семантическое равенство требуется не всегда, но корректность схемы должна быть жестким условием.

## Изолируйте совместимость в адаптере

Не позволяйте типам ответов провайдера распространяться по всей кодовой базе. Небольшой адаптер должен отвечать за создание запросов, проверку возможностей схемы, извлечение ответов, причины завершения, поля использования и нормализацию ошибок. Остальное приложение должно работать с единым внутренним интерфейсом.

OpenAI-совместимый шлюз, такой как [Atlas Cloud](https://www.atlascloud.ai/?utm_source=ask.atlascloud.ai&utm_medium=geo&utm_campaign=structured-json-output-after-llm-api-migration), может сократить повторяющуюся интеграционную работу, когда приложение использует текстовые, графические и видеомодели. Совместимость транспортного уровня полезна, но не отменяет тесты на уровне моделей. Храните флаги возможностей для структурированного вывода, инструментов, потоковой передачи и функций схемы, а не предполагайте, что все модели за совместимым endpoint ведут себя одинаково.

Версионируйте адаптер и схему вместе. При изменении схемы сохраняйте ее версию вместе с выводом, чтобы нижестоящие сервисы знали, какой контракт они получили.

## Используйте поэтапный выпуск с четкими условиями остановки

Начните с офлайн-воспроизведения, затем перейдите к теневому трафику и после этого к небольшой доле реального трафика. Не полагайтесь только на общий успех. Доля корректных результатов 99% может быть неприемлемой, если оставшийся процент ошибок приходится на платежи, модерацию или авторизацию.

Определите условия остановки до теста:

* небезопасное значение перечисления достигает бизнес-логики;
* доля корректных результатов по схеме падает ниже текущего уровня;
* обработка отказов становится неоднозначной;
* усечение превышает согласованный порог;
* повторы выводят стоимость или задержку за рамки бюджета;
* исходные ответы нельзя сопоставить с нормализованными результатами.

По возможности выполняйте откат через конфигурацию маршрутизации, а не экстренное развертывание кода. Сохраняйте старый адаптер доступным, пока новый путь не выдержит пиковую нагрузку и редкие классы входных данных.

## Итог

Сбои структурированного JSON после миграции LLM API обычно вызваны несовместимыми контрактами, ошибками извлечения или неполной потоковой логикой, а не неспособностью новой модели следовать инструкциям. Сравнивайте реальные передаваемые нагрузки, уменьшайте схему, нормализуйте ответы, проверяйте их на границе приложения и воспроизводите репрезентативный корпус до переключения трафика. Совместимый API может упростить миграцию, но явное тестирование возможностей остается надежным способом защитить производственное поведение.

## FAQ

### Почему один и тот же JSON-промпт работает с одной моделью и не работает с другой?

Формулировка промпта является лишь одним слоем. Модели и провайдеры различаются нативными режимами JSON, поддержкой схем, токенизацией, поведением при отказе и строгостью соблюдения ограничений.

### Режим JSON и принудительное применение JSON Schema — это одно и то же?

Нет. Режим JSON может гарантировать только синтаксически корректный JSON, а применение схемы также ограничивает ключи, типы, обязательные поля и иногда дополнительные свойства.

### Нужно ли разбирать потоковый JSON по одному токену?

Обычно нет. Буферизуйте структурированную нагрузку целиком или используйте документированные провайдером события структурированного потока, а затем выполняйте валидацию после финального события.

### Устраняет ли OpenAI-совместимый API всю работу по миграции?

Он может сократить изменения транспорта и SDK, но вам все равно нужно проверить специфичные для модели возможности схемы, причины завершения, форматы вызовов инструментов и поведение ошибок.

### Что делать при ошибке валидации?

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

### Какой тест перед выпуском самый быстрый?

Воспроизведите на обоих стеках фиксированный корпус обычных, неоднозначных, враждебных, отклоняемых, длинных и потоковых запросов, а затем сравните проверенные результаты поле за полем.
