> Для получения чистого Markdown любой страницы добавьте .md к URL‑адресу страницы.  
> Для полного индекса документации см. https://api.vega.chat/docs/llms.txt.  
> Для полного содержания документации см. https://api.vega.chat/docs/llms-full.txt.  
> Для интеграции AI‑клиентов (Claude Code, Cursor и др.) подключитесь к MCP‑серверу по адресу https://api.vega.chat/docs/_mcp/server.

# Инструменты и вызов функций

Вызовы инструментов (также известные как вызовы функций) дают LLM доступ к внешним инструментам. LLM не вызывает инструменты напрямую. Вместо этого он предлагает, какой инструмент вызвать. Пользователь затем вызывает инструмент отдельно и передаёт результаты обратно LLM. Наконец LLM формирует ответ в виде ответа на исходный вопрос пользователя.

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

**Поддерживаемые модели**: Вы можете найти модели, поддерживающие вызов инструментов, отфильтровав их по [/aimodels?supported_parameters=tools](/aimodels?supported_parameters=tools).

Если вы хотите увидеть полный сквозной пример, продолжайте чтение.

## Примеры тела запросов

Вызов инструментов в VEGA включает три ключевых шага. Ниже представлены обязательные форматы тела запросов для каждого шага:

### Шаг 1: Запрос инференса с инструментами

```json
{
  "model": "google/gemini-3-flash-preview",
  "messages": [
    {
      "role": "user",
      "content": "What are the titles of some James Joyce books?"
    }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "search_gutenberg_books",
        "description": "Search for books in the Project Gutenberg library",
        "parameters": {
          "type": "object",
          "properties": {
            "search_terms": {
              "type": "array",
              "items": {"type": "string"},
              "description": "List of search terms to find books"
            }
          },
          "required": ["search_terms"]
        }
      }
    }
  ]
}
```

### Шаг 2: Выполнение инструмента (на клиенте)

После получения ответа модели с `tool_calls` выполните запрошенный инструмент локально и подготовьте результат:

```javascript
// Model responds with tool_calls, you execute the tool locally
const toolResult = await searchGutenbergBooks(["James", "Joyce"]);
```

### Шаг 3: Запрос инференса с результатами инструмента

```json
{
  "model": "google/gemini-3-flash-preview",
  "messages": [
    {
      "role": "user",
      "content": "What are the titles of some James Joyce books?"
    },
    {
      "role": "assistant",
      "content": null,
      "tool_calls": [
        {
          "id": "call_abc123",
          "type": "function",
          "function": {
            "name": "search_gutenberg_books",
            "arguments": "{\"search_terms\": [\"James\", \"Joyce\"]}"
          }
        }
      ]
    },
    {
      "role": "tool",
      "tool_call_id": "call_abc123",
      "content": "[{\"id\": 4300, \"title\": \"Ulysses\", \"authors\": [{\"name\": \"Joyce, James\"}]}]"
    }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "search_gutenberg_books",
        "description": "Search for books in the Project Gutenberg library",
        "parameters": {
          "type": "object",
          "properties": {
            "search_terms": {
              "type": "array",
              "items": {"type": "string"},
              "description": "List of search terms to find books"
            }
          },
          "required": ["search_terms"]
        }
      }
    }
  ]
}
```

**Примечание**: Параметр `tools` должен присутствовать в каждом запросе (Шаги 1 и 3), чтобы роутер мог валидировать схему инструмента при каждом вызове.

### Пример вызова инструмента

Ниже приведён пример кода на Python, который даёт LLM возможность вызывать внешний API — в данном случае Project Gutenberg — для поиска книг.

Сначала сделаем базовую настройку:

```typescript title="TypeScript SDK"
import { OpenRouter } from '@openrouter/sdk';

const VEGA_API_KEY = "{{API_KEY_REF}}";

// You can use any model that supports tool calling
const MODEL = "{{MODEL}}";

const client = new OpenRouter({
  apiKey: VEGA_API_KEY,
});

const task = "What are the titles of some James Joyce books?";

const messages = [
  {
    role: "system",
    content: "You are a helpful assistant."
  },
  {
    role: "user",
    content: task,
  }
];
```

