Claude Platform Docs
MessagesИнфраструктура инструментов

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

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

«Programmatic tool calling» (программный вызов инструментов) позволяет Claude писать код, который программно вызывает ваши инструменты внутри контейнера выполнения кода, вместо того чтобы требовать обращения к модели для каждого вызова инструмента. Это снижает «latency» (задержку) в рабочих процессах с несколькими инструментами и уменьшает расход токенов, позволяя Claude фильтровать или обрабатывать данные до того, как они попадут в «context window» (контекстное окно) модели. В бенчмарках агентного поиска, таких как BrowseComp и DeepSearchQA, которые проверяют многошаговое веб-исследование и сложный поиск информации, добавление программного вызова инструментов поверх базовых инструментов поиска улучшило результаты в среднем на 11% при использовании на 24% меньше входных токенов (см. Improved web search with dynamic filtering).

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

Программный вызов инструментов требует инструмента выполнения кода с версией инструмента code_execution_20260120 или более поздней. Чтобы до отправки запроса проверить, поддерживает ли модель программный вызов инструментов, прочитайте её значение capabilities.code_execution.supported из Models API. Это поле описано в разделе Использование Models API.

Быстрый старт

Вот пример, в котором Claude программно несколько раз запрашивает базу данных и агрегирует результаты. Добавление allowed_callers: ["code_execution_20260120"] в определение инструмента — это то, что делает этот инструмент вызываемым из выполнения кода (см. Поле allowed_callers):

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=4096,
    messages=[
        {
            "role": "user",
            "content": "Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue",
        }
    ],
    tools=[
        {"type": "code_execution_20260120", "name": "code_execution"},
        {
            "name": "query_database",
            "description": "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
            "input_schema": {
                "type": "object",
                "properties": {
                    "sql": {"type": "string", "description": "SQL query to execute"}
                },
                "required": ["sql"],
            },
            "allowed_callers": ["code_execution_20260120"],
        },
    ],
)

print(response)

Ответ останавливается с stop_reason: "tool_use", идентификатором container и блоком tool_use для query_database, поле caller которого идентифицирует запуск выполнения кода, вызвавший его. Верните результат, как показано в шаге 3 примера рабочего процесса, чтобы код мог завершиться.

Как работает программный вызов инструментов

Когда вы настраиваете инструмент так, чтобы его можно было вызывать из выполнения кода, и Claude определяет, что этот инструмент нужен:

  1. Claude пишет код на Python, который вызывает инструмент как функцию, потенциально включая несколько вызовов инструментов и логику пред- и постобработки
  2. Claude запускает этот код в изолированном контейнере через выполнение кода
  3. Когда вызывается функция инструмента, выполнение кода приостанавливается, и API возвращает блок tool_use
  4. Вы предоставляете результат инструмента, и выполнение кода продолжается (промежуточные результаты не загружаются в контекстное окно Claude)
  5. Когда всё выполнение кода завершается, Claude получает итоговый вывод и продолжает работу над задачей

Этот подход особенно полезен для:

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

Основные понятия

Поле allowed_callers

Поле allowed_callers указывает, какие контексты могут вызывать инструмент:

{
  "name": "query_database",
  "description": "Execute a SQL query against the database",
  "input_schema": {
    // ...
  },
  "allowed_callers": ["code_execution_20260120"]
}

Возможные значения:

  • ["direct"] — Claude направляется вызывать этот инструмент напрямую (по умолчанию, если не указано)
  • ["code_execution_20260120"] — Claude направляется вызывать этот инструмент только из выполнения кода
  • ["direct", "code_execution_20260120"] — Claude может вызывать этот инструмент напрямую или из выполнения кода

И "code_execution_20260120", и "code_execution_20260521" принимаются в allowed_callers и взаимозаменяемы: запрос, использующий любую из версий инструмента выполнения кода, удовлетворяет инструментам, в которых указан любой из этих вызывающих. Блоки ответа всегда помечают вызывающего как code_execution_20260120 независимо от того, какую версию объявил запрос.

