Function calling на практике: подключаем LLM к инструментам
Как работает function calling: полный пример на Python, форматы OpenAI, Anthropic и Gemini, проверка аргументов и отличия от Structured Outputs и MCP.
Опубликовано

Function calling, или вызов функций, позволяет большой языковой модели (LLM) запрашивать действия у вашего приложения. Механизм строится на чётком разделении ответственности и состоит из 3 частей: модель формирует структурированный запрос на вызов, ваш код проверяет и выполняет его, а затем передаёт результат обратно. Подключить базу данных к модели — значит выстроить этот механизм, а не выдать модели пароль от базы.
Как работает function calling
Модель формирует запрос. Приложение выполняет операцию. Разработчику, который подключает LLM к API или базе данных, стоит разобраться в этом разделении ещё до того, как писать код.
Представьте заказ в ресторане. Официант может передать заказ на блюдо, но сам бланк ничего не приготовит. На кухне проверят заказ, приготовят то, что возможно, и сообщат о результате. В приложении вызов инструмента — это такой бланк, а ваша функция — кухня.
Вы передаёте модели схему инструмента: описание доступной операции и полей, которые она принимает на входе. В ответ модель может указать имя функции и аргументы — значения для этих полей. Приложение проверяет запрос, вызывает функцию и возвращает результат, чтобы модель могла ответить пользователю или запросить следующую операцию. В руководстве Google по вызову функций прямо сказано, что за выполнение отвечает приложение.
Инструмент может как читать данные, так и изменять их. Поиск заказа, поиск по коллекции документов и проверка наличия — тоже операции с инструментами, даже если они ничего не меняют. Для первой интеграции удобно выбрать простую операцию чтения с узкой задачей: так можно проследить весь путь запроса, не добавляя транзакцию, которую может понадобиться откатить.

