Программный вызов инструментов
Позвольте 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 определяет, что этот инструмент нужен:
- Claude пишет код на Python, который вызывает инструмент как функцию, потенциально включая несколько вызовов инструментов и логику пред- и постобработки
- Claude запускает этот код в изолированном контейнере через выполнение кода
- Когда вызывается функция инструмента, выполнение кода приостанавливается, и API возвращает блок
tool_use - Вы предоставляете результат инструмента, и выполнение кода продолжается (промежуточные результаты не загружаются в контекстное окно Claude)
- Когда всё выполнение кода завершается, 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 приостанавливается и возвращает:
{
"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 предоставляет итоговый ответ:
{
"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_input | error_code в блоке ошибки code_execution_tool_result в ответе | Инструменту выполнения кода были переданы недопустимые параметры | См. ошибки инструмента выполнения кода |
invalid_request_error (для tool_choice) | Ответ с ошибкой HTTP 400 | tool_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не поддерживается при программном вызове