Більшість LLM-функцій у реальних продуктах закінчуються не текстом на екрані, а даними: категорією тікета, полями з рахунку, списком задач, аргументами для виклику API. Для цього «будь ласка, відповідай у JSON» недостатньо. Навіть 1% помилок розбору означає сотні зламаних запитів на день за помірного навантаження. У гайді — механізми, що роблять структурований вивід надійним, як проєктувати схеми, які моделі заповнюють правильно, і як валідувати й відновлюватися, коли щось усе ж іде не так.

Три способи отримати структурований вивід

Підхід Як працює Надійність
Лише промпт «Відповідай JSON за цим прикладом» Добра із сучасними моделями, але без гарантій
Виклик інструментів (tool / function calling) Ви описуєте інструмент зі схемою JSON; модель повертає аргументи Висока; широка підтримка
Structured output / обмежене декодування API обмежує генерацію так, що вивід мусить відповідати схемі Найвища — синтаксис гарантовано

Основні провайдери вже нативно підтримують вивід, обмежений схемою (OpenAI structured outputs; Anthropic structured outputs). Для self-hosted моделей бібліотеки на кшталт Outlines і сервери інференсу як vLLM реалізують обмежене декодування, маскуючи токени, що порушили б граматику (Willard & Louf, 2023).

Використовуйте обмежений вивід, коли тільки можете. Він усуває цілий клас збоїв — JSON, що не розбирається, відсутні обов'язкові поля, неправильні типи — і дає зосередитися на складній частині: чи правильні значення.

Синтаксис гарантовано, семантику — ні

Обмежене декодування гарантує, що вивід розбирається і відповідає схемі. Воно не гарантує, що сума рахунку правильна, категорія вірна, а дату не вигадано. Модель, змушена заповнити обов'язкове поле, заповнить його — навіть якщо інформації у вхідних даних немає. Це найважливіше, що треба розуміти про структурований вивід.

Проєктуйте з урахуванням цього:

  • Дайте змогу представити відсутню інформацію. Використовуйте nullable-поля або явні значення "unknown", а не змушуйте вгадувати.
  • Валідуйте бізнес-правила в коді: сума дорівнює сумі рядків, дати в правдоподібному діапазоні, ідентифікатори існують у вашій базі.
  • Оцінюйте точність значень на розміченому наборі, як і будь-яку іншу LLM-функцію; див. evals для LLM.

Дизайн схем, які моделі заповнюють правильно

Схема — частина промпту. Моделі читають назви полів, описи та значення enum. Добрі схеми дотримуються кількох правил:

  1. Описові назви полів. invoice_total_with_vat краще за amt2.
  2. Описи для кожного неочевидного поля, з одиницями й форматами: «дата ISO 8601», «сума в UAH, десяткове число, без символу валюти».
  3. Enum для закритих множин значень, з варіантом "other", коли світ хаотичніший за ваш список.
  4. Пласка структура краща за глибоку. Два рівні вкладеності — нормально; п'ять — джерело помилок.
  5. Порядок полів, що підтримує міркування. Ставте поле reasoning чи evidence перед полем рішення, якщо модель має обґрунтувати класифікацію; генерація йде зліва направо.
  6. Уникайте величезних enum. Для сотень категорій спершу відберіть кандидатів і дайте моделі короткий список.
from enum import Enum
from pydantic import BaseModel, Field

class Category(str, Enum):
    billing = "billing"
    technical = "technical"
    account = "account"
    other = "other"

class TicketTriage(BaseModel):
    evidence: str = Field(description="Quote the sentence(s) that justify the category.")
    category: Category
    urgency: int = Field(ge=1, le=3, description="1 = low, 3 = service down for customer")
    customer_order_id: str | None = Field(
        default=None,
        description="Order ID like ORD-123456 if explicitly mentioned, otherwise null.",
    )

Валідація і відновлення

Навіть з обмеженим декодуванням валідуйте кожну відповідь у коді застосунку. Pydantic у Python і Zod у TypeScript дозволяють описати схему один раз і використовувати її і для запиту до API, і для валідації:

import { z } from "zod"

const LineItem = z.object({
  description: z.string(),
  quantity: z.number().positive(),
  unitPrice: z.number().nonnegative(),
})

export const Invoice = z.object({
  supplierName: z.string(),
  supplierTaxId: z.string().regex(/^\d{8,10}$/).nullable(),
  issueDate: z.string().date(),
  currency: z.enum(["UAH", "EUR", "USD"]),
  lines: z.array(LineItem).min(1),
  total: z.number().nonnegative(),
}).refine(
  (inv) => Math.abs(inv.lines.reduce((s, l) => s + l.quantity * l.unitPrice, 0) - inv.total) < 0.01,
  { message: "total does not match sum of lines" },
)

Коли валідація не проходить, є варіанти:

  1. Повторити з помилкою. Надішліть моделі її вивід і повідомлення валідації: «total does not match sum of lines — перевір рядки ще раз». Одна повторна спроба виправляє більшість проблем; більше двох рідко допомагає.
  2. Перейти на сильнішу модель для елемента з помилкою.
  3. Передати людині з попередньо заповненим частковим результатом. Для обробки документів це часто правильний варіант за замовчуванням; див. обробка документів з LLM.