Function calling и tool calling: в чём разница
Когда речь о собственных функциях, оба термина часто обозначают один механизм. OpenAI использует оба названия, а Anthropic указывает, что tool use также называют function calling. Более широкое понятие «инструмент» может охватывать и возможности, которые работают на стороне провайдера, и функции, которые реализуете вы. Важнее названия то, где выполняется операция: за запуск вашей функции для работы с базой отвечает ваше приложение.
Ниже это различие показано на документации трёх провайдеров. Форматы сообщений у них различаются, поэтому формат запроса и формат результата нужно брать у одного и того же провайдера.
Минимальный пример: полный цикл вызова функции
Предоставьте модели инструмент для проверки статуса заказа, а затем верните только тот статус, который получен из базы данных. В примере ниже используется намеренно вымышленный заказ ORD-1042 в локальной базе SQLite. Это учебные данные, а не запись реального клиента и не фактический ответ модели.
Function calling в OpenAI: полный цикл запроса и ответа
Документация по вызову функций OpenAI показывает полный цикл в Responses API: объявить функцию, получить function_call, выполнить свой код и вернуть function_call_output с тем же call_id.
Установите Python SDK командой python -m pip install openai, задайте переменную окружения OPENAI_API_KEY и сохраните код в файле order_tool.py. Здесь используется gpt-6-astra — модель из примера Responses в текущей документации. Это не означает, что подходит только она. Скрипт отправляет реальные запросы к API, а сама база данных остаётся в памяти.
import json
import re
import sqlite3
from openai import OpenAI
client = OpenAI()
model = "gpt-6-astra"
# Demo identity. In a web app, obtain this from your authenticated session.
tenant_id = "tenant_demo"
db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE orders (tenant_id TEXT, order_id TEXT, status TEXT)")
db.execute(
"INSERT INTO orders VALUES (?, ?, ?)",
(tenant_id, "ORD-1042", "shipped"),
)
# The tool schema: a name, a purpose, and an input contract.
tool = {
"type": "function",
"name": "get_order_status",
"description": (
"Look up the current status of an existing order by order ID. "
"Use for status questions, not refunds or address changes. "
"Returns the order ID and status, or an error."
),
"strict": True,
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The order ID supplied by the user, such as ORD-1042.",
}
},
"required": ["order_id"],
"additionalProperties": False,
},
}
def execute(name, raw_arguments):
if name != "get_order_status":
return {"error": "unknown_tool"}
try:
args = json.loads(raw_arguments)
except (json.JSONDecodeError, TypeError):
return {"error": "invalid_arguments"}
if not isinstance(args, dict) or set(args) != {"order_id"}:
return {"error": "invalid_arguments"}
order_id = args["order_id"]
if not isinstance(order_id, str) or not re.fullmatch(r"ORD-[0-9]+", order_id):
return {"error": "invalid_order_id"}
row = db.execute(
"SELECT status FROM orders WHERE tenant_id = ? AND order_id = ?",
(tenant_id, order_id),
).fetchone()
if row is None:
return {"error": "order_not_found"}
return {"order_id": order_id, "status": row[0]}
history = [{"role": "user", "content": "What is the status of order ORD-1042?"}]
response = client.responses.create(
model=model,
input=history,
tools=[tool],
tool_choice={"type": "function", "name": "get_order_status"},
parallel_tool_calls=False,
)
# Keep every output item, including any reasoning items.
history.extend(response.output)
calls = [item for item in response.output if item.type == "function_call"]
if len(calls) != 1:
raise RuntimeError("Expected one tool call; inspect the response before continuing.")
call = calls[0]
result = execute(call.name, call.arguments)
history.append({
"type": "function_call_output",
"call_id": call.call_id,
"output": json.dumps(result),
})
answer = client.responses.create(
model=model,
input=history,
tools=[tool],
tool_choice="none",
instructions="Answer from the tool result. If it contains an error, report the failure.",
)
print(answer.output_text)
db.close()В этом намеренно ограниченном примере успешное выполнение включает 2 запроса к модели и 1 обращение к базе данных. В первом запросе принудительно выбирается заранее известная операция поиска, а во втором дальнейшие вызовы инструментов запрещены. Так проще увидеть, как устроен механизм. Универсальному ассистенту пришлось бы также обрабатывать ситуации, когда инструмент не нужен, не хватает данных или требуется ещё один запрос к базе.
Поля tenant_id нет в схеме инструмента, потому что приложение уже знает, кто вошёл в систему. Запрос к базе использует этот доверенный идентификатор вместе с запрошенным номером заказа. Если передать выбор учётной записи модели через аргумент функции, исчезнет именно та граница ответственности, которую иллюстрирует пример.
Вызов от модели и результат от приложения
Запрос и результат — отдельные сообщения, связанные идентификатором вызова. Типичный вызов может выглядеть так. Идентификаторы и значения приведены для иллюстрации; это не сохранённый ответ API.
{
"type": "function_call",
"id": "fc_demo",
"call_id": "call_demo",
"name": "get_order_status",
"arguments": "{\"order_id\":\"ORD-1042\"}"
}Когда обработчик возвращает статус из учебных данных, приложение добавляет следующий результат. При работе с реальным ответом копируйте фактическое значение call.call_id; не создавайте новый идентификатор.
{
"type": "function_call_output",
"call_id": "call_demo",
"output": "{\"order_id\":\"ORD-1042\",\"status\":\"shipped\"}"
}Теперь у модели есть основание ответить: «Заказ ORD-1042 отправлен». Статус поступил из базы данных. Задача модели состояла в том, чтобы преобразовать запрос пользователя в аргументы, а полученные данные — в ответ.
Как этот механизм описывают три провайдера
Принцип выполнения одинаков у разных провайдеров, но форматы обмена данными различаются. Таблица сверена с актуальной документацией провайдеров 17 октября 2026 года. Она показывает различия между текущими примерами: похожие названия полей не означают, что эти поля взаимозаменяемы.
Использование инструментов в Anthropic
В документации Anthropic по использованию инструментов входные параметры функции задаются через input_schema. Если инструмент выполняется на стороне клиента, ваш код обрабатывает блок tool_use из сообщения ассистента и отправляет tool_result внутри следующего сообщения пользователя, сохраняя сообщение ассистента в истории диалога.
Документация явно разделяет ответственность: клиентские инструменты работают в вашем приложении, серверные — в инфраструктуре Anthropic. Поэтому наличие поиска на стороне провайдера не означает, что Claude может напрямую выполнить вашу функцию для доступа к закрытой базе данных. Реализацию этой функции по-прежнему предоставляете вы.
Вызов функций в Gemini
Документация Google по вызову функций в Gemini также отделяет запрос модели от выполнения в приложении. В текущем руководстве используется Interactions API: шаги function_call и function_result, а для продолжения взаимодействия — previous_interaction_id.
Следуйте примеру для того API, с которым работаете. Если взять объявление функции из одного API, а сообщение с результатом — из другого, переносимого решения не получится. Сохраните саму операцию, например get_order_status, а её объявление и формат передачи результата адаптируйте вместе.
Почему вызов инструментов лежит в основе рабочего цикла агента
Агент с инструментами снова и снова выбирает действие, получает результат и решает, что делать дальше. Function calling связывает это решение с операцией, которую может выполнить ваше приложение.
Например, ассистент по заказам может найти отправление, изучить полученный идентификатор отправления у перевозчика, затем запросить данные отслеживания и только после этого ответить. Вторая операция зависит от результата первой. Такой цикл с обратной связью полезнее, чем просьба к модели заранее придумать все шаги.
В рекомендациях Anthropic по архитектуре агентов проводится различие между заранее заданными процессами и агентами, которые сами управляют использованием инструментов. Обратная связь от инструментов и условия остановки также описаны как части этого цикла.
Фраза «основа любого агента» удобна, но лишь до определённого предела. Документация не утверждает, что всякая система, которую называют агентом, использует конкретный API вызова функций. Общий принцип — цикл, связывающий модель с действиями. Для единственного запроса к базе достаточно заранее заданного процесса; автономный планировщик ему не нужен.
Три типичные проблемы в рабочем приложении
Качество схемы, выбор инструмента и проверка аргументов — разные задачи. Исправление одной не означает, что решены остальные.
Неудачная схема: формально верно, по смыслу — нет
Поле id не объясняет модели, что требуется: номер заказа, идентификатор клиента или код отправления. Строковое поле action без ограничений позволяет передать значения, которые обработчик может не распознать. Прежде чем дописывать инструкции, уточните сам контракт функции.
Давайте параметрам точные имена, объясняйте их смысл и используйте перечисление допустимых значений, если их набор фиксирован. Для первого инструмента выберите настолько узкую задачу, чтобы его назначение помещалось в простое предложение. В примере имя order_id однозначно, а описание исключает возвраты средств и изменение адреса.
Строгий режим OpenAI требует задавать additionalProperties: false для объектов и включать все свойства в required; для значения, которое может отсутствовать, можно разрешить null. Эти требования строгого режима описаны в руководстве. Google тоже документирует ограничения схем, поэтому схема, прошедшая локальную проверку, не обязательно будет принята любым API модели.
Предел возможностей схемы: она ограничивает формат данных, но корректный идентификатор всё равно может указывать не на тот заказ. Это должно проверять приложение.
Модель выбрала не тот инструмент: аргументы верные, операция — нет
Клиенту, который спрашивает «Что с моим возвратом денег?», нужна проверка статуса, а не новый возврат средств. Если описание обоих инструментов сводится к «работает с возвратами», выбор становится неоднозначным.
Объясните, когда каждый инструмент следует применять, а когда — нет. Предоставляйте только те операции, которые подходят для текущей задачи и пользователя. В рекомендациях Anthropic по описанию инструментов отдельно предлагается раскрывать назначение, смысл параметров, границы применения и ограничения.
Когда приложение уже знает, какая операция нужна, используйте предусмотренный API механизм выбора инструмента или вызовите операцию напрямую. В учебном примере поиск выбирается принудительно; из этого не следует правило «каждое сообщение пользователя должно вызывать инструмент». На приветствие или неполный вопрос о заказе может быть достаточно обычного ответа в диалоге.
Предел проверки аргументов: правильный формат не показывает, верно ли модель выбрала действие с точки зрения бизнеса. Проверяйте выбор инструмента на характерных запросах пользователей, включая случаи, в которых вызова вообще быть не должно.
Непроверенные аргументы: правильный формат ещё не даёт права доступа
Обработчик — это точка проверки входных данных. Убедитесь, что функция входит в явно заданный список разрешённых, разберите её аргументы, проверьте их структуру и смысл. Для контроля доступа используйте идентификатор пользователя, полученный приложением при аутентификации.
В примере с заказами полезно попробовать получить заказ, принадлежащий другой клиентской учётной записи. Запрос к базе не должен возвращать этот заказ, даже если его идентификатор — совершенно допустимая строка. Другие проверки: отсутствие номера заказа, лишнее поле и запрос операции, которую обработчик не поддерживает.
В обзоре Anthropic отмечается, что при нехватке информации модель может самостоятельно предположить значения аргументов. Наличие обязательного поля не доказывает, что его значение предоставил пользователь. Если действие имеет существенные последствия, сопоставьте запрошенную операцию и её параметры с тем, на что пользователь действительно дал разрешение.
Если выполнение не удалось, верните ошибку, которую можно распознать. Тайм-аут не доказывает, что операция не была выполнена. Прежде чем повторять запись, предусмотрите в приложении механизм идемпотентности: способ распознать одну и ту же задуманную операцию и не допустить повторного эффекта. Это рекомендации по реализации, а не гарантии API модели.
Когда лучше использовать структурированный вывод
Структурированный вывод подходит, когда нужен ответ заданного формата на основе уже доступных данных. Инструмент нужен, когда требуется внешняя операция. Классификатор обращений в поддержку может возвращать категорию и срочность в структурированном виде. Ассистенту, которому нужен текущий статус заказа, сначала придётся запросить его.
Руководство OpenAI по Structured Outputs проводит именно это различие: подключение функций приложения и форматирование ответа модели. Там же объясняется, что строгие схемы функций используют Structured Outputs. Эти возможности дополняют друг друга, а не исключают.

