Function Calling в LLM: сравнение форматов tool use у OpenAI, Anthropic, DashScope, DeepSeek и SiliconFlow (2026)
Кросс-провайдерное сравнение function calling (tool use): форматы определения tool, структура ответов, параллельные вызовы, streaming tool chunk, strict mode и нормализация форматов на уровне gateway.
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.
Сводная таблица
| Параметр | OpenAI | Anthropic Claude | DashScope (Qwen) | DeepSeek | SiliconFlow |
|---|---|---|---|---|---|
| Терминология | Function calling | Tool use | Function calling | Tool calls | Function calling |
| Поле запроса | массив tools | массив tools | массив tools | массив tools | массив tools |
| Ключ schema | parameters | input_schema | parameters | parameters | parameters |
| Формат ответа | tool_calls в сообщении assistant | tool_use content block | tool_calls в сообщении assistant | tool_calls в сообщении assistant | tool_calls в сообщении assistant |
| Тип аргументов | JSON-строка | Распарсенный объект | JSON-строка | JSON-строка | JSON-строка |
| Параллельные вызовы | Да | Да | Да | Да (без thinking) | Да |
| Strict mode | Да (strict: true) | Нет | Нет | Да (Beta, endpoint /beta) | Нет |
| Streaming tool chunk | tool_calls delta chunk | content_block_delta + input_json_delta | tool_calls delta chunk | tool_calls delta chunk | tool_calls delta chunk |
| Tool choice | auto / 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). Маппинг прямолинейный: parameters → input_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:
content_block_startсtype: "tool_use", toolidиnamecontent_block_deltaсtype: "input_json_delta"и фрагментами аргументов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, additionalProperties — false.
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 | Авто (по умолч.) |
|---|---|---|---|---|
| OpenAI | tool_choice: "required" | tool_choice: "none" | tool_choice: {"type": "function", "function": {"name": "X"}} | tool_choice: "auto" |
| Anthropic | tool_choice: {"type": "any"} | tool_choice: {"type": "none"} | tool_choice: {"type": "tool", "name": "X"} | tool_choice: {"type": "auto"} |
| DashScope | tool_choice: "required" | tool_choice: "none" | tool_choice: {"type": "function", "function": {"name": "X"}} | tool_choice: "auto" |
| DeepSeek | tool_choice: "required" | tool_choice: "none" | tool_choice: {"type": "function", "function": {"name": "X"}} | tool_choice: "auto" |
| SiliconFlow | tool_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 → провайдер):
- Формат tool-определений: маппинг
parameters↔input_schema, развёртывание/оборачивание объектаfunctionдля Anthropic - Формат tool choice: трансляция
"required"↔{"type": "any"} - Структура запроса: различные role для сообщений с tool result
Исходящие (провайдер → gateway → клиент):
- Формат ответа:
tool_usecontent block Anthropic → массивtool_callsв стиле OpenAI - Парсинг аргументов: Anthropic возвращает объекты; OpenAI-совместимые — JSON-строки
- Причина остановки:
stop_reason: "tool_use"→finish_reason: "tool_calls" - События streaming:
content_block_start/content_block_deltaAnthropic →delta.tool_callschunk в стиле 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
| Вам нужно... | Лучший вариант | Почему |
|---|---|---|
| Строгая валидация schema | OpenAI или DeepSeek (Beta) | Единственные провайдеры с серверным strict: true |
| Чередование текста и tool call | Anthropic | Архитектура content block нативно поддерживает |
| Совместимость с OpenAI SDK | DashScope, DeepSeek, SiliconFlow | Без изменений кода для tool-определений и ответов |
| Максимум tool | OpenAI | Практический лимит ~128+; tool_search для больших реестров |
| Бюджетный function calling | DeepSeek или SiliconFlow | Самая низкая цена за token с полной поддержкой tool call |
| Thinking mode + tool calling | DeepSeek V3.2+ или Qwen3 | Оба поддерживают tool call в режиме рассуждений |
| Multi-provider fallback | TheRouter | Нормализует форматы tool call всех провайдеров |
Типичные ловушки
-
Двойное JSON-кодирование: некоторые OpenAI-совместимые клиенты двойно кодируют arguments при проксировании. DashScope отвергает такие запросы — известная проблема.
-
Формат ID tool call: OpenAI использует префикс
call_, Anthropic —toolu_, DeepSeek —call_. Не полагайтесь на единый формат. -
Пустой
contentв tool call сообщениях: при возврате tool call у OpenAI-совместимых моделейcontentобычноnull. Некоторые клиентские библиотеки не обрабатываютnull. -
DashScope GLM модели: при использовании моделей GLM через DashScope обязательно добавьте
extra_body={"tool_stream": True}. Без этого модель не вернётtool_calls. -
DeepSeek thinking mode: V3.2+ поддерживает tool call в thinking mode, но параллельное поведение может отличаться от обычного режима.
-
Anthropic
tool_resultrole: результаты tool отправляются как сообщенияrole: "user", а неrole: "tool". Это ловит каждого разработчика, мигрирующего с OpenAI. -
Накопление 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).