Structured Outputs · 19-c05
26 Haziran 2026 · Sağlamlık notları
Aynı JSON Schema'yı üç farklı modele verdiğinizde üç farklı sonuç alabilirsiniz: biri tam uyar, biri sessizce alanları düşürür, biri kısıtlamayı yok sayar. "Structured output" artık her büyük sağlayıcıda var, ama "garanti" kelimesi her birinde aynı şeyi anlatmıyor. Portability bedava değildir: şemanızı her sağlayıcıda ayrı test edin.
- Soru
- şema her yerde aynı mı?
- Katman
- syntax vs semantics
- Mekanizma
- constrained decoding
- Tuzak
- tek şemaya güvenmek
Bu yazıdaki kavramlar
- structured output
- JSON Schema
- constrained decoding
- strict mode
- schema coverage
- portability
- JSONSchemaBench
Bir LLM'in çıktısını bir koda, bir API çağrısına ya da bir veritabanı kaydına bağladığınızda, o çıktının her seferinde aynı şekli tutması gerekir. Sağlayıcılar bunun için "structured output" özellikleri sundu: bir JSON Schema verirsiniz, model de o şemaya uyan bir cevap döndürür. Kulağa taşınabilir bir sözleşme gibi geliyor. Pratikte öyle değil.
Bu yazıda aynı şemanın neden her model ve sağlayıcıda aynı davranmadığını çiziyorum. Sağlayıcı-içi (native) modlar (OpenAI Structured Outputs, Anthropic, Google Gemini) ile kütüphane tarafı constrained decoding (Outlines, XGrammar, automata) arasındaki farkı, "garanti" kelimesinin sağlayıcıya göre ne anlama geldiğini ve JSONSchemaBench'in ölçtüğü kanıtı ele alıyorum. Şemalar kendi başına sürekli oynayarak her mekanizmayı gösteriyor.
Aynı şema, üç sonuç
Tek bir JSON Schema yazarsınız: birkaç zorunlu alan, bir optional alan, bir enum, biraz da iç içe yapı. Sonra onu üç sağlayıcıya gönderirsiniz. Sonuçlar çoğu zaman aynı olmaz. Birinde çıktı şemaya tam uyar (pass). Birinde JSON geçerlidir ama şemanın bir kısmı sessizce kaybolmuştur, örneğin desteklenmeyen bir kısıtlama atlanmıştır (partial). Birinde ise model kısıtlamayı tutturamaz (fail).
Bunun nedeni "structured output"un tek bir standart olmaması. Her sağlayıcı JSON Schema'nın farklı bir alt kümesini destekliyor, farklı bir motorla zorluyor ve desteklemediği özelliği farklı bir şekilde ele alıyor. JSONSchemaBench bu tabloyu net gösteriyor: en iyi framework en kötüsünün kabaca iki katı kadar gerçek dünya şemasını destekliyor.
Aşağıda tek bir şema üç sağlayıcıya açılıyor ve her biri farklı bir sonuç hücresinde duruyor. Aynı girdi, farklı çıktı. Yazının geri kalanı bu farkın nereden geldiğini açıklıyor.
- · zorunlu alanlar
- · optional alan
- · enum
- · iç içe nesne
"Garanti" tam olarak neyi garanti eder?
Sağlayıcılar "şema-uyumlu cevap garanti ediyoruz" derken iki çok farklı şeyi karıştırmak kolay. Birincisi syntactic (sözdizimsel) doğruluk: çıktı geçerli bir JSON mu, zorunlu alanlar var mı, enum değerleri tanımlı kümeden mi, tipler doğru mu? İkincisi semantic (anlamsal) doğruluk: alanların içindeki değerler gerçekten doğru mu, mantıklı mı, kullanıcının istediğini karşılıyor mu?
Constrained decoding yalnızca birincisini garanti eder. OpenAI ve Anthropic, structured output'un şema-geçerli bir cevap ürettiğini söyler; ama bu, içeriğin doğru olduğu anlamına gelmez. Google Gemini dokümantasyonu bunu açıkça uyarır: sözdizimsel olarak doğru JSON bile uygulama tarafında değer doğrulaması ve şemaya uygun ama anlamsal olarak yanlış değerler için sağlam hata yönetimi gerektirir.
Yani "garanti" şeklin garantisidir, içeriğin değil. Aşağıda iki kapı var: structured output ilk kapıyı (geçerli şekil) her zaman geçirir, ama ikinci kapıyı (doğru değer) geçirmez, onu sizin doğrulamanız gerekir.
geçerli JSON · zorunlu alanlar · enum · tipler
constrained decoding burayı garanti eder
değerler doğru mu, mantıklı mı, hedefi karşılıyor mu
bunu uygulama doğrular
şema-geçerli bir cevap, yanlış bir cevap olabilir
Mekanizma: constrained decoding
Native modların ve açık kaynak motorların çoğunun altında aynı fikir yatar: constrained decoding. Model her adımda kelime dağarcığındaki (vocabulary) bir sonraki token için olasılıklar üretir. Constrained decoding, o anki kısmi çıktıyı şema-geçersiz hale getirecek tüm tokenları maskeler, yani onların olasılığını sıfırlar. Model yalnızca şemayı geçerli tutan tokenlardan birini seçebilir.
OpenAI'nin anlattığı tablo şudur: yalnızca model eğitimi yeterli değildi (kendi değerlendirmelerinde eğitim tek başına %93'te kalmış), bu yüzden eğitimi deterministik constrained decoding ile birleştirdiler. Aynı yaklaşım Willard ve Louf'un Outlines çalışmasında model-bağımsız olarak formüle edilir: yöntem yeniden eğitim gerektirmez, birçok modelin ve onları tüketen sistemlerin arasına oturabilir.
Aşağıda iki üretim hattı yan yana. Üstteki kısıtsız hat bazen şema-dışı bir token (kırmızı) emisyona izin verir ve çıktı kırılır. Alttaki kısıtlı hat aynı geçersiz tokenı maskeler (üzeri çizili), o yüzden hiçbir zaman geçersiz bir token üretmez.
geçersiz token emisyona girer, çıktı kırılır
geçersiz token maskelenir, akış şema-geçerli kalır
Native modlar: aynı isim, farklı sözleşme
Üç büyük sağlayıcı da artık bir JSON Schema arayüzü sunuyor, ama her biri farklı bir operasyonel sözleşmeyle. OpenAI iki yüzey verir: strict tool/function calling ve final cevap için response_format ile json_schema. Çalışması için strict: true gerekir, tüm alanlar zorunlu olmalı (optionality, içinde null olan union tipiyle taklit edilir) ve nesneler additionalProperties: false kullanmalıdır.
Anthropic, final cevabın şekli için "JSON outputs" ile doğrulanmış tool adları/girdileri için "strict tool use"u ayırır; ikisi bağımsız ya da birlikte kullanılabilir. Yapılı çıktıların constrained decoding ile şema-uyumlu olduğunu söyler, ama SDK'ları desteklenmeyen kısıtlamaları dönüştürebilir: minimum, maximum, minLength gibi kısıtları kaldırıp açıklamalara metin olarak gömer ve cevabı orijinal şemaya karşı istemci tarafında doğrular. Bu, naif portability varsayımını zayıflatır.
Google Gemini, structured output'u tahmin edilebilir bir cevap şekli için, function calling'i ise konuşma sırasında aksiyon almak için konumlandırır. Gemini de JSON Schema'nın bir alt kümesini destekler ve dokümantasyon yine de değer doğrulaması yapmanızı önerir. Üç sağlayıcı da JSON Schema'ya yaklaşıyor, ama hiçbiri tamamını desteklemiyor ve dönüşüm davranışları birbirinden farklı.
üçü de JSON Schema'ya yakınsıyor, üçü de farklı bir alt küme ve farklı dönüşüm uyguluyor
Şema özellik boşlukları ve portability
Asıl portability sorunu burada: "JSON Schema desteği" tek tip bir şey değil. OpenAI binlerce nesne özelliğine ve birkaç düzey iç içe yapıya kadar destek verir, ama tüm alanların zorunlu olmasını ve nesnelerin kapalı (additionalProperties: false) olmasını ister. Anthropic SDK'ları numerik ve uzunluk kısıtlarını sessizce kaldırabilir. Gemini yalnızca seçili nesne, string, sayı ve dizi özelliklerini kabul eder.
Sonuç şu: bir sağlayıcıda kusursuz çalışan bir şema, başka bir sağlayıcıda ya reddedilir ya da sessizce budanır. Sessiz budama daha sinsidir, çünkü çıktı geçerli görünür ama beklediğiniz kısıt (örneğin minimum: 0) hiç uygulanmamıştır. Bunu test etmeden fark edemezsiniz.
Pratik çıkış yolu, şemayı en küçük ortak payda etrafında tasarlamaktır: zorunlu alanlar, açıkça kapalı nesneler, sığ yapı, basit tipler ve enumlar, ve sayısal/anlamsal kısıtları dışarıda, kendi doğrulama katmanınızda kontrol etmek. Aşağıda zengin bir şema sağlayıcıların alt kümelerinden geçiyor; her sağlayıcıda farklı bir özellik düşüyor, ortada kalan çekirdek her yerde taşınabilir olan kısımdır.
en küçük ortak payda: çekirdeği şemaya, geri kalanı kendi doğrulamanıza koyun
Açık kaynak motorlar birbirinin yerine geçmez
Sağlayıcı dışına çıkıp kendi constrained decoding'inizi çalıştırdığınızda da iş bitmiyor. Outlines, XGrammar, llama.cpp ve benzeri motorlar aynı garantiyi vaat eder ama grammar sınıfı, tokenizer ele alışı, şema kapsamı, derleme gecikmesi ve token başına maliyet bakımından çok farklıdır. Outlines, guided generation'ı sonlu durum makineleri (finite-state machine) üzerinde state geçişleri olarak formüle eder ve vocabulary indeksleme ile regex için ortalama O(1) maskeleme sağlar.
Koo, Liu ve He'nin automata tabanlı çalışması altta yatan zorluğu netleştirir: subword tokenizerlar belirsizdir ve formal grammar tokenlarıyla hizalı değildir, bu yüzden naif hizalama ya kaliteyi bozar ya da çok sayıda özel durum yaratır. Automata ve finite-state transducer ile bu problemi temizler ve kısıtları kabaca 7000 kat daha hızlı derlediklerini, üstelik provably correct olduğunu bildirirler.
XGrammar başka bir ekseni vurgular: maliyet. CFG tabanlı esnek yapılı üretim pahalı olabilir; XGrammar, vocabulary tokenlarını önceden kontrol edilebilen (context-independent) ve runtime'da yığın durumuna bakılması gereken (context-dependent) tokenlara ayırarak mevcut çözümlere göre 100 kata kadar hızlanma bildirir. Yani "şema desteği" bir commodity değil: motor tasarımı hem güvenilirliği hem gecikmeyi belirler.
göreli derleme/çalışma maliyeti (düşük = daha iyi)
hepsi syntax garanti eder; fark hızda ve şema kapsamında
Structured output bir taşınabilir sözleşme değil, sağlayıcıya bağlı bir davranıştır. Garanti edilen şekildir, içerik değil; ve şeklin bedeli her motorda farklıdır.
Kanıt: JSONSchemaBench
Bu farkları somutlaştıran şey JSONSchemaBench. Çalışma, 10 binden fazla gerçek dünya JSON şemasını resmî JSON Schema Test Suite ile eşleştirir; bu, el seçimi oyuncak şemalardan çok daha sert bir test ortamıdır. Guidance, Outlines, llama.cpp, XGrammar, OpenAI ve Gemini'yi üç eksende karşılaştırır: verimlilik, JSON Schema özellik kapsamı ve çıktı kalitesi.
Üç eksen ayrı ayrı okunmalı, çünkü farklı şeyleri ölçerler. Şema uyumu (çıktı şemayı tutuyor mu), şema özellik kapsamı (framework gerçek dünya şemasını ne kadar destekliyor) ve görev kalitesi (anlamsal olarak doğru mu) bağımsız sinyallerdir. Bir framework yüksek uyum gösterip düşük kapsamda kalabilir, yani az şemayı destekler ama desteklediğini iyi tutar.
Raporlanan başlıca bulgular: constrained decoding bazı ayarlarda üretimi yaklaşık %50 hızlandırabiliyor; framework'ler arasında gerçek şema desteği ciddi biçimde değişiyor (en iyi, en kötünün kabaca iki katı); ve downstream görev doğruluğu raporlanan görevlerde %4'e kadar iyileşiyor. Aşağıda kapsam barları sağlayıcı/yöntem başına doluyor; üstte üç değerlendirme ekseni ayrı kutularda.
çıktı şemayı tutuyor mu
kaç gerçek şema destekleniyor
anlamsal olarak doğru mu
gerçek dünya şema kapsamı (göreli)
≈ 2×
JSONSchemaBench: en iyi framework, en kötünün kabaca iki katı kadar gerçek dünya şemasını destekliyor
Geng et al., JSONSchemaBench, 2025 (arXiv:2501.10868)
~%50
JSONSchemaBench: constrained decoding bazı ayarlarda üretimi yaklaşık yarı yarıya hızlandırabiliyor
Geng et al., JSONSchemaBench, 2025 (arXiv:2501.10868)
Şemanızı her sağlayıcıda test edin
Toparlarsak: structured output, sağlayıcılar arasında taşınabilir bir standart değil. Aynı şema bir yerde tam uyar, başka yerde sessizce budanır, üçüncüsünde reddedilir. "Garanti" şeklin garantisidir; anlamsal doğruluğu hâlâ siz doğrularsınız. Native modlar ve açık kaynak motorlar aynı constrained decoding fikrini paylaşır ama farklı şema alt kümeleri, farklı dönüşümler ve farklı maliyetlerle gelir.
Pratik kural basit: şemayı en küçük ortak payda etrafında tasarlayın, sayısal ve anlamsal kısıtları kendi doğrulama katmanınıza koyun, ve en önemlisi, her hedef sağlayıcıda gerçek şemanızla gerçek bir test koşturun. Portability bedava değildir; ama hangi özelliğin nerede düştüğünü ölçerseniz, taşınabilir olanı seçebilirsiniz.
Şema bir sözleşme gibi görünür, ama sağlayıcı sınırında yeniden müzakere edilir.
Bu yüzden "şema garanti ediliyor" demeden önce sorun: hangi sağlayıcıda, hangi alt kümeyle, hangi dönüşümle ve ne pahasına garanti ediliyor?
Kaynaklar
- S1OpenAI, Introducing Structured Outputs in the API, 2024
- S2OpenAI API docs, Structured model outputs
- S3Anthropic Claude docs, Structured outputs
- S4Google Gemini API docs, Structured outputs
- S5Geng et al., JSONSchemaBench, 2025 (arXiv:2501.10868)
- S6Willard & Louf, Efficient Guided Generation for LLMs, 2023 (arXiv:2307.09702)
- S7Koo, Liu & He, Automata-based constraints for LM decoding, COLM 2024 (arXiv:2407.08103)
- S8Dong et al., XGrammar, MLSys 2025 (arXiv:2411.15100)
