Menu

Structured output: niezawodny JSON z modelu LLM

Structured output (ustrukturyzowana odpowiedź) to prośba o odpowiedź w stałym kształcie, takim jak JSON, tabela albo szablon, żeby program albo człowiek mógł jej użyć bez przerabiania. Podaj schemat, powiedz, co zrobić z brakującymi wartościami, i zwaliduj wynik w kodzie.

Każdy prompt poniżej możesz edytować: zmień go, a potem otwórz w ChatGPT, Claude lub innej aplikacji AI.

Structured output to odpowiedź w kształcie ustalonym z góry: obiekt JSON z nazwanymi kluczami, tabela ze stałymi kolumnami albo szablon z tymi samymi nagłówkami za każdym razem. Potrzebujesz go zawsze, gdy odpowiedź czyta program, a pomaga też, gdy czyta ją człowiek, bo wtedy każda odpowiedź wygląda tak samo i łatwo ją przejrzeć albo porównać.

Cała sztuka polega na tym, żeby opisać kształt tak dokładnie, by model nie miał już nic do wyboru. Samo słowo "JSON" nie jest opisem. Poniższy blok wyciąga szczegóły ze zgłoszenia błędu. Porównaj prośbę o "najważniejsze szczegóły" z prośbą, która rozpisuje schemat.

Wyciągnij najważniejsze szczegóły z tego zgłoszenia błędu: "Od wczorajszej aktualizacji aplikacja się wysypuje, gdy klikam Eksportuj w projekcie z ponad 50 zdjęciami. Mniejsze projekty eksportują się bez problemu. Mam Androida 14, wersja aplikacji 3.2.0. To blokuje mi oddanie projektu klientowi."
Try it
Example replyReplies vary between models and runs.

Oto najważniejsze szczegóły ze zgłoszenia błędu:

  • Problem: aplikacja się wysypuje przy eksporcie projektów
  • Wyzwalacz: kliknięcie Eksportuj w projektach z ponad 50 zdjęciami
  • Od kiedy: od ostatniej aktualizacji
  • Platforma: Android 14
  • Wersja aplikacji: 3.2.0
  • Wpływ: wysoki, bo blokuje oddanie projektu klientowi

Mniejsze projekty eksportują się najwyraźniej bez problemów.

Odpowiedź w wolnym tekście jest trafna i czytelna, ale każde uruchomienie użyje innych etykiet, waga błędu jest zdaniem, a nie wartością, a program musiałby zgadywać, gdzie zaczyna się każde pole. Odpowiedź w JSON może trafić prosto do systemu zgłoszeń. Zwróć uwagę, że odpowiedź i tak przyszła w bloku kodu: aplikacje czatowe zwykle tak opakowują JSON, co ma znaczenie, gdy parsujesz go w kodzie.

Jak prosić o JSON

Dobra prośba o JSON odpowiada na każde pytanie, na które model inaczej odpowiedziałby za ciebie:

  • Każdy klucz, zapisany dokładnie. Wpisz nazwy kluczy w cudzysłowie dokładnie tak, jak mają się pojawić. Napisz "dokładnie te klucze", żeby model nie dodawał innych.
  • Typ każdej wartości. String, liczba, wartość logiczna, tablica stringów, zagnieżdżony obiekt.
  • Dozwolone wartości dla każdego pola o stałym zestawie, takiego jak waga błędu czy kategoria. Bez tej listy w czterech uruchomieniach dostaniesz "High", "high", "severe" i "P1".
  • Co zrobić, gdy w danych wejściowych brakuje wartości. Jeśli nie napiszesz "null, jeśli nie podano", model ma skłonność do wypełniania luki wiarygodnym strzałem, a zgadnięta wersja aplikacji wygląda dokładnie jak prawdziwa.
  • Nic dookoła. "Zwróć tylko JSON, bez tekstu przed nim ani po nim" usuwa uprzejme zdanie na początku.

Gdy kształt jest zagnieżdżony albo nietypowy, pokazanie jednego kompletnego przykładowego obiektu działa lepiej niż opis. To few-shot prompting zastosowany do formatu. Wartości w przykładzie powinny wyraźnie różnić się od prawdziwych danych wejściowych, żeby model ich nie skopiował.

