
LLM strukturerte utdata er mekanismen som garanterer at en språkmodells svar samsvarer med et forhåndsdefinert skjema -- ikke bare gyldig JSON, men skjemagyldig JSON med nøyaktig de feltene, typene og begrensningene du angav. Alle store leverandører støtter nå dette nativt, og det har endret hvordan LLM-applikasjoner i produksjon bygges.
Hurtigoppsummering: Strukturerte Utdata i Korthet
Hvis du har lite tid, her er bildet i 2026:
| Aspekt | Detaljer |
|---|---|
| Hva det er | Skjemastyrt respons fra LLM-er -- garantert struktur, ikke "best mulig" |
| Hvem støtter det | OpenAI, Anthropic, Gemini, Cohere, xAI (Grok), pluss lokalt via Ollama/vLLM |
| Nøkkelmekanisme | Begrenset dekoding -- ugyldige tokens maskeres før sampling |
| JSON-modus vs. Streng modus | JSON-modus = kun gyldig syntaks. Streng modus = full samsvar med skjema |
| Python-bibliotek | Pydantic (BaseModel + Field) for skjemadefinisjon |
| TypeScript-bibliotek | Zod (z.object + .describe) for skjemadefinisjon |
| Beste startmetode | OpenAI med Pydantic eller Zod via native SDK |
| Beste produksjonsbibliotek | Instructor (Python) eller native SDK (TypeScript) |
| Største fallgruve | Å legge resoneringsfeltet ETTER svarsfeltet -- modellen bestemmer seg før den tenker |
| Latensoverhead | 50-200ms ved første anrop (skjemakompilering), deretter bufret |
Nå skal vi gå gjennom hvert punkt.
Hva Er LLM Strukturerte Utdata?
Strukturerte utdata er forskjellen mellom å håpe at et LLM returnerer gyldig JSON og å garantere det. Når du aktiverer strukturerte utdata, kan modellen fysisk ikke produsere tokens som bryter ditt skjema. Du definerer et JSON-skjema (eller en Pydantic-modell, eller et Zod-skjema), sender det til API-et, og får tilbake et svar som matcher det hver gang.
Hvorfor er dette viktig? Før strukturerte utdata skrev utviklere skjøre regex-parsere, pakket inn hvert LLM-anrop i try/catch JSON.parse-blokker, og måtte fortsatt håndtere "nesten riktige" svar -- gyldig JSON som manglet et felt eller hadde feil type. Hele den feilklassen er borte.
Det er tre nivåer av strukturhåndhevelse, og de representerer en tydelig utvikling:
- Promptteknikk -- "Vennligst returner JSON med disse feltene." Upålitelig. Modellen kan følge 80-90% av gangene.
- JSON-modus -- Garanterer syntaktisk gyldig JSON, men håndhever ikke skjemaet ditt. Du kan få
{"foo": "bar"}når du forventet{"name": string, "age": number}. - Streng modus / Begrenset dekoding -- Garanterer 100% samsvar med skjema. Modellen kan bokstavelig talt ikke sende ut ugyldige tokens. Det er det "strukturerte utdata" betyr i 2026.
Fra tidlig 2026 støtter OpenAI, Anthropic og Google Gemini alle native strukturerte utdata. Økosystemet har konvergert.
Konklusjon: Hvis du analyserer LLM-svar med regex eller JSON.parse i produksjon, gjør du det på den vanskelige måten. Native strukturerte utdata eliminerer hele den feilklassen.
JSON-modus vs. Streng Modus: Hva Har Egentlig Endret Seg?
Denne distinksjonen forvirrer mange utviklere fordi navnene høres like ut. Det er de ikke.
| Funksjon | JSON-modus | Streng modus (Strukturerte Utdata) |
|---|---|---|
| API-parameter | type: "json_object" | type: "json_schema" med strict: true |
| Garanterer gyldig JSON | Ja | Ja |
| Garanterer samsvar med skjema | Nei | Ja |
| Mekanisme | Post-hoc token-skjevhet | Begrenset dekoding (FSM) |
| Kan returnere uventede felt | Ja | Nei |
| Kan utelate obligatoriske felt | Ja | Nei |
| Typehåndhevelse | Ingen | Full (string, number, array, osv.) |
| Når man bruker det | Du har ikke et skjema på forhånd | Alt i produksjon |
Tidslinjen: OpenAI introduserte JSON-modus i slutten av 2023. Det var et skritt fremover, men utviklere innså raskt at "gyldig JSON" ikke var nok -- de trengte skjemagyldig JSON. I august 2024 lanserte OpenAI Strukturerte Utdata med Streng modus, som bruker begrenset dekoding for å garantere samsvar med skjema. Innen 2025-2026 hadde alle store leverandører adoptert den samme tilnærmingen.
JSON-modus har fortsatt et smalt brukstilfelle: når du virkelig ikke kjenner formen på svaret på forhånd og bare vil ha noe gyldig JSON for ustrukturert utforskning. Men det er sjeldent i produksjon.
Konklusjon: Bruk streng modus for alt i produksjon. JSON-modus er effektivt utdatert for skjemabundne brukstilfeller. Hvis du har et skjema (og det bør du ha), bruk type: "json_schema" med strict: true.
Hvordan Fungerer Egentlig Begrenset Dekoding?
Her er mekanismen som gjør 100% samsvar med skjema mulig -- ikke 99,9%, men bokstavelig talt 100%.
Når du sender et JSON-skjema til en leverandør med Streng modus aktivert, kompileres skjemaet til en endelig tilstandsmaskin (FSM). Denne FSM representerer hver gyldige vei gjennom skjemaet ditt. Ved hvert tokengenerasjonssteg sjekker slutningsenginen hvilke tokens som ville holde utdataene på en gyldig vei og hvilke som ikke ville det. Ugyldige tokens får sine logits satt til negativt uendelig før sampling, noe som betyr at de har nullsannsynlighet for å bli valgt.
<!-- IMAGE: constrained-decoding diagram showing FSM token masking during structured output generation -->Tenk på det som automatisk fullføring på steroider. Hvis modellen nettopp har produsert {"rating": og skjemaet ditt sier at rating er et heltall, er de eneste tillatte neste tokenene siffertoken. Anførselstegn, bokstaver, parenteser -- alt maskert. Modellen kan ikke produsere "fem" selv om den "vil" det.
Dette er den samme kjernemekansen som brukes av XGrammar (motoren bak vLLM, SGLang og de fleste lokale slutningsservere) og Outlines (Python-biblioteket med åpen kildekode for begrenset generering). API-leverandørene har bare bygd det inn i sin slutningsinfrastruktur.
Det er én avveining å kjenne til: den første forespørselen med et nytt skjema gir en kompileringslatenstillegg (typisk 50-200ms) mens FSM bygges. Etterfølgende forespørsler med samme skjema bruker en bufret FSM og legger til nesten null overhead. Det er også en subtil kvalitetsvurdering -- å begrense token-vokabularet kan noen ganger redusere utdatakvaliteten for kreative eller friformsfelt, så hold skjemaene dine fokusert på virkelig strukturerte data.
Konklusjon: Begrenset dekoding er det som skiller "fungerer vanligvis" fra "fungerer alltid." Det er ingeniørarbeidet som gjør strukturerte utdata produksjonsklare. Se også vår beste LLM structured output-biblioteker.
Implementering hos Flere Leverandører: OpenAI, Anthropic og Gemini
Her er noe ingen av de andre guidene viser deg: den samme ekstraksjonsoppgaven implementert hos alle tre store leverandørene. Vi vil trekke ut en strukturert produktanmeldelse fra ustrukturert tekst.
Pydantic-skjemaet (delt mellom alle leverandører):
from pydantic import BaseModel, Field
from typing import Literal
class ProductReview(BaseModel):
reasoning: str = Field(description="Think through the review before scoring")
rating: int = Field(description="Rating from 1-5", ge=1, le=5)
sentiment: Literal["positive", "negative", "neutral"]
pros: list[str] = Field(description="Key positive points")
cons: list[str] = Field(description="Key negative points")
summary: str = Field(description="One-sentence summary")OpenAI-implementering
from openai import OpenAI
client = OpenAI()
response = client.beta.chat.completions.parse(
model="gpt-4o-2024-08-06",
messages=[
{"role": "system", "content": "Extract a structured review from the text."},
{"role": "user", "content": review_text}
],
response_format=ProductReview, # Pydantic-modell direkte
)
review = response.choices[0].message.parsed # Typet ProductReview-objektOpenAIs implementering er den mest modne. parse()-metoden aksepterer en Pydantic-modell direkte og returnerer et typet objekt. En begrensning: OpenAIs Streng modus støtter et delsett av JSON-skjema -- ingen $ref, begrenset anyOf, og alle felt må være obligatoriske med additionalProperties: false.
Anthropic-implementering
from anthropic import Anthropic
client = Anthropic()
response = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=1024,
messages=[
{"role": "user", "content": f"Extract a structured review:\n\n{review_text}"}
],
output_config={
"format": {
"type": "json_schema",
"json_schema": ProductReview.model_json_schema()
}
}
)
import json
review_data = json.loads(response.content[0].text)
review = ProductReview(**review_data)Anthropics native strukturerte utdata bruker output_config.format med et JSON-skjema. Det nådde GA tidlig i 2026. Anthropic støtter også det eldre mønsteret med å definere et "falskt" verktøy og trekke ut via tool_use -- det fungerer fortsatt, men native strukturerte utdata er renere for ren ekstraksjon.
Gemini-implementering
from google import genai
client = genai.Client()
response = client.models.generate_content(
model="gemini-2.0-flash",
contents=f"Extract a structured review:\n\n{review_text}",
config={
"response_mime_type": "application/json",
"response_schema": ProductReview, # Pydantic-modell direkte
}
)
import json
review = ProductReview(**json.loads(response.text))Gemini støtter Pydantic-modeller direkte i Python SDK via response_schema. En unik funksjon: Gemini respekterer propertyOrdering i skjemaet, slik at du kan kontrollere feltutdataordenen (nyttig for resonemang-først-mønstret).
Leverandørsammenligning
| Funksjon | OpenAI | Anthropic | Gemini |
|---|---|---|---|
| API-parameter | response_format | output_config.format | response_schema |
| Skjemainput | Pydantic eller JSON-skjema | JSON-skjema | Pydantic eller JSON-skjema |
| Streng modus | strict: true | Implisitt med json_schema | Implisitt |
| Streaming | Ja (delvis JSON) | Ja | Ja |
| Håndtering av avvisning | message.refusal-felt | Feilsvar | Feilsvar |
| Verktøybruk alternativ | Ja | Ja (original metode) | Ja |
| Skjemakompileringsbuffer | Ja (serversiden) | Ja | Ja |
| Egenskapsordning | Ingen native støtte | Nei | Ja (propertyOrdering) |
Konklusjon: OpenAI har den mest polerte DX med sin parse()-metode. Anthropic tilbyr de mest kapable underliggende modellene. Geminis egenskapsordning er unikt nyttig. Alle tre gjør jobben -- velg basert på det eksisterende leverandørforholdet ditt.
Pydantic-mønstre for Python-utviklere
Pydantic er de facto-standarden for å definere strukturerte utdataskjemaer i Python. Her er mønstrene som teller.
Grunnleggende Skjema med Beskrivelser
from pydantic import BaseModel, Field
from typing import Literal, Optional
class ExtractedEntity(BaseModel):
reasoning: str = Field(description="Think step by step about the entity")
name: str = Field(description="Full name of the entity")
entity_type: Literal["person", "company", "location"]
confidence: float = Field(description="Confidence score 0.0-1.0", ge=0.0, le=1.0)
context: Optional[str] = Field(description="Surrounding context, if relevant")Disse description-strengene er ikke bare for dokumentasjon -- de blir en del av JSON-skjemaet som sendes til modellen og påvirker direkte hva modellen genererer. Tenk på dem som promptteknikk innenfor skjemaet.
Nestede Modeller
class Address(BaseModel):
street: str
city: str
country: str
postal_code: Optional[str] = None
class Company(BaseModel):
reasoning: str = Field(description="Analysis of the company details")
name: str
industry: Literal["tech", "finance", "healthcare", "retail", "other"]
headquarters: Address # Nestet modell
key_products: list[str] = Field(description="Top 3 products or services")Hold nesting til maks 2-3 nivåer. Dypt nestede skjemaer øker feilratene og bremser skjemakompileringen.
Resonemang-Først-Mønstret
Dette er det enkelt mest innflytelsesrike skjemadesignmønstret. Legg et reasoning-felt før svarsfeltet:
# Dårlig -- modellen binder seg til et svar før den tenker
class ClassificationBad(BaseModel):
category: Literal["spam", "ham"]
confidence: float
# Godt -- modellen resonnerer gjennom problemet først
class ClassificationGood(BaseModel):
reasoning: str = Field(description="Analyze the text before classifying")
category: Literal["spam", "ham"]
confidence: float = Field(ge=0.0, le=1.0)LLM-er genererer tokens fra venstre til høyre. Feltrekkefølgen er promptrekkefølgen. Resonemang først betyr at modellen må arbeide gjennom problemet før den binder seg til en kategori. Det er tankerekke bakt inn i skjemaet.
JSON-skjema-eksport
# Generer JSON-skjemaet for en hvilken som helst Pydantic-modell
schema = ProductReview.model_json_schema()
# Send dette til hvilken som helst leverandør som aksepterer rå JSON-skjemaKonklusjon: Pydantic + beskrivende felt + resonemang-først-rekkefølge er Python-triaden for strukturerte utdata. Mestre disse tre mønstrene og du håndterer 90% av brukstilfellene.
Zod-mønstre for TypeScript-utviklere
Zod er TypeScript-ekvivalenten til Pydantic -- og like sentral for arbeidsflytene for strukturerte utdata.
Grunnleggende Skjema med Beskrivelser
import { z } from "zod";
const ProductReview = z.object({
reasoning: z.string().describe("Think through the review before scoring"),
rating: z.number().int().min(1).max(5),
sentiment: z.enum(["positive", "negative", "neutral"]),
pros: z.array(z.string()).describe("Key positive points"),
cons: z.array(z.string()).describe("Key negative points"),
summary: z.string().describe("One-sentence summary"),
});
// Utlede TypeScript-typen automatisk
type ProductReview = z.infer<typeof ProductReview>;Akkurat som Pydantics Field(description=...) blir Zods .describe() en del av JSON-skjemaet og veileder modellens utdata.
Integrasjon med OpenAI Node SDK
import OpenAI from "openai";
import { zodResponseFormat } from "openai/helpers/zod";
const client = new OpenAI();
const response = await client.beta.chat.completions.parse({
model: "gpt-4o-2024-08-06",
messages: [
{ role: "system", content: "Extract a structured review." },
{ role: "user", content: reviewText },
],
response_format: zodResponseFormat(ProductReview, "product_review"),
});
const review = response.choices[0].message.parsed; // Typet!Integrasjon med Vercel AI SDK
import { generateObject } from "ai";
import { openai } from "@ai-sdk/openai";
const { object: review } = await generateObject({
model: openai("gpt-4o"),
schema: ProductReview,
prompt: `Extract a structured review:\n\n${reviewText}`,
});
// review er fullt typet som ProductReviewVercel AI SDK bruker Zod nativt med generateObject(), noe som gjør det til den reneste TypeScript-integrasjonen. Det fungerer med OpenAI, Anthropic, Gemini og andre leverandører gjennom et enhetlig API.
JSON-skjema-konvertering
import { zodToJsonSchema } from "zod-to-json-schema";
const jsonSchema = zodToJsonSchema(ProductReview);
// Bruk med hvilken som helst leverandør som aksepterer rå JSON-skjemaKonklusjon: Zod + .describe() + Vercel AI SDK er TypeScript-stacken for strukturerte utdata. Hvis du er i Node/Next.js-økosystemet, er dette veien med minst motstand.
Strukturerte Utdata vs. Funksjonskalling: Når Bruker du Hva?
Dette er en av de vanligste forvirringskildene. Begge involverer skjemaer, begge returnerer strukturerte data -- men de løser forskjellige problemer.
Strukturerte utdata sier: "Gi meg data i denne eksakte formen." Det er for ekstraksjon, klassifisering og formatering. Du trekker ut strukturert informasjon fra ustrukturert tekst.
Funksjonskalling (verktøybruk) sier: "Her er handlinger du kan ta -- bestem hvilken du skal kjøre og oppgi argumentene." Det er for agentarbeidsflyter der modellen velger blant flere verktøy og utløser handlinger.
Forvirringen er historisk forståelig. Anthropics originale "strukturerte utdata" var bokstavelig talt funksjonskalling -- du definerte et falskt verktøy kalt extract_review og hentet argumentene. Det fungerer fortsatt, men native strukturerte utdata er enklere for ren ekstraksjon.
| Scenario | Beste tilnærming | Hvorfor |
|---|---|---|
| Trekke ut data fra tekst | Strukturerte utdata | Direkte, lavere latens, enkelt skjema |
| Klassifisere i kategorier | Strukturerte utdata | Ett svar, ett skjema |
| Agent som bestemmer hvilket verktøy å kalle | Funksjonskalling | Modellen velger blant flere verktøy |
| Flerstegs-orkestrering | Funksjonskalling | Sekvensielle verktøyanrop |
| Trekke ut data OG bestemme neste handling | Begge | Strukturerte utdata for ekstraksjon, funksjonskalling for orkestrering |
Strukturerte utdata driver verktøykalling-pipelinen i AI-agentsystemer. Se vår guide om AI-agenter for bedrifter for hvordan disse passer inn i produksjonsarbeidsflyter.
Konklusjon: Bruk strukturerte utdata når du vet hvilken form data skal ha. Bruk funksjonskalling når modellen trenger å velge en handling. I praksis bruker de fleste applikasjoner begge -- strukturerte utdata for dataekstraksjon og funksjonskalling for agentorkestrering. Du kan også være interessert i guide til LLM function calling.
Produksjonsmønstre: Feil, Nye Forsøk og Streaming
Å få strukturerte utdata til å fungere i en demo er enkelt. Å holde dem pålitelige i produksjon krever håndtering av tre ting: avvisninger, valideringsfeil og streaming.
Håndtering av Avvisninger
Noen ganger nekter en modell å generere den forespurte utdaten -- typisk fordi sikkerhetsfiltre flagget inndataene. Når dette skjer, returnerer API-er for strukturerte utdata ikke skjemaet ditt. De returnerer en avvisning.
response = client.beta.chat.completions.parse(
model="gpt-4o-2024-08-06",
messages=messages,
response_format=ProductReview,
)
# ALLTID sjekk for avvisning før du aksesserer analysert innhold
if response.choices[0].message.refusal:
print(f"Model refused: {response.choices[0].message.refusal}")
else:
review = response.choices[0].message.parsedHvis du hopper over avvisningssjekken og prøver å aksessere .parsed ved en avvisning, får du None og en forvirrende nedstrømsfeil. Sjekk alltid først.
Mønster for Nye Forsøk med Valideringsfeedback
Samsvar med skjema garanteres av begrenset dekoding, men semantisk korrekthet gjøres ikke. Modellen kan returnere {"rating": 1, "sentiment": "positive"} -- gyldig skjema, motstridende innhold. Det er der validering + nye forsøk kommer inn.
import instructor
client = instructor.from_openai(OpenAI())
# Instructor håndterer nye forsøk automatisk
review = client.chat.completions.create(
model="gpt-4o",
response_model=ProductReview,
max_retries=3, # Nye forsøk med valideringsfeilfeedback
messages=[
{"role": "user", "content": review_text}
],
)Instructor sender valideringsfeilen tilbake til modellen ved nytt forsøk, slik at den kan selvkorrigere. For manuelle gjenforsøksmønstre uten Instructor:
from pydantic import ValidationError
for attempt in range(3):
try:
response = client.beta.chat.completions.parse(
model="gpt-4o-2024-08-06",
messages=messages,
response_format=ProductReview,
)
review = response.choices[0].message.parsed
# Kjør ytterligere semantisk validering her
break
except ValidationError as e:
messages.append({"role": "assistant", "content": str(response)})
messages.append({"role": "user", "content": f"Validation error: {e}. Fix it."})Streaming av Strukturerte Utdata
For store strukturerte svar -- lange matriser, mange felt, komplekse nestede objekter -- lar streaming deg gjengi delvise resultater progressivt.
import instructor
client = instructor.from_openai(OpenAI())
# Stream delvise resultater etter hvert som felt fylles
review_stream = client.chat.completions.create_partial(
model="gpt-4o",
response_model=ProductReview,
messages=[{"role": "user", "content": review_text}],
)
for partial_review in review_stream:
# Felt fylles ett etter ett etter hvert som tokens streames
if partial_review.summary:
print(f"Summary so far: {partial_review.summary}")En fallgruve: Individuelle streaming-deler er ikke skjemagyltige alene. reasoning-feltet kan være fylt mens rating fortsatt er None. Planlegg grensesnittet ditt deretter -- vis en lastetilstand for ikke-fylte felt.
Konklusjon: Avvisningssjekker er ikke omsettelige. Nye forsøk med valideringsfeedback fanger semantiske feil. Streaming er verdt det for svar som tar mer enn et par sekunder.
Sammenligning av Biblioteker for Strukturerte Utdata
Du kan bruke strukturerte utdata via native API-er, men biblioteker legger til validering, nye forsøk, streaming og støtte for flere leverandører. Her er bildet.
Instructor er det mest populære alternativet med 11K+ GitHub-stjerner og 3M+ månedlige nedlastinger. Det omslutter OpenAI, Anthropic, Gemini, Cohere, Ollama og mer med et enhetlig Pydantic-basert grensesnitt. Nøkkelfunksjoner: automatiske nye forsøk med valideringsfeedback, streaming via create_partial() og enkel oppsett (instructor.from_openai(client)). Hvis du er et Python-team, begynn her.
BAML tar en annen tilnærming: skjema-først via et tilpasset DSL. Du definerer skjemaer i .baml-filer og auto-genererer klienter for Python, TypeScript, Ruby og mer. SAP-algoritmen (schema-aligned parsing) håndterer rotete modellutdata på en elegant måte. Best for tverrspråklige team eller når du vil ha kontrakter mellom LLM-laget og applikasjonslaget. Avveining: ekstra byggtrinn og ny syntaks å lære.
LangChain tilbyr .with_structured_output(schema) for leverandøruavhengige strukturerte utdata. Praktisk hvis du allerede er i LangChain-økosystemet. Avveining: det er en tung avhengighet, og abstraksjonen kan skjule leverandørspesifikke funksjoner du kanskje trenger.
Native API-er -- direkte anrop med response_format / output_config -- krever ingen avhengigheter utover leverandørens SDK. Du får full kontroll og full synlighet. Best for enkle brukstilfeller eller team som foretrekker minimal abstraksjon.
| Bibliotek | Språk | Leverandører | Auto-nye forsøk | Streaming | GitHub-stjerner | Læringskurve |
|---|---|---|---|---|---|---|
| Instructor | Python, TS | 15+ | Ja | Ja | 11K+ | Lav |
| BAML | Python, TS, Ruby, Go | Alle (DSL-agnostisk) | Ja | Ja | 7K+ | Middels |
| LangChain | Python, TS | 20+ | Delvis | Ja | 100K+ | Middels-Høy |
| Native API-er | Hvilket som helst | 1 per SDK | Nei | Ja | N/A | Lav |
Å velge riktig bibliotek for strukturerte utdata er en del av en bredere AI-stack-beslutning. Vi bryter ned hele stacken i vår guide for Beste AI-stack for SaaS.
Se våre Beste biblioteker for LLM Strukturerte Utdata [kommer snart] for en dybdesammenligning av Instructor, BAML, Mirascope og mer.
Konklusjon: Begynn med Instructor for Python, native API-er for TypeScript. Gå til BAML hvis du trenger tverrspråklige skjemakontrakter. Unngå LangChain bare for strukturerte utdata -- det er overdrevent.
Beste Praksis for Skjemadesign (og Vanlige Feil)
Skjemadesignet ditt påvirker direkte utdatakvaliteten. Her er mønstrene som teller og feilene som koster deg nøyaktighet.
Sett Resonnement Foran Svar
Vi dekket dette i Pydantic-seksjonen, men det fortjener å bli gjentatt fordi det er den mest innflytelsesrike designavgjørelsen:
# Før: modellen gjetter svaret, rasjonaliserer det deretter
class Bad(BaseModel):
answer: str
reasoning: str
# Etter: modellen tenker først, binder seg deretter
class Good(BaseModel):
reasoning: str = Field(description="Think step by step")
answer: strLLM-er genererer fra venstre til høyre. Feltrekkefølgen er promptrekkefølgen. Resonemang først betyr at modellen må jobbe gjennom problemet før den binder seg til et svar. Les mer om guide til LLM-evaluering.
Anti-mønster-tabellen
| Feil | Problem | Løsning |
|---|---|---|
| Resonemangsfelt etter svar | Modellen bestemmer seg før den tenker | Flytt resonemang foran svar |
| Dypt nestet (4+ nivåer) | Høyere feilrate, tregere kompilering | Flat ut til 2-3 nivåer |
| Ingen feltbeskrivelser | Modellen gjetter hva du vil | Legg til .describe() / Field(description=...) |
| Manglende null-håndtering | Modellen hallusinerer en verdi for å fylle feltet | Bruk Optional / .nullable() |
| For store skjemaer (50+ felt) | Kompileringstimeout, kvalitetsforringelse | Del opp i flere anrop |
| Vage enum-alternativer | Modellen velger feil kategori | Bruk spesifikke, ikke-overlappende alternativer |
Håndter Null-verdier Eksplisitt
Hvis et felt kanskje ikke har data i kildeteksten, gjør det valgfritt. Å tvinge et obligatorisk felt når data ikke eksisterer fører til hallusinasjon:
class PersonInfo(BaseModel):
name: str # Alltid til stede
email: Optional[str] = Field(None, description="Email if mentioned, null otherwise")
phone: Optional[str] = Field(None, description="Phone if mentioned, null otherwise")Hold Skjemaene Fokuserte
Ett skjema per oppgave. Ikke prøv å trekke ut alt i ett enkelt massivt skjema. Hvis du trenger 50+ felt, del opp i flere ekstraksjonsanrop. OpenAIs strenge modus har praktiske grenser for skjemakompleksitet, og selv når det fungerer, forringer veldig store skjemaer utdatakvaliteten.
Konklusjon: Resonemang-først, beskrivende felt, eksplisitte null-verdier og fokuserte skjemaer. Gjør disse fire riktig og nøyaktigheten din for strukturerte utdata øker målbart.
Strukturerte Utdata med Lokale LLM-er
Du trenger ikke en API-leverandør for strukturerte utdata. Lokale slutningsmotorer støtter det gjennom grammatikkbasert begrenset dekoding -- den samme grunnleggende mekanismen, kjørende på din egen maskinvare.
Ollama
Den enkleste veien for lokale strukturerte utdata. Ollama aksepterer et JSON-skjema via format-parameteren:
import ollama
from pydantic import BaseModel
class Country(BaseModel):
name: str
capital: str
languages: list[str]
response = ollama.chat(
model="llama3.2",
messages=[{"role": "user", "content": "Tell me about Japan."}],
format=Country.model_json_schema(),
)
import json
country = Country(**json.loads(response.message.content))Ollama bruker XGrammar internt for begrenset dekoding. Samme garanti som API-leverandørene: 100% samsvar med skjema.
vLLM og SGLang
For produksjonskvalitets lokal slutning støtter vLLM og SGLang begge strukturerte utdata gjennom guided_json- og guided_regex-parametere. XGrammar er standardbackenden og leverer nesten null overhead på JSON-generering -- opptil 3,5x raskere enn alternative grammatikkmotorer.
Outlines
Outlines er Python-biblioteket med åpen kildekode som var pioner for grammatikkbasert begrenset generering. Det fungerer med enhver Hugging Face-modell og støtter JSON-skjema-, regex- og fullstendige kontekstfrie grammatikk (CFG/EBNF)-begrensninger. Det er også integrert i vLLM og SGLang som et grammatikkbackend-alternativ.
Nøkkelforskjellen fra API-leverandører: lokal strukturert utdata har ingen begrensninger for skjema-delsett. Du kontrollerer grammatikken fullstendig. Men modellkvaliteten varierer mer -- en lokal 7B-parametermodell matcher ikke GPT-4o eller Claude på komplekse ekstraksjonspppgaver. Skjemaet vil alltid være gyldig; innholdskvaliteten avhenger av modellen.
Konklusjon: Ollama for utvikling, vLLM/SGLang med XGrammar for produksjon. Lokal strukturert utdata er moden nok for de fleste brukstilfeller, med forbehold om at mindre modeller produserer innhold av lavere kvalitet innenfor skjemaet.
Vanlige Spørsmål
Hva er strukturerte utdata i LLM-er?
Strukturerte utdata er en mekanisme som garanterer at et LLMs svar samsvarer med et forhåndsdefinert JSON-skjema. I motsetning til ren tekst eller til og med JSON-modus bruker strukturerte utdata begrenset dekoding for å sikre at hvert felt, type og begrensning i skjemaet ditt oppfylles -- 100% av gangene, ikke "vanligvis".
Hva er forskjellen mellom JSON-modus og Strukturerte Utdata?
JSON-modus garanterer syntaktisk gyldig JSON men håndhever ikke skjemaet ditt -- du kan få hvilket som helst gyldig JSON-objekt. Strukturerte Utdata (Streng modus) garanterer full samsvar med skjema via begrenset dekoding. Bruk Streng modus for produksjon; JSON-modus er bare relevant når du ikke har et skjema på forhånd.
Hvilke LLM-leverandører støtter strukturerte utdata nativt?
OpenAI (siden august 2024), Google Gemini (2024, utvidet 2026), Anthropic (beta november 2025, GA tidlig 2026), Cohere og xAI (Grok) støtter alle native strukturerte utdata. På den lokale siden støtter Ollama, vLLM og SGLang det via grammatikkbasert begrenset dekoding.
Hvordan garanterer begrenset dekoding samsvar med skjema?
JSON-skjemaet kompileres til en endelig tilstandsmaskin (FSM). Ved hvert tokengenerasjonssteg er bare tokens tillatt som holder utdataene på en gyldig vei gjennom FSM -- ugyldige tokens får sine logits satt til negativt uendelig. Det betyr at ugyldige tokens har nullsannsynlighet for å bli generert, noe som gir deg en matematisk garanti, ikke en statistisk.
Bør jeg bruke strukturerte utdata eller funksjonskalling?
Bruk strukturerte utdata for ekstraksjon og klassifisering -- når du vil ha data i en bestemt form. Bruk funksjonskalling for agentarbeidsflyter -- når modellen trenger å bestemme hvilken handling som skal tas. Mange produksjonsapplikasjoner bruker begge: strukturerte utdata for dataekstraksjon og funksjonskalling for orkestrering.
Kan jeg strømme strukturerte utdata?
Ja. OpenAI støtter streaming med parse()-metoden, og Instructor gir create_partial() for streaming av Pydantic-modeller som fylles felt for felt. Husk at individuelle streaming-biter ikke er individuelt skjemagyltige -- felt fylles inkrementelt.
Hva er Instructor-biblioteket?
Instructor er det mest populære biblioteket for strukturerte utdata (11K+ GitHub-stjerner, 3M+ månedlige nedlastinger). Det omslutter leverandørs-SDK-er med Pydantic-basert validering, automatiske nye forsøk med valideringsfeedback og streamingstøtte. Det fungerer med OpenAI, Anthropic, Gemini, Cohere, Ollama og 10+ andre leverandører.
Fungerer strukturerte utdata med lokale LLM-er?
Ja. Ollama støtter strukturerte utdata via format-parameteren med JSON-skjema. vLLM og SGLang støtter det via guided_json-parametere. Alle tre bruker XGrammar eller Outlines for begrenset dekoding. Garantien for samsvar med skjema er den samme som API-leverandørene; innholdskvaliteten avhenger av modellen.
Hva er vanlige feil i skjemadesign?
De viktigste feilene: å legge resoneringsfeltet etter svarfeltet (modellen bestemmer seg før den tenker), dypt nestede skjemaer (4+ nivåer øker feil), manglende feltbeskrivelser (modellen gjetter intensjonen), ingen null-håndtering for valgfrie data (tvinger hallusinasjon) og for store skjemaer (50+ felt forringer kvaliteten).
Legger strukturerte utdata til latens?
Det er en skjemakompileringsoverhead ved den første forespørselen -- typisk 50-200ms mens FSM bygges. Etterfølgende forespørsler med samme skjema bruker en bufret FSM og legger til nesten null latens. For de fleste applikasjoner er dette ubetydelig sammenlignet med den totale modellslutningstiden.
Kan jeg bruke strukturerte utdata med bilder eller multimodale inndata?
Ja. Strukturerte utdata gjelder svars-formatet, ikke inndataene. Du kan sende et bilde til GPT-4o eller Gemini med et strukturert utdatasskjema og få tilbake en skjemasamsvarende analyse av bildet. Dette er kraftig for visuelle ekstraksjonarbeidsflyter -- ekstraksjon av strukturerte data fra kvitteringer, skjemaer eller produktbilder.