Как использовать tool calling в OpenRouter: функции, JSON-схема и агенты

Как использовать tool calling в OpenRouter: функции, JSON-схема и агенты

 

Разберёмся, как использовать tool calling в OpenRouter, чтобы модель не просто болтала, а вызывала ваши функции и отвечала строго по схеме. Это тот шаг, после которого из чат-бота получается рабочий агент. В статье соберём вызов функций, многошаговый цикл и строгий JSON по схеме. Покажем и Response Healing, который чинит битый JSON до того, как он долетит до приложения. Весь код прогнан на реальном стенде, вывод настоящий.

 

ИИ-агенты для оптимизации бизнес-процессов

Код курса
AGENT
Ближайшая дата курса
26 октября, 2026
Продолжительность
24 ак.часов
Стоимость обучения
66 000

 

Зачем моделям инструменты и схемы

Модель сама по себе только генерирует ответ, но не выполняет действий. Она не знает вашу базу, не ходит в API и не считает точно. Tool calling это мост, вы описываете функцию, модель решает, когда её вызвать, а исполняете её вы. Так модель получает доступ к реальным данным и действиям, оставаясь в рамках вашего кода.

Примеров масса. Первый инструмент лезет в вашу базу за заказом клиента. Другой инструмент дёргает внешний API за курсом валют. Третий считает то, что модель на глаз не осилит. Вы решаете, что модели доступно, и держите её в безопасных рамках.

Также важная составляющая это форма ответа. Когда результат уходит в другую систему, вольный текст не годится, нужен предсказуемый JSON. Строгий структурированный вывод и Response Healing как раз про это. Если вы ещё не проходили базу, начните со статьи про старт с OpenRouter, а этот материал ведёт прямо к курсу про ИИ-агентов.

Зачем моделям инструменты и схемы

Именно этот цикл мы сейчас соберём на коде.

 

Tool calling и функции по требованию модели

Схема простая. Вы передаёте список инструментов, модель в ответе просит вызвать нужные, вы исполняете и возвращаете результат, модель собирает финальный ответ. Всё в привычном формате OpenAI, поэтому работает через тот же ключ OpenRouter.

Как повелось в нашей практике  весь код используемый в статье 5 выкладываем на наш GitHub репозиторий
Tool calling, структурированный вывод и агенты from airflow import DAG from airflow.operators.bash import BashOperator from datetime import datetime with DAG( dag_id="spark_submit_demo", start_date=datetime(2025, 1, 1), schedule="@daily", catchup=False ) as dag: run = BashOperator( task_id="run_job", bash_command="spark-submit app.py" ) GitHub code example Tool calling, структурированный вывод и агенты

 

Определение инструмента и первый вызов

Инструмент это функция с именем, описанием и схемой параметров. Описание важно, по нему модель понимает, когда инструмент уместен. Ниже функция погоды и запрос, который просит погоду в двух городах сразу.

# протестировано на EU-ноде (AWS Stockholm) 2026-08-04: Python 3.12.3, openai 2.53.0, OpenRouter API
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Погода в указанном городе",
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
        },
    },
}]

messages = [{"role": "user", "content": "Какая погода в Москве и Берлине? Ответь одной фразой."}]
first = client.chat.completions.create(model="openai/gpt-4o-mini", messages=messages, tools=tools)
call = first.choices[0].message
messages.append(call.model_dump())

for tc in call.tool_calls or []:
    args = json.loads(tc.function.arguments)
    result = get_weather(args["city"])
    messages.append({"role": "tool", "tool_call_id": tc.id, "content": result})

final = client.chat.completions.create(model="openai/gpt-4o-mini", messages=messages, tools=tools)
print("финал:", final.choices[0].message.content)

Модель сама попросила два вызова разом, по одному на город. Мы исполнили их и вернули результаты, а финальный ответ она собрала из фактов, а не выдумала. Каждый результат кладётся в историю с ролью tool и своим tool_call_id. Полный сценарий в файле tool_calling.py.

Базовые вызовы функций инструментов OpenRouter - практическое задание

Обратите внимание на параллельность. Модель попросила оба вызова сразу, а не по очереди, потому что они независимы. Если функция упадёт, верните ей понятный текст ошибки в поле content. Модель это учтёт и либо повторит вызов иначе, либо честно скажет, что данных нет.

 

Многошаговый цикл

Один расчет модели годится, когда всё параметры известны сразу. В реальных задачах инструменты зависят друг от друга: сначала узнать город пользователя, потом его погоду. Тогда нужен цикл, который крутится, пока модель просит инструменты, и завершается, когда она даёт финальный ответ.

# протестировано на EU-ноде (AWS Stockholm) 2026-08-04: Python 3.12.3, openai 2.53.0, OpenRouter API
step = 0
while True:
    step += 1
    resp = client.chat.completions.create(model="openai/gpt-4o-mini", messages=messages, tools=tools)
    msg = resp.choices[0].message
    messages.append(msg.model_dump())
    if not msg.tool_calls:
        print("шаг", step, "финал:", msg.content)
        break
    for tc in msg.tool_calls:
        args = json.loads(tc.function.arguments or "{}")
        result = funcs[tc.function.name](args)
        messages.append({"role": "tool", "tool_call_id": tc.id, "content": str(result)})