Поле caller в ответах

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

Прямой вызов (традиционное использование инструментов):

{
  "type": "tool_use",
  "id": "toolu_abc123",
  "name": "query_database",
  "input": { "sql": "<sql>" },
  "caller": { "type": "direct" }
}

Программный вызов:

{
  "type": "tool_use",
  "id": "toolu_xyz789",
  "name": "query_database",
  "input": { "sql": "<sql>" },
  "caller": {
    "type": "code_execution_20260120",
    "tool_id": "srvtoolu_abc123"
  }
}

tool_id — это id блока server_tool_use выполнения кода, который сделал вызов, поэтому вы можете сопоставить каждый программный tool_use с запуском выполнения кода, который его породил.

Жизненный цикл контейнера

Программный вызов инструментов использует те же контейнеры, что и выполнение кода:

  • Создание контейнера: для каждого запроса создаётся новый контейнер, если вы не используете существующий повторно
  • Идентификатор контейнера: возвращается в ответах в поле container вместе с временной меткой expires_at
  • Повторное использование: передайте идентификатор контейнера обратно в следующем запросе, чтобы сохранить состояние. Пока программный вызов инструмента ожидает вашего результата, идентификатор контейнера в этом запросе обязателен, а не опционален: API отклоняет запрос без него.
  • Истечение срока: expires_at сообщает, сколько времени осталось у контейнера. Простаивающие контейнеры в настоящее время освобождаются примерно через 5 минут, и ни один контейнер нельзя использовать повторно более чем через 30 дней после его создания.

Пример рабочего процесса

Вот как работает полный поток программного вызова инструментов:

Шаг 1: Начальный запрос

Отправьте запрос с выполнением кода и инструментом, допускающим программный вызов. Чтобы включить программный вызов, добавьте поле allowed_callers в определение инструмента.

Форма запроса идентична примеру из раздела Быстрый старт: включите code_execution в список инструментов, добавьте allowed_callers: ["code_execution_20260120"] к любому инструменту, который вы хотите, чтобы Claude вызывал из кода, и отправьте пользовательское сообщение. Остальные шаги этого рабочего процесса используют пользовательское сообщение "Query customer purchase history from the last quarter and identify our top 5 customers by revenue".

Шаг 2: Ответ API с вызовом инструмента

Claude пишет код, который вызывает ваш инструмент. API приостанавливается и возвращает:

Output
{
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "I'll query the purchase history and analyze the results."
    },
    {
      "type": "server_tool_use",
      "id": "srvtoolu_abc123",
      "name": "code_execution",
      "input": {
        "code": "import json\n\nrows = json.loads(await query_database({'sql': '<sql>'}))\ntop_customers = sorted(rows, key=lambda x: x['revenue'], reverse=True)[:5]\nprint(f'Top 5 customers: {top_customers}')"
      }
    },
    {
      "type": "tool_use",
      "id": "toolu_def456",
      "name": "query_database",
      "input": { "sql": "<sql>" },
      "caller": {
        "type": "code_execution_20260120",
        "tool_id": "srvtoolu_abc123"
      }
    }
  ],
  "container": {
    "id": "container_xyz789",
    "expires_at": "2026-01-20T14:30:00Z"
  },
  "stop_reason": "tool_use"
}

Шаг 3: Предоставьте результат инструмента

Отправьте полную историю разговора плюс результат вашего инструмента. В этом запросе важны три детали:

  • Пользовательское сообщение, несущее ваш результат, может содержать только блоки tool_result. См. Ограничения форматирования сообщений.
  • Передайте идентификатор container из приостановленного ответа. API отклоняет продолжение, в котором есть ожидающие программные вызовы инструментов, но нет идентификатора контейнера.
  • Отправьте тот же массив tools, что и в исходном запросе. Инструмент выполнения кода должен по-прежнему присутствовать, чтобы приостановленный код мог возобновиться, а инструменты, которые вы отправляете в этом запросе, — это определения, которые Claude и выполняющийся код могут использовать до конца хода.
