Все статьи

Function Calling в LLM: сравнение форматов tool use у OpenAI, Anthropic, DashScope, DeepSeek и SiliconFlow (2026)

Кросс-провайдерное сравнение function calling (tool use): форматы определения tool, структура ответов, параллельные вызовы, streaming tool chunk, strict mode и нормализация форматов на уровне gateway.

· TheRouter

Function calling — или «tool use» в терминологии Anthropic — это механизм, который превращает LLM из генератора текста в agent, способный обращаться к базам данных, вызывать API и запускать workflow. Все крупные провайдеры поддерживают эту функцию. Ни один из них не согласен с остальными по формату.

Мы каждый день маршрутизируем function calling трафик между OpenAI, Anthropic, DashScope, DeepSeek и SiliconFlow. Эта статья — справочник, которого нам не хватало, когда мы впервые нормализовали tool call форматы всех провайдеров: различия в schema, несовместимости в форматах ответов, особенности streaming и практические ловушки, ломающие multi-provider routing.

OpenAI-совместимость означает, что провайдер предоставляет endpoint chat-completions, чей контракт запроса и ответа достаточно близок к API OpenAI, чтобы немодифицированный вызов OpenAI SDK работал после замены трёх значений: API key, base URL, название модели. Минимальная поверхность на практике —POST /v1/chat/completions с messages, model и потоковым ответом в форме OpenAI.

Сводная таблица

ПараметрOpenAIAnthropic ClaudeDashScope (Qwen)DeepSeekSiliconFlow
ТерминологияFunction callingTool useFunction callingTool callsFunction calling
Поле запросамассив toolsмассив toolsмассив toolsмассив toolsмассив tools
Ключ schemaparametersinput_schemaparametersparametersparameters
Формат ответаtool_calls в сообщении assistanttool_use content blocktool_calls в сообщении assistanttool_calls в сообщении assistanttool_calls в сообщении assistant
Тип аргументовJSON-строкаРаспарсенный объектJSON-строкаJSON-строкаJSON-строка
Параллельные вызовыДаДаДаДа (без thinking)Да
Strict modeДа (strict: true)НетНетДа (Beta, endpoint /beta)Нет
Streaming tool chunktool_calls delta chunkcontent_block_delta + input_json_deltatool_calls delta chunktool_calls delta chunktool_calls delta chunk
Tool choiceauto / required / none / именованныйauto / any / tool (именованный)auto / required / none / именованныйauto / required / none / именованныйauto / required / none / именованный
Макс. число toolБез жёсткого лимита (~128 на практике)64+Зависит от моделиЗависит от моделиЗависит от модели

Определение tool: первая точка расхождения

Базовая идея одинакова: вы описываете функцию с name, description и JSON Schema для параметров. Различия начинаются с имени ключа schema.

OpenAI оборачивает определение функции в элемент массива tools с type: "function":

{
  "type": "function",
  "function": {
    "name": "get_weather",
    "description": "Получить текущую погоду в городе",
    "parameters": {
      "type": "object",
      "properties": {
        "location": { "type": "string" }
      },
      "required": ["location"],
      "additionalProperties": false
    }
  }
}

Anthropic использует ту же верхнеуровневую структуру, но заменяет parameters на input_schema и убирает обёртку function:

{
  "name": "get_weather",
  "description": "Получить текущую погоду в городе",
  "input_schema": {
    "type": "object",
    "properties": {
      "location": { "type": "string" }
    },
    "required": ["location"]
  }
}

DashScope (Qwen) и DeepSeek полностью повторяют формат OpenAI — type: "function", вложенный объект function, ключ parameters. В этом преимущество OpenAI-совместимых API: код, написанный для OpenAI, работает с DashScope и DeepSeek без изменения tool-определений.

SiliconFlow тоже следует OpenAI-совместимому формату.

Практический вывод: для нормализации tool-определений на уровне gateway нужно поддерживать два формата — OpenAI-стиль (OpenAI, DashScope, DeepSeek, SiliconFlow) и Anthropic-стиль (только Anthropic). Маппинг прямолинейный: parametersinput_schema, развернуть обёртку function.

