Function calling
Dar a un LLM 299 endpoints como 299 herramientas es lo más obvio, y también un error. Aquí tienes la estructura que funciona y los cuatro fallos que conviene prevenir desde el diseño.
No definas 299 herramientas
En todos los grandes proveedores el rendimiento empeora a medida que crece la lista de herramientas: los esquemas llenan la ventana de contexto antes de que empiece la conversación, y el modelo elige peor en cuanto varias herramientas tienen descripciones parecidas. Con 299 endpoints, la mayoría con los mismos cuatro campos, gastarías miles de tokens por turno para que el modelo eligiera peor.
Define una herramienta, o dos. El modelo indica la ruta como argumento en lugar de elegir entre cientos de esquemas.
Con una sola herramienta
{
"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 descripción cumple dos funciones. Dice cuándo usar la herramienta y dice que no haga los cálculos por su cuenta, algo más importante de lo que parece. Si le pides un nakshatra a un modelo, te dará sin problema uno creíble sacado de sus datos de entrenamiento si nada le indica que las posiciones planetarias requieren unas efemérides.
El handler
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;
}Hay tres cosas que conviene conservar cuando lo adaptes. La clave se lee en el servidor: una clave en el código del navegador es una clave que ahora gasta otra persona. El 402 no se reintenta: significa que el saldo está a cero, así que un bucle de reintentos con espera solo pierde tiempo y no arregla nada. El mensaje de error se transmite tal cual: la API indica qué campo estaba mal, y un modelo que lo recibe corregirá su propia solicitud en el siguiente turno.
Con dos herramientas, cuando el modelo necesita explorar
Si tus usuarios hacen preguntas abiertas, el modelo no sabrá qué ruta necesita. Dale también una herramienta de búsqueda: buscar no consume créditos, así que explorar es gratis y solo se cobra la respuesta.
[
{
"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"]
}
}
]Apunta occult_search a /llms.txt o a la versión más completa, /llms-full.txt; los dos existen precisamente para esto. También hay una especificación OpenAPI 3.1 si tu framework genera las herramientas a partir de una.
Cuatro fallos que conviene prevenir
- Una llamada por cálculo. La mayoría de los endpoints aceptan un array
keysy calculan varias cosas en una solicitud por un crédito. Si lo dejas a su aire, el modelo llamará cuatro veces y gastará cuatro. Indícalo en la descripción de la herramienta. - Reintentar un 402. Es un problema de saldo, no de red. Márcalo como no reintentable en el handler, porque el modelo no puede distinguirlo solo por el código de estado.
- Que la clave llegue al cliente. Las herramientas se ejecutan en tu servidor. El modelo nunca necesita ver la clave, y cualquier diseño en el que pudiera verla está a una inyección de prompt de filtrarla.
- Nombres de campo inventados. Los modelos prueban con
dateolat. Los campos reales sondate_time,latitude,longitudeytimezone_as_float. Ponlos en la descripción en lugar de dejar que el modelo los descubra a base de errores 400.
Así es una llamada real
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"]
}'Cuatro cálculos, un crédito. La respuesta incluye X-Credits-Charged y X-Credits-Remaining, la forma más sencilla de mostrar el gasto acumulado en tu propia interfaz.