Tools (инструменты) — это функции, которые ты даёшь агенту. Без них он может только говорить. С ними — он может делать: проверять погоду, записывать в таблицу, отправлять сообщения, читать файлы. Инструменты — это руки агента.
В прошлом уроке мы увидели разницу: чат-бот отвечает текстом, а агент выполняет действия. Но как именно агент выполняет действия? Через tools — функции, которые он может вызывать.
Без tools: Claude может только генерировать текст
С tools: Claude может проверять погоду, отправлять письма,
записывать в таблицы, искать в интернете...
Аналогия: Claude без инструментов — как сотрудник, которого заперли в комнате с бумагой и ручкой. Он может писать ответы, но не может позвонить, выйти, проверить данные. Инструменты — это телефон, компьютер, ключи от базы данных. С ними он превращается из писателя в исполнителя.
Создание инструмента состоит из двух частей:
Это JSON-схема, которую Claude читает и решает, когда вызвать.
tools = [
{
"name": "get_exchange_rate",
# Имя инструмента — Claude будет ссылаться на него
"description": "Получить текущий курс обмена валют",
# Описание — Claude читает его, чтобы понять, когда использовать
# Чем точнее описание, тем лучше Claude решает, когда вызвать
"input_schema": {
"type": "object",
"properties": {
"from_currency": {
"type": "string",
"description": "Валюта, из которой конвертируем (например USD)"
},
"to_currency": {
"type": "string",
"description": "Валюта, в которую конвертируем (например RUB)"
}
},
"required": ["from_currency", "to_currency"]
# required — обязательные параметры
}
}
]
def get_exchange_rate(from_currency, to_currency):
# В реальности — запрос к API курсов валют
# Для примера — хардкод
rates = {
("USD", "RUB"): 92.5,
("EUR", "RUB"): 100.3,
("USD", "EUR"): 0.92,
}
rate = rates.get((from_currency, to_currency), "Курс не найден")
return f"1 {from_currency} = {rate} {to_currency}"
Важно: Claude видит только описание (Часть 1). Он не видит код функции (Часть 2). Он не знает, как она работает внутри — он знает только что она делает и какие параметры принимает.
Создадим агента, который умеет: - Проверять курс валют - Считать стоимость - Сохранять результат в файл
from anthropic import Anthropic
import json
client = Anthropic()
# === ОПИСАНИЯ ИНСТРУМЕНТОВ ===
tools = [
{
"name": "get_exchange_rate",
"description": "Получить курс обмена валют",
"input_schema": {
"type": "object",
"properties": {
"from_currency": {"type": "string", "description": "Из какой валюты"},
"to_currency": {"type": "string", "description": "В какую валюту"}
},
"required": ["from_currency", "to_currency"]
}
},
{
"name": "calculate",
"description": "Выполнить математический расчёт",
"input_schema": {
"type": "object",
"properties": {
"expression": {"type": "string", "description": "Математическое выражение, например '100 * 92.5'"}
},
"required": ["expression"]
}
},
{
"name": "save_to_file",
"description": "Сохранить текст в файл",
"input_schema": {
"type": "object",
"properties": {
"filename": {"type": "string", "description": "Имя файла"},
"content": {"type": "string", "description": "Содержимое файла"}
},
"required": ["filename", "content"]
}
}
]
# === РЕАЛЬНЫЕ ФУНКЦИИ ===
def get_exchange_rate(from_currency, to_currency):
rates = {
("USD", "RUB"): 92.5,
("EUR", "RUB"): 100.3,
("BTC", "USD"): 67000,
}
rate = rates.get((from_currency, to_currency))
if rate:
return f"1 {from_currency} = {rate} {to_currency}"
return f"Курс {from_currency}/{to_currency} не найден"
def calculate(expression):
try:
result = eval(expression)
# eval() — вычисляет строку как математическое выражение
return str(result)
except Exception as e:
return f"Ошибка: {e}"
def save_to_file(filename, content):
with open(filename, "w") as f:
f.write(content)
return f"Файл {filename} сохранён"
# === МАППИНГ: имя инструмента → функция ===
tool_functions = {
"get_exchange_rate": lambda args: get_exchange_rate(args["from_currency"], args["to_currency"]),
"calculate": lambda args: calculate(args["expression"]),
"save_to_file": lambda args: save_to_file(args["filename"], args["content"]),
}
# lambda — маленькая функция, которая принимает args и вызывает нужную функцию
# args — словарь с параметрами, которые Claude передал
# === АГЕНТНЫЙ ЦИКЛ ===
def run_agent(task):
messages = [{"role": "user", "content": task}]
print(f"\nЗадача: {task}")
print("-" * 50)
while True:
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=tools,
messages=messages
)
# Claude закончил
if response.stop_reason == "end_turn":
for block in response.content:
if hasattr(block, "text"):
print(f"\nРезультат: {block.text}")
break
# Claude хочет вызвать инструмент
if response.stop_reason == "tool_use":
messages.append({"role": "assistant", "content": response.content})
tool_results = []
for block in response.content:
if block.type == "tool_use":
print(f" → Вызов: {block.name}({json.dumps(block.input, ensure_ascii=False)})")
# Выполняем функцию
result = tool_functions[block.name](block.input)
print(f" ← Результат: {result}")
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": result
})
messages.append({"role": "user", "content": tool_results})
# === ЗАПУСК ===
run_agent("Сколько стоят 500 долларов в рублях? Сохрани результат в файл report.txt")
Задача: Сколько стоят 500 долларов в рублях? Сохрани результат в файл report.txt
--------------------------------------------------
→ Вызов: get_exchange_rate({"from_currency": "USD", "to_currency": "RUB"})
← Результат: 1 USD = 92.5 RUB
→ Вызов: calculate({"expression": "500 * 92.5"})
← Результат: 46250.0
→ Вызов: save_to_file({"filename": "report.txt", "content": "500 USD = 46,250 RUB (курс: 1 USD = 92.5 RUB)"})
← Результат: Файл report.txt сохранён
Результат: 500 долларов = 46,250 рублей по курсу 92.5.
Результат сохранён в файл report.txt.
Claude сам решил:
1. Сначала вызвать get_exchange_rate чтобы узнать курс
2. Потом calculate чтобы умножить
3. Потом save_to_file чтобы сохранить
Три шага, три инструмента, ноль ручного управления.
Claude решает, какой инструмент вызвать, по description. Если описание плохое — он выберет не тот инструмент или не вызовет его вообще.
# Плохое описание — Claude не поймёт когда использовать
{"name": "func1", "description": "Делает что-то"}
# Хорошее описание — Claude точно знает когда вызвать
{"name": "get_weather", "description": "Получить текущую погоду (температуру, осадки, ветер) в указанном городе. Используй когда пользователь спрашивает про погоду."}
"input_schema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "Название города на русском или английском"
# Описание параметра помогает Claude правильно заполнить
},
"units": {
"type": "string",
"description": "Единицы измерения: 'celsius' или 'fahrenheit'",
"enum": ["celsius", "fahrenheit"]
# enum — ограничивает возможные значения
}
},
"required": ["city"]
# required — обязательные поля
# units не в required — значит необязательный
}
"type": "string" # текст: "Москва", "USD", "hello@mail.com"
"type": "number" # число: 42, 3.14, -10
"type": "integer" # целое число: 1, 2, 100
"type": "boolean" # да/нет: true, false
"type": "array" # список: ["Москва", "Питер"]
"type": "object" # словарь: {"name": "Иван", "age": 25}
Инструментом может быть любая функция. Вот примеры по категориям:
{"name": "search_database", "description": "Поиск информации в базе данных по запросу"}
{"name": "add_row_to_sheets", "description": "Добавить строку в Google Таблицу"}
{"name": "read_csv", "description": "Прочитать CSV-файл и вернуть данные"}
{"name": "send_telegram", "description": "Отправить сообщение в Telegram"}
{"name": "send_email", "description": "Отправить email"}
{"name": "post_to_instagram", "description": "Опубликовать пост в Instagram"}
{"name": "get_weather", "description": "Получить погоду"}
{"name": "get_exchange_rate", "description": "Получить курс валют"}
{"name": "search_web", "description": "Поиск в интернете"}
{"name": "read_file", "description": "Прочитать содержимое файла"}
{"name": "save_to_file", "description": "Сохранить текст в файл"}
{"name": "list_files", "description": "Список файлов в папке"}
{"name": "calculate", "description": "Математический расчёт"}
{"name": "convert_currency", "description": "Конвертация валют с расчётом"}
{"name": "generate_report", "description": "Сгенерировать отчёт из данных"}
OpenClaw — это бесплатный open-source AI-агент, созданный Peter Steinberger. Он работает локально на компьютере и подключается к 40+ мессенджерам (Telegram, WhatsApp, Discord, Slack, Signal, iMessage и др.). Внутри OpenClaw — ровно тот же принцип, который ты только что изучил: описания инструментов + реальные функции + агентный цикл.
┌──────────────────────────────────────────────────┐
│ OpenClaw — как устроен │
│ │
│ 1. Channel Adapters │
│ Принимает сообщения из Telegram, WhatsApp, │
│ Discord и 40+ других мессенджеров. │
│ Приводит всё к единому формату. │
│ │
│ 2. Gateway Server │
│ Центральный координатор. Распределяет │
│ сообщения, управляет сессиями. │
│ Сам НЕ думает — только маршрутизирует. │
│ │
│ 3. Agent Runner │
│ Собирает контекст перед отправкой в LLM: │
│ - выбирает модель │
│ - собирает system prompt + tools │
│ - загружает историю разговора │
│ │
│ 4. Agentic Loop (мозг) │
│ Цикл думай-действуй из урока 3.1: │
│ LLM отвечает → tool call? → выполни → │
│ → верни результат → повтори │
│ │
│ 5. Response Path │
│ Отправляет финальный ответ обратно │
│ в мессенджер через Channel Adapter │
│ │
└──────────────────────────────────────────────────┘
OpenClaw поставляется с примерно 49 готовыми инструментами (skills). Вот основные категории:
| Категория | Что делают | Примеры |
|---|---|---|
| Браузер | Открывают сайты, заполняют формы | Автоматический вход, сбор данных |
| Файлы | Читают/пишут файлы, обрабатывают PDF | Создание отчётов, анализ документов |
| Команды | Выполняют команды в терминале | Запуск скриптов, управление сервером |
| Веб-поиск | Ищут информацию в интернете | Ответы на вопросы с актуальными данными |
| Почта | Читают и отправляют email (Gmail) | Автоматические ответы, уведомления |
| Календарь | Управляют расписанием | Создание встреч, напоминания |
| Мессенджеры | Отправляют/получают сообщения | Ответы в чатах, мониторинг |
| Cron / Webhooks | Выполняют задачи по расписанию | Ежедневные отчёты, мониторинг |
Сообщество добавляет свои инструменты через ClawHub — библиотеку расширений. Есть интеграции с GitHub, Spotify, умным домом (Philips Hue), фондовым рынком и другими сервисами.
Допустим, в Telegram-чате появляется сообщение. Вот что происходит:
1. Channel Adapter: Telegram → единый формат
Сообщение: "Ищу поставщика ASIC, контакт @mining_pro"
2. Agent Runner: собирает контекст
→ Модель: Claude / GPT-4 / локальная (на выбор)
→ System prompt + список tools + память
3. Agentic Loop (ReAct-паттерн):
→ LLM думает: "Это потенциальный клиент. Нужно записать."
→ Tool call: add_row_to_sheets(name="@mining_pro", request="поставщик ASIC")
→ Результат: "Строка добавлена"
→ LLM думает: "Задача выполнена."
→ Финальный ответ (или тишина — зависит от настройки)
4. Response Path: ответ → Telegram
Внутри — ровно тот же agentic loop, который мы писали в уроке 3.1. Разница только в масштабе: 49 инструментов вместо 2, десятки мессенджеров вместо одного терминала.
OpenClaw хранит информацию в Markdown-файлах (да, обычные .md файлы):
SOUL.md — личность агента, стиль поведения
TOOLS.md — документация по инструментам
user.md — предпочтения пользователя
memory.md — что агент запомнил между разговорами
HEARTBEAT.md — расписание автоматических задач
Эти файлы загружаются в контекст LLM при каждом сообщении. Благодаря этому агент помнит предыдущие разговоры и учитывает предпочтения. Подробнее о памяти — в следующем уроке.
Сам OpenClaw бесплатный и open-source. Платить нужно только за API модели:
Типичные расходы: $2-75/день в зависимости от активности
Оптимизация: OpenClaw может маршрутизировать запросы —
простые задачи → дешёвая модель (Haiku),
сложные → мощная (Claude Sonnet / Opus).
При таком подходе расходы ~$2.50/день.
Это применение стратегий из урока 2.5 — выбор модели по задаче, а не "самой умной" для всего.
Claude не имеет прямого доступа к системе. Он может вызвать только те инструменты, которые ему дали.
# Ты даёшь:
tools = [get_weather, calculate]
# Claude может: проверить погоду, посчитать
# Claude НЕ может: удалить файлы, отправить деньги, читать пароли
# Ты контролируешь:
# - Какие инструменты доступны
# - Что они делают внутри
# - Какие данные они возвращают
Правила безопасности:
delete_everything если не нужноprint(f"→ Вызов: ..."))OpenClaw, например, имеет встроенные approval gates — перед выполнением опасных команд он запрашивает разрешение у пользователя.
Напиши описание инструмента search_contacts — поиск контакта по имени в списке.
# Заполни:
tool = {
"name": "search_contacts",
"description": "???",
"input_schema": {
"type": "object",
"properties": {
# ???
},
"required": [???]
}
}
tool = {
"name": "search_contacts",
"description": "Найти контакт по имени в списке контактов. Возвращает имя, email и телефон.",
"input_schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Имя или часть имени для поиска"
}
},
"required": ["name"]
}
}
Скопируй полный пример с тремя инструментами (курс, калькулятор, файл) и запусти. Попробуй разные задачи: - "Сколько стоят 1000 EUR в рублях?" - "Посчитай 15% от 46250 и сохрани в файл tax.txt"
Добавь к агенту инструмент get_time — возвращает текущее время:
from datetime import datetime
def get_time():
return datetime.now().strftime("%H:%M:%S")
Опиши его в tools и добавь в tool_functions. Проверь, что работает: "Который сейчас час?"
Задача 1: Зачем инструменту нужно описание (description)?
Задача 2: Claude видит код функции?
Задача 3: Можно ли дать агенту 10 инструментов одновременно?
Задача 4: OpenClaw — это чат-бот или агент? Почему?
| Термин | Что значит |
|---|---|
| Tool (инструмент) | Функция, которую ты описываешь для Claude, чтобы он мог выполнять действия во внешнем мире. |
| tool_use | Тип блока в ответе Claude, означающий, что модель хочет вызвать конкретный инструмент с определёнными параметрами. |
| tool_result | Тип сообщения, которое ты отправляешь обратно Claude с результатом выполнения запрошенного инструмента. |
| input_schema | Описание параметров инструмента в формате JSON Schema, чтобы Claude знал, какие данные передавать при вызове. |
| JSON Schema | Стандартный формат описания структуры данных, используемый для определения параметров инструментов. |
| Function calling | Механизм, при котором LLM выбирает нужную функцию и формирует параметры для её вызова на основе контекста разговора. |
| Name (имя инструмента) | Уникальное название инструмента, по которому Claude обращается к нему при вызове. |
| Description (описание) | Текстовое пояснение для Claude, объясняющее что делает инструмент и когда его стоит использовать. |
| Параметры инструмента | Входные данные, которые Claude передаёт при вызове инструмента — описываются через input_schema. |
| MCP (Model Context Protocol) | Открытый протокол от Anthropic, который позволяет подключать внешние инструменты и данные к LLM через единый стандарт. |
В следующем уроке — Память агента. Как агент запоминает информацию между разговорами, чтобы не начинать каждый раз с чистого листа. На примере OpenClaw мы уже увидели, что он хранит память в Markdown-файлах — в следующем уроке разберём этот подход подробнее и реализуем его в коде.