Porównanie strukturalnego wyjścia wśród popularnych dostawców LLM – OpenAI, Gemini, Anthropic, Mistral i AWS Bedrock

Slightly different APIs require special approach. Slightly different APIs require special approach.

Page content

Oto porównanie wsparcia w formie obok siebie dla strukturalnego wyjścia (otrzymywanie niezawodnego JSON) wśród popularnych dostawców LLM, wraz z minimalnymi przykładami w Pythonie

kolorowe paski stojące

Już wcześniej zajmowaliśmy się tematem, jak żądać strukturalnego wyjścia z LLM hostowanego na Ollama. Tu omówimy, jak żądać tego samego od innych dostawców.

TL;DR macierz

Dostawca Natywne „tryb JSON” Wymuszanie JSON Schema Typowy pokrętło Uwagi
OpenAI Tak Tak (pierwszorzędne) response_format={"type":"json_schema", ...} Działa poprzez API odpowiedzi lub Chat Completions; można również wykonywać wywołania funkcji.
Google Gemini Tak Tak (pierwszorzędne) response_schema= + response_mime_type="application/json" Zwraca ściśle zweryfikowany JSON, gdy jest ustawiony schemat.
Anthropic (Claude) Pośrednie Tak (poprzez Użycie Narzędzia z JSON schema) tools=[{input_schema=...}] + tool_choice Wymuszanie modelu, aby „wywołał” narzędzie zdefiniowane przez schemat; daje argumenty zgodne ze schematem.
Mistral Tak Częściowe (tylko JSON; brak schematu serwerowego) response_format={"type":"json_object"} Zapewnia JSON, ale walidacja odbywa się po stronie klienta.
AWS Bedrock (platforma) Zależy od modelu Tak (poprzez schemat Narzędzia/Converse) toolConfig.tools[].toolSpec.inputSchema API Converse Bedrocka waliduje wejście narzędzia względem JSON schema.

Strukturalne wyjście z LLM — Ogólne informacje

Strukturalne wyjście z LLM odnosi się do możliwości dużych modeli językowych (LLMs) generowania odpowiedzi, które ściśle przestrzegają określonego, zdefiniowanego wcześniej formatu lub struktury zamiast generowania wolnej formy tekstu. To strukturalne wyjście może być w formatach takich jak JSON, XML, tabele lub szablony, co sprawia, że dane są czytelne dla maszyn, spójne i łatwo parsowalne przez oprogramowanie do użycia w różnych aplikacjach.

Strukturalne wyjścia różnią się od tradycyjnych wyjść LLM, które zwykle generują otwarte, naturalne teksty. Zamiast tego, strukturalne wyjścia wymuszają schemat lub format, taki jak obiekty JSON z zdefiniowanymi kluczami i typami wartości, lub konkretne klasy w wyjściu (np. odpowiedzi wielokrotnego wyboru, klasy sentymentu lub formaty wierszy bazy danych). Ten podejście poprawia niezawodność, zmniejsza błędy i halucynacje oraz ułatwia integrację do systemów takich jak bazy danych, API lub przepływów pracy.

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

  • Określanie szczegółowych instrukcji w promptach, aby kierować modelem do generowania wyjścia w pożądanym formacie.
  • Użycie narzędzi walidacji i parsowania, takich jak Pydantic w Pythonie, aby upewnić się, że wyjście odpowiada schematowi.
  • Czasami wymuszanie ograniczeń dekodowania opartych na gramatyce lub maszynach stanów skończonych, aby zapewnić zgodność na poziomie tokenów z formatem.

Zalety strukturalnych wyjść LLM obejmują:

  • Czytelność dla maszyn i łatwość integracji.
  • Zmniejszenie zmienności i błędów.
  • Poprawna przewidywalność i weryfikowalność dla zadań wymagających spójnych formatów danych.

Wyzwania obejmują projektowanie skutecznych schematów, obsługa złożonych danych zagnieżdżonych oraz potencjalne ograniczenia w możliwościach rozumowania w porównaniu do generowania wolnej formy tekstu.

