Tool Calling на максималках для хард инженеровКак на самом деле работает Tool Calling: от HTTP до парсеровСаша
@polyakovbest затронул тему
неточности на курсах ШАД про tool callingВсе правы но каждый в своём слое, я даже ответил достаточно развернуто в
комментариях Давайте закрепим как это работает по-настоящему, я залезал в кишки vLLM и переписывал парсер для тулов чтобы проследить весь путь
Три слоя одной правдыЛектор ШАД говорит: «тулы передаются через системный промпт». Саша поправляет: «тулы объявляются в отдельной секции API-запроса». Оба правы, но на разных уровнях абстракции.
Вот что происходит на самом деле (см. диаграмму):
Слой 1: HTTP-запросТы отправляешь
tools: [...] отдельной секцией
На уровне API это отдельное поле, не system prompt
Саша тут прав на 1000%.
Слой 2: Chat Template (Jinja2)Вот тут начинается магия
Открываем
tokenizer_config.json любой модели (Qwen3, Hermes и т.д.):
{%- if tools %}
"# Tools\n\nYou may call one or more functions..."
<tools>{tool schemas}</tools>Chat template берёт твои
tools: [...] и рендерит их ПРЯМО в system prompt. Лектор ШАД на этом уровне не врёт (но вводит в заблуждение если не раскрывает сути дальше)
Слой 3: Constrained Decoding (xgrammar)Параллельно JSON-схемы тулов попадают в
guided_decoding=GuidedDecodingParams(json={...})Это xgrammar/outlines — constrained decoding на уровне логитов та магическая штука что заставляет тулы возвращаться не сломанными и со всеми аргументами что вы заложили на старте
Модель физически не может сгенерировать невалидный JSON, маска токенов не даст
Слой 4: Hermes Parser (post-processing)Модель генерит обычный текст:
<tool_call>{"name": "...", "arguments": {...}}</tool_call>Это приходит в поле
contentПарсер Hermes ловит XML-теги, извлекает JSON и перекладывает в структурированный ответ
Важный нюанс:
tool_calls: null,
finish_reason: "stop"Не
tool_use!
Парсер делает всю магию post-processing'ом. Инженерия, да и только.
Почему важно изучать матчасть всю а не по кусочкам? И Саша поднимает очень крутую тему с тем что стоит смотреть чуть чуть дальше доки)
Все банально, залезть в chat template, поднять модельку самому, вывести full логи (это уровень тру llm инженеров) и на этот уровень почти никто не заглядывает, редко вижу где авторы разбирают логиты или темплейты или уж упаси того за ногу запуск чего-то экзотического с разбором инференс движка
На сегодня очень небольшой список тех за кем я слежу и читаю, это те кто лезет настолько глубого в инференс и выкладывает что-то на гитхаб что даже я разбираю их посты с гуглом в обнимку!
https://t.me/evilfreelancer (Паша очень хардовый инженер по обучение запуску и девопсу)
https://t.me/sergeinotevskii (Сергей, лично тоже. наком рулит всей AI платформой в Bitrix)
Но вы будете так же правы если на сегодня не сталкиваетесь с проблемами tool call при работе с облачными моделями, оно вам не текущем этапе жизни точно не нужно
Проприетарные API (Anthropic, OpenAI) прячут всё это серверно ты не видишь ни chat template, ни constrained decoding, ни парсер
Получаешь красивый
tool_calls: [{...}] и
finish_reason: tool_useНо внутри ровно та же цепочка, это просто стоит понимать
Ранее разбирал историю SO + FC + MCP в трёх частях: [
часть 1] | [
часть 2] | [
часть 3]
В коментариях прислал пример полного запроса в LLM когда включен максимальный уровень дебага в vLLM
Считаю базой то что сюда надо заглянуть любому уважающему себя инженеру
Не обязательно переписывать парсер но понимать контракт между API, chat template и constrained decoding нужно
Между инференс-фреймворком и провайдером существует контракт что и куда попадает через chat template
Старайтесь не нарушать его и изучать как он работает
Иначе ты не инженер, а пользователь чужих абстракций