Strukturierte Ausgabe zur Einschränkung von LLMs: Ollama, Qwen3 und Python oder Go
Einige Möglichkeiten, strukturierte Ausgaben von Ollama zu erhalten
Large Language Models (LLMs) sind leistungsstark, aber im Produktivbetrieb benötigen wir selten freie Textabsätze. Stattdessen wünschen wir uns vorhersagbare Daten: Attribute, Fakten oder strukturierte Objekte, die in eine Anwendung eingespeist werden können. Das ist Strukturierte Ausgabe von LLMs.
Die Durchsetzung von Schemata reduziert die Häufigkeit, mit der schlechte Logits in ungültiges JSON umgewandelt werden, aber Temperatur und Strafen sind dennoch relevant für Wiederholungsstürme; siehe Parameter für agentisches Inferenz bei Qwen und Gemma, wenn Sie format-Einschränkungen mit Agents kombinieren.
Vor einiger Zeit stellte Ollama die Unterstützung für strukturierte Ausgaben vor (Ankündigung), wodurch es möglich wurde, die Antworten eines Modells so einzuschränken, dass sie einem JSON-Schema entsprechen. Dies ermöglicht konsistente Datenextraktionspipelines für Aufgaben wie die Katalogisierung von LLM-Funktionen, das Benchmarking von Modellen oder die Automatisierung der Systemintegration.