Формат ответов: где всё по-настоящему расходится

Tool-определения обрабатываются легко. Формат ответов — вот где multi-provider routing становится нетривиальным.

OpenAI / DashScope / DeepSeek / SiliconFlow (OpenAI-совместимые)

Когда модель решает вызвать tool, ответ приходит как сообщение assistant с массивом tool_calls:

{
  "role": "assistant",
  "content": null,
  "tool_calls": [
    {
      "id": "call_abc123",
      "type": "function",
      "function": {
        "name": "get_weather",
        "arguments": "{\"location\": \"Tokyo\"}"
      }
    }
  ]
}

Важная деталь: arguments — это JSON-строка, а не распарсенный объект. Нужен JSON.parse() перед использованием значений.

Результат возвращается сообщением с role: "tool" и ссылкой на tool_call_id:

{
  "role": "tool",
  "tool_call_id": "call_abc123",
  "content": "{\"temperature\": 24, \"unit\": \"celsius\"}"
}

Anthropic Claude

Anthropic использует архитектуру content block. Tool call и текст — отдельные блоки в одном ответе assistant:

{
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "Давайте проверю погоду."
    },
    {
      "type": "tool_use",
      "id": "toolu_01abc",
      "name": "get_weather",
      "input": { "location": "Tokyo" }
    }
  ],
  "stop_reason": "tool_use"
}

Ключевые отличия от формата OpenAI:

  • Аргументы — распарсенный объект (input), а не JSON-строка (arguments)
  • Tool call находятся внутри блоков content, а не в отдельном массиве tool_calls
  • stop_reason равен "tool_use", а не "tool_calls"
  • Текст и tool call могут чередоваться в одном ответе

Результат возвращается как сообщение user с блоками tool_result:

{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01abc",
      "content": "{\"temperature\": 24, \"unit\": \"celsius\"}"
    }
  ]
}

Это принципиально отличается от подхода OpenAI с role: "tool". У Anthropic tool result отправляется как часть user turn.

Параллельные и последовательные вызовы tool

Все основные провайдеры поддерживают параллельные tool call. Но семантика реализации различается.

OpenAI возвращает несколько записей в массиве tool_calls. Можно использовать parallel_tool_calls: false для принудительного последовательного вызова.

Anthropic возвращает несколько блоков tool_use в массиве content. Поддерживает disable_parallel_tool_use: true через tool_choice.

DeepSeek поддерживает параллельные tool call в режиме без thinking. В thinking mode (V3.2+) tool calling поддерживается, но поведение параллельных вызовов может отличаться.

DashScope следует формату параллельных tool call OpenAI.

Streaming tool call chunk

Streaming function calling добавляет ещё один уровень различий в форматах.

OpenAI / DashScope / DeepSeek / SiliconFlow стримят tool call как delta-объекты:

{
  "delta": {
    "tool_calls": [
      {
        "index": 0,
        "id": "call_abc",
        "type": "function",
        "function": { "name": "get_weather", "arguments": "" }
      }
    ]
  }
}

Затем фрагменты аргументов:

{
  "delta": {
    "tool_calls": [
      {
        "index": 0,
        "function": { "arguments": "{\"loc" }
      }
    ]
  }
}

Нужно накапливать строку arguments по chunk и парсить полный JSON по завершении stream.

Anthropic использует совершенно другую модель событий streaming:

  1. content_block_start с type: "tool_use", tool id и name
  2. content_block_delta с type: "input_json_delta" и фрагментами аргументов
  3. content_block_stop — конец tool call

Для реализации gateway это самая сложная часть нормализации. OpenAI-совместимые провайдеры используют один и тот же формат chunk с delta.tool_calls[index].function.arguments, но событийная модель Anthropic требует совершенно другого parser.

Strict mode и валидация JSON Schema

Strict mode гарантирует, что аргументы tool call модели строго соответствуют объявленной JSON Schema. Не все провайдеры его поддерживают.

OpenAI"strict": true в определении function. Все свойства должны быть required, additionalPropertiesfalse.

