Agents and LLMs
Wrap a quick run or an endpoint as a tool for agent frameworks and function calling, and narrate the bundle with your own LLM.
An endpoint is a stable REST microservice with its own OpenAPI description at https://api.schemalabs.ai/v2/openapi/{endpoint_id} (reference): POST data, get the full bundle. That is the universal path for agent frameworks, function calling, and LLM tool wrappers. A stateless quick run (POST /v2/run) is the same call without state, for agents that meet a new source mid-task with nobody available to explain it.
The pattern
A data agent wraps the endpoint’s REST API; the orchestrator routes data tasks to it; it returns compact JSON (the bundle) that flows to reasoning and action agents. The agent reasons over structured JSON, not raw rows: sector, column profile, entity matches with confidences, filled values, predictions with probabilities.
The lifecycle: quick run to understand a new source (nothing persists), create an endpoint when the same understanding should persist and carry a held-out score (a vertical product typically creates one per customer at onboarding), refresh and upgrade to keep it current. Branch on confidence at every step: act above your threshold, queue the rest for review, and mask columns flagged pii before they leave the agent.
orchestrator ──► data agent ──► POST /v2/serve/:id (or /v2/run)
▲ │
└── the bundle ◄─┘
sector · profile · map · imputation · predictions
Function-calling tool definition
Give your LLM one tool. The schema below is what the model needs to call run; for a live endpoint, fix the URL and drop base.
{
"name": "schema_run",
"description": "Sector, roles, PII, matches, imputation, prediction on raw tables.",
"parameters": {
"type": "object",
"properties": {
"tables": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "string" },
"columns": { "type": "array", "items": { "type": "string" } },
"rows": { "type": "array", "items": { "type": "array" } }
},
"required": ["id", "columns", "rows"]
}
},
"target": {
"type": "object",
"properties": { "mode": { "enum": ["auto", "none"] },
"column": { "type": "string" } }
}
},
"required": ["tables"]
}
}
The tool implementation is a POST /v2/run with the arguments as the body and Authorization: Bearer $SCHEMA_API_KEY. Return the bundle (or a trimmed view of it) as the tool result.
Frameworks
| Framework | How |
|---|---|
| LLM function calling, any provider | The tool definition above; the tool body calls the API |
| LangChain, LlamaIndex, CrewAI, AutoGen | Wrap the endpoint as a tool from its OpenAPI spec (https://api.schemalabs.ai/v2/openapi/{endpoint_id}); observability layers such as LangSmith trace it like any tool call |
| MCP-style tool servers | Expose run and serve as tools; the bundle is the tool result |
Narration
Pass the bundle to your LLM with the endpoint’s system_prompts (active fragments, in order) as the system prompt; read them from the endpoint object (GET /v2/endpoints/:id, which needs the read scope; a serve-only key covers the serve call alone). See System prompts.
import os, json, requests
SCHEMA_API_KEY = os.environ["SCHEMA_API_KEY"]
HEADERS = {"Authorization": f"Bearer {SCHEMA_API_KEY}", "Content-Type": "application/json"}
EP_ID = "{endpoint_id}"
SERVE_URL = f"https://api.schemalabs.ai/v2/serve/{EP_ID}"
# llm.chat(): your own model call
bundle = requests.post(SERVE_URL, headers=HEADERS, json={"tables": tables}).json()
ep_url = f"https://api.schemalabs.ai/v2/endpoints/{EP_ID}"
ep = requests.get(ep_url, headers=HEADERS).json()
fragments = sorted(ep["system_prompts"], key=lambda f: f["order"])
system = "\n\n".join(f["body"] for f in fragments if f["active"])
user = f"Question: {q}\n\nSchema bundle:\n{json.dumps(bundle)}"
answer = llm.chat(system=system, user=user)const SCHEMA_API_KEY = process.env.SCHEMA_API_KEY;
const HEADERS = { Authorization: `Bearer ${SCHEMA_API_KEY}`, 'Content-Type': 'application/json' };
const EP_ID = '{endpoint_id}';
const SERVE_URL = `https://api.schemalabs.ai/v2/serve/${EP_ID}`;
// llm.chat(): your own model call
const res = await fetch(SERVE_URL, { method: 'POST', headers: HEADERS,
body: JSON.stringify({ tables }) });
const bundle = await res.json();
const epUrl = `https://api.schemalabs.ai/v2/endpoints/${EP_ID}`;
const ep = await (await fetch(epUrl, { headers: HEADERS })).json();
const system = ep.system_prompts.filter(f => f.active)
.sort((a, b) => a.order - b.order).map(f => f.body).join('\n\n');
const answer = await llm.chat({ system,
user: `Question: ${q}\n\nSchema bundle:\n${JSON.stringify(bundle)}` });Keys for agents
Give the agent a run key for quick runs, or a serve-only key for production; a leaked agent key then cannot create, change, or delete anything. See Authentication. Pin datasets an agent re-queries so unchanged rows bill the cached rate (Data and pinning); elect batch for offline sweeps.