Structured output — memaksa model menjawab dalam format tetap
LLM secara alami menghasilkan teks bebas. Begitu jawabannya harus masuk ke sistem lain — database, API, UI — kamu butuh format yang bisa diprediksi. Structured output adalah jawabannya.
Intisari
- Tanpa dipaksa, model bisa menambahkan basa-basi ('Tentu, berikut jawabannya:') di sekitar JSON yang diminta — merusak
json.loads(). - Pendekatan paling dasar: instruksikan format lewat prompt + berikan contoh (few-shot) — bekerja cukup baik tapi tidak 100% dijamin.
- Pendekatan lebih kuat: berikan skema eksplisit (JSON Schema) yang dipaksakan oleh provider — output dijamin valid sesuai skema.
- Skema yang sama juga dipakai untuk tool use/function calling (Fase 2) — keduanya memakai mekanisme dasar yang mirip: memberi model 'bentuk' yang harus diisi.
- Selalu validasi ulang di sisi aplikasi (mis. dengan Pydantic) — 'dijamin oleh provider' tetap berarti kode konsumennya harus siap menangani kegagalan parsing.
Masalahnya: prosa vs data
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=200,
messages=[{"role": "user", "content": "Ekstrak nama dan umur dari: 'Nama saya Sari, umur 28 tahun'"}],
)
print(response.content[0].text)
# "Tentu, berikut hasilnya: {\"nama\": \"Sari\", \"umur\": 28}"
# ^^^^^^^^^^^^^^^^^^^^^^^^^ basa-basi ini merusak json.loads() langsung
Model dilatih untuk "membantu" secara percakapan — kecenderungan ini bagus untuk chatbot, tapi mengganggu
saat kamu butuh output yang langsung bisa di-parse oleh kode. Ada dua tingkat solusi.
Tingkat 1: instruksi eksplisit + contoh
Solusi paling dasar — dan sering cukup — adalah instruksi yang sangat eksplisit di system prompt, dibahas juga di materi sebelumnya:
system = """Jawab HANYA dengan objek JSON valid, tanpa teks lain sebelum atau sesudahnya.
Jangan gunakan markdown code block. Skema: {"nama": string, "umur": number}"""
Kelemahannya: ini "permintaan baik-baik", bukan jaminan. Model kadang tetap menambahkan penjelasan, salah format angka, atau lupa satu field — terutama pada prompt yang kompleks. Untuk prototipe cepat ini sering cukup; untuk sistem produksi yang harus andal, naik ke tingkat berikutnya.
Tingkat 2: skema yang dipaksakan (structured output / tool schema)
Provider modern menyediakan mekanisme yang benar-benar memaksa struktur output sesuai skema yang kamu definisikan — bukan sekadar meminta lewat prompt. Anthropic melakukannya lewat mekanisme tool use (dibahas penuh di Fase 2): kamu definisikan "tool" dengan skema JSON, dan minta model selalu memanggilnya — hasilnya adalah objek terstruktur yang valid, bukan teks bebas.
tools = [{
"name": "catat_pelanggan",
"description": "Catat data pelanggan yang diekstrak dari pesan",
"input_schema": {
"type": "object",
"properties": {
"nama": {"type": "string"},
"umur": {"type": "integer"},
},
"required": ["nama", "umur"],
},
}]
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=200,
tools=tools,
tool_choice={"type": "tool", "name": "catat_pelanggan"}, # paksa selalu panggil tool ini
messages=[{"role": "user", "content": "Nama saya Sari, umur 28 tahun"}],
)
data = response.content[0].input # {"nama": "Sari", "umur": 28} — sudah objek Python, bukan string
| Pendekatan | Keandalan | Kompleksitas |
|---|---|---|
| Instruksi prompt saja | Cukup baik, tidak dijamin 100% | Paling sederhana |
| Skema dipaksakan (tool/structured output) | Dijamin sesuai skema oleh provider | Butuh definisi skema eksplisit |
Tetap validasi di sisi aplikasi. "Dijamin oleh provider" berarti bentuknya sesuai skema — bukan berarti isinya selalu masuk akal (umur -5 tetap "integer" yang valid). Bungkus hasilnya dengan validator seperti Pydantic supaya aturan bisnis (rentang nilai, format email, dst) tetap ditegakkan di kodemu sendiri.
from pydantic import BaseModel, Field
class Pelanggan(BaseModel):
nama: str
umur: int = Field(ge=0, le=120)
pelanggan = Pelanggan(**data) # ValidationError kalau umur di luar rentang wajar
Latihan: jalankan pendekatan "instruksi prompt saja" sepuluh kali dengan input yang sedikit
bervariasi, dan hitung berapa kali json.loads() gagal. Lalu ganti ke pendekatan skema
terpaksa (tool) dan ulangi — bandingkan tingkat kegagalannya.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.