Клиентские инструменты
Клиентские инструменты выполняются на вашей стороне. Когда модель вызывает такой инструмент, задача переходит в interrupted, а вы возвращаете результат и обработка продолжается.
В отличие от инструментов ассистента (которые вызывает сервер), клиентские инструменты задаёте вы прямо в запросе. Их удобно использовать, когда данные или действие доступны только на вашей стороне: геолокация пользователя, доступ к вашей БД, интерактивное подтверждение.
1. Объявить инструменты
Добавьте client_tools в тело POST /api/tasks:
{
"assistant_id": 1,
"input": [{ "type": "text", "text": "Что рядом со мной?" }],
"client_tools": [
{
"name": "get_user_location",
"description": "Возвращает город пользователя",
"input_schema": {
"type": "object",
"properties": { "precision": { "type": "string" } }
}
}
]
}
Поля name и description обязательны, input_schema (JSON Schema) — опционально.
2. Дождаться прерывания
Если модель вызвала клиентский инструмент, опрос GET /api/tasks/{uuid} вернёт статус
interrupted и список ожидающих вызовов в pending_tool_calls:
{
"uuid": "550e8400-…",
"status": "interrupted",
"pending_tool_calls": [
{ "id": "call_abc", "name": "get_user_location", "arguments": { "precision": "city" } }
]
}
3. Вернуть результаты
Выполните инструменты у себя и отправьте результаты. Нужно покрыть все вызовы из pending_tool_calls.
{
"tool_results": [
{ "id": "call_abc", "content": "Берлин" }
]
}
Ошибку инструмента верните так:
{ "id": "call_abc", "error": "Location unavailable", "reason": "permission_denied" }
Каждый элемент несёт id (из pending_tool_calls) и либо content, либо error (+опциональный reason).
4. Продолжение
После отправки результатов задача возвращается в processing. Продолжайте опрашивать: цикл
interrupted → tool-results → processing может повториться несколько раз, пока задача не
достигнет completed или failed.
POST /api/tasks → pending
GET /api/tasks/{uuid} → interrupted # модель вызвала client-инструмент
POST …/tool-results → processing # вернули результат
GET /api/tasks/{uuid} → completed # готово (или снова interrupted)
POST …/cancel → canceled # либо отказались отвечать
POST /api/tasks/cancel-interrupted # отменить все прерванные разом
5. Отказаться от прерванной задачи
Если ответить на вызов нечем — пользователь ушёл, инструмент недоступен, сценарий больше не нужен —
не бросайте задачу висеть в interrupted: она так и останется активной и будет занимать
слот в лимите одновременных задач. Отмените её:
{ "reason": "Пользователь закрыл приложение" } // опционально, до 1000 символов
Задача переходит в терминальный статус canceled, reason сохраняется в
error (по умолчанию — Task canceled by the client.), слот в лимите
освобождается сразу. Если задан callback_url, отмена тоже доставляется вебхуком.
{ "uuid": "550e8400-…", "status": "canceled" }
Отменить можно только задачу в статусе interrupted — иначе 409.
Задачу в pending или processing отменять нечем: Aist Core про отмену не знает,
всё равно досчитает ход и пришлёт результат, за который уже списан баланс.
Отменить все прерванные разом
Получили 429 и не знаете, какие задачи держат лимит? Посмотреть их можно в
списке задач (GET /api/tasks?status=interrupted),
а снести все разом — одним запросом: типичная уборка хвостов после падения процесса. Тело такое же
(reason опционален и применится ко всем):
{ "canceled": 2, "uuids": ["550e8400-…", "660f9511-…"] }
Трогаются только ваши задачи в статусе interrupted — pending и
processing продолжают работать. Запрос идемпотентен: отменять нечего — вернётся
{ "canceled": 0, "uuids": [] }, никакого 409.
Итоговый tool_call в output
После завершения вызов клиентского инструмента появляется в output как обычный tool_call (без префикса):
{
"type": "tool_call",
"tool_call": {
"name": "get_user_location",
"input": { "precision": "city" },
"output": { "type": "content", "content": [{ "type": "text", "text": "Берлин" }] }
}
}
tool_resultsдолжны покрывать все ожидающие вызовы, иначе422.POST /tool-resultsдля задачи не в статусеinterruptedвернёт409.- Прерванная задача считается активной и учитывается в лимите одновременных задач — пока вы не ответите на вызов или не отмените её.
POST /cancelработает только для задачи в статусеinterrupted, иначе409.POST /cancel-interrupted409не возвращает: отменяет то, что есть, хоть ноль задач.
runTask сам проходит весь interrupt-цикл:
вызывает ваш обработчик, отправляет результат и продолжает — до завершения. Обе отмены тоже есть в SDK:
cancelTask(uuid, { reason }) и cancelInterruptedTasks().