Tool Calling і структуровані виходи: контракт, що витримує
24 виклики, жодного зламаного JSON і дві придатні дати. Той самий endpoint із кращим описом — і межі того, що schema не виправить.
На цій сторінці
Дайте моделі інструмент пошуку авіарейсів і попросіть її знайти рейс із Мадрида до Берліна. Ось що повертається:
<tool_call>
{"name": "search_flights",
"arguments": {"from": "Madrid", "to": "Berlin", "date": "3rd October 2026"}}
</tool_call>JSON валідний. Назва інструмента правильна. Усі обов’язкові поля присутні. І виклик марний: жоден API авіарейсів не прийме "Madrid" там, де очікує код аеропорту, або "3rd October 2026" там, де очікує дату.
Саме про цей розрив — синтаксично ідеально, семантично непридатно — цей розділ. І перше, що треба встановити: це не проблема JSON. У двадцяти чотирьох запитах із цим інструментом модель створила 24 валідні tool calls і жодного зламаного JSON. Вона жодного разу не провалилася на тій частині, яку всі зазвичай налагоджують.
Модель нічого не виконує
Посилання на розділ: Модель нічого не виконуєПеред механікою — речення, яке усуває найбільше плутанини: tool call — це запит, а не дія.
Модель видає структуроване повідомлення, яке означає: я хотіла б викликати search_flights з такими аргументами. Потім вона зупиняється. Ваш код отримує це повідомлення, вирішує, чи виконувати його, викликає те, що має викликати, і надсилає результат назад як ще одне повідомлення. Модель ніколи не торкалася вашої бази даних, не робила HTTP-запиту, не мала облікових даних.
Усе про безпеку agent у Розділі 30 випливає з цього поділу, як і все про дизайн agent у Розділі 23: модель пропонує, а ваш код розпоряджається, і саме в коді живуть усі гарантії.
Отже, якщо прибрати термінологію, інструмент — це дві речі:
Схема. JSON Schema, що описує функцію: її назву, що вона робить і які аргументи приймає разом із їхніми типами та обмеженнями. Саме це потрапляє в prompt, і це єдине, що модель коли-небудь бачить.
Endpoint. Функція у вашому коді, яка приймає ці аргументи й щось повертає. Модель ніколи її не бачить, не знає, якою мовою вона написана, і не може відрізнити запит до бази даних від захардкодженого рядка.
Ви надсилаєте схеми разом із запитом
Посилання на розділ: Ви надсилаєте схеми разом із запитомВизначення інструментів потрапляють у prompt, серіалізовані у формат, на якому навчали модель. Вони коштують tokens у кожному окремому виклику — факт, який повернеться з числом далі в цьому розділі.
Модель відповідає викликом замість тексту
Посилання на розділ: Модель відповідає викликом замість текстуЗамість прози відповідь містить структурований запит, а API повідомляє відповідну причину завершення. Ця причина важлива: саме так ваш код розуміє, що треба запустити інструмент, а не показати користувачу відповідь.
Ваш код запускає його — або відмовляє
Посилання на розділ: Ваш код запускає його — або відмовляєЦе крок, у якому моделі немає. Перевірте аргументи за схемою, вирішіть, чи має цей викликач право це робити, і виконайте.
Ви надсилаєте результат назад як повідомлення
Посилання на розділ: Ви надсилаєте результат назад як повідомленняРезультат стає ще одним ходом у розмові, у спеціально відведеній для нього ролі. Модель читає його як будь-який інший context.
Модель відповідає або просить ще один інструмент
Посилання на розділ: Модель відповідає або просить ще один інструментЦе і є цикл із Розділу 23, і причина, чому один запит може перетворитися на десяток кругових проходів.
У цьому немає нічого емерджентного. Як показав Розділ 11, tool calling — це навчена поведінка:1 під час post-training модель бачила тисячі розмов, сформованих саме так. Ось чому формат залежить від моделі, чому надійність так сильно відрізняється між моделями схожого розміру і чому модель може викликати інструмент, якого ніколи не бачила: форму було навчено, а конкретний інструмент приходить із вашого prompt.
У що обходиться погана схема: вимірювання
Посилання на розділ: У що обходиться погана схема: вимірюванняОсь інструмент у тому вигляді, у якому більшість пише його вперше. Зверніть увагу: у ньому немає нічого неправильного; він просто тонкий:
{
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"],
},
}Двадцять чотири запити, шість пар міст у поєднанні з чотирма способами виразити дату ("the 3rd of next month", "next Friday", "15 December", "tomorrow"), greedy decoding, щоб результати відтворювалися:
| інструмент викликано | зламаний JSON | дата в ISO | аеропорти як IATA | усе правильно | |
|---|---|---|---|---|---|
| схема вище | 24/24 | 0 | 2/24 | 4/24 | 1/24 |
Прочитайте перші дві колонки перед останніми трьома. Модель щоразу викликає правильний інструмент і щоразу створює коректно сформований JSON. Помилка повністю у значеннях, і ці значення непридатні: "Madrid" замість MAD, "3rd October 2026" замість 2026-10-03.
На цьому варто наполягти, бо саме це визначає, куди дивитися, коли щось ламається. Інстинкт — додати JSON-парсер із повторною спробою або ще наполегливіше попросити модель видати валідний JSON. Жодне з цього не вирішує того, що сталося тут.
Тепер змініть лише опис
Посилання на розділ: Тепер змініть лише описТой самий endpoint. Той самий код за ним. Та сама модель, ті самі prompts, те саме decoding. Змінюється лише текст у схемі:
{
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"],
},
}| FORMAT дати | VALUE дати | FORMAT аеропорту | VALUE аеропорту | |
|---|---|---|---|---|
| тонка схема | 2/24 | 1/24 | 4/24 | 4/24 |
| описана схема | 24/24 | 12/24 | 16/24 | 8/24 |
Формат дати зростає з 2 із 24 до 24 із 24. Ідеально — лише від зміни тексту, без жодної зміни коду і без логіки повторних спроб. Якщо з цього розділу варто винести одну операційну звичку, то ось її: коли інструмент викликається неправильно, виправлення майже завжди в описі, і це найдешевше виправлення в системі.
Тепер прочитайте другу колонку — важливішу половину.
Схема обмежує форму. Вона не може додати знання.
Посилання на розділ: Схема обмежує форму. Вона не може додати знання.Дата у форматі ISO — 24 рази з 24. Це правильний день — 12 разів із 24.
Отже, половина викликів тепер несе ідеально відформатовану дату, яка є неправильною датою. Опис сказав моделі, яку форму створити, і модель створила її бездоганно — але перетворити "next Friday" на 2026-09-11 вимагає знати сьогоднішню дату й виконати календарну арифметику, а жоден опис цього не надає. Та сама історія з аеропортами: формат зріс із 4 до 16, але значення — лише з 4 до 8, бо написати MAD вимагає знати, що аеропорт Мадрида — MAD.
Ця відмінність — опорна ідея розділу:
Схема — це контракт про форму. Вона може зробити вихід моделі придатним для парсингу, типізованим і послідовним. Вона не може зробити його істинним, і кожен режим відмови, який переживає хорошу схему, є відмовою знання, а не відмовою формату.
Для цих двох потрібні різні виправлення, і плутанина між ними марнує тижні. Помилки формату виправляються в описі або за допомогою обмеженого decoding, нижче. Помилки знання виправляються тим, що знання кладуть у prompt — поточну дату в системне повідомлення, пошук аеропорту як другий інструмент, який модель викликає першим, enum у схемі, коли множина достатньо мала, щоб її перелічити. Зверніть увагу, що спільне в усіх трьох: вони переносять проблему з пам’яті моделі в її вхід, а це і є весь Розділ 24.
Структуровані виходи й що насправді означає "constrained decoding"
Посилання на розділ: Структуровані виходи й що насправді означає "constrained decoding"Усе вище досі покладається на те, що модель обере правильну форму. Є сильніша гарантія, і це найкраща віддача від Розділу 17.
Згадайте, як працює генерація: на кожному кроці модель створює logit для кожного token у словнику, а семплер обирає один. Обмежений decoding вставляє між ними ще один крок. Маючи граматику — виведену з вашої JSON Schema — він обчислює, які tokens можуть легально йти далі, встановлює logits усіх інших у мінус нескінченність і дає семплеру обирати з того, що залишилося.
Якщо схема каже, що наступною має бути {, тоді кожен token, який не є {, має ймовірність нуль. Не "малоймовірно": нуль. Модель не може видати невалідний JSON, бо невалідні tokens були прибрані з розподілу ще до sampling.
Саме це лежить під капотом "structured outputs", "JSON mode" і "guided generation", і це пояснює їхні дві властивості. Гарантія повна для всього, що може виразити граматика — типів, обов’язкових полів, enums, вкладеності, — бо вона забезпечується механічно, а не ввічливо проситься. І вона нічого не каже про вміст: граматика може змусити "date" бути рядком, що відповідає шаблону дати, але не може змусити його бути правильним днем. Це та сама стіна, що й у попередньому розділі, тільки з іншого боку.
Дві практичні нотатки. Це не безкоштовно: маску треба обчислювати на кожному кроці, а складні граматики коштують вимірюваної затримки. І це змінює те, що робить модель: модель, яку відхилили від бажаного token, може створити гірший вміст, водночас створивши ідеальну структуру. Тому "ввічливо попросити й провалідувати" досі є розумним дефолтом для простих форм, а обмежений decoding виправдовує свою ціну тоді, коли форма складна або споживач суворий.
Побічні ефекти й єдина властивість, що має значення
Посилання на розділ: Побічні ефекти й єдина властивість, що має значенняРозділ 14 виміряв тайм-аут із подальшою повторною спробою, який виставив рахунок за дві генерації за одну відповідь. З інструментами та сама відмова стає гіршою, бо інструмент може щось зробити.
Якщо ваш код викликає charge_card, отримує тайм-аут і повторює спробу, у вас два списання. Модель не має уявлення, що це сталося; вона бачить один результат інструмента. Виправлення таке саме, як у будь-якій розподіленій системі, і це не проблема моделі: зробіть операцію ідемпотентною, давши виклику ключ, щоб друге виконання розпізнало перше й повернуло його результат замість повторної роботи.
Правило дизайну, яке з цього випливає, варто сформулювати прямо. Відокремлюйте читання від записів у своєму каталозі інструментів. Читання можна вільно повторювати, запускати паралельно й кешувати. Запис — ні; він має нести ключ, перевірку дозволів і — для всього, про що користувач захотів би знати до того, як це станеться, — крок approval, який ставить людину між запитом і дією. Цей крок approval — не ввічливість: це одна з небагатьох речей між prompt injection і реальним наслідком — і, як вимірює Розділ 30, найслабша з них.
Скільки інструментів потрібно, щоб якість просіла?
Посилання на розділ: Скільки інструментів потрібно, щоб якість просіла?Фольклор каже, що завантаження багатьох інструментів змушує модель обирати гірше. Варто виміряти, а не повторювати, тож: ті самі двадцять чотири запити, інструмент для рейсів плюс зростаючий набір інших — зокрема три навмисно схожі, які можна сплутати (розклад поїздів, поромні переправи, автобусні маршрути).
| завантажено інструментів | prompt tokens | обрано search_flights | дата в ISO |
|---|---|---|---|
| 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 |
Вибір не погіршився. З двадцятьма інструментами, три з яких цілком можна сплутати, модель із пів мільярда parameters обрала правильний у двадцяти чотирьох випадках із двадцяти чотирьох. Просідання на десяти — це три виклики, що назвали інший інструмент, і воно не переживає переходу до двадцяти.
Це негативний результат, і його слід так і повідомляти: у цьому завданні, з цими інструментами, "занадто багато інструментів" не було проблемою. Те, що справді зростало монотонно й у шість разів, — це prompt: від 353 tokens до 2,119, оплачуваних у кожному запиті розмови, завжди, незалежно від того, чи використано якийсь інструмент.
Тож чесна версія фольклору — про вартість і context, а не про точність. Двадцять інструментів — це постійний податок на кожне повідомлення, а Розділ 16 уже показав, що постійний префікс робить із рахунком за сорок ходів. Коли люди повідомляють, що багато інструментів шкодять якості, механізм зазвичай у тому, що визначення витіснили важливий context — тобто це проблема Розділу 24 у костюмі Розділу 18. Інструменти, які справді майже дублюють один одного, теж є реальною проблемою, і виправлення для них — не менше інструментів, а кращі описи й namespaces: префіксуйте їх за системою (crm.search_customer, billing.search_customer), щоб два каталоги, злиті з двох команд, не конфліктували, і щоб модель мала за що зачепитися для розрізнення.
Три види інструментів і той, що відкриває наступну частину
Посилання на розділ: Три види інструментів і той, що відкриває наступну частинуКорисно сортувати інструменти за тим, що вони роблять зі світом, бо інженерія для кожного відрізняється.
Інструменти даних читають: шукають, отримують, запитують. Їх можна повторювати, паралелити, кешувати. Вони провалюються, повертаючи нічого корисного, а їхній головний ризик у тому, що вони приносять недовірений текст у context — а це вся поверхня атаки Розділу 30.
Інструменти дій записують: надсилають, створюють, списують, видаляють. Їх не можна безпечно повторювати без ключа, не можна безпечно паралелити, і саме через них існують approval flows.
Інструменти оркестрації викликають інші моделі. Інструмент, реалізація якого — інший agent, із власним prompt, власними інструментами й власним циклом, — а для моделі, що його викликає, він виглядає точно так само, як перші два, бо схема й endpoint — це все, що вона коли-небудь бачить.
Третій вид — не цікавинка. Це механізм за половиною agent-as-a-tool у Розділі 25 — інша топологія, handoff, віддає розмову й ніколи не отримує її назад — і він працює саме тому, що інтерфейс у цьому розділі достатньо вузький, щоб за ним помістився цілий agent.
Куди це веде далі
Посилання на розділ: Куди це веде даліТепер у вас є модель, яка може просити про речі, і контракт, який робить це прохання придатним для парсингу. Чого у вас немає — так це чогось, про що вона може питати, крім того, що вміщується в її prompt.
Найпоширеніший інструмент у продакшені, з великим відривом, — це пошук у масиві тексту, якого модель ніколи не бачила під час навчання: ваша документація, ваші тікети, ваші контракти. Це звучить як розв’язана проблема — embed, знайти найближчих сусідів, вставити їх, — але нерозв’язані частини саме ті, що визначають, чи можна довіряти відповіді: як текст ріжеться перед embedding, який поріг схожості достатньо низький, щоб означати я не знаю, і як цитата прикріплюється до твердження, щоб читач міг його перевірити.
Розділ 19 — про retrieval, і це розділ, у якому неправильна відповідь перестає бути цікавинкою й стає відповідальністю.
Джерела й метод
Посилання на розділ: Джерела й методВимірювання в цьому розділі походять із Qwen/Qwen2.5-0.5B-Instruct з greedy decoding, на 24 згенерованих запитах, що поєднують шість пар міст із чотирма формулюваннями дат, використовуючи власний chat template моделі для визначень інструментів. Вони відтворюються точно, і це мала модель: читайте розділення format/value як демонстрацію механізму, а не як benchmark того, що роблять сучасні моделі. Frontier model правильно розв’язує "next Friday" значно частіше — і все одно schema не може змусити її це зробити, а саме ця частина узагальнюється.
Словник JSON Schema, використаний вище (type, properties, required, pattern, format, enum), визначено в draft JSON Schema, який називає документація вашого провайдера; корисна підмножина мала й однакова між провайдерами, а відмінності, які справді існують — які keywords забезпечуються constrained decoding, а не просто передаються моделі, — варто прочитати в guide провайдера зі structured output, а не припускати.
Щодо constrained decoding як техніки, бібліотеки guidance-стилю та проєкт outlines документують побудову grammar-to-logit-mask так, що вона прямо мапиться на семплер із Розділу 17. А для самого кругового проходу найчіткіша специфікація — не tutorial, а протокол: Розділ 26 читає його рядок за рядком.
Примітки
Посилання на розділ: Примітки-
Ouyang, L. et al. Training language models to follow instructions with human feedback. arXiv:2203.02155 (2022). Стаття, яка зробила рецепт post-training стандартом; форма tool call вивчається там із демонстрацій — точно так само, як форма відповіді. ↩