Tool design: описания = главный механизм выбора
Цель: писать описания инструментов так, чтобы модель выбирала правильный инструмент **надёжно**. Это Домен 2 экзамена Architect (18%).
Главный принцип
Описания инструментов — основной механизм, по которому LLM выбирает, какой инструмент вызвать.
Минимальные описания → ненадёжный выбор. Похожие описания у разных инструментов → модель путается и вызывает не то.
Пример ломающейся системы
У тебя два инструмента:
analyze_content — Analyzes content
analyze_document — Analyzes documents
Что выберет модель когда пользователь говорит "analyze this file"? Никто не знает. Оба описания бесполезны.
Лечение
analyze_content — Analyzes structured text inputs (emails, messages, reviews).
Input: raw text string.
Returns: sentiment score + key entities.
Use when user provides text directly.
analyze_document — Analyzes file-based documents (PDF, DOCX, images).
Input: file path or URL.
Returns: OCR + structured extraction.
Use when user provides a file path or uploaded document.
Теперь выбор однозначный.
Чеклист хорошего описания
- Назначение — что именно делает
- Форматы ввода — что принимает
- Примеры запросов — типичные вызовы
- Граничные случаи — когда НЕ вызывать
- Пояснения границ — чем отличается от похожих
- Формат вывода — что возвращает
Вопрос экзамена (реальный пример)
Логи показывают что агент часто вызывает
get_customerкогда пользователи спрашивают о заказах ("проверь мой заказ #12345"), вместоlookup_order. Оба инструмента имеют минимальные описания и принимают похожие форматы идентификаторов. Какой первый шаг улучшит надёжность выбора инструментов?
- A) Добавить few-shot примеры в системный промпт
- B) Расширить описание каждого инструмента: форматы, примеры, граничные случаи ✅
- C) Реализовать слой маршрутизации на основе ключевых слов
- D) Объединить оба в один
lookup_entity
Правильный ответ — B. Корневая причина — недостаточные описания. Few-shot (A) добавляет токены без фикса основной проблемы. Маршрутизация (C) — избыточно. Объединение (D) — допустимо архитектурно, но требует больше усилий чем оправдано.
Влияние системного промпта
Осторожно: формулировки в системном промпте могут переопределить описания инструментов.
Пример: системный промпт содержит "always prefer analyze_content for user messages". Даже если описания чёткие — Claude может выбрать analyze_content для файлов.
Правило: держи системный промпт свободным от прямых указаний на инструменты. Пиши роль и цели. Выбор инструментов — через их описания.
tool_choice — принуждение
Агент SDK/API позволяет заставить модель вызвать инструмент:
tool_choice = "auto" # модель решает (дефолт)
tool_choice = "any" # обязана вызвать любой
tool_choice = {"type": "tool", "name": "extract_metadata"} # конкретныйКогда "any"
Когда любой текстовый ответ — ошибка. Например, извлечение структурированных данных: если модель "задумается текстом" вместо вызова extractor'а — пайплайн ломается.
Когда {name: "X"}
Когда порядок обязательный: "сначала валидируй, потом обрабатывай". Forced first call → extract → потом free auto → оставшаяся часть.
Разделение универсальных инструментов
Плохо:
fetch_url — Fetches URL content
Агент может вызвать на любой URL, включая опасные. Нет контекста о типе контента.
Хорошо (разделение):
load_document — Loads PDF/DOCX/TXT from URL. Validates content-type.
Rejects: images, video, executables.
fetch_api_data — Makes GET request to trusted API endpoints.
Allowed domains: api.company.com, api.partner.com.
fetch_image — Downloads image for analysis.
Max size: 10MB. Formats: PNG, JPG, WebP.
Каждый — с чётким контрактом ввода/вывода.
Структурированные ответы об ошибках
Плохо:
{"error": "Operation failed"}Агент не знает — повторить? Эскалировать? Попробовать альтернативу?
Хорошо:
{
"isError": true,
"errorCategory": "transient",
"isRetryable": true,
"retryAfter": 30,
"message": "Database connection timed out",
"partialResults": null
}Категории ошибок:
errorCategory | Значит | Действие агента |
|---|---|---|
transient | Временная (таймаут, сервис недоступен) | Повторить с backoff |
validation | Некорректный ввод | НЕ повторять, спросить пользователя |
permission | Нет доступа | Эскалировать на человека |
business | Нарушение политики | Эскалировать, объяснить |
Различие: сбой доступа vs валидный пустой результат
Это часто путают. Но агенту критически важно различать:
search_products("foobar")вернул[]— валидный пустой результат. Продуктов просто нет. Не надо повторять, надо сказать пользователю.search_products("foobar")вернул{isError: true, errorCategory: "transient"}— сбой. Повторить.
Если API всегда возвращает [] на оба случая — агент не может принять правильное решение.
Ограничение набора инструментов
18 инструментов у одного агента → надёжность выбора падает катастрофически.
4-5 релевантных роли инструментов → работает хорошо.
Пример: агент "returns specialist" не нуждается в инструментах для новых заказов. Ограничь его.
Практика (20 минут)
Задача 1. Аудит описаний
Возьми свой код где ты используешь Agent SDK или Claude API с инструментами. Прочитай описания. Задай 5 вопросов каждому:
- Понятно что делает?
- Видны форматы ввода?
- Есть граничные случаи?
- Можно спутать с другим инструментом из набора?
- Есть пример вызова?
Если на 3+ нет — переписывай.
Задача 2. Структурированные ошибки
Добавь isError, errorCategory, isRetryable в свои tool-ответы. Посмотри как агент меняет поведение — повторяет transient, эскалирует business, спрашивает при validation.
Что дальше
Урок 4.3: структурированный вывод. tool_use + JSON schema + Pydantic retry loops. Как получить стабильный JSON от LLM который никогда не ломается.