Допустим, вы уже передали запись о заказе и для интерфейса нужен только объект {order_id, status, explanation}. Здесь достаточно структурированного ответа. Если записи нет или её необходимо обновить, сначала запросите данные. После этого итоговый ответ тоже можно привести к нужной структуре.
JSON-объект с сообщением о том, что письмо отправлено, остаётся лишь объектом на выходе. Для отправки письма нужна выполненная операция и результат, который можно проверить.
Function calling и MCP: за что отвечает каждый
Function calling обрабатывает запрос модели на выполнение операции. Model Context Protocol (MCP) задаёт общий интерфейс, через который инструменты и другой контекст становятся доступны совместимым приложениям. Функцию можно напрямую подключить к одному приложению ещё до появления отдельного сервера инструментов.
MCP и tool calling: когда стоит сделать инструменты общими
Если одна и та же проверка статуса заказа нужна нескольким приложениям, MCP-сервер может предоставить инструмент для обнаружения и вызова. Документация по архитектуре MCP описывает tools/list для получения списка инструментов и tools/call для запросов на выполнение. Серверу по-прежнему нужны реализация операций и проверки доступа.
Следующий шаг — разобраться, что такое MCP и как связаны его хост, клиент и сервер. Когда прямой запрос к базе уже работает, руководство по созданию MCP-сервера поможет оформить эти возможности для повторного использования. Добавление MCP не отменяет проверку запрошенной операции.
Какие ожидания завышены
Успешная проверка схемы подтверждает соблюдение формата, а не выполнение задачи. Операция может не подходить к запросу, доступ к записи может быть закрыт, а нужный сервис — недоступен. Убедительно сформулированный ответ сам по себе не разрешает ни одну из этих проблем.
Список инструментов тоже не создаёт агента сам по себе. Всё ещё нужны правила: какие инструменты доступны, как их результаты попадают в диалог, когда останавливаться и что делать после ошибки. Фреймворк может собрать эти механизмы вместе, но правила выполнения остаются вашим архитектурным решением.
Первая полезная цель скромнее: запрос попадает в нужный обработчик, обратно возвращаются только разрешённые данные, а ответ соответствует результату. Сделайте этот путь наблюдаемым, прежде чем расширять полномочия системы.
С чего начать внедрение
Начните с операции только для чтения, которая отвечает на частый вопрос пользователей. Проверка статуса заказа — удобный образец: ожидаемый результат и границы доступа легко проверить.
Разработчикам стоит начинать уже сейчас, если ответ зависит от данных, которые приложение может надёжно получить. Если следующая операция заранее известна, оставьте процесс фиксированным. Отложите операции записи по выбору модели до тех пор, пока не появятся явные разрешения, обработка ошибок и защита от повторного выполнения.
Тем, кто отвечает за эксплуатацию, нужно видеть, что выполнилось и что вернулось. Записывайте в журнал выбранный инструмент, результат проверки, статус выполнения и идентификаторы для сопоставления вызова с результатом. При этом конфиденциальные аргументы не должны попадать в обычные логи. Установите тайм-ауты и подходящий для задачи предел числа циклов обращения к инструментам; диалог не должен продолжаться без ограничений.
Заказчикам стоит попросить команду показать не только успешный ответ, но и отказ в доступе к данным, и сбой операции. Если продукт лишь обобщает уже переданную информацию, вызов функций может не понадобиться. Структурированного вывода или обычной генерации текста бывает достаточно.
Задача на понедельник: реализуйте один узкий запрос к данным, проверьте доступную запись, недоступную запись и неполный запрос. В каждом случае изучите результат, который вернулся модели. Расширяйте список инструментов только тогда, когда можете объяснить, почему каждый запрос был принят или отклонён.
Частые вопросы
Что такое function calling?
Это механизм, с помощью которого модель запрашивает операцию, указывая имя функции и аргументы. Если функцию определяете вы, её выполняет ваше приложение и возвращает результат в диалог.
Чем tool calling в LLM отличается от function calling?
Для собственных функций провайдеры часто используют эти термины как синонимы. При этом использование инструментов может включать и возможности на стороне провайдера. Поэтому проверяйте, где выполняется операция: само название этого не определяет.
Как выглядит пример вызова функции?
Модель запрашивает get_order_status, передавая ORD-1042 в поле order_id. Ваш код проверяет запрос и права доступа, находит заказ и возвращает его статус. Заказ и статус в этой статье — вымышленные учебные данные.
Как использовать function calling в GPT-5?
Следуйте описанному в документации циклу Responses с доступной вашему проекту моделью, которая его поддерживает: передайте определения инструментов, обработайте элементы ответа с вызовами функций и верните результаты с соответствующими идентификаторами вызовов. При продолжении диалога сохраняйте элементы рассуждений. В запускаемом примере выше используется модель из текущего руководства; он не предполагает, что в вашей учётной записи включена какая-либо конкретная модель GPT-5.
Практические руководства по разработке с ИИ — в рассылке.
- Опубликовано
- Категория
- Build
- Язык