Prompt do wyciągania danych wielokrotnego użytku

Ten blok zawiera tę samą prośbę podzieloną na części. Wyłącz część z formatem, a model nadal zwróci JSON, bo wymagają tego ograniczenia, ale sam dobierze nazwy kluczy, na przykład title zamiast job_title, i kod, który oczekuje twoich kluczy, przestanie działać. To część z ograniczeniami powstrzymuje go przed dokończeniem niepełnego numeru telefonu albo wywnioskowaniem firmy z domeny adresu e-mail. Wklej prawdziwą stopkę do pola wejściowego, żeby to przetestować.

Wyciągnij dane kontaktowe jako JSON
Fill in
Parts
Wyciągnij dane kontaktowe z poniższej stopki e-maila.
Zwróć obiekt JSON z dokładnie tymi kluczami: { "name": "string", "job_title": "string lub null", "company": "string lub null", "email": "string lub null", "phone": "string lub null" }
Użyj null dla każdej wartości, której nie ma w tekście. Nie zgaduj i nie uzupełniaj niepełnych wartości. Zwróć tylko JSON.
Priya Nair | Kierowniczka ds. danych, Northwind Labs | priya.nair@northwind.example
Try it
Example replyReplies vary between models and runs.
{
  "name": "Priya Nair",
  "job_title": "Kierowniczka ds. danych",
  "company": "Northwind Labs",
  "email": "priya.nair@northwind.example",
  "phone": null
}

Tabele i stałe szablony

Structured output nie jest tylko dla programów. Gdy odpowiedź czytasz ty, tabela markdown albo stały szablon dają tę samą korzyść: zanim spojrzysz, wiesz, gdzie będzie każda informacja.

Tabela
Porównaj listy, krotki i zbiory w Pythonie w tabeli markdown z kolumnami: Typ, Uporządkowany, Modyfikowalny, Dopuszcza duplikaty, Typowe zastosowanie. Jeden wiersz na typ. Żadnego tekstu poza tabelą.
Try it
Example replyReplies vary between models and runs.
TypUporządkowanyModyfikowalnyDopuszcza duplikatyTypowe zastosowanie
listTakTakTakSekwencja, do której dodajesz elementy, usuwasz je albo ją sortujesz
tupleTakNieTakStała grupa wartości, na przykład współrzędne
setNieTakNieUsuwanie duplikatów i szybkie sprawdzanie przynależności

Szablon działa tak samo przy dłuższym tekście: podaj nagłówki w kolejności i napisz, co ma się znaleźć pod każdym. "Odpowiedz z trzema pogrubionymi etykietami: Przyczyna, Poprawka, Jak sprawdzić" daje za każdym razem te same trzy etykiety, więc partię odpowiedzi łatwo porównać.

Tryb JSON w API

Kilka API modeli ma ustawienie, które wymusza składniowo poprawny JSON. W SDK OpenAI dla Pythona to response_format. Tryb JSON wymaga, żeby słowo "JSON" pojawiło się gdzieś w wiadomościach, więc poniższy prompt systemowy je wymienia i wypisuje klucze.

import json
from openai import OpenAI

client = OpenAI()
MODEL = "your-model-id"  # e.g. from your provider's model list

report_text = "Since yesterday's update the app crashes when I tap Export..."

response = client.chat.completions.create(
    model=MODEL,
    response_format={"type": "json_object"},
    messages=[
        {
            "role": "system",
            "content": (
                "Extract the bug report into JSON with the keys "
                "summary (string), severity (one of low, medium, high, critical) "
                "and steps_to_reproduce (array of strings)."
            ),
        },
        {"role": "user", "content": report_text},
    ],
)

data = json.loads(response.choices[0].message.content)

Tryb JSON gwarantuje, że tekst da się sparsować (chyba że odpowiedź zostanie ucięta na limicie tokenów), a nie że pasuje do twojego schematu. Klucza nadal może brakować, a waga błędu nadal może wynosić "urgent". Kilku dostawców przyjmuje też pełny JSON Schema, jako format wyjściowy albo jako schemat wejściowy definicji narzędzia (funkcji), a niektóre z tych trybów ograniczają odpowiedź do schematu. Dokładny parametr sprawdź w dokumentacji swojego dostawcy, bo te funkcje różnią się między API.

