İçeriğe geç
18/3030 bölümden 18. bölüm

Tool Calling ve Yapılandırılmış Çıktılar: Bozulmayan Sözleşme

24 çağrı, bozuk JSON yok, iki kullanılabilir tarih. Sonra daha iyi açıklamalı aynı endpoint ve bir şemanın düzeltemedikleri.

Bu sayfada

Bir modele uçuş arama aracı verip Madrid’den Berlin’e bir uçuş bulmasını iste. Gelen yanıt şu olur:

TEXT
<tool_call>
{"name": "search_flights",
 "arguments": {"from": "Madrid", "to": "Berlin", "date": "3rd October 2026"}}
</tool_call>

JSON geçerli. Araç adı doğru. Zorunlu her alan var. Ve çağrı işe yaramaz: hiçbir uçuş API’si havaalanı kodu beklediği yerde "Madrid", tarih beklediği yerde de "3rd October 2026" kabul etmez.

Bu boşluk — sözdizimsel olarak kusursuz, anlamsal olarak kullanılamaz — bu bölümün konusu. Baştan netleştirilmesi gereken ilk şey de bunun bir JSON problemi olmadığı. Bu araçla yapılan yirmi dört istekte model 24 geçerli araç çağrısı ve sıfır bozuk JSON üretti. Herkesin debug ettiği kısımda bir kez bile başarısız olmadı.

Mekaniklere geçmeden önce, en çok kafa karışıklığını önleyen cümle: bir araç çağrısı bir eylem değil, bir istektir.

Model, search_flights şu argümanlarla çağrılsın istiyorum diyen yapılandırılmış bir mesaj üretir. Sonra durur. Kodun bu mesajı alır, onu yerine getirip getirmeyeceğine karar verir, neyi çağıracaksa çağırır ve sonucu başka bir mesaj olarak geri gönderir. Model veritabanına hiç dokunmadı, HTTP isteği hiç yapmadı, kimlik bilgilerine hiç sahip olmadı.

30. Bölüm’deki agent güvenliğine dair her şey bu ayrımdan çıkar; 23. Bölüm’deki agent tasarımına dair her şey de öyle: model önerir, kodun karar verir; her garantinin yaşadığı yer de koddur.

Dolayısıyla, sözlüğünden arındırıldığında bir araç iki şeydir:

Bir şema. Bir fonksiyonu tanımlayan JSON Schema: adı, ne yaptığı ve hangi argümanları hangi türler ve kısıtlarla aldığı. prompt içine giren şey budur ve modelin gördüğü tek şey de budur.

Bir endpoint. Kodunda bu argümanları alan ve bir şey döndüren bir fonksiyon. Model onu hiç görmez, hangi dilde olduğunu asla bilmez ve bir veritabanı sorgusunu sabit kodlanmış bir string’den ayırt edemez.

Araç tanımları prompt içine, modelin üzerinde eğitildiği hangi formatsa ona serileştirilmiş şekilde girer. Her bir çağrıda token maliyeti yaratırlar — bu bölümün ilerleyen kısmında bunun sayısal karşılığı geri dönecek.

Model metin yerine bir çağrıyla yanıt verir

Bölüme bağlantı: Model metin yerine bir çağrıyla yanıt verir

Düz yazı yerine yanıt yapılandırılmış bir istek içerir ve API bunu belirten bir bitiş nedeni bildirir. Bu neden önemlidir: kodunun kullanıcıya bir yanıt göstermek yerine bir araç çalıştırması gerektiğini buradan bilir.

Kodun onu çalıştırır — ya da reddeder

Bölüme bağlantı: Kodun onu çalıştırır — ya da reddeder

Bu adımda model yoktur. Argümanları şemaya göre doğrula, bu çağrıcının bunu yapmaya yetkili olup olmadığına karar ver ve yürüt.

Sonuç, konuşmada ona ayrılmış bir rolde başka bir tur olur. Model onu diğer her context gibi okur.

Model yanıt verir ya da başka bir araç ister

Bölüme bağlantı: Model yanıt verir ya da başka bir araç ister

Bu, 23. Bölüm’deki döngüdür ve tek bir isteğin neden bir düzine gidiş gelişe dönüşebildiğini açıklar.

Bunların hiçbiri kendiliğinden beliren bir şey değildir. 11. Bölüm’de gösterildiği gibi, tool calling bir eğitilmiş davranıştır:1 post-training sırasında model tam olarak bu biçime sahip binlerce konuşma gördü. Formatın modele özgü olmasının, benzer boyuttaki modeller arasında güvenilirliğin bu kadar değişmesinin ve bir modelin hiç görmediği bir aracı çağırabilmesinin nedeni budur — biçim eğitimden gelir, belirli araç ise senin prompt’undan.

