Lensa ML

Structured Outputs

Langkah 1 dari 6: Masalahnya

Sampling bebas bisa merusak format

Temperature T0.80
Sampel N100
Sampel 1, prompt: "Return the user Ada, age 36, as JSON"{"name": "Ada", "age": 36} <eos>Semua 100 sampel, satu kotak per sampelKenapa yang tidak valid gagalTeks sebelum JSON14Umur bukan angka7Field hilang5Berhenti terlalu cepat2Syntax error7Toy model, logit ilustratif atas vocabulary 26 token
seed 1
Output valid
65 / 100
Tingkat kegagalan
35.0%
Kegagalan terbanyak
Teks sebelum JSON

Sampel 1 lolos parse, tetapi 35 dari 100 sampel tidak. Menaikkan T menyebarkan probabilitas ke token yang merusak format, dan tingkat kegagalan pun naik.

Geser Temperature T dan Sampel N, lalu tekan Sampel ulang untuk mengambil set balasan baru. Model bahasa tidak punya pemahaman bawaan tentang JSON. Ia mempelajari formatnya dari teks, jadi setiap token yang dikeluarkannya adalah soal probabilitas, dan setelah banyak panggilan, token buruk yang langka akan muncul. Satu karakter nyasar saja membuat json.loads memunculkan error. Prompt yang berbunyi "reply only with JSON" menurunkan tingkat kegagalan, tetapi tidak pernah sampai nol, dan biayanya naik seiring jumlah panggilan dalam pipeline.

Langkah 2 dari 6: Logit Masking

Token ilegal mendapat logit −∞

