What is a tool?
In What is an agent harness? and The harness loop, step by step we kept saying the model "asks for a tool" like
look_up_orders — and we kept hand-waving what that actually means. How does
the model even know that tool exists? How does it know what to pass it? This
lesson answers that, and then shows the real code.
A tool is a function the model is told about
Here's the thing that trips people up at first: the model can't see your code.
It has no idea you wrote a look_up_orders function. The only way it learns a
tool exists is that you describe it and hand that description to the model
on every call (remember from The harness loop, step by step — the model has no memory).
So a tool has two halves:
- The function — the real code the harness runs. The model never touches this.
- The description (the "schema") — a small, structured note that tells the model the tool exists, what it's for, and what arguments it takes. This is the only part the model ever sees.
A tool is a function the harness can run, paired with a description that teaches the model when and how to ask for it.
What goes in the description
The model is choosing, in plain language, "which tool fits this situation and what do I pass it?" To make a good choice it needs three things:
- A name —
look_up_orders. Short, so the model can refer to it. - A description — one sentence in plain English: "Look up the most recent order for the current customer." This is what the model reads to decide whether this tool is the right one.
- The inputs — what arguments the tool needs, each with a type and its own
little description. For
look_up_ordersthat's a singlecustomerfield.
The model picks tools by reading these descriptions. Vague ones ("does order stuff") lead to the model calling the wrong tool or guessing arguments. Treat each tool description like instructions to a new teammate: clear, specific, and honest about what it does.
The shape of inputs: a schema
That "list of inputs" has a standard format called a JSON schema. Don't let
the name scare you — it's just a structured way of saying "this tool takes an
object with a customer field, which is a string, and it's required."
Why so formal? Because the model's answer has to be machine-readable. When the model asks for a tool, the harness needs to reliably pull out the arguments and pass them to the real function. A loose "call it with the customer" wouldn't be safe to run automatically; a schema makes the request predictable.
The real thing
Here's look_up_orders exactly as it lives in our agent-harness project —
the two halves side by side.
The function (what the harness runs):
def look_up_orders(customer: str) -> dict:
"""Pretend to look up a customer's most recent order in the database."""
return {
"order_number": "4521",
"item": "running shoes",
"status": "shipped",
"tracking_number": "1Z999",
}
(It returns fixed data here so the example runs with no real database. Swap the body for a real query and nothing else changes.)
The schema (what the model is told):
{
"name": "look_up_orders",
"description": "Look up the most recent order for the current customer.",
"input_schema": {
"type": "object",
"properties": {
"customer": {
"type": "string",
"description": "Identifier for the customer (e.g. their id or email).",
},
},
"required": ["customer"],
},
}
Read the schema top to bottom and it's just the three things from above: a
name, a description, and the inputs (an object with one required
string, customer). That's a whole tool.
How the two connect
When the model decides it wants this tool, it sends back a request that names
the tool and fills in the inputs — something like "call look_up_orders with
customer = "the current customer"." The harness then:
- reads the tool name from the request,
- looks up the matching function,
- calls it with the arguments the model supplied,
- and sends the return value back into the conversation.
In our code that "name → function" lookup is literally a dictionary:
TOOL_FUNCTIONS = {
"look_up_orders": look_up_orders,
"get_tracking_status": get_tracking_status,
}
The model says a name; the harness uses this table to find the real function. The schema is the menu the model orders from; this table is the kitchen.
What we deliberately skipped
- Wiring the schema into a real model call — how
tools=[...]gets passed to the SDK and how the model's request comes back. (Next lesson — we'll run the whole loop for real.) - Tool errors and validation — what happens when a tool fails or the model sends bad arguments. (Later.)
Recap
- A tool is two halves: the function the harness runs, and the schema that describes it to the model.
- The model only ever sees the schema — a name, a plain-English description, and an input schema for the arguments.
- The description does real work: it's how the model decides which tool to use, so write it clearly.
- The harness maps the model's chosen tool name back to the real function, runs it, and feeds the result into the conversation.
Next up: we'll plug these tools into a real model call and watch the full
harness loop run end to end — the actual shopbot.py.