Tool Calling (Function Calling) Explained
How LLMs call functions: tool schemas, the request → tool_use → tool_result cycle, and how to design tools a model uses well.
What is tool calling?
Tool calling (also called function calling) lets an LLM ask your application to run a function. The model can't browse the web, read your database or do exact maths on its own — but it can say "please call get_order_status with order_id = 1042", and your code does the rest.
Tool calling is the foundation of every AI agent. Get it right and agents become reliable; get it wrong and they guess, loop or call the wrong thing.
The four-step cycle
You → Model : user message + list of tool definitions
Model → You : stop_reason = "tool_use", plus a tool_use block {name, input}
You → Model : tool_result block with the output (matched by tool_use id)
Model → You : final answer (or another tool call)
Two rules make this work:
- Every
tool_useblock has an id. Yourtool_resultmust reference the same id. - The model only requests a call. Running it — and deciding whether it's allowed — is your job.
Defining a tool
A tool definition has three parts: a name, a description, and a JSON Schema for its input.
{
"name": "get_order_status",
"description": "Look up the current shipping status of a customer order. Use this whenever the user asks where their order is or when it will arrive. Returns status, carrier and estimated delivery date.",
"input_schema": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The order number, e.g. '1042'. Digits only, no # sign."
}
},
"required": ["order_id"]
}
}
The model reads the description to decide when to use a tool, and the schema to decide how to call it. Treat both like documentation written for a new teammate.
What the model sends back
When the model wants a tool, the response stops with stop_reason: "tool_use" and includes a block like this:
{
"type": "tool_use",
"id": "toolu_01A9...",
"name": "get_order_status",
"input": { "order_id": "1042" }
}
Your code runs get_order_status("1042") and replies with a tool_result:
{
"type": "tool_result",
"tool_use_id": "toolu_01A9...",
"content": "{\"status\": \"shipped\", \"carrier\": \"TCS\", \"eta\": \"2026-11-02\"}"
}
If the tool fails, still send a result — add "is_error": true and explain what went wrong. The model can then retry with different input or tell the user, instead of guessing.
Parallel tool calls
A model may request several tools in one response — for example, weather for three cities. Run them (in parallel if you like), then return all the tool_result blocks together in a single message. Splitting them across separate messages teaches the model to stop making parallel calls.
How to design tools a model uses well
1. Write descriptions for "when", not just "what". "Gets weather" is weak. "Get the current weather for a city. Use this when the user asks about temperature, rain or what to wear" is strong.
2. Keep tools focused and distinct.
search_orders and find_orders side by side will confuse the model. Merge or rename so each tool has one clear job.
3. Use enums and formats.
If a field accepts only "celsius" or "fahrenheit", say so with "enum". Constrained inputs mean fewer invalid calls.
4. Return useful, compact results. Send back what the model needs to reason — not a 50,000-token raw API dump. Summarise or paginate large outputs.
5. Make errors actionable.
"Order 1042x not found. Order IDs are digits only." lets the model fix its own mistake.
Strict schemas
Many APIs let you mark a tool as strict (for example "strict": true with "additionalProperties": false). The model's arguments are then guaranteed to match your schema exactly, which removes a whole class of parsing bugs. Use it for tools whose inputs feed straight into code.
Tool calling vs structured output
| Use case | Choose |
|---|---|
| The model must do something (look up, compute, act) | Tool calling |
| You just need the answer as JSON in a fixed shape | Structured output / JSON schema response format |
Don't create a fake tool only to get JSON back — most modern APIs support structured outputs directly.
Common mistakes
- Vague descriptions. The model can't call a tool correctly if it can't tell when the tool applies.
- Mismatched ids. A
tool_resultwhosetool_use_iddoesn't match is rejected by the API. - Dropping failed calls. Always return a result, even an error — silence makes the model assume success.
- Parsing tool input with string matching. Always parse
inputas JSON; escaping can differ between models. - Giving tools more power than needed. A "read orders" tool should not be able to delete orders.
Key takeaways
- Tool calling = the model requests, your code executes and returns a matched
tool_result. - Name, description and JSON Schema are the model's only documentation — write them carefully.
- Return all parallel results in one message, and return errors with
is_error. - Next lesson: wire tools into a complete agent loop in Python.