Membatasi LLM dengan Output Terstruktur: Ollama, Qwen3 & Python atau Go
Beberapa cara untuk mendapatkan output terstruktur dari Ollama
Model Bahasa Besar (LLMs) sangat kuat, tetapi dalam produksi kita jarang ingin paragraf bebas. Sebaliknya, kita ingin data yang dapat diprediksi: atribut, fakta, atau objek terstruktur yang dapat Anda masukkan ke dalam aplikasi. Itu adalah Keluaran Terstruktur LLM.
Beberapa waktu yang lalu Ollama memperkenalkan dukungan keluaran terstruktur (pengumuman), membuat mungkin untuk membatasi respons model agar sesuai dengan skema JSON. Ini membuka jalur ekstraksi data yang konsisten untuk tugas seperti katalogisasi fitur LLM, benchmarking model, atau mengotomatisasi integrasi sistem.
Dalam posting ini, kita akan membahas:
- Apa itu keluaran terstruktur dan mengapa penting
- Cara sederhana untuk mendapatkan keluaran terstruktur dari LLM
- Bagaimana fitur baru Ollama bekerja
- Contoh ekstraksi kemampuan LLM:
Apa Itu Keluaran Terstruktur?
Biasanya, LLM menghasilkan teks bebas:
“Model X mendukung penalaran dengan chain-of-thought, memiliki jendela konteks 200K, dan berbicara dalam bahasa Inggris, Cina, dan Spanyol.”
Itu mudah dibaca, tetapi sulit untuk diparsir.
Sebaliknya, dengan keluaran terstruktur kita meminta skema yang ketat:
{
"name": "Model X",
"supports_thinking": true,
"max_context_tokens": 200000,
"languages": ["English", "Chinese", "Spanish"]
}
JSON ini mudah divalidasi, disimpan dalam database, atau diberikan ke UI.
Cara Sederhana Mendapatkan Keluaran Terstruktur dari LLM
LLMs terkadang memahami apa skema itu dan kita dapat meminta LLM untuk mengembalikan output dalam JSON menggunakan skema tertentu. Model Qwen3 dari Alibaba dioptimalkan untuk penalaran dan respons terstruktur. Anda dapat secara eksplisit memerintahkan untuk menjawab dalam JSON.
Contoh 1: Menggunakan Qwen3 dengan ollama
dalam Python, meminta JSON dengan skema
import json
import ollama
prompt = """
Anda adalah ekstraktor data terstruktur.
Hanya kembalikan JSON.
Teks: "Elon Musk berusia 53 dan tinggal di Austin."
Skema: { "name": string, "age": int, "city": string }
"""
response = ollama.chat(model="qwen3", messages=[{"role": "user", "content": prompt}])
output = response['message']['content']
# Parse JSON
try:
data = json.loads(output)
print(data)
except Exception as e:
print("Error parsing JSON:", e)
Keluaran:
{"name": "Elon Musk", "age": 53, "city": "Austin"}
Memaksa Validasi Skema dengan Pydantic
Untuk menghindari output yang tidak benar, Anda dapat memvalidasi terhadap skema Pydantic dalam Python.
from pydantic import BaseModel
class Person(BaseModel):
name: str
age: int
city: str
# Misalkan 'output' adalah string JSON dari Qwen3
data = Person.model_validate_json(output)
print(data.name, data.age, data.city)
Ini memastikan output sesuai dengan struktur yang diharapkan.
Keluaran Terstruktur Ollama
Ollama sekarang memungkinkan Anda untuk meneruskan skema dalam parameter format
. Model kemudian dibatasi untuk merespons hanya dalam JSON yang sesuai dengan skema (dokumentasi).
Dalam Python, Anda biasanya mendefinisikan skema Anda dengan Pydantic dan membiarkan Ollama menggunakan itu sebagai skema JSON.
Contoh 2: Ekstrak Metadata Fitur LLM
Misalkan Anda memiliki cuplikan teks yang menggambarkan kemampuan LLM:
“Qwen3 memiliki dukungan multibahasa yang kuat (Inggris, Cina, Prancis, Spanyol, Arab). Ia memungkinkan langkah-langkah penalaran (chain-of-thought). Jendela konteksnya adalah 128K token.”
Anda ingin data terstruktur:
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 = """
Analisis deskripsi berikut dan kembalikan fitur model dalam JSON hanya.
Deskripsi model:
'Qwen3 memiliki dukungan multibahasa yang kuat (Inggris, Cina, Prancis, Spanyol, Arab).
Ia memungkinkan langkah-langkah penalaran (chain-of-thought).
Jendela konteksnya adalah 128K token.'
"""
resp = chat(
model="qwen3",
messages=[{"role": "user", "content": prompt}],
format=LLMFeatures.model_json_schema(),
options={"temperature": 0},
)
print(resp.message.content)
Keluaran yang mungkin:
{
"name": "Qwen3",
"supports_thinking": true,
"max_context_tokens": 128000,
"languages": ["English", "Chinese", "French", "Spanish", "Arabic"]
}
Contoh 3: Bandingkan Banyak Model
Masukkan deskripsi dari beberapa model dan ekstrak ke bentuk terstruktur:
from typing import List
class ModelComparison(BaseModel):
models: List[LLMFeatures]
prompt = """
Ekstrak fitur setiap model ke dalam JSON.
1. Llama 3.1 mendukung penalaran. Jendela konteks adalah 128K. Bahasa: Inggris saja.
2. GPT-4 Turbo mendukung penalaran. Jendela konteks adalah 128K. Bahasa: Inggris, Jepang.
3. Qwen3 mendukung penalaran. Jendela konteks adalah 128K. Bahasa: Inggris, Cina, Prancis, Spanyol, Arab.
"""
resp = chat(
model="qwen3",
messages=[{"role": "user", "content": prompt}],
format=ModelComparison.model_json_schema(),
options={"temperature": 0},
)
print(resp.message.content)
Keluaran:
{
"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"]
}
]
}
Ini membuatnya sangat mudah untuk membenchmark, memvisualisasi, atau menyaring model berdasarkan fiturnya.
Contoh 4: Deteksi Celah Secara Otomatis
Anda bahkan dapat memungkinkan nilai null
ketika bidang hilang:
from typing import Optional
class FlexibleLLMFeatures(BaseModel):
name: str
supports_thinking: Optional[bool]
max_context_tokens: Optional[int]
languages: Optional[List[str]]
Ini memastikan skema Anda tetap valid meskipun beberapa informasi tidak diketahui.
Manfaat, Peringatan & Praktik Terbaik
Menggunakan keluaran terstruktur melalui Ollama (atau sistem lain yang mendukungnya) menawarkan banyak keuntungan — tetapi juga memiliki beberapa peringatan.
Manfaat
- Jaminan yang lebih kuat: Model diminta untuk sesuai dengan skema JSON daripada teks bebas.
- Pemrosesan yang lebih mudah: Anda dapat langsung
json.loads
atau memvalidasi dengan Pydantic / Zod, daripada regex atau heuristik. - Evolusi berbasis skema: Anda dapat mengversi skema Anda, menambahkan bidang (dengan nilai default), dan mempertahankan kompatibilitas mundur.
- Interoperabilitas: Sistem downstream mengharapkan data terstruktur.
- Determinisme (lebih baik dengan suhu rendah): Ketika suhu rendah (misalnya, 0), model lebih mungkin untuk ketat mengikuti skema. Ollama merekomendasikan ini.
Peringatan & Pitfall
- Ketidakcocokan skema: Model mungkin masih menyimpang — misalnya, melewatkan properti yang diperlukan, mengubah urutan kunci, atau menambahkan bidang ekstra. Anda perlu validasi.
- Skema yang kompleks: Skema JSON yang sangat dalam atau rekursif mungkin membingungkan model atau menyebabkan kegagalan.
- Ambiguitas dalam prompt: Jika prompt Anda kabur, model mungkin menebak bidang atau unit secara salah.
- Konsistensi antar model: Beberapa model mungkin lebih baik atau lebih buruk dalam mematuhi batasan terstruktur.
- Batas token: Skema itu sendiri menambahkan biaya token ke prompt atau panggilan API.
Praktik Terbaik & Tips (diambil dari blog Ollama + pengalaman)
- Gunakan Pydantic (Python) atau Zod (JavaScript) untuk mendefinisikan skema Anda dan menghasilkan JSON skema secara otomatis. Ini menghindari kesalahan manual.
- Selalu sertakan instruksi seperti “jawab hanya dalam JSON” atau “jangan termasuk komentar atau teks tambahan” dalam prompt Anda.
- Gunakan suhu = 0 (atau sangat rendah) untuk meminimalkan keacakan dan memaksimalkan kepatuhan skema. Ollama merekomendasikan determinisme.
- Validasi dan potensi fallback (misalnya, ulang atau bersihkan) ketika pemrosesan JSON gagal atau validasi skema gagal.
- Mulai dengan skema yang lebih sederhana, lalu secara bertahap perluas. Jangan terlalu rumit pada awalnya.
- Sertakan instruksi kesalahan yang terbatas tetapi membantu: misalnya, jika model tidak dapat mengisi bidang yang diperlukan, jawab dengan
null
daripada menghilangkannya (jika skema Anda memungkinkan).
Contoh Go 1: Ekstraksi Fitur LLM
Berikut adalah program Go sederhana yang meminta Qwen3 untuk keluaran terstruktur tentang fitur LLM.
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 := `
Analisis deskripsi berikut dan kembalikan fitur model dalam JSON hanya.
Deskripsi:
"Qwen3 memiliki dukungan multibahasa yang kuat (Inggris, Cina, Prancis, Spanyol, Arab).
Ia memungkinkan langkah-langkah penalaran (chain-of-thought).
Jendela konteksnya adalah 128K token."
`
// Definisikan skema JSON untuk keluaran terstruktur
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"},
}
// Konversi skema ke JSON
formatJSON, err := json.Marshal(formatSchema)
if err != nil {
log.Fatal("Gagal mengubah format skema:", 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 {
// Akumulasikan konten saat mengalir
rawResponse += response.Response
// Hanya parse ketika respons selesai
if response.Done {
if err := json.Unmarshal([]byte(rawResponse), &features); err != nil {
return fmt.Errorf("kesalahan parse JSON: %v", err)
}
}
return nil
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Struktur yang diparse: %+v\n", features)
}
Untuk mengompilasi dan menjalankan program Go ini — asumsikan kita memiliki file main.go ini dalam folder ollama-struct
,
Kita perlu menjalankan di dalam folder ini:
# inisialisasi modul
go mod init ollama-struct
# unduh semua dependensi
go mod tidy
# bangun & jalankan
go build -o ollama-struct main.go
./ollama-struct
Contoh Keluaran
Struktur yang diparse: {Name:Qwen3 SupportsThinking:true MaxContextTokens:128000 Languages:[English Chinese French Spanish Arabic]}
Contoh Go 2: Membandingkan Banyak Model
Anda dapat memperluas ini untuk mengekstrak daftar model untuk perbandingan.
type ModelComparison struct {
Models []LLMFeatures `json:"models"`
}
prompt = `
Ekstrak fitur dari deskripsi model berikut dan kembalikan sebagai JSON:
1. PaLM 2: Model ini memiliki kemampuan penalaran terbatas dan fokus pada pemahaman bahasa dasar. Ia mendukung jendela konteks 8.000 token. Ia secara utama mendukung bahasa Inggris saja.
2. LLaMA 2: Model ini memiliki kemampuan penalaran sedang dan dapat menangani beberapa tugas logis. Ia dapat memproses hingga 4.000 token dalam konteksnya. Ia mendukung bahasa Inggris, Spanyol, dan Italia.
3. Codex: Model ini memiliki kemampuan penalaran kuat secara khusus untuk pemrograman dan analisis kode. Ia memiliki jendela konteks 16.000 token. Ia mendukung bahasa Inggris, Python, JavaScript, dan Java.
Kembalikan objek JSON dengan array "models" yang berisi semua model.
`
// Definisikan skema JSON untuk perbandingan model
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"},
}
// Konversi skema ke JSON
comparisonFormatJSON, err := json.Marshal(comparisonSchema)
if err != nil {
log.Fatal("Gagal mengubah skema perbandingan:", 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 {
// Akumulasikan konten saat mengalir
comparisonResponse += response.Response
// Hanya parse ketika respons selesai
if response.Done {
if err := json.Unmarshal([]byte(comparisonResponse), &comp); err != nil {
return fmt.Errorf("kesalahan parse JSON: %v", err)
}
}
return nil
})
if err != nil {
log.Fatal(err)
}
for _, m := range comp.Models {
fmt.Printf("%s: Konteks=%d, Bahasa=%v\n", m.Name, m.MaxContextTokens, m.Languages)
}
Contoh Keluaran
PaLM 2: Konteks=8000, Bahasa=[English]
LLaMA 2: Konteks=4000, Bahasa=[English Spanish Italian]
Codex: Konteks=16000, Bahasa=[English Python JavaScript Java]
Secara samping, qwen3:4b pada contoh-contoh ini bekerja dengan baik, sama seperti qwen3:8b.
Praktik Terbaik untuk Pengembang Go
- Atur suhu ke 0 untuk kepatuhan skema maksimal.
- Validasi dengan
json.Unmarshal
dan fallback jika parsing gagal. - Jaga skema sederhana — struktur JSON yang sangat bersarang atau rekursif mungkin menyebabkan masalah.
- Izinkan bidang opsional (gunakan
omitempty
dalam tag struct Go) jika Anda mengharapkan data yang hilang. - Tambahkan ulang jika model secara terkadang menghasilkan JSON yang tidak valid.
Contoh Penuh - Menggambar Grafik dengan Spesifikasi LLM (Langkah-langkah: dari JSON terstruktur ke tabel perbandingan)
- Definisikan skema untuk data yang Anda inginkan
Gunakan Pydantic sehingga Anda dapat (a) menghasilkan JSON Schema untuk Ollama dan (b) memvalidasi respons model.
from pydantic import BaseModel
from typing import List, Optional
class LLMFeatures(BaseModel):
name: str
supports_thinking: bool
max_context_tokens: int
languages: List[str]
- Minta Ollama untuk mengembalikan hanya JSON dalam bentuk itu
Kirimkan skema dalam format=
dan turunkan suhu untuk determinisme.
from ollama import chat
prompt = """
Ekstrak fitur untuk setiap model. Kembalikan JSON hanya yang cocok dengan skema.
1) Qwen3 mendukung chain-of-thought; 128K konteks; Inggris, Cina, Prancis, Spanyol, Arab.
2) Llama 3.1 mendukung chain-of-thought; 128K konteks; Inggris.
3) GPT-4 Turbo mendukung chain-of-thought; 128K konteks; Inggris, Jepang.
"""
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 # Daftar JSON LLMFeatures
- Validasi & normalisasi
Selalu validasi sebelum digunakan dalam produksi.
from pydantic import TypeAdapter
adapter = TypeAdapter(list[LLMFeatures])
models = adapter.validate_json(raw_json) # -> list[LLMFeatures]
- Bangun tabel perbandingan (pandas)
Ubah objek yang divalidasi menjadi DataFrame yang dapat diurutkan/filter dan diekspor.
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))
# Ubah urutan kolom untuk keterbacaan
df = df[["name", "supports_thinking", "max_context_tokens", "languages_count", "languages"]]
# Simpan sebagai CSV untuk penggunaan lebih lanjut
df.to_csv("llm_feature_comparison.csv", index=False)
- (Opsional) Visualisasi cepat
Grafik sederhana membantu Anda melihat perbedaan antara model secara cepat.
import matplotlib.pyplot as plt
plt.figure()
plt.bar(df["name"], df["max_context_tokens"])
plt.title("Jendela Konteks Maksimum per Model (token)")
plt.xlabel("Model")
plt.ylabel("Token Konteks Maksimum")
plt.xticks(rotation=20, ha="right")
plt.tight_layout()
plt.savefig("max_context_window.png")
TL;DR
Dengan dukungan keluaran terstruktur baru dari Ollama, Anda dapat memperlakukan LLMs bukan hanya sebagai chatbot tetapi sebagai mesin ekstraksi data.
Contoh di atas menunjukkan bagaimana secara otomatis mengekstrak metadata terstruktur tentang fitur LLM seperti dukungan penalaran, ukuran jendela konteks, dan bahasa yang didukung — tugas yang sebelumnya memerlukan pemrosesan rapuh.
Apakah Anda sedang membangun katalog model LLM, dashboard evaluasi, atau asisten penelitian berbasis AI, keluaran terstruktur membuat integrasi halus, andal, dan siap produksi.
Tautan yang Berguna
- https://ollama.com/blog/structured-outputs
- Ollama cheatsheet
- Python Cheatsheet
- AWS SAM + AWS SQS + Python PowerTools
- Golang Cheatsheet
- Membandingkan Go ORMs untuk PostgreSQL: GORM vs Ent vs Bun vs sqlc
- Mengurutkan ulang dokumen teks dengan Ollama dan Qwen3 Embedding model - dalam Go
- Kinerja AWS lambda: JavaScript vs Python vs Golang