Begränsa LLMs med strukturerad utdata: Ollama, Qwen3 & Python eller Go

Några sätt att få strukturerad utdata från Ollama

Sidinnehåll

Stora språkmodeller (LLMs) är kraftfulla, men i produktion vill vi sällan ha fritt formulerade stycken. Istället vill vi ha förutsägbart data: attribut, fakta eller strukturerade objekt som du kan mata in i en app. Det är LLM Strukturerad Utdata.

Nyligen introducerade Ollama stöd för strukturerad utdata (annons), vilket gör det möjligt att begränsa en modells svar till att matcha ett JSON-schema. Detta låser upp konsekventa datautdragningspipelines för uppgifter som att katalogisera LLM-funktioner, benchmarka modeller eller automatisera systemintegrering.

anordnade ankor

I det här inlägget kommer vi att täcka:

  • Vad strukturerad utdata är och varför det är viktigt
  • En enkel metod för att få strukturerad utdata från LLMs
  • Hur Ollamas nya funktion fungerar
  • Exempel på att extrahera LLM-funktioner:

Vad är Strukturerad Utdata?

Normalt genererar LLMs fri text:

“Modell X stöder resonemang med chain-of-thought, har ett kontextfönster på 200K tokens och talar engelska, kinesiska och spanska.”

Det är läsbart, men svårt att tolka.

Istället, med strukturerad utdata, frågar vi efter ett strikt schema:

{
  "name": "Modell X",
  "supports_thinking": true,
  "max_context_tokens": 200000,
  "languages": ["Engelska", "Kinesiska", "Spanska"]
}

Den här JSON-en är enkel att validera, lagra i en databas eller mata in i ett gränssnitt.


Enkel metod för att få Strukturerad Utdata från LLM

LLM:ar förstår ibland vad schemat är och vi kan be LLM att returnera utdata i JSON med ett visst schema. Qwen3-modellen 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ärande JSON med schema

import json
import ollama