response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=4096,
    container="container_xyz789",  # Reuse the container
    messages=[
        {
            "role": "user",
            "content": "Query customer purchase history from the last quarter and identify our top 5 customers by revenue",
        },
        {
            "role": "assistant",
            "content": [
                {
                    "type": "text",
                    "text": "I'll query the purchase history and analyze the results.",
                },
                {
                    "type": "server_tool_use",
                    "id": "srvtoolu_abc123",
                    "name": "code_execution",
                    "input": {"code": "..."},
                },
                {
                    "type": "tool_use",
                    "id": "toolu_def456",
                    "name": "query_database",
                    "input": {"sql": "<sql>"},
                    "caller": {
                        "type": "code_execution_20260120",
                        "tool_id": "srvtoolu_abc123",
                    },
                },
            ],
        },
        {
            "role": "user",
            "content": [
                {
                    "type": "tool_result",
                    "tool_use_id": "toolu_def456",
                    "content": '[{"customer_id": "C1", "revenue": 45000}, {"customer_id": "C2", "revenue": 38000}, ...]',
                }
            ],
        },
    ],
    # Тот же массив tools, что и в исходном запросе
    tools=[
        {"type": "code_execution_20260120", "name": "code_execution"},
        {
            "name": "query_database",
            "description": "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
            "input_schema": {
                "type": "object",
                "properties": {
                    "sql": {"type": "string", "description": "SQL query to execute"}
                },
                "required": ["sql"],
            },
            "allowed_callers": ["code_execution_20260120"],
        },
    ],
)

print(response)

Шаг 4: Следующий вызов инструмента или завершение

Код продолжает с того места, где он приостановился, и обрабатывает ваш результат. Каждый ответ-продолжение либо снова приостанавливается с дополнительными программными блоками tool_use, либо завершает выполнение кода и позволяет Claude продолжить ход (шаг 5). Проверяйте stop_reason и caller каждого блока tool_use, чтобы различить эти два случая: ответ, который приостанавливается в ожидании вас, имеет stop_reason: "tool_use" и блок tool_use, в caller которого указана версия выполнения кода, и вы повторяете шаг 3 с tool_result для каждого ожидающего программного вызова в одном пользовательском сообщении.

Шаг 5: Итоговый ответ

Когда выполнение кода завершается, Claude предоставляет итоговый ответ:

Output
{
  "content": [
    {
      "type": "code_execution_tool_result",
      "tool_use_id": "srvtoolu_abc123",
      "content": {
        "type": "code_execution_result",
        "stdout": "Top 5 customers: [{'customer_id': 'C1', 'revenue': 45000}, {'customer_id': 'C2', 'revenue': 38000}, {'customer_id': 'C5', 'revenue': 32000}, {'customer_id': 'C8', 'revenue': 28500}, {'customer_id': 'C3', 'revenue': 24000}]",
        "stderr": "",
        "return_code": 0,
        "content": []
      }
    },
    {
      "type": "text",
      "text": "I've analyzed the purchase history from last quarter. Your top 5 customers generated $167,500 in total revenue, with Customer C1 leading at $45,000."
    }
  ],
  "stop_reason": "end_turn"
}

Продвинутые паттерны

Пакетная обработка с циклами

Claude может писать код, который эффективно обрабатывает несколько элементов:

regions = ["West", "East", "Central", "North", "South"]
results = {}
for region in regions:
    rows = json.loads(await query_database({"sql": f"<sql for {region}>"}))
    results[region] = sum(row["revenue"] for row in rows)

# Программная обработка результатов
top_region = max(results.items(), key=lambda x: x[1])
print(f"Top region: {top_region[0]} with ${top_region[1]:,} in revenue")

Этот паттерн:

  • Сокращает количество обращений к модели с N (по одному на регион) до 1
  • Программно обрабатывает большие наборы результатов перед возвратом к Claude
  • Экономит токены, возвращая только агрегированные выводы вместо сырых данных

Раннее завершение

