Генерация структурированного JSON из LLM: Dottxt Outlines в SageMaker | AiManual
AiManual Logo Ai / Manual.
24 Фев 2026 Гайд

Практическое руководство по генерации структурированного JSON из LLM с помощью Dottxt Outlines в AWS SageMaker

Пошаговая инструкция по интеграции Dottxt Outlines в AWS SageMaker для гарантированно валидного JSON вывода из больших языковых моделей в продакшене.

Почему ваш LLM пайплайн постоянно ломается об JSON

Вы ставите модель в продакшен. Пишете промпт: "Верни JSON с полями name, email, phone". Модель кивает (метафорически) и выдает... почти JSON. Где-то запятая лишняя. Где-то кавычки не те. Иногда модель решает, что вам нужны ещё и "полезные пояснения" поверх самого JSON. Ваш парсер падает, пайплайн останавливается, инженеры ночами пишут костыли на регулярках. Знакомо?

Проблема не в вашем коде. И не в модели. LLM обучены предсказывать следующее слово, а не генерировать синтаксически безупречный JSON. Структурированный вывод — это не их родная стихия. Ручная валидация и постробработка съедают 30% времени разработки и создают хрупкие зависимости.

Попытки "починить" вывод промптами вроде "Только JSON, ничего лишнего" работают в 80% случаев. В продакшене 80% — это гарантированный ежедневный фейл. Методы вроде упомянутого в наших материалах SAE Steering иногда ломают структуру ещё сильнее. Нужен механизм, а не просьбы.

Dottxt Outlines: не проси, а заставь

Dottxt Outlines — это не магический промпт. Это библиотека и подход, которые жёстко ограничивают пространство вывода модели заданной JSON-схемой или грамматикой. Модель физически не может сгенерировать токен, который выведет её за рамки валидного JSON. Всё, что не соответствует схеме, отсекается на уровне генерации.

До 2025 года это требовалось кастомизировать под каждую модель. Теперь решение пришло в AWS Marketplace в виде готового контейнера и моделей, предварительно адаптированных для работы с Outlines. Интеграция с SageMaker стала одноэтапной.

💡
Outlines использует технику "констрейнтивной генерации". Представьте, что у модели есть 50 000 возможных следующих токенов. Outlines в реальном времени отфильтровывает 49 900 из них, оставляя только те, которые ведут к валидному JSON согласно вашей схеме. Модель "выбирает" из разрешённого.

Пошаговый развертывание: от Marketplace до работающего эндпоинта

Забудьте про тонкую настройку моделей с нуля (хотя если интересно, у нас есть отдельный гайд). Мы берём готовые компоненты и собираем работающую систему за час.

1 Находим и подписываемся на модель в AWS Marketplace

Идём в AWS Marketplace и ищем "Dottxt Outlines" или "Structured Output". На февраль 2026 актуальны два основных предложения:

  • Dottxt Outlines Enforcer Container: Контейнер, который можно использовать с любыми совместимыми моделями в SageMaker (Llama 3.2, Mistral-Nemo, и др.).
  • Pre-configured Models with Outlines: Готовые модели (например, на базе Llama 3.2 8B или Command-R 2025), уже оптимизированные для структурированного вывода.

Нажимаем "Subscribe". Внимательно смотрим на ценовую модель: обычно это почасовой платёж за использование инстанса SageMaker плюс возможная плата за модель (если она не открытая).

# После подписки в консоли SageMaker вы увидите новый "Model Package"
# Запишите его ARN. Он понадобится для развертывания.
# Выглядит примерно так:
# arn:aws:sagemaker:us-east-1:123456789012:model-package/dottxt-outlines-llama32/2.0

2 Создаем модель и эндпоинт через SageMaker Console или SDK

Здесь проще использовать Python SDK (boto3 или sagemaker). Консоль скрывает некоторые важные настройки.

import boto3
import sagemaker
from sagemaker import ModelPackage
from sagemaker.predictor import Predictor
import json

# Инициализация сессии SageMaker
sagemaker_session = sagemaker.Session()
role = 'arn:aws:iam::ACCOUNT:role/SageMakerExecutionRole'  # Ваша роль

# Указываем ARN модели из Marketplace
model_package_arn = 'arn:aws:sagemaker:region:account:model-package/your-package'