Wszystkim razem, strukturalne wyjście umożliwia LLM, aby były bardziej przydatne w aplikacjach wymagających precyzyjnych, sformatowanych danych, a nie tylko tekstu czytelnego dla człowieka.

Przykłady strukturalnego wyjścia w Pythonie

Wszystkie fragmenty kodu wyodrębniają zdarzenie jako JSON: {title, date, location}. Można zmieniać klucze/modeli według własnego uznania.

1) OpenAI — JSON Schema (ś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": "Wyodrębnij zdarzenie z: 'PyData Sydney odbędzie się 2025-11-03 w Darling Harbour.'"}
    ],
    response_format={"type": "json_schema", "json_schema": schema},
)

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

Funkcja Strukturalnych Wyjść w OpenAI wymusza ten schemat po stronie serwera.


2) Google Gemini — schemat odpowiedzi + MIME JSON

import google.generativeai as genai
from google.genai import types

# Skonfiguruj z własnym kluczem API
# 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="Wyodrębnij zdarzenie z: 'PyData Sydney odbędzie się 2025-11-03 w Darling Harbour.'",
    generation_config=genai.GenerationConfig(
        response_mime_type="application/json",
        response_schema=schema,
    ),
)

print(resp.text)  # już poprawny JSON zgodny ze schematem

Gemini zwróci ściśle określony JSON, który będzie zgodny z response_schema.


3) Anthropic (Claude) — Użycie Narzędzia z JSON schema

from anthropic import Anthropic
import json

client = Anthropic()

tool = {
    "name": "extract_event",
    "description": "Zwróć szczegóły zdarzenia.",
    "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"},  # wymuszanie schematu
    messages=[{"role": "user", "content":
        "Wyodrębnij zdarzenie z: 'PyData Sydney odbędzie się 2025-11-03 w Darling Harbour.'"}],
)

# Claude „wywoła” narzędzie; pobierz argumenty (które odpowiadają Twojemu schematowi)
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 ogólnego przełącznika „tryb JSON”; zamiast tego Użycie Narzędzia z input_schema daje Ci zweryfikowane, zgodne ze schematem argumenty (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":
        "Zwróć JSON z kluczami title, date (YYYY-MM-DD), location dla: "
        "'PyData Sydney odbędzie się 2025-11-03 w Darling Harbour.'"}],
    response_format={"type": "json_object"}  # gwarantuje poprawny JSON
)

data = json.loads(resp.choices[0].message.content)
print(data)
# Wskazówka: waliduj `data` lokalnie względem swojego schematu Pydantic/JSON.

Tryb json_object w Mistralu wymusza kształt JSON (nie konkretny schemat) — walidacja odbywa się po stronie klienta.


5) AWS Bedrock — API Converse Schemat Narzędzia (model-agnostyczny)

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"}},  # wymuszanie schematu
    messages=[{"role":"user","content":[{"text":
        "Wyodrębnij zdarzenie z: 'PyData Sydney odbędzie się 2025-11-03 w Darling Harbour.'"}]}],
)

# Pobierz zawartość toolUse
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 JSON schema i wiele hostowanych modeli (np. Claude) obsługuje to poprzez Converse.


Praktyczne wskazówki i walidacja

  • Jeśli chcesz najsilniejszych gwarancji po stronie serwera: strukturalne wyjścia z OpenAI lub schemat odpowiedzi w Gemini.
  • Jeśli korzystasz już z Claude/Bedrock: zdefiniuj Narzędzie z JSON schema i wymuś jego użycie; odczytaj argumenty narzędzia jako swój obiekt typu.
  • Jeśli korzystasz z Mistrala: włącz json_object i waliduj lokalnie (np. z użyciem 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` z dowolnego dostawcy
except ValidationError as e:
    # obsłuż / ponów próbę / poproś model, aby naprawił z e.errors()
    print(e)

Przydatne linki