Function calling: как агент взаимодействует с внешними системами

Модель сама ничего не вызывает: она лишь просит вашу программу выполнить функцию. Разбираем весь цикл по шагам, с кодом на Python и расчётом цены.

5 мин · · агенты · разработка

Когда говорят, что агент «сходил в базу» или «создал задачу в трекере», может показаться, что модель сама подключается к системам. На деле всё проще и надёжнее: взаимодействие агента с внешними системами через function calling построено на том, что модель только формулирует просьбу, а выполняет её ваш код. Ниже — полный цикл по шагам, рабочий пример на Python и расчёт того, во что такой цикл обходится.

Идея в одном абзаце

Вы описываете модели набор функций: имя, назначение, параметры. Модель читает вопрос пользователя и либо отвечает сразу, либо возвращает структурированную просьбу вида «вызови get_order_status с order_id = A-1042». Ваша программа выполняет вызов, отправляет результат обратно, и модель формулирует финальный ответ. Модель ничего не запускает и не получает доступа к вашим системам — только к тексту, который вы ей вернули.

Цикл по шагам

  1. Описание инструментов. В запрос добавляется список tools с JSON-схемой параметров каждой функции.
  2. Решение модели. Если нужна функция, ответ приходит не с текстом, а с вызовом: имя и аргументы в JSON. Признак — поле tool_calls в OpenAI-формате или блок tool_use и stop_reason: "tool_use" в формате Anthropic.
  3. Выполнение. Ваш код разбирает аргументы, проверяет их и вызывает настоящую функцию: запрос в базу, HTTP-вызов, чтение файла.
  4. Возврат результата. Вы добавляете в историю сообщение с результатом, привязанное к идентификатору вызова.
  5. Повтор. Вы снова отправляете всю историю. Модель либо делает ещё вызов, либо пишет ответ. Цикл заканчивается, когда в ответе нет вызовов.

Этот цикл и есть агент: всё остальное (память, планирование, сабагенты) надстраивается сверху.

Пример на Python (OpenAI-формат)

Код ниже работает через /v1/chat/completions. Адрес оканчивается на /v1, как для любого клиента на OpenAI SDK.

bash
import json
from openai import OpenAI

client = OpenAI(base_url="https://api.zukko.pro/v1", api_key="fetch_ваш_ключ")

def get_order_status(order_id: str) -> dict:
    # здесь был бы запрос в вашу систему
    return {"order_id": order_id, "status": "shipped", "eta_days": 2}

REGISTRY = {"get_order_status": get_order_status}

tools = [{
    "type": "function",
    "function": {

Что здесь важно:

  • for _ in range(5) — предохранитель. Агент, который по ошибке зовёт функцию снова и снова, расходует баланс. О защите — в статье агент зациклился и потратил бюджет.
  • Аргументы приходят строкой с JSON и могут быть невалидными: оборачивайте json.loads в try, а результат ошибки возвращайте модели как данные, чтобы она могла поправиться.
  • Функции берутся из белого списка REGISTRY, а не через eval: имя от модели нельзя исполнять напрямую.
  • Результат всегда строка. Верните компактный JSON, а не страницу HTML.

Параметры и поля ответа описаны в справочнике по chat completions.

То же в формате Anthropic

Для Claude можно использовать /v1/messages. Структура отличается названиями, суть цикла та же:

bash
import anthropic

client = anthropic.Anthropic(base_url="https://api.zukko.pro", api_key="fetch_ваш_ключ")

tools = [{
    "name": "get_order_status",
    "description": "Возвращает статус заказа по его номеру.",
    "input_schema": {
        "type": "object",
        "properties": {"order_id": {"type": "string"}},
        "required": ["order_id"],
    },
}]

Здесь base_url без /v1: официальный Anthropic SDK добавляет путь сам. Подробный разбор формата — в статье tool use в Claude на Python.

Ollama и другие локальные рантаймы

Запрос «ollama tool calling» отражает тот же принцип: локальная модель тоже получает описание функций и возвращает просьбу о вызове. Но поддержка зависит от конкретной модели и версии Ollama, а формат запроса определяет сама программа. Сверяйтесь с документацией Ollama: какие модели умеют вызывать инструменты и как передаются tools. Цикл на стороне вашего кода остаётся тем же.

Что влияет на стоимость

Каждый круг цикла — отдельный запрос, в который снова входят описание инструментов, вся история и результаты функций. Поэтому цена растёт не от числа вызовов самих по себе, а от длины истории.

Расчёт-пример (цены на 18 октября 2026, gpt-6.1-sol: вход $0.27, выход $1.32 за 1M). Допущения: описание инструмента и вопрос — 500 токенов, решение модели — 80 токенов, результат функции — 150 токенов, финальный ответ — 200 токенов.

  • Круг 1: вход 500, выход 80. 500 × 0.27 / 1 000 000 + 80 × 1.32 / 1 000 000 = $0.000135 + $0.0001056 = $0.0002406.
  • Круг 2: вход 500 + 80 + 150 = 730, выход 200. 730 × 0.27 / 1 000 000 + 200 × 1.32 / 1 000 000 = $0.0001971 + $0.000264 = $0.0004611.
  • Итого $0.0007017, около $0.0007 за диалог; на 1 000 диалогов выходит примерно $0.70.

На deepseek-v4.1-flash ($0.013 / $0.038) те же числа дают около $0.027 на 1 000 диалогов, на claude-sonnet-5-5 ($0.4 / $2) около $1.05. Реальные суммы зависят от размера схем и результатов.

Как это уменьшить:

  • Сокращайте описания и не подключайте инструменты, которые не нужны в этом диалоге.
  • Возвращайте из функций только нужные поля.
  • Для длинных агентных сессий используйте кэш. Для Claude скидку за чтение из кэша шлюз видит в формате /v1/messages; в OpenAI-формате весь вход для Claude идёт по цене входа. У GPT чтение кэша видно в обоих форматах. Подробнее про кэш — на странице лимитов и кэша.
  • Логируйте поле usage каждого ответа: так видно, как растёт вход от круга к кругу и какой инструмент раздувает историю.

Какая модель надёжнее справится именно с вашим набором функций, заранее не скажешь: прогоните одни и те же сценарии на двух-трёх моделях и сравните результат и стоимость.

Частые вопросы

Модель сама подключается к базе или API?

Нет. Она возвращает имя функции и аргументы, а выполняет вызов ваш код. Поэтому права и ограничения вы задаёте сами.

Чем function calling отличается от MCP?

Function calling — механизм на уровне запроса к модели. MCP — стандарт, по которому клиент получает описания инструментов от внешнего сервера и пересылает ему вызовы, а сами функции выполняет сервер; модели клиент всё равно передаёт эти описания как tools.

Можно ли вызвать несколько функций за один раз?

Модель может вернуть несколько вызовов в одном ответе. Обработайте их все и добавьте по сообщению с результатом на каждый tool_call_id.

Что делать, если модель выдумывает аргументы?

Проверяйте схему на своей стороне, делайте параметры строгими (перечисления, обязательные поля) и возвращайте понятную ошибку, чтобы модель могла исправиться. Ключ для опытов можно создать в кабинете Zukko.

zukko

Платите в 25×
меньше

за Claude Code & ChatGPT

Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.