Menu

Структурированный вывод: надёжный JSON от LLM

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

Каждый промпт на этой странице можно изменить и открыть в ChatGPT, Claude или другом ИИ-приложении.

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

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

Выдели ключевые детали из этого баг-репорта: "После вчерашнего обновления приложение падает, когда я нажимаю «Экспорт» в проекте, где больше 50 фотографий. Проекты поменьше экспортируются нормально. У меня Android 14, версия приложения 3.2.0. Из-за этого я не могу сдать работу клиенту."
Try it
Example replyReplies vary between models and runs.

Вот ключевые детали из баг-репорта:

  • Проблема: приложение падает при экспорте проектов
  • Условие: нажатие «Экспорт» в проектах, где больше 50 фотографий
  • Началось: после последнего обновления
  • Платформа: Android 14
  • Версия приложения: 3.2.0
  • Влияние: высокое, так как блокирует сдачу работы клиенту

Проекты поменьше, судя по всему, экспортируются без проблем.

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

Как просить JSON

Хороший запрос JSON отвечает на каждый вопрос, на который иначе ответила бы за вас модель:

  • Каждый ключ в точном написании. Пишите имена ключей в кавычках так, как они должны выглядеть. Говорите «ровно такие ключи», чтобы модель не добавила лишних.
  • Тип каждого значения. Строка, число, логическое значение, массив строк, вложенный объект.
  • Допустимые значения для любого поля с фиксированным набором, например серьёзность или категория. Без списка за четыре запуска вы получите "High", "high", "severe" и "P1".
  • Что делать, когда во входных данных нет значения. Если не сказать «null, если не указано», модель обычно заполняет пробел правдоподобной догадкой, а выдуманная версия приложения выглядит точно так же, как настоящая.
  • Ничего вокруг. «Верни только JSON, без текста до или после него» убирает дружелюбную вводную фразу.

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

Промпт для извлечения данных, который можно переиспользовать

В этом блоке тот же запрос разбит на части. Отключите часть с форматом, и модель всё равно вернёт JSON, потому что его требуют ограничения, но выберет свои имена ключей, например title вместо job_title, и код, который ждёт ваши ключи, сломается. Часть с ограничениями не даёт ей дописать недописанный номер телефона или вывести компанию из домена почты. Вставьте в поле ввода настоящую подпись, чтобы проверить.

Извлечь контакты в JSON
Fill in
Parts
Извлеки контактные данные из подписи письма ниже.
Верни JSON-объект ровно с такими ключами: { "name": "строка", "job_title": "строка или null", "company": "строка или null", "email": "строка или null", "phone": "строка или null" }
Используй null для любого значения, которого нет в тексте. Не угадывай и не дописывай неполные значения. Верни только JSON.
Прия Наир | Руководитель отдела данных, Northwind Labs | priya.nair@northwind.example
Try it
Example replyReplies vary between models and runs.
{
  "name": "Прия Наир",
  "job_title": "Руководитель отдела данных",
  "company": "Northwind Labs",
  "email": "priya.nair@northwind.example",
  "phone": null
}

Таблицы и фиксированные шаблоны

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

Таблица
Сравни списки, кортежи и множества в Python в таблице markdown с такими столбцами: Тип, Упорядочен, Изменяемый, Допускает дубликаты, Типичное применение. Одна строка на тип. Никакого текста вне таблицы.
Try it
Example replyReplies vary between models and runs.
ТипУпорядоченИзменяемыйДопускает дубликатыТипичное применение
listДаДаДаПоследовательность, в которую добавляют, из которой удаляют или которую сортируют
tupleДаНетДаФиксированная группа значений, например координаты
setНетДаНетУдаление дубликатов и быстрая проверка вхождения

Шаблон работает так же для длинного текста: дайте заголовки по порядку и скажите, что идёт под каждым. «Ответь тремя жирными подзаголовками: Причина, Исправление, Как проверить» даёт одни и те же три подзаголовка каждый раз, и пачку ответов легко сравнивать.

JSON mode в API

У нескольких API моделей есть настройка, которая принуждает к синтаксически корректному JSON. В Python SDK от OpenAI это response_format. JSON mode требует, чтобы слово "JSON" встречалось где-то в ваших сообщениях, поэтому системный промпт ниже его называет и перечисляет ключи.

import json
from openai import OpenAI

client = OpenAI()
MODEL = "your-model-id"  # e.g. from your provider's model list

report_text = "Since yesterday's update the app crashes when I tap Export..."

response = client.chat.completions.create(
    model=MODEL,
    response_format={"type": "json_object"},
    messages=[
        {
            "role": "system",
            "content": (
                "Extract the bug report into JSON with the keys "
                "summary (string), severity (one of low, medium, high, critical) "
                "and steps_to_reproduce (array of strings)."
            ),
        },
        {"role": "user", "content": report_text},
    ],
)

data = json.loads(response.choices[0].message.content)

JSON mode гарантирует, что текст разбирается (если ответ не обрезан на лимите токенов), но не то, что он соответствует вашей схеме. Ключ всё ещё может отсутствовать, а серьёзность всё ещё может оказаться "urgent". Несколько провайдеров также принимают полную JSON Schema, как формат вывода или как входную схему определения инструмента (функции), и некоторые из этих режимов ограничивают ответ схемой. Точный параметр смотрите в документации своего провайдера: эти функции отличаются от API к API.

Проверяйте результат в коде

Относитесь к JSON от модели так же, как к любым внешним входным данным вашей программы: разберите, затем проверьте. Разбор ловит сломанный синтаксис. Проверка ловит корректный объект с неверным содержимым.

ALLOWED_SEVERITIES = {"low", "medium", "high", "critical"}

def problems(data):
    if not isinstance(data, dict):
        return ["the answer must be a JSON object"]
    found = []
    if not isinstance(data.get("summary"), str):
        found.append("summary must be a string")
    if data.get("severity") not in ALLOWED_SEVERITIES:
        found.append("severity must be low, medium, high or critical")
    steps = data.get("steps_to_reproduce")
    if not isinstance(steps, list) or not all(isinstance(s, str) for s in steps):
        found.append("steps_to_reproduce must be an array of strings")
    return found

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

Ещё две привычки предотвращают тихие ошибки. Ставьте лимит выходных токенов достаточно высоким для самого большого ожидаемого ответа, потому что обрезанный объект никогда не разберётся. А когда входные данные длинные или приходят от пользователей, отделяйте их от инструкций разделителями или XML-тегами, чтобы текст внутри реже принимался за инструкцию. Если одному промпту нужно и рассуждать, и выдавать JSON, подумайте о цепочке промптов: пусть один шаг думает свободным текстом, а второй превращает результат в структуру.

Часто задаваемые вопросы

Как заставить ChatGPT отвечать в JSON?

Скажите, что ответ должен быть в JSON, перечислите все ключи с их типами и дайте допустимые значения для каждого поля с фиксированным набором. Добавьте «Верни только JSON, без текста до или после него». В API также включите JSON mode через response_format={"type": "json_object"}, для этого слово JSON должно встречаться в ваших сообщениях.

Почему модель добавляет текст вокруг JSON?

Чат-модели обучены вести разговор, поэтому часто начинают с фразы вроде «Вот JSON» или оборачивают объект в блок кода markdown. Просите только JSON, а в коде либо используйте JSON mode в API, либо убирайте окружающий блок кода перед разбором.

Что такое JSON mode?

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

Может ли LLM всегда возвращать корректный JSON?

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

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