Структурированный вывод — это когда LLM возвращает не свободный текст, а строгий JSON, XML или другую схему. Без него невозможно парсить ответы AI в коде, интегрировать LLM в пайплайны и строить надёжные AI-агенты. Четыре основные библиотеки — Instructor (Pydantic + retry), Outlines (constrained decoding на GPU), Guidance (шаблоны с контролем) и JSONFormer (лёгкий FSM) — решают эту задачу по-разному.

14.3KOutlines ★ на GitHub
13.3KInstructor ★ на GitHub
21.5KGuidance ★ на GitHub
3M+загрузки Instructor/mo

Почему JSON ломается?

LLM по умолчанию генерируют текст. Даже если попросить «верни JSON», модель может: забыть закрыть скобку, написать лишнюю запятую, вставить комментарий //, начать с пояснения «Вот ваш JSON:», завернуть ключи в одинарные кавычки. В 2026 году все топовые модели (GPT-4o, Claude Sonnet 4, Gemini 2.5 Pro, DeepSeek V3) поддерживают JSON mode — но он не гарантирует валидность схемы. Только то, что ответ будет синтаксически корректным JSON.

Проблема становится острой в продакшене: по данным Confident AI, до 15% JSON-ответов от LLM содержат структурные ошибки при сложных схемах с вложенными объектами и массивами. Для AI-агентов, где каждая упавшая парсинга — это потеря транзакции, 15% брака неприемлемы.

Поэтому индустрия пошла двумя путями. Первый — post-hoc валидация с повторными запросами (Instructor). Второй — constrained decoding, когда генерация сразу идёт в рамках заданной грамматики, и кривой JSON просто невозможен физически (Outlines, Guidance).

Pipeline of structured output: from raw text to validated JSON through Pydantic with retry

Pipeline structured output: raw text JSON mode Pydantic validation retry on error

Три способа получить JSON из LLM

Все существующие подходы к структурированному выводу можно разделить на три категории.

1. JSON mode провайдера

OpenAI, Anthropic и Google предоставляют нативный JSON mode через API. Вы передаёте response_format={ "type": "json_object" } (OpenAI) или response_format={ "type": "json_schema", "json_schema": ... } — и модель старается вернуть валидный JSON. Работает неплохо для плоских схем, но на вложенных объектах начинаются сбои. Плюс: ноль зависимостей. Минус: привязанность к провайдеру и отсутствие валидации — если JSON кривой, вы узнаете об этом только в парсере.

2. Post-hoc валидация с retry

Библиотека Instructor делает так: вы описываете Pydantic-модель → LLM генерирует JSON → Pydantic валидирует → если ошибка — повторный запрос с сообщением о проблеме. Просто и эффективно: до 99.8% валидных ответов после 2-3 retry. Работает с любым провайдером, поддерживающим function calling или JSON mode.

3. Constrained decoding (грамматики)

Outlines и Guidance идут дальше: они модифицируют процесс генерации на уровне токенов, «запрещая» модели выдать токен, который нарушит JSON-схему. Результат — гарантированно валидный JSON с первой попытки. Эффективность этого подхода доказана — Outlines использует методику индексирования регулярных выражений через конечные автоматы для работы на GPU (до 1000x быстрее наивной реализации). Минус: работает только для локальных моделей (через transformers или llama.cpp), не через API.

Raw text → JSON mode → JSON (но иногда кривой)
Raw text → Pydantic model → Validated JSON (Instructor)
Raw text → Constrained FSM → Guaranteed structure (Outlines/Guidance)

Instructor: Pydantic-first подход

Instructor (13 384 ★ на GitHub, 3M+ загрузок в месяц) — самая популярная библиотека для структурированного вывода на Python. Её концепция: «patch» для API-клиента, который перехватывает ответ и прогоняет его через Pydantic-валидацию.

pip install instructor  # or: uv add instructor

Minimal example:

import instructor
from pydantic import BaseModel

client = instructor.from_openai(OpenAI())

class UserExtract(BaseModel):
    name: str
    age: int

user = client.chat.completions.create(
    model="gpt-4o",
    response_model=UserExtract,
    messages=[{"role": "user", "content": "Ivan, 28 years"}]
)
print(user.name)  # Ivan
print(user.age)   # 28

Instructor делает три вещи под капотом: передаёт Pydantic-схему через response_format провайдера, получает JSON, валидирует его. Если валидация не прошла — формирует новый запрос с сообщением об ошибке. Поддерживает 15+ провайдеров: OpenAI, Anthropic, Google, Mistral, Ollama, DeepSeek, vLLM.

Ключевые возможности: Streaming-режим (частичные результаты с partial), Iterable (извлечение списков), модульные валидаторы, async-режим, повторные попытки с кастомной логикой. Есть биндинги для TypeScript, Go, Ruby, Elixir и Rust.

Outlines: constrained decoding на уровне токенов

Outlines (14 374 ★) — библиотека от команды .txt (dottxt.co), которую используют NVIDIA, Cohere и HuggingFace. Вместо post-hoc валидации Outlines модифицирует генерацию: на каждом шаге из словаря модели выбираются только те токены, которые соответствуют заданной JSON-схеме, регулярному выражению или контекстно-свободной грамматике.

pip install outlines

Пример с JSON-схемой через Pydantic:

import outlines

class Person(BaseModel):
    name: str
    age: int

model = outlines.from_transformers(...)
result = model.generate(prompt, Person)

Outlines поддерживает: JSON по Pydantic-модели, function calling (из сигнатуры Python), регулярные выражения, грамматики (CFG), множественный выбор (через Literal["Yes", "No"]). Работает с локальными моделями (transformers, llama.cpp) и через серверные API (vLLM, Ollama).

