Tool callingAgentsLive demo

Good Tools, Bad Tools

Two agents. The same question. The same data, the same model, the same functions underneath. The only thing that differs is how those tools are described to the model.

In the recorded runs, one of them tells an operations officer not to worry about a flight that is sitting under fog. It makes no errors doing it.

Press the button and watch their execution diverge.

One question, two agents. The only difference is how their tools are described. Each run takes about ten seconds.

Watch which airport each one checks the weather for.

Lazy tool description

1

Gets weather info.

The model reads this alongside the tool name and parameter schema. It never sees the code behind them.

2

Waiting for a run.

3

Waiting for a run.

Careful tool description

1

Returns current visibility in kilometres, wind speed, temperature, and conditions at ONE airport, identified by its 3-letter IATA code (e.g. 'SFO', 'JFK', 'ORD'). Use this whenever a question depends on flying conditions - in particular, whether fog, wind, or low visibility at the ORIGIN airport could delay a departure. A flight can show as on time with a clean aircraft and still be at risk because of weather, so check this before judging whether a departure will hold. Requires an airport code - it cannot resolve a flight number or a city name. If you only have a flight number, call get_flight_status first to get the origin airport.

The model reads this alongside the tool name and parameter schema. It never sees the code behind them.

2

Waiting for a run.

3

Waiting for a run.

What just happened

A tool is something you already built

There is nothing about a tool that is an AI concept. A tool wraps whatever you point it at: an API call, a database query, an MCP operation, a step in a workflow. In this lab they happen to be six ordinary Python functions that read flight, gate, passenger, maintenance, and weather records. They worked before any model existed and they would keep working if you deleted the model tomorrow.

What turns that code into a tool is the declaration you write alongside it: a name, a description, and a schema for the parameters. The model never sees the code. It sees the declaration and nothing else. This is not documentation. It is the interface.

The model does not run your code

When an agent calls a tool, it executes nothing. It returns a small block of JSON containing a tool name and some arguments. That is the second step you watched appear above.

Your own program reads that request and decides what to do with it. Run it. Refuse it. Log it. Stop and ask a human first. Every action an agent has ever taken was a line of somebody's code choosing to comply.

So the declaration decides the outcome

The model chooses the tool and builds the arguments from the declaration alone. If nothing in it says that the airport must be a three letter code, you get Chicago. If nothing says that departure risk lives at the origin, you get the weather at the destination.

The name and the schema both carry signal, but the prose is where most of it lives. A name can tell the model what a tool does. It can never tell the model what the argument should look like. That is where these agents go wrong.

Why this matters more than it looks

The lazy agent does not fail loudly. That is the problem with it.

It calls sensible tools in a sensible order. Every call returns ok. It produces a confident, well organised answer. Nothing in the trace looks wrong, because nothing went wrong. The tool was simply asked about the wrong place.

No error rate dashboard catches this. No retry fires. A human reading the output cannot tell the two apart, because both answers read like competent work.

Every tool in this lab only reads data, so the worst case here is a misleading answer. When the same agent cancels a booking or reassigns a gate, the distance between succeeded and was correct stops being a demonstration and becomes an incident.

The least glamorous artefact in an agentic system is the sentence describing what a tool does. It is not a comment. It is the contract.

If you want the mechanism in more detail, including the exact JSON that crosses the wire, read the guide.

Every run above is live. It is made when you press the button. The supporting figures quoted underneath the results come from 36 runs recorded earlier against the same code. All of them are published and none were selected, in the repository, alongside the five scenarios that were expected to break and did not.