Function calling
Transformar 299 endpoints em 299 ferramentas para um LLM é o caminho óbvio, e o errado. Veja o formato que funciona e as quatro falhas que vale a pena evitar desde o design.
Não crie 299 ferramentas
Todos os grandes provedores pioram conforme a lista de ferramentas cresce. Os schemas ocupam a janela de contexto antes de a conversa começar, e o modelo erra mais a escolha quando várias ferramentas têm descrições parecidas. Com 299 endpoints, quase todos com os mesmos quatro campos, você gastaria milhares de tokens por turno só para o modelo escolher pior.
Crie uma ferramenta, ou duas. O modelo informa a rota como argumento, em vez de escolher entre centenas de schemas.
O formato com uma ferramenta
{
"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"]
}
}A descrição tem duas funções. Ela diz quando usar a ferramenta e diz para o modelo não fazer as contas sozinho, o que importa mais do que parece. Se você pede um nakshatra a um modelo e nada avisa que posições planetárias exigem efemérides, ele inventa um plausível a partir dos dados de treinamento sem pestanejar.
O 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;
}Ao adaptar o código, mantenha três coisas. A chave é lida no servidor: chave no código do navegador é chave que outra pessoa já está gastando. Um 402 não é repetido: ele quer dizer que o saldo acabou, então um loop de backoff só perde tempo e não resolve nada. A mensagem de erro é repassada: a API diz qual campo estava errado, e o modelo que recebe essa informação corrige a própria requisição no turno seguinte.
O formato com duas ferramentas, para quando o modelo precisa explorar
Se os seus usuários fazem perguntas abertas, o modelo não vai saber qual rota usar. Dê a ele também uma ferramenta de busca. Buscar não consome créditos, então a exploração sai de graça e só a resposta é cobrada.
[
{
"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"]
}
}
]Aponte occult_search para /llms.txt ou para a versão mais completa, /llms-full.txt; os dois existem exatamente para isso. Também há uma especificação OpenAPI 3.1, caso o seu framework gere ferramentas a partir dela.
Quatro falhas que vale a pena evitar desde o design
- Uma chamada por cálculo. A maioria dos endpoints aceita um array
keyse calcula várias coisas em uma só requisição, por um crédito. Sem orientação, o modelo chama quatro vezes e gasta quatro. Diga isso na descrição da ferramenta. - Repetir um 402. É problema de saldo, e não de rede. Marque esse erro como não repetível no handler, porque só pelo código de status o modelo não percebe a diferença.
- A chave chegando ao cliente. As ferramentas devem rodar no seu servidor. O modelo nunca precisa ver a chave, e qualquer design em que ele possa vê-la está a uma injeção de prompt de um vazamento.
- Nomes de campo inventados. Os modelos chutam
dateoulat. Os campos de verdade sãodate_time,latitude,longitudeetimezone_as_float. Coloque-os na descrição, em vez de deixar o modelo descobrir com um erro 400.
Como fica uma chamada de verdade
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"]
}'Quatro cálculos, um crédito. A resposta traz X-Credits-Charged e X-Credits-Remaining, o jeito mais barato de mostrar o custo acumulado na sua própria interface.