AI Agents Claude

Claude Tool Use: как я даю своим ИИ-агентам реальные возможности

Alejandro Rioja
Alejandro Rioja
10 мин чтения
TL;DR

Claude tool use позволяет агенту выполнять действия — не только генерировать текст. Вы определяете инструменты в виде JSON-схем, Claude решает, когда их вызывать, а ваш код выполняет реальное действие. Цикл состоит из трёх шагов: отправить сообщение → получить блок tool_use → выполнить и вернуть результат. Я использовал этот паттерн в 15+ рабочих агентах на Cloudflare Workers. Точка отказа почти никогда не в ИИ — это неоднозначные результаты инструментов.

Бесплатная рассылка

Каждую среду. 28 400+ читателей. Никакой воды.

Содержание

Обновлено июль 2026.

TL;DR: Claude tool use позволяет агенту выполнять действия — не только генерировать текст. Вы определяете инструменты в виде JSON-схем, Claude решает, когда их вызывать, а ваш код выполняет реальное действие. Цикл состоит из трёх шагов: отправить сообщение → получить блок tool_use → выполнить и вернуть результат. Я использовал этот паттерн в 15+ рабочих агентах на Cloudflare Workers. Точка отказа почти никогда не в ИИ — это неоднозначные результаты инструментов.

[Опыт оператора] Я управляю 30+ рабочими ИИ-агентами в консалтинговом бренде и Pickleland — центре пикклбола в Пфлюгервилле, штат Техас. Примерно половина из них использует tool use — функцию API Claude, которая позволяет модели вызывать функции, определённые в вашем коде. Вот паттерн, к которому я пришёл после развёртывания и итерации в production.

Почему tool use меняет то, что может делать агент

Без инструментов агент может только генерировать текст. Это полезно для резюмирования, написания черновиков и классификации — но не то, что нужно большинству бизнес-автоматизаций. Им нужно искать информацию, писать в базы данных, вызывать API, отправлять сообщения.

Tool use — это способ дать Claude такой доступ. Вы определяете набор инструментов в виде JSON-схем. Claude читает схемы, решает, какой инструмент вызвать и с какими аргументами, и возвращает структурированный блок содержимого tool_use. Ваш код выполняет реальную функцию. Claude получает результат и решает, что делать дальше — в том числе вызвать другой инструмент или создать окончательный текстовый ответ.

Ключевой момент: Claude решает, когда и нужно ли вызывать инструмент. Вы определяете возможности. Модель рассуждает о том, когда их использовать.

Как работает поток API

Цикл tool use состоит из трёх шагов. Вы будете проходить этот цикл один или несколько раз в зависимости от того, сколько вызовов инструментов сделает модель.

Шаг 1: Отправьте сообщение с определёнными инструментами

typescript
const response = await anthropic.messages.create({
  model: "claude-haiku-4-5-20251001",
  max_tokens: 1024,
  tools: [
    {
      name: "check_court_availability",
      description:
        "Check if a court is available at a given date, time, and duration",
      input_schema: {
        type: "object",
        properties: {
          date: {
            type: "string",
            description: "Date in YYYY-MM-DD format",
          },
          time: {
            type: "string",
            description: "Start time in HH:MM format (24h)",
          },
          duration_minutes: {
            type: "number",
            description: "Duration of the booking in minutes",
          },
        },
        required: ["date", "time", "duration_minutes"],
      },
    },
  ],
  messages: [
    {
      role: "user",
      content: "Is a court available tomorrow at 2pm for 90 minutes?",
    },
  ],
});

Шаг 2: Проверьте, хочет ли Claude вызвать инструмент

typescript
if (response.stop_reason === "tool_use") {
  const toolUseBlock = response.content.find(
    (block): block is Anthropic.ToolUseBlock => block.type === "tool_use"
  );

  if (!toolUseBlock) throw new Error("Expected tool_use block");

  // Run your actual function
  const toolResult = await checkCourtAvailability(
    toolUseBlock.input as CourtAvailabilityInput
  );

  // Step 3: Return the result to Claude
  const finalResponse = await anthropic.messages.create({
    model: "claude-haiku-4-5-20251001",
    max_tokens: 1024,
    tools: [
      /* same tools as before */
    ],
    messages: [
      {
        role: "user",
        content: "Is a court available tomorrow at 2pm for 90 minutes?",
      },
      { role: "assistant", content: response.content },
      {
        role: "user",
        content: [
          {
            type: "tool_result",
            tool_use_id: toolUseBlock.id,
            content: JSON.stringify(toolResult),
          },
        ],
      },
    ],
  });

  // finalResponse.content now has the text answer
}

