Att begränsla LLM:er med strukturerad output: Ollama, Qwen3 och Python eller Go
Några sätt att få strukturerad output från Ollama
Storspråkmodeller (LLM) är kraftfulla, men i produktionsmiljöer vill vi sällan ha fritt formulerade stycken. Istället vill vi ha förutsägbar data: attribut, fakta eller strukturerade objekt som kan matas in i en applikation. Det är vad Strukturerad utdata från LLM handlar om.
Schema-tvingande minskar hur ofta felaktiga logits leder till ogiltig JSON, men temperatur och straff är fortfarande viktiga för att undvika återförsökstormar; se parametrar för agentbaserad inferens för Qwen och Gemma när du kombinerar format-begränsningar med agenter.
För ett tag sedan införde Ollama stöd för strukturerad utdata (meddelande), vilket gör det möjligt att begränsa en modells svar så att de följer ett JSON-schema. Detta möjliggör konsekventa pipelines för dataextraktion för uppgifter som att katalogisera LLM-funktioner, benchmarka modeller eller automatisera systemintegration.

I detta inlägg ska vi täcka:
- Vad strukturerad utdata är och varför det är viktigt
- Ett enkelt sätt att få strukturerad utdata från LLM
- Hur Ollamas nya funktion fungerar
- Exempel på extrahering av LLM-funktioner:
Vad är strukturerad utdata?
Normalt genererar LLM fritt text:
“Modell X stöder resonemang med chain-of-thought, har ett kontextfönster på 200K och talar engelska, kinesiska och spanska.”
Det är läsbar, men svår att parsas.
Istället, med strukturerad utdata, ber vi om ett strikt schema:
{
"name": "Model X",
"supports_thinking": true,
"max_context_tokens": 200000,
"languages": ["English", "Chinese", "Spanish"]
}
Denna JSON är enkel att validera, lagra i en databas eller mata till ett användargränssnitt.
Enkel metod för att få strukturerad utdata från LLM
LLM förstår ibland vad schemat är och vi kan be LLM att returnera utdata i JSON med ett visst schema. Modellen Qwen3 från Alibaba är optimerad för resonemang och strukturerade svar. Du kan explicit instruera den att svara i JSON.
Exempel 1: Använda Qwen3 med ollama i Python, begäran om JSON med schema
import json
import ollama
prompt = """
Du är en strukturerad dataextraktor.
Returnera endast JSON.
Text: "Elon Musk är 53 år och bor i Austin."
Schema: { "name": string, "age": int, "city": string }
"""
response = ollama.chat(model="qwen3", messages=[{"role": "user", "content": prompt}])
output = response['message']['content']
# Parsa JSON
try:
data = json.loads(output)
print(data)
except Exception as e:
print("Fel vid parsning av JSON:", e)
Utdata:
{"name": "Elon Musk", "age": 53, "city": "Austin"}
Tvinga schemavalidering med Pydantic
För att undvika felaktiga utdata kan du validera mot ett Pydantic-schema i Python.
from pydantic import BaseModel
class Person(BaseModel):
name: str
age: int
city: str
# Antag att 'output' är JSON-strängen från Qwen3
data = Person.model_validate_json(output)
print(data.name, data.age, data.city)
Detta säkerställer att utdatan följ den förväntade strukturen.
Ollamas strukturerade utdata
Ollama låter dig nu skicka ett schema i format-parametern. Modellen begränsas då att svara endast i JSON som följer schemat (dokumentation).
I Python definierar du vanligtvis ditt schema med Pydantic och låter Ollama använda det som ett JSON-schema.
Exempel 2: Extrahera metadata om LLM-funktioner
Antag att du har en textbit som beskriver en LLMs förmågor:
“Qwen3 har starkt flerspråkligt stöd (engelska, kinesiska, franska, spanska, arabiska). Den tillåter resonemangsteg (chain-of-thought). Kontextfönstret är 128K tokens.”
Du vill ha strukturerad data:
from pydantic import BaseModel
from typing import List
from ollama import chat
class LLMFeatures(BaseModel):
name: str
supports_thinking: bool
max_context_tokens: int
languages: List[str]
prompt = """
Analysera följande beskrivning och returnera modellens funktioner endast i JSON.
Modellbeskrivning:
'Qwen3 har starkt flerspråkligt stöd (engelska, kinesiska, franska, spanska, arabiska).
Den tillåter resonemangsteg (chain-of-thought).
Kontextfönstret är 128K tokens.'
"""
resp = chat(
model="qwen3",
messages=[{"role": "user", "content": prompt}],
format=LLMFeatures.model_json_schema(),
options={"temperature": 0},
)
print(resp.message.content)
Möjlig utdata:
{
"name": "Qwen3",
"supports_thinking": true,
"max_context_tokens": 128000,
"languages": ["English", "Chinese", "French", "Spanish", "Arabic"]
}
Exempel 3: Jämför flera modeller
Skicka in beskrivningar av flera modeller och extrahera dem i strukturerad form:
from typing import List
class ModelComparison(BaseModel):
models: List[LLMFeatures]
prompt = """
Extrahera funktioner för varje modell till JSON.
1. Llama 3.1 stöder resonemang. Kontextfönstret är 128K. Språk: endast engelska.
2. GPT-4 Turbo stöder resonemang. Kontextfönstret är 128K. Språk: engelska, japanska.
3. Qwen3 stöder resonemang. Kontextfönstret är 128K. Språk: engelska, kinesiska, franska, spanska, arabiska.
"""
resp = chat(
model="qwen3",
messages=[{"role": "user", "content": prompt}],
format=ModelComparison.model_json_schema(),
options={"temperature": 0},
)
print(resp.message.content)
Utdata:
{
"models": [
{
"name": "Llama 3.1",
"supports_thinking": true,
"max_context_tokens": 128000,
"languages": ["English"]
},
{
"name": "GPT-4 Turbo",
"supports_thinking": true,
"max_context_tokens": 128000,
"languages": ["English", "Japanese"]
},
{
"name": "Qwen3",
"supports_thinking": true,
"max_context_tokens": 128000,
"languages": ["English", "Chinese", "French", "Spanish", "Arabic"]
}
]
}
Detta gör det trivialt att benchmarka, visualisera eller filtrera modeller baserat på deras funktioner.
Exempel 4: Detektera luckor automatiskt
Du kan till och med tillåta null-värden när ett fält saknas:
from typing import Optional
class FlexibleLLMFeatures(BaseModel):
name: str
supports_thinking: Optional[bool]
max_context_tokens: Optional[int]
languages: Optional[List[str]]
Detta säkerställer att ditt schema förblir giltigt även om viss information är okänd.
Fördelar, fallgropar och bästa praxis
Att använda strukturerad utdata genom Ollama (eller något system som stöder det) erbjuder många fördelar – men har också vissa fallgropar.
Fördelar
- Starkare garantier: Modellen be om att följa ett JSON-schema snarare än fritt formulerad text.
- Enklare parsning: Du kan direkt använda
json.loadseller validera med Pydantic / Zod, istället för regex eller heuristik. - Schema-baserad evolution: Du kan versionera ditt schema, lägga till fält (med standardvärden) och bibehålla bakåtkompatibilitet.
- Interoperabilitet: Nedströmsystem förväntar sig strukturerad data.
- Determinism (bättre med låg temperatur): När temperaturen är låg (t.ex. 0) är modellen mer benägen att strikt hålla sig till schemat. Ollamas dokumentation rekommenderar detta.
Fallgropar och risker
- Schema-missmatch: Modellen kan ändå avvika – t.ex. missa en obligatorisk egenskap, reordna nycklar eller inkludera extra fält. Du behöver validering.
- Komplexa scheman: Mycket djupa eller rekursiva JSON-scheman kan förvirra modellen eller leda till fel.
- Tvetydighet i prompt: Om din prompt är vag kan modellen gissa fält eller enheter felaktigt.
- Inkonsekvens mellan modeller: Vissa modeller kan vara bättre eller sämre på att respektera strukturerade begränsningar.
- Token-gränser: Själva schemat lägger till tokenkostnad till prompten eller API-anropet.
Bästa praxis och tips (från Ollamas blogg + erfarenhet)
- Använd Pydantic (Python) eller Zod (JavaScript) för att definiera dina scheman och auto-generera JSON-scheman. Detta undviker manuella fel.
- Inkludera alltid instruktioner som “svara endast i JSON” eller “inkludera inte kommentarer eller extra text” i din prompt.
- Använd temperature = 0 (eller mycket lågt) för att minimera slumpmässighet och maximera schemalydnad. Ollama rekommenderar determinism.
- Validera och potentiellt fallback (t.ex. försök igen eller rensa upp) när JSON-parsning eller schemavalidering misslyckas.
- Börja med ett enklare schema och utvidga sedan gradvis. Komplikera inte allt från början.
- Inkludera hjälpsamma men begränsade felinstruktioner: t.ex. om modellen inte kan fylla ett obligatoriskt fält, svara med
nullistället för att utelämna det (om ditt schema tillåter det).
Go-exempel 1: Extrahera LLM-funktioner
Här är ett enkelt Go-program som frågar Qwen3 om strukturerad utdata om en LLMs funktioner.
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"github.com/ollama/ollama/api"
)
type LLMFeatures struct {
Name string `json:"name"`
SupportsThinking bool `json:"supports_thinking"`
MaxContextTokens int `json:"max_context_tokens"`
Languages []string `json:"languages"`
}
func main() {
client, err := api.ClientFromEnvironment()
if err != nil {
log.Fatal(err)
}
prompt := `
Analysera följande beskrivning och returnera modellens funktioner endast i JSON.
Beskrivning:
"Qwen3 har starkt flerspråkligt stöd (engelska, kinesiska, franska, spanska, arabiska).
Den tillåter resonemangsteg (chain-of-thought).
Kontextfönstret är 128K tokens."
`
// Definiera JSON-schemat för strukturerad utdata
formatSchema := map[string]any{
"type": "object",
"properties": map[string]any{
"name": map[string]string{
"type": "string",
},
"supports_thinking": map[string]string{
"type": "boolean",
},
"max_context_tokens": map[string]string{
"type": "integer",
},
"languages": map[string]any{
"type": "array",
"items": map[string]string{
"type": "string",
},
},
},
"required": []string{"name", "supports_thinking", "max_context_tokens", "languages"},
}
// Konvertera schema till JSON
formatJSON, err := json.Marshal(formatSchema)
if err != nil {
log.Fatal("Misslyckades med att marshalera formatschemat:", err)
}
req := &api.GenerateRequest{
Model: "qwen3:8b",
Prompt: prompt,
Format: formatJSON,
Options: map[string]any{"temperature": 0},
}
var features LLMFeatures
var rawResponse string
err = client.Generate(context.Background(), req, func(response api.GenerateResponse) error {
// Ackumulera innehåll när det strömmas
rawResponse += response.Response
// Parsa endast när svaret är komplett
if response.Done {
if err := json.Unmarshal([]byte(rawResponse), &features); err != nil {
return fmt.Errorf("JSON-parsfel: %v", err)
}
}
return nil
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Parsad struct: %+v\n", features)
}
För att kompilera och köra detta exempel Go-program – låt oss anta att vi har denna main.go-fil i en mapp ollama-struct,
Vi behöver exekvera inuti denna mapp:
# initiera modul
go mod init ollama-struct
# hämta alla beroenden
go mod tidy
# bygga & exekvera
go build -o ollama-struct main.go
./ollama-struct
Exempel på utdata
Parsed struct: {Name:Qwen3 SupportsThinking:true MaxContextTokens:128000 Languages:[English Chinese French Spanish Arabic]}
Go-exempel 2: Jämför flera modeller
Du kan utöka detta för att extrahera en lista av modeller för jämförelse.
type ModelComparison struct {
Models []LLMFeatures `json:"models"`
}
prompt = `
Extrahera funktioner från följande modellbeskrivningar och returnera som JSON:
1. PaLM 2: Denna modell har begränsade resonemangsförmågor och fokuserar på grundläggande språkförståelse. Den stöder ett kontextfönster på 8 000 tokens. Den stöder främst endast engelska.
2. LLaMA 2: Denna modell har måttliga resonemangsförmågor och kan hantera vissa logiska uppgifter. Den kan processa upp till 4 000 tokens i sitt kontext. Den stöder engelska, spanska och italienska.
3. Codex: Denna modell har starka resonemangsförmågor specifikt för programmering och kodanalys. Den har ett kontextfönster på 16 000 tokens. Den stöder engelska, Python, JavaScript och Java.
Returnera ett JSON-objekt med en "models"-array som innehåller alla modeller.
`
// Definiera JSON-schemat för modelljämförelse
comparisonSchema := map[string]any{
"type": "object",
"properties": map[string]any{
"models": map[string]any{
"type": "array",
"items": map[string]any{
"type": "object",
"properties": map[string]any{
"name": map[string]string{
"type": "string",
},
"supports_thinking": map[string]string{
"type": "boolean",
},
"max_context_tokens": map[string]string{
"type": "integer",
},
"languages": map[string]any{
"type": "array",
"items": map[string]string{
"type": "string",
},
},
},
"required": []string{"name", "supports_thinking", "max_context_tokens", "languages"},
},
},
},
"required": []string{"models"},
}
// Konvertera schema till JSON
comparisonFormatJSON, err := json.Marshal(comparisonSchema)
if err != nil {
log.Fatal("Misslyckades med att marshalera jämförelseschemat:", err)
}
req = &api.GenerateRequest{
Model: "qwen3:8b",
Prompt: prompt,
Format: comparisonFormatJSON,
Options: map[string]any{"temperature": 0},
}
var comp ModelComparison
var comparisonResponse string
err = client.Generate(context.Background(), req, func(response api.GenerateResponse) error {
// Ackumulera innehåll när det strömmas
comparisonResponse += response.Response
// Parsa endast när svaret är komplett
if response.Done {
if err := json.Unmarshal([]byte(comparisonResponse), &comp); err != nil {
return fmt.Errorf("JSON-parsfel: %v", err)
}
}
return nil
})
if err != nil {
log.Fatal(err)
}
for _, m := range comp.Models {
fmt.Printf("%s: Context=%d, Languages=%v\n", m.Name, m.MaxContextTokens, m.Languages)
}
Exempel på utdata
PaLM 2: Context=8000, Languages=[English]
LLaMA 2: Context=4000, Languages=[English Spanish Italian]
Codex: Context=16000, Languages=[English Python JavaScript Java]
Förresten, qwen3:4b fungerar bra på dessa exempel, precis som qwen3:8b.
Bästa praxis för Go-utvecklare
- Sätt temperaturen till 0 för maximal schemalydnad.
- Validera med
json.Unmarshaloch fallback om parsning misslyckas. - Håll scheman enkla – djupt nästlade eller rekursiva JSON-strukturer kan orsaka problem.
- Tillåt valfria fält (använd
omitemptyi Go-struct-taggar) om du förväntar dig saknad data. - Lägg till försök om modellen ibland genererar ogiltig JSON.
Fullständigt exempel – Rita en diagram med LLM-specifikationer (Steg-för-steg: från strukturerad JSON till jämförelsetabeller)

- Definiera ett schema för den data du vill ha
Använd Pydantic så att du både (a) kan generera ett JSON-schema för Ollama och (b) validera modellens svar.
from pydantic import BaseModel
from typing import List, Optional
class LLMFeatures(BaseModel):
name: str
supports_thinking: bool
max_context_tokens: int
languages: List[str]
- Be Ollama att returnera endast JSON i den formen
Skicka schemat i format= och sänk temperaturen för determinism.
from ollama import chat
prompt = """
Extrahera funktioner för varje modell. Returnera endast JSON som matchar schemat.
1) Qwen3 stöder chain-of-thought; 128K kontext; engelska, kinesiska, franska, spanska, arabiska.
2) Llama 3.1 stöder chain-of-thought; 128K kontext; engelska.
3) GPT-4 Turbo stöder chain-of-thought; 128K kontext; engelska, japanska.
"""
resp = chat(
model="qwen3",
messages=[{"role": "user", "content": prompt}],
format={"type": "array", "items": LLMFeatures.model_json_schema()},
options={"temperature": 0}
)
raw_json = resp.message.content # JSON-lista av LLMFeatures
- Validera & normalisera
Validera alltid innan användning i produktion.
from pydantic import TypeAdapter
adapter = TypeAdapter(list[LLMFeatures])
models = adapter.validate_json(raw_json) # -> list[LLMFeatures]
- Bygg en jämförelsetabell (pandas)
Omändla dina validerade objekt till en DataFrame som du kan sortera/filtera och exportera.
import pandas as pd
df = pd.DataFrame([m.model_dump() for m in models])
df["languages_count"] = df["languages"].apply(len)
df["languages"] = df["languages"].apply(lambda xs: ", ".join(xs))
# Reordna kolumner för läsbarhet
df = df[["name", "supports_thinking", "max_context_tokens", "languages_count", "languages"]]
# Spara som CSV för vidare användning
df.to_csv("llm_feature_comparison.csv", index=False)
- (Valfritt) Snabba visualiseringar
Enkla diagram hjälper dig att snabbt se skillnader mellan modeller.
import matplotlib.pyplot as plt
plt.figure()
plt.bar(df["name"], df["max_context_tokens"])
plt.title("Max Kontextfönster per Modell (tokens)")
plt.xlabel("Modell")
plt.ylabel("Max Kontext Tokens")
plt.xticks(rotation=20, ha="right")
plt.tight_layout()
plt.savefig("max_context_window.png")
TL;DR
Med Ollamas nya stöd för strukturerad utdata kan du behandla LLM inte bara som chattbotar utan som dataextraktionsmotorer.
Exemplen ovan visade hur man automatiskt kan extrahera strukturerad metadata om LLM-funktioner som stöd för tänkande, storlek på kontextfönster och språk – uppgifter som annars skulle kräva bräcklig parsning.
Oavsett om du bygger en LLM-modellkatalog, ett utvärderingsdashboard eller en AI-driven forskningsassistent, gör strukturerade utdata integrationen smidig, pålitlig och produktionsklar.
Användbara länkar
- https://ollama.com/blog/structured-outputs
- Ollama-fuskblad
- Python-fuskblad
- AWS SAM + AWS SQS + Python PowerTools
- Golang-fuskblad
- Jämförelse av Go ORM för PostgreSQL: GORM vs Ent vs Bun vs sqlc
- Omrankning av textdokument med Ollama och Qwen3 Embedding-modell - i Go
- AWS Lambda-prestanda: JavaScript vs Python vs Golang