13 · Giving the model tools
A tool is just a function the model is allowed to call: a name, a description, and an argument schema. The model emits a tool call; your code dispatches it and feeds the result back as an observation.
Tools let an LLM act on the world. Each tool is a function with a name, a description, and an argument schema; you advertise them to the model, it replies with a structured tool call, and YOUR code validates, dispatches, and returns the result as an observation. The model never runs code — it only requests it.
Without this:
Without tools the model can only emit text from its frozen weights — it can't compute exactly, look up live data, or change anything. Tools are how it reaches outside its own context.
In the last lesson the agent's actions were hard-coded (add_coin). Tools generalize that: a tool is any function you let the model invoke. Each tool you expose has three parts the model needs to use it well:
- name — a short identifier the model emits to pick the tool (
calculator). - description — one sentence telling the model when to use it. This is the most important part: the model decides which tool to call purely from these descriptions.
- argument schema — the parameters and their types, so the model knows how to fill in the call.
The protocol is a clean round-trip:
- You send the model the list of available tools (name + description + schema).
- The model replies — instead of plain text — with a tool call: a structured object naming a tool and its arguments, e.g.
{"name": "calculator", "args": {"expression": "12 * 7"}}. - Your code validates the call, runs the matching function, and captures the result.
- You feed that result back to the model as an observation, and it continues.
The critical safety point: the model never executes anything itself. It only requests a call. Your runtime decides whether to honor it, with what permissions, and how to sanitize the arguments. That boundary is where all of tool-use security lives.
Below is a small tool registry — exactly the dictionary you'd serialize and show the model — with three tools, plus the dispatcher that turns a tool call into an observation. (The calculator is a tiny hand-written arithmetic parser so the cell stays safe and self-contained.)
Python (in browser)
A tool registry + dispatcher: name/description/schema advertised to the model, then a call is validated and run by YOUR code (note the unknown tool is rejected).
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Real providers formalize this exact protocol as function calling (OpenAI) or tool use (Anthropic). You describe each tool with a JSON Schema; the model returns a structured tool_calls field instead of free text; you run the function and send the result back with a special "tool" message. Read the OpenAI-style version below and map each piece onto the registry you just ran.
Provider function-calling is our registry made official: tools described as JSON Schema, the model returns a structured `tool_calls`, your code dispatches and returns the result.
When an LLM 'uses a tool', what actually executes the tool's code?
- A tool = a function exposed to the model with a name, a description, and an argument schema.
- The model only emits a structured tool call; YOUR code validates, dispatches, and returns the result as an observation.
- Clear descriptions + tight schemas are how the model chooses correctly — they're the model's interface to your tools.
Function calling / tool use powers retrieval, code execution, web search, database queries, and every plugin — the model decides *what* to call and your registry decides *whether and how* to run it.
If you remove it: Without tools the model is sealed inside its training data: no exact arithmetic, no live facts, no side effects — it can only describe actions, never take them.