```python
import json, requests
from openai import OpenAI

VEGA_API_KEY = f"{{API_KEY_REF}}"

# You can use any model that supports tool calling
MODEL = "{{MODEL}}"

openai_client = OpenAI(
  base_url="https://api.vega.chat/api/v1",
  api_key=VEGA_API_KEY,
)

task = "What are the titles of some James Joyce books?"

messages = [
  {
    "role": "system",
    "content": "You are a helpful assistant."
  },
  {
    "role": "user",
    "content": task,
  }
]

```

```typescript title="TypeScript (fetch)"
const response = await fetch('https://api.vega.chat/api/v1/chat/completions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer {{API_KEY_REF}}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: '{{MODEL}}',
    messages: [
      { role: 'system', content: 'You are a helpful assistant.' },
      {
        role: 'user',
        content: 'What are the titles of some James Joyce books?',
      },
    ],
  }),
});
```

### Определение инструмента

Далее определяем инструмент, который будем вызывать. Помните, что инструмент будет *запрошен* LLM, но именно наш код отвечает за выполнение вызова и возврат результатов LLM.

```typescript title="TypeScript SDK"
async function searchGutenbergBooks(searchTerms: string[]): Promise<Book[]> {
  const searchQuery = searchTerms.join(' ');
  const url = 'https://gutendex.com/books';
  const response = await fetch(`${url}?search=${searchQuery}`);
  const data = await response.json();

  return data.results.map((book: any) => ({
    id: book.id,
    title: book.title,
    authors: book.authors,
  }));
}

const tools = [
  {
    type: 'function',
    function: {
      name: 'searchGutenbergBooks',
      description:
        'Search for books in the Project Gutenberg library based on specified search terms',
      parameters: {
        type: 'object',
        properties: {
          search_terms: {
            type: 'array',
            items: {
              type: 'string',
            },
            description:
              "List of search terms to find books in the Gutenberg library (e.g. ['dickens', 'great'] to search for books by Dickens with 'great' in the title)",
          },
        },
        required: ['search_terms'],
      },
    },
  },
];

const TOOL_MAPPING = {
  searchGutenbergBooks,
};
```

```python
def search_gutenberg_books(search_terms):
    search_query = " ".join(search_terms)
    url = "https://gutendex.com/books"
    response = requests.get(url, params={"search": search_query})

    simplified_results = []
    for book in response.json().get("results", []):
        simplified_results.append({
            "id": book.get("id"),
            "title": book.get("title"),
            "authors": book.get("authors")
        })

    return simplified_results

tools = [
  {
    "type": "function",
    "function": {
      "name": "search_gutenberg_books",
      "description": "Search for books in the Project Gutenberg library based on specified search terms",
      "parameters": {
        "type": "object",
        "properties": {
          "search_terms": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "List of search terms to find books in the Gutenberg library (e.g. ['dickens', 'great'] to search for books by Dickens with 'great' in the title)"
          }
        },
        "required": ["search_terms"]
      }
    }
  }
]

TOOL_MAPPING = {
    "search_gutenberg_books": search_gutenberg_books
}
```

Обратите внимание, что «инструмент» — это обычная функция. Мы затем формируем JSON‑«спецификацию», совместимую с параметрами вызова функций OpenAI, и передаём её LLM, чтобы модель знала, что инструмент доступен и как его использовать. При необходимости модель запросит инструмент вместе с аргументами. Мы локально обрабатываем вызов, вызываем функцию и возвращаем результат LLM.

### Использование инструмента и получение результатов

Сделаем первый запрос к API VEGA:

```typescript title="TypeScript SDK"
const result = await client.chat.send({
  model: '{{MODEL}}',
  tools,
  messages,
  stream: false,
});

const response_1 = result.choices[0].message;
```

```python
request_1 = {
    "model": {{MODEL}},
    "tools": tools,
    "messages": messages
}

response_1 = openai_client.chat.completions.create(**request_1).message
```

```typescript title="TypeScript (fetch)"
const request_1 = await fetch('https://api.vega.chat/api/v1/chat/completions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer {{API_KEY_REF}}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: '{{MODEL}}',
    tools,
    messages,
  }),
});

const data = await request_1.json();
const response_1 = data.choices[0].message;
```

