Клиентские функции Claude используют цикл tool_use → tool_result с серверным промптом и без него. Функцию выполняет ваш клиент. Форматы и ограничения зависят от выбранного API.
{
"model": "claude-opus-5",
"max_tokens": 1024,
"tools": [{
"name": "get_weather",
"description": "Погода по городу",
"input_schema": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]
}
}],
"messages": [{"role": "user", "content": "Какая погода в Москве?"}]
}В ответе придёт блок tool_use с именем инструмента и аргументами — выполните его у себя и верните результат блоком tool_result.
{
"role": "user",
"content": [{
"type": "tool_result",
"tool_use_id": "toolu_…",
"content": "Москва: +18 °C, ясно"
}]
}Codex поддерживает client tool loop на Responses, Chat Completions и Messages; функции исполняет клиент. GPT-6 Sol и GPT-6 Luna объявляют parallel tool calls. Сохраняйте исходные tool/call ID при возврате результата и не подменяйте их между форматами.
| Семейство | Клиентские функции |
|---|---|
| Kimi | Работают на Messages, Chat Completions и Responses; формат tools и выбора инструмента зависит от API |
| Composer | Работают на всех трёх поверхностях; инструмент исполняет клиент. tool_choice — только автоматический |
| Grok | Работают на всех трёх поверхностях; инструмент исполняет клиент. tool_choice — только автоматический |
toolsдолжен быть массивом объектов, у функции обязательно есть имя, имена не повторяются.- Схема аргументов проверяется на глубину до 32 уровней, до 4096 узлов и до 256 КиБ в каноническом виде. Превышение даёт локальный
400до списания. - У моделей
kimi-for-codingиkimi-for-coding-highspeedзначениеtool_choice: "required"не поддерживается и отклоняется локально. Уk3иk3-256kоно проходит.
Цикл работает на всех трёх поверхностях в их собственном формате. Модель возвращает вызов; клиент исполняет инструмент и возвращает результат в тот же диалог. До результата запрос остаётся незавершённым.
| Поверхность | Что приходит вместо ответа | Чем отвечать |
|---|---|---|
/v1/messages | блоки tool_use в content и stop_reason: "tool_use" | блок tool_result с тем же tool_use_id |
/v1/chat/completions | choices[0].message.tool_calls и finish_reason: "tool_calls" | сообщение с ролью tool и полем tool_call_id |
/v1/responses | элемент type: "function_call" в output с call_id | элемент function_call_output с тем же call_id |
Незавершённый цикл инструментов ограничен временем ожидания результата и общей длительностью. Завершайте вызовы в том же диалоге и учитывайте ограничения ниже.
| Предел | Значение | Что происходит при выходе за него |
|---|---|---|
| Ответ на вызов инструмента | 120 секунд с момента, когда вы получили ход с вызовом | прогон закрывается, и уже потраченная его часть списывается. Вернувшись позже, вы начнёте разговор заново и заплатите за вход второй раз |
| Длительность одного прогона | 600 секунд | 504 cursor_run_timeout, а в потоке — событие error с тем же кодом. Прогон оплачен |
| Незаконченные разговоры на один ключ | Число одновременно ожидающих результата циклов ограничено для каждого ключа | 429 cursor_key_pause_limit до резервирования денег: отказ бесплатный. Закончите или бросьте один разговор, чтобы начать следующий |
Модель только выбирает функцию и формирует arguments. Функцию выполняет ваше приложение, после чего возвращает результат тем же API. Guard не исполняет клиентские функции.
- Отправьте
toolsи запрос пользователя. - Получите tool/function call и сохраните его ID вместе со всем assistant item/message.
- Выполните функцию локально.
- Верните результат с исходным ID и запросите финальный ответ.
| API | Вызов | Результат |
|---|---|---|
| Chat | message.tool_calls[].id | assistant message + role: tool, tool_call_id |
| Messages | tool_use.id | assistant content + user tool_result.tool_use_id |
| Responses | function_call.call_id | previous_response_id + function_call_output.call_id |
const apiKey = process.env.GUARD_API_KEY;
if (!apiKey) throw new Error("GUARD_API_KEY is not set");
const url = "https://api.guardrelay.ai/glm/v1/chat/completions";
async function request(body) {
const response = await fetch(url, {
method: "POST",
headers: {
authorization: "Bearer " + apiKey,
"content-type": "application/json",
},
body: JSON.stringify(body),
});
if (!response.ok) throw new Error(await response.text());
return response.json();
}
const tools = [{
type: "function",
function: {
name: "lookup_invoice",
description: "Return one invoice amount",
parameters: {
type: "object",
properties: { code: { type: "string" } },
required: ["code"],
},
},
}];
const user = { role: "user", content: "Look up invoice QA-42" };
const first = await request({
model: "glm-5.3",
messages: [user],
tools,
tool_choice: { type: "function", function: { name: "lookup_invoice" } },
max_tokens: 4096,
});
const assistant = first.choices[0].message;
const call = assistant.tool_calls[0];
const result = { amount: 7319 }; // execute your real function here
const second = await request({
model: "glm-5.3",
messages: [
user,
assistant,
{ role: "tool", tool_call_id: call.id, content: JSON.stringify(result) },
],
tools,
max_tokens: 4096,
});
console.log(second.choices[0].message.content);Не собирайте assistant message заново из одного tool_use: сохраните весь content, включая thinking blocks и оригинальный tool_use.id.
const toolUse = first.content.find(block => block.type === "tool_use");
if (!toolUse) throw new Error("GLM did not request the tool");
const nextMessages = [
{ role: "user", content: "Look up invoice QA-42" },
{ role: "assistant", content: first.content },
{
role: "user",
content: [{
type: "tool_result",
tool_use_id: toolUse.id,
content: JSON.stringify({ amount: 7319 }),
}],
},
];{
"model": "glm-5.3",
"previous_response_id": "<first response.id>",
"input": [{
"type": "function_call_output",
"call_id": "<function_call.call_id>",
"output": "{\"amount\":7319}"
}],
"max_output_tokens": 4096
}Клиентские функции работают во всех трёх API: модель возвращает вызов, функцию исполняет ваш клиент и присылает результат. tool_choice принимает auto, none, обязательный вызов (required или any) и вызов конкретной функции; none отключает и функции, и поиск. Обязательный вызов и вызов конкретной функции нельзя совмещать с поиском в одном запросе: такой запрос получает ошибку gemini_tool_choice_unsupported. Имена функций в одном запросе не повторяются, strict: true не принимается. Других инструментов, кроме функций и поиска, Gemini не принимает (gemini_tool_unsupported). Картинку в результате функции передавайте base64: ссылка там получает gemini_inline_media_required.
Клиентские функции работают во всех трёх API и уходят в Meta без изменений: модель возвращает вызов, функцию исполняет ваш клиент и присылает результат. Инструменты MCP на стороне поставщика через Guard недоступны: запрос с ними получает ошибку unsupported_tool.