Structured Outputs
Структурированный вывод ограничивает ответы модели ИИ схемой, например JSON Schema, во время декодирования, чтобы приложения могли надёжно разбирать каждый ответ.
Структурированные выходные данные — это ответы модели, ограниченные заранее заданным форматом, обычно схемой JSON, в которой указаны обязательные ключи, их типы и допустимые значения. Благодаря этому программа может обработать любой ответ без дополнительной очистки. Большая языковая модель (LLM), которую вежливо попросили вернуть JSON, обычно выполняет просьбу, но все же может добавить предложение обычного текста, допустить ошибку в ключе или оборвать ответ на полуслове. Любая из этих ошибок ломает программу, которая читает ответ. Структурированные выходные данные устраняют этот риск с помощью ограниченного декодирования: на каждом шаге генерации система инференса разрешает только те токены, которые сохраняют соответствие ответа схеме. Когда OpenAI добавила эту функцию в свой API в августе 2024 года, компания сообщила, что gpt-4o-2024-08-06 со структурированными выходными данными набрала 100% в тесте на соблюдение сложных схем JSON, тогда как GPT-4 (gpt-4-0613) без этой функции показала результат ниже 40%.
Режим JSON и структурированные выходные данные#
API провайдеров предлагают два уровня гарантий. Режим JSON, представленный OpenAI в ноябре 2023 года, гарантирует только синтаксически корректный JSON, поэтому ключи по-прежнему могут отсутствовать или называться неправильно. Структурированные выходные данные гарантируют соответствие ответа предоставленной схеме. В API Structured Outputs OpenAI разработчики задают схему в виде модели Pydantic на Python или объекта Zod на JavaScript, а отказ возвращается в отдельном поле и не нарушает формат. Структурированные выходные данные Claude от Anthropic преобразуют каждую схему в грамматику, что увеличивает задержку при первом использовании схемы; затем скомпилированная грамматика кэшируется на 24 часа. Оба провайдера поддерживают только подмножество JSON Schema. Например, схема Anthropic не поддерживает рекурсивные схемы и числовые ограничения, такие как minimum.
Как работает декодирование с ограничениями#
На каждом шаге LLM вычисляет оценку, или логит, для каждого токена в своем словаре, а декодирование выбирает токен из полученного распределения softmax. Сначала ограниченное декодирование присваивает минус бесконечность оценке каждого токена, который нарушил бы формат, поэтому такие токены невозможно выбрать. Brandon Willard и Rémi Louf показали, как эффективно реализовать этот подход в статье Efficient Guided Generation for Large Language Models: регулярное выражение или схема JSON преобразуется в конечный автомат, а построенный заранее индекс перечисляет допустимые токены для каждого состояния, поэтому на каждом шаге достаточно выполнить поиск в таблице. Метод реализован в библиотеке с открытым исходным кодом Outlines, а среды для обслуживания, такие как vLLM, предлагают генерацию с ограничениями по грамматике для моделей с открытыми весами.
В следующем примере эта идея вручную применяется к GPT-2 из библиотеки Transformers от Hugging Face. Фиксированный текст схемы — фигурные скобки, ключи и кавычки — записывается программно, а для каждого значения модель может выбирать только из разрешенных схемой токенов:
import json
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("gpt2")
model = AutoModelForCausalLM.from_pretrained("gpt2")
prompt = "Review: The battery died after two days and support never replied.\nJSON:"
# Жадное декодирование без ограничений: ничто не заставляет ответ соответствовать схеме
ids = tokenizer(prompt, return_tensors="pt").input_ids
free = model.generate(ids, max_new_tokens=16, do_sample=False, pad_token_id=tokenizer.eos_token_id)
print("free:", repr(tokenizer.decode(free[0, ids.shape[1] :])))
def choose(text, options):
"""Return the option GPT-2 scores highest after text, with every other token masked out."""
logits = model(tokenizer(text, return_tensors="pt").input_ids).logits[0, -1]
allowed = torch.tensor([tokenizer.encode(option)[0] for option in options])
masked = torch.full_like(logits, float("-inf"))
masked[allowed] = logits[allowed]
return tokenizer.decode(masked.argmax())
# Ограниченное декодирование: схема задает ключи, а модель может выбирать только допустимые значения
with torch.no_grad():
text = prompt + ' {"sentiment": "'
text += choose(text, ["positive", "negative"]) + '", "stars": '
text += choose(text, ["1", "2", "3", "4", "5"]) + "}"
print("constrained:", json.loads(text[len(prompt) :]))Программа выводит free: ' The battery died after two days and support never replied.\n\nJSON: The' и constrained: {'sentiment': 'positive', 'stars': 1}. Без ограничений GPT-2 повторяет отзыв вместо того, чтобы сформировать JSON, а ограниченная версия всегда выдает данные, которые можно разобрать. Результат после разбора также показывает ограничение этого метода: модель с 124 миллионами параметров оценила разряженную батарею и молчащую службу поддержки положительно, поставив одну звезду. Схема исправила формат, но значения внутри нее по-прежнему отражали собственную оценку модели.
Применение и компромиссы#
- Извлечение данных: Извлечение имен, дат, итоговых сумм и отдельных позиций из счетов, договоров или электронных писем непосредственно в строки базы данных.
- Вызов функций: Вызов инструмента — это структурированный выходной результат, схема которого задает параметры инструмента. Строгий вызов инструментов в API OpenAI и Anthropic обеспечивает такую же гарантию для аргументов инструментов.
- Агентные рабочие процессы: Представление решений о маршрутизации в виде значений перечисления и передача промежуточных результатов между этапами цепочки промптов в виде типизированных объектов.
- Мультимодальные модели «зрение — язык»: Возврат обнаруженных объектов в виде списков меток и координат рамок, которые можно напрямую передать коду отрисовки или оценки.
Умная разметка по названиям классов в Ultralytics Platform зависит от последнего типа выходных данных. Она просит визуальные модели таких поставщиков, как OpenAI, Anthropic и Google, обнаружить классы набора данных и добавляет каждый найденный объект как редактируемый ограничивающий бокс. Поэтому каждый ответ должен указывать класс и координаты в формате, который может обработать код. Эти модели не возвращают оценки уверенности, поэтому Platform скрывает настройки уверенности и IoU, пока выбрана такая модель. Таблица лидеров VLM сравнивает точность обнаружения объектов этими моделями в реальных наборах данных, ранжируя боксы каждой модели по порядку их перечисления.
Строгие форматы могут снижать точность при решении задач на рассуждение. В работе Let Me Speak Freely? Zhi Rui Tam и его коллеги обнаружили значительное ухудшение рассуждений LLM при ограничении формата; чем строже ограничения, тем сильнее снижение. API OpenAI генерирует ключи в порядке, заданном схемой, поэтому распространенный способ обойти это — поставить перед полем ответа поле со свободным текстом для рассуждений, чтобы модель записала свою цепочку мыслей до формирования ответа, ограниченного схемой.