LLM отвечает с `finish_reason` = `tool_calls` и массивом `tool_calls`. В типичном обработчике ответа LLM следует проверять `finish_reason` перед обработкой вызовов, но здесь мы будем считать, что он уже есть. Обрабатываем вызов инструмента:

```typescript title="TypeScript SDK"
// Добавляем ответ в массив messages, чтобы у LLM был полный контекст
// Легко забыть этот шаг!
messages.push(response_1);

// Обрабатываем запрошенные вызовы инструментов, используя наш инструмент поиска книг
for (const toolCall of response_1.tool_calls) {
  const toolName = toolCall.function.name;
  const { search_params } = JSON.parse(toolCall.function.arguments);
  const toolResponse = await TOOL_MAPPING[toolName](search_params);
  messages.push({
    role: 'tool',
    toolCallId: toolCall.id,
    name: toolName,
    content: JSON.stringify(toolResponse),
  });
}
```

```python
# Append the response to the messages array so the LLM has the full context
# It's easy to forget this step!
messages.append(response_1)

# Now we process the requested tool calls, and use our book lookup tool
for tool_call in response_1.tool_calls:
    '''
    In this case we only provided one tool, so we know what function to call.
    When providing multiple tools, you can inspect `tool_call.function.name`
    to figure out what function you need to call locally.
    '''
    tool_name = tool_call.function.name
    tool_args = json.loads(tool_call.function.arguments)
    tool_response = TOOL_MAPPING[tool_name](**tool_args)
    messages.append({
      "role": "tool",
      "tool_call_id": tool_call.id,
      "content": json.dumps(tool_response),
    })
```

Теперь массив `messages` содержит:

1. Наш исходный запрос  
2. Ответ LLM (с запросом вызова инструмента)  
3. Результат вызова инструмента (JSON‑объект, полученный от API Project Gutenberg)

Делаем второй запрос к VEGA, надеясь получить окончательный результат:

```typescript title="TypeScript SDK"
const response_2 = await client.chat.send({
  model: '{{MODEL}}',
  messages,
  tools,
  stream: false,
});

console.log(response_2.choices[0].message.content);
```

```python
request_2 = {
  "model": MODEL,
  "messages": messages,
  "tools": tools
}

response_2 = openai_client.chat.completions.create(**request_2)

print(response_2.choices[0].message.content)
```

```typescript title="TypeScript (fetch)"
const response = await fetch('https://api.vega.chat/api/v1/chat/completions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer {{API_KEY_REF}}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: '{{MODEL}}',
    messages,
    tools,
  }),
});

const data = await response.json();
console.log(data.choices[0].message.content);
```

Вывод будет примерно таким:

```text
Here are some books by James Joyce:

*   *Ulysses*
*   *Dubliners*
*   *A Portrait of the Artist as a Young Man*
*   *Chamber Music*
*   *Exiles: A Play in Three Acts*
```

Мы сделали это! Инструмент успешно использован в запросе.

## Перемежающийся рассудок

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

**Важно**: Перемежающийся рассудок увеличивает расход токенов и задержку ответа. Оцените бюджет и требования к производительности перед включением этой функции.

### Как работает перемежающийся рассудок

С перемежающимся рассудком модель может:

* Рассуждать о результатах вызова инструмента, прежде чем решить, что делать дальше  
* Цепочкой вызывать несколько инструментов, вставляя между ними шаги рассуждения  
* Принимать более нюансированные решения на основе промежуточных результатов  
* Предоставлять прозрачные обоснования своего выбора инструмента

### Пример: многошаговое исследование с рассуждениями

Ниже пример, показывающий, как модель может использовать перемежающийся рассудок для исследования темы из разных источников:

**Исходный запрос:**

```json
{
  "model": "anthropic/claude-sonnet-4.5",
  "messages": [
    {
      "role": "user",
      "content": "Research the environmental impact of electric vehicles and provide a comprehensive analysis."
    }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "search_academic_papers",
        "description": "Search for academic papers on a given topic",
        "parameters": {
          "type": "object",
          "properties": {
            "query": {"type": "string"},
            "field": {"type": "string"}
          },
          "required": ["query"]
        }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "get_latest_statistics",
        "description": "Get latest statistics on a topic",
        "parameters": {
          "type": "object",
          "properties": {
            "topic": {"type": "string"},
            "year": {"type": "integer"}
          },
          "required": ["topic"]
        }
      }
    }
  ]
}
```

