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

Иногда модель в одном ответе просит вызвать несколько инструментов сразу. Разберём, как это обработать.

Параллельные вызовы — несколько блоков tool_use в одном ответе модели; их результаты возвращаются вместе, одним сообщением.

Когда так бывает

Если задача распадается на независимые подзадачи, модель может запросить их разом. Например, «сравни погоду в Москве и Париже» → два вызова get_weather сразу. Это экономит раунды обмена: не «вызвал — дождался — вызвал второй», а оба сразу.

{
  "stop_reason": "tool_use",
  "content": [
    {"type": "tool_use", "id": "a1", "name": "get_weather", "input": {"city": "Москва"}},
    {"type": "tool_use", "id": "a2", "name": "get_weather", "input": {"city": "Париж"}}
  ]
}

Правило возврата результатов

Все результаты отправляют одним сообщением с ролью user, в котором лежит список блоков tool_result — по одному на каждый вызов, с соответствующими tool_use_id. Нельзя возвращать их по одному в разных сообщениях.

Обработка параллельных вызовов (запускаемо)

Выполним все вызовы и соберём результаты в один список — чистый Python:

import json

def get_weather(city):
    return {"city": city, "temp": {"Москва": 7, "Париж": 14}.get(city, 0)}

REGISTRY = {"get_weather": get_weather}

# Модель в одном ответе попросила два вызова
tool_uses = [
    {"id": "a1", "name": "get_weather", "input": {"city": "Москва"}},
    {"id": "a2", "name": "get_weather", "input": {"city": "Париж"}},
]

results = []
for tu in tool_uses:
    out = REGISTRY[tu["name"]](**tu["input"])
    results.append({
        "tool_use_id": tu["id"],
        "content": json.dumps(out, ensure_ascii=False),
    })

# Все результаты уходят ОДНИМ сообщением user
for r in results:
    print(r["tool_use_id"], "->", r["content"])
print("Результатов в одном сообщении:", len(results))

Вывод:

a1 -> {"city": "Москва", "temp": 7}
a2 -> {"city": "Париж", "temp": 14}
Результатов в одном сообщении: 2

Можно ускорить выполнение

Раз вызовы независимы, сами функции тоже можно выполнить параллельно (например, два сетевых запроса асинхронно), а потом собрать результаты. Главное — собрать их в одно сообщение перед отправкой модели.

Управление параллельностью

Если параллельные вызовы нежелательны (например, тяжёлые операции), их можно отключить параметром (у Anthropic — disable_parallel_tool_use в tool_choice), и модель будет звать инструменты по одному.

Итог

  • Модель может вернуть несколько tool_use в одном ответе.
  • Все результаты возвращают одним сообщением: список tool_result с нужными tool_use_id.
  • Независимые вызовы можно выполнять параллельно ради скорости.
  • Параллельность при желании отключается настройкой.
Проверьте себя
1. Как нужно возвращать результаты нескольких параллельных вызовов инструментов?
AКаждый отдельным сообщением по очереди
BВсе вместе, одним сообщением user со списком блоков tool_result
CТолько результат первого вызова
DВ системном промпте
2. Зачем модель делает несколько вызовов инструментов сразу?
AЧтобы потратить больше токенов
BЧтобы выполнить независимые подзадачи разом и сократить число раундов обмена
CЭто случайное поведение
DЧтобы обойти rate limit
3. Можно ли запретить модели параллельные вызовы инструментов?
AНет, это невозможно
BДа, например параметром disable_parallel_tool_use в tool_choice у Anthropic
CТолько сменив модель
DТолько отключив tools полностью
Поддержать проект