## Wymuszanie formatu

*Agenci*

*Ostatnia zmiana: 28 września 2026*

Structured output w trybie ścisłym nie prosi modelu o format, tylko go wymusza: w każdym kroku sampler zeruje prawdopodobieństwo tokenów, które złamałyby schemat. Dostajesz gwarancję składni, nie poprawności treści.

**Po ludzku:** Formularz z polami wyboru zamiast pustej kartki. Nie da się wpisać nic spoza dozwolonych opcji, ale zaznaczyć złą kratkę nadal można. A gdy brakuje kratki na właściwą odpowiedź, zaznaczysz najbliższą.

*Interaktywny widżet na stronie: Przejdź krok po kroku przez generowanie. Zobacz, które tokeny maska wycina, i zatrzymaj się przy tokenie 4.*

### Jak działa maskowanie (constrained decoding)

- Schemat kompiluje się do automatu. Dla wyrażeń regularnych i schematów bez rekurencji wystarcza automat skończony, dla schematów rekurencyjnych i gramatyk bezkontekstowych potrzebny jest automat ze stosem. Stan automatu mówi, jakie znaki mogą paść dalej.
- Tokeny nie pokrywają się ze składnią JSON-a, jeden token to na przykład `{"` albo `":"`. Dlatego silnik (Outlines, XGrammar) wylicza dla każdego stanu automatu, które tokeny słownika są dozwolone. Pozostałe dostają logit minus nieskończoność, a sampler losuje z tego, co zostało (temat „Następny token”).
- Narzut na token jest bliski zera, bo większość sprawdzeń liczy się z góry. Płaci się przy pierwszym użyciu schematu: kompilacja dodaje opóźnienie, potem gramatyka jest cache’owana (u Anthropic 24 godziny od ostatniego użycia).

### JSON mode, structured output i tool calling

- JSON mode gwarantuje poprawny JSON, ale nie zgodność ze schematem. Tryb ścisły gwarantuje schemat: `json_schema` ze `strict: true` w OpenAI, `output_config.format` w Anthropic.
- Tool calling to ten sam JSON w innej roli: model wypisuje nazwę narzędzia i argumenty (temat „Narzędzia (function calling)”). Bez trybu ścisłego format jest tylko wyuczony, więc argumenty zwykle, ale nie zawsze, pasują do schematu. `strict: true` przy definicji narzędzia włącza to samo maskowanie. W Responses API OpenAI narzędzia są domyślnie strict, jeśli schemat na to pozwala, a jeśli nie, po cichu wracają do trybu best effort (odpowiedź pokazuje `strict: false`), więc ustawiaj flagę jawnie.
- Tryby ścisłe obsługują podzbiór JSON Schema. OpenAI wymaga, by każde pole było w `required` (pole opcjonalne to typ z `null`) i by obiekty miały `additionalProperties: false`, przy limicie 5000 pól i 10 poziomów zagnieżdżenia. Anthropic nie obsługuje m.in. schematów rekurencyjnych ani limitów długości tekstu, a dopuszcza najwyżej 20 narzędzi strict na request, 24 pola opcjonalne i 16 pól z typem unii łącznie we wszystkich schematach strict. Powyżej tych limitów API zwraca błąd „Schema is too complex for compilation”.
- Maska obejmuje tylko odpowiedź. Myślenie modelu rozumującego nie jest ograniczone, więc model może najpierw swobodnie rozumować, a potem wypełnić schemat (temat „Modele rozumujące”).

### Czego gwarancja nie obejmuje

