Files
crank/docs/tool-design.md
T
bsodfather 63f8ee333f
CI / Rust Checks (push) Successful in 12m5s
CI / UI Checks (push) Successful in 9s
CI / Deployment Manifests (push) Successful in 6s
CI / Frontend E2E (push) Failing after 30s
CI / Deploy (push) Has been skipped
наблюдаемость: измерять бюджет каталога MCP
2026-07-21 01:59:09 +03:00

172 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Проектирование MCP-инструментов
Этот документ описывает, как делать REST-инструменты, с которыми LLM-клиент работает предсказуемо.
## Главная идея
Хороший инструмент решает одну понятную задачу. Модель должна легко понять:
- когда вызывать инструмент;
- какие параметры нужны;
- что считается успешным результатом;
- что делать при ошибке.
Плохой инструмент обычно слишком общий: `call_api`, `manage_user`, `execute_request`. Такие названия и описания заставляют модель угадывать.
## Название и описание
Имя инструмента должно быть техническим и стабильным:
```text
get_customer_by_email
create_support_ticket
delete_draft_invoice
```
Описание должно начинаться с действия и объяснять сценарий:
```text
Получает карточку клиента по email. Используйте, когда нужно найти клиента перед созданием обращения или проверкой статуса заказа. Возвращает идентификатор клиента, имя, email и текущий статус.
```
Не используйте расплывчатые формулировки:
```text
Работает с клиентами.
Обрабатывает данные.
Выполняет запрос к API.
```
## Входные параметры
Каждый параметр должен иметь понятный смысл. Если значение выбирается из небольшого набора, лучше задать `enum`.
Плохо:
```json
{
"action": "string",
"data": "object"
}
```
Хорошо:
```json
{
"email": "user@example.com",
"include_orders": true
}
```
Если endpoint делает несколько разных действий через параметр `action`, чаще всего лучше разделить его на несколько инструментов.
## Ответ инструмента
Не отдавайте модели весь ответ внешнего API без необходимости. Лучше выбрать только поля, которые нужны для следующего шага рассуждения.
Плохо:
```json
{
"response": "{ весь JSON от внешнего API }"
}
```
Хорошо:
```json
{
"customer_id": "cus_123",
"status": "active",
"open_orders_count": 2
}
```
Для массивов ограничивайте размер ответа и возвращайте только важные поля.
## Ошибки
Crank возвращает структурированные ошибки. MCP-клиент получает код ошибки, сообщение, признак повторяемости и рекомендацию.
Пример:
```json
{
"error_code": "upstream_rate_limited",
"message": "Внешний API вернул HTTP 429.",
"recoverable": true,
"suggested_action": "Повторите запрос позже.",
"request_id": "req_123"
}
```
Это лучше, чем отдавать сырой ответ внешнего API или stack trace.
## Повторное выполнение POST/PATCH
Для операций, которые меняют состояние, используйте idempotency key. Это защищает от повторного создания сущности, если MCP-клиент повторит вызов после таймаута.
Пример входа:
```json
{
"request_id": "order-2026-06-21-001",
"customer_id": "cus_123",
"amount": 1200
}
```
В конфигурации операции можно указать, что `request_id` используется как ключ идемпотентности и передается во внешний API через заголовок `Idempotency-Key`.
## Опасные операции
Для `DELETE` Crank использует двухшаговое подтверждение.
Первый вызов не отправляет запрос во внешний API. Он возвращает confirmation token:
```json
{
"error_code": "confirmation_required",
"message": "Операция требует подтверждения. Повторите вызов с _crank_confirmation_token=\"ct_...\"."
}
```
Второй вызов должен повторить те же аргументы и добавить токен:
```json
{
"order_id": "ord_123",
"_crank_confirmation_token": "ct_..."
}
```
Токен одноразовый, имеет короткий срок жизни и привязан к агенту, операции, версии и исходным аргументам.
## Каталог агента
Не привязывайте к одному агенту весь API. Лучше создать несколько агентов под конкретные задачи:
- агент для курсов валют;
- агент для поддержки клиентов;
- агент для заявок и инцидентов;
- агент для отчетов.
Если у агента слишком много похожих инструментов, модель чаще ошибается при выборе.
Crank измеряет опубликованный каталог по тому же компактному JSON, который возвращается в `tools/list`. В расчёт входят имя, заголовок, описание, входная JSON-схема и добавляемое Crank описание подтверждения опасной операции. Для сравнения используется независимая от конкретной модели консервативная оценка: один токен на три байта UTF-8. Это не счётчик токенов конкретного поставщика, а стабильная инженерная метрика для поиска регрессий.
Рекомендуемый бюджет одного агентского каталога — не более 4096 оценочных токенов. Превышение не блокирует публикацию, потому что допустимый объём зависит от модели, но создаёт предупреждение. Сначала сокращайте лишние описания и схемы. Если инструменты решают разные задачи, разделяйте их между специализированными агентами. Выбор нужного агента и постепенное раскрытие каталогов выполняет оркестратор Drivetrain, а не Crank.
## Проверочный список
- Имя инструмента конкретное и не похоже на `call_api`.
- Описание объясняет, когда вызывать инструмент.
- У каждого важного параметра есть понятное назначение.
- Multi-action endpoint разделен на отдельные инструменты.
- Ответ не содержит лишний большой JSON.
- Для POST/PATCH задан idempotency key, если повторный вызов может создать дубль.
- Для DELETE пользователь видит двухшаговое подтверждение.
- Агенту привязаны только инструменты, нужные для его задачи.
- Опубликованный каталог укладывается в выбранный бюджет контекста модели.