Вот и весь паттерн. Три взаимодействия с API на один вызов инструмента: определить инструменты → получить блок tool_use → вернуть результат.

Реальный пример: проверка доступности кортов Pickleland

Pickleland — центр пикклбола. Мы получаем запросы на бронирование в Facebook Messenger, в комментариях и через чат-бот. Вопрос почти всегда является вариацией «вы открыты в субботу в 15:00?» или «могу ли я забронировать корт для группы из 8 человек?»

Агент проверки доступности использует tool use для запроса реальной системы бронирования в режиме реального времени, а не для выдачи шаблонного ответа.

Вот полный агент — упрощённый, но точный для production:

typescript
// workers/availability-checker.ts
import Anthropic from "@anthropic-ai/sdk";

const anthropic = new Anthropic();

const AVAILABILITY_TOOLS: Anthropic.Tool[] = [
  {
    name: "check_availability",
    description:
      "Check court availability for a date, time, and group size. Returns available courts and their prices.",
    input_schema: {
      type: "object",
      properties: {
        date: { type: "string", description: "YYYY-MM-DD" },
        start_time: { type: "string", description: "HH:MM (24h)" },
        duration_minutes: { type: "number" },
        players: { type: "number", description: "Number of players" },
      },
      required: ["date", "start_time", "duration_minutes"],
    },
  },
  {
    name: "get_pricing",
    description:
      "Get current pricing for court rentals and open play sessions",
    input_schema: {
      type: "object",
      properties: {
        session_type: {
          type: "string",
          enum: ["court_rental", "open_play", "clinics"],
        },
      },
      required: ["session_type"],
    },
  },
];

export async function handleInquiry(
  userMessage: string,
  env: Env
): Promise<string> {
  const messages: Anthropic.MessageParam[] = [
    { role: "user", content: userMessage },
  ];

  // Agentic loop — keep going until stop_reason is "end_turn"
  while (true) {
    const response = await anthropic.messages.create({
      model: "claude-haiku-4-5-20251001",
      max_tokens: 512,
      system:
        "You are the booking assistant for Pickleland, a pickleball facility in Pflugerville, TX. " +
        "Use the tools to look up real availability and pricing. Never make up availability or prices. " +
        "If the customer wants to book, direct them to pickleland.com/book.",
      tools: AVAILABILITY_TOOLS,
      messages,
    });

    // Push the assistant's response into message history
    messages.push({ role: "assistant", content: response.content });

    if (response.stop_reason === "end_turn") {
      const textBlock = response.content.find(
        (b): b is Anthropic.TextBlock => b.type === "text"
      );
      return (
        textBlock?.text ??
        "I wasn't able to answer that — please call us directly."
      );
    }

    if (response.stop_reason === "tool_use") {
      // Process ALL tool calls in this response (Claude can request multiple at once)
      const toolResults: Anthropic.ToolResultBlockParam[] = [];

      for (const block of response.content) {
        if (block.type !== "tool_use") continue;

        let result: unknown;

        switch (block.name) {
          case "check_availability":
            result = await checkAvailability(
              block.input as AvailabilityInput,
              env
            );
            break;
          case "get_pricing":
            result = await getPricing(block.input as PricingInput, env);
            break;
          default:
            result = { error: `Unknown tool: ${block.name}` };
        }

        toolResults.push({
          type: "tool_result",
          tool_use_id: block.id,
          content: JSON.stringify(result),
        });
      }

      // Return all tool results in a single user message
      messages.push({ role: "user", content: toolResults });
    }
  }
}

Два важных момента.

Агентический цикл. Я продолжаю, пока stop_reason === "end_turn". Claude может вызвать check_availability, решить, что ему также нужны цены, вызвать get_pricing, а затем выдать окончательный ответ — это три вызова API на одно сообщение пользователя. Цикл обрабатывает это без какой-либо специальной логики.

Несколько вызовов инструментов за ход. Claude может вернуть несколько блоков tool_use в одном ответе. Я обрабатываю их все и возвращаю все результаты в одном сообщении user. Если обрабатывать их по одному и возвращать по отдельности, вы нарушите поток разговора и потратите токены впустую.

Реальный пример: агент исследования лидов

Мой консалтинговый бренд использует агента исследования, который обогащает входящие лиды до того, как я с ними разговариваю. Когда кто-то заполняет контактную форму, агент исследует компанию и извлекает то, что мне нужно знать перед звонком.

Определения инструментов для него включают инструмент записи — и именно здесь паттерн становится интересным:

typescript
const RESEARCH_TOOLS: Anthropic.Tool[] = [
  {
    name: "search_company",
    description: "Search for information about a company",
    input_schema: {
      type: "object",
      properties: {
        company_name: { type: "string" },
        website: { type: "string", description: "Company website if known" },
      },
      required: ["company_name"],
    },
  },
  {
    name: "save_research",
    description:
      "Save the completed research summary to Airtable. Call this when all research is complete.",
    input_schema: {
      type: "object",
      properties: {
        company_summary: { type: "string" },
        estimated_size: {
          type: "string",
          enum: ["1-10", "11-50", "51-200", "200+"],
        },
        likely_use_case: { type: "string" },
        priority: { type: "string", enum: ["high", "medium", "low"] },
        notes: { type: "string" },
      },
      required: [
        "company_summary",
        "estimated_size",
        "likely_use_case",
        "priority",
      ],
    },
  },
];

save_research — это то, что я называю инструментом записи: его цель — не получить информацию, а зафиксировать вывод Claude в базе данных в структурированном виде. Я использую этот паттерн вместо попыток парсить JSON из текстового ответа. Claude знает, когда исследование завершено, и вызывает save_research с правильно типизированными полями. Я никогда не пишу парсер.

Это самое чистое применение tool use: определите инструмент «финального действия» с точной схемой, которую вы хотите, и Claude предоставит структурированный вывод через вызов инструмента. Никакого парсинга текста, никакого regex, никакой JSONSchema-валидации свободного текстового вывода.

Один инструмент или много

Когда начинаешь работать с tool use, инстинктивно хочется создать один гигантский инструмент, который делает всё. Сопротивляйтесь этому. Небольшие целенаправленные инструменты лучше по трём причинам:

  1. Claude лучше рассуждает о небольших инструментах. Инструмент под названием get_court_status, который возвращает доступность, проще для модели, чем инструмент manage_facility, принимающий параметр mode и ветвящийся внутри.

  2. Небольшие инструменты проще тестировать. Каждый инструмент — это TypeScript-функция, которую можно юнит-тестировать независимо от LLM. Так и нужно делать — баги в инструментах сложно отлаживать внутри живого разговора.

  3. Claude может параллелизировать небольшие инструменты. Если два инструмента не зависят друг от друга, Claude может вызвать их в одном ответе, и вы обрабатываете их параллельно. Это работает только если инструменты действительно независимы.

Исключение: инструменты, которым нужен доступ к большому общему внутреннему состоянию. Если функции нужно 10 переменных из одного источника данных, один инструмент с более богатой схемой лучше, чем 10 инструментов, каждый из которых отдельно обращается к базе данных.

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

Влияние на стоимость

Tool use добавляет токены. Каждое определение инструмента входит в контекст системного промпта. Каждый блок tool_use и tool_result потребляет токены в истории разговора. Для многоходового агентического цикла это накапливается быстро.

Для проверки доступности Pickleland типичный разговор выполняет 3–4 вызова API (начальное сообщение + 1–2 вызова инструментов + финальный ответ), каждый обрабатывает 600–900 токенов. По ценам Haiku это обходится менее чем в $0,001 за запрос. Как я объясняю в посте о расчёте стоимости ИИ-агентов, Haiku надёжно справляется с хорошо определёнными задачами вызова инструментов и в 10 раз дешевле Sonnet при том же объёме токенов.

Агент исследования лидов работает на Sonnet, потому что суждения — приоритизация лида, оценка соответствия — требуют большей способности к рассуждению, чем Haiku надёжно обеспечивает на открытых входных данных. Расчёт всё равно работает, потому что он запускается редко (несколько раз в неделю, а не тысячи раз в день). Выбор модели определяется сложностью задачи, а не личными предпочтениями.

Точка отказа, о которой никто не говорит

Наиболее распространённая точка отказа в tool use в production — это не Claude, вызывающий не тот инструмент. Это инструмент, возвращающий что-то, о чём Claude не может ясно рассуждать.

Если ваш инструмент возвращает сырой объект из базы данных с 40 полями, Claude запутывается в том, какие поля важны. Если ваш инструмент выбрасывает исключение (которое проявляется как сбой Worker, а не как результат инструмента), цикл молча прерывается. Если ваш инструмент возвращает null, подразумевая «нет результатов», Claude не знает, повторить попытку или сдаться.

Три правила для результатов инструментов:

Возвращайте компактные, явные результаты. { available: true, courts: ["Court 3", "Court 5"], price_per_hour: 20 } — не всю строку из базы данных.

Перехватывайте ошибки внутри функции инструмента и возвращайте их как структурированные результаты. { error: "booking system timeout", retry: true } — не выброшенное исключение, которое роняет Worker.

