РуководстваОпубликовано: 07.09.20260 просмотров

Как добиться стабильного JSON-ответа от AI API: структурированный вывод, Schema и обработка ошибок (2026)

JSON mode, JSON Schema и проверка на стороне клиента: практическая инструкция со snippets для Python и Node.js, обработкой SSE, повторными запросами и защитой от невалидных ответов.

Как добиться стабильного JSON-ответа от AI API: структурированный вывод, Schema и обработка ошибок (2026)

Короткий ответ: одной фразы «верни JSON» в промпте недостаточно. Надёжная интеграция сочетает три уровня: параметр структурированного вывода, чёткое описание полей в промпте и строгую проверку ответа на стороне клиента. Тогда лишний комментарий модели или изменение маршрута не сломает ваш код.


Почему «похожий на JSON» ответ не подходит программе

Модель может добавить Markdown-ограждение, вступительную фразу, пропустить кавычку или вернуть число строкой. Для человека такой ответ понятен, но JSON.parse() его не примет. В потоковом режиме проблема ещё заметнее: до события завершения вы получили только часть объекта.

Поэтому нужно отдельно проверять синтаксис JSON и соответствие бизнес-схеме. Валидный JSON всё ещё может содержать неправильные поля.

Сначала проверьте возможности провайдера

У OpenAI-совместимых сервисов набор параметров различается:

  • JSON mode обычно гарантирует валидный JSON, но не наличие всех полей.
  • JSON Schema / Structured Outputs дополнительно ограничивает структуру объекта.
  • При отсутствии этих режимов остаётся промпт и проверка в приложении.

Не считайте параметр поддерживаемым только потому, что endpoint называется OpenAI-compatible. Ориентируйтесь на документацию и реальный ответ. Ошибка 400 invalid parameter означает, что несовместимый параметр нужно убрать, а не повторять тот же запрос.

Проектируйте небольшую и однозначную схему

Допустим, нужно классифицировать обращение в поддержку:

{
  "category": "billing",
  "priority": "high",
  "confidence": 0.92,
  "needs_human": false
}

Практические правила:

  1. Используйте стабильные имена полей на английском, а текст переводите на уровне интерфейса.
  2. Ограничивайте варианты через перечисление (enum), чтобы не получать синонимы.
  3. Явно различайте числа, строки, массивы и Boolean.
  4. Не добавляйте в Schema поля, которые не нужны бизнес-логике.
  5. Если нужен строгий контракт, запретите дополнительные поля.

Пример системной инструкции:

Ты классификатор обращений. Верни только один JSON-объект без Markdown и пояснений.
category: только billing, technical или account;
priority: только low, normal или high;
confidence: число от 0 до 1;
needs_human: Boolean.

Промпт снижает вероятность ошибки, но не заменяет валидатор.

Python: запрос и проверка полей

import json
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["AI_API_KEY"],
    base_url=os.environ["AI_BASE_URL"],
)

response = client.chat.completions.create(
    model=os.environ["AI_MODEL"],
    temperature=0,
    messages=[
        {"role": "system", "content": (
            "Верни только JSON. category: billing, technical или account; "
            "priority: low, normal или high; confidence: число 0..1; "
            "needs_human: Boolean."
        )},
        {"role": "user", "content": "Пользователь не может пополнить баланс: платёж отклонён."},
    ],
    # Оставляйте параметр только если провайдер его поддерживает
    response_format={"type": "json_object"},
)

raw = response.choices[0].message.content or ""
try:
    data = json.loads(raw)
except json.JSONDecodeError as exc:
    raise RuntimeError(f"Ответ не является корректным JSON: {raw[:200]}") from exc

if (
    not isinstance(data, dict)
    or data.get("category") not in {"billing", "technical", "account"}
    or data.get("priority") not in {"low", "normal", "high"}
    or not isinstance(data.get("confidence"), (int, float))
    or not 0 <= data["confidence"] <= 1
    or not isinstance(data.get("needs_human"), bool)
):
    raise RuntimeError(f"Поля ответа не соответствуют контракту: {data}")

В рабочем проекте лучше использовать Pydantic или JSON Schema. Регулярные выражения не подходят для проверки вложенных объектов.

Node.js: парсинг без опасной «чистки» строки

import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.AI_API_KEY,
  baseURL: process.env.AI_BASE_URL,
});

const completion = await client.chat.completions.create({
  model: process.env.AI_MODEL,
  temperature: 0,
  messages: [
    { role: "system", content: "Верни только JSON без Markdown и пояснений." },
    { role: "user", content: "Классифицируй отзыв: платёж не проходит при пополнении." },
  ],
  response_format: { type: "json_object" },
});

const raw = completion.choices?.[0]?.message?.content ?? "";
let result;
try {
  result = JSON.parse(raw);
} catch {
  throw new Error(`Ответ не является JSON: ${raw.slice(0, 200)}`);
}

if (typeof result.category !== "string" || typeof result.needs_human !== "boolean") {
  throw new Error("В ответе отсутствует обязательное поле или неверный тип");
}

Не полагайтесь только на replace("```json", ""): это не исправляет обрезанный ответ и неправильные типы. Если нужно поддержать старую модель, можно аккуратно удалить кодовое ограждение, но после этого всё равно обязательны JSON.parse() и проверка Schema.

Как повторять запрос после ошибки

Повторяйте только исправимые ошибки:

  1. Записывайте request ID, модель, HTTP-код и первые 200 символов ответа с удалением чувствительных данных. API Key в логах быть не должно.
  2. Для тайм-аута и 5xx используйте экспоненциальную задержку, например 1, 2 и 4 секунды, с ограниченным числом попыток.
  3. При HTTP 200 и невалидном JSON можно один раз отправить модели краткое сообщение об ошибке и попросить исправить формат.
  4. При 400 из-за неподдерживаемого параметра удалите response_format или Schema и повторите совместимый вариант.
  5. После исчерпания попыток передайте задачу в ручную обработку. Не записывайте обрезанный объект в базу.

Потоковый ответ разбирайте только после завершения

В каждом событии SSE может находиться лишь часть JSON. Сначала соберите весь текст и дождитесь события завершения, затем выполните JSON.parse(). Если пользователю нужно показывать текст сразу, отделите поток предпросмотра от итогового структурированного результата. О сбоях SSE подробно рассказано в руководстве по прерыванию потокового вывода AI API.

Чек-лист перед публикацией

  • Проверена поддержка response_format или Schema у провайдера
  • Протестированы обычные, пограничные и пустые входные данные
  • Проверены обязательные поля, перечисления и типы
  • Раздельно обработаны timeout, 429, 5xx и невалидный JSON
  • В логах есть request ID, но нет ключей и чувствительного содержимого
  • Неполный ответ никогда не записывается в рабочую базу
  • После смены модели или маршрута запущены регрессионные тесты

Итог: параметр структурированного вывода задаёт направление, Schema решает, принимать ли результат, а повтор и запасной сценарий сохраняют работоспособность сервиса. Только сочетание всех трёх уровней даёт поддерживаемую интеграцию JSON API.

Полезные материалы:

Дата обновления: 7 сентября 2026 года

Опубликовано: 7 сентября 2026 г.
最后更新: 07.09.2026

Похожие статьи

Отказ от ответственности

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

© 2026 Выбор API. All rights reserved.