В этой статье
Function calling в OpenAI API — это протокол согласования между моделью и вашим приложением. Модель получает описание доступных функций и, если видит подходящую задачу, формирует запрос на вызов одной из них: имя функции и аргументы в JSON. Код при этом выполняется не моделью, а вашим приложением. У модели нет прямого доступа к серверу, базе данных или файловой системе — граница проходит там, где вы разбираете её запрос и решаете, что разрешено выполнить. Именно так механизм описан в документации OpenAI по function calling; полезно также свериться с разбором механизма в справке OpenAI.
Как выглядит цикл вызова
В Responses API схема обычно состоит из пяти шагов:
- Приложение отправляет запрос с текстом пользователя и списком
tools. - Модель возвращает элемент
function_callс именем функции и аргументами. - Приложение проверяет вызов и выполняет собственный код.
- Результат отправляется обратно как
function_call_outputс тем жеcall_id. - Модель формирует финальный ответ или просит вызвать ещё одну функцию.
Последний шаг важен: один запрос пользователя может привести к нескольким вызовам, поэтому обработчик лучше строить как цикл, а не как разовую проверку одного результата.
Function calling работает внутри API-проекта OpenAI: ему нужны права проекта, доступная вашему проекту модель и настроенная оплата API. Если для отдельной задачи вам нужен зарубежный цифровой сервис, можно посмотреть каталог Amber Market и проверить условия, срок и наличие услуги в карточке. Заказ оплачивается через СБП. Это самостоятельный маршрут к конкретной услуге, а не способ получить API-ключ, пополнить OpenAI API или выдать приложению права на выполнение функций.
Ниже — минимальный пример на Python. Он не обращается к погодному сервису: демонстрационная функция возвращает данные из локального словаря. Это позволяет увидеть протокол без внешних зависимостей.
import json
from openai import OpenAI
client = OpenAI() # SDK читает OPENAI_API_KEY из окружения
tools = [
{
"type": "function",
"name": "get_weather",
"description": "Return a demonstration weather value for a city.",
"strict": True,
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name, for example Yerevan",
}
},
"required": ["city"],
"additionalProperties": False,
},
}
]
def get_weather(city: str) -> dict:
"""Demo implementation; this is not a real forecast."""
values = {
"Yerevan": {"temperature_c": 18, "condition": "clear"},
"Moscow": {"temperature_c": 7, "condition": "cloudy"},
}
return values.get(city, {"temperature_c": None, "condition": "unknown"})
allowed_functions = {
"get_weather": get_weather,
}
input_items = [
{"role": "user", "content": "Какая погода сейчас в Ереване?"}
]
while True:
response = client.responses.create(
model="YOUR_AVAILABLE_MODEL_ID",
input=input_items,
tools=tools,
)
# Сохраняем элементы ответа, чтобы второй запрос видел историю вызова.
input_items += response.output
calls = [item for item in response.output if item.type == "function_call"]
if not calls:
print(response.output_text)
break
for call in calls:
function = allowed_functions.get(call.name)
if function is None:
# Не выполняем имя, которого нет в явном allowlist.
input_items.append(
{
"type": "function_call_output",
"call_id": call.call_id,
"output": json.dumps(
{"error": "Unknown function"}, ensure_ascii=False
),
}
)
continue
try:
arguments = json.loads(call.arguments)
city = arguments["city"]
if not isinstance(city, str) or not city.strip():
raise ValueError("city must be a non-empty string")
result = function(city)
input_items.append(
{
"type": "function_call_output",
"call_id": call.call_id,
"output": json.dumps(result, ensure_ascii=False),
}
)
except (json.JSONDecodeError, KeyError, TypeError, ValueError) as error:
input_items.append(
{
"type": "function_call_output",
"call_id": call.call_id,
"output": json.dumps(
{"error": f"Invalid function arguments: {error}"},
ensure_ascii=False,
),
}
)
В коде есть несколько деталей, которые легко потерять при переносе примера в проект.
tools описывает контракт, а не реализацию. В Responses API функция задаётся как tool с именем, описанием и JSON Schema параметров. strict: True просит модель соблюдать эту схему. Для строгого режима аргументы должны быть перечислены в required, а объект — явно запретить дополнительные поля через additionalProperties: False. Подробные ограничения strict-режима приведены в руководстве OpenAI по function calling.
Но схема — это проверка формы, а не доверие к содержимому. Даже строка city может быть неожиданно длинной, не соответствовать вашему справочнику или содержать значение, которое нельзя передавать дальше. Поэтому приложение всё равно валидирует типы, диапазоны и допустимые значения перед выполнением.
Что именно возвращает модель
Ответ модели содержит элементы разных типов. Нас интересует function_call: у него есть имя функции, строка arguments с JSON и идентификатор call_id. Аргументы нужно декодировать через json.loads, а не подставлять в команду или SQL-запрос как текст.
После выполнения приложение возвращает отдельный элемент function_call_output. Его call_id должен совпадать с идентификатором исходного вызова. В поле output передаётся сериализованный результат — обычно JSON-строка или понятное сообщение об ошибке. Затем приложение снова вызывает Responses API с накопленной историей. Модель связывает результат именно с тем вызовом и решает, что ответить пользователю дальше.
Allowlist в примере не декоративен. Нельзя брать call.name и динамически искать атрибут или функцию по этому имени: так модель фактически получает возможность выбирать больше операций, чем вы планировали. Для каждой разрешённой функции задайте явное отображение и отдельные правила валидации. Неизвестное имя в примере не выполняется: приложение возвращает контролируемую ошибку с тем же call_id.
Function calling не делает данные истинными
Function calling отвечает на вопрос «как передать модели запрос на действие и вернуть результат», но не отвечает на вопросы безопасности и достоверности. Если функция читает базу, проверьте права текущего пользователя и область данных. Если меняет состояние, заранее определите идемпотентность, обработку повторов и условия подтверждения. Для платежа, удаления или отправки письма обычно нужно явное подтверждение человека, даже если аргументы идеально соответствуют JSON Schema.
Это также отличается от Structured Outputs. Structured Outputs полезен, когда нужно получить ответ модели в заданной JSON-форме без выполнения внешней функции. Function calling нужен, когда приложение должно по решению модели вызвать свой код, API или слой доступа к данным. В обоих случаях схема помогает с форматом, но не заменяет бизнес-проверки.
Если вам нужен именно JSON-ответ по схеме без выполнения функций, пригодится отдельный разбор Structured Outputs. А перед добавлением tools можно пройти подготовку Python-окружения и первый запрос к OpenAI API.
Название модели в примере оставлено как YOUR_AVAILABLE_MODEL_ID: его нужно заменить на модель, доступную вашему проекту в актуальном каталоге. Ключ при этом хранится в окружении и не попадает ни в исходный код, ни в текст запроса — такой способ настройки показан в Developer quickstart. Повторять модельный вызов можно сколько угодно раз в рамках вашего цикла, но каждый вызов функции должен проходить через allowlist, валидацию аргументов и правила побочных эффектов.
Источники
- Function calling | OpenAI APIOpenAI Developers
- Developer quickstart - OpenAI APIOpenAI Developers
- Function Calling in the OpenAI APIOpenAI Help Center