prompt = """
Du är en extraherare av strukturerad data.
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']

# Tolka JSON
try:
    data = json.loads(output)
    print(data)
except Exception as e:
    print("Fel vid tolkningsförsök av JSON:", e)

Utdata:

{"name": "Elon Musk", "age": 53, "city": "Austin"}

Tvinga Schema-validering 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)

Det här säkerställer att utdata följer den förväntade strukturen.


Ollamas Strukturerade Utdata

Ollama låter nu dig skicka med ett schema i parametern format. Modellen begränsas då till 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 LLM-funktionsmetadata

Antag att du har en text som beskriver en LLM:s förmågor:

“Qwen3 har stark flerspråkig stöd (engelska, kinesiska, franska, spanska, arabiska). Den tillåter resonemangssteg (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 i JSON endast.
Modellbeskrivning:
'Qwen3 har stark flerspråkig stöd (engelska, kinesiska, franska, spanska, arabiska).
Den tillåter resonemangssteg (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": ["Engelska", "Kinesiska", "Franska", "Spanska", "Arabiska"]
}

Exempel 3: Jämföra flera modeller

Mata in beskrivningar av flera modeller och extrahera 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önster är 128K. Språk: endast engelska.
2. GPT-4 Turbo stöder resonemang. Kontextfönster är 128K. Språk: engelska, japanska.
3. Qwen3 stöder resonemang. Kontextfönster ä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": ["Engelska"]
    },
    {
      "name": "GPT-4 Turbo",
      "supports_thinking": true,
      "max_context_tokens": 128000,
      "languages": ["Engelska", "Japanska"]
    },
    {
      "name": "Qwen3",
      "supports_thinking": true,
      "max_context_tokens": 128000,
      "languages": ["Engelska", "Kinesiska", "Franska", "Spanska", "Arabiska"]
    }
  ]
}

Det här gör det trivialt att benchmarka, visualisera eller filtrera modeller efter deras funktioner.


Exempel 4: Detektera luckor automatiskt

Du kan 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]]

Det här säkerställer att ditt schema förblir giltigt även om viss information saknas.


Fördelar, begränsningar & bästa praxis

Att använda strukturerad utdata genom Ollama (eller något annat system som stöder det) erbjuder många fördelar - men har också några begränsningar.

Fördelar

  • Starkare garantier: Modellen begärs att följa ett JSON-schema istället för fri text.
  • Enklare tolkningsförsök: Du kan direkt json.loads eller validera med Pydantic/Zod, istället för regex eller heuristisk metod.
  • Schema-baserad utveckling: 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 följa schemat. Ollamas dokumentation rekommenderar detta.

Begränsningar & fallgropar

  • Schema-inkompatibilitet: Modellen kan fortfarande avvika - t.ex. missa ett obligatoriskt fält, byta ordning på 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 följa strukturerade begränsningar.
  • Tokenbegränsningar: Schemat i sig lägger till tokenkostnad till prompten eller API-anropet.

Bästa praxis & tips (hämtade från Ollamas blogg + erfarenhet)

  • Använd Pydantic (Python) eller Zod (JavaScript) för att definiera dina scheman och automatiskt 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åg) för att minimera slumpmässighet och maximera schemaöverensstämmelse. Ollama rekommenderar determinism.
  • Validera och eventuellt återförsök (t.ex. återförsök eller rensa upp) när JSON-tolkningsförsök misslyckas eller schemavalidering misslyckas.
  • Börja med ett enklare schema, sedan utöka gradvis. Överkomplicera inte från början.
  • Inkludera hjälpsamma men begränsade felinstruktioner: t.ex. om modellen inte kan fylla ett obligatoriskt fält, svara med null istä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 LLM:s 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 i JSON endast.
  Beskrivning:
  "Qwen3 har stark flerspråkig stöd (engelska, kinesiska, franska, spanska, arabiska).
  Den tillåter resonemangssteg (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 marshala format-schema:", 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 medan det strömmar
		rawResponse += response.Response

		// Tolka endast när svaret är komplett
		if response.Done {
			if err := json.Unmarshal([]byte(rawResponse), &features); err != nil {
				return fmt.Errorf("JSON-tolkningsfel: %v", err)
			}
		}
		return nil
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Tolkat struct: %+v\n", features)
}

För att kompilera och köra det här Go-programmet - antag att vi har den här main.go-filen i en mapp ollama-struct, Vi måste köra följande kommandon i den här mappen:

# initialisera modul
go mod init ollama-struct
# hämta alla beroenden
go mod tidy
# bygg & kör
go build -o ollama-struct main.go
./ollama-struct

Exempel på utdata

Tolkat struct: {Name:Qwen3 SupportsThinking:true MaxContextTokens:128000 Languages:[Engelska Kinesiska Franska Spanska Arabiska]}

Go Exempel 2: Jämföra Flera Modeller

Du kan utöka detta för att extrahera en lista med 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 primärt endast engelska.
	2. LLaMA 2: Denna modell har måttliga resonemangsförmågor och kan hantera vissa logiska uppgifter. Den kan bearbeta upp till 4,000 tokens i sitt kontextfönster. 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 marshala jämförelseschema:", 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 medan det strömmar
		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-parsningsfel: %v", err)
			}
		}
		return nil
	})
	if err != nil {
		log.Fatal(err)
	}

	for _, m := range comp.Models {
		fmt.Printf("%s: Kontext=%d, Språk=%v\n", m.Name, m.MaxContextTokens, m.Languages)
	}

Exempel på utdata

PaLM 2: Kontext=8000, Språk=[English]
LLaMA 2: Kontext=4000, Språk=[English Spanish Italian]
Codex: Kontext=16000, Språk=[English Python JavaScript Java]

Av någon anledning fungerar qwen3:4b bra på dessa exempel, precis som qwen3:8b.

Bästa Praktiker för Go-Utvecklare

  • Använd temperatur 0 för maximal schemakompatibilitet.
  • Validera med json.Unmarshal och fallback om parsning misslyckas.
  • Håll scheman enkla - djupnästade eller rekursiva JSON-strukturer kan orsaka problem.
  • Tillåt valfria fält (använd omitempty i Go-struktureringsmärken) om du förväntar dig saknade data.
  • Lägg till återförsök om modellen ibland genererar ogiltig JSON.

Fullständigt exempel - Rita en Graf med LLM-Specifikationer (Steg-för-steg: från strukturerad JSON till jämförelsetabeller)

llm-chart

  1. Definiera ett schema för de 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]
  1. 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 med LLMFeatures
  1. Validera & normalisera

Alltid validera innan du använder i produktion.

from pydantic import TypeAdapter

adapter = TypeAdapter(list[LLMFeatures])
models = adapter.validate_json(raw_json)  # -> list[LLMFeatures]
  1. Bygg en jämförelsetabell (pandas)

Omvandla 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))

# Ordna om 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)
  1. (Valfritt) Snabba visuella representationer

Enkla grafer hjälper dig att snabbt öga 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 strukturerade utdata-stöd kan du behandla LLMs inte bara som chattbotar utan som dataextraktionsmotorer.

Exemplen ovan visade hur man automatiskt extraherar strukturerad metadata om LLM-funktioner som resonemangsförmåga, kontextfönsterstorlek och stödda språk - uppgifter som annars skulle kräva bräckliga parsningsmetoder.

Oavsett om du bygger en LLM-modellkatalog, en utvärderingsdashboard eller en AI-drivet forskningsassistent, gör strukturerade utdata integrationen smidig, tillförlitlig och produktionsklar.

Användbara länkar