Porównanie strukturalnych danych wyjściowych w popularnych dostawcach LLM – OpenAI, Gemini, Anthropic, Mistral i AWS Bedrock
Nieco odmienne interfejsy API wymagają specjalnego podejścia.
Oto porównanie obsługi strukturyzowanej odpowiedzi (uzyskiwanie niezawodnego JSONa) u popularnych dostawców modeli LLM, wraz z minimalnymi przykładami w języku Python

Już wcześniej przyglądaliśmy się temu, jak żądać strukturyzowanej odpowiedzi od modelu LLM hostowanego na Ollama. Gdy JSON wraca po sieci, artykuł o walidacji strukturyzowanej odpowiedzi LLM w Pythonie, która trzyma się schematu omija kwestie parsowania, sprawdzeń z Pydantic, ponownych prób i testów w Twojej usłudze. Tutaj przeglądamy, jak żądać tego samego od innych dostawców.
Macierz TL;DR
| Dostawca | Nativny „tryb JSON” | Egzekucja Schematu JSON | Typowa konfiguracja | Uwagi |
|---|---|---|---|---|
| OpenAI | Tak | Tak (pierwszoklasowa) | response_format={"type":"json_schema", ...} |
Działa poprzez Responses API lub Chat Completions; można też używać function calling. |
| Google Gemini | Tak | Tak (pierwszoklasowa) | response_schema= + response_mime_type="application/json" |
Zwraca rygorystycznie zwalidowany JSON, gdy schemat jest ustawiony. |
| Anthropic (Claude) | Niebezpośrednio | Tak (poprzez Tool Use ze schematem JSON) | tools=[{input_schema=...}] + tool_choice |
Wymusza na modelu „wywołanie” narzędzia zdefiniowanego schematem; zwraca argumenty zgodne ze schematem. |
| Mistral | Tak | Częściowa (tylko JSON; brak schematu po stronie serwera) | response_format={"type":"json_object"} |
Zapewnia JSON, ale walidujesz go pod kątem schematu po stronie klienta. |
| AWS Bedrock (platforma) | Zależy od modelu | Tak (poprzez schemat Tool/Converse) | toolConfig.tools[].toolSpec.inputSchema |
API Converse w Bedrock waliduje dane wejściowe narzędzia pod kątem schematu JSON. |
Strukturyzowana odpowiedź LLM - Informacje ogólne
Strukturyzowana odpowiedź LLM odnosi się do możliwości dużych modeli językowych (LLMs) generowania odpowiedzi, które ściśle przestrzegają zdefiniowanego z góry formatu lub struktury, zamiast produkować tekst w formie swobodnej. Ta strukturyzowana odpowiedź może przybierać formaty takie jak JSON, XML, tabele lub szablony, co sprawia, że dane są czytelne dla maszyny, spójne i łatwo parsowalne przez oprogramowanie do użytku w różnych aplikacjach.
Strukturyzowane odpowiedzi różnią się od tradycyjnych odpowiedzi LLM, które typowo generują otwarty, naturalny tekst. Zamiast tego, strukturyzowane odpowiedzi egzekwują schemat lub format, taki jak obiekty JSON z zdefiniowanymi kluczami i typami wartości, lub konkretne klasy w odpowiedzi (np. odpowiedzi wielokrotnego wyboru, klasy sentymentu lub formaty wierszy bazy danych). To podejście poprawia niezawodność, redukuje błędy i halucynacje oraz upraszcza integrację z systemami takimi jak bazy danych, API lub przepływy pracy.
Generowanie strukturyzowanych odpowiedzi w LLM często obejmuje techniki takie jak:
- Określanie szczegółowych instrukcji w prompcie, aby prowadzić model do wygenerowania odpowiedzi w pożądanym formacie.
- Używanie narzędzi walidacji i parsowania, takich jak Pydantic w Python, aby zapewnić zgodność odpowiedzi ze schematem.
- Czasem wymuszanie ograniczeń dekodowania opartych na gramatyce lub automatach skończonych, aby zapewnić zgodność na poziomie tokenów z formatem.
Korzyści ze strukturyzowanych odpowiedzi LLM obejmują:
- Czytelność dla maszyny i łatwość integracji.
- Zredukowaną zmienność i błędy.
- Poprawioną przewidywalność i weryfikowalność w zadaniach wymagających spójnych formatów danych.
Wyzwania obejmują projektowanie skutecznych schematów, obsługę złożonych, zagnieżdżonych danych oraz potencjalne ograniczenia w zdolnościach rozumowania w porównaniu do generowania tekstu w formie swobodnej.
Ogólnie rzecz biorąc, strukturyzowana odpowiedź umożliwia LLMom bycie bardziej przydatnymi w aplikacjach wymagających precyzyjnych, sformatowanych danych, a nie tylko tekstu czytelnego dla człowieka.
Przykłady strukturyzowanej odpowiedzi w Pythonie
Wszystkie fragmenty kodu wydobywają informacje o wydarzeniu jako JSON: {title, date, location}. Zmień klucze/modeli według uznania.
1) OpenAI — Schemat JSON (rygorystyczny)
from openai import OpenAI
import json
client = OpenAI()
schema = {
"name": "Event",
"schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"date": {"type": "string", "description": "YYYY-MM-DD"},
"location": {"type": "string"}
},
"required": ["title", "date", "location"],
"additionalProperties": False
},
"strict": True
}
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "user", "content": "Extract the event from: 'PyData Sydney is on 2025-11-03 at Darling Harbour.'"}
],
response_format={"type": "json_schema", "json_schema": schema},
)
data = json.loads(resp.choices[0].message.content)
print(data)
Funkcja Structured Outputs OpenAI egzekwuje ten schemat po stronie serwera.
2) Google Gemini — schemat odpowiedzi + MIME JSON
import google.generativeai as genai
from google.genai import types
# Configure with your API key
# genai.configure(api_key="your-api-key")
schema = types.Schema(
type=types.Type.OBJECT,
properties={
"title": types.Schema(type=types.Type.STRING),
"date": types.Schema(type=types.Type.STRING),
"location": types.Schema(type=types.Type.STRING),
},
required=["title", "date", "location"],
additional_properties=False,
)
resp = genai.generate_content(
model="gemini-2.0-flash",
contents="Extract the event from: 'PyData Sydney is on 2025-11-03 at Darling Harbour.'",
generation_config=genai.GenerationConfig(
response_mime_type="application/json",
response_schema=schema,
),
)
print(resp.text) # already valid JSON per schema
Gemini zwróci rygorystyczny JSON, który zgodny jest z response_schema.
3) Anthropic (Claude) — Używanie narzędzi (Tool Use) ze schematem JSON
from anthropic import Anthropic
import json
client = Anthropic()
tool = {
"name": "extract_event",
"description": "Return event details.",
"input_schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"date": {"type": "string"},
"location": {"type": "string"}
},
"required": ["title", "date", "location"],
"additionalProperties": False
}
}
msg = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=256,
tools=[tool],
tool_choice={"type": "tool", "name": "extract_event"}, # force schema
messages=[{"role": "user", "content":
"Extract the event from: 'PyData Sydney is on 2025-11-03 at Darling Harbour.'"}],
)
# Claude will "call" the tool; grab the args (which match your schema)
tool_use = next(b for b in msg.content if b.type == "tool_use")
print(json.dumps(tool_use.input, indent=2))
Claude nie ma generycznego przełącznika „trybu JSON”; zamiast tego Tool Use ze schematem input_schema daje Ci zwalidowane argumenty zgodne ze schematem (i możesz wymusić jego użycie).
4) Mistral — Tryb JSON (walidacja po stronie klienta)
from mistralai import Mistral
import json
client = Mistral()
resp = client.chat.complete(
model="mistral-large-latest",
messages=[{"role":"user","content":
"Return JSON with keys title, date (YYYY-MM-DD), location for: "
"'PyData Sydney is on 2025-11-03 at Darling Harbour.'"}],
response_format={"type": "json_object"} # guarantees valid JSON
)
data = json.loads(resp.choices[0].message.content)
print(data)
# Tip: validate `data` against your Pydantic/JSON Schema locally.
json_object w Mistralu egzekwuje kształt JSONa (nie Twój dokładny schemat) — waliduj po stronie klienta.
5) AWS Bedrock — Schemat narzędzia w API Converse (niezależny od modelu)
import boto3, json
bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
model_id = "anthropic.claude-3-5-sonnet-20240620-v1:0"
tools = [{
"toolSpec": {
"name": "extract_event",
"inputSchema": {
"json": {
"type": "object",
"properties": {
"title": {"type": "string"},
"date": {"type": "string"},
"location": {"type": "string"}
},
"required": ["title","date","location"],
"additionalProperties": False
}
}
}
}]
resp = bedrock.converse(
modelId=model_id,
toolConfig={"tools": tools},
toolChoice={"tool": {"name": "extract_event"}}, # force schema
messages=[{"role":"user","content":[{"text":
"Extract the event from: 'PyData Sydney is on 2025-11-03 at Darling Harbour.'"}]}],
)
# Pull toolUse content
tool_use = next(
c["toolUse"] for c in resp["output"]["message"]["content"] if "toolUse" in c
)
print(json.dumps(tool_use["input"], indent=2))
Bedrock waliduje dane wejściowe narzędzia pod kątem Twojego schematu JSON, a wiele hostowanych modeli (np. Claude) obsługuje je poprzez Converse.
Praktyczne wskazówki i walidacja
- Jeśli chcesz najsilniejszych gwarancji po stronie serwera: strukturyzowane odpowiedzi OpenAI lub schemat odpowiedzi Gemini.
- Jeśli korzystasz już z Claude/Bedrock: zdefiniuj Narzędzie ze schematem JSON i wymuś jego użycie; odczytaj argumenty narzędzia jako swój typowany obiekt.
- Jeśli używasz Mistral: włącz
json_objecti waliduj lokalnie (np. z Pydantic).
Wzorzec walidacji (działa dla wszystkich)
from pydantic import BaseModel, ValidationError
class Event(BaseModel):
title: str
date: str
location: str
try:
event = Event.model_validate(data) # `data` from any provider
except ValidationError as e:
# handle / retry / ask model to fix with e.errors()
print(e)
Przydatne linki
- https://platform.openai.com/docs/guides/structured-outputs
- https://ai.google.dev/gemini-api/docs/structured-output
- https://docs.mistral.ai/capabilities/structured-output/json_mode/
- https://aws.amazon.com/blogs/machine-learning/structured-data-response-with-amazon-bedrock-prompt-engineering-and-tool-use
- https://github.com/aws-samples/anthropic-on-aws/blob/main/complex-schema-tool-use/README.md
- https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages.html
- Jak żądać strukturyzowanej odpowiedzi od LLM hostowanego na Ollama
- Cheat sheet Python
- AWS SAM + AWS SQS + Python PowerTools
- Porównanie wydajności AWS lambda: JavaScript vs Python vs Golang