Tool use і function calling
Зміст
LLM — це генератор тексту. Вона не має доступу до реального світу: не знає поточної дати, не бачить вашого баг-трекера, не вміє запустити тест і подивитися, чи він зелений. Усе, що вона «знає», зафіксоване на момент навчання (knowledge cutoff — див. /ai/iak-pratsiuiut-llm), а решту вона просто вигадує правдоподібно. Tool use (використання інструментів), він же function calling (виклик функцій), — це механізм, який дає моделі руки: спосіб попросити ваш код виконати конкретну дію і повернути результат.
Для AQA це фундамент усього стека. Claude Code, що читає файли й запускає тести; -сервер, що керує браузером; асистент, що заводить баг у Jira — усе це той самий цикл «модель попросила інструмент → код виконав → модель побачила результат». Хто розуміє цей цикл, той розуміє, чому агент іноді «застряє», чому вигадує неіснуючий метод замість того, щоб покликати інструмент, і де проходить межа відповідальності між моделлю і вашим кодом.
Чому модель не виконує функції сама
Головна пастка — у назві. «Function calling» звучить так, ніби модель сама викликає ваші функції. Це не так. Модель не виконує коду — ані вашого, ані свого. Усе, що вона робить, — генерує токени.
Що відбувається насправді: ви разом із запитом передаєте моделі список інструментів (їхні описи). Модель, замість звичайної текстової відповіді, може згенерувати структурований JSON: «я хочу викликати інструмент create_bug з аргументами {"title": "...", "severity": "major"}». На цьому її участь закінчується. Далі ваш код (той, що крутить цикл) вирішує, чи виконувати запит, реально виконує функцію й повертає моделі результат. Модель його «бачить» і продовжує.
Ця межа — не деталь реалізації, а суть безпеки. Модель нічого не запускає. Вона лише пропонує. Виконує — ваш код, і саме він відповідає за валідацію, дозволи й наслідки. Тому «AI видалив прод» — це завжди про те, що хтось дав інструменту з правом видалення виконуватися без підтвердження, а не про те, що «модель сама вирішила».
Одне важливе уточнення про те, чий саме це код. Сказане вище точне для інструментів, які визначаєте ви, — дока Anthropic називає їх client tools і прямо каже, що вони виконуються у вашому застосунку (сюди ж належать інструменти з наперед визначеною схемою на кшталт bash і text_editor). Але є й другий рід — server tools (web_search, web_fetch, code_execution, tool_search): їх виконує сам провайдер на своїй інфраструктурі, а ви «бачите результати напряму, не обробляючи виконання». Межа відповідальності від цього не зникає, вона зсувається: у продукті з server tools частина дій відбувається поза вашим кодом, тож ваша модель має враховувати й те, чого ваш цикл не бачить, — наприклад, що пошук у вебі стався, а ви його не логували.
Опис інструмента: ім'я, опис, JSON-схема
Інструмент описують трьома частинами:
- Ім'я (name) — унікальний ідентифікатор, за яким код розрізняє інструменти:
get_test_case,create_bug,run_playwright. - Опис (description) — текст природною мовою, який пояснює, що інструмент робить і, головне, коли його викликати. Це фактично для інструмента.
- JSON-схема ( schema) — опис аргументів: типи полів, які обов'язкові, які значення допустимі (), опис кожного поля.
const createBug = {
name: "create_bug",
description:
"Заводить баг у трекер. Виклич, коли користувач описав дефект " +
"і явно попросив його зафіксувати.",
input_schema: {
type: "object",
properties: {
title: { type: "string", description: "Короткий заголовок дефекту" },
severity: {
type: "string",
enum: ["blocker", "critical", "major", "minor"],
description: "Ступінь впливу",
},
steps: { type: "string", description: "Кроки відтворення" },
},
required: ["title", "severity"],
},
};
Ключова думка про опис: модель вибирає інструмент за описом, а не за назвою. Розмитий опис → модель кличе інструмент не тоді, коли треба, або не кличе взагалі. На сучасних моделях опис має бути прескриптивним: не лише «що робить», а «коли викликати» — на кшталт «Виклич, коли користувач питає статус конкретного тесту».
JSON-схема — це контракт. required каже, які поля модель мусить заповнити; enum обмежує значення набором; опис поля підказує формат. Ті самі принципи, що й у схемах для API-тестування (розділ «API-тестування»): required проти опціонального, типи, обмеження. Різні провайдери називають поля трохи по-різному (в одних input_schema, в інших — parameters), але всередині це та сама JSON Schema.
Цикл виклику
Function calling — це не одноразовий обмін, а цикл, яким керує ваш код, а не модель. Кроки:
- Ви надсилаєте моделі: + повідомлення + список інструментів.
- Модель відповідає. Або звичайним текстом (готова відповідь), або запитом на виклик інструмента. Ознака другого — окремий статус завершення (наприклад,
stop_reason: "tool_use"). У відповіді — ім'я інструмента та аргументи (JSON). - Ваш код парсить запит, виконує реальну функцію, дістає результат.
- Ви повертаєте результат моделі окремим повідомленням —
tool_result, прив'язаним доidтого виклику. - Модель бачить результат і продовжує: або дає фінальну текстову відповідь, або просить наступний інструмент. Цикл крутиться, доки модель не завершить (
stop_reason: "end_turn").
Ось той самий цикл кодом — мінімальний, але робочий кістяк:
let messages = [{ role: "user", content: userInput }];
while (true) {
const res = await model.create({ tools, messages });
messages.push({ role: "assistant", content: res.content });
if (res.stop_reason !== "tool_use") break; // фінальна відповідь
const results = [];
for (const call of res.content.filter((b) => b.type === "tool_use")) {
const output = await runTool(call.name, call.input); // виконує ВАШ код
results.push({ type: "tool_result", tool_use_id: call.id, content: output });
}
messages.push({ role: "user", content: results });
}
Важлива деталь: історія розмови накопичується, і API без стану (stateless). Ви щоразу надсилаєте всю розмову цілком — включно з попередніми викликами інструментів і їхніми результатами. Модель нічого «не пам'ятає» між запитами; пам'ять — це те, що ви їй передаєте (а разом з нею росте кількість токенів і вартість — див. /ai/tokeny-kontekst-i-vartist).
Structured output і валідація
Той самий механізм інструментів — це основа структурованого виводу (structured output). Якщо вам треба, щоб модель повернула дані у фіксованій формі (наприклад, розпарсити вільний баг-репорт у поля title / severity / steps), ви описуєте інструмент зі схемою й змушуєте модель його викликати — і на виході отримуєте JSON за схемою.
Але без строгого режиму схема — це сильна підказка, а не гарантія: модель може повернути значення поза enum, пропустити обов'язкове поле чи зіпсувати тип. Виглядає як валідний JSON, а насправді severity там "Critical", коли схема чекала на "critical". Строгий режим (strict: true) прибирає саме ці структурні порушення — вивід обмежується схемою через grammar-constrained sampling, тож тип, enum і обов'язкові поля гарантовано збігаються. Але й тоді валідуйте на своєму боці (ajv, zod): схема стежить за формою, не за змістом — значення в межах enum усе одно може бути неправильним по суті, а строгий режим підтримує лише підмножину JSON Schema.
Практичний наслідок для QA: якщо ви генеруєте тест-артефакти у структурованому форматі (див. /ai/veryfikatsiia-rezultativ-ai) — не довіряйте формі наосліп. Валідуйте так само, як валідували б відповідь чужого API.
Помилки інструментів
Помилки бувають двох родів.
Модель покликала погано. Вигадала неіснуючий інструмент, передала аргумент не того типу, пропустила обов'язкове поле. Тут рятує схема (строгий режим ловить частину помилок) і валідація перед виконанням.
Реальна функція впала. API повернуло 500, , «не знайдено». Тут ключове правило: не обривайте цикл. Поверніть помилку як tool_result з ознакою is_error: true — і модель прочитає її та адаптується (спробує інші аргументи, інший підхід або чесно скаже користувачу, що не вийшло). Якщо ви замість цього кинете виняток, цикл обірветься на півдорозі — і агент просто «замовкне».
Дві додаткові дисципліни:
- Ліміт ітерацій. Модель може зациклитися: кличе інструмент → падає → кличе знову. Обмежуйте кількість кроків, інакше цикл крутитиметься нескінченно й палитиме токени та гроші.
- Аргументи інструмента — недовірений вхід. Це вивід моделі, а не ваш код. Інструмент, що виконує shell або SQL з аргументів моделі, — це вектор . Валідуйте перед виконанням так само параноїдально, як користувацький ввід.
Паралельні виклики
Модель може попросити кілька інструментів в одній відповіді — коли вони незалежні. «Дай статус тесту A і тесту B» → два виклики get_test_case одразу. Ваш код виконує їх — можна конкурентно (concurrency), тобто з перекриттям у часі, — і повертає всі результати разом, в одному повідомленні.
Дві межі:
- Паралель має сенс лише для незалежних викликів. Якщо результат першого потрібен для аргументів другого (створити баг → прикріпити його
idдо тесту), виклики мусять бути послідовними. - Повертайте всі
tool_resultоднією порцією. Якщо розбити їх на кілька окремих повідомлень, ви «привчаєте» модель не робити паралельних викликів — вона бачить, що формат ламається, і в наступних кроках переходить на послідовні виклики.
Де це в житті QA
Tool use — не абстракція, а те, з чим AQA стикається щодня, навіть не називаючи це так.
- Агенти. Агент (див. /ai/ahenty-i-agentic-loop) — це і є цикл інструментів, обгорнутий у план. Claude Code читає файли, запускає тести, редагує код — усе це виклики функцій
Read,Bash,Edit. - MCP. Model Context Protocol (див. /ai/mcp-protokol-servery-instrumenty) — це стандартизований tool use. Playwright MCP віддає моделі дії браузера (клік, введення тексту, ) як інструменти.
- Генерація артефактів. через інструмент — надійний спосіб отримати тест-кейси чи у формі, яка одразу лягає в TMS.
- Тестування застосунків із function calling. Якщо продукт, який ви тестуєте, сам використовує інструменти (чат-бот, що заводить заявки), то ваш об'єкт перевірки — уже не лише текст. Ви тестуєте: чи модель кличе правильний інструмент, чи коректно заповнює схему, чи оброблено помилку інструмента, що буде за паралельних викликів. Це місток до /ai/testuvannia-ai-zastosunkiv і /ai/evals-ta-llm-as-judge.
Типові помилки
- Виглядає як «модель сама виконала функцію», а насправді вона лише згенерувала запит на виклик — виконав ваш код, і саме він відповідає за наслідки.
- Виглядає як баг агента (не кличе потрібний інструмент), а насправді розмитий або неповний опис інструмента: модель не зрозуміла, коли його застосувати.
- Виглядає як валідний structured output, а насправді значення поза схемою (severity не з enum, зайве поле) — бо схема це підказка, а не гарантія без валідації.
- Виглядає як «агент завис» після помилки інструмента, а насправді код кинув виняток і обірвав цикл замість повернути
tool_resultзis_error, який модель могла б обробити. - Виглядає як розумний вибір моделі не робити паралельні виклики, а насправді ви розбили
tool_resultна кілька повідомлень і привчили її до послідовності.
Підсумок
- Модель не виконує функцій — вона генерує структурований запит на виклик; ваші інструменти (client tools) виконує і відповідає за наслідки ваш код. Виняток за родом — server tools (
web_search,web_fetch,code_execution,tool_search): їх виконує провайдер, і ви отримуєте вже результат. - Інструмент = ім'я + опис + JSON-схема; модель вибирає його за описом, тож опис має казати, коли його кликати.
- Цикл «запит →
tool_use→ виконання →tool_result→ продовження» крутить ваш код, доки модель не завершить; історія передається щоразу цілком. - Помилку інструмента повертайте моделі (
is_error), а не обривайте цикл винятком; став ліміт ітерацій і валідуй аргументи як недовірений вхід. - Structured output — це той самий механізм інструментів; схема не звільняє від валідації результату.
Можливі питання
- «Чим function calling відрізняється від того, що модель просто пише код?» Дивляться, чи розумієте, що модель не виконує нічого — лише повертає структурований запит, а виконує зовнішній код.
- «Опишіть цикл виклику інструмента.» Чекають на послідовність: інструменти в запиті →
tool_use→ виконання на вашому боці →tool_resultзаid→ повтор доend_turn. - «Що станеться, якщо інструмент упаде посеред роботи агента?» Перевіряють, чи знаєте про повернення помилки моделі (
is_error) і про ліміт ітерацій замість обірваного циклу. - «Як ви тестували б чат-бота, що заводить заявки через інструменти?» Хочуть побачити перевірки: правильний інструмент, коректна схема, обробка помилок, паралельні та послідовні виклики, недетермінізм.
- «Чи гарантує JSON-схема правильний вивід?» Червоний — сказати «так». Правильно: схема сильно допомагає, але результат усе одно валідують.
Джерела
Чому модель не виконує функції сама
- Claude Platform Docs — Tool use with Claude (overview) — джерело поправки про два роди інструментів:
client toolsвиконує ваш застосунок,server tools— провайдер на своїй інфраструктурі. - OpenAI Docs — Function calling — виклик інструмента як особливий рід відповіді моделі: ваш код виконує дії, які модель лише запропонувала.
- OWASP — LLM06:2025 Excessive Agency — межа відповідальності нормативно: шкідливі дії стають можливими незалежно від причини несподіваного виводу моделі.
Опис інструмента: ім'я, опис, JSON-схема
- Claude Platform Docs — Tool use with Claude (overview) — три частини опису (
name,description,input_schema) іrequiredу схемі; модель вирішує, коли кликати, спираючись на запит і опис інструмента. - OpenAI Docs — Function calling — опис як головний важіль: описувати треба, коли і коли НЕ застосовувати функцію; схему проєктують так, щоб невалідні стани були непредставними.
- JSON Schema Validation, draft 2020-12 — що саме перевіряє
enum: рівність одному з елементів масиву, і нічого понад це. - ISTQB CT-GenAI Syllabus v1.1 — вендор-нейтральне означення інструмента: предвизначений набір функцій, викликом яких агент «діє».
- OpenAI Docs — Function calling — пʼять кроків потоку дослівно: запит з інструментами → виклик інструмента → виконання на боці застосунку → другий запит із результатом → фінальна відповідь.
- Claude Platform Docs — Tool use with Claude (overview) — форма відповіді для client-інструментів:
stop_reason: "tool_use", блокиtool_use, зворотнийtool_resultі прив'язка черезtool_use_id. - Claude Platform Docs — Context windows — чому історію надсилають цілком щоразу: попередні ходи зберігаються повністю, а вхідна фаза кожного ходу містить усю попередню розмову.
- OpenAI Docs — Function calling — без строгого режиму виклики
best effort;strict: trueробить їх відповідними схемі, але це підмножина JSON Schema з нормативними вимогами до неї. - Claude Platform Docs — Tool use with Claude (overview) — те саме поле в другого провайдера:
strict: trueгарантує точний збіг викликів зі схемою. - JSON Schema Validation, draft 2020-12 — чому схема стежить за формою, а не за змістом:
enumперевіряє рівно рівність одному з елементів масиву. - JSON Schema — головна сторінка — екосистема валідаторів, генераторів і лінтерів, якою й роблять перевірку на своєму боці.
- OWASP Gen AI Security Project — Top 10 for LLM Applications (2025) — нескінченний цикл має власний рядок у переліку :
LLM10:2025 Unbounded Consumption. - Claude Code Docs — Create custom subagents — ліміт ітерацій існує як поле конфігурації (
maxTurns), після якого агент зупиняється. - OWASP — LLM01:2025 Prompt Injection — чому аргументи інструмента небезпечні: вимога валідувати очікувані формати детермінованим кодом і відокремлювати недовірений контент.
- OpenAI Docs — Function calling — кілька викликів за хід є нормою:
you must execute it and return the result, іit is best practice to assume there are several. - Claude Platform Docs — Tool use with Claude (overview) — паралельність вимикають явно полем
disable_parallel_tool_use, тобто за замовчуванням вона є.
- Model Context Protocol — What is the Model Context Protocol (MCP)? — що стандартизує MCP: зʼєднання AI-застосунків із зовнішніми системами, даними й .
- Model Context Protocol — Architecture overview — , який тут важить:
toolsяк виконувані функції, що їх AI-застосунок може викликати для дій. - Anthropic Engineering — Building Effective Agents — чому агент — це і є цей цикл: LLM, що використовує інструменти на основі зворотного звʼязку від середовища.
- ISTQB CT-GenAI Syllabus v1.1 — вендор-нейтральна рамка: саме здатність «діяти» через предвизначені функції відрізняє агента від чат-бота.
Що таке tool use (function calling) і яку проблему воно розвʼязує?
LLM уміє лише генерувати текст на основі того, що бачила під час навчання: поточної дати, вашого баг-трекера чи стану CI вона не знає, а решту правдоподібно вигадує. Tool use дає моделі спосіб попросити зовнішній код зробити конкретну дію — сходити в API, прочитати файл, запустити тест — і повернути результат назад у діалог. Механізм такий: разом із запитом ви передаєте моделі перелік доступних інструментів, і замість тексту вона може згенерувати структурований запит «поклич ось цей інструмент з такими аргументами». Практичний наслідок для AQA: увесь стек (Claude Code, -сервери, чат-боти, що заводять заявки) стоїть саме на цьому циклі, тож розуміння tool use — це розуміння того, чому агент іноді застрягає чи вигадує неіснуючий метод замість виклику.
Чому назва «function calling» оманлива?
Бо звучить так, ніби модель сама запускає ваш код, а це не так. Модель нічого не виконує — ні ваших функцій, ні власних; єдине, що вона робить, — генерує токени. Коли їй дали список інструментів, вона може згенерувати запит «хочу викликати інструмент X з аргументами Y» — і на цьому її роль закінчується. Далі вирішує й виконує ваш код: чи запускати функцію взагалі, як її виконати, що повернути. Ця межа не косметична — саме тому за валідацію, дозволи й наслідки відповідає ваш код, а не «модель, яка сама вирішила». Сильна відповідь додасть уточнення: це точно для client tools, тобто ваших власних інструментів; окремим родом стоять server tools (web_search, web_fetch, code_execution, tool_search), які виконує провайдер на своїй інфраструктурі — ви отримуєте вже результат, не обробляючи виконання.
З чого складається опис інструмента?
З трьох частин: імʼя, опис і схема аргументів. Імʼя (name) — унікальний ідентифікатор, за яким ваш код розрізняє інструменти (create_bug, get_test_case). Опис (description) — текст природною мовою, що пояснює не лише що інструмент робить, а й коли його кликати; фактично це міні- для моделі. Схема аргументів ( schema або parameters) — це JSON Schema: типи полів, які обовʼязкові (required), які значення допустимі (enum), опис кожного поля. Різні провайдери називають зовнішнє поле по-різному, але всередині це та сама JSON Schema.
Чому кажуть, що модель обирає інструмент за описом, а не за назвою?
Бо саме текст опису — головний сигнал, за яким модель вирішує, чи цей інструмент підходить під поточний запит користувача. Розмитий чи неповний опис призводить до того, що модель кличе інструмент не до речі або взагалі його ігнорує, — і зовні це виглядає як баг агента. На сучасних моделях опис має бути прескриптивним: не просто «заводить баг», а «виклич, коли користувач описав дефект і попросив його зафіксувати». Практичний наслідок: якщо агент «не бачить» потрібного інструмента, першими правлять не модель, а опис.
Опишіть цикл виклику інструмента крок за кроком.
Крок 1: у запиті до моделі їдуть , історія повідомлень і перелік доступних інструментів. Крок 2: модель відповідає — або готовим текстом, або запитом на виклик інструмента; ознака другого — окремий статус завершення, наприклад stop_reason: "tool_use", і в тілі імʼя інструмента з аргументами. Крок 3: ваш код розбирає цей запит і запускає справжню функцію. Крок 4: її результат їде назад до моделі як tool_result із посиланням на id виклику. Крок 5: модель бачить результат і або дає фінальну відповідь (end_turn), або просить наступний інструмент — цикл крутиться, доки не завершиться. Ключове: цим циклом керує ваш код, а не модель.
Хто керує циклом і чому це важливо для безпеки?
Циклом керує ваш код — модель лише реагує на кожному кроці. Це важливо, бо ваш код стоїть між «модель попросила» і «дія сталася»: він валідує аргументи, перевіряє дозволи, вирішує, чи потрібне підтвердження людини перед небезпечною операцією. Тому інцидент «AI видалив прод» — це не про те, що модель сама вирішила видаляти, а про те, що інструменту з правом видалення дозволили виконуватися без перевірки. Для QA це означає, що обʼєкт перевірки — не лише вибір моделі, а й обгортка навколо інструмента: валідація, дозволи, ліміти.
Чому tool use API називають stateless і що це означає для вартості?
Бо сервер моделі не памʼятає попередніх кроків: щоб модель «бачила» історію, ви щоразу надсилаєте весь діалог цілком — включно з минулими викликами інструментів і їхніми результатами. Памʼять діалогу — це те, що передаєте ви, а не те, що зберігає модель. Наслідок: із кожним кроком циклу історія росте, а разом з нею — кількість вхідних токенів і вартість запиту. Тому довгі з великими tool_result (наприклад, увесь лог тесту) швидко роздувають контекст — це те, що варто тримати в голові, оцінюючи вартість автоматизації.
Як через механізм інструментів отримати структурований вивід (structured output)?
Structured output — це той самий tool use, повернутий іншим боком. Замість дії ви описуєте інструмент зі схемою потрібної структури (наприклад, поля title / severity / steps) і змушуєте модель його викликати — на виході дістаєте JSON за цією схемою. Типовий кейс для QA: розпарсити вільний баг-репорт у поля або згенерувати тест-кейси у формі, що одразу лягає в TMS. Але поки не ввімкнено строгий режим, схема лише скеровує модель, нічого не гарантуючи: у виводі трапляються значення поза enum, пропущені обовʼязкові поля, зламані типи.
Чи гарантує JSON-схема, що модель поверне валідні дані?
Ні — і на співбесіді відповідь «так» це червоний . Без строгого режиму схема лише орієнтує модель, а результат може виглядати як валідний JSON, але містити "Critical" замість очікуваного "critical", зайве поле чи відсутнє обовʼязкове. Строгий режим (strict: true) прибирає структурні порушення — тип, enum і обовʼязкові поля збігаються, бо генерацію обмежують схемою. Але навіть тоді валідуйте на своєму боці: схема стежить за формою, а не за змістом (значення в межах може бути неправильним по суті), плюс строгий режим підтримує лише підмножину JSON Schema.
Що робить строгий режим (strict mode) і що саме він гарантує?
Строгий режим обмежує генерацію моделі так, щоб вивід структурно відповідав схемі — це роблять через grammar-constrained sampling, коли модель фізично не може згенерувати токен, що ламає схему. Він гарантує форму: правильний тип поля, значення в межах enum, наявність обовʼязкових полів. Чого він не гарантує — семантику: модель може обрати правильний за форматом, але хибний по суті severity. Плюс обмеження: строгий режим покриває лише підмножину JSON Schema, тож не кожна схема в нього вкладається. Тому валідація на своєму боці (ajv, zod) лишається обовʼязковою.
Які два роди помилок бувають при роботі з інструментами?
Перший — модель покликала погано: попросила інструмент, якого не існує, дала аргументу чужий тип або лишила обовʼязкове поле порожнім. Проти цього працюють схема (строгий режим відсікає частину порушень) і валідація перед виконанням. Другий — реальна функція впала: API віддало 500, стався , ресурс не знайдено. Тут головне правило — не обривати цикл, а повернути помилку моделі як tool_result з ознакою is_error, щоб вона могла адаптуватися. Це дві різні площини: перша про контракт «модель проти схеми», друга про надійність вашої інтеграції.
Інструмент упав посеред роботи агента. Як правильно вчинити?
Не кидати виняток, що обірве цикл, а повернути помилку моделі як tool_result з is_error: true і текстом, що саме сталося. Тоді модель прочитає помилку й адаптується: спробує інші аргументи, обере інший підхід або чесно скаже користувачу, що не вдалося. Якщо ж код кине виняток і зупинить цикл, агент просто «замовкне» посеред роботи — і зовні це виглядає як зависання, хоча причина в обробці помилки. Додатково варто мати ліміт ітерацій, щоб модель не зациклилася на «кличу → падає → кличу знову».
Навіщо в агентному циклі ліміт ітерацій?
Бо модель може зациклитися: покликати інструмент, отримати помилку, покликати знову з тими самими аргументами — і так нескінченно. Без обмеження кількості кроків цикл крутитиметься вічно, палячи токени й гроші, а в CI ще й впираючись у таймаут . Ліміт ітерацій — це запобіжник, який зупиняє агента після N кроків і дає керовано завершити («не впорався за N спроб»). Для QA це один з інваріантів, який варто перевіряти в агентних продуктах: що є стеля кроків і що система коректно поводиться, досягнувши її.
Чому аргументи інструмента — це недовірений вхід?
Бо аргументи генерує модель, а не ваш код, і на них впливає все, що потрапило в контекст — включно з текстом користувача чи навіть вмістом сторінки, яку модель прочитала. Тул, що виконує shell-команду або SQL прямо з аргументів моделі, — це готовий вектор : досить підсунути моделі шкідливий текст, і вона згенерує небезпечні аргументи. Тому валідувати їх треба так само параноїдально, як користувацький ввід: перевірка типів, , екранування, а для небезпечних операцій — підтвердження людини. Це прямий місток до тем безпеки й prompt injection.
Що таке паралельні виклики інструментів і коли вони доречні?
Це коли модель в одній відповіді просить кілька інструментів одразу — доречно, коли виклики незалежні. Приклад: «дай статус тесту A і тесту B» — два виклики get_test_case, які ваш код може виконати конкурентно й повернути результати разом. Межа: коли другий виклик не може стартувати без результату першого (спершу створити баг, потім прикріпити отриманий id до тесту), паралель неможлива — лишається послідовність. Тобто модель сама вирішує розпаралелити незалежні дії, але коректність залежностей — на вашому боці дизайну інструментів.
Модель перестала робити паралельні виклики. Це вона «порозумнішала»?
Найчастіше ні — це наслідок того, що ваш код розбив tool_result на кілька окремих повідомлень замість повернути їх однією порцією. Коли модель просить кілька інструментів разом, усі результати треба повернути в одному повідомленні, привʼязавши кожен до свого id. Якщо ж їх слати частинами, модель бачить, що формат ламається, і в наступних кроках «вчиться» переходити на послідовні виклики. Тобто це не про інтелект моделі, а про те, як ваш код зібрав відповідь — типова пастка «виглядає як розумний вибір, а насправді зіпсований формат».
Як ви тестували б чат-бота, що заводить заявки через інструменти?
Обʼєкт перевірки тут не лише текст відповіді, а й поведінка навколо інструментів. Перевіряю: чи модель кличе правильний інструмент на відповідний запит (і не кличе, коли не треба); чи коректно заповнює схему (обовʼязкові поля, значення в межах enum, правильні типи); як оброблено помилку інструмента (API впало — бот адаптувався чи завис); що буде за паралельних і послідовних викликів; і чи система стабільна при недетермінізмі (той самий запит → той самий вибір інструмента). Оскільки модель недетермінована, тут ідуть не жорсткі на текст, а evals і LLM-as-judge — це місток до відповідних тем.
Де AQA стикається з tool use щодня, навіть не називаючи це так?
Практично скрізь в агентному стеку. Агент — це і є цикл інструментів, обгорнутий у план: Claude Code читає файли, запускає тести, редагує код через виклики Read, Bash, Edit. MCP (Model Context Protocol) — це стандартизований tool use: Playwright MCP віддає моделі дії браузера (клік, ввід, ) як інструменти. Генерація тест-артефактів через — теж цей механізм. І якщо продукт, який ви тестуєте, сам використовує інструменти, function calling стає безпосереднім обʼєктом ваших перевірок.
«AI видалив прод» — модель сама вирішила це зробити?
Ні. Модель не виконує дій — вона лише згенерувала структурований запит «поклич інструмент видалення з такими аргументами». Реально видалив ваш код, який виконав цей запит без валідації, перевірки дозволів чи підтвердження людини. Тому корінь інциденту завжди в обгортці: небезпечному інструменту дозволили виконуватися автоматично, без запобіжників. Правильна архітектура тримає руйнівні операції за підтвердженням або звужує права інструмента — і саме це, а не «поведінку моделі», має перевіряти QA.
Три кейси, де tool use перестає бути абстракцією: читання виклику (що каже кожне повідомлення в обміні), правильна обробка помилки інструмента (повернути моделі, а не впасти) і те, що саме перевіряти в чат-боті з function calling. Скрізь — що дивитися і чому.
Кейс 1. Читаємо трейс виклику: що каже кожне повідомлення
Чат-бот заводить баги. Користувач пише баг-репорт вільним текстом, а нас цікавить не фінальна фраза бота, а весь обмін під капотом. Ось як виглядає повний трейс однієї заявки (спрощено, у стилі Anthropic Messages):
[
{ "role": "user", "content": "Кнопка Save не працює на мобільному, застосунок падає. Заведи баг." },
{ "role": "assistant", "stop_reason": "tool_use", "content": [
{ "type": "text", "text": "Заводжу дефект." },
{ "type": "tool_use", "id": "toolu_01", "name": "create_bug",
"input": { "title": "Save не працює на мобільному, застосунок падає", "severity": "critical" } }
]},
{ "role": "user", "content": [
{ "type": "tool_result", "tool_use_id": "toolu_01",
"content": "{\"id\": \"BUG-4213\", \"url\": \"https://tracker/BUG-4213\"}" }
]},
{ "role": "assistant", "stop_reason": "end_turn", "content": [
{ "type": "text", "text": "Готово: BUG-4213 (critical)." }
]}
]
Що дивитися і чому:
stop_reason: "tool_use"на другому повідомленні — це ознака, що модель попросила інструмент, а не дала фінальну відповідь. У тесті іде не на текст «Заводжу дефект», а на блокtool_use: йогоnameіinput.inputпроти схеми.titleзаповнено,severity— у межах (critical), обовʼязкові поля на місці. Це те, що перевіряє тест схеми. Зверніть увагу: модель сама вивелаseverity: criticalз фрази «застосунок падає», хоча користувач ступеня впливу не називав.tool_resultпривʼязаний доid.tool_use_id: "toolu_01"посилається на конкретний виклик — без правильного звʼязку модель не зрозуміє, на що це відповідь. У трейсі це перше, що звіряють, коли «модель ігнорує результат інструмента».- Останнє повідомлення без
tool_use— цеend_turn, цикл завершено. Якби тут знову зʼявивсяtool_use, модель просила б наступний крок, і цикл крутився б далі.
Кейс 2. Помилка інструмента: повернути моделі, а не впасти
Трекер лежить, API віддає 500. Різниця між «агент завис» і «агент упорався» — у тому, як ваш код обробив цю помилку.
// НЕПРАВИЛЬНО: виняток обриває цикл, і агент "замовкне"
async function runToolBad(name: string, input: unknown) {
const res = await tracker.createBug(input); // кине, якщо 500
return JSON.stringify(res);
}
// ПРАВИЛЬНО: помилку повертаємо моделі як tool_result з is_error
async function runTool(name: string, input: unknown) {
try {
const res = await tracker.createBug(input);
return { content: JSON.stringify(res), is_error: false };
} catch (e) {
return { content: `Трекер недоступний: ${String(e)}`, is_error: true };
}
}
Сам цикл — з лімітом ітерацій, щоб модель не крутилася вічно, і зі збором усіх результатів в одне повідомлення:
let messages = [{ role: "user", content: userInput }];
const MAX_STEPS = 10;
for (let step = 0; step < MAX_STEPS; step++) {
const res = await model.create({ tools, messages });
messages.push({ role: "assistant", content: res.content });
if (res.stop_reason !== "tool_use") break; // фінальна відповідь
const results = [];
for (const call of res.content.filter((b) => b.type === "tool_use")) {
const out = await runTool(call.name, call.input);
results.push({
type: "tool_result",
tool_use_id: call.id,
content: out.content,
is_error: out.is_error,
});
}
messages.push({ role: "user", content: results }); // усі результати однією порцією
}
Що дивитися і чому:
is_error: trueдає моделі шанс адаптуватися — повторити з іншими аргументами чи чесно сказати користувачу «трекер недоступний». Виняток такого шансу не лишає: цикл обірветься, і зовні це виглядатиме як зависання .MAX_STEPS— запобіжник від зациклення «кличу → падає → кличу знову». Без нього цикл крутиться, доки не впреться в CI й не спалить токени.- Результати однією порцією. Усі
tool_resultідуть у єдиному повідомленні. Розібʼєте на кілька — і модель поступово перестане робити паралельні виклики.
Кейс 3. Що саме перевіряти в чат-боті з інструментами
Коли продукт сам користується інструментами, обʼєкт перевірки — не текст відповіді, а вибір і аргументи інструмента. Ось карта перевірок:
| Перевірка | Що асертимо | Пастка |
|---|---|---|
| Вибір інструмента | на «заведи баг» модель кличе create_bug, а не відповідає текстом | розмитий опис → інструмент не кличеться або кличеться не той |
| Заповнення схеми | обовʼязкові поля є, severity у межах enum, типи правильні | без strict модель віддасть "Critical" замість "critical" |
| Негатив: не кликати | на «як справи?» інструмент не викликається | over-eager модель кличе інструмент там, де не треба |
| Помилка інструмента | API впало → бот повідомляє, а не зависає | код кинув виняток замість is_error |
| Залежні виклики | «створи баг і прикріпи до тесту» йде послідовно, id з першого — у другий | спроба розпаралелити залежні дії |
| Недетермінізм | той самий запит → стабільно правильний інструмент на N прогонів | одиничний зелений прогін нічого не доводить |
Мінімальний тест дивиться на сам виклик, а не на фінальну фразу:
import { test, expect } from '@playwright/test';
test('на баг-репорт бот кличе create_bug з валідною схемою', async () => {
const res = await agent.step('Save не працює на мобільному, застосунок падає. Заведи баг.');
const call = res.content.find((b) => b.type === 'tool_use');
expect(call, 'модель мала попросити інструмент, а не відповісти текстом').toBeTruthy();
expect(call.name).toBe('create_bug');
// схему перевіряємо, як перевіряли б відповідь чужого API
expect(call.input.title).toBeTruthy();
expect(['blocker', 'critical', 'major', 'minor']).toContain(call.input.severity);
});
Що дивитися і чому:
- Асерт на
tool_use, а не на текст. У продукті з function calling обʼєкт перевірки — це вибір і аргументи інструмента, а не формулювання відповіді. - Валідація схеми на своєму боці. Навіть якщо модель зазвичай віддає правильний enum, тест має ловити рідкісні зриви — бо в проді
severityпоза enum зламає інтеграцію з трекером. - Один прогін недостатній. Через недетермінізм такий тест ганяють кілька разів або замінюють на eval-набір із порогом проходження — це вже територія evals і LLM-as-judge.
Суть механізму
- Розумію, що модель не виконує коду — лише генерує структурований запит на виклик; виконує й відповідає за наслідки мій код (тому назва «function calling» оманлива).
- Знаю межу цього твердження: воно точне для моїх інструментів (client tools), а server tools (
web_search,web_fetch,code_execution,tool_search) виконує провайдер — частина дій відбувається поза моїм кодом, і модель це враховує. - Знаю, що циклом керує мій код, а не модель, і чому це основа безпеки (валідація, дозволи, підтвердження перед небезпечною дією).
- Можу пояснити, чому «AI видалив прод» — це про відсутність запобіжника в обгортці інструмента, а не про «рішення моделі».
Опис інструмента
- Знаю три частини опису: імʼя (name), опис природною мовою (description), схема аргументів (JSON Schema).
- Розумію, що модель обирає інструмент за описом, а не за назвою, тож опис має казати не лише «що робить», а й «коли кликати».
- Можу пояснити роль
requiredіenumу схемі й що зовнішнє поле різні провайдери звуть по-різному (input_schemaпротиparameters), але всередині це та сама JSON Schema.
Цикл виклику
- Можу відтворити цикл: запит + інструменти →
tool_use→ виконання на моєму боці →tool_resultзаid→ повтор доend_turn. - Знаю, що ознака запиту на інструмент — окремий статус завершення (
stop_reason: "tool_use"). - Розумію, що API stateless: історію я передаю щоразу цілком, і разом з нею ростуть токени й вартість.
Structured output і валідація
- Розумію, що — це той самий механізм інструментів, повернутий іншим боком.
- Можу пояснити, чому схема без строгого режиму — підказка, а не гарантія (значення поза , пропущене поле, зіпсований тип).
- Знаю, що дає строгий режим (форма: тип, enum, обовʼязкові поля) і чого не дає (семантику), що він покриває лише підмножину JSON Schema, тож результат усе одно валідують на своєму боці (ajv, zod), як відповідь чужого API.
Помилки і безпека
- Знаю два роди помилок: «модель покликала погано» (схема + валідація) і «реальна функція впала» (
is_error). - Можу пояснити, чому помилку інструмента повертають моделі як
tool_resultзis_error, а не кидають виняток, що обриває цикл. - Розумію, навіщо ліміт ітерацій, і що без нього зациклюється й палить токени та гроші.
- Розумію, чому аргументи інструмента — недовірений вхід, і чому інструмент із shell/SQL з аргументів моделі — вектор .
Паралельні виклики
- Можу пояснити, коли доречні паралельні виклики (незалежні дії) і коли лише послідовні (результат першого — аргумент другого).
- Знаю, що всі
tool_resultтреба повертати однією порцією, інакше «привчаєш» модель до послідовних викликів.
Де це в QA
- Розумію, що агент — це цикл інструментів у плані, а — стандартизований tool use (Playwright MCP віддає дії браузера як інструменти).
- Можу назвати, що перевіряти в продукті з function calling: правильний інструмент, коректна схема, обробка помилок, паралельні/послідовні виклики, недетермінізм.
Квіз
Перед стартом
- Питань: 14
- Поріг «зараховано»: ≥70% правильних відповідей.
- Результат впливає на прогрес; завалені питання підуть у чергу повторення.
- Квіз впливає на компліт теми: тема стає «пройдено», лише коли прочитано теорію І квіз складено на ≥70%.
Питання
Tool use (function calling) — що це одним реченням?