урок 4 из 16

Описание инструмента: интерфейс для модели

Писать описания и схемы так, чтобы инструмент звали к месту: имя глаголом и предметом, строка «когда не звать», перечисления вместо свободных строк, сообщение об ошибке с путём к исправлению — и понимать, зачем существует общий протокол подключения.

17 мин чтения · 4 главы ·4 задания ·домашняя работа

Четыре поля описания

Описание инструмента — это единственное, по чему модель решает, звать его или нет. Не код, не документация, не ваши намерения: четыре поля, которые приходят к ней в каждом вызове.

Имякак его позвать и о чём он вообщеНазваниекак показать человеку в интерфейсеОписаниекогда звать и когда не зватьСхема параметровчто передать и в каком видетри части из четырёх читает модель, одну — человек
Четыре части, и каждая отвечает на свой вопрос модели. Пропущенная часть не остаётся пустой — она достраивается догадкой.

Выглядит это примерно так — формат отличается у разных поставщиков в мелочах, а по сути везде одинаков:

{
  "name": "search_in_files",
  "title": "Поиск по файлам",
  "description": "Ищет строку в файлах проекта и возвращает список совпадений
                  с именем файла и номером строки. Для поиска по имени файла
                  используйте find_file.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "query":  { "type": "string", "description": "что искать, обычный текст" },
      "folder": { "type": "string", "description": "где искать; по умолчанию весь проект" },
      "limit":  { "type": "integer", "description": "сколько совпадений вернуть, 1..100" }
    },
    "required": ["query"]
  }
}

Имя — то, чем инструмент зовётся. Оно же первое, что модель видит в списке, и на нём она экономит внимание: беглый взгляд по именам, и только если ничего не подошло — чтение описаний. Отсюда правило, которое звучит наивно и работает: глагол и предмет. Что делает и с чем. read_file, run_tests, parse_csv_report.

Так не надоТак надоdo_itread_filehelper2run_testssearch / find / lookupsearch_in_filesprocess_dataparse_csv_reportглагол и предмет: что делает и с чем
Модель выбирает из списка имён быстрее, чем читает описания. Имя, из которого не видно назначения, вынуждает её читать всё — и ошибаться.

Название — строка для человека в интерфейсе. Модель её не читает, и писать туда пояснения бессмысленно. Это поле есть не везде и не обязательно.

Описание — самое важное поле и самое недооценённое. Оно отвечает не на вопрос «что делает инструмент» (это уже сказано именем), а на вопрос «когда его звать и когда не звать». Хорошее описание почти всегда содержит отсылку к соседу: «для поиска по имени файла используйте find_file». Это и есть то, чего модель не может узнать сама.

Схема параметров — что передать и в каком виде. Обычно это JSON Schema: типы, обязательность, описания полей, иногда перечисление допустимых значений. У каждого параметра тоже есть описание, и его тоже читают.

Стоит понимать и обратную сторону: описание — это текст, который влияет на модель, и влияет он всегда, а не только в момент выбора. Фраза «этот инструмент работает медленно, используйте с осторожностью» приведёт к тому, что модель начнёт его избегать даже там, где он нужен, — и вы получите обходные пути вместо прямого вызова. Описание не место для предупреждений себе на будущее и для заметок «надо переписать»: всё, что там написано, будет прочитано и учтено.

Отсюда привычка, которая экономит много времени: читать описание глазами модели. Прочтите его так, будто вы видите этот инструмент впервые, не знаете, как он устроен, и должны за две секунды решить, подходит ли он к задаче. Большинство плохих описаний после такого чтения становятся очевидно плохими — в них написано, как инструмент устроен, а не когда его брать.

Как написать, чтобы звали к месту

Теперь про то, как писать описание, чтобы инструмент звали к месту. Это ремесло, и у него есть приёмы.

Первое предложение — что делает, одной фразой. Не «утилита для работы с файлами», а «читает файл и возвращает его содержимое». Модель читает описания бегло, и первая фраза решает.

Второе — что возвращает. Не в общих словах, а конкретно: «список совпадений с именем файла и номером строки». Знание формата возврата влияет на то, будет ли модель звать инструмент: если непонятно, что придёт, проще не звать.

Третье — когда не звать. Это то, чего почти никто не пишет, и это самая полезная строка описания. «Не подходит для поиска по имени файла — для этого есть find_file». «Не используйте для больших файлов: вернёт первые 200 строк». Каждая такая фраза убирает целый класс неверных вызовов.

Четвёртое — пример, если инструмент нетривиальный. Одна строка примера вызова стоит абзаца объяснений.

Теперь про схему параметров, где ошибок делают больше всего.

Перечисление вместо свободной строки. Если параметр принимает три значения, так и напишите: "enum": ["read", "write", "append"]. Свободная строка приглашает к творчеству, и модель непременно однажды пришлёт "readonly".

Значения по умолчанию — в описании, а не только в коде. Если limit по умолчанию 20, модель должна об этом знать: иначе она будет указывать его каждый раз, тратя знаки, или удивляться, почему вернулось двадцать.

Обязательных параметров — минимум. Каждый обязательный параметр — это возможность ошибиться. Если у инструмента есть разумное поведение без параметра, сделайте его необязательным.

Единицы и форматы — явно. «Время в секундах», «дата в виде ГГГГ-ММ-ДД», «путь от корня проекта». Без этого модель выберет формат сама, и выберет разумно — но не обязательно так же, как вы.

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

Ошибка, адресованная агенту

