Aller au contenu

Function calling

Donner 299 endpoints à un LLM sous forme de 299 outils, c’est l’approche évidente, et la mauvaise. Voici la structure qui fonctionne, et les quatre échecs à prévenir dès la conception.

Ne définissez pas 299 outils

Chez tous les grands fournisseurs, la qualité baisse à mesure que la liste d’outils s’allonge : les schémas remplissent la fenêtre de contexte avant même le début de la conversation, et la précision du choix chute dès que plusieurs outils ont des descriptions proches. Avec 299 endpoints, dont la plupart prennent les quatre mêmes champs, vous dépenseriez des milliers de tokens par tour pour rendre le modèle moins bon dans ses choix.

Définissez un outil, ou deux. Le modèle passe la route en argument, au lieu de choisir parmi des centaines de schémas.

La structure à un outil

définition de l’outil
{
  "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"]
  }
}

La description remplit deux rôles. Elle dit quand utiliser l’outil, et elle dit de ne pas faire le calcul soi-même, ce qui compte plus qu’il n’y paraît. Un modèle à qui l’on demande un nakshatra en produira volontiers un plausible à partir de ses données d’entraînement, si rien ne lui dit que les positions planétaires exigent des éphémérides.

Le gestionnaire

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;
}

Trois points méritent d’être conservés quand vous l’adaptez. La clé est lue côté serveur : une clé dans du code navigateur, c’est une clé que quelqu’un d’autre dépense déjà. Une erreur 402 n’est pas relancée : elle signifie que le solde est vide, et une boucle de relance ne ferait que perdre du temps sans rien régler. Le message d’erreur est transmis tel quel : l’API indique quel champ était faux, et un modèle qui reçoit cette information corrige lui-même sa requête au tour suivant.

La structure à deux outils, quand le modèle doit explorer

Si vos utilisateurs posent des questions ouvertes, le modèle ne saura pas de quelle route il a besoin. Donnez-lui aussi un outil de recherche : la recherche ne coûte aucun crédit, donc l’exploration est gratuite et seule la réponse est facturée.

définitions des outils
[
  {
    "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"]
    }
  }
]

Faites pointer occult_search vers /llms.txt ou vers la version complète /llms-full.txt, qui existent précisément pour cela. Une spécification OpenAPI 3.1 est aussi disponible si votre framework génère des outils à partir d’une spécification.

Quatre échecs à prévenir dès la conception

  • Un appel par calcul. La plupart des endpoints acceptent un tableau keys et calculent plusieurs choses en une requête, pour un crédit. Livré à lui-même, un modèle fera quatre appels et dépensera quatre crédits. Écrivez-le dans la description de l’outil.
  • Relancer une erreur 402. C’est un problème de solde, pas de réseau. Marquez-la comme non relançable dans le gestionnaire, car le modèle ne peut pas faire la différence à partir du seul code de statut.
  • La clé qui arrive côté client. L’exécution des outils doit se faire sur votre serveur. Le modèle n’a jamais besoin de voir la clé, et toute conception où il le pourrait est à une injection de prompt d’une fuite.
  • Des noms de champs inventés. Les modèles devinent date ou lat. Les vrais champs sont date_time, latitude, longitude et timezone_as_float. Indiquez-les dans la description au lieu de laisser le modèle les découvrir via une erreur 400.

À quoi ressemble vraiment un appel

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"]
  }'

Quatre calculs, un crédit. La réponse contient X-Credits-Charged et X-Credits-Remaining : c’est le moyen le plus simple d’afficher le coût en cours dans votre propre interface.

Suivant