Делайте «нет результатов» явным. { available: false, next_available: "2026-07-23T14:00:00Z" } — не null и не пустой массив без контекста.

Claude гораздо лучше рассуждает о чётких сигналах, чем о неоднозначных возвращаемых значениях. Каждый час, который я потратил на отладку tool use в production, был связан с неясными результатами, а не с рассуждениями модели.

Вывод оператора

Tool use — это функция, которая превращает Claude из генератора текста в оператора. Определяйте целенаправленные инструменты с чёткими схемами ввода. Обрабатывайте все блоки tool_use в одном ответе модели. Выполняйте агентический цикл до тех пор, пока stop_reason === "end_turn". Возвращайте чистые, компактные результаты из ваших функций инструментов — не сырые объекты данных, не выброшенные исключения, не неоднозначные nulls.

Модель занимается рассуждением. Ваш код занимается реальными действиями. Сохраняйте чёткое разделение этих двух задач — и архитектура останется поддерживаемой даже по мере добавления инструментов.

Если вы создаёте свой первый агент с tool use, начните с приведённого выше паттерна проверки доступности — один инструмент, одна цель, один агентический цикл. Запустите это в production. Затем добавьте второй инструмент.


По теме: Стек агентов, который я использую для запуска 30+ рабочих агентов · Haiku vs Sonnet: расчёт стоимости для задач агентов · Агенты, запускаемые по событию, vs. по расписанию: какой паттерн для какой задачи

Создаёте агент с tool use и застряли? Свяжитесь со мной — я проектирую и создаю архитектуры рабочих агентов для команд операторов.

FAQ

Работает ли Claude tool use со всеми моделями?

Да — tool use поддерживается всеми текущими моделями Claude. Claude Haiku надёжно обрабатывает хорошо определённые инструменты с чёткими схемами и является самым дешёвым вариантом для задач с высоким объёмом. Sonnet лучше справляется с более неоднозначными или открытыми решениями о вызове инструментов. Начните с Haiku; переходите выше, если качество вывода недостаточно.

В чём разница между Claude tool use и OpenAI function calling?

Механически идентичны. OpenAI придумал «function calling»; Anthropic называет это «tool use». В обоих случаях: вы определяете JSON-схемы, модель возвращает структурированные вызовы, ваш код выполняет функцию. Форма API различается, но концепция та же.

Может ли Claude вызвать несколько инструментов в одном ответе?

Да. Claude может вернуть несколько блоков tool_use в одном ответе assistant. Обработайте их все и верните все результаты в одном сообщении user. Смотрите паттерн агентического цикла в примере с Pickleland выше — цикл for по response.content корректно обрабатывает это.

Сколько инструментов мне следует определить на одного агента?

Я остаюсь в пределах 8–10 инструментов на агента. Сверх этого я видел, что Claude иногда выбирает не тот инструмент с первой попытки, что тратит токены на цикл исправления. Если вам нужно более 10 возможностей, разбейте агента на несколько агентов со специализированными наборами инструментов, а не создавайте одного агента, который знает всё.

Стоит ли использовать tool use для получения структурированного вывода?

Да — паттерн инструмента записи save_research чище, чем просить Claude вернуть JSON в текстовом блоке, а затем его парсить. Определите инструмент «финального действия» с точной схемой, которую вы хотите. Claude вызовет его с правильно типизированными полями, когда закончит. Парсер не нужен.

Читать дальше

Похожие статьи

AI Agents

ИИ-агенты с контролем человека: когда строить ворота одобрения (и когда нет)

Обновлено для 2026 года. Система принятия решений, которую я использую, чтобы определить, когда производственному ИИ-агенту нужен шаг одобрения человека — и когда его добавление незаметно убивает внедрение.

AI Agents

Claude против ChatGPT для бизнеса в 2026 году: честный взгляд оператора

Обновлено для 2026 года. Я запускаю более 30 производственных ИИ-агентов на Claude. Вот моё честное сравнение Claude и ChatGPT для бизнеса — где каждый побеждает, где терпит неудачу и как выбрать подходящий для вашего стека.

AI Agents

Как я создал Courtlines: SaaS для управления клубами, разработанный вместе с Claude

История Courtlines — операционной системы для клубов и студий ракеточных видов спорта. Зачем я её создал, что она умеет и как использование Claude в роли моего главного инженерного партнёра позволило одному оператору выпустить полноценный мультитенантный SaaS.

Читать дальше

Получайте ИИ-руководство на почту

Каждую среду. 28 400+ читателей. Никакой воды.

↵ — все результаты esc esc — закрыть