Бібліотеки на кшталт Instructor обгортають цей цикл «валідуй і повтори» для багатьох провайдерів. Vercel AI SDK пропонує generateObject зі схемами Zod для TypeScript; ми використовуємо його у статті AI-чат на Next.js.

Чи шкодить примусовий JSON якості?

Деякі дослідження показали, що жорсткі обмеження формату можуть погіршувати міркування на окремих задачах порівняно з вільними відповідями (Tam et al., 2024). На практиці ефект залежить від моделі й задачі, а сучасні реалізації structured output скоротили розрив. Добре працюють два прийоми:

  • Спершу міркування, потім структура. Дайте моделі подумати (розширене мислення або текстове поле міркувань перед полями відповіді), а потім видати структурований результат.
  • Двокрокові пайплайни для складних задач: один виклик робить вільний аналіз, другий, дешевший, видобуває з нього структуровані поля.

Міряйте на власному наборі evals, а не припускайте ні те, ні інше.

Виклик інструментів як структурований вивід

Виклик інструментів і structured output — дві сторони одного механізму: модель видає аргументи, що відповідають схемі. Використовуйте виклик інструментів, коли модель має вирішити, чи і яку дію виконати; structured output — коли вам завжди потрібна одна конкретна форма. В агентах схема вхідних даних кожного інструмента заслуговує на таку саму увагу; див. проєктування інструментів для LLM-агентів.

Стрімінг структурованого виводу

У функціях для користувачів часто хочеться показувати часткові результати під час генерації: список задач, що з'являються по одній, форму, що поступово заповнюється. Багато SDK підтримують стрімінг часткових об'єктів: клієнт отримує неповний JSON, який «лагодиться» до валідного часткового об'єкта. Валідуйте лише фінальний об'єкт і проєктуйте UI, що витримує появу й зміну полів.

Поради щодо продуктивності й вартості

  • Схеми коштують токенів. Великі схеми з довгими описами додають вхідні токени до кожного виклику; вони стабільні, тому кешування промптів робить їх дешевими — див. оптимізація витрат на LLM.
  • Затримка першого виклику. Деякі провайдери компілюють схему в граматику при першому використанні; очікуйте одноразову затримку для нових схем.
  • Пакетне видобування. Для офлайн-задач (тисячі документів) batch API суттєво зменшують вартість.
  • Менших моделей часто достатньо для чітко визначеного видобування з добрими схемами. Оцініть, перш ніж за замовчуванням брати найбільшу.

Вимірювання якості видобування

Структурований вивід спрощує оцінювання порівняно з вільним текстом, бо поля можна порівнювати напряму. Зберіть розмічений набір зі 100–300 реальних вхідних даних і міряйте за кожним полем:

Метрика Що показує
Точність поля Частка точних (або нормалізованих) збігів для поля
Precision для null Коли модель повертає null, чи значення справді відсутнє?
Recall для null Коли значення відсутнє, чи повернула модель null замість вигадки?
Точність на рівні документа Частка вхідних даних, де всі критичні поля правильні
Частка невдалих валідацій Як часто бізнес-правила відхиляють вивід

Recall для null — метрика, про яку команди забувають найчастіше, і саме вона виявляє вигадані значення. Для рішень про автоматизацію важить точність на рівні документа: якщо 92% рахунків повністю правильні, ті, що пройшли валідацію, можна обробляти автоматично, а решту відправляти на перевірку.

Типові помилки

  • Обов'язкові поля для необов'язкової інформації, що призводять до вигаданих значень.
  • Сліпа довіра до enum. Правильна на вигляд категорія може бути хибною; міряйте точність за класами.
  • Відсутність бізнес-валідації. Відповідність схемі не означає правильність.
  • Розбір JSON із markdown регулярками у 2026 році. Використовуйте нативні можливості structured output.
  • Одна гігантська схема на все. Розбивайте видобування на сфокусовані виклики, коли схема виростає більше, ніж людина заповнила б за один підхід.

FAQ

Чи JSON mode — те саме, що structured outputs? Ні. Старіший «JSON mode» гарантує валідний JSON, але не конкретну схему. Structured outputs з обмеженням схемою гарантують обидва.

А XML чи YAML? JSON зі схемою — найкраще підтримуваний варіант. XML-теги залишаються корисними для текстових секцій усередині промптів і відповідей.

Як працювати з дуже довгими списками? Розбивайте вхідні дані на частини, видобувайте по кожній і об'єднуйте в коді. Довгий вивід підвищує ймовірність обрізання й пропусків.

Чи можна використовувати structured output із self-hosted моделями? Так. vLLM і подібні сервери підтримують керовану генерацію зі схемами JSON; див. власний хостинг LLM.

Джерела

  1. OpenAI. Structured outputs і function calling.
  2. Anthropic. Structured outputs і tool use.
  3. Willard, B., Louf, R. (2023). Efficient Guided Generation for Large Language Models.
  4. Tam et al. (2024). Let Me Speak Freely? A Study on the Impact of Format Restrictions on Performance of Large Language Models.
  5. JSON Schema, Pydantic, Zod, Instructor.