Tryb JSON i response_format
Uczestnicy włączą tryb JSON w OpenAI API, przygotują prompty konsekwentnie generujące poprawny JSON oraz obsłużą sytuacje, w których model mimo wszystko naruszy wymagany format.
Tryb JSON i response_format to bezpłatna lekcja AI Engineering Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej AI Engineering Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs AI Engineering Academy zawiera 4 lekcji w sumie.
Problem z nieustrukturyzowanymi danymi wyjściowymi LLM
Domyślnie LLM-y zwracają tekst o dowolnej strukturze. Parsowanie tego tekstu w celu wyodrębnienia danych ustrukturyzowanych jest zawodne: zmiana zachowania modelu, niewielka modyfikacja promptu lub przypadek brzegowy w danych wejściowych mogą nieoczekiwanie zmienić format danych wyjściowych, powodując awarię parsera i aplikacji.
Załóżmy, że prosimy LLM o „zwrócenie imienia i wieku użytkownika w formacie JSON”. Czasami zwróci {"name":"Alice","age":30}, czasami umieści wynik w bloku kodu Markdown, a czasami doda objaśniający tekst. Każda z tych różnic wymaga innej logiki parsowania. Niezawodne dane wyjściowe czytelne maszynowo wymagają wymuszenia na modelu określonej struktury, a nie liczenia na to, że sam jej przestrzega.
Tryb JSON w OpenAI
OpenAI wprowadziło tryb JSON za pomocą parametru response_format. Po ustawieniu wartości {"type": "json_object"} model jest ograniczony do zwracania wyłącznie prawidłowego obiektu JSON. Model nigdy nie wygeneruje niczego, co nie byłoby prawidłowym kodem JSON — bez obudowy w Markdown, tekstu objaśniającego ani końcowej treści.
import openai
import json
client = openai.OpenAI()
response = client.chat.completions.create(
model='gpt-4o-mini',
messages=[
{
'role': 'system',
'content': 'Extract information from the text and return valid JSON only.'
},
{
'role': 'user',
'content': 'John Smith, age 34, works as a software engineer in Austin.'
}
],
response_format={'type': 'json_object'} # Guarantee valid JSON output
)
# Safe to parse - guaranteed valid JSON
data = json.loads(response.choices[0].message.content)
print(data)
# Example output: {"name": "John Smith", "age": 34, "job": "software engineer", "city": "Austin"}Uwagi dotyczące trybu JSON
Tryb JSON gwarantuje prawidłową składnię JSON, ale NIE gwarantuje, że JSON będzie zawierać potrzebne pola. Model nadal decyduje, które klucze uwzględnić, jak je nazwać i jakich typów danych użyć. Możesz poprosić o pole name, a w odpowiedzi otrzymać full_name, albo poprosić o tablicę i otrzymać ciąg znaków.
Pamiętaj również, że tryb JSON wymaga wspomnienia o JSON w promptcie. Jeśli włączysz tryb JSON, ale w promptcie nie poprosisz o dane wyjściowe w formacie JSON, model może wygenerować pusty obiekt JSON albo odmówić wygenerowania odpowiedzi. Zawsze wyraźnie poinstruuj model, aby odpowiadał w formacie JSON, w wiadomości systemowej lub wiadomości użytkownika.
Ustrukturyzowane dane wyjściowe z Pydantic (wersja testowa)
Nowsza funkcja OpenAI, ustrukturyzowane dane wyjściowe, idzie dalej niż tryb JSON: podajesz schemat JSON, a model jest ograniczony do zwrócenia dokładnie danych zgodnych z tym schematem — z określonymi nazwami pól, typami i zagnieżdżeniami. Eliminuje to problem niespójności schematu występujący w podstawowym trybie JSON.
Python SDK przyjmuje bezpośrednio modele Pydantic, automatycznie konwertując je na schemat JSON i deserializując odpowiedź z powrotem do typowanego obiektu Python. To najprostszy sposób na uzyskanie niezawodnych danych strukturalnych z LLM w języku Python.
import openai
from pydantic import BaseModel
from typing import Optional
client = openai.OpenAI()
class PersonInfo(BaseModel):
name: str
age: Optional[int]
job_title: str
city: str
completion = client.beta.chat.completions.parse(
model='gpt-4o-mini',
messages=[
{'role': 'system', 'content': 'Extract person information from the text.'},
{'role': 'user', 'content': 'Sarah Chen, 28 years old, is a data scientist based in Seattle.'}
],
response_format=PersonInfo # Pass Pydantic model directly
)
# Already deserialized into a PersonInfo instance
person = completion.choices[0].message.parsed
print(person.name) # Sarah Chen
print(person.age) # 28
print(person.job_title) # data scientist
print(person.city) # SeattleTworzenie promptów zapewniających spójny format JSON
Nawet przy włączonym trybie JSON sposób zaprojektowania promptu wpływa na jakość danych wyjściowych. Najlepsze praktyki dotyczące promptów JSON:
- Jawnie nazywaj pola: powiedz modelowi dokładnie, jakich pól oczekujesz, a nie tylko „zwróć JSON”
- Określaj typy: „Zwróć cenę jako liczbę, nie jako ciąg znaków” zapobiega niezgodności typów
- Definiuj wartości wyliczeniowe: „Kategoria musi być jedną z następujących wartości: bug, feature, question” zapobiega nieoczekiwanym wartościom
- Określaj sposób obsługi brakujących danych: „Jeśli pole nie występuje w tekście, zwróć dla niego null”
Potraktuj prompt jak częściowy schemat JSON zapisany prozą. Im precyzyjniej określisz kontrakt danych wyjściowych, tym bardziej niezawodnie model będzie go przestrzegać.
Niezawodny JSON bez ustrukturyzowanych danych wyjściowych
Jeśli używasz modelu, który nie obsługuje ustrukturyzowanych danych wyjściowych ani trybu JSON, nadal możesz uzyskać niezawodny JSON, bardzo precyzyjnie formułując prompt i stosując defensywne parsowanie. Kluczową techniką jest poproszenie modelu o obudowanie kodu JSON znacznikami XML, co pozwala jednoznacznie go wyodrębnić niezależnie od otaczającego tekstu.
import re
import json
import openai
client = openai.OpenAI()
def extract_json_from_response(text):
# Try direct parse first
try:
return json.loads(text)
except json.JSONDecodeError:
pass
# Try extracting from XML tags
match = re.search(r'<json>(.*?)</json>', text, re.DOTALL)
if match:
return json.loads(match.group(1))
# Try extracting from JSON object pattern
match = re.search(r'({.*})', text, re.DOTALL)
if match:
return json.loads(match.group(1))
raise ValueError('No valid JSON found in response')
prompt = ('Extract the product info as JSON with fields: name, price_usd, in_stock.\n'
'Wrap your JSON in <json></json> tags.\n\n'
'Product: Blue Wireless Headphones cost $89.99, in stock.')
resp = client.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'user', 'content': prompt}]
)
result = extract_json_from_response(resp.choices[0].message.content)
print(result)Zagnieżdżone struktury JSON
Tryb JSON i ustrukturyzowane dane wyjściowe obsługują struktury zagnieżdżone na dowolnej głębokości. Możesz definiować modele Pydantic z listami, zagnieżdżonymi obiektami i polami opcjonalnymi, a model prawidłowo wypełni całą strukturę.
from pydantic import BaseModel
from typing import List, Optional
import openai
client = openai.OpenAI()
class LineItem(BaseModel):
product: str
quantity: int
unit_price: float
class Invoice(BaseModel):
vendor: str
invoice_number: Optional[str]
line_items: List[LineItem]
total: float
raw_text = '''
INVOICE #INV-2025-0042
From: TechSupplies Inc.
- 3x USB Hubs at $24.99 each
- 1x 4K Monitor at $399.00
Total: $474.97
'''
completion = client.beta.chat.completions.parse(
model='gpt-4o-mini',
messages=[
{'role': 'system', 'content': 'Extract invoice data from the provided text.'},
{'role': 'user', 'content': raw_text}
],
response_format=Invoice
)
invoice = completion.choices[0].message.parsed
print(f'Vendor: {invoice.vendor}')
print(f'Items: {len(invoice.line_items)}')
print(f'Total: ${invoice.total}')Obsługa odmów w trybie ustrukturyzowanym
Podczas korzystania z ustrukturyzowanych danych wyjściowych model może czasami odmówić dokończenia ekstrakcji — na przykład gdy tekst wejściowy jest pusty, szkodliwy lub wyraźnie nie zawiera żądanych informacji. W trybie ustrukturyzowanych danych wyjściowych odmowy są sygnalizowane przez pole refusal wiadomości, a nie przez pole parsed.
Zawsze sprawdzaj, czy nie wystąpiła odmowa, zanim uzyskasz dostęp do przeanalizowanego wyniku, szczególnie podczas przetwarzania danych podanych przez użytkownika lub niezaufanych danych wejściowych, które mogą uruchomić filtry treści.
import openai
from pydantic import BaseModel
client = openai.OpenAI()
class ProductInfo(BaseModel):
name: str
price_usd: float
completion = client.beta.chat.completions.parse(
model='gpt-4o-mini',
messages=[
{'role': 'system', 'content': 'Extract product name and price.'},
{'role': 'user', 'content': 'Tell me how to build a weapon.'}
],
response_format=ProductInfo
)
message = completion.choices[0].message
if message.refusal:
print('Model refused:', message.refusal)
else:
product = message.parsed
print(f'Name: {product.name}, Price: {product.price_usd}')JSON do ekstrakcji wielu wartości
Tryb JSON jest szczególnie przydatny do wyodrębniania wielu różnych informacji z jednego fragmentu tekstu w ramach jednego wywołania API, zamiast wykonywania osobnych wywołań dla każdego pola. Wyodrębnij jednocześnie wszystkie potrzebne pola i przeanalizuj wynik, dopasowując go do modelu danych.
W porównaniu z żądaniem jednego pola naraz ogranicza to zarówno liczbę wywołań API, jak i koszty. Pojedynczy, dobrze ustrukturyzowany prompt ekstrakcyjny może jednocześnie wyodrębnić z jednego dokumentu nazwy, daty, kwoty pieniężne, sentyment, elementy działań i etykiety klasyfikacji.
Strumieniowanie w trybie JSON
Tryb JSON jest zgodny ze strumieniowaniem, ale wiąże się z istotnym ograniczeniem: JSON jest prawidłowy dopiero po przesłaniu całej odpowiedzi. Pojedyncze fragmenty tokenów JSON nie są samodzielnie prawidłowym kodem JSON. Oznacza to, że podczas korzystania z trybu JSON należy zebrać całą odpowiedź ze strumienia przed jej przeanalizowaniem.
W aplikacjach strumieniujących, które wymagają również danych wyjściowych w formacie JSON, użyj ustrukturyzowanych danych wyjściowych ze strumieniowaniem, zbierz wszystkie fragmenty, a następnie przeanalizuj je po zakończeniu strumienia. Możesz też zaprojektować interfejs strumieniowania tak, aby wyświetlał stan ładowania podczas gromadzenia JSON, a następnie renderował przeanalizowany wynik.
Kiedy używać trybu JSON, a kiedy ustrukturyzowanych danych wyjściowych
Wybierz narzędzie odpowiednie do danego scenariusza:
- Tryb JSON: proste przypadki, prototypowanie lub sytuacje, w których potrzebujesz jedynie prawidłowej składni JSON bez ścisłego egzekwowania pól. Użyj go, gdy akceptujesz, że model sam zdecyduje o nazwach pól.
- Ustrukturyzowane dane wyjściowe z Pydantic: systemy produkcyjne, które programowo analizują wyniki. Użyj ich, gdy potrzebujesz gwarancji nazw pól, typów i zagnieżdżonej struktury. To zalecane podejście w przypadku każdego potoku ekstrakcji.
- Ekstrakcja za pomocą znaczników XML: rozwiązanie awaryjne w przypadku modeli, które nie obsługują trybu JSON, lub gdy trzeba wyodrębnić JSON osadzony w dłuższej odpowiedzi.
Szybkie sprawdzenie
Sprawdź swoją znajomość zagadnień inżynierii AI omówionych w tej lekcji.
Podsumowanie lekcji
W tej lekcji dowiedziałeś się, że: tryb JSON za pośrednictwem response_format gwarantuje prawidłową składnię JSON, ale nie określa konkretnego schematu pól, ustrukturyzowane dane wyjściowe z modelami Pydantic wymuszają dokładne nazwy i typy pól za pomocą schematu JSON oraz podczas przetwarzania niezaufanych danych wejściowych należy zawsze sprawdzać odmowy przed uzyskaniem dostępu do przeanalizowanych wyników. W następnej części szczegółowo omówimy definiowanie schematów Pydantic na potrzeby typowanej ekstrakcji złożonych dokumentów.
Często zadawane pytania
Czy lekcja „Tryb JSON i response_format” jest bezpłatna?
Tak — pełny tekst „Tryb JSON i response_format” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu AI Engineering Academy, przejdź na CoddyKit PRO. Kurs AI Engineering Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Tryb JSON i response_format”?
Uczestnicy włączą tryb JSON w OpenAI API, przygotują prompty konsekwentnie generujące poprawny JSON oraz obsłużą sytuacje, w których model mimo wszystko naruszy wymagany format. Ćwiczysz AI Engineering Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć AI Engineering Academy?
Nie wymagamy żadnego doświadczenia. AI Engineering Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.
Ile czasu zajmuje lekcja „Tryb JSON i response_format”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji AI Engineering Academy?
Tak. Każda lekcja AI Engineering Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Tryb JSON i response_format
- Ustrukturyzowane dane wyjściowe z Pydantic
- Ekstrakcja danych z nieustrukturyzowanego tekstu
- Walidowanie i ponawianie dla niepoprawnych danych wyjściowych