Structured Outputs · 19-c04
26 Haziran 2026 · Üretim okuması
Model bazen kırık çıktı üretir: kapanmayan bir JSON, beklenen alanı eksik bir nesne, schema'ya uymayan bir tip. Sağlam sistem bu anı bir hata değil, bir adım olarak görür. Çıktıyı doğrular, neyin bozuk olduğunu modele geri söyler, düzeltilmiş halini ister ve bu döngü sonsuza kadar sürmesin diye bir bütçeye bağlar.
- Sorun
- kırık ya da anlamsız çıktı
- Döngü
- doğrula · geri besle · onar
- Sınır
- retry budget · terminal durum
- Çıkış
- fallback chain
Bu yazıdaki kavramlar
- repair loop
- reask
- error feedback
- bounded retry
- retry budget
- deterministic fix
- fallback chain
- human review
Structured output, bir dil modelinden serbest metin değil belirli bir şemaya (schema) uyan veri istemektir: alanları, tipleri ve kuralları önceden belli bir JSON. Constrained decoding, yani üretimi şemaya zorlamak, biçim hatalarının büyük kısmını baştan keser. Ama OpenAI'nin kendi dokümantasyonu bile artık durumları sayar: güvenlik gerekçesiyle reddedilmiş (refusal) bir yanıt, token sınırına takılıp yarıda kalmış (incomplete) bir çıktı, ya da tam bir JSON nesnesi olmayan bir gövde. Yani biçim garantisi tek başına yetmez; çağıran tarafın hâlâ bir kurtarma planı olmalı.
Bu yazı o planı parça parça kuruyor: önce repair loop (doğrula, hatayı modele geri besle, düzeltilmiş çıktıyı al), sonra error feedback'in nasıl yazılacağı, bounded retry ve retry budget, deterministik onarım, ve en sonda fallback chain: constrained decoding → repair → daha basit schema → insan. Şemalar buton olmadan kendi başına dönüyor; her biri bir mekanizmayı gösteriyor.
Onarım döngüsü
Repair loop (onarım döngüsü) tek bir fikre dayanır: çıktıyı körlemesine kabul etme, doğrula; geçmezse hatayı modele geri ver ve yeniden iste. Microsoft TypeChat bunu kütüphanenin sözleşmesi olarak anlatır: prompt'u tiplerden kur, modelin yanıtının şemaya uyduğunu doğrula, uymayan çıktıyı daha fazla model etkileşimiyle onar. Onarım ayrı bir manuel iş değil, akışın bir parçasıdır.
Döngünün dört durağı var: generate (üret), validate (parse ve schema kontrolü), feedback (neyin bozuk olduğunu derle), repair (hatayı ekleyerek yeniden iste). Validate geçerse döngü kapanır ve çıktı geçer; geçmezse hata, bir sonraki üretimin girdisine eklenir. Bu, Self-Refine makalesinin biçimlendirdiği üret → geri bildirim → düzelt döngüsünün ta kendisidir.
Aşağıda kırık bir JSON döngüye giriyor: validator onu reddediyor, hata feedback olarak modele dönüyor, model düzeltilmiş halini üretiyor ve bu sefer validator geçiriyor. Akış sürekli dönüyor; her tur nerede olduğunu yumuşakça gösteriyor.
model çıktı üretir
parse + schema kontrolü
neyin bozuk olduğu
hatayı ekleyip yeniden iste
Önce constrained decoding
Onarıma geçmeden önce hatayı en aza indiren katman vardır: constrained decoding, yani üretimi doğrudan şemaya zorlamak. OpenAI'nin Structured Outputs özelliği, verilen bir JSON Schema'ya uyumu garanti ettiğini söyler ve böylece yanlış biçim için doğrulama ve retry ihtiyacını azaltır. LangChain ise native desteği olan modeller için ProviderStrategy, olmayanlar için tool tabanlı ToolStrategy ayrımını yapar. Yani önce mümkünse biçimi baştan garanti et.
Ama "geçerli JSON" ile "şemaya uygun JSON" aynı şey değildir. JSON mode geçerli JSON üretir, alanların tip ve kurallara uyduğunu garanti etmez; doğrulama kütüphaneleri ve retry yine gerekebilir. Üstüne, constrained decoding biçimi çözse de anlamı çözmez: tarih geçerli string olabilir ama mantıksız olabilir, bir kimlik geçerli görünüp veritabanında karşılığı olmayabilir. Bunlar semantic (anlamsal) hatalardır ve onarım döngüsü asıl burada devreye girer.
Aşağıda iki kapı var: structural (biçim) ve semantic (anlam). Constrained decoding soldaki kapıyı büyük ölçüde kapatır; ama sağdaki anlam kapısından geçemeyen çıktılar onarım döngüsüne düşer.
constrained decoding büyük ölçüde keser
biçim doğru, anlam yanlış → repair gerek
Error feedback'i yazmak
Onarım döngüsünün kalbi, modele geri verdiğiniz hatadır. Birçok sistem doğrulama hatasını bir log satırı değil, onarımın asıl sinyali olarak kullanır. Guardrails AI'da OnFailAction.REASK, modelden validator'ı sağlayan bir çıktı ister ve reask prompt'una hangi kalite kriterinin neden başarısız olduğunu üreten bilgiyi ekler. LangChain'de ToolStrategy.handle_errors default hata mesajıyla, özel mesajla ya da seçilmiş exception tipleriyle retry edebilir. Yani error feedback bir prompt hilesi değil, bir uygulama yüzeyidir.
Geri bildirim belirli ve uygulanabilir olmalı. Self-Refine, CRITIC ve Instructor aynı noktaya işaret eder: "tekrar dene" demek zayıftır; "price alanı number olmalı ama string geldi" demek güçlüdür. CRITIC ayrıca dış araç olmadan yapılan saf öz düzeltmenin kazanımı sınırlı kalabileceğini, hatta bozabileceğini söyler. Bu yüzden feedback genelde bir parser, JSON Schema validator ya da Pydantic modelinden gelen somut hatadır, modelin kendi tahmininden değil.
Aşağıda aynı bozuk çıktıya iki tür feedback veriliyor. Sol prompt belirsiz ("geçersiz, düzelt") ve döngü boşa dönüyor; sağ prompt tam olarak hangi alanın hangi kuralı ihlal ettiğini söylüyor ve bir turda düzeliyor.
belirsiz feedback
belirli feedback
Retry'ı sınırlamak
Onarım döngüsünün bariz tehlikesi şudur: model düzeltemezse döngü sonsuza kadar döner, token yakar, gecikmeyi büyütür. Bu yüzden retry her zaman bounded (sınırlı) olmalı. Instructor bunu somut yapar: client'ı bir max_retries ile başlatırsın ve Pydantic doğrulama hatası geri besleme olarak modele döner. Doküman örneğinde max_retries=2, ilk denemenin üstüne iki retry demektir; yani toplam üç deneme.
Sınır sadece bir sayı değil, bir politikadır. Bütçe dolunca ne olacağı önceden tanımlı olmalı: başarısızlığı yukarı taşı, bir exception fırlat ya da fallback'e geç. Sessizce son (muhtemelen hâlâ bozuk) çıktıyı döndürmek en kötü seçenektir, çünkü hatayı gizler. Self-Refine ve CRITIC de aynı yapısal noktayı koyar: döngü ya sabit iterasyon sayısında ya da açık bir durma koşulunda bitmeli.
Aşağıda bir retry budget sayacı her başarısız denemede bir azalıyor. Üç senaryo yan yana: erken başarı bütçeyi korur; geç başarı bütçeyi tüketmeden tutar; ve bütçe sıfıra inerse döngü durur ve fallback'e devreder.
Deterministik onarım
Her hata modele geri dönmeyi gerektirmez. Dar, mekanik biçim hataları için deterministik onarım daha ucuz ve daha güvenli olabilir: kod, modeli beklemeden çıktıyı düzeltir. Guardrails bunu FIX aksiyonu olarak ayırır; programatik olarak çıktıyı onarır. FILTER geçersiz alanları çıkarır, FIX_REASK ise önce deterministik düzeltmeyi dener, hâlâ geçmezse reask'e geçer.
Ayrım net olmalı. Eksik bir kapanış parantezi, sayıyı çevreleyen tırnak, ya da fazladan bir virgül: bunları kodla düzeltmek güvenliyse modele sormaktan iyidir. Ama "bu kimlik veritabanında var mı", "bu tutar policy'ye uyuyor mu" gibi anlamsal ihlaller deterministik olarak düzeltilemez; bunlar reask, retry ya da uygulama/insan fallback'i ister. Yanlış yerde deterministik fix uygulamak sessizce hatalı veri üretir, ki bu kırık JSON'dan daha tehlikelidir.
Aşağıda bir çıktı bir karar noktasından geçiyor. Hata mekanik biçimse sol yola, kodla düzeltilip geçiyor; anlamsalsa sağ yola, modele reask olarak dönüyor. İki dal ayrı renkte ve her ikisi de okunur.
mekanik / biçim hatası
anlamsal hata
yanlış yerde FIX sessizce hatalı veri üretir
Fallback chain
Tek bir strateji her zaman yetmez. Fallback chain, bir aşama başarısız olduğunda kontrolün bir sonrakine devredildiği basamaklı bir zincirdir. Tipik sıra şudur: önce provider-native constrained decoding; o yoksa ya da yetmezse validator güdümlü reask; sonra deterministik fix ya da filter ile kısmi kurtarma; ve hepsi başarısızsa fail closed, yani exception fırlat ya da insan review'a yükselt. Guardrails'in aksiyon taksonomisi (retry, reask, fix, filter, refrain, exception) bu zincirin yapı taşlarını verir.
Zincirin gücü, her basamağın bir öncekinden farklı bir kaldıracı denemesidir: çıktı modunu değiştirmek, repair stratejisini değiştirmek, daha kısıtlı bir prompt denemek ya da başarısızlık politikasını sertleştirmek. LangChain'in provider-native → tool strategy ayrımı bunun bir örneğidir. Önemli olan: her basamak sınırlı, her geçiş bilinçli, ve zincirin sonu daima tanımlı bir terminal durumdur (kabul, reddet ya da insan).
Aşağıda kontrol zincirde aşağı kayıyor. Her basamak sırayla deneniyor; başarısız olan kırmızı yanıp bir sonrakine devrediyor, geçen yeşil yanıp zinciri durduruyor. En altta her zaman bir terminal durum var.
constrained decoding
biçimi baştan zorla
validator reask
hatayı geri besleyip onar
deterministic fix / filter
kısmi kurtarma
human review / exception
fail closed, terminal
Başarısızlık hafızası
Retry'lar stateless olmak zorunda değildir. Reflexion makalesi, ortamdan gelen ikili ya da skaler geri bildirimi sözel bir özet'e çevirip hafızada saklar; böylece sonraki denemeler önceki hatalardan kaçınabilir. Structured output bağlamında bu, her başarısız denemenin kısa bir başarısızlık özeti bırakması demektir: hangi alan, hangi kural, hangi değer kırıldı.
Bu özellikle fallback chain'de değerlidir. Aynı çağrıya basit bir retry atarken hafıza belki gereksizdir; ama zincir model, prompt, parser ya da tool stratejisini değiştirdiğinde, önceki başarısızlıkların kompakt özeti yeni basamağı çok daha isabetli yapar. CRITIC de aynı yönde: dış doğrulamadan gelen kritikleri taşımak, körlemesine yeniden denemekten iyidir. Yani retry'ları sıfırdan başlatma; ne kırıldığını bir sonrakine taşı.
Aşağıda üç deneme sırayla başarısız oluyor ama her biri hafızaya bir not bırakıyor. Sağdaki hafıza dolarken sonraki deneme bu notları girdi alıyor; sonunda biriken bağlam başarıyı getiriyor.
deneme 1
price: tip hatası
deneme 2
date: format hatası
deneme 3
iki notu da kullandı
hafıza
biriken notlar sonraki denemeye girer
Kurtarma katmanları
Tek bir mekanizma kırık çıktıyı tek başına kurtarmaz. Sağlam bir tasarım katman katmandır: en altta biçimi baştan garanti eden constrained decoding; üstünde doğrulama ve belirli error feedback'le çalışan repair loop; onu çerçeveleyen bir retry budget; gerektiğinde deterministik fix; ve zincirin sonunda her zaman tanımlı bir terminal durum (kabul, kısmi kurtarma ya da insan).
Bu katmanları birlikte okumak, "model neden bozuk çıktı verdi?" sorusunu "bozuk çıktı geldiğinde sistem ne yapıyor, kaç turda, ne pahasına, ve nerede duruyor?" sorusuna çevirir. İyi bir kurtarma tasarımı modelin mükemmel olmasını beklemez; bozuk çıktıyı öngörür, sınırlar ve daima bir terminal duruma bağlar.
Sağlam structured output, modelin hiç hata yapmadığı değil; hata yaptığında sistemin onu sınırlı, gözlemlenebilir ve terminal bir biçimde kurtardığı sistemdir.
Bu yüzden "model bozuk çıktı verdi" demeden önce sorun: hangi katmanda yakalıyorum, kaç retry'la sınırlıyorum, error feedback'im ne kadar belirli, ve zincirin sonu nerede duruyor?
Kaynaklar
- S1OpenAI, Structured model outputs (API guide)
- S2LangChain, Structured output (docs)
- S3Guardrails AI, Error and Remediation (docs)
- S4Microsoft, TypeChat (README)
- S5Instructor, Retry Mechanisms (docs)
- S6Madaan et al., Self-Refine, 2023 (arXiv:2303.17651)
- S7Gou et al., CRITIC, ICLR 2024 (arXiv:2305.11738)
- S8Shinn et al., Reflexion, NeurIPS 2023 (OpenReview)