DeepSeek — Beta-версия strict mode. Используйте /beta base URL (https://api.deepseek.com/beta) и "strict": true на каждой function. Поддерживает pattern, format, minimum/maximum, enum и anyOf.

Anthropic, DashScope и SiliconFlow не предлагают strict mode. Валидируйте аргументы в коде приложения.

Управление Tool choice

ПровайдерПринудительный вызовЗапрет вызоваКонкретный toolАвто (по умолч.)
OpenAItool_choice: "required"tool_choice: "none"tool_choice: {"type": "function", "function": {"name": "X"}}tool_choice: "auto"
Anthropictool_choice: {"type": "any"}tool_choice: {"type": "none"}tool_choice: {"type": "tool", "name": "X"}tool_choice: {"type": "auto"}
DashScopetool_choice: "required"tool_choice: "none"tool_choice: {"type": "function", "function": {"name": "X"}}tool_choice: "auto"
DeepSeektool_choice: "required"tool_choice: "none"tool_choice: {"type": "function", "function": {"name": "X"}}tool_choice: "auto"
SiliconFlowtool_choice: "required"tool_choice: "none"tool_choice: {"type": "function", "function": {"name": "X"}}tool_choice: "auto"

Формат tool_choice у Anthropic структурно отличается — {"type": "any"} вместо "required", для выбора конкретного tool используется {"type": "tool", "name": "X"}.

GPT-5.6 Sol Programmatic Tool Calling

GPT-5.6 Sol от OpenAI представил значительное развитие: programmatic tool calling. Модель может генерировать и исполнять JavaScript-код в изолированном V8 sandbox для оркестрации вызовов tool.

Это другая парадигма по сравнению с классическим function calling. Ни один другой провайдер пока не предложил аналогичную функцию. Для multi-provider routing традиционный function calling остаётся универсальным интерфейсом.

Нормализация на уровне gateway

Если вы маршрутизируете function calling запросы между несколькими провайдерами — как это делаем мы в TheRouter — слой нормализации должен обрабатывать:

Входящие (клиент → gateway → провайдер):

  1. Формат tool-определений: маппинг parametersinput_schema, развёртывание/оборачивание объекта function для Anthropic
  2. Формат tool choice: трансляция "required"{"type": "any"}
  3. Структура запроса: различные role для сообщений с tool result

Исходящие (провайдер → gateway → клиент):

  1. Формат ответа: tool_use content block Anthropic → массив tool_calls в стиле OpenAI
  2. Парсинг аргументов: Anthropic возвращает объекты; OpenAI-совместимые — JSON-строки
  3. Причина остановки: stop_reason: "tool_use"finish_reason: "tool_calls"
  4. События streaming: content_block_start/content_block_delta Anthropic → delta.tool_calls chunk в стиле OpenAI

OpenAI-совместимые провайдеры (DashScope, DeepSeek, SiliconFlow) — простой случай: формат одинаковый, маршрутизация между ними не требует нормализации на уровне function calling.

Пример кода: минимальный multi-provider tool calling клиент

import OpenAI from "openai";
import Anthropic from "@anthropic-ai/sdk";

const openaiTool: OpenAI.ChatCompletionTool = {
  type: "function",
  function: {
    name: "get_weather",
    description: "Получить погоду в городе",
    parameters: {
      type: "object",
      properties: {
        location: { type: "string", description: "Название города" }
      },
      required: ["location"],
      additionalProperties: false
    }
  }
};

const anthropicTool: Anthropic.Tool = {
  name: "get_weather",
  description: "Получить погоду в городе",
  input_schema: {
    type: "object" as const,
    properties: {
      location: { type: "string", description: "Название города" }
    },
    required: ["location"]
  }
};

// Парсинг tool call из OpenAI-совместимого ответа
function parseOpenAIToolCalls(message: OpenAI.ChatCompletionMessage) {
  return (message.tool_calls ?? []).map(tc => ({
    id: tc.id,
    name: tc.function.name,
    args: JSON.parse(tc.function.arguments) // JSON-строка → объект
  }));
}

// Парсинг tool call из ответа Anthropic
function parseAnthropicToolCalls(response: Anthropic.Message) {
  return response.content
    .filter((b): b is Anthropic.ToolUseBlock => b.type === "tool_use")
    .map(b => ({
      id: b.id,
      name: b.name,
      args: b.input as Record<string, unknown> // Уже объект
    }));
}

Ключевое различие в слое парсинга: OpenAI даёт JSON.parse(tc.function.arguments), Anthropic — b.input напрямую как объект.

Матрица выбора: нужен X — выбирайте Y

Вам нужно...Лучший вариантПочему
Строгая валидация schemaOpenAI или DeepSeek (Beta)Единственные провайдеры с серверным strict: true
Чередование текста и tool callAnthropicАрхитектура content block нативно поддерживает
Совместимость с OpenAI SDKDashScope, DeepSeek, SiliconFlowБез изменений кода для tool-определений и ответов
Максимум toolOpenAIПрактический лимит ~128+; tool_search для больших реестров
Бюджетный function callingDeepSeek или SiliconFlowСамая низкая цена за token с полной поддержкой tool call
Thinking mode + tool callingDeepSeek V3.2+ или Qwen3Оба поддерживают tool call в режиме рассуждений
Multi-provider fallbackTheRouterНормализует форматы tool call всех провайдеров

Типичные ловушки

  1. Двойное JSON-кодирование: некоторые OpenAI-совместимые клиенты двойно кодируют arguments при проксировании. DashScope отвергает такие запросы — известная проблема.

  2. Формат ID tool call: OpenAI использует префикс call_, Anthropic — toolu_, DeepSeek — call_. Не полагайтесь на единый формат.

  3. Пустой content в tool call сообщениях: при возврате tool call у OpenAI-совместимых моделей content обычно null. Некоторые клиентские библиотеки не обрабатывают null.

  4. DashScope GLM модели: при использовании моделей GLM через DashScope обязательно добавьте extra_body={"tool_stream": True}. Без этого модель не вернёт tool_calls.

  5. DeepSeek thinking mode: V3.2+ поддерживает tool call в thinking mode, но параллельное поведение может отличаться от обычного режима.

  6. Anthropic tool_result role: результаты tool отправляются как сообщения role: "user", а не role: "tool". Это ловит каждого разработчика, мигрирующего с OpenAI.

  7. Накопление streaming tool call: при streaming нужно накапливать строку arguments по chunk перед парсингом. Попытка парсить каждый chunk отдельно приведёт к ошибкам JSON.

FAQ

Q: Можно ли использовать одни и те же tool-определения у всех провайдеров? A: У OpenAI, DashScope, DeepSeek и SiliconFlow — идентичные. Для Anthropic нужно переименовать parameters в input_schema и убрать обёртку function.

Q: Какие провайдеры поддерживают function calling в режиме рассуждений? A: DeepSeek (V3.2+) и DashScope (серия Qwen3 с enable_thinking: true). OpenAI o3/o4-mini поддерживают tool use. Extended thinking Anthropic с Claude тоже поддерживает tool use.

Q: Сколько tool можно определить в одном запросе? A: OpenAI — без жёсткого лимита, но производительность падает после ~128. Anthropic рекомендует до 64. DashScope и DeepSeek — зависит от модели. Для больших реестров у OpenAI есть tool_search (GPT-5.4+).

Q: Все провайдеры поддерживают additionalProperties: false? A: OpenAI strict mode — обязательно. DeepSeek /beta strict mode — обязательно. Anthropic, DashScope, SiliconFlow принимают, но не принуждают.

Q: Что если модель сгенерировала невалидные аргументы tool call? A: Без strict mode модель может выдать аргументы, не соответствующие schema. Всегда валидируйте аргументы перед исполнением tool. С strict mode (OpenAI, DeepSeek Beta) API гарантирует валидность.


Источники: документация OpenAI Function Calling (получено 2026-08-01), документация Anthropic Tool Use (получено 2026-08-01), документация DeepSeek Tool Calls (получено 2026-08-01), документация DashScope Function Calling (получено 2026-08-01), Qveris Function Calling Guide (получено 2026-08-01), Digital Applied AI Function Calling Guide (получено 2026-08-01).

Модели, упомянутые в статье

Поддержка