Väčšina tímov narazí na rovnaký problém zhruba v treťom týždni práce s LLM: model odpovie takmer správne. JSON má navyše komentár. Alebo chýba záverečná kučeravá zátvorka. Alebo — a to je zákerné — vrátil validný JSON, len s poľom status nastaveným na "ok" miesto "approved", čo vaša downstream logika nerozozná. Aplikácia padne o štyri hodiny neskôr, keď ten konkrétny branch kód konečne zavolá. Vo výrobe.
Problém nie je v tom, že LLM sú nespoľahlivé. Problém je v tom, že formátovanie textu a sémantická správnosť sú dve rôzne veci, a väčšina nasadení ich mieša dohromady. Tento článok vysvetľuje, kde je hranica medzi nimi, aké nástroje existujú na zaistenie syntaktickej korektnosti a ako postaviť validačnú vrstvu, ktorá zachytí zvyšok.
Prečo „vráť JSON" v prompte nestačí
Keď do promptu napíšete "Respond in JSON format", dávate modelu pokyn, nie záruku. Autoregressívne jazykové modely generujú token po tokene podľa pravdepodobnosti — každý ďalší token je výsledkom distribúcie podmienečnej na všetko predchádzajúce. Model nevidí „otvorenú zátvorku, ktorú musím zatvoriť". Vidí pravdepodobnosti nasledujúcich tokenov.
Z tohto vyplývajú tri kategórie problémov:
- Syntaktická chyba — invalid JSON, nadbytočný text pred/za, markdown bloky `
`json ...`` obalené okolo výstupu, kontrolné znaky, unicode escape chyby - Schemová chyba — validný JSON, ale chýba povinné pole, pole má nesprávny dátový typ (string namiesto int), enum hodnota mimo povoleného zoznamu, zanorená štruktúra je plochá
- Sémantická chyba — validný JSON, správna schéma, ale obsah je nesprávny (nesprávna klasifikácia, vymyslená hodnota, logický nesúlad medzi poľami)
Prompt engineering vie čiastočne potlačiť prvú kategóriu. Na druhú a tretiu nestačí sám o sebe.
JSON mode vs. constrained decoding — rozdiel, na ktorom záleží
Keď provideri hovoria o „JSON mode", zvyčajne myslia jednu z dvoch vecí, ktoré sú technicky veľmi odlišné.
JSON mode (token-level bias): API parametrom sa zvýši pravdepodobnosť tokenov, ktoré patria do JSON gramatiky, a potlačia tokeny, ktoré by porušili validitu. Výsledok je takmer vždy syntakticky validný JSON, ale model stále rozhoduje o štruktúre sám. Neviete zaručiť, že výsledok bude mať pole invoice_number — len to, že bude parsovateľný.
Constrained decoding / grammar-based generation: Model generuje iba také tokeny, ktoré sú v danom stave povolené podľa formálnej gramatiky (napríklad JSON Schema, RegEx, bezkontextová gramatika). Implementácia typicky používa konečný automat (FSM — finite state machine) alebo podobnú štruktúru, ktorá sleduje stav parsingu a v každom kroku obmedzí výber tokenov na povolené. Výsledok garantuje zhodu s gramatikou — vrátane konkrétnej schémy s povinnými poľami a dátovými typmi.
Rozdiel v praxi: JSON mode vám dá parsovateľný výsledok, grammar-based generation vám dá výsledok zhodný s vašou Pydantic alebo zod schémou.
XGrammar: aktuálny štandard pre constrained decoding
Pre väčšinu produkčných serving frameworkov — vLLM, SGLang, TensorRT-LLM — je od začiatku roka 2026 defaultným backendom pre constraintovanú dekódovanie XGrammar. Oproti staršiemu prístupu (knižnica Outlines, ktorá priekopnícky zaviedla FSM prístup, ale mala problémy s kompilačnými časmi pri komplexných schémach) XGrammar dosahuje overhead menej ako 40 mikrosekúnd na token — prakticky nulový vplyv na latenciu. Ak teda používate moderný self-hosted serving stack, máte constrained decoding dostupné bez ďalšej konfigurácie.
Pre cloudové API (OpenAI, Anthropic, Google) constrained decoding typicky nie je vystavené priamo — máte JSON mode alebo „response format" parameter so schémou, ktorý funguje podobne, ale implementácia je na strane providera.
Viac o self-hosted serving: vLLM vs SGLang vs Ollama — kedy čo použiť.
Definícia schémy: Pydantic a zod ako zdroj pravdy
Ak constrained decoding závisí od formálnej schémy, potrebujete túto schému niekde definovať a udržiavať. V praxi sú dve dominantné cesty:
Python — Pydantic v2:
from pydantic import BaseModel, Field
from enum import Enum
class RiskLevel(str, Enum):
low = "low"
medium = "medium"
high = "high"
class SupplierAssessment(BaseModel):
supplier_name: str
risk_level: RiskLevel
score: int = Field(ge=0, le=100)
flags: list[str]
summary: str = Field(max_length=300)Pydantic model sa priamo serializuje do JSON Schema, ktorú väčšina LLM frameworkov akceptuje. Validácia pri parsingu (model.model_validate(json_data)) odhalí schemové chyby s presnou lokáciou.
TypeScript / Node.js — zod:
import { z } from "zod";
const SupplierAssessment = z.object({
supplier_name: z.string(),
risk_level: z.enum(["low", "medium", "high"]),
score: z.number().int().min(0).max(100),
flags: z.array(z.string()),
summary: z.string().max(300),
});Kľúčový princíp: schéma je jediný zdroj pravdy. Nedefinujte ju raz v prompte a raz v kóde — budú sa rozísť. Generujte popis schémy pre prompt dynamicky zo samotnej Pydantic alebo zod definície.
Validácia a retry — produkčná vrstva
Ani constrained decoding, ani JSON mode nechránia pred sémantickými chybami. A pri cloudových API môžu občas nastať syntaktické anomálie napriek JSON mode (sieťové prerušenia, krátenie pri max_tokens). Produkčný pipeline preto potrebuje validačnú vrstvu s retry logikou.
Základný vzor:
import json
from pydantic import ValidationError
async def call_with_validation(prompt: str, schema: type[BaseModel], max_retries: int = 3):
for attempt in range(max_retries):
raw = await llm_call(prompt)
try:
data = json.loads(raw)
return schema.model_validate(data)
except (json.JSONDecodeError, ValidationError) as e:
if attempt == max_retries - 1:
raise
# Vrátiť chybu do promptu pre opravu
prompt = repair_prompt(prompt, raw, str(e))
raise RuntimeError("Max retries exceeded")Niekoľko praktických bodov:
- Retry s chybou v kontexte: Pri ďalšom pokuse vložte do promptu presný chybový výstup validátora. Model typicky opraví chybu keď vie, kde chyba je — nie keď dostane iba „skús znova".
- Exponential backoff: Pri API rate limitoch alebo nestabilnej sieti.
- Limit retries: V praxi stačia 2–3 pokusy. Ak model po troch pokusoch stále vracia invalid output, problém je vo vašej schéme alebo prompte, nie v náhode.
- Logujte každý failed attempt: Bez logu neviete, ako často to nastáva ani prečo.
Spoľahlivosť tool callingu — úzce príbuzný problém — rozoberáme v článku Tool calling spoľahlivo v produkcii.
Prečo ani vysoká spoľahlivosť nestačí a čo s tým
Spoľahlivosť "Respond in JSON" promptu bez ďalšej infraštruktúry silno závisí od modelu, zložitosti schémy a promptu — a pri náročnejších schémach bežne klesá pod úroveň použiteľnú v produkcii. JSON mode (token bias) situáciu výrazne zlepšuje. Grammar-based generation + Pydantic validácia + retry vrstva posúva spoľahlivosť na úroveň, kde väčšina tímov prestáva riešiť syntaktické chyby a môže sa sústrediť na sémantickú správnosť.
Pre mnohé use-casy to znie dobre. Ale predstavte si pipeline, ktorý beží tisíckrát denne: pri 99 % spoľahlivosti máte 10 zlyhaní denne. Ak každé zlyhanie znamená ručný zásah operátora alebo nesprávne spracovaný dokument, tých 10 prípadov denne sa rýchlo stáva problémom.
Praktické opatrenia okrem retry:
- Jednoduché schémy: Každá úroveň zanorenia a každé nové pole znižuje spoľahlivosť. Ak potrebujete komplexnú schému, rozdeľte ju na viaceré sekvenčné volania s jednoduchšími výstupmi.
- Enum namiesto free-text: Tam kde je to možné, nahraďte voľný text pevným zoznamom hodnôt. Model musí vybrať z
["approved", "rejected", "pending"], nie vymyslieť formát sám. - Nízke teploty:
temperature=0alebo blízko nuly pre extrakčné úlohy. Kreativita tu nie je žiaduca. - Explicitné príklady v prompte:
few-shotukážky validného JSON výstupu sú stále jeden z najúčinnejších trikov.
Sémantická správnosť: hranica, kde technológia nestačí
Constrained decoding garantuje, že výstup je syntakticky a schemovo korektný. Nezaručuje, že je obsahovo správny.
Príklady sémantických chýb, ktoré prejdú cez všetky technické vrstvy:
- Klasifikácia faktúry ako
"approved"napriek tomu, že suma prekračuje schválený limit — pretože model nebol dotrénovaný na vašich interných pravidlách - Extrakcia nesprávneho dátumu z dokumentu (zamenené pole)
- Sentiment
"positive"pre text, ktorý je ironicky kritický - Hodnota
score: 87, ktorá nevyplýva z textu, ale je generovaná ako „rozumné číslo"
Tieto chyby riešite inak: evals, testy na reprezentatívnej vzorke výstupov, a kde je riziko vysoké — human-in-the-loop vrstvou. Ako systematicky merať kvalitu LLM výstupov vrátane sémantickej dimenzie, popisujeme v článku Ako zmerať kvalitu LLM aplikácie (evals).
Structured outputs cez API providerov vs. self-hosted
Ak používate cloudové API, máte k dispozícii:
- OpenAI: Parameter
response_formatstype: "json_schema"a inline JSON Schema definíciou. Od určitej verzie API garantuje zhodu so schémou na úrovni gramatiky (nie len token bias). - Anthropic Claude: Natívne štruktúrované výstupy cez
output_formatparameter — constraintovaná dekódovanie priamo na tokenovej úrovni, garantuje zhodu s JSON Schema. K dispozícii aj strict tool use s presne definovanou vstupnou schémou. - Google Gemini:
responseMimeType: "application/json"+responseSchemaparameter.
Všetky tri prístupy fungujú dobre pre typické use-casy. Praktický rozdiel nastáva pri:
- Veľmi komplexných schémach (hlboká zanorenosť,
oneOf/anyOf) — cloudové API môžu mať obmedzenia na podporované JSON Schema features - Vysokom throughpute — pri tisíckach requestov za minútu je self-hosted
vLLM+XGrammarekonomicky výhodnejší, viď porovnanie v vLLM vs SGLang vs Ollama - Citlivých dátach — ak vstupné dokumenty obsahujú PII alebo obchodné tajomstvá, cloudové API generuje data egress risk; self-hosted on-prem ho eliminuje
Pre regulované odvetvia (zdravotníctvo, financie, právo) je on-prem cesta takmer vždy relevantná — viac o tom v Lokálne LLM vs cloud: kedy čo má zmysel.
Štruktúrovaný výstup v RAG a agentnom pipeline
Structured outputs nie sú len pre extrakčné úlohy. V každom pipeline, kde LLM generuje medzivýstup, ktorý konzumuje ďalší komponent, je štruktúrovaný formát podmienkou spoľahlivosti.
V RAG pipeline typicky potrebujete štruktúrovaný výstup na:
- Query planning — model rozhoduje, ktoré zdroje dotázať, s akým filtrom, koľko výsledkov
- Reranking rozhodnutie — pri agentic RAG model hodnotí relevantnosť chunku a vracia skóre
- Citácie — kde v zdrojovom dokumente sa nachádza odpoveď (chunk ID, offset, dôvera)
- Finálny výstup pre downstream — keď RAG pipeline konzumuje iný systém, nie iba zobrazuje text
V agentnom pipeline platí to isté pre každé tool volanie — agent musí vrátiť štruktúrovaný výber akcie (tool name + parametre), aby orchestrátor vedel, čo vykonať. Výpadok štruktúry tu neznamená iba chybu parsingu — môže znamenať vykonanie nesprávnej akcie.
Praktická skúsenosť: v pipeline s viac ako troma nástrojmi sa oplatí mať každý tool vstup validovaný cez Pydantic model, nie iba voľný dict. Chyby sa tým odhalia na hranici vstupu do toolu, nie hlboko v jeho logike.
Monitoring a drifting v produkcii
Štruktúrovaný výstup má tendenciu fungovať dobre po nasadení a zhoršovať sa postupne — keď sa model zmení na strane providera (cloudové API ticho aktualizujú model), keď sa mení distribúcia vstupných dát, alebo keď schéma narastie o nové polia bez aktualizácie promptu.
Minimum monitoringu, ktoré odporúčame:
- Parse error rate: Koľko percent volaní skončí validation error po prvom pokuse? Ak toto číslo rastie, niečo sa zmenilo.
- Retry rate: Koľko percent volaní potrebovalo viac ako jeden pokus? Nad 5 % je signál.
- Field-level null/default rate: Ak pole
invoice_numberje null v 30 % prípadov, buď vstupy ho neobsahujú, alebo model ho prestáva extrahovať. - Schema version tracking: Každá zmena schémy je deploy event — logujte verziu schémy pri každom volaní, aby ste vedeli korelovať zmeny kvality.
Bez tohto monitoringu sa degradácia odhalí až keď downstream systém zlyhá v produkcii — nie keď nastane.
Časté otázky
Čo je rozdiel medzi JSON mode a structured outputs?
JSON mode (token-level bias) zaručuje iba syntaktickú validitu — výsledok bude parsovateľný JSON, ale bez záruky konkrétnej štruktúry. Structured outputs (grammar-based / schema-constrained) zaručujú zhodu s definovanou schémou vrátane povinných polí a dátových typov. Pre produkčné pipeline potrebujete druhý prístup.
Ovplyvňuje constrained decoding kvalitu alebo rýchlosť modelu?
Pri moderných implementáciách (XGrammar) je overhead pod 40 mikrosekúnd na token — v praxi nemerateľný. Kvalita odpovede sa mierne môže líšiť, pretože model nemôže voľne formulovať — pri úzkych schémach s dlhými textovými poľami je to zanedbateľné, pri veľmi obmedzujúcich schémach (napr. malý enum na odpoveď v prirodzenom jazyku) môže byť výstup menej prirodzený.
Môžem použiť structured outputs s lokálne bežiacim modelom?
Áno, s vLLM alebo SGLang je XGrammar dostupný out-of-the-box. Ollama JSON mode podporuje, ale nie grammar-based constrained decoding v plnom rozsahu — pre produkčné pipeline s presnou schémou je vLLM lepšia voľba. Viac v vLLM vs SGLang vs Ollama.
Čo robiť, keď model stále vracia nesprávne hodnoty napriek správnemu formátu?
Toto je sémantická, nie syntaktická chyba — constrained decoding ju nevyrieši. Riešenia: few-shot príklady s correct/incorrect vzormi, explicitné pravidlá v systémovom prompte, fine-tuning na doménovej vzorke, alebo human-in-the-loop vrstva pre kritické rozhodnutia.
Ako definovať schému, aby sa model nemýlil v dátových typoch?
Buďte explicitní: namiesto score: number použite score: integer, minimum: 0, maximum: 100. Namiesto date: string použite date: string, format: date (ISO 8601). Enum hodnoty definujte vždy — voľný string je pozvánka na variácie. Každé spresnenie v schéme znižuje pravdepodobnosť chyby.
*MP Industrial Solutions pomáha firmám navrhnúť a nasadiť produkčné LLM pipeline — od validácie výstupov po monitoring v reálnom prostredí. Ak riešite spoľahlivosť štruktúrovaných výstupov vo vašom systéme, radi sa pozrieme na konkrétnu architektúru spolu s vami.*
