Porównanie strukturalnych wyjść u popularnych dostawców LLM – OpenAI, Gemini, Anthropic, Mistral i AWS Bedrock

Lekko różne API wymagają specjalnego podejścia.

Page content

Oto porównanie wsparcia dla ustrukturyzowanego wyjścia (podejmowanie wiarygodnego JSON z powrotem) między popularnymi dostawcami LLM, oraz minimalistyczne przykłady w Pythonie

colorised bars standing

Już wcześniej sprawdziliśmy, jak żądać ustrukturyzowanego wyjścia od LLM hostowanego na Ollama. Gdy JSON wróci w sieci, walidacja ustrukturyzowanego wyjścia LLM w Pythonie, która się sprawdza omawia parsowanie, sprawdzenia Pydantic, ponowne próby i testy w Twojej usłudze. Tutaj sprawdzamy, jak żądać tego samego od innych dostawców.

Macierz TL;DR

Dostawca Natywny „tryb JSON” Egzekucja Schematu JSON Typowy parametr Uwagi
OpenAI Tak Tak (pierwszoklasowy) response_format={"type":"json_schema", ...} Działa przez Responses API lub Chat Completions; może również wykonywać wywoływanie funkcji.
Google Gemini Tak Tak (pierwszoklasowy) response_schema= + response_mime_type="application/json" Zwraca ściśle zwalidowany JSON, gdy schemat jest ustawiony.
Anthropic (Claude) Pośrednio Tak (przez Tool Use ze schematem JSON) tools=[{input_schema=...}] + tool_choice Wymuś model na „wołanie” Twojego narzędzia zdefiniowanego schematem; zwraca argumenty w kształcie schematu.
Mistral Tak Częściowo (tylko JSON; brak schematu po stronie serwera) response_format={"type":"json_object"} Gwarantuje JSON, ale walidujesz w swoim schemacie po stronie klienta.
AWS Bedrock (platforma) Zależy od modelu Tak (przez Tool/Converse schema) toolConfig.tools[].toolSpec.inputSchema API Converse w Bedrocku waliduje wejście narzędzia względem schematu JSON.

LLM Ustrukturyzowane Wyjście - Informacje ogólne

Ustrukturyzowane wyjście LLM odnosi się do zdolności dużych modeli językowych (LLM) do generowania odpowiedzi, które ściśle adhere do zdefiniowanego z góry, konkretnego formatu lub struktury, zamiast generowania tekstu wolnego. To ustrukturyzowane wyjście może mieć formę JSON, XML, tabel lub szablonów, co czyni dane maszynowo czytanymi, spójnymi i łatwymi do sparsowania przez oprogramowanie do użytku w różnych aplikacjach.

Ustrukturyzowane wyjścia różnią się od tradycyjnych wyjść LLM, które typowo generują otwarty, naturalny tekst językowy. Zamiast tego, ustrukturyzowane wyjścia egzekwują schemat lub format, taki jak obiekty JSON z zdefiniowanymi kluczami i typami wartości, lub konkretne klasy w wyjściu (np. odpowiedzi testowe z wielokrotnym wyborem, klasy sentymentu lub formaty wierszy bazy danych). To podejście poprawia wiarygodność, redukuje błędy i halucynacje oraz upraszcza integrację z systemami takimi jak bazy danych, API lub przepływy pracy.

Generowanie ustrukturyzowanych wyjść w LLM często obejmuje techniki takie jak:

  • Określanie szczegółowych instrukcji w promptach, aby skierować model do wygenerowania wyjścia w pożądanym formacie.
  • Używanie narzędzi do walidacji i parsowania, takich jak Pydantic w Pythonie, aby upewnić się, że wyjście pasuje do schematu.
  • Czasami egzekwowanie ograniczeń dekodowania opartych na gramatyce lub maszynach stanów skończonych, aby zapewnić zgodność tokenów z formatem.

Korzyści z ustrukturyzowanych wyjść LLM obejmują:

  • Maszynową czytelność i łatwość integracji.
  • Zmniejszoną zmienność i błędy.
  • Poprawioną przewidywalność i weryfikowalność dla zadań wymagających spójnych formatów danych.

Wyzwania obejmują projektowanie efektywnych schematów, obsługę złożonych, zagnieżdżonych danych oraz potencjalne ograniczenia w zdolnościach rozumowania w porównaniu z generowaniem swobodnego tekstu.

Ogólnie rzecz biorąc, ustrukturyzowane wyjście pozwala LLMom być bardziej użytecznymi w aplikacjach wymagających precyzyjnych, sformatowanych danych, a nie tylko tekstu czytelnego dla człowieka.

Przykłady Ustrukturyzowanego Wyjścia w Pythonie

Wszystkie fragmenty kodu wyodrębniają informacje o wydarzeniu jako JSON: {title, date, location}. Zamieniaj klucze/modeli według uznania.

1) OpenAI — Schemat JSON (ściśle)

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 firmy 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 ściśle JSON, który odpowiada response_schema.


3) Anthropic (Claude) — Użycie 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 posiada ogólnego przełącznika „trybu JSON”; zamiast tego Tool Use z input_schema daje Ci zwalidowane, w kształcie schematu argumenty (a Ty 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 Mistrala egzekwuje kształt JSON (nie dokładny Twój schemat) — waliduj po stronie klienta.


5) AWS Bedrock — Schemat narzędzi 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 wejście narzędzia względem Twojego schematu JSON, a wiele hostowanych modeli (np. Claude) wspiera to przez Converse.


Praktyczne wskazówki i Walidacja

  • Jeśli chcesz najsilniejszych gwarancji po stronie serwera: ustrukturyzowane wyjścia OpenAI lub schemat odpowiedzi Gemini.
  • Jeśli już korzystasz z Claude/Bedrock: zdefiniuj Tool ze schematem JSON i wymuś jego użycie; odczytaj argumenty narzędzia jako swój obiekt typowany.
  • Jeśli używasz Mistral: włącz json_object i 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

Subskrybuj

Otrzymuj nowe wpisy o systemach, infrastrukturze i inżynierii AI.