Waliduj wynik w kodzie

Traktuj JSON od modelu tak jak każde dane wejściowe spoza twojego programu: sparsuj go, a potem sprawdź. Parsowanie wyłapuje zepsutą składnię. Sprawdzenie wyłapuje poprawny obiekt z niewłaściwą zawartością.

ALLOWED_SEVERITIES = {"low", "medium", "high", "critical"}

def problems(data):
    if not isinstance(data, dict):
        return ["the answer must be a JSON object"]
    found = []
    if not isinstance(data.get("summary"), str):
        found.append("summary must be a string")
    if data.get("severity") not in ALLOWED_SEVERITIES:
        found.append("severity must be low, medium, high or critical")
    steps = data.get("steps_to_reproduce")
    if not isinstance(steps, list) or not all(isinstance(s, str) for s in steps):
        found.append("steps_to_reproduce must be an array of strings")
    return found

Gdy sprawdzenie nie przejdzie, jedna ponowna próba często wystarcza: wyślij modelowi jego własną odpowiedź razem z listą problemów i poproś o poprawiony JSON. Ogranicz liczbę ponowień i loguj niepowodzenia, bo pole, które często nie przechodzi, jest sygnałem, że prompt jest niejasny. W większych projektach ręcznie napisaną funkcję zastępuje biblioteka do walidacji, taka jak Pydantic, albo walidator JSON Schema.

Dwa kolejne nawyki zapobiegają cichym błędom. Ustaw limit tokenów wyjściowych na tyle wysoko, żeby zmieściła się największa spodziewana odpowiedź, bo ucięty obiekt nigdy się nie sparsuje. A gdy dane wejściowe są długie albo pochodzą od użytkowników, oddziel je od instrukcji delimiterami albo tagami XML, żeby tekst w środku rzadziej był odczytywany jako instrukcja. Jeśli jeden prompt ma jednocześnie rozumować i tworzyć JSON, rozważ prompt chaining: niech jeden krok myśli w wolnym tekście, a drugi zamieni wynik w strukturę.

Najczęściej zadawane pytania

Jak sprawić, żeby ChatGPT zwracał JSON?

Napisz, że odpowiedź ma być w JSON, wypisz każdy klucz z typem i podaj dozwolone wartości dla każdego pola o stałym zestawie wartości. Dodaj "Zwróć tylko JSON, bez tekstu przed nim ani po nim". W API włącz też tryb JSON przez response_format={"type": "json_object"}, który wymaga, żeby słowo JSON pojawiło się w twoich wiadomościach.

Dlaczego model dodaje tekst wokół JSON?

Modele czatowe są trenowane do prowadzenia rozmowy, więc często zaczynają od zdania typu "Oto JSON" albo opakowują obiekt w blok kodu markdown. Poproś o sam JSON, a w kodzie użyj trybu JSON w API albo usuń otaczający blok kodu przed parsowaniem.

Czym jest tryb JSON?

Tryb JSON to ustawienie API, które sprawia, że model generuje składniowo poprawny JSON, o ile odpowiedź nie zostanie ucięta przez limit tokenów wyjściowych. Nie sprawia, że model trzyma się twojego schematu: kluczy nadal może brakować, mogą też mieć literówki albo zły typ. Niektórzy dostawcy oferują też bardziej rygorystyczny tryb, który przyjmuje pełny JSON Schema i ogranicza do niego odpowiedź.

Czy LLM zawsze może zwrócić poprawny JSON?

Nie przy samym prompcie. Nawet jasna instrukcja czasem zawodzi, a odpowiedź ucięta przez limit tokenów wyjściowych zawsze jest niepoprawna. Parsuj każdą odpowiedź prawdziwym parserem JSON, sprawdzaj potrzebne pola i ponawiaj próbę albo zgłaszaj wyraźny błąd, gdy sprawdzenie nie przejdzie.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