A language model can't book a flight, run a query, or read today's price on its own - it only produces text. Tool calling bridges that gap: you describe your functions as typed schemas, the model emits a structured call when it needs one, your host executes it, and the result flows back into the conversation. Repeat the loop and a chat model becomes an agent that can act.
Tool calling (also called function calling) lets a language model invoke code you control. You give the model a list of tool definitions - each with a name, a description, and a JSON-Schema for its arguments. When the model decides a tool would help, it doesn't run anything itself; it emits a structured request naming the tool and its arguments. Your application executes the function and hands the result back, and the model continues with that new information in hand.
Declare tools as name + description + typed parameter schema. This is the contract the model reads to decide what it can do.
Given the conversation and tools, the model emits a structured tool_use block with arguments - or answers directly if no tool is needed.
Your host validates the args, runs the function, and sends a tool_result back so the model can finish the answer.
| Piece | What it does | Notes |
|---|---|---|
| Tool definition | Name, description, and JSON-Schema of parameters | The description is prompt - write it for the model to read |
| tool_use block | Model's structured request: tool name + argument object | Carries a unique id to pair with its result |
| Host executor | Validates args, runs the function, catches errors | Your trust boundary - never execute blindly |
| tool_result block | Return value (or error) sent back to the model | References the same tool_use_id |
| Agent loop | Repeats call โ execute โ return until done | Ends when the model stops requesting tools |
On its own a model is a closed box: no live data, no side effects, no arithmetic you can trust. Tool calling turns it into a controller that reasons about which action to take, while your code does the acting - reliably, verifiably, and within your rules.
Fetch today's inventory, a customer record, or a live price. The model reasons; your tool provides ground truth it never memorized.
Send an email, open a ticket, run a deployment. Tools let a model do things, not just describe them - turning chat into an agent.
A JSON-Schema forces arguments into typed, validated shapes, so you get parseable structure instead of free-text you must guess at.
Every action passes through your executor, where you enforce permissions, log calls, and gate anything risky behind confirmation.
Same idea, four ways to picture it, so it clicks whoever you are.
Tool calling is like a take-home test where you may use a calculator. You still do the thinking, but for the arithmetic you fill in the exact numbers on the calculator, press the button, and copy the result back into your answer.
It's the model calling a typed API. You publish function signatures as JSON-Schema; the model returns a structured argument object; your code runs the real function and returns the value, just like an RPC where the model is the caller.
Think of a manager who delegates by filling out a proper request form. The manager decides what needs doing and specifies the details, but a specialist actually executes it and reports the outcome back.
A waiter taking a structured order to the kitchen. The waiter does not cook; they write down exactly what you want in a form the kitchen understands, hand it over, and bring back the finished dish.
Tool calling is a multi-turn loop, not a single request. The host sends the tools plus the conversation; the model replies with either an answer or one or more tool_use blocks; the host executes and appends tool_result blocks; the loop repeats until the model is done.
Each tool is name + description + a JSON-Schema of parameters (types, enums, required fields). The model reads this like documentation to choose a tool and shape its arguments.
Model requests a call โ host validates & runs it โ host returns the result โ model reads it and either calls again or answers. Parallel calls fan out and rejoin before the model continues.
flowchart LR
subgraph Client["๐ฅ๏ธ Host application"]
U["๐ค User message"] --> ORC["๐ Agent loop"]
ORC --> REG["๐ Tool registry + schemas"]
VAL["โ
Validate args"] --> EXEC["๐ ๏ธ Execute tool"]
EXEC --> EXT[("๐ APIs / DB / systems")]
end
subgraph Model["๐ง LLM"]
REASON["๐ญ Reason over tools"]
end
ORC --> REASON
REASON -->|"tool_use"| VAL
EXEC -->|"tool_result"| ORC
ORC -->|"final text"| ANS["โ
Answer to user"]
// 1) You declare the tool (name + description + JSON-Schema)
{
"name": "get_weather",
"description": "Get the current weather for a city. Use when the user asks about weather.",
"input_schema": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "City name, e.g. 'Paris'" },
"units": { "type": "string", "enum": ["celsius", "fahrenheit"], "default": "celsius" }
},
"required": ["city"]
}
}
// 2) The loop the host runs
messages = [ user("What's the weather in Paris?") ]
while True:
reply = model.generate(messages, tools=[get_weather, ...])
if reply.stop_reason != "tool_use":
return reply.text # model is done
for call in reply.tool_calls: # may be several (parallel)
args = validate(call.input, schema) # reject bad args early
result = registry[call.name](**args) # actually run it
messages.append(tool_result(call.id, result))
messages.append(reply) # keep the tool_use turn too
Notice the id that ties each tool_result to its originating tool_use, and that the host - never the model - is the thing that validates arguments and executes code. That boundary is where safety lives.
Three views: a single tool-call round trip, parallel tool calls that fan out and rejoin, and an invalid-arguments validation-and-retry loop.
sequenceDiagram
autonumber
participant U as ๐ค User
participant H as ๐ฅ๏ธ Host app
participant M as ๐ง LLM
participant T as ๐ ๏ธ Tool
U->>H: Ask a question
H->>M: messages + tool definitions
M-->>H: tool_use get_weather city=Paris
H->>H: Validate arguments against schema
H->>T: Execute get_weather(Paris)
T-->>H: 22C sunny
H->>M: tool_result for that tool_use id
M-->>H: Final natural-language answer
H-->>U: It is 22C and sunny in Paris
sequenceDiagram
autonumber
participant H as ๐ฅ๏ธ Host app
participant M as ๐ง LLM
participant W as ๐ฆ๏ธ Weather tool
participant F as ๐ฑ FX tool
H->>M: Compare weather and currency for two cities
M-->>H: Two tool_use blocks in one turn
Note over H: Dispatch both calls concurrently
par Weather lookup
H->>W: get_weather(Tokyo)
W-->>H: 18C rain
and FX lookup
H->>F: get_rate(JPY, USD)
F-->>H: 0.0064
end
Note over H: Join both results before replying
H->>M: tool_result weather + tool_result fx
M-->>H: Combined answer using both results
sequenceDiagram
autonumber
participant H as ๐ฅ๏ธ Host app
participant M as ๐ง LLM
participant V as โ
Validator
participant T as ๐ ๏ธ Tool
H->>M: Book a flight for the user
M-->>H: tool_use book_flight date=32/13
H->>V: Validate args against schema
V-->>H: Invalid date and missing passenger
alt arguments invalid
H->>M: tool_result is_error with reason
M-->>H: Corrected tool_use with valid args
end
H->>V: Re-validate corrected args
V-->>H: OK
H->>T: Execute book_flight
T-->>H: Booking confirmed
H->>M: tool_result success
M-->>H: Confirmation for the user
From defining a tool to delivering an answer built from real actions - the whole journey in order.
Give each tool a clear name, a description written for the model, and a JSON-Schema for its parameters with types, enums, and required fields.
Hand the model only the tools this task needs. Too many overlapping tools confuse selection; a tight, well-named set improves accuracy.
The host sends the conversation plus the tool definitions. The model reads the descriptions and schemas to understand what it can do.
The model reasons about the request and either answers directly or emits one or more structured tool_use blocks with arguments.
The host validates the emitted arguments against the schema - types, ranges, enums, required fields - rejecting anything malformed before it runs.
For side-effecting tools, check the user's rights, apply rate limits, and gate destructive actions behind confirmation or a dry run.
Call the underlying API, database, or service. Parallel calls fan out concurrently; use idempotency keys so retries never double-apply.
Return a structured result, or a clear is_error message on failure, so the model can adapt, retry, or ask the user for missing input.
Append each tool_result keyed to its tool_use id, plus the model's own tool-use turn, so the conversation stays consistent.
The model reads the results and either requests more tools - repeating the loop - or produces a final natural-language answer.
The user gets a response built from real data and real effects. Logs of every call feed evals and guardrail tuning over time.
Most tool-calling failures come from the tool design, not the model. These are the usual culprits.
Handing the model dozens of tools, several doing near-identical things, wrecks selection accuracy. It picks the wrong one or dithers. Expose a small, distinct set scoped to the task.
Names like do_thing and one-line descriptions leave the model guessing when to call and how. The description is prompt, write it to say precisely what the tool does and when to use it.
Trusting the model's emitted arguments and passing them straight to an API invites bad dates, injection, and out-of-range values. Validate types, enums, and ranges against the schema before executing.
Write actions with no confirmation or idempotency key double-charge, double-book, or double-send on a retry. Gate destructive actions behind approval and key them so replays are safe.
Returning raw stack traces, or looping forever on the same failing call, burns tokens and stalls the agent. Return a clear is_error message and cap retries so the model can adapt or stop.
Sprawling schemas with deep nesting, dozens of optional fields, and no descriptions confuse the model and bloat the prompt. Keep parameter shapes flat, minimal, and clearly documented.
Track selection and execution separately, so you know whether to fix the tool set or the tools themselves.
| Metric | What it tells you | Good sign |
|---|---|---|
| Correct tool-selection rate | How often the model picks the right tool for the task | High: names and descriptions are clear |
| Argument validity rate | Fraction of calls whose arguments pass schema validation | High: schemas guide the model well |
| Task completion rate | Share of requests the agent finishes end to end | High: the loop resolves reliably |
| Tool error / exception rate | How often executions fail or throw | Low: robust tools and inputs |
| Unnecessary-call rate | Calls made when none was needed, or redundant repeats | Low: the model calls only when useful |
| Latency per call | Time each tool round trip adds to the turn | Within your SLA and budget |
Three representative patterns showing tool calling in production-style use.
A travel assistant plans trips end to end - searching flights, checking hotels, then booking - mixing read-only lookups with real, side-effecting purchases.
search_flights, get_hotel_availability) run freely and often in parallel to compare options fast.book_flight is gated: it requires explicit user confirmation and carries an idempotency key so a retry never double-books.An analytics assistant answers business questions by turning them into SQL through a single, tightly-scoped query tool over the warehouse.
run_sql tool accepts a query string but runs against a read-only replica with row limits and a statement timeout.An on-call assistant triages incidents - reading logs and metrics, then executing remediation like scaling a service or rolling back a deploy.
get_logs, get_metrics) are read-only and unrestricted for fast investigation.scale_service, rollback) require an approval step and run with idempotency keys so replays are safe.Tool calling is evolving from a single function invocation into the backbone of agentic systems - standardized, composable, and safer by design.
Open standards like the Model Context Protocol let any model connect to any tool server, so integrations become plug-and-play instead of bespoke.
Specialized agents expose their skills as tools to one another, composing into systems where each agent calls the right teammate for a subtask.
Capability scopes, per-tool policies, and human-in-the-loop gates make side-effecting actions auditable and safe by default.
Models increasingly plan and dispatch independent calls at once, cutting latency for tasks that touch many systems.
Measuring selection accuracy, argument validity, and recovery from errors becomes the metric that governs agent reliability.
Agents that read tool errors, reflect, and retry with corrected arguments - turning brittle calls into robust, resilient workflows.