Как проектировать инструменты для ИИ-агентов: 8 архитектурных принципов tool design из Claude Code. Разбор от Anthropic.
VibeLab
Поделиться

10 апреля Anthropic опубликовала статью о проектировании инструментов для Claude Code. Мы разобрали tool layer изнутри — от JSON-схем и паттернов именования до механик делегирования в субагенты — и собрали конкретные принципы для тех, кто строит собственные агентные системы на базе LLM.
Когда разработчик проектирует REST API, он рассчитывает на коллегу, который прочитает документацию, изучит примеры, запомнит особенности и при ошибке пойдёт дебажить. LLM-агент работает иначе. Он «читает» JSON Schema инструмента в runtime, интерпретирует description и параметры, формирует вызов — и делает всё это за один проход, без возможности загуглить пример использования.
Отсюда фундаментальное отличие: хороший инструмент для агента — это не удобный интерфейс, а предсказуемый контракт с минимальной когнитивной нагрузкой.
Что это значит на практике:
description в tool schema. Если описание неточное или двусмысленное, агент вызовет инструмент неправильно.Read, Edit, Grep — каждое имя однозначно. process_data — нет.Тарик Шихипар из Anthropic описывает это через аналогию: представьте, что вам дали сложную математическую задачу. Лист бумаги — минимум, но вы ограничены ручными вычислениями. Калькулятор лучше, но нужно знать, какие кнопки нажимать. Компьютер — максимальная мощь, но требует навыка программирования. Инструменты агента должны соответствовать его способностям — не проще и не сложнее.
Ни маркетинговые страницы Claude Code, ни FAQ в поддержке, ни README на GitHub не дают систематического разбора «почему инструменты спроектированы именно так». Мы этот разбор провели.
Claude Code работает примерно с 20 инструментами. Вот их классификация по функциям:
| Категория | Инструменты | Назначение |
|---|---|---|
| Чтение | Read, Grep, Glob | Получение информации из файловой системы |
| Запись | Edit, Write | Модификация и создание файлов |
| Исполнение | Bash | Выполнение shell-команд |
| Оркестрация | Agent, Skill, ToolSearch | Делегирование и динамическая загрузка |
| Планирование | TodoWrite, EnterPlanMode, ExitPlanMode | Управление задачами и режимами работы |
| Коммуникация | AskUserQuestion | Взаимодействие с пользователем |
Несколько закономерностей бросаются в глаза.
Инструменты чтения — самые многочисленные. Три инструмента для одной задачи: Read для файлов, Grep для поиска по содержимому, Glob для поиска по именам. Это не случайность — агент тратит бо́льшую часть цикла на сбор контекста, и каждый инструмент оптимизирован под свой паттерн поиска.
Запись разделена на Edit и Write. Edit работает через поиск и замену конкретных строк, Write — полная перезапись файла. При этом Edit требует предварительного вызова Read. Агент не может редактировать файл, который не прочитал — это архитектурное ограничение, а не баг.
Оркестрация вынесена в отдельный слой. Agent запускает субагентов с отдельным контекстным окном. ToolSearch подгружает схемы инструментов по запросу, а не держит их все в памяти. Skill вызывает именованные навыки. Каждый из этих инструментов управляет не данными, а другими агентами и инструментами.
Инструменты эволюционируют вместе с моделью. Когда Claude Code только запустился, модели нужен был TodoWrite — список задач, чтобы не терять фокус. Со временем модели стали лучше работать с субагентами, и жёсткий список задач начал мешать: агент боялся отклониться от плана, даже когда это было нужно. TodoWrite заменили на Task — инструмент, через который агенты координируют работу, создают зависимости и могут менять задачи на ходу.
Почему Anthropic не сделала один file_tool с параметром action: read | write | edit? Ответ связан с тем, как LLM принимает решения.
Когда модель видит универсальный инструмент с множеством режимов, ей нужно: выбрать инструмент из списка, выбрать режим внутри инструмента, заполнить параметры для этого режима. Каждый дополнительный уровень выбора увеличивает вероятность ошибки. Три отдельных инструмента с чёткими именами — Read, Edit, Write — исключают промежуточный шаг полностью.
Есть и вторая причина: разные инструменты имеют разные уровни риска. Read — безопасная операция. Edit модифицирует файл с проверкой уникальности заменяемого фрагмента. Write перезаписывает файл целиком. Разделение позволяет системе применять разные уровни подтверждения к каждому инструменту.
Эволюция AskUserQuestion в Claude Code иллюстрирует этот принцип. Сначала команда попробовала добавить массив вопросов в параметры существующего ExitPlanTool — два дела в одном инструменте. Модель путалась: план и вопросы конфликтовали. Затем попробовали специальный markdown-формат в текстовом выводе — модель не могла его стабильно воспроизводить: добавляла лишние предложения, теряла варианты, ломала структуру. Только отдельный инструмент с собственной схемой дал стабильный результат.
Вывод: один инструмент — одно действие. Если вы ловите себя на добавлении параметра mode или action_type, скорее всего перед вами два разных инструмента.
JSON Schema инструмента — единственная «документация», которую видит агент. Разберём конкретные приёмы из Claude Code.
Описания параметров работают как инструкции. Реальный пример из инструмента Grep:
Описание содержит аналогию (equivalent to '| head -N'), значение по умолчанию, и предупреждение о последствиях (large result sets waste context). Это не документация в привычном смысле — это управление поведением агента через метаданные.
Enum вместо свободного текста. Параметр output_mode в Grep ограничен тремя значениями: content, files_with_matches, count. Агент не может придумать четвёртый вариант. Чем меньше свободы в типах данных — тем меньше галлюцинаций.
Значения по умолчанию снижают когнитивную нагрузку. Из 12+ параметров Grep обязателен только один — pattern. Остальные имеют разумные дефолты. Агент вызывает Grep(pattern="TODO") и получает работающий результат без указания path, output_mode, head_limit и прочих параметров.
Описания инструментов содержат полноценные инструкции. Описание Bash — это не строка «executes a command», а текст с разделами: что запрещено делать, как работать с файлами, как коммитить в git, какие ограничения по таймауту. Подробности занимают токены, но резко снижают количество ошибок.
Несколько антипаттернов, которые видны в архитектуре Claude Code:
Бинарные данные. LLM работает с текстом. Передача бинарных данных через параметры инструмента — попытка прогнать JPEG через строковое поле. Claude Code решает это иначе: Read отображает изображения через мультимодальные возможности модели напрямую, но не через текстовые параметры.
Глубоко вложенные объекты. Чем глубже вложенность JSON, тем выше вероятность структурной ошибки. Параметры инструментов Claude Code преимущественно плоские: строки, числа, boolean, одноуровневые enum. Никаких options.formatting.style.color.
Слишком длинные строки. Параметр prompt в Agent — одно из немногих исключений, где ожидается объёмный текст. Для большинства инструментов длинная строка в параметре — признак того, что данные нужно передавать иначе. Edit принимает old_string и new_string — конкретные фрагменты кода, а не файл целиком.
Неконтролируемый вывод. Read ограничивает ответ 2000 строками по умолчанию. Grep устанавливает head_limit в 250. Без таких ограничений один вызов инструмента может забить контекстное окно агента на десятки тысяч токенов, не оставив места для рассуждений.
Контекстное окно — конечный ресурс. У Claude — до 200K токенов, но на практике полезная ёмкость меньше: часть занимают системный промпт, история диалога и инструкции инструментов. Дизайн tool layer Claude Code явно учитывает эту экономику.
Частичное чтение. Read принимает параметры offset и limit. Агент может прочитать строки 100–150 из файла на 5000 строк, потратив токены только на нужный фрагмент. Grep поддерживает offset для пагинации — следующую «страницу» результатов можно запросить отдельно.
Режимы вывода. Grep возвращает данные в трёх форматах: полное содержимое совпадений, только имена файлов, или счётчик совпадений. Переключение с content на files_with_matches может сократить объём вывода в десятки раз при сохранении полезного сигнала.
Автоматическое сжатие. Системный промпт Claude Code прямо сообщает агенту: при приближении к лимиту контекста ранние сообщения будут сжаты. Инструменты проектируются с учётом того, что их вывод — временный ресурс, а не постоянная запись.
Прогрессивное раскрытие. Anthropic описывает паттерн progressive disclosure: вместо загрузки всей информации сразу агент получает ссылки и подсказки, по которым подгружает контекст при необходимости. ToolSearch — реализация этого принципа: схемы «отложенных» инструментов загружаются по запросу, а не занимают место постоянно. Когда Claude Code впервые запустился, контекст строился через RAG — векторная база индексировала кодовую базу и выдавала фрагменты. Проблема: агент получал контекст пассивно, а не искал его сам. Замена RAG на Grep позволила агенту строить контекст активно — и по мере роста способностей моделей этот подход стал работать лучше предварительной индексации.
Инструмент Agent в Claude Code — один из самых архитектурно интересных. Он запускает отдельного агента с собственным контекстным окном, набором инструментов и задачей.
Ключевые особенности:
isolation: "worktree" создаёт временную копию git-репозитория. Субагент может экспериментировать с кодом, не затрагивая основную ветку.Anthropic описывает конкретный кейс — Claude Code Guide. Это субагент, который отвечает на вопросы о самом Claude Code. Вместо загрузки всей документации в основной контекст, главный агент делегирует вопрос субагенту, тот ищет ответ в своём контексте и возвращает только результат. Основной контекст остаётся чистым.
Вывод для разработчиков: субагент — не просто «вложенный вызов». Это отдельная единица с собственным контекстом, инструментами и ограничениями. Промпт для субагента нужно писать как для нового коллеги, который только что вошёл в комнату и не видел предыдущего разговора.
Ошибка, которую LLM не может интерпретировать, — потерянный ход в агентном цикле. Агент потратит токены на вызов, получит непонятный ответ и, скорее всего, попробует то же самое повторно. Или пойдёт по ложному пути.
Ошибка должна быть actionable. Edit в Claude Code не возвращает просто «error». Если old_string не найден в файле, агент получает объяснение и подсказку: «The edit will FAIL if old_string is not unique in the file. Either provide a larger string with more surrounding context to make it unique or use replace_all». Не сигнал о проблеме, а инструкция по её решению.
Silent failure — худший сценарий. Если инструмент тихо проглатывает ошибку и возвращает пустой результат, агент решает, что операция прошла успешно. В контексте кодирования это «успешно» применённый патч, который ничего не изменил.
Превентивные ошибки лучше реактивных. Edit требует предварительного Read не для формальности — это предотвращение ошибок до их возникновения. Агент, прочитавший файл, с высокой вероятностью передаст корректный old_string. Агент, не читавший файл, почти гарантированно промахнётся.
Конкретные примеры из Claude Code:
git add -i — ошибка с пояснением: интерактивный режим не поддерживается.Claude Code явно делит инструменты по уровню риска:
Безопасные (read-only). Read, Grep, Glob — не меняют состояние системы. Агент может вызывать их многократно без последствий.
Модифицирующие. Edit, Write — меняют файлы, но с защитными механизмами. Edit требует точного совпадения old_string. Write перезаписывает файл целиком, но только после Read.
Потенциально опасные. Bash — может выполнить произвольную команду, включая деструктивные: rm -rf, git reset --hard, git push --force. Системный промпт содержит детальные инструкции: деструктивные операции запрещены без явного запроса пользователя. Более того, force push в main/master вызывает предупреждение даже при явном запросе.
Для разработчиков агентных систем: категоризация инструментов по уровню риска — не опция, а необходимость. Если агент может удалить production-базу одним вызовом без подтверждения — это вопрос вероятности, не времени.
Проектировать инструменты без понимания ограничений агента — строить интерфейс для пользователя, которого не изучал. Вот что LLM принципиально не умеет в контексте tool use:
Хранить состояние между вызовами. У LLM нет персистентной памяти в пределах одного цикла. Если агент прочитал файл в начале диалога, а через 50 сообщений хочет его отредактировать — он не «помнит» содержимое. Результат первого Read мог быть сжат или удалён из контекста. Поэтому Claude Code требует Read перед Edit каждый раз, а не один раз за сессию.
Видеть побочные эффекты. Когда агент выполняет Bash-команду, он видит stdout и stderr. Но он не видит, что команда создала файл в другой директории, изменила переменную окружения или запустила фоновый процесс. Инструменты должны возвращать все значимые побочные эффекты явно.
Понимать время. LLM не ощущает, сколько длился вызов. Фоновый процесс мог завершиться мгновенно или работать пять минут — агент не знает, пока не проверит. Claude Code решает это через уведомления: при run_in_background агент получает сигнал по завершении, а не пытается угадать или «спать» в цикле.
Оценивать объём данных до вызова. Агент не знает, вернёт Grep 3 результата или 30 000. Поэтому инструменты устанавливают дефолтные лимиты: head_limit: 250 в Grep, limit: 2000 строк в Read. Без ограничений один неудачный запрос исчерпает контекстное окно.
Откатывать действия. У LLM нет undo. Если Edit повредил файл, агент должен заново прочитать его, понять проблему и применить исправление. Каждая запись — осознанное, проверяемое действие. Ещё один аргумент за строгое разделение чтения и записи.
Эти ограничения фундаментальны для архитектуры LLM. Они не исчезнут с ростом размера модели. Инструментальный слой должен их компенсировать.
По результатам разбора архитектуры Claude Code — 8 принципов для проектирования собственного tool layer.
1. Один инструмент — одно действие. Не совмещайте чтение и запись, не добавляйте параметр mode. Если действия имеют разный уровень риска или разные входные данные — это разные инструменты.
2. Описание — единственная документация. Поле description в JSON Schema — всё, что LLM прочитает перед вызовом. Пишите его как инструкцию: что делает инструмент, когда использовать, чего избегать. Аналогии с знакомыми концепциями работают: equivalent to '| head -N'.
3. Ограничивайте вывод по умолчанию. Каждый инструмент должен иметь разумный лимит на объём ответа. head_limit: 250 — пример. Дайте агенту возможность запросить больше явно, но не позволяйте забивать контекст по умолчанию.
4. Ошибки должны быть actionable. Не «error: invalid input», а «old_string not found — provide more context or use replace_all». Агент должен получить достаточно информации для коррекции следующего вызова без помощи человека.
5. Категоризируйте по уровню риска. Read-only, модификация, деструктивное действие — минимум три уровня. К каждому — своя политика подтверждения. Деструктивные операции требуют явного согласия пользователя.
6. Проектируйте под конечный контекст. Параметры offset/limit, режимы вывода с разной детализацией, дефолтные ограничения — механизмы экономии контекстного окна. Инструмент, способный вернуть мегабайт текста без лимита, — архитектурный дефект.
7. Делегируйте в субагенты для изоляции контекста. Задача, требующая глубокого погружения — поиск по документации, анализ большого файла — выносится в субагент с отдельным контекстом. Основной агент получает только результат.
8. Компенсируйте ограничения восприятия. LLM не хранит состояние, не видит побочных эффектов, не чувствует время. Требуйте чтение перед записью. Возвращайте побочные эффекты в output. Уведомляйте о завершении фоновых процессов. Не полагайтесь на «память» агента.
Эти принципы не теоретические. Они извлечены из системы, которая обрабатывает миллионы агентных сессий, и отражают год итераций и ошибок команды Anthropic. Claude Code — не единственный способ строить агентные инструменты, но его архитектура — один из немногих публичных примеров, где дизайн-решения можно изучить в деталях.
Если у вас есть опыт проектирования инструментов для LLM-агентов или вы строите такую систему — будем рады обсудить. Пишите команде VibeLab: разберём архитектуру вашего tool layer, поделимся наблюдениями из собственной практики.
Подписывайтесь на наш канал: @vibelogia
Поделимся опытом
8 800 201 85 68