Разработка ИИ-агентов · Модуль 2 · Урок 2.1

Проектирование инструментов: описания, схемы, гранулярность

Инструмент — это интерфейс для модели

Качество агента на 80% определяется качеством его инструментов. Инструмент — это не «функция», а интерфейс, который читает и использует модель. Если человек по сигнатуре и описанию понимает, что делает функция и когда её звать, то и модель поймёт. Если описание мутное — модель будет ошибаться, причём незаметно: вызывать инструмент не вовремя, передавать неверные аргументы, зацикливаться.

Поэтому инструменты проектируют как публичный API: продуманные имена, ясные описания, строгие типы. Разница между «работает иногда» и «работает надёжно» — обычно именно в формулировках схем, а не в модели.

Описание и схема

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

Параметры описываются в JSON Schema. Делайте их строгими: указывайте типы, помечайте обязательные поля в required, используйте enum для закрытых наборов значений (статусы, категории) — это резко снижает число невалидных вызовов. Каждое поле тоже снабжайте описанием: модель читает описания полей так же, как описание инструмента. Чем меньше у модели свободы выдумать аргумент, тем надёжнее агент.

Гранулярность: сколько инструментов и какого размера

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

  • Слишком крупный инструмент (do_everything(action, params)) перекладывает выбор действия внутрь аргументов — модель путается, а вы теряете типизацию.
  • Слишком мелкие инструменты (отдельная функция на каждое поле) раздувают список схем, забивают контекст и усложняют выбор.

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

Плохая и хорошая схема одного инструмента
// ПЛОХО: размытое имя, нет описаний полей, свободная строка вместо enum.
bad := map[string]any{
    "name":        "orders",
    "description": "работа с заказами",
    "parameters": map[string]any{
        "type": "object",
        "properties": map[string]any{
            "q":      map[string]any{"type": "string"},
            "status": map[string]any{"type": "string"}, // что сюда писать?
        },
    },
}

// ХОРОШО: глагол в имени, ясное описание «что/когда», enum и required.
good := map[string]any{
    "name": "search_orders",
    "description": "Ищет заказы пользователя по тексту и статусу. " +
        "Используй, когда спрашивают про конкретные заказы или их состояние.",
    "parameters": map[string]any{
        "type": "object",
        "properties": map[string]any{
            "query": map[string]any{
                "type":        "string",
                "description": "Поисковый текст: номер заказа или товар.",
            },
            "status": map[string]any{
                "type":        "string",
                "enum":        []string{"new", "paid", "shipped", "cancelled"},
                "description": "Фильтр по статусу заказа.",
            },
        },
        "required": []string{"query"},
    },
}

Anti-patterns

Анти-паттернПочему плохоКак правильно
Описание-заглушка («работа с X»)Модель не понимает, когда звать инструментОписать что/когда/что возвращает, добавить пример
Свободная строка вместо enumМодель шлёт произвольные значенияЗакрытые наборы — через enum; обязательные — в required
Один инструмент do_everything(action)Выбор действия уходит в аргументы, теряется типизацияОдин инструмент = одно понятное действие
Десятки почти одинаковых инструментовРаздувают контекст, усложняют выборГруппировать по смыслу; объединять близкие действия

Практическое задание

  • Возьмите инструмент из капстоуна Модуля 1 и перепишите его описание по схеме «что / когда / что возвращает».
  • Добавьте enum хотя бы для одного параметра и пометьте обязательные поля в required.
  • Снабдите описанием каждое поле параметров, а не только сам инструмент.
  • Проверьте на 3–4 запросах, что модель стала точнее выбирать инструмент и аргументы.
  • Найдите в своём наборе два инструмента-кандидата на объединение (или один — на разделение) и обоснуйте.

Проверка знаний

Зачем для параметра-статуса использовать enum вместо обычной строки?

  • A Так короче схема
  • B enum ограничивает значения закрытым набором и резко снижает число невалидных вызовов
  • C Строки модель вообще не поддерживает
  • D Это требование Go

В агенте есть инструмент manage(action, payload), где action — это "create"/"update"/"delete", а payload — произвольный JSON.

Что не так с такой гранулярностью?

  • A Всё хорошо — один инструмент удобнее
  • B Выбор действия спрятан в аргументе, теряется типизация и ясность; лучше отдельные инструменты на каждое действие
  • C Нужно добавить ещё больше действий в один инструмент
  • D Проблема только в названии payload

Кто основной «читатель» описания инструмента?

  • A Компилятор Go
  • B Сама модель — она по описанию решает, когда и как вызвать инструмент
  • C Только другие разработчики
  • D Линтер