Tool Use and Function Calling: How AI Agents Touch Real Systems
Function calling is easy to demo and hard to productionize. How we design tool layers for AI agents across 9 connected systems: schemas, idempotency, permissions.
Ostap Kovalisko
Founder & AI Systems Architect
Function calling is the moment an AI system stops being a text box and starts being an agent: the model emits a structured call, your code executes it against a real API, and something actually happens — an email sends, a task appears, a billing entry posts. It's also the moment your error budget drops to near zero, because the failures are no longer wrong words but wrong actions.
Our production platform exposes 30 actions across 9 connected sources — email, tasks, documents, chat, billing, CRM, signatures, meetings, and calendar-adjacent tooling. Here's what we've learned about building the tool layer that sits between an LLM and systems that matter.
Tools Are an API You Design for a Model
The biggest mental shift: your tool definitions are a UI, and the model is the user. The same design failures that confuse humans confuse models.
- One tool, one job. A manage_tasks tool with a mode parameter gets misused constantly. Separate create_task, complete_task, and reassign_task tools almost never do.
- Descriptions are prompts. Every tool and parameter description gets read on every call. Say when to use the tool, when not to, and what happens on execution — the "when not to" clause prevents more misfires than anything else.
- Enums over free text. Any parameter with a known value set becomes an enum. Free-text status fields produce "Done", "done", "completed", and "finished ✅" within a week.
- Small tool sets per context. We never hand the model all 30 actions' tools at once. Intent classification picks the action first; the execution step sees only the 2–5 tools that action needs. Accuracy goes up, tokens go down.
The Execution Wrapper
The model proposes a call; it never touches the API directly. Every call passes through a wrapper that enforces the properties LLMs can't guarantee:
| Layer | What it does | Failure it prevents |
|---|---|---|
| Schema validation | Reject malformed or incomplete arguments before execution | Garbage writes to downstream systems |
| Reference resolution | Verify IDs exist; resolve names to records, ask if ambiguous | Acting on hallucinated or wrong entities |
| Permission check | Enforce the acting user's rights, not the agent's | Privilege escalation through the AI |
| Risk gate | Route consequential calls to the approval queue | Autonomous mistakes with external blast radius |
| Idempotency key | Deduplicate retried calls | Double-sent emails, duplicate billing entries |
| Audit write | Record call, arguments, result, and approver | Unreconstructable incidents |
Idempotency deserves emphasis. Agent loops retry; networks flake; users double-click. Every mutating tool takes a deterministic key derived from the triggering event, and the wrapper drops duplicates. The first duplicated client email teaches this lesson expensively — learn it from this paragraph instead.
Error Messages Are for the Model
When a tool fails, the error text goes back into the model's context — so write errors the model can act on. "Error 422" produces a retry loop. "Client 'Acme Corp' matched 2 records: Acme Corp (ID 118), Acme Corp Holdings (ID 240). Specify which."produces a clarifying question to the user. We treat tool error strings with the same care as prompts, because that's what they are.
A good tool layer makes the model's job boring: pick the obvious tool, fill the obvious fields, let the wrapper handle everything sharp.
Read Tools vs Write Tools
We split the catalog hard along this line. Read tools (search, fetch, summarize across the 9 sources) execute freely — worst case is a useless answer. Write tools carry risk tiers: low-risk writes auto-complete with a revert path, and anything client-facing lands in the approval queue where a human sees the exact payload before it runs. This split is also how new integrations ship fast: reads go live in shadow-validated days, writes graduate one at a time as approval rates prove out.
Testing the Tool Layer
- Unit-test tools without the model: every tool runs against fixtures and a sandbox tenant — a tool you can't test in isolation is a tool you can't trust
- Replay real transcripts: store production tool-call sequences and replay them against changes to catch regressions in argument construction
- Fuzz the arguments: models will eventually send empty strings, wrong-locale dates, and 10,000-character notes; the wrapper must reject all of it gracefully
- Chaos the dependencies: kill a downstream API in staging and verify the agent reports the failure instead of hallucinating success
At 500+ tool executions per week, the model is honestly the most reliable component in the chain — modern LLMs format calls well. The engineering value is all in the wrapper: validation, resolution, permissions, idempotency, audit. Build that layer once, properly, and every new action becomes a schema, a handler, and a risk tier — a day's work instead of a project.
Let's Talk About Your Project
Have questions about nearshoring or AI development? Our team is here to help you make the right decision.
- ✓Free consultation on your AI project
- ✓Custom cost estimates and timeline
- ✓Access to nearshore talent pools