Більшість 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. Добрі схеми дотримуються кількох правил:
- Описові назви полів.
invoice_total_with_vatкраще заamt2. - Описи для кожного неочевидного поля, з одиницями й форматами: «дата ISO 8601», «сума в UAH, десяткове число, без символу валюти».
- Enum для закритих множин значень, з варіантом
"other", коли світ хаотичніший за ваш список. - Пласка структура краща за глибоку. Два рівні вкладеності — нормально; п'ять — джерело помилок.
- Порядок полів, що підтримує міркування. Ставте поле
reasoningчиevidenceперед полем рішення, якщо модель має обґрунтувати класифікацію; генерація йде зліва направо. - Уникайте величезних 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" },
)
Коли валідація не проходить, є варіанти:
- Повторити з помилкою. Надішліть моделі її вивід і повідомлення валідації: «total does not match sum of lines — перевір рядки ще раз». Одна повторна спроба виправляє більшість проблем; більше двох рідко допомагає.
- Перейти на сильнішу модель для елемента з помилкою.
- Передати людині з попередньо заповненим частковим результатом. Для обробки документів це часто правильний варіант за замовчуванням; див. обробка документів з 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.
Джерела
- OpenAI. Structured outputs і function calling.
- Anthropic. Structured outputs і tool use.
- Willard, B., Louf, R. (2023). Efficient Guided Generation for Large Language Models.
- Tam et al. (2024). Let Me Speak Freely? A Study on the Impact of Format Restrictions on Performance of Large Language Models.
- JSON Schema, Pydantic, Zod, Instructor.