**Рассуждения модели и вызовы инструментов:**

1. **Начальное рассуждение**: «Мне нужно исследовать экологическое воздействие электромобилей. Сначала посмотрю академические статьи, чтобы получить рецензируемые данные».  
2. **Первый вызов инструмента**: `search_academic_papers({"query": "electric vehicle lifecycle environmental impact", "field": "environmental science"})`  
3. **После первого результата**: «Статьи показывают разнородные результаты по влиянию производства. Нужно добавить актуальную статистику».  
4. **Второй вызов инструмента**: `get_latest_statistics({"topic": "electric vehicle carbon footprint", "year": 2024})`  
5. **После второго результата**: «Теперь есть и академические данные, и свежие цифры. Нужно изучить влияние производства батарей».  
6. **Третий вызов инструмента**: `search_academic_papers({"query": "electric vehicle battery manufacturing environmental cost", "field": "materials science"})`  
7. **Итоговый анализ**: Синтезирует всю собранную информацию в комплексный ответ.

### Лучшие практики перемежающегося рассудка

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

### Соображения при реализации

* Модели могут отвечать дольше из‑за дополнительных шагов рассуждения.  
* Расход токенов будет выше из‑за процесса рассуждения.  
* Качество рассуждений зависит от возможностей конкретной модели.  
* Некоторые модели лучше подходят для этого подхода, чем другие.

## Простой агентный цикл

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

Ниже пример простого агентного цикла (использует те же `tools` и начальные `messages`, что и выше):

```typescript title="TypeScript SDK"
async function callLLM(messages: Message[]): Promise<ChatResponse> {
  const result = await client.chat.send({
    model: '{{MODEL}}',
    tools,
    messages,
    stream: false,
  });

  messages.push(result.choices[0].message);
  return result;
}

async function getToolResponse(response: ChatResponse): Promise<Message> {
  const toolCall = response.choices[0].message.toolCalls[0];
  const toolName = toolCall.function.name;
  const toolArgs = JSON.parse(toolCall.function.arguments);

  // Look up the correct tool locally, and call it with the provided arguments
  // Other tools can be added without changing the agentic loop
  const toolResult = await TOOL_MAPPING[toolName](toolArgs);

  return {
    role: 'tool',
    toolCallId: toolCall.id,
    content: toolResult,
  };
}

const maxIterations = 10;
let iterationCount = 0;

while (iterationCount < maxIterations) {
  iterationCount++;
  const response = await callLLM(messages);

  if (response.choices[0].message.toolCalls) {
    messages.push(await getToolResponse(response));
  } else {
    break;
  }
}

if (iterationCount >= maxIterations) {
  console.warn("Warning: Maximum iterations reached");
}

console.log(messages[messages.length - 1].content);
```

```python
def call_llm(msgs):
    resp = openai_client.chat.completions.create(
        model={{MODEL}},
        tools=tools,
        messages=msgs
    )
    msgs.append(resp.choices[0].message.dict())
    return resp

def get_tool_response(response):
    tool_call = response.choices[0].message.tool_calls[0]
    tool_name = tool_call.function.name
    tool_args = json.loads(tool_call.function.arguments)

    # Look up the correct tool locally, and call it with the provided arguments
    # Other tools can be added without changing the agentic loop
    tool_result = TOOL_MAPPING[tool_name](**tool_args)

    return {
        "role": "tool",
        "tool_call_id": tool_call.id,
        "content": tool_result,
    }

max_iterations = 10
iteration_count = 0

while iteration_count < max_iterations:
    iteration_count += 1
    resp = call_llm(_messages)

    if resp.choices[0].message.tool_calls is not None:
        messages.append(get_tool_response(resp))
    else:
        break

if iteration_count >= max_iterations:
    print("Warning: Maximum iterations reached")

print(messages[-1]['content'])
```

## Лучшие практики и продвинутые шаблоны

### Руководство по определению функций

При описании инструментов для LLM придерживайтесь следующих рекомендаций:

**Ясные и описательные имена**: Выбирайте имена функций, чётко отражающие их назначение.

