Siirry sisältöön
18/30Luku 18/30

Tool Calling ja rakenteiset tulosteet: sopimus, joka pitää

24 kutsua, ei rikkinäistä JSONia ja kaksi käyttökelpoista päivämäärää. Mitä parempi kuvaus korjaa — ja mitä schema ei voi korjata.

Tällä sivulla

Anna mallille lennonhakutyökalu ja pyydä sitä etsimään lento Madridista Berliiniin. Tämä palautuu:

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

JSON on validi. Työkalun nimi on oikea. Jokainen pakollinen kenttä on mukana. Ja kutsu on hyödytön: mikään lento-API ei hyväksy arvoa "Madrid" silloin, kun se haluaa lentokenttäkoodin, tai arvoa "3rd October 2026" silloin, kun se haluaa päivämäärän.

Tuo kuilu — syntaktisesti täydellinen, semanttisesti käyttökelvoton — on tämän luvun aihe, ja ensimmäiseksi on todettava, ettei kyse ole JSON-ongelmasta. Kahdenkymmenenneljän tällä työkalulla tehdyn pyynnön aikana malli tuotti 24 validia tool callia eikä yhtään rikkinäistä JSONia. Se ei kertaakaan epäonnistunut siinä osassa, jota kaikki debuggaavat.

Ennen mekaniikkaa lause, joka ehkäisee eniten sekaannusta: tool call on pyyntö, ei toiminto.

Malli tuottaa rakenteisen viestin, joka sanoo haluaisin, että search_flights kutsutaan näillä argumenteilla. Sitten se pysähtyy. Koodisi vastaanottaa viestin, päättää kunnioittaako sitä, kutsuu mitä kutsuukin ja lähettää tuloksen takaisin uutena viestinä. Malli ei koskaan koskenut tietokantaasi, ei tehnyt HTTP-pyyntöä eikä sillä ollut tunnistetietoja.

Kaikki agent-turvallisuudessa luvussa 30 seuraa tästä jaosta, samoin kaikki agent-suunnittelussa luvussa 23: malli ehdottaa ja koodisi päättää, ja koodi on paikka, jossa jokainen takuu elää.

Sanastosta riisuttuna työkalu on siis kaksi asiaa:

Schema. JSON Schema, joka kuvaa funktion: sen nimen, mitä se tekee ja mitä argumentteja se ottaa sekä niiden tyypit ja rajoitteet. Tämä menee promptiin, ja se on ainoa asia, jonka malli koskaan näkee.

Endpoint. Koodissasi oleva funktio, joka ottaa nuo argumentit ja palauttaa jotakin. Malli ei koskaan näe sitä, ei koskaan tiedä millä kielellä se on kirjoitettu eikä pysty erottamaan tietokantakyselyä kovakoodatusta merkkijonosta.

Lähetät schemat pyynnön mukana

Linkki osioon: Lähetät schemat pyynnön mukana

Työkalumäärittelyt menevät promptiin sarjallistettuina siihen muotoon, jolla malli on koulutettu. Ne maksavat tokeneita jokaisella yksittäisellä kutsulla — asia, johon palaamme numeron kanssa myöhemmin tässä luvussa.

Malli vastaa tekstin sijaan kutsulla

Linkki osioon: Malli vastaa tekstin sijaan kutsulla

Proosan sijaan vastaus sisältää rakenteisen pyynnön, ja API raportoi sen kertovan lopetussyyn. Syyllä on väliä: siitä koodisi tietää, että sen pitää ajaa työkalu eikä näyttää käyttäjälle vastausta.

Koodisi ajaa sen — tai kieltäytyy

Linkki osioon: Koodisi ajaa sen — tai kieltäytyy

Tämä on vaihe, jossa mallia ei ole mukana. Validoi argumentit schemaa vasten, päätä saako tämä kutsuja tehdä tämän, ja suorita.

Lähetät tuloksen takaisin viestinä

Linkki osioon: Lähetät tuloksen takaisin viestinä

Tuloksesta tulee keskustelun uusi vuoro sille varatussa roolissa. Malli lukee sen kuten minkä tahansa muun contextin.

Malli vastaa tai pyytää toista työkalua

Linkki osioon: Malli vastaa tai pyytää toista työkalua

Tämä on luvun 23 silmukka ja syy siihen, miksi yksi pyyntö voi muuttua tusinaksi edestakaiseksi kierrokseksi.

