The Big Question
RAG lets a language model read external information. It can retrieve policy pages, meeting notes, documentation, and other text, then place that evidence into the context.
But what if the answer does not exist in a document?
Questions like What is my bank balance?, What is the weather right now?, Calculate the mortgage payment, Send this email, or Book me the cheapest flight from Vancouver to Tokyo next Friday require computation, live state, or action.
The model does not need more static knowledge. It needs a way to interact with the outside world. Tool use is how an LLM becomes the controller of a larger software system.
Core Intuition
A human employee does not memorize everything. They know when to use a calculator, open a calendar, search a database, ask accounting, or send an email. Intelligence is partly reasoning and partly choosing the correct tool.
LLM tool use works similarly. The model reads the conversation and the available tool descriptions. It decides whether a tool is needed. If so, it emits a structured tool call. The application validates and executes that call. The result returns to the model, which writes the final user-facing answer.
LLM chooses a tool
LLM emits JSON arguments
Application validates
Tool executes
Tool returns data
LLM writes final answer
The model itself does not check the weather, book the flight, or multiply large numbers. It produces structured instructions that software can execute.
Interactive Demo
User request
Available tools
Search available flights by origin, destination, date, and sort order.
Get current or forecast weather for a city and date.
Evaluate arithmetic expressions exactly.
Send an email to a recipient with a subject and body.
Experiments
{
"function": "search_flights",
"arguments": {
"origin": "Vancouver",
"destination": "Tokyo",
"date": "next Friday",
"sort_by": "price"
}
}Application boundary
- Parse structured output.
- Validate schema, types, required fields, and permissions.
- Execute the real tool outside the model.
- Return tool output to the model or user.
{
"flight": "AC003",
"price_usd": 641,
"departure": "10:15",
"arrival": "14:35 +1"
}Final response
The cheapest option I found is AC003 from Vancouver to Tokyo for $641, departing 10:15 and arriving 14:35 the next day. I would ask for confirmation before booking.
Disable tools, corrupt the JSON, or simulate an API failure. The point is the boundary: the model proposes; the application validates, executes, and enforces safety.
Mechanics
Function Calling Is Still Token Prediction
Tool use is not a separate mental organ inside the model. The model still predicts tokens:
The output may be natural language, or it may be a structured string that represents a function call. The conversation, system prompt, tool descriptions, and schemas all become context that changes the next-token distribution.
If the context includes a tool named search_flights with fields origin, destination, and date, the model can learn to emit JSON matching that schema:
{
"function": "search_flights",
"arguments": {
"origin": "Vancouver",
"destination": "Tokyo",
"date": "next Friday",
"sort_by": "price"
}
}Why Schemas Work
Without a schema, the model can output arbitrary text. With a schema, the application can require a specific structure: required fields, types, enums, ranges, and nested objects. Some systems also use constrained decoding, where invalid next tokens are removed from the decoder's valid set.
If the valid token set at a step is rather than the full vocabulary , decoding searches a smaller space:
This does not make the tool call semantically correct, but it improves syntactic reliability.
What Is A Tool?
A tool is an external capability exposed to the model through a description and schema, then executed by the application.
| Part | Purpose | Example |
|---|---|---|
| Name | A stable identifier the model can refer to | search_flights |
| Description | When the tool should be used | Search available flights by origin, destination, date, and sort order. |
| Input schema | The expected arguments and types | origin, destination, date, sort_by |
| Implementation | The actual code or API call | Call the flight provider API |
| Returned output | Structured result passed back to the model | flight number, price, departure time |
Function Signatures
A tool schema resembles a normal function signature:
The model must choose the function and generate arguments. Argument generation is information extraction: from weather tomorrow in Vancouver, extract city=Vancouver, date=tomorrow, and a default units=metric.
Concrete Walkthrough
For What is the weather tomorrow in Vancouver?, the system might proceed like this:
- User asks the question.
- The model reads available tool descriptions.
- The model selects get_weather.
- The model emits JSON arguments.
- The application parses and validates the JSON.
- The application calls the weather API.
- The API returns temperature, condition, humidity, and wind.
- The model turns raw JSON into a useful natural-language answer.
For Email Sarah tomorrow's forecast, the system needs sequential tools: first weather, then email. Sending the email should require confirmation because it changes the outside world.
Validation And Error Handling
Applications should never blindly trust model output. Validate before execution:
- Required fields are present.
- Values have the correct type.
- Enums use allowed values.
- Numbers are in safe ranges.
- The user has permission.
- The tool actually exists.
- The action is safe or confirmed.
{
"function": "search_flights",
"arguments": {
"origin": "Vancouver",
"destination": 42,
"date": null
}
}This is valid-looking JSON, but invalid for the schema. destination should be a string, and date is required.
Tool calls also fail at runtime: API timeout, invalid city, authentication failure, network error, unavailable provider, or partial failure. A good assistant should recover gracefully, retry when appropriate, ask for missing information, or explain what failed.
Multi-step Orchestration
A single function call solves one narrow step. Many requests require a loop:
model
tool call?
validate and execute
append tool result
model
repeat until final answer
The loop should have guardrails: maximum tool calls, timeouts, tool allowlists, confirmation for side effects, and clear stopping criteria. Without these, a model can enter an infinite tool loop or take actions the user did not approve.
Security
Tool use raises the stakes because model output can become real action. Security must be enforced by application code.
- Prompt injection can trick the model into calling dangerous tools.
- Tool injection can hide malicious instructions in tool output.
- SQL injection can appear inside generated database arguments.
- Command execution tools require strict sandboxing and allowlists.
- Emails, purchases, bookings, deletes, and transfers require confirmation.
- Tool results should be treated as data, not higher-priority instructions.
The rule is simple: never trust model outputs merely because they are structured. Structure makes execution possible; validation and permission checks make it safe.
Prompting, RAG, And Function Calling
| Capability | Prompting | RAG | Function Calling |
|---|---|---|---|
| Uses model memory | Yes | Yes | Yes |
| Reads external documents | No | Yes | Optional |
| Performs calculations | Weak | Weak | Yes |
| Uses live APIs | No | No | Yes |
| Takes actions | No | No | Yes |
| Updates external systems | No | No | Yes, with permission |
Implementation
From Scratch Dispatcher
from dataclasses import dataclass
from typing import Callable, Any
@dataclass
class Tool:
name: str
description: str
required: dict[str, type]
execute: Callable[[dict[str, Any]], dict[str, Any]]
def validate(tool: Tool, arguments: dict[str, Any]) -> None:
for field, expected_type in tool.required.items():
if field not in arguments:
raise ValueError(f"Missing required field: {field}")
if not isinstance(arguments[field], expected_type):
raise TypeError(f"{field} must be {expected_type.__name__}")
def dispatch(call: dict[str, Any], tools: dict[str, Tool]) -> dict[str, Any]:
tool_name = call.get("function")
if tool_name not in tools:
raise ValueError(f"Unknown tool: {tool_name}")
tool = tools[tool_name]
arguments = call.get("arguments", {})
validate(tool, arguments)
return tool.execute(arguments)Execution Loop
messages = [{"role": "user", "content": user_request}]
for step in range(max_tool_calls):
model_output = llm(messages, tools=tool_schemas)
if model_output.type == "final":
return model_output.content
if model_output.type == "tool_call":
result = dispatch(model_output.tool_call, tools)
messages.append({
"role": "tool",
"name": model_output.tool_call["function"],
"content": result,
})
raise RuntimeError("Tool loop exceeded the allowed step limit.")Real systems add authentication, retries, logging, rate limits, user confirmation, audit trails, and typed schemas.
Interview Discussion
What is function calling?
It is a pattern where the model emits a structured request for an external function, and the application validates and executes it.
How does the model choose a tool?
Tool descriptions and schemas are part of the context. The model predicts an output that names the tool and supplies arguments.
Why is validation necessary?
The model can produce missing fields, wrong types, invalid enums, unsafe values, or tools that do not exist.
What is the difference between RAG and tools?
RAG retrieves evidence. Tools perform computation, query live systems, or take actions.
Why require confirmation before some tool calls?
Actions like sending emails, booking flights, deleting files, or transferring money change external state and need user approval.
Active Recall
1. Why can RAG not answer every user request?
2. What five parts define a tool?
3. Why does function calling still count as next-token prediction?
4. What does a JSON schema give the application?
5. What should happen when a required argument is missing?
6. Why should tool outputs be treated as data rather than instructions?
7. What is the difference between raw tool output and a final user-facing answer?
8. Why do tool loops need limits?
Common Mistakes
- Wrong tool selected: The model routes a weather question to search or calculator.
- Wrong arguments: The model extracts Tokyo as origin instead of destination.
- Hallucinated function: The model calls a tool that was never registered.
- Invalid JSON: The output cannot be parsed.
- Tool unavailable: The API is down, disabled, or missing credentials.
- Infinite tool loop: The model keeps calling tools without reaching a final answer.
- Partial failure: One tool succeeds and a later tool fails.
- Unsafe action: The model tries to send, buy, delete, or execute without confirmation.
Connection To Memory
Tool use lets the application interact with the outside world. But a useful assistant also needs to remember what happened before: user preferences, previous tool results, stable facts, and long-running task state.
That leads naturally to memory, where the application stores and retrieves information across interactions instead of relying only on the current prompt.