Tool calling és structured outputs: a szerződés, ami kitart
Huszonnégy hívás, nulla törött JSON és két használható dátum. Ugyanaz az endpoint jobb leírással — és amit egy séma nem javít ki.
Ezen az oldalon
Adj egy modellnek egy járatkereső toolt, és kérd meg, hogy találjon járatot Madridból Berlinbe. Ez érkezik vissza:
<tool_call>
{"name": "search_flights",
"arguments": {"from": "Madrid", "to": "Berlin", "date": "3rd October 2026"}}
</tool_call>A JSON érvényes. A tool neve helyes. Minden kötelező mező jelen van. És a hívás használhatatlan: egyetlen járat-API sem fogad el "Madrid" értéket ott, ahol repülőtéri kódot vár, vagy "3rd October 2026" értéket ott, ahol dátumot vár.
Ez a rés — szintaktikailag tökéletes, szemantikailag használhatatlan — ennek a fejezetnek a témája, és az első dolog, amit tisztázni kell: ez nem JSON-probléma. Ezzel a toollal huszonnégy kérés során a modell 24 érvényes tool callt és nulla törött JSON-t adott. Egyszer sem azon a részen bukott el, amit mindenki debugol.
A modell nem hajt végre semmit
Link a szakaszhoz: A modell nem hajt végre semmitA mechanika előtt jöjjön a mondat, amely a legtöbb félreértést megelőzi: a tool call kérés, nem művelet.
A modell egy strukturált üzenetet bocsát ki, amely azt mondja: szeretném, ha a search_flights ezekkel az argumentumokkal lenne meghívva. Aztán megáll. A kódod megkapja ezt az üzenetet, eldönti, teljesíti-e, meghívja, amit meg kell hívnia, majd az eredményt egy újabb üzenetként visszaküldi. A modell soha nem nyúlt az adatbázisodhoz, soha nem indított HTTP-kérést, soha nem voltak hitelesítő adatai.
Az agent-biztonságról szóló 30. fejezet minden következtetése ebből a felosztásból ered, ahogy az agent-tervezésről szóló 23. fejezet minden következtetése is: a modell javasol, a kódod dönt, és minden garancia a kódban él.
Tehát egy tool, a szókincstől lecsupaszítva, két dolog:
Egy séma. Egy JSON Schema, amely leír egy függvényt: a nevét, hogy mit csinál, és milyen argumentumokat fogad, azok típusaival és korlátaival együtt. Ez kerül be a promptba, és ez az egyetlen dolog, amit a modell valaha lát.
Egy endpoint. Egy függvény a kódodban, amely megkapja ezeket az argumentumokat, és visszaad valamit. A modell soha nem látja, soha nem tudja, milyen nyelven íródott, és nem tud különbséget tenni egy adatbázis-lekérdezés és egy hardcoded string között.
A sémákat a kéréssel együtt küldöd
Link a szakaszhoz: A sémákat a kéréssel együtt küldödA tool-definíciók bekerülnek a promptba, abban a formátumban szerializálva, amelyen a modellt tanították. Minden egyes hívásnál tokenekbe kerülnek — ez a tény később ebben a fejezetben számmal tér vissza.
A modell szöveg helyett hívással válaszol
Link a szakaszhoz: A modell szöveg helyett hívással válaszolPróza helyett a válasz egy strukturált kérést tartalmaz, az API pedig ennek megfelelő befejezési okot jelent. Az ok számít: a kódod ebből tudja, hogy toolt kell futtatnia, nem pedig választ kell megmutatnia a felhasználónak.
A kódod lefuttatja — vagy visszautasítja
Link a szakaszhoz: A kódod lefuttatja — vagy visszautasítjaEz az a lépés, amelyben nincs modell. Validáld az argumentumokat a sémával szemben, döntsd el, hogy ez a hívó megteheti-e ezt, majd hajtsd végre.
Az eredményt üzenetként visszaküldöd
Link a szakaszhoz: Az eredményt üzenetként visszaküldödAz eredmény a beszélgetés újabb körévé válik, egy erre fenntartott szerepben. A modell úgy olvassa, mint bármely más contextet.
A modell válaszol, vagy újabb toolt kér
Link a szakaszhoz: A modell válaszol, vagy újabb toolt kérEz a 23. fejezet ciklusa, és ezért válhat egyetlen kérésből akár egy tucat oda-vissza kör.
Ebből semmi sem emergens. Ahogy a 11. fejezet megállapította, a tool calling tanított viselkedés:1 a post-training során a modell több ezer, pontosan ilyen alakú beszélgetést látott. Ezért modell-specifikus a formátum, ezért változik ennyire a megbízhatóság hasonló méretű modellek között, és ezért tud a modell olyan toolt is hívni, amelyet még soha nem látott — az alakot tanulta meg, a konkrét tool a promptodból érkezik.
Mennyibe kerül egy rossz séma, mérve
Link a szakaszhoz: Mennyibe kerül egy rossz séma, mérveÍgy írja meg a legtöbb ember először a toolt. Figyeld meg, hogy semmi sincs benne rosszul; csak vékony:
{
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"],
},
}Huszonnégy kérés, hat várospár keresztezve egy dátum négyféle megfogalmazásával ("the 3rd of next month", "next Friday", "15 December", "tomorrow"), greedy decoding, hogy az eredmények reprodukálhatók legyenek:
| tool meghívva | törött JSON | dátum ISO-ban | repülőterek IATA-ként | minden helyes | |
|---|---|---|---|---|---|
| a fenti séma | 24/24 | 0 | 2/24 | 4/24 | 1/24 |
Az utolsó három előtt olvasd el az első két oszlopot. A modell minden alkalommal a megfelelő toolt hívja, és minden alkalommal jól formált JSON-t ad. A hiba teljes egészében az értékekben van, és az értékek használhatatlanok: "Madrid" MAD helyett, "3rd October 2026" 2026-10-03 helyett.
Ezt érdemes hangsúlyozni, mert meghatározza, hol keresel, amikor valami elromlik. Az ösztön az, hogy hozzáadj egy JSON parsert retry-jal, vagy erélyesebben kéred a modelltől az érvényes JSON-t. Egyik sem kezeli azt, ami itt történt.
Most csak a leírást változtasd meg
Link a szakaszhoz: Most csak a leírást változtasd megUgyanaz az endpoint. Ugyanaz a mögötte lévő kód. Ugyanaz a modell, ugyanazok a promptok, ugyanaz a decoding. Csak a sémában lévő szöveg változik:
{
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"],
},
}| dátum FORMÁTUM | dátum ÉRTÉK | repülőtér FORMÁTUM | repülőtér ÉRTÉK | |
|---|---|---|---|---|
| vékony séma | 2/24 | 1/24 | 4/24 | 4/24 |
| leírt séma | 24/24 | 12/24 | 16/24 | 8/24 |
A dátumformátum 24-ből 2-ről 24-ből 24-re megy. Tökéletes, egy szövegmódosítástól, kódérintés és retry logika nélkül. Ha ebből a fejezetből egy működtetési szokást viszel magaddal, ez legyen az: amikor egy toolt rosszul hívnak meg, a javítás szinte mindig a leírásban van, és ez a rendszer legolcsóbb javítása.
Most olvasd el a második oszlopot, ami a fontosabb fele.
A séma az alakot korlátozza. Tudást nem tud adni.
Link a szakaszhoz: A séma az alakot korlátozza. Tudást nem tud adni.A dátum 24-ből 24-szer ISO formátumban van. A megfelelő nap 24-ből 12-szer.
Tehát a hívások fele most tökéletesen formázott dátumot hordoz, amely rossz dátum. A leírás megmondta a modellnek, milyen alakot adjon ki, és a modell hibátlanul kiadta — de a "next Friday" 2026-09-11 értékké alakításához tudni kell a mai dátumot és naptári aritmetikát kell végezni, és ezt semmilyen mennyiségű leírás nem pótolja. Ugyanez a történet a repülőterekkel: a formátum 4-ről 16-ra ment, de az érték csak 4-ről 8-ra, mert a MAD leírásához tudni kell, hogy Madrid repülőtere MAD.
Ez a megkülönböztetés a fejezet teherhordó gondolata:
A séma formára vonatkozó szerződés. Parse-olhatóvá, típusossá és konzisztenssé teheti a modell kimenetét. Nem tudja igazzá tenni, és minden hibamód, amely egy jó sémát túlél, tudáshiba, nem formátumhiba.
A kettő más javítást igényel, és az összekeverésük heteket pazarol el. A formátumhibákat a leírásban vagy constrained decodinggal lehet javítani, lentebb. A tudáshibákat úgy lehet javítani, hogy a tudást a promptba teszed — az aktuális dátumot a rendszerüzenetbe, egy repülőtér-lookupot második toolként, amelyet a modell először hív, vagy enumot a sémába, ha a halmaz elég kicsi a felsoroláshoz. Figyeld meg, mi a közös mindháromban: a problémát a modell memóriájából a bemenetébe mozgatják, és erről szól a teljes 24. fejezet.
Structured outputs, és mi valójában a "constrained decoding"
Link a szakaszhoz: Structured outputs, és mi valójában a "constrained decoding"A fentiek mind továbbra is arra támaszkodnak, hogy a modell úgy dönt, a megfelelő alakot állítja elő. Létezik erősebb garancia is, és ez a 17. fejezet legjobb megtérülése.
Idézd fel, hogyan működik a generálás: a modell minden lépésben logitot állít elő a szókészlet minden tokenjére, a sampler pedig kiválaszt egyet. A constrained decoding közéjük szúr be egy lépést. Egy grammatikából kiindulva — amely a JSON Schemádból származik — kiszámolja, mely tokenek jöhetnek jogszerűen következőként, az összes többi logitját negatív végtelenre állítja, és hagyja, hogy a sampler a megmaradtak közül válasszon.
Ha a séma szerint a következő dolognak {-nek kell lennie, akkor minden tokennek, ami nem {, nulla a valószínűsége. Nem „valószínűtlen”: nulla. A modell nem tud érvénytelen JSON-t kibocsátani, mert az érvénytelen tokeneket a mintavétel előtt eltávolították az eloszlásból.
Ez van a "structured outputs", a "JSON mode" és a "guided generation" alatt, és ez magyarázza a két tulajdonságukat. A garancia teljes mindenre, amit a grammatika ki tud fejezni — típusokra, kötelező mezőkre, enumokra, egymásba ágyazásra —, mert mechanikusan érvényesítik, nem udvariasan kérik. És semmit nem mond a tartalomról: egy grammatika rákényszerítheti a "date" mezőt arra, hogy dátummintának megfelelő string legyen, de nem kényszerítheti arra, hogy a megfelelő nap legyen. Ez ugyanaz a fal, mint az előző szakaszban, csak a másik oldalról érkeztünk meg hozzá.
Két gyakorlati megjegyzés. Nem ingyenes: a maszkot minden lépésben ki kell számolni, és az összetett grammatikák mérhető késleltetésbe kerülnek. És megváltoztatja, mit csinál a modell — egy modell, amelyet eltérítenek a preferált tokenjétől, rosszabb tartalmat adhat, miközben tökéletes struktúrát produkál. Ezért az "ask nicely and validate" továbbra is ésszerű alapértelmezés egyszerű alakoknál, a constrained decoding pedig akkor éri meg az árát, ha az alak összetett vagy a fogyasztó szigorú.
Mellékhatások, és az egyetlen tulajdonság, ami számít
Link a szakaszhoz: Mellékhatások, és az egyetlen tulajdonság, ami számítA 14. fejezet egy timeoutot mért, amelyet retry követett, és így egy válaszért két generálást számlázott. Toolokkal ugyanaz a hiba rosszabb lesz, mert egy tool csinálhat valamit.
Ha a kódod meghívja a charge_card-t, timeoutol, majd retry-zik, két terhelésed van. A modellnek fogalma sincs, hogy bármi ebből megtörtént; ő egy tool eredményt lát. A javítás ugyanaz, mint bármely elosztott rendszerben, és nem a modell problémája: tedd a műveletet idempotenssé azzal, hogy kulcsot adsz a hívásnak, így a második végrehajtás felismeri az elsőt, és annak eredményét adja vissza ahelyett, hogy újra elvégezné a munkát.
Az ebből következő tervezési szabályt érdemes kimondani. Válaszd szét az olvasásokat és az írásokat a tool-katalógusodban. Egy olvasás szabadon retry-zható, párhuzamosítható és cache-elhető. Egy írás nem, és kulcsot, jogosultságellenőrzést, valamint — minden olyan dolognál, amiről a felhasználó tudni szeretne, mielőtt megtörténik — jóváhagyási lépést kell hordoznia, amely egy embert tesz a kérés és a művelet közé. Ez a jóváhagyási lépés nem udvariasság: azon kevés dolgok egyike, amelyek egy prompt injection és egy valós következmény között állnak — és, ahogy a 30. fejezet méri, ezek közül a leggyengébb.
Hány tool után romlik?
Link a szakaszhoz: Hány tool után romlik?A folklór szerint sok tool betöltése rosszabb választásra készteti a modellt. Ezt érdemes mérni, nem ismételgetni, tehát: ugyanaz a huszonnégy kérés, a járatkereső toollal plusz egy növekvő készlet másik toollal — köztük három szándékosan összetéveszthetővel (vonatmenetrendek, kompátkelések, buszjáratok).
| betöltött toolok | prompt tokenek | search_flights választva | dátum ISO-ban |
|---|---|---|---|
| 1 | 353 | 24/24 | 24/24 |
| 5 | 730 | 24/24 | 24/24 |
| 10 | 1,193 | 21/24 | 21/24 |
| 20 | 2,119 | 24/24 | 24/24 |
A kiválasztás nem romlott. Húsz toollal, amelyek közül három hihetően összetéveszthető volt, egy félmilliárd paraméteres modell huszonnégyszer huszonnégyből a megfelelőt választotta. A tíznél látható beesés három olyan hívás, amely más toolt nevezett meg, és ez nem marad meg húsznál.
Ez negatív eredmény, és így is kell jelenteni: ezen a feladaton, ezekkel a toolokkal a "túl sok tool" nem volt probléma. Ami monoton módon és hatszorosára nőtt, az a prompt: 353 tokenről 2,119-re, minden kérésnél fizetve a beszélgetésben, örökké, függetlenül attól, hogy használtak-e bármilyen toolt.
Tehát a folklór őszinte változata költségről és contextről szól, nem pontosságról. Húsz tool állandó adó minden üzeneten, és a 16. fejezet már megmutatta, mit tesz egy állandó prefix egy számlával negyven körön át. Amikor az emberek arról számolnak be, hogy sok tool rontja a minőséget, a mechanizmus általában az, hogy a definíciók kiszorították a fontos contextet — ez pedig egy 24. fejezetbeli probléma, 18. fejezetnek öltözve. Az egymáshoz valóban nagyon közeli duplikátum toolok is valós problémát jelentenek, és ezekre nem a kevesebb tool a megoldás, hanem a jobb leírások és namespace-ek: prefixáld őket rendszer szerint (crm.search_customer, billing.search_customer), hogy két csapat két katalógusának összeolvasztásakor ne ütközzenek, és hogy a modellnek legyen mi alapján különbséget tennie.
Háromféle tool, és az, amely megnyitja a következő részt
Link a szakaszhoz: Háromféle tool, és az, amely megnyitja a következő résztSegít aszerint rendezni a toolokat, hogy mit tesznek a világgal, mert mindegyikhez más mérnöki munka tartozik.
Adattoolok olvasnak: keresnek, lekérnek, query-znek. Retry-zhatók, párhuzamosíthatók, cache-elhetők. Úgy hibáznak, hogy nem adnak vissza semmi hasznosat, fő kockázatuk pedig az, hogy nem megbízható szöveget hoznak be a contextbe — ez a 30. fejezet teljes támadási felülete.
Akciótoolok írnak: küldenek, létrehoznak, terhelnek, törölnek. Kulcs nélkül nem retry-zhatók, biztonságosan nem párhuzamosíthatók, és miattuk léteznek jóváhagyási folyamatok.
Orchestration toolok más modelleket hívnak. Egy tool, amelynek implementációja egy másik agent, saját prompttal, saját toolokkal és saját ciklussal — a hívó modell számára pedig pontosan ugyanúgy néz ki, mint a másik kettő, mert a séma és az endpoint minden, amit valaha lát.
Ez a harmadik fajta nem érdekesség. Ez a mechanizmus áll a 25. fejezet agent-as-a-tool felének hátterében — a másik topológia, a handoff, átadja a beszélgetést, és soha nem kapja vissza —, és pontosan azért működik, mert ebben a fejezetben az interfész elég szűk ahhoz, hogy egy egész agent elférjen mögötte.
Merre tovább
Link a szakaszhoz: Merre továbbMost már van egy modelled, amely tud kérni dolgokat, és van egy szerződésed, amely parse-olhatóvá teszi a kérést. Ami nincs, az bármi olyan, amiről kérdezhetne azon túl, ami belefér a promptjába.
A productionben messze a leggyakoribb tool egy olyan szövegkorpuszon futó keresés, amelyet a modell soha nem látott a tanítása során: a dokumentációd, a ticketjeid, a szerződéseid. Ez megoldott problémának hangzik — embedeld, találd meg a legközelebbi szomszédokat, illeszd be őket —, és a nem megoldott részek döntik el, hogy a válasz megbízható-e: hogyan darabolod fel a szöveget az embedding előtt, milyen hasonlósági küszöb elég alacsony ahhoz, hogy azt jelentse: nem tudom, és hogyan kapcsolódik egy hivatkozás egy állításhoz, hogy az olvasó ellenőrizhesse.
A 19. fejezet a retrieval, és ez az a fejezet, ahol egy rossz válasz megszűnik érdekességnek lenni, és felelősséggé válik.
Források és módszer
Link a szakaszhoz: Források és módszerA fejezet mérései a Qwen/Qwen2.5-0.5B-Instruct-ből származnak greedy decodinggal, 24 generált kérésen, amelyek hat várospárt kereszteztek négy dátummegfogalmazással, a modell saját chat template-jét használva a tool-definíciókhoz. Pontosan reprodukálhatók, és kis modellről van szó: a formátum/érték szétválasztást a mechanizmus demonstrációjaként olvasd, ne benchmarkként arról, hogy a mai modellek mire képesek. Egy frontier modell sokkal gyakrabban oldja meg helyesen a "next Friday" kifejezést — de egy séma továbbra sem tudja rákényszeríteni, és ez az a rész, amely általánosítható.
A fent használt JSON Schema-szókincset (type, properties, required, pattern, format, enum) az a JSON Schema draft specifikálja, amelyet a szolgáltatód dokumentációja megnevez; a hasznos részhalmaz kicsi és szolgáltatók között azonos, a létező különbségeket pedig — mely kulcsszavakat érvényesíti a constrained decoding, és melyeket csak továbbítanak a modellnek — érdemes a szolgáltató structured-output útmutatójában elolvasni, nem feltételezni.
A constrained decoding technikájához a guidance-stílusú könyvtárak és a outlines projekt úgy dokumentálják a grammatika–logit-maszk konstrukciót, hogy az közvetlenül leképezhető a 17. fejezet samplerére. Magához az oda-vissza körhöz pedig a legtisztább specifikáció nem tutorial, hanem protokoll: a 26. fejezet sorról sorra olvassa végig.
Hivatkozások
Link a szakaszhoz: Hivatkozások-
Ouyang, L. et al. Training language models to follow instructions with human feedback. arXiv:2203.02155 (2022). A tanulmány, amely standarddá tette a post-training receptet; a tool call alakját ott tanulja meg a modell, demonstrációkból, pontosan úgy, mint egy válasz alakját. ↩