Mikään tästä ei ole emergenttiä. Kuten luvussa 11 todettiin, tool calling on opittua käyttäytymistä:1 post-training-vaiheessa malli näki tuhansia keskusteluja, jotka oli muotoiltu täsmälleen näin. Siksi muoto on mallikohtainen, siksi luotettavuus vaihtelee niin paljon samankokoisten mallien välillä ja siksi malli voi kutsua työkalua, jota se ei ole koskaan nähnyt — muoto on koulutettu, tietty työkalu tulee promptistasi.

Mitä huono schema maksaa, mitattuna

Linkki osioon: Mitä huono schema maksaa, mitattuna

Tässä on työkalu sellaisena kuin useimmat sen ensin kirjoittavat. Huomaa, ettei siinä mikään ole väärin; se on vain ohut:

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"],
  },
}

Kaksikymmentäneljä pyyntöä, kuusi kaupunkiparia ristiin neljän päivämäärän ilmaisutavan kanssa ("ensi kuun 3. päivä", "ensi perjantaina", "15. joulukuuta", "huomenna"), greedy decoding, jotta tulokset toistuvat:

työkalua kutsuttiinrikkinäinen JSONpäivämäärä ISO-muodossalentokentät IATA-koodeinakaikki oikein
yllä oleva schema24/2402/244/241/24

Lue kaksi ensimmäistä saraketta ennen kolmea viimeistä. Malli kutsuu oikeaa työkalua joka kerta ja tuottaa hyvin muodostettua JSONia joka kerta. Epäonnistuminen on kokonaan arvoissa, ja arvot ovat käyttökelvottomia: "Madrid" arvon MAD sijaan, "3rd October 2026" arvon 2026-10-03 sijaan.

Tätä kannattaa painottaa, koska se määrittää mistä katsot, kun jokin hajoaa. Vaisto on lisätä JSON-parseri ja retry tai pyytää mallia tiukemmin tuottamaan validia JSONia. Kumpikaan ei korjaa mitään, mitä tässä tapahtui.

Sama endpoint. Sama koodi sen takana. Sama malli, samat promptit, sama decoding. Ainoa muuttuva asia on scheman teksti:

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"],
  },
}
päivämäärän MUOTOpäivämäärän ARVOlentokentän MUOTOlentokentän ARVO
ohut schema2/241/244/244/24
kuvattu schema24/2412/2416/248/24

Päivämäärän muoto nousee 2:sta 24:stä arvoon 24/24. Täydellinen tulos pelkällä tekstimuutoksella, koskematta koodiin ja ilman retry-logiikkaa. Jos otat tästä luvusta yhden toimintatavan, se on tämä: kun työkalua kutsutaan väärin, korjaus on lähes aina kuvauksessa, ja se on järjestelmän halvin korjaus.

Lue nyt toinen sarake, joka on tärkeämpi puolikas.

Schema rajoittaa muotoa. Se ei voi tuoda tietoa.

Linkki osioon: Schema rajoittaa muotoa. Se ei voi tuoda tietoa.

Päivämäärä on ISO-muodossa 24 kertaa 24:stä. Se on oikea päivä 12 kertaa 24:stä.

Puolet kutsuista kantaa siis nyt täydellisesti muotoiltua päivämäärää, joka on väärä päivämäärä. Kuvaus kertoi mallille, minkä muodon tuottaa, ja malli tuotti sen virheettömästi — mutta ilmauksen "ensi perjantaina" muuttaminen arvoksi 2026-09-11 vaatii tämän päivän päivämäärän tietämistä ja kalenteriaritmetiikkaa, eikä mikään määrä kuvausta anna sitä. Sama pätee lentokenttiin: muoto nousi 4:stä 16:een, mutta arvo vain 4:stä 8:aan, koska arvon MAD kirjoittaminen edellyttää tietoa siitä, että Madridin lentokenttä on MAD.

Tämä erottelu on luvun kantava ajatus:

Schema on muotoa koskeva sopimus. Se voi tehdä mallin tulosteesta parsittavan, tyypitetyn ja johdonmukaisen. Se ei voi tehdä siitä totta, ja jokainen hyvän scheman jälkeen jäljelle jäävä epäonnistumistapa on tietoepäonnistuminen, ei muotoepäonnistuminen.

Nämä kaksi tarvitsevat eri korjaukset, ja niiden sekoittaminen tuhlaa viikkoja. Muotoepäonnistumiset korjataan kuvauksessa tai constrained decodingilla, alla. Tietoepäonnistumiset korjataan laittamalla tieto promptiin — nykyinen päivämäärä system-viestiin, lentokenttähaku toiseksi työkaluksi, jota malli kutsuu ensin, tai enum schemaan, kun joukko on tarpeeksi pieni lueteltavaksi. Huomaa, mitä kaikilla kolmella on yhteistä: ne siirtävät ongelman pois mallin muistista sen syötteeseen, mikä on luvun 24 koko ydin.

