LLM API и интеграция
Function calling: как модель вызывает ваши функции
Function calling — механизм, из-за названия которого возникает половина заблуждений. Модель не вызывает ваши функции и вообще ничего не выполняет: она возвращает структуру с названием функции и аргументами, а решение выполнять принимает ваш код. Это принципиально, потому что именно здесь находится единственная точка контроля во всей конструкции — и именно её чаще всего оставляют без внимания, отдавая модели больше, чем собирались. Разбираем механику по шагам, объясняем, почему описание функции важнее выбора модели, считаем, во что это обходится, и перечисляем места, где схема ломается.
Как это работает на самом деле
Цикл из пяти шагов, и модель участвует только в двух.
- Вы передаёте описания функций вместе с запросом: название, что делает, какие аргументы принимает и какого типа.
- Модель возвращает намерение. Не результат, а структуру вида «нужно вызвать вот эту функцию с вот такими аргументами». Это по-прежнему просто текст в оговорённом формате.
- Ваш код решает, выполнять ли. Здесь можно проверить аргументы, спросить человека, отказать.
- Вы выполняете и кладёте результат обратно в контекст следующим сообщением.
- Модель продолжает работу, уже видя результат.
Из этой схемы следуют две вещи, которые важнее любых деталей формата.
Между намерением модели и действием всегда стоит ваш код. Модель не может ничего сделать сама — только попросить. Значит вопрос безопасности сводится к тому, что именно ваш код готов выполнить по просьбе.
Описания функций — часть промпта. Модель выбирает по тексту описания, и качество этого текста определяет, попадёт ли она в нужную функцию.
Как описывать функции
Здесь сосредоточена основная работа, и здесь же самая частая причина неверных вызовов.
Название говорящее, а не техническое. get_order_status понятнее, чем proc_ord_st. Модель читает название как часть описания.
Описание отвечает на вопрос «когда её вызывать». Не «получает статус заказа», а «получает текущий статус заказа по его номеру; использовать, когда пользователь спрашивает, где его заказ или что с доставкой».
Аргументы описаны по одному. Тип, формат, обязательность, пример. Особенно важен формат: без указания модель придумает свой вид даты или номера.
Границы применения указаны явно. Если функция работает только с заказами за последний год — напишите это. Иначе модель будет вызывать её и для старых.
Проверка качества описаний простая: покажите их человеку, не знакомому с вашей системой, и спросите, когда какую вызывать. Если он путается — путается и модель.
Сколько это стоит
Про деньги, потому что эффект недооценивают.
Описания уходят в модель при каждом обращении. Не когда функция нужна, а всегда. Десяток функций с подробными описаниями — это постоянный довесок к каждому запросу, даже если ни одна не вызвана.
Результаты вызовов попадают в историю. И переотправляются на следующих шагах. В цикле, где модель вызывает несколько функций подряд, объём растёт быстро — как считать такой расход.
Отсюда практическое правило: подключённые, но неиспользуемые функции стоят денег каждый день. Ревизия списка раз в квартал окупается — обычно выясняется, что треть вызывается раз в месяц.
Второй приём: не подавать все функции сразу. Если по типу запроса понятно, что нужен только один набор из трёх, передавайте только его.
Где схема ломается
Пять типичных проблем.
Модель вызывает не ту функцию. Почти всегда причина в описаниях: две функции описаны похоже, и различить их нельзя. Лечится переписыванием описаний, а не сменой модели.
Аргументы приходят в неверном формате. Дата в произвольном виде, номер строкой вместо числа. Лечится явным указанием формата и примером в описании.
Модель придумывает несуществующую функцию. Реже, но случается. Ваш код обязан это проверять и не падать.
Вызов зациклился. Модель вызывает функцию, получает результат, вызывает снова. Нужен жёсткий предел числа шагов, иначе цикл будет крутиться, пока не кончится баланс.
Поддержка не проверена у выбранной модели. В нашем каталоге это поля capabilities: verified — проверено, unsupported — не поддерживается, unknown — проверка не проводилась. Последнее не означает «не работает», но и закладывать его в обязательства перед клиентом нельзя — как читать поля контракта.
Как отлаживать, когда модель выбирает не то
Отдельно про диагностику, потому что порядок действий здесь неочевиден и время обычно тратят не на то.
Первое, что нужно сделать при неверном вызове, — посмотреть, что модель вообще получила. Описания функций собираются кодом, и в них регулярно оказывается не то, что автор имел в виду: обрезанный текст, пустое описание аргумента, устаревшая формулировка из прошлой версии. Логируйте итоговый набор описаний, а не тот, что лежит в исходниках.
Второе — проверить на человеке. Возьмите ровно те описания, что ушли в модель, дайте коллеге и попросите выбрать функцию под ваш запрос. Если он ошибается там же, где ошибалась модель, проблема в описаниях, и менять модель бессмысленно.
Третье — разделить похожие функции явным различием. Самый частый случай неверного выбора: две функции решают близкие задачи, и в описаниях нет ни одной фразы, которая бы их развела. Помогает прямая формулировка вида «использовать, только если у пользователя есть номер заказа; если номера нет — вызывать другую функцию».
Четвёртое — сократить список. Чем больше функций подано одновременно, тем чаще модель промахивается: выбор из четырёх точнее выбора из двадцати. Если у вас двадцать, почти наверняка их можно разбить на группы и подавать по одной группе за раз.
И то, что делать не стоит: наращивать инструкции в системном промпте вида «выбирай функцию внимательно». На выбор это почти не влияет, а входные токены расходует при каждом обращении. Работает переписывание описания самой функции, а не общие увещевания рядом.
Отдельно стоит завести лог вызовов с их результатом: какая функция была выбрана, с какими аргументами, чем закончилось. Через неделю такой лог сам покажет, какие описания надо переписать, — без него вы будете чинить по единичным жалобам.
Про безопасность
Раздел, который стоит прочитать до того, как дать модели что-то полезное.
Функция, которую модель может попросить вызвать, — это возможность, которую вы ей дали. Если среди функций есть отправка письма, изменение записи в базе или платёж, то любой текст, попавший в контекст, потенциально способен эту возможность задействовать.
Мы разбирали случай, когда инструкцию спрятали в документе белым шрифтом по белому фону. Модель не отличает данные от команд: всё, что в контексте, читается однородно.
Три меры, которые действительно работают:
Проверяйте аргументы в своём коде. Не доверяйте тому, что модель прислала корректный идентификатор или сумму.
Разделите функции по риску. Чтение — одно, изменение — другое, отправка наружу — третье. Разрешение стоит давать на категорию, а не на каждый вызов — как это устроено на практике.
Ставьте человека на необратимом. Всё, что нельзя откатить, должно подтверждаться живым проверяющим.
Одна функция или несколько
Короткое замечание о проектировании, которое экономит больше всего сил.
Соблазн сделать одну универсальную функцию с параметром «действие» велик: меньше описаний, меньше кода. На практике это работает хуже. Модель выбирает между функциями по описанию — а когда функция одна, выбирать ей не из чего, и вся нагрузка переносится на угадывание значения параметра, для которого никакого описания нет.
Обратная крайность — двадцать узких функций — тоже плоха: чем длиннее список, тем чаще промах. Разумная середина обычно лежит на пяти-восьми функциях, каждая из которых отвечает за понятное человеку действие, плюс группировка по сценариям, если их больше.
Отдельно: не делайте функцию, которая принимает произвольный код или запрос к базе. Это выглядит универсально и снимает вопрос выбора, но фактически означает, что вы отдали модели всё, к чему у неё есть доступ, — и никакая проверка аргументов такую функцию уже не спасёт.
Чего мы не утверждаем
Мы не описываем формат запроса построчно. Он различается между протоколами и меняется вместе с версиями; актуальную схему смотрите в документации.
Поддержка различается между моделями. Проверять нужно по полю capabilities для конкретной позиции, а не по семейству.
Не даём готовых описаний функций. Они пишутся под вашу предметную область, и чужие переносятся плохо.
Что держать в голове
Модель не вызывает функции — она возвращает намерение, а выполняет ваш код. Отсюда и контроль, и ответственность: что вы готовы выполнить по просьбе, то модель и может сделать.
Работает эта конструкция ровно настолько, насколько хорошо описаны функции: выбор делается по тексту описания, а не по силе модели. И оплачиваются описания при каждом обращении — даже когда ни одна функция не нужна.
Как мы проверяем факты и что храним из запросов — на странице о проекте и в llms.txt. Проверить поддержку на нужной модели можно по ключу за минуту: keydealer.ru/login.
Частые вопросы
Что такое function calling?
Механизм, при котором модель в ответ на запрос возвращает структуру с названием функции и аргументами. Выполняет функцию ваш код, а не модель.
Модель правда вызывает мои функции?
Нет. Она возвращает намерение вызвать — обычный текст в оговорённом формате. Между намерением и действием всегда стоит ваш код.
Как модель выбирает нужную функцию?
По текстовому описанию, которое вы передали вместе с запросом. Качество описания определяет точность выбора сильнее, чем сила модели.
Сколько это стоит?
Описания функций уходят в модель при каждом обращении и оплачиваются как входные токены, даже если ни одна не вызвана.
У всех ли моделей это работает?
Нет. Поддержка публикуется в поле capabilities ответа GET /v1/models; значение unknown означает, что проверка не проводилась.