Kötü bir şemanın maliyeti, ölçülmüş haliyle

Bölüme bağlantı: Kötü bir şemanın maliyeti, ölçülmüş haliyle

Aracı çoğu kişinin ilk yazdığı haliyle görelim. Dikkat et: bunda yanlış bir şey yok; sadece zayıf:

tools/badFlights.tsTS
{
  name: "search_flights",
  description: "Search for flights.",
  parameters: {
    type: "object",
    properties: {
      from: { type: "string", description: "Airport." },   
      to:   { type: "string", description: "Airport." },   
      date: { type: "string", description: "The date." },  
    },
    required: ["from", "to", "date"],
  },
}

Yirmi dört istek, altı şehir çifti ve tarih ifade etmenin dört yolu ("gelecek ayın 3’ü", "önümüzdeki cuma", "15 Aralık", "yarın"), sonuçlar tekrar üretilebilsin diye greedy decoding:

araç çağrıldıbozuk JSONtarih ISO formatındahavaalanları IATA olarakher şey doğru
yukarıdaki şema24/2402/244/241/24

Son üç sütundan önce ilk iki sütunu oku. Model her seferinde doğru aracı çağırıyor ve her seferinde düzgün biçimli JSON üretiyor. Hata tamamen değerlerde ve değerler kullanılamaz durumda: "Madrid" yerine MAD, "3rd October 2026" yerine 2026-10-03.

Bunun üzerinde durmaya değer, çünkü bir şey bozulduğunda nereye bakacağını belirler. İçgüdü, retry yapan bir JSON parser eklemek ya da modelden geçerli JSON’u daha kesin bir dille istemektir. İkisi de burada olan hiçbir şeyi çözmez.

Aynı endpoint. Arkasında aynı kod. Aynı model, aynı prompts, aynı decoding. Değişen tek şey şemadaki metin:

tools/goodFlights.tsTS
{
  name: "search_flights",
  description: "Search scheduled flights between two airports on a given day.",
  parameters: {
    type: "object",
    properties: {
      from: {
        type: "string",
        description: "Departure airport as a three-letter IATA code, e.g. MAD for Madrid. Never a city name.",   
        pattern: "^[A-Z]{3}$",
      },
      to: { /* same */ },
      date: {
        type: "string",
        description: "Departure date as an ISO 8601 calendar date, YYYY-MM-DD. Resolve relative dates against today before calling.",   
        format: "date",
        pattern: "^\\d{4}-\\d{2}-\\d{2}$",
      },
    },
    required: ["from", "to", "date"],
  },
}
tarih FORMATItarih DEĞERİhavaalanı FORMATIhavaalanı DEĞERİ
zayıf şema2/241/244/244/24
açıklanmış şema24/2412/2416/248/24

Tarih formatı 24’te 2’den 24’te 24’e çıkar. Kusursuz; sadece metin değişikliğiyle, koda dokunmadan ve retry mantığı eklemeden. Bu bölümden tek bir operasyonel alışkanlık çıkaracaksan o da şudur: bir araç yanlış çağrıldığında düzeltme neredeyse her zaman açıklamadadır ve sistemdeki en ucuz düzeltme budur.

Şimdi daha önemli olan ikinci sütunu oku.

Şema biçimi kısıtlar. Bilgi sağlayamaz.

Bölüme bağlantı: Şema biçimi kısıtlar. Bilgi sağlayamaz.

Tarih 24 denemenin 24’ünde ISO formatındadır. Doğru gün ise 24 denemenin 12’sindedir.

Yani çağrıların yarısı artık kusursuz biçimlendirilmiş ama yanlış bir tarih taşır. Açıklama modele hangi biçimi üretmesi gerektiğini söyledi, model de onu hatasız üretti — ama "önümüzdeki cuma" ifadesini 2026-09-11’e çevirmek bugünün tarihini bilmeyi ve takvim aritmetiği yapmayı gerektirir; hiçbir açıklama bunu sağlayamaz. Havaalanlarında da aynı hikâye var: format 4’ten 16’ya çıktı, ama değer yalnızca 4’ten 8’e çıktı; çünkü MAD yazmak Madrid’in havaalanının MAD olduğunu bilmeyi gerektirir.

Bu ayrım, bölümün taşıyıcı fikridir:

