Structured Outputs · 19-c01
26 Haziran 2026 · Format okuması
Bir LLM'den "sadece JSON dön" demek, çıktıyı bir makinenin güvenle okuyabileceği bir sözleşmeye bağlamanın ilk adımıdır. Ama format seçimi tek bir kalıp değildir: JSON, XML, YAML, Markdown tablo ve bunların üstüne oturan typed object, her biri okunabilirlik, parse edilebilirlik, token maliyeti ve nesting arasında farklı bir denge kurar.
- Soru
- hangi format?
- Eksen
- okunur · parse · token
- Varsayılan
- JSON + schema
- Tuzak
- geçerli ≠ doğru şekil
Bu yazıdaki kavramlar
- JSON
- JSON Schema
- XML
- YAML
- Markdown tablo
- typed object
- Pydantic / Zod
- constrained decoding
Bir uygulama LLM çıktısını kod olarak kullanacaksa, prose değil parse edilebilir bir değer ister: bir alan adı, bir sayı, bir liste, bir enum. Serbest metin (free-form generation) insanlar için harikadır ama bir fonksiyona girdi olamaz. JSONSchemaBench'in dediği gibi, makine tarafından tüketilen model çıktıları önceden tanımlı formatlara ve kısıtlara sıkı uyum gerektirir, oysa olasılıksal üretim bunu kendiliğinden garanti etmez.
Bu yazıda her formatı tek tek geziyorum ve seçimleri ilgili standarda bağlıyorum: JSON (RFC 8259), JSON Schema 2020-12, XML 1.0, YAML 1.2.2 ve GitHub Flavored Markdown. Şemalar kendi başına dönerek aynı kaydın her formatta nasıl göründüğünü, neyin pahalı neyin kırılgan olduğunu gösteriyor.
Neden yapı gerekir?
Structured output, modelin cevabını önceden belirlenmiş bir biçime sokmaktır: serbest cümleler yerine, alanları ve tipleri belli bir veri yapısı. Bir uygulama için bu zorunluluktur, çünkü kod "saat üçte toplantı var" cümlesini değil, { "saat": 15 } gibi okunabilir bir alanı bekler. Yapısız metin, downstream tarafta tahmin ve regex demektir; ikisi de kırılır.
Sadece formatı söylemek de yetmez. OpenAI, structured outputs özelliğini tam olarak bunun için tanıtır: model cevabının verilen bir JSON Schema'ya uymasını sağlar, böylece eksik zorunlu alan ya da geçersiz enum gibi hata modları azalır. Buradaki incelik şu: geçerli JSON üretmek ile doğru şekle uyan JSON üretmek aynı şey değildir. İlki gramer, ikincisi sözleşme meselesidir.
Soldaki serbest cümle insan için anlaşılır ama makine için belirsizdir. Sağdaki yapılandırılmış kayıt aynı bilgiyi alanlara ayırır; her alanın bir adı, bir tipi ve net bir sınırı vardır.
"Ali ile yarın saat 3'te 30 dakikalık bir görüşme ayarla."
kod alanları okur; cümleyi tahmin etmek zorunda kalmaz
JSON: neden varsayılan
JSON (RFC 8259) hafif, metin tabanlı ve dilden bağımsız bir veri değişim formatıdır. Yalnızca dört primitif (string, number, boolean, null) artı iki kapsayıcı (array ve object) tanır. Bu küçük gramer onu varsayılan yapan şeydir: her dilde olgun bir parser var, kuralları az ve nettir, kapalı ekosistemler dışında UTF-8 kullanımı standartla güvenceye alınmıştır.
Birkaç ince nokta var. JSON object'i sırasız bir isim/değer koleksiyonudur; eğer sıra anlamlıysa object key sırasına değil, array'e güvenmek gerekir. Aynı şekilde RFC, birlikte çalışabilirlik için object isimlerinin benzersiz olmasını ister; tekrarlı key parser'lar arasında öngörülemez davranır. Bunlar "JSON kırılgan" demek değil; aksine, sınırları net olduğu için güvenle otomatikleştirilebilir demektir.
JSON'un tek eksiği, kendi başına domain seviyesinde doğrulama tanımlamamasıdır: hangi alan zorunlu, hangi değer enum, sayı hangi aralıkta? RFC bunu kapsamaz. İşte JSON Schema tam burada devreye girer ve bu yazının ilerleyen bölümünün konusudur.
4 + 2
RFC 8259: 4 primitif tip (string, number, boolean, null) + 2 yapı (array, object). Küçük gramer = her dilde sağlam parser.
Bray (ed.), RFC 8259: The JSON Data Interchange Format, IETF 2017
Aynı kayıt, dört format
Format tartışması soyut kalmasın diye aynı küçük kaydı dört biçimde yazalım: bir kullanıcı, bir rol ve iki etiket. JSON, XML, YAML ve Markdown tablo aynı bilgiyi taşır ama farklı bir bedelle. Aşağıdaki diyagram her birinin kabaca kaç token harcadığını gösterir; çubuklar sırayla dolar.
İki şey öne çıkar. XML, her değeri açıcı ve kapatıcı tag arasına aldığı için en pahalı olandır; <tag>…</tag> yapısı bilgiyi iki kez tekrar eder. YAML, indentation'a dayandığı için süslü parantezleri atar ve görece kompakttır, ama bu okunabilirlik whitespace hassasiyetiyle gelir. Markdown tablo insan gözü için en derli toplu görünür, fakat bir sonraki bölümlerde göreceğimiz gibi makine sözleşmesi olarak en zayıfıdır.
Token maliyeti boş bir kaygı değildir: girdi ve çıktı token üzerinden ödeme yaparsınız ve nesting derinleştikçe tekrar eden sözdizimi (özellikle XML tag'leri) faturayı büyütür. Aynı veri için en ucuz güvenli format genelde JSON'dur.
çubuklar kabaca göreli token sayısını gösterir; tekrar eden sözdizimi maliyeti artırır
XML: tag ile sınırlamak
XML (W3C XML 1.0) bir markup dilidir: belge element, attribute, character data ve comment gibi parçalardan oluşur. Tam olarak bir kök element ister ve doğru nesting zorunludur; bir conforming processor için well-formedness ihlali ölümcül hatadır. Bu katılık bir özelliktir: yapı bozulursa sessizce yanlış veri üretmez, durur.
XML'in ayırt edici gücü, üretilen bir metin parçasının (span) etrafına net sınır koymaktır. Bir modelden uzun bir alıntıyı ya da bir kod bloğunu tag içinde döndürmesini istemek, JSON string kaçış kurallarıyla uğraşmaktan kolay olabilir; <answer>…</answer> gibi tag'ler doğal delimiter işlevi görür. Bu yüzden XML, document yapısı ya da mixed content (metin ile element'in iç içe geçtiği içerik) önemli olduğunda anlamlıdır.
Bedeli iki yerde: token ve doğrulama. Her değer iki tag tekrar ettiği için XML, basit typed record'lar için JSON ve YAML'dan ağırdır. Ayrıca XML, well-formedness (sözdizimi doğru mu) ile validity (DTD gibi ek kısıtları sağlıyor mu) kavramlarını ayırır; yani sözdizimi doğru olan XML hâlâ beklenen şekle uymuyor olabilir. Downstream tarafta bir parser ve bir doğrulama sözleşmesi yine gerekir.
well-formed olmak, beklenen şekle uymak demek değildir
YAML: insana okunur, makineye hassas
YAML (YAML 1.2.2) insan dostu, diller arası bir veri serileştirme dilidir; veriyi mapping, sequence ve scalar ile temsil eder. Block style scope'u indentation ile belirler, flow style ise köşeli ve süslü parantez gibi açık göstergeler kullanır. YAML 1.2, JSON'un katı bir superset'i olmayı hedefler; yani çoğu JSON aynı zamanda geçerli YAML'dır.
YAML'ı çekici kılan şey, onu hassas kılan şeyle aynıdır. Comment'ler, block scalar'lar, anchor/alias ve birden çok scalar style yazımı kolaylaştırır ama parse etmeyi karmaşıklaştırır. Boşluğa duyarlı indentation tek bir kaçık girinti ile yapıyı bozabilir; implicit typing yüzünden yes bir boolean'a, bir posta kodu sayıya dönüşebilir. Mapping key sırası temsil modelinin parçası değildir, sıra önemliyse spec sequence'e yönlendirir.
Sonuç: YAML, insanın elle düzenlediği config ve prompt için mükemmeldir. Sadece makinenin tükettiği bir LLM pipeline'ında ise JSON'a göre daha kırılgandır, çünkü whitespace, implicit typing, key tekrarı ve scalar folding'in hepsi parser farkında bir doğrulama ister.
implicit typing: yes → boolean, 0613 → sayı, dikkat
Markdown tablo: sunum, sözleşme değil
Markdown tablo insana bakan özetler için harikadır, ama makine arayüzü olarak en zayıf seçenektir. Önce bir teknik gerçek: tablo sözdizimi temel CommonMark'ın parçası değil, GitHub Flavored Markdown'ın bir extension'ıdır. Yani "Markdown" diyen her parser tabloyu aynı şekilde, hatta hiç, anlamak zorunda değildir.
GFM tablosunun bir header satırı, bir delimiter satırı (tirelerle, hizalama için opsiyonel iki nokta) ve opsiyonel veri satırları vardır; hücreler pipe ile ayrılır. Spec'in affedici kuralları tam da güveni zorlaştırır: header ile delimiter satırının hücre sayısı eşleşmeli, body satırında hücre eksikse boş hücre eklenir, fazlaysa atılır. Yani sessizce veri kaybedebilir ya da uydurabilirsiniz.
Buna pipe karakterini kaçırma, hizalama sözdizimi ve renderer'a özgü davranışlar eklenince, tablodan sağlam bir şekilde veri okumak zorlaşır. Doğru zihniyet şudur: Markdown tabloyu sunum katmanı say, sözleşme katmanı değil. Makineye giden veri için JSON'a çevir, tabloyu sadece insana göster.
eksik hücre → sessizce boş eklenir
fazla hücre → sessizce atılır
Typed object: wire format + schema
Typed object, ayrı bir format değildir; bir wire format'ın (genelde JSON) üstüne oturan schema + validation katmanıdır. Python'da Pydantic, TypeScript'te Zod ile uygulamanızın tip tanımını yazarsınız; SDK bu tipi bir JSON Schema'ya çevirir, modele verir, dönen JSON'u parse eder ve sizin tipinize doğrulayarak teslim eder. OpenAI'nin SDK yardımcıları tam olarak bunu yapar: Pydantic ya da Zod modelini schema'ya map eder.
Bunu mümkün kılan JSON Schema 2020-12'dir: type, enum, required, object property sayıları, array item kısıtları ve string kuralları gibi anahtarlarla bir instance'ın nasıl görünmesi gerektiğini söyler. Bir uyarı: format anahtarı nüanslıdır; spec annotation ile assertion davranışını ayırır, implementasyonlar değişir, yani her format her zaman zorlanmaz. Çerçevenin desteklediği schema alt kümesini bilmek uygulamanın işidir.
Aşağıdaki hat dört adımı gösterir: model JSON üretir, parser ham metni veri yapısına çevirir, schema bu yapıyı kontrol eder, geçen kayıt typed object olur. Önemli olan şu: schema bir prompt geleneği değil, bir doğrulama dilidir; ve geçen değer artık uygulamanın tip sistemiyle aynı dili konuşur.
Typed object drift'i azaltır ama her şeyi çözmez. OpenAI dokümanı, structured output'un refusal, max output token nedeniyle yarım kalan cevap, content filter, stop token ve network hatası gibi durumların yine uygulama tarafından ele alınmasını gerektirdiğini açıkça söyler. Schema, pipeline seviyesindeki hata yönetiminin yerine geçmez.
JSON
model üretir
parse
metin → yapı
schema
şekli doğrula
typed object
uygulama tipi
şekil tutmazsa: reddet, tekrar dene ya da onar
schema bir doğrulama dilidir, sadece prompt değil
Hangi formatı seçmeli?
Pratik kural sade: makine tüketecekse JSON ile başla, üstüne JSON Schema koy, mümkünse provider'ın schema'ya uyumu garanti eden structured output yolunu kullan. JSON, küçük gramer, evrensel parser ve doğrudan schema desteğiyle typed object çıktısı için en güvenli varsayılandır.
İstisnalar nettir. Uzun metin span'lerini ya da mixed content'i sınırlamak gerekiyorsa XML tag'leri işe yarar. İnsanın elle düzenleyeceği config ya da prompt için YAML okunabilirlik kazandırır. Sadece insana gösterilecek bir özet tablo için Markdown idealdir; ama o tabloyu makineye veri olarak verme, önce JSON'a çevir. Markdown tabloyu her zaman sunum say.
Son olarak, yüksek güvenilirlik isteyen sistemlerde prompt'a "JSON dön" yazmak yeterli değildir. JSONSchemaBench, LLM-only yaklaşımların deneylerinde en düşük uyum oranına sahip olduğunu raporlar. Guided generation çalışmalarının gösterdiği gibi, gerçek garanti constrained decoding ile gelir: model her adımda yalnızca hedef dile uyan token'lara izin verecek şekilde kısıtlanır. Yani format seçimi başlangıçtır; sözleşmeyi schema, parser, doğrulama ve gerektiğinde retry/repair akışı kapatır.
Format, çıktıyı bir sözleşmeye bağlamanın yalnızca ilk yarısıdır; ikinci yarı schema, parser ve doğrulamadır.
Bu yüzden "hangi format?" sorusundan önce sorun: bu çıktıyı kim okuyacak, hangi şekle uymalı ve uymadığında ne olacak?
Kaynaklar
- S1OpenAI, Structured model outputs (API docs)
- S2Bray (ed.), RFC 8259: The JSON Data Interchange Format, IETF 2017
- S3Wright et al., JSON Schema 2020-12 (core + validation)
- S4YAML Language Development Team, YAML 1.2.2 specification
- S5Bray et al., Extensible Markup Language (XML) 1.0 (5th ed.), W3C 2008
- S6GitHub, GitHub Flavored Markdown Spec (0.29-gfm)
- S7Geng et al., JSONSchemaBench, 2025 (arXiv:2501.10868)
- S8Willard & Louf, Efficient Guided Generation for LLMs, 2023 (arXiv:2307.09702)
