Курс Anthropic: Tool use, часть 1 — что такое инструменты, первый инструмент, JSON через инструменты
Перевод уроков 1–3 курса Tool use из anthropics/courses: как устроен вызов инструментов (function calling), калькулятор как первый инструмент, описание инструмента JSON-схемой и приём «инструмент как форма для JSON» — анализ тональности, извлечение сущностей, классификация статьи.
Tool use — пятый, последний курс в репозитории anthropics/courses. Он учит давать модели инструменты (tools): функции на вашей стороне, которые модель может «попросить» вызвать. В курсе шесть уроков; перевод разбит на две части:
- Основы (эта страница): уроки 1–3 — как устроен вызов инструментов, первый инструмент-калькулятор и приём, при котором инструмент служит формой для структурированного JSON.
- Полный цикл и чат-бот: уроки 4–6 — возврат результата модели (
tool_result), параметрtool_choiceи чат-бот поддержки с несколькими инструментами.
Как читать вместе с курсом
Заголовок раздела «Как читать вместе с курсом»- Когда. После урока 8.3.1 «Агентный цикл и ReAct»: там раздел
«Tool calling» объясняет ту же идею в формате OpenAI-совместимых API. Этот курс показывает формат Anthropic: блоки
tool_useиtool_resultвнутри спискаcontent. Дальше — урок 8.3.2 «Инструменты и MCP»: как описывать инструменты хорошо и как подключать их через протокол MCP. - Что нужно знать. Формат сообщений Anthropic (уроки 1–2 курса основ API) и словари Python.
- Сколько времени. Полтора-два часа на три урока вместе с упражнениями.
- Что можно пропустить. Длинный список отраслевых примеров в уроке 1 — его можно пролистать.
Урок 1. Основы tool use
Заголовок раздела «Урок 1. Основы tool use»Цели урока
Заголовок раздела «Цели урока»- Понять, что такое tool use.
- Разобраться, где он применяется.
- Понять в общих чертах, как проходит работа с инструментами в Claude.
Что такое tool use
Заголовок раздела «Что такое tool use»Tool use, или вызов функций (function calling), — способность расширять возможности Claude: мы описываем внешние инструменты или функции, и модель может их вызывать. Мы даём Claude доступ к набору заранее определённых инструментов, к которым он может обратиться в любой момент. Инструменты позволяют написать код, который выполняет конкретные задачи или вычисления, недоступные Claude самому по себе. Коротко: tool use — способ расширить функциональность Claude.
(В оригинале здесь картинка с примерами инструментов: поиск по базе данных, калькулятор, погода, отправка письма и т. п.)
Почему это важно
Заголовок раздела «Почему это важно»Tool use меняет правила игры: он раскрывает настоящий потенциал Claude и открывает множество новых возможностей. Это ключевая часть, которая может заметно повысить ценность ваших приложений.
- Расширить возможности Claude. Инструменты выводят Claude за пределы встроенных умений. Описывая и вызывая внешние инструменты, вы позволяете модели делать то, чего иначе она не смогла бы. Значит, можно строить более мощные и гибкие приложения, которые закрывают больше потребностей пользователей.
- Встроиться в существующие системы. Инструменты связывают Claude с вашими системами, базами данных и API. Можно описать инструменты, которые обращаются к бэкенду, достают данные из базы или запускают действия в приложении. Так вы используете силу Claude, не меняя свою инфраструктуру и данные.
- Автоматизировать сложные задачи. С инструментами можно автоматизировать многошаговые процессы и специализированные вычисления. Упаковав такие задачи в инструменты, вы упрощаете процессы, сокращаете ручную работу и повышаете эффективность. Claude сам вызывает нужные инструменты в нужный момент — по запросу пользователя или по заданным условиям.
- Улучшить опыт пользователя. Получив доступ к подходящим инструментам, Claude даёт более точные, учитывающие контекст и персональные ответы. Пользователь говорит с приложением обычным языком, а Claude с помощью инструментов находит нужные сведения или выполняет действия от его имени.
- Масштабировать и настраивать. Когда пользователей становится больше или меняются требования, можно легко добавить новые инструменты или изменить старые. Такая гибкость позволяет быстро развивать приложение.
Примеры применения
Заголовок раздела «Примеры применения»Общие темы, которые мы видим у клиентов, использующих инструменты:
- Получение информации. Инструменты достают данные из внешних источников: баз данных, API, веб-сервисов. Например, инструмент может узнавать погоду, курсы акций или новости по запросу пользователя.
- Вычисления. Инструменты выполняют сложные расчёты, которые могут быть не по силам самому Claude: финансовые, научные, статистические.
- Обработка данных. Инструменты преобразуют данные разных форматов: форматирование, извлечение, конвертация.
- Взаимодействие с внешними системами. Инструменты отправляют письма и уведомления, управляют устройствами интернета вещей.
- Создание контента. Инструменты помогают делать картинки, графики, оформленные документы по запросу или по шаблону.
Более конкретные варианты:
- Интеграция с корпоративными данными: подключить Claude к CRM, ERP, ITSM-системам, чтобы получать данные клиентов, автоматизировать процессы и персонально поддерживать пользователей.
- Финансовый анализ и отчётность: анализ финансовых данных, инвестиционные отчёты, оценка рисков, соблюдение регуляторных требований.
- Медицинская диагностика и планы лечения: связать Claude с электронными медкартами и медицинскими базами знаний, чтобы помогать врачам принимать решения и составлять индивидуальные планы лечения.
- Обучение и учебные материалы: персональное репетиторство, генерация учебных материалов, подстройка под стиль обучения.
- Анализ юридических документов: извлечение ключевых сведений, поиск возможных проблем, краткие выжимки.
- Автоматизация поддержки: связать Claude с базами знаний и системой заявок, чтобы отвечать клиентам, разбираться с проблемами и сокращать время ответа.
- Продажи и маркетинг: анализ данных клиентов, персональные рекламные тексты, оценка лидов.
- Помощь в разработке: подключить Claude к IDE, системе контроля версий и трекеру задач, чтобы помогать писать код, искать ошибки и вести проекты.
- Исследования и инновации: анализ рынка и патентов, генерация идей.
- Создание и оптимизация контента для сайтов, соцсетей и рекламных кампаний.
Как работает tool use
Заголовок раздела «Как работает tool use»Посмотрим, как вызов инструментов устроен на самом деле. Самое важное, что нужно понять сразу: Claude сам не запускает никакой код. Мы рассказываем Claude о наборе инструментов, которые он может попросить нас вызвать, а запустить код инструмента и сообщить Claude результат — наша работа.
Заметьте: у Claude нет встроенных инструментов на стороне сервера. Все инструменты вы явно передаёте в каждом запросе к API. То есть вы описываете доступные инструменты — с понятными описаниями и схемами входных данных — и вы же реализуете и выполняете их логику: запускаете функцию или обращаетесь к API, когда Claude об этом просит. Так у вас остаётся полный контроль над тем, какими инструментами пользуется Claude.
(В оригинале здесь схема: Claude запрашивает инструмент «поиск в базе данных», ваш код выполняет запрос к базе и возвращает результат.)
Работа с инструментами в Claude состоит из таких шагов:
-
Дать Claude инструменты и запрос пользователя (запрос к API).
- Опишите набор инструментов, к которым у Claude будет доступ: названия, описания и схемы входных данных.
- Передайте запрос пользователя, для ответа на который может понадобиться один или несколько инструментов, например: «Сколько акций General Motors я могу купить на 500 долларов?»
-
Claude вызывает инструмент (ответ API).
- Claude оценивает запрос и решает, поможет ли какой-то из инструментов. Если да — решает, какой инструмент (или какие) использовать и с какими входными данными.
- Claude выдаёт правильно оформленный запрос на вызов инструмента.
- У ответа API будет
stop_reason, равныйtool_use: это значит, что Claude хочет воспользоваться внешним инструментом.
-
Извлечь входные данные, запустить код и вернуть результат (запрос к API).
- На стороне клиента достаньте из запроса Claude название инструмента и входные данные.
- Запустите код инструмента у себя.
- Верните результат Claude: продолжите диалог новым сообщением пользователя с блоком
tool_result.
-
Claude использует результат, чтобы ответить (ответ API).
- Получив результаты инструментов, Claude составляет окончательный ответ на исходный запрос.
Шаги 3 и 4 необязательны: в некоторых сценариях нам достаточно самого факта, что Claude вызвал инструмент, и возвращать ему результат не нужно. Об этом — в следующих уроках.
Воображаемый сценарий
Заголовок раздела «Воображаемый сценарий»Чтобы лучше понять поток работы, представим, что мы делаем чат-приложение, где пользователь может поговорить с Claude о фондовом рынке и узнать текущие цены акций.
Конечно, Claude не знает, что происходит на рынке в данный момент, поэтому дадим ему инструмент get_stock_price,
который узнаёт текущую цену акций компании.
Вот как движется информация между нашим приложением и Claude:
sequenceDiagram participant P as Пользователь participant A as Наше приложение participant C as Claude P->>A: Сколько акций GM я куплю на 500 долларов? A->>C: запрос + описание get_stock_price C-->>A: tool_use: get_stock_price(company='General Motors') A->>A: вызываем API биржи: GM = 43.09 A->>C: tool_result: price 43.09 C-->>A: Примерно 11 акций A->>P: ответ
Подробный разбор
Заголовок раздела «Подробный разбор»Шаг 0: написать сам инструмент
Заголовок раздела «Шаг 0: написать сам инструмент»Прежде чем рассказывать Claude об инструменте, нужно написать его функциональность. Для get_stock_price мы,
вероятно, напишем функцию, которая принимает название компании или биржевой тикер и отправляет запрос к API биржи за
живыми данными. Примерно так:
def get_stock_price(company): # Отправить запрос к API биржи и узнать текущую цену акций компании
# Вернуть словарь с информацией о текущей цене акций указанной компании ...Если бы функция была реализована полностью и мы вызвали бы get_stock_price("General Motors"), то могли бы получить
такое значение:
{ "symbol": "GM", "price": 43.09}Шаг 1: дать Claude инструменты и запрос (запрос к API)
Заголовок раздела «Шаг 1: дать Claude инструменты и запрос (запрос к API)»Теперь опишем инструмент get_stock_price: имя, описание и схему входных данных. Подробнее об описании инструментов
поговорим позже, а пока вот возможное описание:
tool_definition = { "name": "get_stock_price", "description": "Возвращает текущую цену акций указанной компании", "input_schema": { "type": "object", "properties": { "company": { "type": "string", "description": "Название компании, для которой нужны данные об акциях" } }, "required": ["company"] }}Затем сообщаем Claude, что инструмент существует, и отправляем запрос, для которого он может понадобиться:
response = client.messages.create( model=MODEL_NAME, # в оригинале — "claude-3-opus-20240229" messages=[{"role": "user", "content": "Сколько акций General Motors я могу купить на 500 долларов?"}], max_tokens=500, tools=[tool_definition] # список инструментов, о которых знает модель)Шаг 2: Claude вызывает инструмент (ответ API)
Заголовок раздела «Шаг 2: Claude вызывает инструмент (ответ API)»Claude получает запрос и оценивает его. Он решает, что инструмент get_stock_price поможет ответить на вопрос о
покупке акций General Motors.
Claude отвечает правильно оформленным запросом на вызов инструмента. Подробнее разберём позже, а пока — упрощённый пример ответа, где Claude «хочет» воспользоваться нашим инструментом:
{ "stop_reason": "tool_use", "tool_use": { "name": "get_stock_price", "input": { "company": "General Motors" } }}Шаг 3: извлечь входные данные, запустить код и вернуть результат (запрос к API)
Заголовок раздела «Шаг 3: извлечь входные данные, запустить код и вернуть результат (запрос к API)»На стороне клиента достаём из запроса Claude имя инструмента (get_stock_price) и входные данные (название компании
General Motors).
Запускаем настоящую функцию get_stock_price, которая получает живые биржевые данные из API, с извлечёнными
аргументами. Функция может вернуть что-то такое:
{ "symbol": "GM", "price": 43.09}Затем сообщаем Claude результат: продолжаем диалог новым сообщением пользователя, в котором лежит блок tool_result
(подробнее — дальше).
Шаг 4: Claude использует результат, чтобы ответить (ответ API)
Заголовок раздела «Шаг 4: Claude использует результат, чтобы ответить (ответ API)»Claude получает цену акций и включает живые данные в окончательный ответ на исходный вопрос — например:
При текущей цене 43,09 доллара за акцию на ваши 500 долларов можно купить примерно 11 акций General Motors.
Вопрос 1
Заголовок раздела «Вопрос 1»Какие из этих задач хорошо подходят для tool use?
- (a) Чат-бот поддержки, которому нужно доставать информацию о заказе из базы данных по номеру заказа.
- (b) Помощник по письму, который помогает исправлять грамматику и стиль.
- (c) Финансовый советник, который даёт персональные рекомендации по инвестициям с учётом готовности к риску и финансовых целей пользователя.
- (d) Чат-бот для путешествий, который бронирует билеты и отели по предпочтениям и наличию мест.
Ответ на вопрос 1
a, c и d
- (a) Чат-бот поддержки, которому нужно доставать информацию о заказе из базы данных по номеру заказа.
- (c) Финансовый советник, который даёт персональные рекомендации по инвестициям.
- (d) Чат-бот для путешествий, который бронирует билеты и отели.
Во всех трёх нужно получать данные из внешних источников, проводить расчёты или обработку данных, взаимодействовать с внешними системами — типичные случаи, где инструменты расширяют возможности Claude.
Задаче (b) инструменты, скорее всего, не нужны: с ней справляются встроенные способности Claude понимать и порождать текст.
Вопрос 2
Заголовок раздела «Вопрос 2»Расставьте шаги работы с инструментом в правильном порядке:
- (a) Claude использует результат инструмента, чтобы ответить на исходный запрос.
- (b) Код на стороне клиента извлекает имя инструмента и входные данные из запроса Claude.
- (c) Claude оценивает запрос пользователя, решает воспользоваться инструментом и выдаёт запрос на вызов.
- (d) Код на стороне клиента выполняет инструмент и возвращает результат Claude.
- (e) Код на стороне клиента передаёт Claude набор доступных инструментов и запрос пользователя.
Ответ на вопрос 2
Правильный порядок: e, c, b, d, a
- (e) Код на стороне клиента передаёт Claude набор доступных инструментов и запрос пользователя.
- (c) Claude оценивает запрос, решает воспользоваться инструментом и выдаёт запрос на вызов.
- (b) Код на стороне клиента извлекает имя инструмента и входные данные из запроса Claude.
- (d) Код на стороне клиента выполняет инструмент и возвращает результат Claude.
- (a) Claude использует результат инструмента, чтобы ответить на исходный запрос.
Урок 2. Ваш первый простой инструмент
Заголовок раздела «Урок 2. Ваш первый простой инструмент»В прошлом уроке мы прошли весь процесс работы с инструментами. Пора взяться за дело и реализовать простой пример. Напомним: в процессе до четырёх шагов.
- Дать Claude инструменты и запрос пользователя (запрос к API): описать инструменты — имена, описания, схемы входных данных — и передать запрос, для ответа на который они могут понадобиться.
- Claude вызывает инструмент (ответ API): Claude решает, нужен ли инструмент и с какими входными данными, и
выдаёт правильно оформленный запрос на вызов;
stop_reasonв ответе равенtool_use. - Извлечь входные данные, запустить код и вернуть результат (запрос к API): достать имя инструмента и входные
данные, запустить код у себя, вернуть результат новым сообщением пользователя с блоком
tool_result. - Claude использует результат, чтобы ответить (ответ API).
Начнём с простой демонстрации, где с Claude нужно «поговорить» только один раз (не волнуйтесь, интересные примеры скоро будут!). Значит, шаг 4 пока пропускаем. Мы зададим Claude вопрос, Claude попросит вызвать инструмент, а мы извлечём входные данные, запустим код и получим значение.
Современные большие языковые модели плохо справляются с арифметикой, и следующий код это показывает. Попросим Claude перемножить 1984135 и 9343116:
from claude_local import client, MODEL_NAME # Claude или локальная модель — см. «Подготовку» курса основ API
# Сравнительно простая задача на умножениеresponse = client.messages.create( model=MODEL_NAME, # в оригинале — "claude-3-haiku-20240307" messages=[{"role": "user", "content": "Перемножь 1984135 и 9343116. В ответе — только результат"}], max_tokens=400)print(response.content[0].text)Вывод в оригинале:
18555375560Если запустить код несколько раз, ответы, скорее всего, будут разными. Вот ещё один ответ Claude:
18593367726060Правильный ответ:
18538003464660Claude ошибся «всего лишь» на 55364261400!
Инструменты спешат на помощь
Заголовок раздела «Инструменты спешат на помощь»Claude плохо справляется со сложной арифметикой, так что расширим его возможности: дадим ему калькулятор.
(В оригинале здесь схема: пользователь спрашивает «У меня было 23 курицы, 2 улетели — сколько осталось?», Claude просит вызвать калькулятор с операцией «вычесть» и числами 23 и 2, наш код считает и получает 21.)
Первый шаг — написать саму функцию калькулятора и убедиться, что она работает, независимо от Claude. Напишем ОЧЕНЬ простую функцию с тремя аргументами:
- операция, например
"add"(сложить) или"multiply"(умножить); - два операнда (числа).
Простейшая реализация:
def calculator(operation, operand1, operand2): if operation == "add": # сложение return operand1 + operand2 elif operation == "subtract": # вычитание return operand1 - operand2 elif operation == "multiply": # умножение return operand1 * operand2 elif operation == "divide": # деление if operand2 == 0: raise ValueError("Делить на ноль нельзя.") return operand1 / operand2 else: raise ValueError(f"Неподдерживаемая операция: {operation}")Эта функция очень ограничена: она умеет только простые выражения вроде 234 + 213 или 3 * 9. Смысл в том, чтобы
пройти весь процесс работы с инструментами на очень простом учебном примере.
Проверим, что функция работает:
calculator("add", 10, 3)13calculator("divide", 200, 25)8.0Следующий шаг — описать инструмент и рассказать о нём Claude. Описание инструмента подчиняется строгому формату. В каждом описании есть:
name— имя инструмента. Должно соответствовать регулярному выражению^[a-zA-Z0-9_-]{1,64}$(латинские буквы, цифры,_и-, от 1 до 64 символов).description— подробное текстовое описание: что делает инструмент, когда его использовать и как он себя ведёт.input_schema— объект JSON Schema («JSON-схема»), который описывает ожидаемые параметры инструмента.
Вот простой пример для воображаемого инструмента:
{ "name": "send_email", "description": "Отправляет письмо указанному адресату с заданной темой и текстом.", "input_schema": { "type": "object", "properties": { "to": { "type": "string", "description": "Адрес электронной почты получателя" }, "subject": { "type": "string", "description": "Тема письма" }, "body": { "type": "string", "description": "Текст письма" } }, "required": ["to", "subject", "body"] }}Этот инструмент, send_email, ожидает такие входные данные:
to— строка, обязательный параметр;subject— строка, обязательный;body— строка, обязательный.
Вот описание другого инструмента, search_product:
{ "name": "search_product", "description": "Ищет товар по названию или ключевому слову и возвращает его текущую цену и наличие.", "input_schema": { "type": "object", "properties": { "query": { "type": "string", "description": "Название товара или ключевое слово, например 'iPhone 13 Pro' или 'беспроводные наушники'" }, "category": { "type": "string", "enum": ["electronics", "clothing", "home", "toys", "sports"], "description": "Категория товара, чтобы сузить поиск" }, "max_price": { "type": "number", "description": "Максимальная цена товара для фильтрации результатов" } }, "required": ["query"] }}У этого инструмента три входа:
- обязательная строка
query— название товара или ключевое слово; - необязательная строка
category, которая должна быть одним из заранее заданных значений. Обратите внимание на"enum"(перечисление допустимых значений) в описании; - необязательное число
max_price, чтобы отсеять товары дороже заданной цены.
Описание нашего калькулятора
Заголовок раздела «Описание нашего калькулятора»Опишем инструмент для функции калькулятора, которую мы написали. У неё три обязательных аргумента:
operation— может быть только"add","subtract","multiply"или"divide";operand1— число;operand2— тоже число.
Вот описание:
calculator_tool = { "name": "calculator", "description": "Простой калькулятор для базовых арифметических операций.", "input_schema": { "type": "object", "properties": { "operation": { "type": "string", "enum": ["add", "subtract", "multiply", "divide"], "description": "Арифметическая операция." }, "operand1": { "type": "number", "description": "Первый операнд." }, "operand2": { "type": "number", "description": "Второй операнд." } }, "required": ["operation", "operand1", "operand2"] }}Упражнение
Заголовок раздела «Упражнение»Потренируемся писать правильно оформленное описание инструмента на примере такой функции:
def inventory_lookup(product_name, max_results): return "эта функция ничего не делает" # Её не нужно трогать и вообще что-то с ней делать!Воображаемая функция inventory_lookup (поиск по складу) вызывается так:
inventory_lookup("AA batteries", 4)
inventory_lookup("birthday candle", 10)Задача: написать соответствующее правильно оформленное описание инструмента. Считайте, что оба аргумента обязательны.
Один из возможных ответов (от переводчика; в оригинале решения нет)
inventory_lookup_tool = { "name": "inventory_lookup", "description": "Ищет товар на складе по названию и возвращает не больше max_results подходящих позиций.", "input_schema": { "type": "object", "properties": { "product_name": {"type": "string", "description": "Название товара, например 'AA batteries'."}, "max_results": {"type": "integer", "description": "Сколько результатов вернуть, не больше."} }, "required": ["product_name", "max_results"] }}Передаём инструмент Claude
Заголовок раздела «Передаём инструмент Claude»Вернёмся к калькулятору. Сейчас Claude ничего о нём не знает: это просто маленький словарь Python. В запросе к Claude можно передать список инструментов, чтобы «рассказать» о них. Попробуем:
response = client.messages.create( model=MODEL_NAME, # в оригинале — "claude-3-haiku-20240307" messages=[{"role": "user", "content": "Перемножь 1984135 и 9343116. В ответе — только результат"}], max_tokens=300, # Рассказываем Claude о нашем инструменте tools=[calculator_tool])Посмотрим, что вернул Claude:
responseToolsBetaMessage(id='msg_01UfKwdmEsgTh99wfpgW4NJ7', content=[ToolUseBlock(id='toolu_015wQ7Wipo589yT9B3YTwjF1', input={'operand1': 1984135, 'operand2': 9343116, 'operation': 'multiply'}, name='calculator', type='tool_use')], model='claude-3-haiku-20240307', role='assistant', stop_reason='tool_use', stop_sequence=None, type='message', usage=Usage(input_tokens=420, output_tokens=93))Ответ выглядит не так, как обычно! Вместо простого Message мы получили ToolsMessage (примечание переводчика: в
выводе — ToolsBetaMessage, имя из бета-версии 2024 года; в современном SDK это обычный Message).
Кроме того, response.stop_reason показывает, что Claude остановился, потому что решил вызвать инструмент:
response.stop_reason'tool_use'response.content — список, в котором лежит ToolUseBlock (блок вызова инструмента) с именем инструмента и
входными данными:
response.content[ToolUseBlock(id='toolu_015wQ7Wipo589yT9B3YTwjF1', input={'operand1': 1984135, 'operand2': 9343116, 'operation': 'multiply'}, name='calculator', type='tool_use')]tool_name = response.content[0].name # имя инструментаtool_inputs = response.content[0].input # входные данные — обычный словарь Python
print("Инструмент, который Claude хочет вызвать:", tool_name)print("Входные данные, с которыми он хочет его вызвать:", tool_inputs)Инструмент, который Claude хочет вызвать: calculatorВходные данные, с которыми он хочет его вызвать: {'operand1': 1984135, 'operand2': 9343116, 'operation': 'multiply'}Осталось взять имя инструмента и входные данные, которые дал Claude, и вызвать с ними нашу функцию калькулятора. Тогда у нас будет окончательный ответ!
operation = tool_inputs["operation"]operand1 = tool_inputs["operand1"]operand2 = tool_inputs["operand2"]
result = calculator(operation, operand1, operand2)print("РЕЗУЛЬТАТ:", result)РЕЗУЛЬТАТ: 18538003464660Мы получили правильный ответ 18538003464660!!! Вместо того чтобы надеяться, что Claude правильно посчитает, мы
задаём вопрос и даём ему инструмент, которым он может воспользоваться, если нужно.
Важное замечание
Заголовок раздела «Важное замечание»Если спросить Claude о чём-то, где инструмент не нужен — в нашем случае о чём-то, не связанном с математикой, — мы, скорее всего, хотим, чтобы он ответил как обычно. Обычно Claude так и делает, но иногда он слишком рвётся пользоваться инструментами!
Вот пример, где Claude иногда пытается применить калькулятор, хотя это бессмысленно. Спросим: «Какого цвета изумруды?»
response = client.messages.create( model=MODEL_NAME, # в оригинале — "claude-3-haiku-20240307" messages=[{"role": "user", "content": "Какого цвета изумруды?"}], max_tokens=400, tools=[calculator_tool])responseToolsBetaMessage(id='msg_01Dj82HdyrxGJpi8XVtqEYvs', content=[ToolUseBlock(id='toolu_01Xo7x3dV1FVoBSGntHNAX4Q', input={'operand1': 0, 'operand2': 0, 'operation': 'add'}, name='calculator', type='tool_use')], model='claude-3-haiku-20240307', role='assistant', stop_reason='tool_use', stop_sequence=None, type='message', usage=Usage(input_tokens=409, output_tokens=89))Claude хочет, чтобы мы вызвали калькулятор (и сложили 0 и 0)? Простейшее исправление — поправить промпт или добавить системный промпт примерно такого содержания: «У тебя есть инструменты, но пользуйся ими, только когда это нужно. Если инструмент не нужен, отвечай как обычно»:
response = client.messages.create( model=MODEL_NAME, # в оригинале — "claude-3-haiku-20240307" system="У тебя есть инструменты, но пользуйся ими, только когда это нужно. Если инструмент не нужен, отвечай как обычно", messages=[{"role": "user", "content": "Какого цвета изумруды?"}], max_tokens=400, tools=[calculator_tool])responseToolsBetaMessage(id='msg_01YRRfnUUhP1u5ojr9iWZGGu', content=[TextBlock(text='Emeralds are green in color.', type='text')], model='claude-3-haiku-20240307', role='assistant', stop_reason='end_turn', stop_sequence=None, type='message', usage=Usage(input_tokens=434, output_tokens=12))Теперь Claude отвечает по существу и не пытается впихнуть инструмент туда, где он не нужен. Новый ответ:
«Изумруды зелёного цвета». Заметьте, что stop_reason теперь end_turn, а не tool_use:
response.stop_reason'end_turn'Собираем всё вместе
Заголовок раздела «Собираем всё вместе»def calculator(operation, operand1, operand2): if operation == "add": return operand1 + operand2 elif operation == "subtract": return operand1 - operand2 elif operation == "multiply": return operand1 * operand2 elif operation == "divide": if operand2 == 0: raise ValueError("Делить на ноль нельзя.") return operand1 / operand2 else: raise ValueError(f"Неподдерживаемая операция: {operation}")
calculator_tool = { "name": "calculator", "description": "Простой калькулятор для базовых арифметических операций.", "input_schema": { "type": "object", "properties": { "operation": { "type": "string", "enum": ["add", "subtract", "multiply", "divide"], "description": "Арифметическая операция.", }, "operand1": {"type": "number", "description": "Первый операнд."}, "operand2": {"type": "number", "description": "Второй операнд."}, }, "required": ["operation", "operand1", "operand2"], },}
def prompt_claude(prompt): messages = [{"role": "user", "content": prompt}] response = client.messages.create( model=MODEL_NAME, # в оригинале — "claude-3-haiku-20240307" system="У тебя есть инструменты, но пользуйся ими, только когда это нужно. Если инструмент не нужен, отвечай как обычно", messages=messages, max_tokens=500, tools=[calculator_tool], )
if response.stop_reason == "tool_use": tool_use = response.content[-1] # блок tool_use обычно последний в списке tool_name = tool_use.name tool_input = tool_use.input
if tool_name == "calculator": print("Claude хочет воспользоваться калькулятором") operation = tool_input["operation"] operand1 = tool_input["operand1"] operand2 = tool_input["operand2"]
try: result = calculator(operation, operand1, operand2) print("Результат вычисления:", result) except ValueError as e: print(f"Ошибка: {str(e)}")
elif response.stop_reason == "end_turn": print("Claude не стал вызывать инструмент") print("Ответ Claude:") print(response.content[0].text)prompt_claude("У меня было 23 курицы, но 2 улетели. Сколько осталось?")Claude хочет воспользоваться калькуляторомРезультат вычисления: 21prompt_claude("Сколько будет 201 умножить на 2")Claude хочет воспользоваться калькуляторомРезультат вычисления: 402prompt_claude("Напиши хокку об океане")Claude не стал вызывать инструментОтвет Claude:Вот хокку об океане:
Синий простор блестит,Волны бьются о песок,Песнь океана.(Хокку — в переводе; в оригинале Claude ответил по-английски.)
Упражнение: помощник для исследования
Заголовок раздела «Упражнение: помощник для исследования»Задача — сделать с помощью Claude помощника для исследований. Пользователь вводит тему, которую хочет изучить, и получает список ссылок на статьи Википедии, сохранённый в markdown-файл, чтобы прочитать позже. Можно было бы просто попросить Claude сгенерировать список адресов статей, но Claude ненадёжен с URL и может выдумать несуществующие адреса. К тому же настоящие статьи могли переехать по новому адресу после даты среза обучающих данных Claude. Вместо этого воспользуемся инструментом, который обращается к настоящему API Википедии!
Мы дадим Claude инструмент, который принимает список возможных названий статей, придуманных Claude (среди них могут быть и выдуманные). Инструмент ищет эти названия в Википедии, находит настоящие названия статей и их адреса, так что в итоговом списке будут только существующие статьи. Затем адреса сохраняются в markdown-файл для чтения.
Вам даны две вспомогательные функции:
import wikipedia # pip install wikipedia
def generate_wikipedia_reading_list(research_topic, article_titles): wikipedia_articles = [] for t in article_titles: results = wikipedia.search(t) # ищем статьи по названию try: page = wikipedia.page(results[0]) # берём первую найденную title = page.title url = page.url wikipedia_articles.append({"title": title, "url": url}) except: continue # ничего не нашлось — пропускаем add_to_research_reading_file(wikipedia_articles, research_topic)
def add_to_research_reading_file(articles, topic): with open("output/research_reading.md", "a", encoding="utf-8") as file: file.write(f"## {topic} \n") for article in articles: title = article["title"] url = article["url"] file.write(f"* [{title}]({url}) \n") file.write(f"\n\n")Первая функция, generate_wikipedia_reading_list, получает тему исследования, например «История Гавайев» или
«Пираты по всему миру», и список возможных названий статей, который сгенерирует Claude. С помощью пакета wikipedia
она ищет соответствующие НАСТОЯЩИЕ страницы Википедии и собирает список словарей с названием и адресом статьи.
Затем она вызывает add_to_research_reading_file, передавая список статей и тему. Эта функция просто дописывает
markdown-ссылки на статьи в файл output/research_reading.md. Имя файла пока зашито в код, и функция предполагает,
что папка output существует. В репозитории курса она есть, а если работаете в другом месте — создайте её сами.
Идея такая: Claude «вызывает» generate_wikipedia_reading_list со списком возможных названий статей, которые могут
существовать, а могут и нет. Например, Claude может передать такой список — часть названий настоящие, часть нет:
["Piracy", "Famous Pirate Ships", "Golden Age Of Piracy", "List of Pirates", "Pirates and Parrots", "Piracy in the 21st Century"]Функция generate_wikipedia_reading_list проходит по названиям и собирает настоящие названия и адреса для статей,
которые существуют. Потом вызывает add_to_research_reading_file, чтобы записать их в markdown-файл.
Конечная цель
Заголовок раздела «Конечная цель»Нужно реализовать функцию get_research_help, которая принимает тему исследования и желаемое число статей. Функция
должна с помощью Claude сгенерировать список возможных статей Википедии и вызвать generate_wikipedia_reading_list.
Примеры вызовов:
get_research_help("Pirates Across The World", 7)
get_research_help("History of Hawaii", 3)
get_research_help("are animals conscious?", 3)После этих трёх вызовов файл research_reading.md выглядит так: три заголовка второго уровня по темам, под каждым —
список ссылок на найденные статьи Википедии (в оригинале — скриншот файла).
Для этого нужно:
- написать описание инструмента для функции
generate_wikipedia_reading_list; - реализовать функцию
get_research_help:- написать промпт, в котором вы просите Claude помочь собрать материалы по теме и говорите, сколько названий статей сгенерировать;
- рассказать Claude о доступном инструменте;
- отправить запрос;
- проверить, вызвал ли Claude инструмент. Если да — передать сгенерированные названия и тему в функцию
generate_wikipedia_reading_list. Она соберёт настоящие ссылки и вызоветadd_to_research_reading_file, чтобы записать их вoutput/research_reading.md; - открыть
output/research_reading.mdи проверить, что всё получилось!
Заготовка кода:
# Вот ваша заготовка!import wikipediadef generate_wikipedia_reading_list(research_topic, article_titles): wikipedia_articles = [] for t in article_titles: results = wikipedia.search(t) try: page = wikipedia.page(results[0]) title = page.title url = page.url wikipedia_articles.append({"title": title, "url": url}) except: continue add_to_research_reading_file(wikipedia_articles, research_topic)
def add_to_research_reading_file(articles, topic): with open("output/research_reading.md", "a", encoding="utf-8") as file: file.write(f"## {topic} \n") for article in articles: title = article["title"] url = article["url"] file.write(f"* [{title}]({url}) \n") file.write(f"\n\n")
def get_research_help(topic, num_articles=3): # Реализуйте эту функцию! passУрок 3. JSON с помощью инструментов
Заголовок раздела «Урок 3. JSON с помощью инструментов»Цели урока
Заголовок раздела «Цели урока»- Понять, как инструменты помогают получить ответ строго заданной структуры.
- Использовать этот «трюк», чтобы генерировать структурированный JSON.
Один из интересных способов применения инструментов — заставить Claude отвечать структурированно, например в формате JSON. Стандартизированный JSON-ответ нужен во многих задачах: извлечь сущности, подытожить данные, определить тональность и т. д.
Можно просто попросить Claude ответить в JSON. Но тогда придётся дополнительно вытаскивать JSON из длинной строки ответа и проверять, что он точно соответствует нужному формату.
Хорошая новость: когда Claude хочет вызвать инструмент, он уже отвечает в точно том формате, который мы задали в описании инструмента.
В прошлом уроке мы дали Claude калькулятор. Когда Claude хотел им воспользоваться, он отвечал примерно так:
{ 'operand1': 1984135, 'operand2': 9343116, 'operation': 'multiply'}Подозрительно похоже на JSON!
Если нам нужен структурированный JSON от Claude, этим можно воспользоваться. Достаточно описать инструмент, входные данные которого и есть нужная структура JSON, и рассказать о нём Claude. Всё. Claude ответит, «думая», что вызывает инструмент, а нам нужен только структурированный ответ.
Как это устроено
Заголовок раздела «Как это устроено»Чем это отличается от прошлого урока? Там Claude получил доступ к инструменту, захотел его вызвать, и мы действительно вызывали функцию инструмента.
В этом уроке мы «перехитрим» Claude: расскажем ему об инструменте, но вызывать функцию не будем. Инструмент служит способом задать структуру ответа:
flowchart LR subgraph L2["Урок 2: настоящий инструмент"] A1["Запрос + описание калькулятора"] --> B1["Claude: tool_use с аргументами"] --> C1["Наш код вызывает calculator()"] end subgraph L3["Урок 3: инструмент как форма"] A2["Текст + описание print_sentiment_scores"] --> B2["Claude: tool_use с аргументами"] --> C2["Берём аргументы как готовый JSON"] end
Анализ тональности
Заголовок раздела «Анализ тональности»Начнём с простого примера. Пусть Claude анализирует тональность текста и отвечает JSON-объектом такого вида:
{ "negative_score": 0.6, "neutral_score": 0.3, "positive_score": 0.1}Нужно лишь описать инструмент, который задаёт эту форму через JSON Schema. Возможный вариант:
tools = [ { "name": "print_sentiment_scores", "description": "Печатает оценки тональности заданного текста.", "input_schema": { "type": "object", "properties": { "positive_score": {"type": "number", "description": "Оценка положительной тональности, от 0.0 до 1.0."}, "negative_score": {"type": "number", "description": "Оценка отрицательной тональности, от 0.0 до 1.0."}, "neutral_score": {"type": "number", "description": "Оценка нейтральной тональности, от 0.0 до 1.0."} }, "required": ["positive_score", "negative_score", "neutral_score"] } }]Теперь расскажем Claude об этом инструменте и явно попросим им воспользоваться, чтобы он точно это сделал. Мы должны получить ответ, в котором Claude хочет вызвать инструмент, а в запросе на вызов будут все данные ровно в нужном формате.
import jsonfrom claude_local import client, MODEL_NAME
tweet = "Я ОГРОМНЫЙ ненавистник солёных огурцов. Я их просто презираю. Это мусор."
query = f"""<text>{tweet}</text>
Используй только инструмент print_sentiment_scores."""
response = client.messages.create( model=MODEL_NAME, # в оригинале — "claude-3-sonnet-20240229" max_tokens=4096, tools=tools, messages=[{"role": "user", "content": query}])responseToolsBetaMessage(id='msg_01BhF4TkK8vDM6z5m4FNGRnB', content=[TextBlock(text='Here is the sentiment analysis for the given text:', type='text'), ToolUseBlock(id='toolu_01Mt1an3KHEz5RduZRUUuTWz', input={'positive_score': 0.0, 'negative_score': 0.791, 'neutral_score': 0.209}, name='print_sentiment_scores', type='tool_use')], model='claude-3-sonnet-20240229', role='assistant', stop_reason='tool_use', stop_sequence=None, type='message', usage=Usage(input_tokens=374, output_tokens=112))Посмотрим на ответ внимательнее. Самое важное в нём — вот эта часть блока ToolUseBlock:
input={‘positive_score’: 0.0, ‘negative_score’: 0.791, ‘neutral_score’: 0.209}
Перед ним идёт текстовый блок «Вот анализ тональности данного текста:». Claude «думает», что вызывает инструмент, который как-то использует эти данные, а мы просто извлечём их и превратим в JSON:
import jsonjson_sentiment = Nonefor content in response.content: # ответ — список блоков; ищем нужный tool_use if content.type == "tool_use" and content.name == "print_sentiment_scores": json_sentiment = content.input break
if json_sentiment: print("Анализ тональности (JSON):") print(json.dumps(json_sentiment, indent=2))else: print("В ответе нет анализа тональности.")Анализ тональности (JSON):{ "positive_score": 0.0, "negative_score": 0.791, "neutral_score": 0.209}Работает! Превратим это в функцию, которая принимает твит или статью и печатает (или возвращает) анализ тональности в JSON.
def analyze_sentiment(content):
query = f""" <text> {content} </text>
Используй только инструмент print_sentiment_scores. """
response = client.messages.create( model=MODEL_NAME, # в оригинале — "claude-3-sonnet-20240229" max_tokens=4096, tools=tools, messages=[{"role": "user", "content": query}] )
json_sentiment = None for content in response.content: if content.type == "tool_use" and content.name == "print_sentiment_scores": json_sentiment = content.input break
if json_sentiment: print("Анализ тональности (JSON):") print(json.dumps(json_sentiment, indent=2)) else: print("В ответе нет анализа тональности.")analyze_sentiment("БОЖЕ, я просто обожаю пенные ванны!!!!")Анализ тональности (JSON):{ "positive_score": 0.8, "negative_score": 0.0, "neutral_score": 0.2}analyze_sentiment("Честно говоря, у меня нет мнения о ваннах")Анализ тональности (JSON):{ "positive_score": 0.056, "negative_score": 0.065, "neutral_score": 0.879}Принудительный вызов через tool_choice
Заголовок раздела «Принудительный вызов через tool_choice»Сейчас мы «заставляем» Claude вызвать print_sentiment_scores промптом: пишем «Используй только инструмент
print_sentiment_scores». Обычно это работает, но есть способ лучше! Параметр tool_choice позволяет потребовать,
чтобы Claude вызвал конкретный инструмент:
tool_choice={"type": "tool", "name": "print_sentiment_scores"}Этот код говорит Claude, что он обязан ответить вызовом инструмента print_sentiment_scores. Обновим функцию:
def analyze_sentiment(content):
query = f""" <text> {content} </text>
Используй только инструмент print_sentiment_scores. """
response = client.messages.create( model=MODEL_NAME, # в оригинале — "claude-3-sonnet-20240229" max_tokens=4096, tools=tools, tool_choice={"type": "tool", "name": "print_sentiment_scores"}, # обязательный вызов этого инструмента messages=[{"role": "user", "content": query}] )
json_sentiment = None for content in response.content: if content.type == "tool_use" and content.name == "print_sentiment_scores": json_sentiment = content.input break
if json_sentiment: print("Анализ тональности (JSON):") print(json.dumps(json_sentiment, indent=2)) else: print("В ответе нет анализа тональности.")Подробнее tool_choice разберём в одном из следующих уроков (урок 5).
Пример: извлечение сущностей
Заголовок раздела «Пример: извлечение сущностей»Тем же приёмом заставим Claude выдать аккуратный JSON с сущностями — людьми, организациями и местами, — извлечёнными из текста:
tools = [ { "name": "print_entities", "description": "Печатает извлечённые именованные сущности.", "input_schema": { "type": "object", "properties": { "entities": { "type": "array", # массив... "items": { # ...из объектов такого вида "type": "object", "properties": { "name": {"type": "string", "description": "Название извлечённой сущности."}, "type": {"type": "string", "description": "Тип сущности (например, PERSON, ORGANIZATION, LOCATION)."}, "context": {"type": "string", "description": "Контекст, в котором сущность встречается в тексте."} }, "required": ["name", "type", "context"] } } }, "required": ["entities"] } }]
text = "Джон работает в Google в Нью-Йорке. На прошлой неделе в Сан-Франциско он встретился с Сарой, генеральным директором Acme Inc."
query = f"""<document>{text}</document>
Используй инструмент print_entities."""
response = client.messages.create( model=MODEL_NAME, # в оригинале — "claude-3-sonnet-20240229" max_tokens=4096, tools=tools, messages=[{"role": "user", "content": query}])
json_entities = Nonefor content in response.content: if content.type == "tool_use" and content.name == "print_entities": json_entities = content.input break
if json_entities: print("Извлечённые сущности (JSON):") print(json.dumps(json_entities, ensure_ascii=False, indent=2)) # ensure_ascii=False — чтобы кириллица печаталась как естьelse: print("В ответе нет сущностей.")Вывод (из оригинала, текст переведён):
Извлечённые сущности (JSON):{ "entities": [ {"name": "Джон", "type": "PERSON", "context": "Джон работает в Google в Нью-Йорке."}, {"name": "Google", "type": "ORGANIZATION", "context": "Джон работает в Google в Нью-Йорке."}, {"name": "Нью-Йорк", "type": "LOCATION", "context": "Джон работает в Google в Нью-Йорке."}, {"name": "Сара", "type": "PERSON", "context": "...он встретился с Сарой, генеральным директором Acme Inc."}, {"name": "Acme Inc.", "type": "ORGANIZATION", "context": "...он встретился с Сарой, генеральным директором Acme Inc."}, {"name": "Сан-Франциско", "type": "LOCATION", "context": "На прошлой неделе в Сан-Франциско он встретился..."} ]}Здесь тот же «трюк»: мы говорим Claude, что у него есть инструмент, чтобы получить ответ в нужном формате. Потом извлекаем отформатированные данные — и готово.
Помните, что в этом сценарии полезно явно сказать Claude, что нужно воспользоваться инструментом:
Используй инструмент print_entities.
Пример посложнее: сводка статьи Википедии
Заголовок раздела «Пример посложнее: сводка статьи Википедии»Возьмём пример сложнее. Пакетом Python wikipedia загрузим целые статьи Википедии и передадим их Claude. Попросим
Claude сгенерировать ответ, в котором есть:
- главная тема статьи;
- краткое содержание статьи;
- список ключевых слов и тем, упомянутых в статье;
- список категорий для статьи (развлечения, политика, бизнес и т. д.) с оценкой классификации — насколько сильно тема относится к категории.
Если передать Claude статью об Уолте Диснее, можно ожидать примерно такой результат:
{ "subject": "Уолт Дисней", "summary": "Уолтер Элайас Дисней — американский аниматор, кинопродюсер и предприниматель. Он был пионером американской анимационной индустрии и ввёл ряд новшеств в производство мультфильмов. Ему принадлежит рекорд по числу премий «Оскар» и номинаций на неё у одного человека. Он также участвовал в создании Диснейленда и других тематических парков и телепрограмм.", "keywords": ["Уолт Дисней", "анимация", "кинопродюсер", "предприниматель", "Диснейленд", "тематические парки", "телевидение"], "categories": [ {"name": "Entertainment", "score": 0.9}, {"name": "Business", "score": 0.7}, {"name": "Technology", "score": 0.6} ]}Вот пример функции, которая получает тему статьи Википедии, находит статью, загружает её текст, передаёт Claude и печатает полученный JSON. Стратегия та же: описать инструмент, чтобы «подсказать» форму ответа.
Примечание: если пакета wikipedia нет, поставьте его командой pip install wikipedia!
import wikipedia
# Описание инструментаtools = [ { "name": "print_article_classification", "description": "Печатает результаты классификации.", "input_schema": { "type": "object", "properties": { "subject": { "type": "string", "description": "Общая тема статьи", }, "summary": { "type": "string", "description": "Краткое содержание статьи в один абзац" }, "keywords": { "type": "array", "items": { "type": "string", "description": "Список ключевых слов и тем статьи" } }, "categories": { "type": "array", "items": { "type": "object", "properties": { "name": {"type": "string", "description": "Название категории."}, "score": {"type": "number", "description": "Оценка классификации для категории, от 0.0 до 1.0."} }, "required": ["name", "score"] } } }, "required": ["subject", "summary", "keywords", "categories"] } }]
# Функция, которая генерирует JSON для статьи на заданную темуdef generate_json_for_article(subject): page = wikipedia.page(subject, auto_suggest=True) query = f""" <document> {page.content} </document>
Используй инструмент print_article_classification. Примеры категорий: Politics, Sports, Technology, Entertainment, Business. """
response = client.messages.create( model=MODEL_NAME, # в оригинале — "claude-3-haiku-20240307" max_tokens=4096, tools=tools, messages=[{"role": "user", "content": query}] )
json_classification = None for content in response.content: if content.type == "tool_use" and content.name == "print_article_classification": json_classification = content.input break
if json_classification: print("Классификация текста (JSON):") print(json.dumps(json_classification, ensure_ascii=False, indent=2)) else: print("В ответе нет классификации текста.")generate_json_for_article("Jeff Goldblum")Вывод в оригинале (переведён): тема — «Джефф Голдблюм»; краткое содержание — американский актёр и музыкант,
снимался в самых кассовых фильмах вроде «Парка юрского периода» и «Дня независимости», долгая карьера в кино и на
телевидении, джазовый музыкант со своим оркестром The Mildred Snitzer Orchestra; ключевые слова — «актёр»,
«музыкант», «Парк юрского периода», «День независимости», «кино», «телевидение», «джаз»; категории —
Entertainment с оценкой 0.9.
generate_json_for_article("Octopus")Вывод в оригинале: тема — «Осьминог»; краткое содержание — обзор анатомии, физиологии, поведения, экологии и
эволюции осьминогов, их сложной нервной системы, маскировки и смены цвета, интеллекта и отношений с людьми; ключевые
слова — «осьминог», «головоногие», «моллюски», «морская биология», «поведение животных», «эволюция»; категории —
Science (0.9) и Nature (0.8).
generate_json_for_article("Herbert Hoover")Вывод в оригинале: тема — «Герберт Гувер»; краткое содержание — биография 31-го президента США: ранние годы, карьера
горного инженера и гуманитарного деятеля, президентство во время Великой депрессии и жизнь после него; десять
ключевых слов, от «Великой депрессии» и «Республиканской партии» до «тарифа Смута — Хоули» и «Нового курса»;
категории — Politics (0.9), History (0.8), Business (0.7).
Обратите внимание: модель сама добавила категории Science, Nature и History, которых не было в примере из
промпта, — список в промпте лишь подсказка.
Упражнение
Заголовок раздела «Упражнение»С помощью этой стратегии напишите функцию translate, которая принимает слово или фразу и генерирует
структурированный JSON: исходная фраза по-английски и её перевод на испанский, французский, японский и арабский.
Пример работы. Если вызвать:
translate("how much does this cost")ожидаем такой вывод:
{ "english": "how much does this cost", "spanish": "¿cuánto cuesta esto?", "french": "combien ça coûte?", "japanese": "これはいくらですか", "arabic": "كم تكلفة هذا؟"}Примечание: чтобы красиво напечатать результат, пригодится такая строка:
print(json.dumps(translations_from_claude, ensure_ascii=False, indent=2))Один из возможных ответов (от переводчика)
translate_tool = { "name": "print_translations", "description": "Печатает фразу на английском и её переводы на испанский, французский, японский и арабский.", "input_schema": { "type": "object", "properties": { "english": {"type": "string", "description": "Исходная фраза на английском"}, "spanish": {"type": "string", "description": "Перевод на испанский"}, "french": {"type": "string", "description": "Перевод на французский"}, "japanese": {"type": "string", "description": "Перевод на японский"}, "arabic": {"type": "string", "description": "Перевод на арабский"}, }, "required": ["english", "spanish", "french", "japanese", "arabic"], },}
def translate(phrase): response = client.messages.create( model=MODEL_NAME, max_tokens=1000, tools=[translate_tool], tool_choice={"type": "tool", "name": "print_translations"}, # Ollama его проигнорирует — выручит промпт messages=[{"role": "user", "content": f"Переведи фразу: {phrase}. Используй инструмент print_translations."}], ) for block in response.content: if block.type == "tool_use": print(json.dumps(block.input, ensure_ascii=False, indent=2)) return block.inputЧто дальше
Заголовок раздела «Что дальше»Во второй части — полный цикл: результат инструмента возвращается модели блоком
tool_result, модель пишет окончательный ответ; затем три режима tool_choice и чат-бот интернет-магазина с четырьмя
инструментами. Как писать описания инструментов так, чтобы модель реже ошибалась, — в конспекте статьи Anthropic
«Writing effective tools for agents».
Словарик
Заголовок раздела «Словарик»| EN | RU |
|---|---|
| tool use, function calling | вызов инструментов, вызов функций |
| tool definition | описание инструмента |
| input schema, JSON Schema | схема входных данных, JSON-схема |
tool_use block | блок вызова инструмента |
tool_result block | блок результата инструмента |
| stop reason | причина остановки |
| structured output | структурированный вывод |
| entity extraction | извлечение (именованных) сущностей |
| sentiment analysis | анализ тональности |