# Создаём объект модели
model = ModelPackage(
    role=role,
    model_package_arn=model_package_arn,
    sagemaker_session=sagemaker_session,
    # КРИТИЧЕСКИ ВАЖНО: задаём окружение для Outlines
    env={
        'OUTLINES_SCHEMA': '/opt/ml/model/schema.json',  # Путь к схеме в контейнере
        'MAX_OUTPUT_TOKENS': '1024'
    }
)

# Развертываем модель на инстансе (например, ml.g5.xlarge для 7B-10B моделей)
predictor = model.deploy(
    initial_instance_count=1,
    instance_type='ml.g5.xlarge',
    endpoint_name='dottxt-structured-output-endpoint',
    # Новая фигура 2025-2026: асинхронное развертывание с автоскейлингом с нуля
    async_inference_config={
        'ClientConfig': {
            'MaxConcurrentInvocationsPerInstance': 2
        },
        'OutputConfig': {
            'S3OutputPath': 's3://your-bucket/async-output/'
        }
    }
)

Не пытайтесь пропустить шаг с env. Без переменной OUTLINES_SCHEMA контейнер будет работать, но не применять JSON-ограничения. Вы получите обычную, "болтливую" модель, и вся цель теряется.

3 Готовим и загружаем JSON-схему в контейнер

Схема — это сердце системы. Её нужно создать и поместить в правильное место до или во время развертывания.

Способ A (рекомендуется): Использовать S3 и скрипт инициализации модели (model.tar.gz).

# 1. Создаем schema.json
schema = {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "title": "CustomerData",
    "type": "object",
    "properties": {
        "name": {"type": "string", "maxLength": 100},
        "email": {"type": "string", "format": "email"},
        "phone": {"type": "string", "pattern": "^\\+?[1-9]\\d{1,14}$"},
        "status": {"type": "string", "enum": ["lead", "active", "churned"]},
        "value": {"type": "number", "minimum": 0}
    },
    "required": ["name", "email", "status"],
    "additionalProperties": False  # Жесткое ограничение!
}

with open('schema.json', 'w') as f:
    json.dump(schema, f, indent=2)

# 2. Упаковываем в model.tar.gz вместе с (возможно) скриптом inference.py
# Структура архива:
# model.tar.gz
#   |- inference.py
#   |- schema.json
#   |- (optional) code/

# 3. Загружаем в S3
import tarfile
with tarfile.open('model.tar.gz', 'w:gz') as tar:
    tar.add('schema.json', arcname='schema.json')
    # ... добавьте inference.py если требуется кастомная логика

s3_client = boto3.client('s3')
s3_client.upload_file('model.tar.gz', 'your-bucket', 'models/dottxt/model.tar.gz')

# 4. Указываем S3-путь в ModelPackage вместо ARN Marketplace,
# если используете кастомный контейнер с вашей схемой.

Способ B (быстрый старт): Передать схему напрямую в запросе к эндпоинту. Некоторые образы Marketplace поддерживают заголовок X-Outlines-Schema с закодированной в Base64 схемой. Проверяйте документацию конкретного предложения.

4 Делаем запрос и получаем гарантированно валидный JSON

# Инициализируем Predictor к нашему эндпоинту
predictor = Predictor(
    endpoint_name='dottxt-structured-output-endpoint',
    sagemaker_session=sagemaker_session,
    serializer=sagemaker.serializers.JSONSerializer(),
    deserializer=sagemaker.deserializers.JSONDeserializer()
)

# Подготавливаем payload
payload = {
    "inputs": "Извлеки данные из текста: Клиент Иван Петров, email ivan@mail.ru, телефон +79161234567. Он активен и принёс нам 50000 рублей.",
    "parameters": {
        "temperature": 0.1,  # Низкая температура для детерминированности
        "max_new_tokens": 150
    }
}

response = predictor.predict(payload)
print(json.dumps(response, indent=2, ensure_ascii=False))

Что вы получите:

{
  "generated_text": {
    "name": "Иван Петров",
    "email": "ivan@mail.ru",
    "phone": "+79161234567",
    "status": "active",
    "value": 50000
  }
}

Чего вы НЕ получите: Лишних комментариев, пропущенных кавычек, полей additional_info, чисел в виде строк (если в схеме "type": "number"). Если модель захочет "дополнить" ответ, Outlines её остановит.

Типичные грабли и как не наступить

