Техническая ветка для разработчиков Собрать руками

Structured outputs для бизнес-процессов

Structured outputs — это не просто «JSON вместо текста». Это фундамент для построения отказоустойчивых AI-агентов и интеграций. В этой статье — как делать это правильно, с примерами, ловушками и готовым чеклистом.

Что такое structured outputs и зачем он бизнесу

Structured outputs — это формат ответа LLM, где результат представлен в строго определённой структуре (обычно JSON), соответствующей заранее заданной схеме (schema). В отличие от свободного текста, такие ответы можно напрямую использовать в коде: сохранять в БД, передавать в API, валидировать, логировать и автоматизировать.

Для бизнеса это означает:

  • Устранение «человеческого» парсинга («надо вытащить сумму из текста»)
  • Гарантированная структура данных — даже если модель ошиблась, она вернёт JSON, а не «сломанный» текст
  • Прямая интеграция с системами: CRM, ERP, бэкенд, workflow-движки
  • Возможность автоматической валидации и ретраев

Как это работает: пайплайн от промпта до JSON

flowchart TD
    A[Пользовательский запрос] --> B[Промпт с schema]
    B --> C[LLM API call]
    C --> D{Ответ валиден?}
    D -->|Да| E[JSON-объект]
    D -->|Нет| F[Retry / Fallback]
    E --> G[Валидация по схеме]
    G --> H[Использование в бизнес-логике]

Ключевой момент: схема (schema) задаётся до вызова API. Это может быть JSON Schema, Pydantic-модель или встроенный тип (например, в OpenAI — response_format).

Пример: из промпта к JSON в Python

Базовый пример с OpenAI API (через Pydantic):

from pydantic import BaseModel
from openai import OpenAI

class InvoiceData(BaseModel):
    amount: float
    currency: str = "RUB"
    invoice_id: str
    status: "paid" | "unpaid" | "overdue"

client = OpenAI()
completion = client.beta.chat.completions.parse(
    model="gpt-4o-mini",
    messages=[
        {"role": "system", "content": "Извлеки данные из счета. Используй только данные из текста."},
        {"role": "user", "content": "Счет №INV-2024-089 от 12.05.2024: сумма 45 000 руб., статус — оплачен."}
    ],
    response_format=InvoiceData
)

invoice = completion.choices[0].message.parsed
print(invoice.amount, invoice.status)  # 45000.0, 'paid'

Важно: parse() вместо create(), и модель сама гарантирует, что ответ будет соответствовать схеме (или вернёт ошибку).

Бизнес-кейс: автоматизация обработки входящих писем

Сценарий: приходит письмо от клиента с просьбой изменить срок оплаты. Нужно:

  1. Извлечь ID заказа, новую дату, причину
  2. Проверить, что заказ существует и не просрочен
  3. Создать задачу в Jira / CRM

Без structured outputs — 3 этапа: LLM → текст → regex → данные. С ним — 1 этап: LLM → JSON → данные.

Пример схемы для запроса:

class RequestChange(BaseModel):
    order_id: str
    new_due_date: str  # ISO 8601
    reason: str
    priority: "low" | "medium" | "high"

После этого — просто вызов API, валидация, и можно отправлять данные в CRM.

Типичные ошибки и как их избежать

  • Слишком сложная схема → модель «обманывает» (возвращает JSON, но с ложными данными). Решение: разбивайте на подзадачи, используйте step-by-step reasoning.
  • Нет fallback-логики → при ошибке пайплайн падает. Решение: всегда обрабатывайте ValidationError и возвращайте fallback-значения (null, default, human review).
  • Игнорирование типов"45 000" вместо 45000.0. Решение: используйте строгую валидацию (Pydantic v2, strict=True).
  • Нет логирования → невозможно отладить, почему модель «неправильно» спарсила. Решение: логируйте вход, выход и ошибки (включая raw response).

Чеклист внедрения structured outputs

  • [ ] Схема описана в виде JSON Schema / Pydantic и версионирована
  • [ ] В API вызове указан response_format (или эквивалент)
  • [ ] Есть обработка ValidationError и fallback-логика
  • [ ] Логируется raw response + parsed объект + ошибка (если есть)
  • [ ] Проведена тестовая валидация на 10–20 реальных примерах
  • [ ] Определён SLA: сколько времени отводится на парсинг и ретраи

Следующий шаг: от outputs к агенту

Structured outputs — это не конечная точка. Это основа для агентов, которые:

  • Парсят вход → принимают решение → вызывают tool → возвращают структурированный результат
  • Могут обрабатывать ошибки и ретраить вызовы
  • Интегрируются в очереди (например, RabbitMQ) для масштабирования

Дальше — в Telegram-канале и в следующих статьях: как собрать агента с tool-calling, как тестировать его поведение и как сделать production-ready workflow.