Чем Outlines лучше Instructor: он гарантирует валидность — JSON не может быть кривым, потому что модель физически не может сгенерировать запрещённый токен. Чем хуже: не работает с OpenAI API напрямую (только с open-source моделями, которые вы хостите сами), более сложная установка, требует GPU для приличной скорости.

Guidance: контроль на уровне промпта

Guidance (21 531 ★) от Microsoft — это гибридный подход: вы пишете промпт с встроенными «управляющими конструкциями» {gen}, {select}, {#each}, которые ограничивают генерацию в реальном времени. Guidance не просто парсит ответ — он диктует модели, что и как генерировать, перехватывая каждый шаг.

pip install guidance

Пример извлечения структуры:

import guidance

program = guidance("""
Извлеки информацию о пользователе:
{{#user~}}
Имя: {{gen 'name' stop="\n"}}
Возраст: {{gen 'age' pattern="\\d+"}}
{{~/user}}
""")

result = program()
print(result["name"])  # строка
print(result["age"])   # строка с цифрой

Guidance поддерживает: регулярные выражения, stop-токены, грамматики, role-based шаблоны (system/user/assistant), вложенные циклы, inline-валидацию. Работает с OpenAI API, Anthropic, HuggingFace Transformers, LlamaCpp, vLLM.

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

JSONFormer и constr: лёгкие альтернативы

JSONFormer (4 931 ★) — минималистичная библиотека для constrained decoding. Она работает по тому же принципу, что Outlines (конечный автомат на токенах), но проще — только JSON, без регулярных выражений и грамматик. JSONFormer подходит, когда нужно быстро получить гарантированный JSON без лишних зависимостей, но он медленнее Outlines на больших схемах и не обновлялся с 2024 года.

BAML (BoundaryML, 8 509 ★) — компилируемый подход на Rust: вы описываете схему на специальном языке (похожем на TypeScript), и BAML генерирует код для валидации на Python, TypeScript и Go. В отличие от Instructor, BAML выполняет всю валидацию на Rust — это на порядок быстрее для сложных схем с сотнями полей. Но требует отдельного шага компиляции.

Есть и платформенные решения: vLLM (Outlines встроен нативно), Ollama (JSON mode с Pydantic-схемой), llama-cpp-python (JSON-грамматики через response_format).

Сравнительная таблица

Какую библиотеку выбрать — зависит от вашего сценария. Вот ключевые параметры:

Библиотека Метод ★ GitHub Провайдеры Retry Stream Когда брать
Instructor Post-hoc + retry 13.3K OpenAI, Claude, Ollama, 15+ Да Да API-first, продакшен, любой провайдер
Outlines Constrained FSM 14.3K Локальные (transformers, GGUF) Нет Да Локальные LLM, гарантированная схема
Guidance Шаблоны + контроль 21.5K OpenAI, Claude, HF, vLLM Нет Да Сложные промпты, генерация с остановками
JSONFormer Constrained FSM 4.9K Локальные (HF) Нет Нет Быстрый прототип, минимум зависимостей
BAML Rust-компиляция 8.5K OpenAI, Claude, локальные Да Нет Высокая производительность, сложные схемы

Коротко

Структурированный вывод — не «фича», а необходимость для продакшена. Без него AI-агент, который должен записать данные в CRM или отправить запрос в API, будет ломаться в 10-15% случаев. Instructor даёт 99.8% валидных ответов за счёт retry — берите его для любого API-сценария. Outlines даёт 100% гарантию за счёт constrained decoding — берите для локальных моделей, когда цена ошибки высока. Guidance — если вам нужен тонкий контроль над процессом генерации.

Современная архитектура часто комбинирует оба подхода: Outlines или Guidance для чанков с критической структурой (финансовые данные, JSON для API) и Instructor для менее критичных полей, где допустимо несколько попыток.

Что ещё важно знать

JSON mode провайдера — это то же самое, что Instructor?

Нет. JSON mode (OpenAI, Anthropic, Google) только говорит модели «верни JSON» — но не валидирует схему. Instructor оборачивает JSON mode, добавляет Pydantic-валидацию и retry при ошибках. Без Instructor вы можете получить синтаксически корректный JSON с неверной структурой — например, поле age как строку вместо числа.

Outlines работает с OpenAI API?

Напрямую — нет. Outlines модифицирует процесс генерации на уровне токенов, что возможно только когда вы контролируете инференс (локальная модель через transformers, llama.cpp, vLLM). Для OpenAI API используйте Instructor — он работает через существующие endpoints без доступа к токенам.

Какая библиотека самая быстрая?

Для API-сценариев (OpenAI/Claude) Instructor не добавляет заметной задержки — retry случаются редко. Для локальных моделей Outlines быстрее Guidance на больших схемах, потому что работает на GPU через индексированные конечные автоматы. Guidance медленнее, но даёт больше контроля. BAML — самый быстрый на Rust, но требует компиляции.

Можно ли комбинировать несколько библиотек?

Да. Типичная связка: Outlines для локального препроцессинга (извлечение сущностей с гарантированной структурой) + Instructor для API-запросов (генерация ответов с retry). Guidance можно добавить для сложных мультишаговых промптов — например, когда следующий шаг генерации зависит от предыдущего.

Что выбрать для хакатона/прототипа?

Instructor. pip install instructor, одна строка client = instructor.from_openai(openai_client), и вы уже получаете валидированный JSON. Никаких грамматик, компиляции и GPU — только Pydantic-модель и промпт. Другие библиотеки добавляйте, когда потребуются гарантии или производительность.