Claude может прекратить обработку, как только критерии успеха выполнены:

endpoints = ["us-east", "eu-west", "apac"]
for endpoint in endpoints:
    status = await check_health({"endpoint": endpoint})
    if status == "healthy":
        print(f"Found healthy endpoint: {endpoint}")
        break  # Stop early, don't check remaining

Условный выбор инструмента

path = "/tmp/example.txt"
file_info = json.loads(await get_file_info({"path": path}))
if file_info["size"] < 10000:
    content = await read_full_file({"path": path})
else:
    content = await read_file_summary({"path": path})
print(content)

Фильтрация данных

server_id = "srv-01"
log_text = await fetch_logs({"server_id": server_id})
errors = [line for line in log_text.splitlines() if "ERROR" in line]
print(f"Found {len(errors)} errors")
for error in errors[-10:]:  # Only return last 10 errors
    print(error)

Формат ответа

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

Когда выполнение кода вызывает инструмент:

{
  "type": "tool_use",
  "id": "toolu_abc123",
  "name": "query_database",
  "input": { "sql": "<sql>" },
  "caller": {
    "type": "code_execution_20260120",
    "tool_id": "srvtoolu_xyz789"
  }
}

Обработка результата инструмента

Результат вашего инструмента передаётся обратно в выполняющийся код:

{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_abc123",
      "content": "[{\"customer_id\": \"C1\", \"revenue\": 45000, \"orders\": 23}, {\"customer_id\": \"C2\", \"revenue\": 38000, \"orders\": 18}, ...]"
    }
  ]
}

Завершение выполнения кода

Когда все вызовы инструментов удовлетворены и код завершается:

{
  "type": "code_execution_tool_result",
  "tool_use_id": "srvtoolu_xyz789",
  "content": {
    "type": "code_execution_result",
    "stdout": "Analysis complete. Top 5 customers identified from 847 total records.",
    "stderr": "",
    "return_code": 0,
    "content": []
  }
}

Обработка ошибок

Распространённые ошибки

ОшибкаГде появляетсяОписаниеРешение
invalid_tool_inputerror_code в блоке ошибки code_execution_tool_result в ответеИнструменту выполнения кода были переданы недопустимые параметрыСм. ошибки инструмента выполнения кода
invalid_request_error (для tool_choice)Ответ с ошибкой HTTP 400tool_choice указывает инструмент, allowed_callers которого не включает "direct"Либо добавьте "direct" в allowed_callers этого инструмента, либо удалите инструмент из tool_choice и позвольте Claude вызывать его из кода

Истечение срока контейнера во время вызова инструмента

Если результат вашего инструмента не поступает в течение примерно 4 минут, ожидающий вызов вызывает TimeoutError внутри выполняющегося кода Claude. Claude видит ошибку в stderr и обычно повторяет вызов:

{
  "type": "code_execution_tool_result",
  "tool_use_id": "srvtoolu_abc123",
  "content": {
    "type": "code_execution_result",
    "stdout": "",
    "stderr": "TimeoutError: Calling tool ['query_database'] timed out (no response after 270s).",
    "return_code": 0,
    "content": []
  }
}

Чтобы предотвратить тайм-ауты:

  • Отслеживайте поле expires_at в ответах
  • Реализуйте тайм-ауты для выполнения ваших инструментов
  • Рассмотрите разбиение длительных операций на более мелкие части

Ошибки выполнения инструмента

Если ваш инструмент возвращает ошибку:

{
  "type": "tool_result",
  "tool_use_id": "toolu_abc123",
  "content": "Error: Query timeout - table lock exceeded 30 seconds"
}

Код Claude получает эту ошибку и может обработать её соответствующим образом.

Ограничения

Несовместимость функций

  • Структурированные выводы: инструменты с strict: true не поддерживаются при программном вызове
  • Выбор инструмента: вы не можете принудительно задать программный вызов конкретного инструмента через tool_choice
  • Параллельное использование инструментов: disable_parallel_tool_use: true не поддерживается при программном вызове

Ограничения входной схемы