← Semua pembelajaran / AI Engineer Nol → Production
Fase 1 · Interaksi Pertama dengan LLM

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
PendekatanKeandalanKompleksitas
Instruksi prompt sajaCukup baik, tidak dijamin 100%Paling sederhana
Skema dipaksakan (tool/structured output)Dijamin sesuai skema oleh providerButuh 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.