Перейти к основному содержимому

Вызов функций

Дать LLM 299 эндпоинтов в виде 299 инструментов — очевидный путь, но неверный. Ниже — схема, которая работает, и четыре ошибки, от которых стоит защититься заранее.

Не заводите 299 инструментов

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

Заведите один инструмент или два. Модель передаёт маршрут как аргумент, а не выбирает среди сотен схем.

Схема с одним инструментом

описание инструмента
{
  "name": "occult_api",
  "description":
    "Vedic astrology calculations from the Occult API. Call this for anything involving a birth chart, panchanga, dasha, muhurta, matching or numerology. Do not compute astrology yourself - planetary positions require an ephemeris and cannot be derived from a date.",
  "input_schema": {
    "type": "object",
    "properties": {
      "route": {
        "type": "string",
        "description": "Endpoint path, e.g. /api/astro/panchanga/. Use the catalogue at https://occultapi.com/llms.txt to choose one."
      },
      "body": {
        "type": "object",
        "description": "Request body. Always include date_time (ISO 8601 with offset), latitude, longitude, timezone_as_float. Batch calculations with a keys array where the endpoint supports it."
      }
    },
    "required": ["route", "body"]
  }
}

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

Обработчик

node
async function occultApi({ route, body }) {
  const response = await fetch(`${process.env.OCCULT_API_BASE}${route}`, {
    method: "POST",
    headers: {
      "X-API-Key": process.env.OCCULT_API_KEY,   // server-side only
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });

  const payload = await response.json();

  // 402 means out of credits. It is NOT transient - retrying spends nothing
  // and fixes nothing, so surface it rather than backing off.
  if (response.status === 402) {
    return { error: "insufficient_credits", retryable: false };
  }

  // The envelope is the same on every endpoint: branch on is_error, and hand
  // the model the message rather than a stack trace it cannot act on.
  if (payload.is_error) {
    return { error: payload.message, retryable: false };
  }

  return payload.data;
}

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

Схема с двумя инструментами — когда модели нужно разведать

Если пользователи задают открытые вопросы, модель не будет знать, какой маршрут ей нужен. Дайте ей ещё и инструмент поиска: поиск кредитов не стоит, поэтому разведка бесплатна, а платите вы только за ответ.

описания инструментов
[
  {
    "name": "occult_search",
    "description": "Find the right Occult API endpoint by name or by what it calculates, e.g. 'manglik' or 'sunrise'. Returns routes and their request fields. Costs nothing.",
    "input_schema": {
      "type": "object",
      "properties": { "query": { "type": "string" } },
      "required": ["query"]
    }
  },
  {
    "name": "occult_call",
    "description": "Call an Occult API endpoint found with occult_search. One credit per successful call (the PDF reports and chat cost 25); failures are free.",
    "input_schema": {
      "type": "object",
      "properties": {
        "route": { "type": "string" },
        "body": { "type": "object" }
      },
      "required": ["route", "body"]
    }
  }
]

Направьте occult_search на /llms.txt или более полный /llms-full.txt — оба файла существуют именно для этого. Если ваш фреймворк генерирует инструменты по спецификации, есть и спецификация OpenAPI 3.1.

Четыре ошибки, от которых стоит защититься

  • Один вызов на каждый расчёт. Большинство эндпоинтов принимают массив keys и считают несколько вещей в одном запросе за один кредит. Предоставленная сама себе, модель сделает четыре вызова и потратит четыре кредита. Напишите об этом в описании инструмента.
  • Повтор при 402. Это проблема баланса, а не сети. Пометьте эту ошибку в обработчике как неповторяемую: по одному коду статуса модель разницы не увидит.
  • Ключ попадает на клиент. Инструменты должны выполняться на вашем сервере. Модели никогда не нужно видеть ключ, и при любой схеме, где она может его увидеть, до утечки остаётся одна prompt-инъекция.
  • Выдуманные названия полей. Модели угадывают date или lat. На самом деле поля называются date_time, latitude, longitude и timezone_as_float. Укажите их в описании, а не ждите, пока модель узнает о них из ошибки 400.

Как выглядит реальный вызов

curl
curl -X POST https://api.occultapi.com/api/astro/panchanga/ \
  -H 'X-API-Key: yt_live_your_key_here' \
  -H 'Content-Type: application/json' \
  -d '{
    "date_time": "2026-09-17T06:00:00+05:30",
    "latitude": 26.9124,
    "longitude": 75.7873,
    "timezone_as_float": 5.5,
    "keys": ["tithi", "nakshatra_segments", "yogam", "karana"]
  }'

Четыре расчёта — один кредит. В ответе есть X-Credits-Charged и X-Credits-Remaining — это самый дешёвый способ показывать текущие расходы в вашем интерфейсе.

Дальше