Ошибка Причина Решение
Эндпоинт возвращает пустой ответ или ошибку 500 Схема слишком строгая или несовместима с промптом. Модель не может сгенерировать ни одной валидной последовательности. Упростите схему. Уберите "additionalProperties": false на первых порах. Используйте "anyOf" для вариативных полей. Логируйте внутренние ошибки контейнера через CloudWatch.
Запросы работают медленнее, чем с обычной моделью Констрейнтивная генерация требует дополнительных вычислений для фильтрации токенов. Сложные схемы усугубляют задержку. Увеличьте MAX_OUTPUT_TOKENS разумно. Используйте более мощный инстанс (например, ml.g5.2xlarge). Рассмотрите асинхронные эндпоинты SageMaker для batch-обработки.
Модель "зацикливается" на одном значении enum Низкая температура (temperature=0) в сочетании с ограниченным enum может привести к детерминированному, но неверному выбору. Поднимите temperature до 0.2-0.3. Используйте логирование вероятностей токенов (если модель поддерживает) для отладки.
Не работает с неанглийскими языками Токенизатор базовой модели плохо сегментирует кириллицу или иероглифы, что ломает внутреннюю валидацию Outlines. Выбирайте мультиязычные модели (Llama 3.2, Command-R 2025). Убедитесь, что в промпте явно указан язык вывода. Тестируйте на небольших примерах.

Если вам нужно решение для локальной разработки без SageMaker, посмотрите наш гайд по SDF Protocol для локальных моделей.

Зачем это всё? Реальные use cases, которые окупают интеграцию

  • Финансовые отчёты: Автоматическое извлечение транзакций, сумм, дат из электронных писем и сканов. Раньше падение из-за кривого JSON означало пропущенную транзакцию. Теперь пайплайн идёт до конца, а невалидные случаи попадают в ручной review (и их стало в 10 раз меньше).
  • Healthcare (анонимизированные данные): Парсинг историй болезни, назначений. Схема гарантирует, что поле "diagnosis_code" будет строго в формате МКБ-11, а "patient_id" никогда не "утечёт" в вывод случайно.
  • Обогащение CRM: Тот самый случай, когда номенклатура превращается в бардак. Outlines загоняет произвольные описания клиентов в строгие поля вашей CRM (lead_source, industry_tier).
  • E-commerce модерация: Извлечение атрибутов товара (бренд, размер, цвет) из описаний продавцов. Если продавец не указал размер, в JSON будет null, а не "размер не указан" строкой.

Частые вопросы (FAQ)

Чем это лучше встроенной функции Structured Outputs в Amazon Bedrock?

Bedrock отлично справляется с простыми схемами. Но Dottxt Outlines на рынке с 2024 года и предлагает более тонкий контроль, поддержку сложных грамматик (не только JSON), и работает с моделями вне экосистемы Bedrock, включая кастомные fine-tuned модели в SageMaker. Если вы уже в SageMaker и используете модели из Hugging Face или собственные — Outlines путь меньшего сопротивления. Про Bedrock мы тоже писали.

Можно ли использовать с любыми моделями в SageMaker?

С контейнером "Enforcer" — теоретически да, если модель совместима с трансформерной архитектурой и её можно загрузить через стандартные интерфейсы (Hugging Face, TGI). Но максимальная эффективность достигается с моделями, предварительно обученными или дообученными с учётом Outlines. Модели из Marketplace от Dottxt — это как раз такой вариант.

Что делать, если модель должна вернуть массив объектов переменной длины?

JSON Schema это поддерживает. Определите "type": "array" с "items", описывающими схему каждого элемента. Outlines будет генерировать токены до тех пор, пока массив остаётся валидным. Можно задать "minItems" и "maxItems" для жёсткого контроля. Главное — дать модели понять в промпте, что требуется список ("Перечисли все продукты...").

Как мониторить качество и детектировать случаи, когда модель хотела сказать одно, но схема её ограничила?

Включите в логи контейнера отладочную информацию (обычно переменная окружения OUTLINES_DEBUG=1). Следите за метриками CloudWatch: высокий rate ограничений (тонов, отброшенных Outlines) может указывать на проблему со схемой или промптом. Всегда имейте fallback-поток для случаев, когда модель возвращает пустой результат или значения по умолчанию — это сигнал к доработке схемы.

Итог: генерация структурированного JSON перестала быть лотереей. Инструменты вроде Dottxt Outlines, доступные через AWS Marketplace, превращают её в инженерную дисциплину. Вы определяете схему — получаете данные. Без сюрпризов. Интеграция с SageMaker убирает всю боль управления инфраструктурой. Остаётся самое сложное — определить, какие именно данные вам нужны из хаоса человеческого языка. А с этим, увы, даже самый продвинутый Outlines не поможет.

Подписаться на канал