```json
// Хорошо: ясно и конкретно
{ "name": "get_weather_forecast" }
```

```json
// Плохо: слишком расплывчато
{ "name": "weather" }
```

**Подробные описания**: Предоставляйте детальные описания, помогающие модели понять, когда и как использовать инструмент.

```json
{
  "description": "Get current weather conditions and 5-day forecast for a specific location. Supports cities, zip codes, and coordinates.",
  "parameters": {
    "type": "object",
    "properties": {
      "location": {
        "type": "string",
        "description": "City name, zip code, or coordinates (lat,lng). Examples: 'New York', '10001', '40.7128,-74.0060'"
      },
      "units": {
        "type": "string",
        "enum": ["celsius", "fahrenheit"],
        "description": "Temperature unit preference",
        "default": "celsius"
      }
    },
    "required": ["location"]
  }
}
```

### Потоковая передача с вызовами инструментов

При использовании потоковых ответов с вызовами инструментов обрабатывайте разные типы контента корректно:

```typescript
const stream = await fetch('/api/chat/completions', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    model: 'anthropic/claude-sonnet-4.5',
    messages: messages,
    tools: tools,
    stream: true
  })
});

const reader = stream.body.getReader();
let toolCalls = [];

while (true) {
  const { done, value } = await reader.read();
  if (done) {
    break;
  }

  const chunk = new TextDecoder().decode(value);
  const lines = chunk.split('\n').filter(line => line.trim());

  for (const line of lines) {
    if (line.startsWith('data: ')) {
      const data = JSON.parse(line.slice(6));

      if (data.choices[0].delta.tool_calls) {
        toolCalls.push(...data.choices[0].delta.tool_calls);
      }

      if (data.choices[0].delta.finish_reason === 'tool_calls') {
        await handleToolCalls(toolCalls);
      } else if (data.choices[0].delta.finish_reason === 'stop') {
        // Regular completion without tool calls
        break;
      }
    }
  }
}
```

### Конфигурация выбора инструмента

Управляйте использованием инструментов параметром `tool_choice`:

```json
// Позволить модели решить сама (по умолчанию)
{ "tool_choice": "auto" }
```

```json
// Отключить использование инструментов
{ "tool_choice": "none" }
```

```json
// Принудительно задать конкретный инструмент
{
  "tool_choice": {
    "type": "function",
    "function": {"name": "search_database"}
  }
}
```

### Параллельные вызовы инструментов

Контролируйте возможность одновременного вызова нескольких инструментов параметром `parallel_tool_calls` (по умолчанию — true для большинства моделей):

```json
// Отключить параллельные вызовы — инструменты будут вызываться последовательно
{ "parallel_tool_calls": false }
```

Когда `parallel_tool_calls` = false, модель будет запрашивать только один вызов инструмента за раз, а не несколько одновременно.

### Многоинструментные рабочие процессы

Разрабатывайте инструменты, которые хорошо сочетаются друг с другом:

```json
{
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "search_products",
        "description": "Search for products in the catalog"
      }
    },
    {
      "type": "function",
      "function": {
        "name": "get_product_details",
        "description": "Get detailed information about a specific product"
      }
    },
    {
      "type": "function",
      "function": {
        "name": "check_inventory",
        "description": "Check current inventory levels for a product"
      }
    }
  ]
}
```

Это позволяет модели естественно цепочкой выполнять операции: поиск → получение деталей → проверка наличия.

### Отслеживание надёжности

VEGA отслеживает, насколько надёжно каждый провайдер завершает вызовы инструментов, и отображает это как **Коэффициент ошибок вызовов инструментов** на вкладке Performance каждой модели. Тот же сигнал используется для упорядочения провайдеров в запросах с вызовами инструментов в [Auto Exacto](/docs/guides/routing/auto-exacto). Подробности о валидаторе, JSON Schema draft, семантике regex и классификации по каждому вызову инструмента см. в разделе [How Tool-Calling Success Rate Is Measured](/docs/guides/routing/auto-exacto#how-tool-calling-success-rate-is-measured).

Для более подробной информации о формате сообщений VEGA и параметрах инструментов см. [API Reference](https://api.vega.chat/docs/api-reference/overview).