Отдельная тема — текст сообщения об ошибке. В обычной разработке его читает человек, и он написан для человека: коротко, с кодом, со ссылкой на документацию. Здесь читатель другой, и это меняет всё.

Сравните два ответа на одну и ту же ситуацию:

ENOENT: no such file or directory, open 'src/cart.js'
Файл src/cart.js не найден. В папке src есть: cart/, checkout.js, index.js.
Возможно, вы имели в виду src/cart/index.js.

Первый — обычная системная ошибка. Модель по ней сделает разумную вещь: попробует другое имя. Наугад. Потратит шаг, может быть два.

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

Разница в цене — один-два шага на каждый такой случай, а случаев много. Это одна из самых выгодных правок во всей обвязке: она не требует ни новых инструментов, ни изменений в модели, и окупается сразу.

Дальше — несколько видов ошибок и что в них стоит класть.

ОтказЧто добавить в сообщение
файл не найденчто лежит в той же папке
параметр неверного типакакой тип ожидался и пример правильного значения
значение вне допустимыхсписок допустимых значений
прав не хватаетчто именно запрещено и что разрешено вместо этого
команда упалатри значимые строки вывода, а не весь вывод
слишком большой результатсколько всего и как запросить часть

Важная оговорка: в сообщение об ошибке не кладут секреты и чужие данные. Соблазн вернуть «подключение не удалось: строка подключения такая-то» велик и понятен — это правда помогло бы. Но всё, что попало в сообщение, попадает в историю, а история идёт в трассу и в следующие вызовы модели. Об этом подробно в уроке про секреты, а правило простое: сообщение об ошибке — это публичный текст.

И последнее. Хорошее сообщение об ошибке помогает не только модели. Оно точно так же помогает человеку, который завтра будет разбирать трассу, — а это, скорее всего, вы. Формулировка «что не так → что есть → что попробовать» одинаково хороша для обоих читателей, и это редкий случай, когда не приходится выбирать.

Есть ещё одна вещь, которая относится к сообщениям и легко пропускается: отказ должен быть отличим от результата. Если инструмент при ошибке возвращает текст, похожий на нормальный ответ, модель его таковым и сочтёт. «Ничего не найдено» и «поиск не выполнен из-за ошибки» — это разные вещи, и первое модель примет как факт о мире («в проекте нет такой строки»), а второе — как повод попробовать снова.

Простой приём, который это закрывает: начинать сообщение об отказе словом «Ошибка» и держать этот вид одинаковым во всех инструментах. Модель узнаёт неудачу с первого слова и не пытается вычитать её из содержания. Тот же приём, что и с единообразием возвратов из прошлого урока, и работает он по той же причине.

Общий протокол вместо своего

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

Ответ отрасли на этот вопрос — общий протокол. Самый распространённый на сегодня называется MCP, Model Context Protocol; это открытый стандарт, и его понимают и чаты, и редакторы кода, и собственные агенты. Устроен он просто.

Приложение-хозяин: ваш агент, редактор, чатклиент 1клиент 2клиент 3сервер файловрядом, по потокусервер базырядом, по потокусервер задачудалённо, по HTTPу каждого сервера свой клиент и своё соединение
Один сервер инструментов подключается к любому приложению, которое понимает протокол. Иначе каждая пара «приложение — набор инструментов» пишется отдельно.

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

Сервер предлагает три вида вещей, и разницу между ними стоит понимать:

Что предлагает серверЧто этоПример
Инструментыдействия, которые можно выполнитьвыполнить запрос, записать файл
Ресурсыданные, которые можно прочитатьсхема базы, содержимое файла
Подсказкизаготовки обращений к моделишаблон разбора отчёта

Разница между первым и вторым важна при проектировании: инструмент что-то делает, ресурс просто есть. Схему базы правильно отдать ресурсом, а не инструментом «получить схему»: ресурс кладётся в контекст сразу и не стоит оборота круга.

Связь бывает двух видов: по потоку, когда сервер запущен рядом и общается через стандартный ввод-вывод, и по HTTP, когда он удалённый. Первый способ быстрее и не требует сети, второй позволяет одному серверу обслуживать многих. Внутри и там и там обычные сообщения в формате JSON-RPC.

Практический вопрос: стоит ли брать общий протокол или писать своё. Честный ответ — зависит от того, сколько у вас пар «приложение — набор инструментов». Одна пара: своё проще, и протокол добавит слоёв без пользы. Несколько приложений или несколько наборов, которые хочется переиспользовать: протокол окупается, потому что иначе вы напишете тот же протокол, только свой и хуже.

И одна важная оговорка, к которой мы вернёмся в уроке про безопасность. Подключить чужой сервер инструментов — это то же самое, что подключить чужую библиотеку, только хуже: библиотека делает то, что вы вызвали, а сервер инструментов приносит описания, которые читает ваша модель, и эти описания — тоже текст, который на неё влияет. Правила про чужой код здесь действуют полностью, и добавляются свои.

Задания урока

Каждое проверяется сразу: ответ сверяется с эталоном, и на неверный вариант приходит разбор.

  1. Соотнесите поле описания и вопрос, на который оно отвечает.средне
  2. Что стоит написать в описании инструмента?средне
  3. Системной ошибки «файл не найден» модели достаточно для исправления.средне
  4. Разложите то, что предлагает сервер инструментов.средне

Практика и материалы по теме курса

Пройти урок с проверкой заданий Курс «ИИ-Инжиниринг» открыт и бесплатен, прогресс сохраняется.
Открыть курс