Rakenteiset tulosteet ja mitä "constrained decoding" oikeasti on

Linkki osioon: Rakenteiset tulosteet ja mitä "constrained decoding" oikeasti on

Kaikki yllä oleva nojaa yhä siihen, että malli valitsee tuottaa oikean muodon. Saatavilla on vahvempi takuu, ja se on luvun 17 paras tuotto.

Muista, miten generointi toimii: jokaisessa vaiheessa malli tuottaa logitin jokaiselle sanaston tokenille, ja sampler valitsee yhden. Constrained decoding lisää väliin yhden vaiheen. Kun sillä on kielioppi — johdettuna JSON Schemastasi — se laskee, mitkä tokenit voisivat laillisesti tulla seuraavaksi, asettaa kaikkien muiden logitit negatiiviseen äärettömyyteen ja antaa samplerin valita jäljelle jäävistä.

Jos schema sanoo, että seuraavan asian täytyy olla {, jokaisen tokenin, joka ei ole {, todennäköisyys on nolla. Ei "epätodennäköinen": nolla. Malli ei voi tuottaa epävalidia JSONia, koska epävalidit tokenit poistettiin jakaumasta ennen samplingia.

Tätä "structured outputs", "JSON mode" ja "guided generation" ovat pinnan alla, ja se selittää niiden kaksi ominaisuutta. Takuu on täydellinen kaikelle, mitä kielioppi voi ilmaista — tyypit, pakolliset kentät, enumit, sisäkkäisyys — koska se pakotetaan mekaanisesti eikä pyydetä kohteliaasti. Ja se ei sano mitään sisällöstä: kielioppi voi pakottaa arvon "date" olemaan merkkijono, joka vastaa päivämääräkuviota, mutta ei pakottaa sitä olemaan oikea päivä. Tämä on sama seinä kuin edellisessä osiossa, vain toiselta puolelta saavuttuna.

Kaksi käytännön huomiota. Se ei ole ilmaista: maski on laskettava jokaisessa vaiheessa, ja monimutkaiset kieliopit maksavat mitattavaa viivettä. Ja se muuttaa sitä, mitä malli tekee — malli, joka ohjataan pois sen suosimasta tokenista, voi tuottaa huonompaa sisältöä samalla kun rakenne on täydellinen. Siksi "pyydä nätisti ja validoi" on yhä järkevä oletus yksinkertaisille muodoille, ja constrained decoding ansaitsee kustannuksensa, kun muoto on monimutkainen tai kuluttaja on tiukka.

Sivuvaikutukset ja ainoa ominaisuus, jolla on väliä

Linkki osioon: Sivuvaikutukset ja ainoa ominaisuus, jolla on väliä

Luvussa 14 mitattiin timeout, jota seurasi retry, ja joka laskutti kaksi generointia yhdestä vastauksesta. Työkalujen kanssa sama epäonnistuminen pahenee, koska työkalu voi tehdä jotakin.

Jos koodisi kutsuu charge_card, saa timeoutin ja yrittää uudelleen, sinulla on kaksi veloitusta. Mallilla ei ole aavistustakaan, että mitään tästä tapahtui; se näkee yhden työkalutuloksen. Korjaus on sama kuin missä tahansa hajautetussa järjestelmässä eikä se ole mallin ongelma: tee operaatiosta idempotentti antamalla kutsulle avain, jotta toinen suoritus tunnistaa ensimmäisen ja palauttaa sen tuloksen sen sijaan, että tekisi työn uudelleen.

Tästä seuraava suunnittelusääntö kannattaa sanoa suoraan. Erota lukuoperaatiot kirjoitusoperaatioista työkalukatalogissasi. Lukua voi yrittää vapaasti uudelleen, ajaa rinnakkain ja cachettaa. Kirjoitusta ei voi, ja sillä pitäisi olla avain, oikeustarkistus ja — kaikessa, mistä käyttäjä haluaisi tietää ennen kuin se tapahtuu — hyväksyntävaihe, joka asettaa ihmisen pyynnön ja toiminnon väliin. Tuo hyväksyntävaihe ei ole kohteliaisuus: se on yksi harvoista asioista, jotka seisovat prompt injectionin ja todellisen seurauksen välissä — ja, kuten luku 30 mittaa, niistä heikoin.

Kuinka monta työkalua ennen kuin laatu heikkenee?

Linkki osioon: Kuinka monta työkalua ennen kuin laatu heikkenee?

Perimätieto sanoo, että monen työkalun lataaminen saa mallin valitsemaan huonosti. Se kannattaa mitata eikä toistaa, joten: samat kaksikymmentäneljä pyyntöä, lentotyökalu sekä kasvava joukko muita — mukana kolme tarkoituksella sekoitettavissa olevaa (junien aikataulut, lauttayhteydet, bussireitit).

ladatut työkalutprompt-tokenitvalitsi search_flightspäivämäärä ISO-muodossa
135324/2424/24
573024/2424/24
101,19321/2421/24
202,11924/2424/24

Valinta ei heikentynyt. Kahdellakymmenellä työkalulla, joista kolme oli uskottavasti sekoitettavissa, puolen miljardin parametrin malli valitsi oikean työkalun kaksikymmentäneljä kertaa kahdestakymmenestäneljästä. Kymmenen kohdalla näkyvä pudotus on kolme kutsua, jotka nimesivät eri työkalun, eikä se säily, kun mennään kahteenkymmeneen.

Tämä on negatiivinen tulos ja se pitäisi raportoida sellaisena: tässä tehtävässä, näillä työkaluilla, "liian monta työkalua" ei ollut ongelma. Se mikä kasvoi, monotonisesti ja kuusinkertaiseksi, oli prompt: 353 tokenista 2,119 tokeniin, maksettuna jokaisesta keskustelun pyynnöstä, ikuisesti, käytettiin työkalua tai ei.

Rehellinen versio perimätiedosta koskee siis kustannusta ja contextia, ei tarkkuutta. Kaksikymmentä työkalua on pysyvä vero jokaiselle viestille, ja luku 16 näytti jo, mitä pysyvä prefiksi tekee laskulle neljänkymmenen vuoron aikana. Kun ihmiset raportoivat, että monet työkalut heikentävät laatua, mekanismi on yleensä se, että määrittelyt syrjäyttivät tärkeän contextin — mikä on luvun 24 ongelma luvun 18 puvussa. Aidosti lähes päällekkäiset työkalut ovat myös todellinen ongelma, ja niiden korjaus ei ole vähemmän työkaluja vaan paremmat kuvaukset ja namespaces: lisää etuliite järjestelmän mukaan (crm.search_customer, billing.search_customer), jotta kahden tiimin kahdesta katalogista yhdistetyt työkalut eivät törmää ja jotta mallilla on jotakin, jonka perusteella erotella.

Kolme työkalulajia ja se yksi, joka avaa seuraavan osan

Linkki osioon: Kolme työkalulajia ja se yksi, joka avaa seuraavan osan

Työkaluja kannattaa lajitella sen mukaan, mitä ne tekevät maailmalle, koska kunkin engineering on erilainen.

Datatyökalut lukevat: hakevat, noutavat, kyselevät. Retry-kelpoisia, rinnakkaistettavia, cachetettavia. Ne epäonnistuvat palauttamalla ei-mitään-hyödyllistä, ja niiden suurin riski on, että ne tuovat epäluotettua tekstiä contextiin — mikä on luvun 30 koko hyökkäyspinta.

Toimintotyökalut kirjoittavat: lähettävät, luovat, veloittavat, poistavat. Ei turvallisesti retry-kelpoisia ilman avainta, ei turvallisesti rinnakkaistettavia, ja syy siihen, miksi hyväksyntävirrat ovat olemassa.

Orkestrointityökalut kutsuvat muita malleja. Työkalu, jonka toteutus on toinen agent, omalla promptilla, omilla työkaluilla ja omalla silmukalla — ja kutsuvalle mallille se näyttää täsmälleen samalta kuin kaksi muuta, koska schema ja endpoint ovat kaikki, mitä se koskaan näkee.

Tuo kolmas laji ei ole kuriositeetti. Se on mekanismi luvun 25 agent-as-a-tool-puoliskon takana — toinen topologia, handoff, luovuttaa keskustelun pois eikä koskaan saa sitä takaisin — ja se toimii täsmälleen siksi, että tämän luvun rajapinta on tarpeeksi kapea, jotta kokonainen agent mahtuu sen taakse.

Sinulla on nyt malli, joka voi pyytää asioita, ja sopimus, joka tekee pyytämisestä parsittavaa. Sinulla ei ole mitään, mistä se voisi kysyä, paitsi se mikä mahtuu sen promptiin.

Tuotannossa yleisin työkalu on ylivoimaisesti haku tekstimassasta, jota malli ei koskaan nähnyt koulutuksessa: dokumentaatiosi, tukipyyntösi, sopimuksesi. Se kuulostaa ratkaistulta ongelmalta — tee embedding, etsi lähimmät naapurit, liitä ne mukaan — ja ratkaisemattomat osat ovat juuri ne, jotka ratkaisevat, onko vastaus luotettava: miten teksti leikataan ennen embeddingiä, mikä samankaltaisuuskynnys on riittävän matala tarkoittamaan en tiedä, ja miten lähdeviite kiinnitetään väitteeseen, jotta lukija voi tarkistaa sen.

Luku 19 on retrieval, ja se on luku, jossa väärä vastaus lakkaa olemasta kuriositeetti ja alkaa olla vastuu.


Tämän luvun mittaukset tulevat kohteesta Qwen/Qwen2.5-0.5B-Instruct greedy decodingilla, 24 generoidun pyynnön yli, joissa risteytettiin kuusi kaupunkiparia neljän päivämäärämuotoilun kanssa, käyttäen mallin omaa chat-templatea työkalumäärittelyille. Ne toistuvat täsmälleen, ja kyseessä on pieni malli: lue muoto/arvo-jako mekanismin demonstraationa eikä benchmarkina siitä, mitä nykyiset mallit tekevät. Frontier-malli ratkaisee "ensi perjantain" oikein paljon useammin — eikä sitä silti voi pakottaa tekemään niin schemalla, mikä on yleistyvä osa.

Yllä käytetty JSON Schema -sanasto (type, properties, required, pattern, format, enum) on määritelty siinä JSON Schema -luonnoksessa, jonka palveluntarjoajasi dokumentaatio nimeää; hyödyllinen osajoukko on pieni ja sama palveluntarjoajasta toiseen, ja olemassa olevat erot — mitkä avainsanat enforced by constrained decoding eivätkä vain välitetty mallille — kannattaa lukea palveluntarjoajan structured-output-oppaasta eikä olettaa.

Constrained decoding -tekniikasta guidance-tyyliset kirjastot ja outlines-projekti dokumentoivat grammar-to-logit-mask-rakenteen tavalla, joka vastaa suoraan luvun 17 sampleria. Ja itse edestakaisesta kierroksesta selkein määrittely ei ole tutoriaali vaan protokolla: luku 26 lukee sen rivi riviltä.

  1. Ouyang, L. et al. Training language models to follow instructions with human feedback. arXiv:2203.02155 (2022). Artikkeli, joka teki post-training-reseptistä standardin; tool callin muoto opitaan siinä demonstraatioista, täsmälleen kuten vastauksen muoto.


Tekijä

David Vicente Campos

NeuraLIA Labsin perustaja ja MyRealFoodin toinen perustaja

Olen valmistunut tietotekniikan insinööriksi Leónin yliopistosta. Olin mukana perustamassa MyRealFoodia, jossa teknologiajohtajana rakensin sovelluksen, jota miljoonat ihmiset ovat käyttäneet syödäkseen paremmin, ja perustin NeuraLIA Labsin, jossa rakennan tekoälytuotteita. Täällä kirjoitan siitä, mitä minun on pitänyt ymmärtää matkan varrella, niin kuin olisin toivonut jonkun selittävän asiat minulle.

Lisää kirjoittajasta

Julkaisija: NeuraLIA Labs.

Uudet julkaisut suoraan sähköpostiisi

AI-uutisia, oppaita ja tuoteuutisia — lyhyt sähköposti, kun julkaisemme jotain aikasi arvoista.

Kurssin hakemisto

Abstract software decision engine with branching paths, probability nodes, and glowing gates.
jev9 min lukuaikaa

Jev AI -malli on rakennettu päätöksiä, ei proosaa varten

TypeSafe AI:n Jev herättää huomiota, koska se käsittelee ohjelmistojen älykkyyttä todennäköisyysongelmana: valitse oikea haara, liitä mukaan varmuus ja vältä maksamasta LLM:lle tekstin kirjoittamisesta, kun koodi tarvitsee päätöksen.

Abstract agent runtime sorting documents, memory blocks and pointer nodes inside a bounded context frame.
context-engineering9 min lukuaikaa

Kontekstisuunnittelu pitkän aikavälin AI-agenteille

Pitkäkestoiset agentit eivät epäonnistu vain siksi, että ikkuna on pieni. Ne epäonnistuvat, kun tiedostot, työkalujen tulosteet ja vanhentunut historia syrjäyttävät tehtävän, joka agentin piti saada valmiiksi.

Valmis antamaan LIA:n valita puolestasi?

Rakenna kaikilla tekoälymalleilla yhdessä paikassa — aloita ilmaiseksi jo tänään.