Şema, biçime dair bir sözleşmedir. Model çıktısını parse edilebilir, türlendirilmiş ve tutarlı hale getirebilir. Onu doğru yapamaz; iyi bir şemadan sonra hayatta kalan her hata modu format hatası değil, bilgi hatasıdır.

İkisi farklı düzeltmeler ister; bunları karıştırmak haftalar kaybettirir. Format hataları açıklamada ya da aşağıdaki constrained decoding ile düzeltilir. Bilgi hataları ise bilgiyi prompt içine koyarak düzeltilir — system message içindeki güncel tarih, modelin önce çağıracağı ikinci araç olarak bir havaalanı araması, küme sayılacak kadar küçükse şemada bir enum. Üçünün ortak noktasına dikkat et: problemi modelin belleğinden çıkarıp girdisine taşırlar; 24. Bölüm’ün tamamı da budur.

Yapılandırılmış çıktılar ve "constrained decoding" aslında nedir

Bölüme bağlantı: Yapılandırılmış çıktılar ve "constrained decoding" aslında nedir

Yukarıdaki her şey hâlâ modelin doğru biçimi üretmeyi seçmesine dayanır. Daha güçlü bir garanti vardır ve 17. Bölüm’ün en iyi getirisi odur.

Üretimin nasıl çalıştığını hatırla: model her adımda sözlükteki her token için bir logit üretir ve sampler birini seçer. Constrained decoding araya bir adım ekler. JSON Schema’ndan türetilmiş bir gramer verildiğinde, sırada yasal olarak hangi tokens gelebilir hesaplar, diğerlerinin logits değerlerini negatif sonsuza ayarlar ve sampler’ın geriye kalandan seçim yapmasına izin verir.