Temperature T1.00
Output sejauh ini (token 1 dari 11, state s1){"softmax tanpa masksetelah maskp setelah mask{"95.7%Sure−∞```−∞{4.3%":␣−∞thirty−∞,␣"−∞",−∞":−∞}−∞10 teratas dari 26 token contoh menurut logit (ilustratif). Baris merah melanggar schema.
Token yang diizinkan
2 / 26
Massa dibuang
30.1%
Token terpilih
{"
p-nya setelah mask
95.7%

Di sini 30,1% probabilitas tanpa mask berada pada token yang melanggar schema, terutama Sure. Mengatur logit-nya ke −∞ memberikan massa itu ke 2 token yang sah.

Tekan Putar, atau pakai Langkah dan Mundur untuk bergerak satu token setiap kali, lalu geser T untuk melihat batang tanpa mask menyebar. Masking terjadi di antara forward pass model dan sampler: logit keluar, entri ilegal menjadi −∞, dan softmax memberinya probabilitas tepat nol. Weight model tidak pernah berubah. Karena probabilitas yang tersisa dinormalisasi ulang, temperature, top-k, dan top-p tetap bekerja pada set yang diizinkan. Greedy decoding di bawah mask memilih logit sah tertinggi di setiap langkah.

Langkah 3 dari 6: Dari Grammar ke State Machine

Schema dikompilasi menjadi FSM

Karakter terbaca0
Schema {"name": string, "age": int}, dibaca satu karakter setiap kali{"name": "Ada", "age": 36}{"name":␣"a-za-z,␣"age":␣0-90-9end24 state. Kotak menunjukkan karakter yang diterima berikutnya; loop mengulang kelas.
State saat ini
s1 dari 24
Boleh berikutnya
{
Accepting
tidak
Token contoh diizinkan
2 / 26

Di dalam teks tetap, FSM punya satu jalan maju: karakter berikutnya harus "{". Karakter lain, atau token yang memuatnya, ditolak.

Geser Karakter terbaca di sepanjang objek contoh dan ikuti state yang disorot. Regular expression dikompilasi menjadi finite-state machine, dan schema JSON dengan key tetap dan tipe sederhana bisa lebih dulu ditulis sebagai regex semacam itu. Layout contoh ini memakai spasi yang tetap; compiler sungguhan mengizinkan spasi dan baris baru opsional, yang menambah cabang. Objek dan array bersarang dengan kedalaman berapa pun butuh stack, jadi library menanganinya dengan context-free grammar dan pushdown automaton, bukan FSM biasa.

Langkah 4 dari 6: Batas Token

Hitung token yang diizinkan per state di awal

FSM state (s1 sampai s24)6
Teks terpendek yang mencapai s7, lalu baris indeks untuk s7{"name▏{ditolak{"ditolak"→ s8nameditolakageditolak":→ s9 (2)":␣"→ s11 (4)":␣→ s10 (3)",ditolak,␣"ditolak␣"ditolak␣ditolak:ditolakAdaditolakAditolakdaditolakthirtyditolak36ditolak3ditolak6ditolak}ditolak36}ditolak"}ditolakSureditolak```ditolak<eos>ditolakBiru (n): token mencakup n simbol grammar. Dibuat sekali, sebelum decoding.
Diizinkan di sini
4 / 26
Mencakup 2+ simbol
3
Entri indeks contoh
624
Dengan vocab 32,000 token
768,000

Satu token ":␣" mencakup 4 simbol grammar dan langsung melompat dari s7 ke s11. Mencocokkan token utuh dengan simbol utuh akan menolaknya, jadi indeks menelusuri setiap karakternya lewat FSM.

Geser FSM state dari s1 sampai s24 dan bandingkan mana dari 26 kotak yang menyala. Willard dan Louf (2023) mengusulkan pembuatan indeks ini sekali per schema: untuk setiap state, telusuri setiap token vocabulary lewat mesin, lalu simpan token yang lolos beserta state akhirnya. Decoding lalu hanya butuh satu lookup per langkah, bukan pemindaian vocabulary. Dengan 32.000 token milik model chat 7B dan 24 state ini, indeksnya butuh 768.000 penelusuran token, yang selesai sebelum request pertama.

Langkah 5 dari 6: Biaya dan Kualitas

Urutan field mengubah jawaban

Temperature T1.00
Prompt: "What is 17 × 24? Reply as JSON."{"answer": ▮, "reasoning": "..."}softmax tanpa maskmask int diterapkanp setelah mask40840.4%41824.5%39814.8%41012.2%3888.1%Let−∞I−∞"−∞Logit ilustratif untuk token pertama nilai answer. Yang benar: 408.
p("408") setelah mask
40.4%
Massa dibuang mask
58.2%
Kerja mask vs forward pass
1 : 421,250

Model ingin mulai menjelaskan ("Let", "I"), tetapi field int memaksa angka sekarang juga. Mask memindahkan 58,2% probabilitas ke digit, dan "408" mendapat 40,4%, dibanding 24,5% untuk "418".

Ganti antara Jawaban dulu dan Penalaran dulu, lalu geser T. Mask-nya sendiri murah: penerapannya menyentuh 32.000 logit, sedangkan satu forward pass model 7B memakan sekitar 2 × 6,74 miliar, atau 13,5 miliar, FLOP. Biaya kualitasnya lain cerita. Tam et al. (2024) menemukan bahwa aturan format yang ketat bisa menurunkan skor pada tugas penalaran. Karena model menulis field sesuai urutan schema, field penalaran yang ditaruh sebelum jawaban berfungsi sebagai chain of thought singkat di dalam JSON.

Langkah 6 dari 6: Dalam Praktik

JSON mode, schema, dan tool

Jumlah panggilan (retry sampai valid)1
Prompt sajaJSON modeConstrainedJSON validSesuai schemaGagal per panggilanTool umumtidak selalutidak selalu43.5%selalutidak selalu13.9%selaluselalu0.0%instruksi, lalu parse dan retryPrompt saja: peluang balasan valid dalam k panggilan56.5%k = 181.1%k = 291.8%k = 396.4%k = 498.4%k = 5Tingkat kegagalan dari toy model langkah 1 pada T = 0.8, 1.000 sampel
Gagal per panggilan
43.5%
Valid dalam 1 panggilan
56.5%
Perkiraan panggilan per balasan valid
1.77

Dengan instruksi saja, 43,5% balasan gagal di-parse atau tidak cocok dengan schema. 1 panggilan menaikkan tingkat keberhasilan menjadi 56,5%, dengan rata-rata 1,77 panggilan per balasan valid.

Pilih Prompt saja, JSON mode, atau Constrained, lalu geser Jumlah panggilan untuk melihat dampak retry. Function calling dan tool calling memakai mekanisme yang sama: schema argumen tool menjadi batasan untuk bagian balasan itu. Outlines mengimplementasikan indeks FSM, XGrammar menangani context-free grammar, dan llama.cpp menerima berkas grammar GBNF. Mesin serving seperti vLLM menyediakan guided decoding lewat backend ini. Beberapa API hosted juga menerima schema JSON secara langsung, sehingga batasannya berjalan di sisi penyedia.

Apa itu structured outputs pada LLM?

Structured outputs adalah balasan model yang dijamin mengikuti format tetap, misalnya JSON yang cocok dengan schema tertentu. Metode yang umum adalah constrained decoding: di setiap langkah, token apa pun yang akan merusak format mendapat logit −∞ sebelum sampling. Schema dikompilasi menjadi state machine, dan mesin itulah yang menentukan token mana yang sah berikutnya.

Program yang memanggil model bahasa biasanya butuh data, bukan prosa. Mengekstrak field dari dokumen, mengisi formulir, mengklasifikasikan tiket, dan memanggil tool atau fungsi semuanya mengharapkan balasan yang bisa dibaca parser pada percobaan pertama. Constrained decoding menghapus putaran parse dan retry, menjaga tipe field tetap benar, dan membuat model lokal kecil mampu menghasilkan output yang bisa dipercaya kode berikutnya.

Ide utama

  • Sampling bebas dari model bahasa menghasilkan JSON valid hampir setiap saat, tetapi tidak pernah selalu.
  • Constrained decoding mengatur logit setiap token yang merusak format menjadi −∞, sehingga token itu mendapat probabilitas nol.
  • Regex atau schema JSON sederhana dikompilasi menjadi finite-state machine yang mencantumkan karakter sah berikutnya di setiap state.
  • Token sering mencakup beberapa simbol grammar, jadi token yang diizinkan per state dicari dengan menelusuri setiap token lewat mesin, sekali, sebelum dipakai.
  • Mask nyaris tidak berbiaya per langkah, tetapi urutan field dalam schema bisa mengubah kualitas jawaban.
  • JSON mode hanya menjamin sintaks yang valid, sedangkan decoding dengan batasan schema juga menjamin field dan tipenya.

Pertanyaan umum

Apakah constrained decoding menjamin jawaban yang benar?

Tidak. Yang dijamin adalah bentuk output, bukan kebenaran nilainya. Field bertipe integer akan berisi integer, tetapi integernya tetap bisa salah. Validasi isi, seperti cek rentang atau pencocokan dengan sumber, tetap diperlukan.

Apa bedanya JSON mode dan structured outputs?

JSON mode membatasi balasan agar menjadi JSON yang valid secara sintaks, dengan key dan tipe nilai apa pun. Structured outputs membatasinya pada satu schema tertentu: key yang wajib, tipenya, dan nilai enum yang diizinkan. Kode yang memakai JSON mode tetap harus memeriksa schema sendiri.

Apakah constrained decoding memperlambat generation?

Per token, tambahan bebannya kecil setelah indeks atau grammar dikompilasi, karena mask hanya berupa lookup ditambah satu operasi atas vocabulary. Kompilasi schema yang besar atau rumit bisa memakan waktu yang terasa pada request pertama. Itu sebabnya library menyimpan grammar hasil kompilasi di cache.

Apakah constrained decoding bisa dipakai dengan model apa pun?

Bisa dengan model apa pun yang membuka logit token berikutnya, karena caranya hanya mengubah token mana yang boleh di-sample. Itu termasuk model open-weight lokal. Pada model hosted, tergantung apakah penyedianya menjalankan batasan itu di sisinya.

Penjelasan terkait

Referensi

  1. Willard and Louf (2023), "Efficient Guided Generation for Large Language Models"
  2. Dong et al. (2024), "XGrammar: Flexible and Efficient Structured Generation Engine for Large Language Models"
  3. Tam et al. (2024), "Let Me Speak Freely? A Study on the Impact of Format Restrictions on Performance of Large Language Models"

Terakhir diperbarui