На первом шаге модель узнала город, на втором его погоду, на третьем собрала ответ. Цикл сам решил, сколько шагов нужно. Это и есть скелет агента: думать, вызывать инструмент, смотреть результат и повторять. Полный сценарий в файле agent_loop.py.

Прохожджение инструментов в цикле каскадом OpenRouter

Одно правило безопасности обязательно. Такой цикл нужно ограничивать по числу шагов, иначе агент рискует зациклиться и жечь токены. На практике ставят потолок в несколько шагов и аккуратно логируют каждый вызов. Так проще ловить проблемы и держать расход под контролем.

 

Структурированный вывод по JSON-схеме

Когда ответ уходит в базу или в другой сервис, важна не только суть, но и форма. Поле response_format с типом json_schema заставляет модель вернуть ровно ту структуру, что вы задали. Флаг strict включает жёсткую проверку схемы на стороне провайдера.

# протестировано на EU-ноде (AWS Stockholm) 2026-08-04: Python 3.12.3, openai 2.53.0, OpenRouter API
schema = {
    "type": "object",
    "properties": {
        "city": {"type": "string"},
        "temp_c": {"type": "integer"},
        "condition": {"type": "string"},
    },
    "required": ["city", "temp_c", "condition"],
    "additionalProperties": False,
}

resp = client.chat.completions.create(
    model="openai/gpt-4o-mini",
    messages=[{"role": "user", "content": "Извлеки данные: В Москве плюс 18 и ясно."}],
    response_format={"type": "json_schema", "json_schema": {"name": "weather", "strict": True, "schema": schema}},
)
print("сырой ответ:", resp.choices[0].message.content)

Ответ пришёл ровно по схеме: строка города, целое число температуры, строка состояния. Такой JSON можно парсить без страха и сразу класть в поля. Схема заодно документирует контракт между моделью и вашим кодом. Полный сценарий в файле structured_output.py.

Структурирование ответа модели от OpenRouter

Есть нюанс с поддержкой. Работать со строгим json_schema умеют не все модели и провайдеры. OpenRouter старается направить запрос туда, где режим поддержан. Если строгая схема недоступна, есть более простой режим json_object: он гарантирует валидный JSON, но без проверки конкретных полей.

Отдельный плюс OpenRouter это переносимость. Один и тот же код с инструментами и схемой работает на разных моделях. Вы меняете только идентификатор, а логика агента остаётся прежней. Так удобно подбирать модель под задачу по цене и качеству, не переписывая сценарий.

 

Response Healing чинит битый JSON автоматически

Модели ломают JSON чаще, чем хотелось бы, лишняя запятая, незакрытая скобка, неэкранированный символ. Для агента это боль, потому что один битый ответ роняет весь шаг. Response Healing это плагин OpenRouter, который чинит синтаксис JSON на лету, до того как ответ дойдёт до вашего приложения.

# протестировано на EU-ноде (AWS Stockholm) 2026-08-04: Python 3.12.3, openai 2.53.0, OpenRouter API
resp = client.chat.completions.create(
    model="openai/gpt-4o-mini",
    messages=[{"role": "user", "content": "Верни JSON объект с полями name и ok"}],
    response_format={"type": "json_object"},
    extra_body={"plugins": [{"id": "response-healing"}]},
)
data = json.loads(resp.choices[0].message.content)
print("распарсено без ошибок, ключи:", list(data.keys()))

Плагин включается одной строкой в поле plugins. Важно понимать границы — он правит синтаксис, а не смысл и не схему. Обрезанный по лимиту токенов ответ он не восстановит, потому что там нет чего чинить.

Плагин чинит синтаксис JSON, а не структуру OpenRouter

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

 

ИИ-агенты для оптимизации бизнес-процессов

Код курса
AGENT
Ближайшая дата курса
26 октября, 2026
Продолжительность
24 ак.часов
Стоимость обучения
66 000

 

От инструментов к агентам

Собрав цикл, строгий вывод и защиту от битого JSON, вы получаете основу агента. Дальше добавляются память, набор инструментов и правила остановки. Отдельный приём это субагенты: тяжёлая модель отдаёт рутину дешёвой прямо по ходу работы. Мы считали экономию от них в статье про токены.

Ещё одно правило это валидация аргументов. Модель предлагает, что и с какими параметрами вызвать, но слепо доверять этому нельзя. Проверяйте типы и границы значений перед исполнением, особенно для действий, которые меняют данные. Инструмент, который удаляет или платит, должен иметь дополнительный барьер.

Такая архитектура ложится и в код, и в low-code. В n8n те же вызовы собираются узлами, что мы показывали в статье про n8n. Глубокий разбор агентных паттернов под бизнес-задачи разбирается в курсе про ИИ-агентов.

 

Что дальше в серии

Итог такой. Tool calling даёт модели доступ к вашим функциям, цикл превращает это в многошагового агента, а строгий JSON и Response Healing делают вывод надёжным. Этого достаточно, чтобы строить агентов на любом стеке. В следующей статье серии займёмся мультимодальностью: как через один ключ работать с картинками, аудио и видео, не поднимая отдельных сервисов.

 

Референсные ссылки