Şema sıradaki şeyin { olması gerektiğini söylüyorsa, { olmayan her token’ın olasılığı sıfırdır. "Pek olası değil" değil: sıfır. Model geçersiz JSON üretemez, çünkü geçersiz tokens sampling’den önce dağılımdan kaldırılmıştır.

"Structured outputs", "JSON mode" ve "guided generation" ifadelerinin altında yatan şey budur; iki özelliklerini de açıklar. Gramerin ifade edebildiği her şey için garanti tamdır — türler, zorunlu alanlar, enums, iç içe yapılar — çünkü nazikçe istenmek yerine mekanik olarak uygulanır. Ve içerik hakkında hiçbir şey söylemez: bir gramer "date" alanını tarih desenine uyan bir string olmaya zorlayabilir, ama doğru gün olmaya zorlayamaz. Bu da önceki bölümdeki aynı duvara, öteki taraftan varmak demektir.

İki pratik not. Bedava değildir: mask her adımda hesaplanmalıdır ve karmaşık gramerler ölçülebilir latency maliyeti yaratır. Ayrıca modelin ne yaptığını değiştirir — tercih ettiği token’dan uzaklaştırılan bir model kusursuz yapı üretirken daha kötü içerik üretebilir. Bu yüzden basit biçimler için "nazikçe iste ve doğrula" hâlâ makul bir varsayılan; constrained decoding ise biçim karmaşık ya da tüketici katı olduğunda maliyetini hak eder.

14. Bölüm, timeout sonrası yapılan retry’nin tek yanıt için iki generation faturalandırdığını ölçmüştü. Araçlarla aynı hata daha kötüleşir, çünkü bir araç bir şey yapabilir.

Kodun charge_card çağırır, timeout olur ve retry yaparsa iki ücret doğar. Model bunların hiçbirinden haberdar değildir; tek bir araç sonucu görür. Çözüm, herhangi bir dağıtık sistemdekiyle aynıdır ve modelin problemi değildir: çağrıya bir anahtar vererek operasyonu idempotent yap; böylece ikinci yürütme birincisini tanır ve işi yeniden yapmak yerine onun sonucunu döndürür.

Buradan çıkan tasarım kuralını açıkça söylemek gerekir. Araç kataloğunda okumaları yazmalardan ayır. Okuma serbestçe retry edilebilir, paralel çalıştırılabilir ve cache’lenebilir. Yazma bunu yapamaz; bir anahtar, izin kontrolü ve — kullanıcı gerçekleşmeden önce bilmek isteyecekse — istek ile eylem arasına bir insan koyan onay adımı taşımalıdır. Bu onay adımı bir nezaket değildir: prompt injection ile gerçek bir sonuç arasında duran az sayıdaki şeyden biridir — ve 30. Bölüm’ün ölçtüğü gibi, aralarındaki en zayıf halkadır.

Yaygın anlatı, çok sayıda araç yüklemenin modelin kötü seçim yapmasına yol açtığını söyler. Bunu tekrarlamak yerine ölçmek gerekir. Yani: aynı yirmi dört istek, uçuş aracı artı giderek büyüyen başka araçlar kümesiyle — kasten karıştırılabilir üç araç dahil (tren tarifeleri, feribot geçişleri, otobüs güzergâhları).

yüklü araçlarprompt tokenssearch_flights seçtitarih ISO formatında
135324/2424/24
573024/2424/24
101,19321/2421/24
202,11924/2424/24

Seçim bozulmadı. Yirmi araçla, bunlardan üçü makul biçimde karıştırılabilirken, yarım milyar parametreli bir model yirmi dört denemenin yirmi dördünde doğru aracı seçti. Ondaki düşüş, farklı bir araç adı veren üç çağrıdan ibaret ve yirmiye çıkıldığında sürmüyor.

Bu negatif bir sonuçtur ve öyle raporlanmalıdır: bu görevde, bu araçlarla, "çok fazla araç" problem değildi. Monoton biçimde ve altı kat artan şey prompt oldu: 353 token’dan 2.119’a; konuşmadaki her istekte, sonsuza kadar, herhangi bir araç kullanılsın ya da kullanılmasın ödenen bir maliyet.

Dolayısıyla yaygın anlatının dürüst versiyonu maliyet ve context hakkındadır, doğruluk hakkında değil. Yirmi araç her mesaja kalıcı bir vergidir ve 16. Bölüm kalıcı bir prefix’in kırk tur boyunca faturaya ne yaptığını zaten göstermişti. İnsanlar çok sayıda aracın kaliteye zarar verdiğini bildirdiğinde mekanizma genellikle tanımların önemli olan context’i dışarı itmesidir — bu, 18. Bölüm kostümü giymiş bir 24. Bölüm problemidir. Gerçekten birbirine çok yakın kopya olan araçlar da gerçek bir problemdir ve bunların çözümü daha az araç değil, daha iyi açıklamalar ve namespaces kullanmaktır: sistemle prefix’le (crm.search_customer, billing.search_customer), böylece iki ekibin iki kataloğu birleştiğinde çakışmaz ve modelin ayrım yapabileceği bir şey olur.

Üç tür araç ve bir sonraki kısmı açan tür

Bölüme bağlantı: Üç tür araç ve bir sonraki kısmı açan tür

Araçları dünyaya ne yaptıklarına göre ayırmak faydalıdır, çünkü her biri için mühendislik farklıdır.

Veri araçları okur: arar, getirir, sorgular. Retry edilebilir, paralelleştirilebilir, cache’lenebilir. Faydalı hiçbir şey döndürmeyerek başarısız olurlar; temel riskleri ise güvenilmeyen metni context içine taşımalarıdır — 30. Bölüm’ün tüm saldırı yüzeyi budur.

Eylem araçları yazar: gönderir, oluşturur, ücretlendirir, siler. Anahtar olmadan retry edilemez, güvenli biçimde paralelleştirilemez ve onay akışlarının var olma sebebidir.

Orkestrasyon araçları başka modelleri çağırır. Uygulaması başka bir agent olan bir araç: kendi prompt’u, kendi araçları ve kendi döngüsüyle. Çağıran modele ise diğer ikisiyle tamamen aynı görünür; çünkü gördüğü tek şey bir şema ve bir endpoint’tir.

Bu üçüncü tür bir merak konusu değildir. 25. Bölüm’deki agent-as-a-tool yarısının arkasındaki mekanizmadır — diğer topoloji olan devir konuşmayı verir ve asla geri almaz — ve tam olarak bu bölümdeki interface yeterince dar olduğu için çalışır: arkasına bütün bir agent sığar.

Artık bir şeyler isteyebilen bir modelin ve bu istemeyi parse edilebilir kılan bir sözleşmen var. Sahip olmadığın şey ise prompt’una sığanların ötesinde hakkında istekte bulunabileceği herhangi bir şey.

Production’daki en yaygın araç, açık ara farkla, modelin training sırasında hiç görmediği bir metin gövdesi üzerinde aramadır: dokümantasyonun, ticket’ların, sözleşmelerin. Bu çözülmüş bir problem gibi duyulur — embedding’e çevir, en yakın komşuları bul, içeri yapıştır — ama çözülmemiş kısımlar yanıtın güvenilir olup olmayacağını belirleyenlerdir: metnin embedding’e dönüştürülmeden önce nasıl parçalara ayrıldığı, hangi similarity threshold’un bilmiyorum demek için yeterince düşük olduğu ve bir okurun kontrol edebilmesi için bir iddiaya citation’ın nasıl bağlandığı.

19. Bölüm retrieval’dır; yanlış yanıtın merak konusu olmaktan çıkıp sorumluluk olmaya başladığı bölümdür.


Bu bölümdeki ölçümler Qwen/Qwen2.5-0.5B-Instruct üzerinden, greedy decoding ile, altı şehir çifti ve dört tarih ifadesini çaprazlayan 24 üretilmiş istekle, araç tanımları için modelin kendi chat template’i kullanılarak elde edildi. Aynen tekrar üretilebilirler ve küçük bir modeldir: format/değer ayrımını güncel modellerin ne yaptığını gösteren bir benchmark olarak değil, mekanizmanın bir gösterimi olarak oku. Frontier model "önümüzdeki cuma"yı çok daha sık doğru çözer — ama yine de bir şema tarafından buna zorlanamaz; genelleşen kısım budur.

Yukarıda kullanılan JSON Schema sözlüğü (type, properties, required, pattern, format, enum), provider dokümantasyonunun adını verdiği JSON Schema draft’ında belirtilir; faydalı alt küme küçüktür ve provider’lar arasında aynıdır. Var olan farklar — hangi keywords’ün yalnızca modele geçirilmek yerine constrained decoding tarafından gerçekten enforced edildiği — varsayılmak yerine provider’ın structured-output guide’ında okunmaya değerdir.

Bir teknik olarak constrained decoding için guidance tarzı kütüphaneler ve outlines projesi, gramerden logit-mask’e giden inşayı 17. Bölüm’ün sampler’ıyla doğrudan eşleşecek şekilde belgeler. Gidiş gelişin kendisi içinse en açık spesifikasyon bir tutorial değil, protokoldür: 26. Bölüm onu satır satır okur.

  1. Ouyang, L. ve diğerleri. Training language models to follow instructions with human feedback. arXiv:2203.02155 (2022). Post-training tarifini standart hale getiren makale; bir araç çağrısının biçimi de tam olarak bir yanıtın biçimi gibi, gösterimlerden öğrenilir.


Hazırlayan

David Vicente Campos

NeuraLIA Labs kurucusu ve MyRealFood kurucu ortağı

León Üniversitesinden mezun bir bilgisayar mühendisiyim. MyRealFood’un kurucu ortaklarındanım; orada CTO olarak milyonlarca insanın daha iyi beslenmek için kullandığı uygulamayı geliştirdim. Ayrıca NeuraLIA Labs’i kurdum ve burada AI ürünleri geliştiriyorum. Bu sitede yol boyunca anlamak zorunda kaldığım şeyleri, keşke biri bana böyle anlatsaydı dediğim şekilde yazıyorum.

Yazar hakkında daha fazla

Yayımlayan: NeuraLIA Labs.

Yeni yazılar gelen kutuna gelsin

AI haberleri, rehberler ve ürün güncellemeleri — zamanına değecek bir şey yayımladığımızda kısa bir e-posta.

Mesajlaşmayı mı tercih ediyorsun? Aynı yazılar, burada:WhatsApp topluluğu (yeni sekmede açılır)Telegram kanalı (yeni sekmede açılır)

Kurs dizini

Abstract software decision engine with branching paths, probability nodes, and glowing gates.
jev10 dk okuma

Jev AI modeli düzyazı için değil, kararlar için tasarlandı

TypeSafe AI’ın Jev’i dikkat çekiyor çünkü yazılım zekâsını bir olasılık problemi olarak ele alıyor: doğru dalı seç, güven düzeyini ekle ve kodun bir karara ihtiyacı varken bir LLM’ye metin yazdırmak için ödeme yapma.

Abstract agent runtime sorting documents, memory blocks and pointer nodes inside a bounded context frame.
context-engineering10 dk okuma

Uzun süreli AI ajanları için bağlam mühendisliği

Uzun süre çalışan ajanlar yalnızca pencere küçük olduğu için başarısız olmaz. Dosyalar, araç çıktıları ve bayatlamış geçmiş, ajanın tamamlaması gereken görevi arka plana ittiğinde başarısız olurlar.

Seçimi LIA'ya bırakmaya hazır mısın?

Tüm yapay zeka modelleriyle tek yerde üret — bugün ücretsiz başla.