# Output Terstruktur LLM: JSON Schema yang Benar-Benar Tervalidasi

> Meminta LLM mengembalikan JSON tidak sama dengan menjaminnya. Berikut cara batasan JSON Schema, pemanggilan berbasis tool, dan validasi Zod mengubah output probabilistik menjadi sesuatu yang bisa dipercaya di produksi.

URL: https://www.matthewswong.com/id/blog/llm-structured-output-json-schema/

- Author: Matthews Wong (https://www.matthewswong.com/id/about/)
- Published: 2026-07-01
- Updated: 2026-07-01
- Category: AI

Pertama kali sebuah job produksi gagal karena pagar markdown yang tiba-tiba membungkus payload JSON yang sebenarnya sudah sempurna, saya belajar dengan cara yang menyakitkan bahwa meminta model bahasa mengembalikan JSON dengan sopan tidak sama dengan benar-benar menerima JSON yang bisa diparse. Model itu mengikuti instruksi. Ia hanya juga menambahkan satu kalimat ramah sebelum objeknya, karena tidak ada yang benar-benar mencegahnya melakukan itu.

Tulisan ini membahas bagian-bagian yang benar-benar membuat output terstruktur bisa diandalkan: decoding terbatas melalui JSON Schema, memilih antara output JSON langsung dan pemanggilan fungsi berbasis tool, mendeteksi penolakan dan pemotongan respons sebelum keduanya merusak pipeline, serta memvalidasi setiap respons dengan Zod agar pelanggaran skema tidak pernah sampai ke database.

## Mengapa sekadar meminta JSON tidak cukup

Rekayasa prompt bisa membawa Anda mendekati hasil yang benar, tetapi mendekati bukan berarti terjamin. Bahkan system prompt yang ditulis rapi dan berisi instruksi untuk hanya merespons dengan JSON valid sesuai bentuk tertentu, sesekali tetap bisa menghasilkan output yang secara teknis memenuhi instruksi namun merusak setiap parser di sisi Anda. Pola kegagalannya cukup dapat diprediksi setelah Anda melihatnya beberapa kali.

Model membungkus objeknya dalam pagar kode markdown, sehingga panggilan JSON parse yang naif langsung melempar error pada karakter pertama.

Field yang diwajibkan skema justru diringkas menjadi kalimat prosa dan tidak terisi nilainya, karena model menganggap penjelasan singkat lebih membantu daripada nilai yang ketat.

Field numerik datang sebagai string yang diberi tanda kutip, atau field enum diisi sinonim yang menurut model setara tetapi tidak dikenali validator Anda.

Semua ini bukan bug pada model. Ini adalah akibat alami dari memperlakukan generator teks probabilistik seolah ia adalah serializer deterministik. Jika sistem Anda bergantung pada bentuk respons, Anda membutuhkan mekanisme yang membatasi proses pembuatannya sendiri, bukan sekadar permintaan yang disusun dengan sopan.

> Baik API Claude maupun API OpenAI sekarang menyediakan mode output terstruktur sungguhan yang membatasi token yang dihasilkan hanya pada token yang diizinkan oleh skema valid. Ini secara fundamental berbeda dari sekadar meminta JSON dan berharap yang terbaik. Gunakan fitur native ini terlebih dahulu sebelum beralih ke pembersihan teks respons dengan regex.

## Decoding terbatas: cara kerja output terstruktur sesungguhnya

Decoding terbatas berarti API mengompilasi JSON Schema Anda menjadi sebuah grammar dan membatasi token mana yang boleh dikeluarkan model pada setiap langkah pembuatan teks. Jika skema Anda menyatakan sebuah field harus salah satu dari tiga nilai enum, model secara mekanis dicegah menghasilkan nilai keempat. Ini diberlakukan pada lapisan sampling, bukan melalui validasi setelah fakta, sehingga tidak ada jendela waktu di mana model bisa saja mengeluarkan sesuatu yang lain lalu lolos dari pemeriksaan yang longgar.

Claude menyediakan ini melalui dua mekanisme yang saling melengkapi: format output JSON yang dilampirkan pada permintaan pesan, dan strict tool use di mana skema input sebuah tool diberlakukan dengan cara yang sama. Fitur setara di OpenAI adalah flag strict pada response_format bertipe json_schema, yang datang dengan aturan tegas: setiap properti harus dicantumkan sebagai required, dan additionalProperties harus secara eksplisit diset ke false. Anda tidak bisa begitu saja menghilangkan sebuah field opsional dari daftar required seperti pada JSON Schema buatan sendiri; cara idiomatik untuk mengatasinya adalah membuat tipe field tersebut nullable.

## Memilih antara mode output JSON dan pemanggilan tool

Kedua penyedia memberi Anda dua bentuk untuk jaminan yang sama, dan pilihan yang tepat bergantung pada apa yang direpresentasikan oleh respons dalam aplikasi Anda, bukan pada bentuk mana yang terasa lebih familiar.

| Pendekatan | Paling cocok untuk | Yang perlu diwaspadai |
| --- | --- | --- |
| Mode output JSON | Satu dokumen terstruktur sebagai seluruh respons, seperti invoice yang diekstraksi atau bagian laporan yang dihasilkan | Latensi pada permintaan pertama saat skema dikompilasi menjadi grammar; kedua penyedia menyimpan cache skema yang sudah dikompilasi untuk dipakai ulang |
| Pemanggilan tool atau fungsi secara strict | Perulangan agent di mana model memilih di antara beberapa aksi yang mungkin, masing-masing dengan bentuk argumennya sendiri | Anda perlu definisi tool yang berbeda untuk setiap jenis aksi, dan model tetap harus memilih tool yang tepat dengan benar |
| JSON hanya berdasarkan prompt, tanpa batasan skema | Hanya untuk prototipe sekali pakai dan eksperimen manual | Tidak ada jaminan struktural sama sekali; jangan pernah menerapkan jalur ini pada sistem produksi yang memparsing output |

## Menangani penolakan dan pemotongan respons dengan baik

Batasan skema menjamin bentuk dari respons yang selesai. Ia tidak menjamin apakah respons itu benar-benar selesai, atau apakah model justru menolak menjawab sama sekali. Kedua penyedia memberi Anda sinyal untuk ini, dan mengabaikannya adalah cara paling umum output yang sudah dibatasi skema tetap bisa gagal di produksi.

Periksa field stop reason atau finish reason sebelum Anda mencoba memparsing apa pun. Penolakan atau pemotongan karena panjang berarti tidak ada payload valid sesuai skema yang bisa diekstraksi, seberapa pun cermat Anda menulis skemanya.

Saat terjadi penolakan, tampilkan error yang jelas dan spesifik ke pemanggil, bukan mencoba menyelamatkan teks parsial. OpenAI mengembalikan field refusal khusus untuk kasus ini, dan Claude melaporkan stop reason berupa refusal.

Saat terjadi pemotongan, artinya respons mencapai batas token maksimum di tengah objek, jangan coba memperbaiki JSON secara manual. Coba lagi dengan batas token yang lebih tinggi atau skema yang lebih sempit yang meminta lebih sedikit sekaligus.

Catat teks respons mentah pada setiap stop reason yang bukan sukses sebelum Anda membuangnya. Saat pertama kali skema yang Anda kira sudah rapat tetap menghasilkan array yang terpotong, Anda akan membutuhkan payload itu untuk debugging.

> Respons yang dibatasi skema tetap bisa gagal diparsing jika Anda melewatkan pemeriksaan stop reason. Penolakan atau pemotongan karena max token menghasilkan teks yang bukan objek JSON lengkap, dan memanggil JSON parse padanya akan tetap melempar error terlepas seberapa ketat skema Anda. Selalu periksa stop reason terlebih dahulu sebelum melangkah lebih jauh.

## Validasi setiap respons dengan Zod sebelum menyentuh database Anda

Pembuatan yang dibatasi skema membuat JSON yang cacat jarang terjadi, bukan mustahil, dan itu tidak menjamin apakah nilai-nilainya memang yang Anda inginkan. Model bisa mengembalikan objek yang secara sintaksis sempurna dengan jumlah bernilai negatif atau nomor invoice yang sebenarnya adalah nama pelanggan. Perlakukan respons yang sudah dibatasi skema sama seperti input eksternal lainnya: parsing dulu, lalu validasi dengan pustaka skema sungguhan sebelum menyentuh proses penyimpanan.

Menjalankan bentuk yang sama melalui safeParse milik Zod memberi Anda dua hal yang tidak bisa diberikan oleh JSON parse mentah: objek bertipe yang bisa dipercaya oleh kode TypeScript Anda di tahap selanjutnya, dan array masalah terstruktur yang bisa Anda catat atau kembalikan ke pemanggil, bukan sekadar exception parsing yang generik. Jika Anda sudah menggunakan skema Zod untuk membangun JSON Schema yang dikirim ke model, seperti pada contoh sebelumnya, langkah validasi ini hampir gratis karena definisi skema yang sama dipakai dua kali.

## Saat output yang dibatasi skema saja tidak cukup

JSON Schema dan Zod keduanya memvalidasi bentuk dan tipe dari satu respons. Tidak ada satu pun yang bisa menyatakan relasi antar-field yang hanya bermakna bila digabungkan, atau fakta yang hidup di luar respons itu sendiri. Rencanakan tahap validasi kedua untuk kasus-kasus seperti ini.

Aturan bisnis yang melibatkan beberapa field sekaligus, seperti tanggal akhir yang harus jatuh setelah tanggal mulai, yang membutuhkan fungsi refine khusus dan bukan skema biasa.

Pemeriksaan lintas-data, seperti memastikan sebuah ID order yang dirujuk benar-benar ada di database Anda, yang hanya bisa dijawab oleh pencarian sungguhan.

Rentang numerik yang spesifik pada domain Anda, seperti tarif pajak yang secara struktural adalah angka valid tetapi tidak masuk akal di atas ambang tertentu untuk bisnis Anda.

> Karena skema Zod bisa menghasilkan JSON Schema yang dikirim ke model dan sekaligus memvalidasi respons yang kembali, satu definisi TypeScript mencakup kedua ujung perjalanan bolak-balik itu. Kesimetrisan ini layak dirancang sejak awal proyek, bukan ditambahkan belakangan.

> Sebelum mempercayai sebuah skema di produksi, uji dengan prompt yang bersifat adversarial: input ambigu, permintaan yang seharusnya memicu penolakan, dan input yang sengaja dibuat mendekati batas token Anda. Skema yang selama pengembangan hanya pernah melihat input happy-path yang bersih akan mengejutkan Anda pada minggu pertama bertemu pengguna sungguhan.

## Sumber:

- Dokumentasi output terstruktur Claude
- Panduan output terstruktur OpenAI
- JSON Schema (Wikipedia)
- Zod: pustaka validasi skema TypeScript-first

## Source links

- https://platform.claude.com/docs/en/build-with-claude/structured-outputs
- https://developers.openai.com/api/docs/guides/structured-outputs
- https://en.wikipedia.org/wiki/JSON_Schema
- https://zod.dev

## FAQ

### Apa perbedaan antara meminta JSON lewat prompt dan menggunakan structured outputs?

Meminta JSON lewat prompt hanya meminta model mengikuti instruksi, tetapi tidak ada yang mencegahnya menambahkan teks lain, membungkus objek dalam pagar markdown, atau melewatkan field yang diwajibkan. Structured outputs mengompilasi JSON Schema Anda menjadi grammar yang membatasi token yang boleh dihasilkan model pada setiap langkah, sehingga respons secara mekanis dibatasi agar sesuai skema, bukan sekadar dianjurkan untuk sesuai.

### Mengapa output terstruktur saya kadang tetap gagal diparsing?

Sebab paling umum adalah melewatkan pemeriksaan stop reason atau finish reason sebelum melakukan parsing. Jika model menolak permintaan atau respons terpotong karena batas max token, teksnya bukan objek JSON yang lengkap seberapa pun ketat skemanya, dan memanggil JSON parse padanya akan tetap melempar error. Selalu periksa stop reason terlebih dahulu, dan hanya coba parsing respons yang selesai secara normal.

### Bisakah saya memakai Zod untuk sekaligus membuat skema dan memvalidasi respons?

Ya, dan ini salah satu pola paling berguna yang tersedia. Satu skema Zod bisa diubah menjadi JSON Schema yang dikirim ke model, lalu dipakai lagi dengan safeParse untuk memvalidasi respons yang kembali. Ini membuat kedua ujung perjalanan bolak-balik terdefinisi di satu tempat dan memberi Anda objek bertipe plus daftar error terstruktur jika validasi gagal.

### Sebaiknya saya pakai mode output JSON atau pemanggilan tool untuk ekstraksi terstruktur?

Gunakan mode output JSON saat seluruh respons adalah satu dokumen terstruktur, seperti invoice yang diekstraksi atau bagian laporan yang dihasilkan. Gunakan strict tool atau function calling saat model memilih di antara beberapa aksi dalam perulangan agent, karena setiap aksi bisa memiliki skema argumennya sendiri. Keduanya memberi jaminan skema yang sama; pilihannya bergantung pada apa yang direpresentasikan respons tersebut.

### Apakah JSON Schema yang valid berarti nilai dalam respons pasti benar?

Tidak. Batasan skema hanya menjamin bentuk dan tipe respons, bukan kebenaran bisnis dari nilai di dalamnya. Model bisa mengembalikan objek yang secara sintaksis sempurna dengan jumlah bernilai negatif atau field yang tertukar. Anda tetap membutuhkan tahap validasi kedua, seperti fungsi refine Zod atau pencarian ke database, untuk aturan yang melibatkan beberapa field atau bergantung pada keadaan eksternal.

---

- HTML version: https://www.matthewswong.com/id/blog/llm-structured-output-json-schema/
- English: https://www.matthewswong.com/en/blog/llm-structured-output-json-schema/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