In diesem Beitrag behandeln wir:
- Was strukturierte Ausgabe ist und warum sie wichtig ist
- Einfache Möglichkeit, strukturierte Ausgabe von LLMs zu erhalten
- Wie die neue Funktion von Ollama funktioniert
- Beispiele zur Extraktion von LLM-Fähigkeiten:
Was ist strukturierte Ausgabe?
Normalerweise generieren LLMs freien Text:
„Modell X unterstützt Reasoning mit Chain-of-Thought, hat ein Kontextfenster von 200K Token und spricht Englisch, Chinesisch und Spanisch.“
Das ist lesbar, aber schwer zu parsen.
Stattdessen fordern wir mit strukturierter Ausgabe ein striktes Schema an:
{
"name": "Modell X",
"supports_thinking": true,
"max_context_tokens": 200000,
"languages": ["Englisch", "Chinesisch", "Spanisch"]
}
Dieses JSON ist einfach zu validieren, in einer Datenbank zu speichern oder an eine UI weiterzuleiten.
Einfache Methode, um strukturierte Ausgabe von LLMs zu erhalten
LLMs verstehen manchmal, was das Schema ist, und wir können das LLM bitten, die Ausgabe in JSON gemäß einem bestimmten Schema zurückzugeben. Das Qwen3-Modell von Alibaba ist auf Reasoning und strukturierte Antworten optimiert. Sie können es explizit anweisen, in JSON zu antworten.
Beispiel 1: Verwendung von Qwen3 mit ollama in Python, Anforderung von JSON mit Schema
import json
import ollama
prompt = """
Sie sind ein strukturierter Datenextraktor.
Geben Sie nur JSON zurück.
Text: "Elon Musk ist 53 und lebt in Austin."
Schema: { "name": string, "age": int, "city": string }
"""
response = ollama.chat(model="qwen3", messages=[{"role": "user", "content": prompt}])
output = response['message']['content']
# JSON parsen
try:
data = json.loads(output)
print(data)
except Exception as e:
print("Fehler beim Parsen von JSON:", e)
Ausgabe:
{"name": "Elon Musk", "age": 53, "city": "Austin"}
Durchsetzung der Schemavalidierung mit Pydantic
Um fehlerhafte Ausgaben zu vermeiden, können Sie die Validierung gegen ein Pydantic-Schema in Python durchführen.
from pydantic import BaseModel
class Person(BaseModel):
name: str
age: int
city: str
# Nehmen wir an, 'output' ist der JSON-String von Qwen3
data = Person.model_validate_json(output)
print(data.name, data.age, data.city)
Dies stellt sicher, dass die Ausgabe der erwarteten Struktur entspricht.
Ollamas strukturierte Ausgabe
Ollama ermöglicht es Ihnen jetzt, ein Schema im format-Parameter zu übergeben. Das Modell ist dann gezwungen, nur in JSON zu antworten, das dem Schema entspricht (Dokumentation).
In Python definieren Sie Ihr Schema typischerweise mit Pydantic und lassen Ollama dieses als JSON-Schema verwenden.
Beispiel 2: Extraktion von LLM-Metadaten
Angenommen, Sie haben einen Textausschnitt, der die Fähigkeiten eines LLM beschreibt:
„Qwen3 bietet starke mehrsprachige Unterstützung (Englisch, Chinesisch, Französisch, Spanisch, Arabisch). Es ermöglicht Reasoning-Schritte (Chain-of-Thought). Das Kontextfenster beträgt 128K Token.“
Sie möchten strukturierte Daten:
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 = """
Analysieren Sie die folgende Beschreibung und geben Sie die Funktionen des Modells nur in JSON zurück.
Modellbeschreibung:
'Qwen3 bietet starke mehrsprachige Unterstützung (Englisch, Chinesisch, Französisch, Spanisch, Arabisch).
Es ermöglicht Reasoning-Schritte (Chain-of-Thought).
Das Kontextfenster beträgt 128K Token.'
"""
resp = chat(
model="qwen3",
messages=[{"role": "user", "content": prompt}],
format=LLMFeatures.model_json_schema(),
options={"temperature": 0},
)
print(resp.message.content)
Mögliche Ausgabe:
{
"name": "Qwen3",
"supports_thinking": true,
"max_context_tokens": 128000,
"languages": ["Englisch", "Chinesisch", "Französisch", "Spanisch", "Arabisch"]
}
Beispiel 3: Vergleich mehrerer Modelle
Speisen Sie Beschreibungen mehrerer Modelle ein und extrahieren Sie diese in strukturierte Form:
from typing import List
class ModelComparison(BaseModel):
models: List[LLMFeatures]
prompt = """
Extrahieren Sie die Funktionen jedes Modells in JSON.
1. Llama 3.1 unterstützt Reasoning. Kontextfenster ist 128K. Sprachen: Nur Englisch.
2. GPT-4 Turbo unterstützt Reasoning. Kontextfenster ist 128K. Sprachen: Englisch, Japanisch.
3. Qwen3 unterstützt Reasoning. Kontextfenster ist 128K. Sprachen: Englisch, Chinesisch, Französisch, Spanisch, Arabisch.
"""
resp = chat(
model="qwen3",
messages=[{"role": "user", "content": prompt}],
format=ModelComparison.model_json_schema(),
options={"temperature": 0},
)
print(resp.message.content)
Ausgabe:
{
"models": [
{
"name": "Llama 3.1",
"supports_thinking": true,
"max_context_tokens": 128000,
"languages": ["Englisch"]
},
{
"name": "GPT-4 Turbo",
"supports_thinking": true,
"max_context_tokens": 128000,
"languages": ["Englisch", "Japanisch"]
},
{
"name": "Qwen3",
"supports_thinking": true,
"max_context_tokens": 128000,
"languages": ["Englisch", "Chinesisch", "Französisch", "Spanisch", "Arabisch"]
}
]
}
Dies macht es trivial, Modelle nach ihren Funktionen zu benchmarken, zu visualisieren oder zu filtern.
Beispiel 4: Automatische Erkennung von Lücken
Sie können sogar null-Werte zulassen, wenn ein Feld fehlt:
from typing import Optional
class FlexibleLLMFeatures(BaseModel):
name: str
supports_thinking: Optional[bool]
max_context_tokens: Optional[int]
languages: Optional[List[str]]
Dies stellt sicher, dass Ihr Schema gültig bleibt, selbst wenn einige Informationen unbekannt sind.
Vorteile, Fallstricke & Best Practices
Die Verwendung strukturierter Ausgabe über Ollama (oder ein anderes System, das dies unterstützt) bietet viele Vorteile – hat aber auch einige Fallstricke.
Vorteile
- Stärkere Garantien: Das Modell wird aufgefordert, einem JSON-Schema zu folgen, anstatt freien Text zu generieren.
- Einfacheres Parsen: Sie können direkt
json.loadsverwenden oder mit Pydantic / Zod validieren, anstatt Regex oder Heuristiken zu verwenden. - Schema-basierte Evolution: Sie können Ihr Schema versionieren, Felder hinzufügen (mit Standardwerten) und Abwärtskompatibilität aufrechterhalten.
- Interoperabilität: Nachgelagerte Systeme erwarten strukturierte Daten.
- Determinismus (besser bei niedriger Temperatur): Wenn die Temperatur niedrig ist (z. B. 0), hält sich das Modell eher strikt an das Schema. Ollamas Dokumentation empfiehlt dies.
Fallstricke & Risiken
- Schema-Mismatch: Das Modell könnte dennoch abweichen – z. B. eine erforderliche Eigenschaft übersehen, Schlüssel neu anordnen oder zusätzliche Felder enthalten. Sie benötigen Validierung.
- Komplexe Schemata: Sehr tiefe oder rekursive JSON-Schemata könnten das Modell verwirren oder zu Fehlern führen.
- Ambiguität im Prompt: Wenn Ihr Prompt vage ist, könnte das Modell Felder oder Einheiten falsch erraten.
- Inkonsistenz zwischen Modellen: Einige Modelle könnten besser oder schlechter darin sein, strukturierte Einschränkungen einzuhalten.
- Token-Limits: Das Schema selbst fügt dem Prompt oder API-Aufruf Token-Kosten hinzu.
Best Practices & Tipps (basierend auf Ollamas Blog + Erfahrung)
- Verwenden Sie Pydantic (Python) oder Zod (JavaScript), um Ihre Schemata zu definieren und JSON-Schemata automatisch zu generieren. Dies vermeidet manuelle Fehler.
- Fügen Sie immer Anweisungen wie „nur in JSON antworten“ oder „keine Kommentare oder zusätzlichen Text einfügen“ in Ihren Prompt ein.
- Verwenden Sie Temperatur = 0 (oder sehr niedrig), um Zufälligkeit zu minimieren und die Schemakonformität zu maximieren. Ollama empfiehlt Determinismus.
- Validieren Sie und fallen Sie ggf. zurück (z. B. Wiederholung oder Bereinigung), wenn das Parsen von JSON fehlschlägt oder die Schemavalidierung fehlschlägt.
- Beginnen Sie mit einem einfacheren Schema und erweitern Sie es schrittweise. Überkomplizieren Sie es nicht von Anfang an.
- Fügen Sie hilfreiche, aber eingeschränkte Fehleranweisungen hinzu: z. B. wenn das Modell ein erforderliches Feld nicht ausfüllen kann, antworten Sie mit
null, anstatt es zu omitieren (wenn Ihr Schema dies erlaubt).
Go-Beispiel 1: Extraktion von LLM-Funktionen
Hier ist ein einfaches Go-Programm, das Qwen3 nach strukturierter Ausgabe über die Funktionen eines LLM fragt.
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 := `
Analysieren Sie die folgende Beschreibung und geben Sie die Funktionen des Modells nur in JSON zurück.
Beschreibung:
"Qwen3 bietet starke mehrsprachige Unterstützung (Englisch, Chinesisch, Französisch, Spanisch, Arabisch).
Es ermöglicht Reasoning-Schritte (Chain-of-Thought).
Das Kontextfenster beträgt 128K Token."
`
// Definieren Sie das JSON-Schema für strukturierte Ausgabe
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"},
}
// Konvertieren Sie das Schema in JSON
formatJSON, err := json.Marshal(formatSchema)
if err != nil {
log.Fatal("Fehler beim Marshalling des Format-Schemas:", 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 {
// Akkumulieren Sie den Inhalt während des Streamings
rawResponse += response.Response
// Nur parsen, wenn die Antwort vollständig ist
if response.Done {
if err := json.Unmarshal([]byte(rawResponse), &features); err != nil {
return fmt.Errorf("JSON-Parsefehler: %v", err)
}
}
return nil
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Geparter Struct: %+v\n", features)
}
Um dieses Beispiel-Go-Programm zu kompilieren und auszuführen – nehmen wir an, wir haben diese main.go-Datei in einem Ordner ollama-struct,
Müssen wir innerhalb dieses Ordners ausführen:
# Modul initialisieren
go mod init ollama-struct
# Alle Abhängigkeiten holen
go mod tidy
# Bauen & ausführen
go build -o ollama-struct main.go
./ollama-struct
Beispielausgabe
Geparter Struct: {Name:Qwen3 SupportsThinking:true MaxContextTokens:128000 Languages:[Englisch Chinesisch Französisch Spanisch Arabisch]}
Go-Beispiel 2: Vergleich mehrerer Modelle
Sie können dies erweitern, um eine Liste von Modellen zum Vergleich zu extrahieren.
type ModelComparison struct {
Models []LLMFeatures `json:"models"`
}
prompt = `
Extrahieren Sie Funktionen aus den folgenden Modellbeschreibungen und geben Sie sie als JSON zurück:
1. PaLM 2: Dieses Modell hat begrenzte Reasoning-Fähigkeiten und konzentriert sich auf grundlegendes Sprachverständnis. Es unterstützt ein Kontextfenster von 8.000 Token. Es unterstützt hauptsächlich nur die englische Sprache.
2. LLaMA 2: Dieses Modell hat moderate Reasoning-Fähigkeiten und kann einige logische Aufgaben bearbeiten. Es kann bis zu 4.000 Token in seinem Kontext verarbeiten. Es unterstützt Englisch, Spanisch und Italienisch.
3. Codex: Dieses Modell hat starke Reasoning-Fähigkeiten speziell für Programmierung und Codeanalyse. Es hat ein Kontextfenster von 16.000 Token. Es unterstützt Englisch, Python, JavaScript und Java.
Geben Sie ein JSON-Objekt mit einem "models"-Array zurück, das alle Modelle enthält.
`
// Definieren Sie das JSON-Schema für Modellvergleich
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"},
}
// Konvertieren Sie das Schema in JSON
comparisonFormatJSON, err := json.Marshal(comparisonSchema)
if err != nil {
log.Fatal("Fehler beim Marshalling des Vergleichsschemas:", 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 {
// Akkumulieren Sie den Inhalt während des Streamings
comparisonResponse += response.Response
// Nur parsen, wenn die Antwort vollständig ist
if response.Done {
if err := json.Unmarshal([]byte(comparisonResponse), &comp); err != nil {
return fmt.Errorf("JSON-Parsefehler: %v", err)
}
}
return nil
})
if err != nil {
log.Fatal(err)
}
for _, m := range comp.Models {
fmt.Printf("%s: Kontext=%d, Sprachen=%v\n", m.Name, m.MaxContextTokens, m.Languages)
}
Beispielausgabe
PaLM 2: Kontext=8000, Sprachen=[Englisch]
LLaMA 2: Kontext=4000, Sprachen=[Englisch Spanisch Italienisch]
Codex: Kontext=16000, Sprachen=[Englisch Python JavaScript Java]
Übrigens funktioniert qwen3:4b bei diesen Beispielen genauso gut wie qwen3:8b.
Best Practices für Go-Entwickler
- Setzen Sie die Temperatur auf 0, um maximale Schemakonformität zu gewährleisten.
- Validieren Sie mit
json.Unmarshalund fallen Sie zurück, falls das Parsen fehlschlägt. - Halten Sie Schemata einfach – tief verschachtelte oder rekursive JSON-Strukturen können Probleme verursachen.
- Erlauben Sie optionale Felder (verwenden Sie
omitemptyin Go-Struct-Tags), wenn Sie fehlende Daten erwarten. - Fügen Sie Wiederholungen hinzu, falls das Modell gelegentlich ungültiges JSON ausgibt.
Vollständiges Beispiel - Zeichnen eines Diagramms mit LLM-Spezifikationen (Schritt-für-Schritt: von strukturiertem JSON zu Vergleichstabellen)

- Definieren Sie ein Schema für die gewünschten Daten
Verwenden Sie Pydantic, damit Sie sowohl (a) ein JSON-Schema für Ollama generieren als auch (b) die Antwort des Modells validieren können.
from pydantic import BaseModel
from typing import List, Optional
class LLMFeatures(BaseModel):
name: str
supports_thinking: bool
max_context_tokens: int
languages: List[str]
- Bitten Sie Ollama, nur JSON in dieser Form zurückzugeben
Übergeben Sie das Schema in format= und senken Sie die Temperatur für Determinismus.
from ollama import chat
prompt = """
Extrahieren Sie Funktionen für jedes Modell. Geben Sie nur JSON zurück, das dem Schema entspricht.
1) Qwen3 unterstützt Chain-of-Thought; 128K Kontext; Englisch, Chinesisch, Französisch, Spanisch, Arabisch.
2) Llama 3.1 unterstützt Chain-of-Thought; 128K Kontext; Englisch.
3) GPT-4 Turbo unterstützt Chain-of-Thought; 128K Kontext; Englisch, Japanisch.
"""
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-Liste von LLMFeatures
- Validieren & normalisieren
Validieren Sie immer, bevor Sie in der Produktion verwenden.
from pydantic import TypeAdapter
adapter = TypeAdapter(list[LLMFeatures])
models = adapter.validate_json(raw_json) # -> list[LLMFeatures]
- Erstellen Sie eine Vergleichstabelle (pandas)
Wandeln Sie Ihre validierten Objekte in einen DataFrame um, den Sie sortieren/filtern und exportieren können.
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))
# Spalten für bessere Lesbarkeit neu anordnen
df = df[["name", "supports_thinking", "max_context_tokens", "languages_count", "languages"]]
# Als CSV speichern für weitere Verwendung
df.to_csv("llm_feature_comparison.csv", index=False)
- (Optional) Schnelle Visualisierungen
Einfache Diagramme helfen Ihnen, Unterschiede zwischen Modellen schnell zu erkennen.
import matplotlib.pyplot as plt
plt.figure()
plt.bar(df["name"], df["max_context_tokens"])
plt.title("Maximales Kontextfenster nach Modell (Token)")
plt.xlabel("Modell")
plt.ylabel("Maximale Kontext-Token")
plt.xticks(rotation=20, ha="right")
plt.tight_layout()
plt.savefig("max_context_window.png")
TL;DR
Mit der neuen Unterstützung für strukturierte Ausgabe von Ollama können Sie LLMs nicht nur als Chatbots, sondern als Datenextraktionsmotoren behandeln.
Die obigen Beispiele zeigten, wie man strukturierte Metadaten über LLM-Funktionen wie Reasoning-Unterstützung, Kontextfenstergröße und unterstützte Sprachen automatisch extrahiert – Aufgaben, die sonst zerbrechliches Parsing erfordern würden.
Ob Sie einen LLM-Modellkatalog, ein Evaluierungs-Dashboard oder einen KI-gestützten Forschungsassistenten aufbauen, strukturierte Ausgaben machen die Integration reibungslos, zuverlässig und produktionsreif.
Nützliche Links
- https://ollama.com/blog/structured-outputs
- Ollama Cheat Sheet
- Python Cheat Sheet
- AWS SAM + AWS SQS + Python PowerTools
- Golang Cheat Sheet
- Vergleich von Go ORMs für PostgreSQL: GORM vs Ent vs Bun vs sqlc
- Neuranking von Textdokumenten mit Ollama und Qwen3 Embedding-Modell - in Go
- AWS Lambda-Leistung: JavaScript vs Python vs Golang