- Treści. Model nadal może wpisać zły numer faktury albo zmyśloną datę w poprawnym formacie. Reguły biznesowe (zakresy, sumy, istnienie identyfikatorów) sprawdza kod, a błąd wraca do modelu z konkretnym komunikatem, z limitem prób.
- Odpowiedzi uciętej limitem długości (`stop_reason: "max_tokens"`, `finish_reason: "length"`) ani odmowy: OpenAI zwraca ją w osobnym polu `refusal`, Anthropic jako `stop_reason: "refusal"`. W obu przypadkach treść może nie pasować do schematu. Sprawdzaj powód zatrzymania przed parsowaniem.
- Wielkości liter w enumach u Anthropic. Wartości `enum` i `const` typu string mogą wrócić z inną wielkością liter („Conversation Topic 3” zamiast „Conversation topic 3”), ze zwykłym powodem zatrzymania i bez błędu, w odpowiedziach JSON i w wywołaniach narzędzi w trybie strict. Porównuj je bez rozróżniania wielkości liter i unikaj wartości, które różnią się tylko nią.
- Tego, co model chciał powiedzieć. Gdy najbardziej prawdopodobna odpowiedź jest poza schematem, maska wpycha model w najbliższą dozwoloną, jak phishing zamieniony na spam w widżecie. Dodaj wyjście awaryjne: wartość „inne” albo „nie wiem” i pole na komentarz.
- Jakości rozumowania. Model pisze od lewej do prawej, więc decyzja przed uzasadnieniem każe mu zdecydować, zanim cokolwiek policzy. Stawiaj pole z uzasadnieniem przed polem z decyzją i wpisz oba do `required`: Anthropic wypisuje pola wymagane przed opcjonalnymi (OpenAI zachowuje kolejność ze schematu, a i tak wymaga wszystkich pól). W badaniu „Let Me Speak Freely?” (2024) ograniczenia formatu obniżały wyniki zadań wymagających rozumowania, częściowo właśnie z tego powodu: w jednym z zadań GPT-3.5 w trybie JSON za każdym razem stawiał odpowiedź przed uzasadnieniem. Wynik jest sporny. Powtórzenie z tymi samymi promptami (.txt, „Say What You Mean”) spadku nie znalazło, a inne prace mierzą koszt samej maski, głównie przy małych modelach i ciasnych schematach (Reddy i in., 2026). Gdy ewaluacje pokazują spadek, pozwól modelowi odpowiedzieć swobodnie, a strukturę wyciągnij drugim, tanim wywołaniem.

### Sprawdź się

**Pytanie:** Jak zagwarantować, że model zwróci JSON zgodny ze schematem, i czego ta gwarancja nie obejmuje?

**Krótka odpowiedź:** W trybie ścisłym schemat kompiluje się do gramatyki, a w każdym kroku sampler zeruje prawdopodobieństwo tokenów, które by ją złamały. JSON pasuje do schematu, chyba że limit długości utnie odpowiedź albo model odmówi, co widać po powodzie zatrzymania. U Anthropic wartości enum mogą też wrócić z inną wielkością liter, więc porównuje się je bez jej rozróżniania. Gwarancja dotyczy składni, nie treści: wartości waliduje kod. Schemat dostaje wyjście awaryjne, bo ciasny enum wpycha model w najbliższą opcję, a pole z uzasadnieniem stoi przed decyzją, bo model pisze od lewej do prawej. Tool calling w trybie strict działa tak samo.

### Pytania pogłębiające

- **Czy wymuszanie formatu może obniżyć jakość?** Tak, gdy schemat każe podać decyzję przed uzasadnieniem albo nie ma opcji „inne”. Pomaga pole na rozumowanie przed decyzją, myślenie modelu rozumującego, którego maska nie obejmuje, albo dwa kroki: swobodna odpowiedź, potem ekstrakcja.
- **JSON mode, structured output czy tool calling: kiedy co?** Structured output, gdy odpowiedź ma zasilić kod. Tool calling, gdy model ma wybrać akcję spośród kilku, z trybem strict dla argumentów. JSON mode tylko tam, gdzie nie ma trybu ścisłego.
- **Jak wymusić format na własnym modelu?** vLLM i SGLang mają wbudowane silniki constrained decoding (m.in. XGrammar i llguidance): schemat zamienia się w automat, a ten w maskę tokenów na każdym kroku. Narzut na token jest mały, kosztem jest kompilacja każdego nowego schematu. Przy modelu rozumującym uruchom serwer z parserem rozumowania (np. --reasoning-parser w vLLM), inaczej gramatyka obowiązuje od pierwszego tokena i odcina myślenie.
- **JSON się parsuje, ale dane są złe. Co dalej?** Walidacja w kodzie (typy z ograniczeniami, reguły biznesowe) i ponowienie z konkretnym komunikatem błędu, z limitem prób. Powtarzający się błąd to sygnał do zmiany schematu albo promptu, a przypadek trafia do zestawu ewaluacyjnego.

### Źródła

- [Dokumentacja OpenAI: Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs)
- [Dokumentacja Claude: structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs)
- [Willard, Louf: Efficient Guided Generation for Large Language Models (arXiv)](https://arxiv.org/abs/2307.09702)
- [Let Me Speak Freely? Format restrictions and LLM performance (arXiv)](https://arxiv.org/abs/2408.02442)
- [.txt: Say What You Mean, odpowiedź na „Let Me Speak Freely?”](https://blog.dottxt.ai/say-what-you-mean.html)

Strona interaktywna: https://howaiworks.dev/pl/format/
