# Tool calling is a round trip

**AI Engineering Visualised** · Agents · Film, 1:41 · Updated 2026-09-11

Tool calling is the round trip where a model writes a structured request for a function, the application runs it, and the result goes back into the model's context before it answers. The model never executes anything itself.

> Follow one customer question through a real tool call: the model writes a request and runs nothing, your code looks the order up, and only when the result goes back does the model answer. Four recorded exchanges show the round trip, a lost result, an error, and a refused cancellation.

Watch the film: https://understandingdata.com/ai-engineering-visualised/tool-calling-round-trip/
Video file: https://assets.understandingdata.com/viz/tool-calling-round-trip/video/film.mp4?v=a114ae9566

A language model reads text and writes text. It cannot look up an order, charge a card or send an email. Tool calling is how an application lets it ask for those things: the request lists the tools the application offers, each with a name, a description and a JSON schema for its arguments, and the model may answer with a request to call one instead of with prose.

Every exchange in the film is a real recording. The questions were sent to `google/gemini-3.8-flash` through OpenRouter at temperature 0 on 11 September 2026, with a system prompt for an online shop and two tools, `get_order_status` and `cancel_order`. The handler in the film's code editor is the real code that ran: a regular expression that checks the order id, a one-row order table, and a guard that refuses to cancel anything that is not still being processed.

The traced request is the whole mechanism. The customer asks where order A-1042 is. The model's first turn contains no text at all, only a tool call naming `get_order_status` with the argument `{"order_id": "A-1042"}`. Your code checks the id, reads the table, and returns the row: shipped with DPD on 9 September, arriving 14 September. That result is sent back to the model as a tool message, in a second request that repeats the conversation, and only then does the model write the answer the customer sees.

The part people miss is where the model's role ends. It writes a request, and nothing more. Everything that happens to that request happens in your code: whether the arguments parse, whether the id is well formed, whether this customer may touch this order, and whether the action runs at all. When the model asked to cancel A-1042, the order had already shipped, so the application refused and said why, and the model passed the refusal on. Validation and permissions belong on your side, because the model cannot enforce them.

The failure mode is forgetting that the trip has two legs. Stop after the first turn and the customer receives an empty message, because the text of a tool-calling turn is usually empty. Worse, send the result back empty, as a bug in the plumbing might, and the model does not say that something went wrong. In three runs of three it told the customer it could not find order A-1042, an order that had shipped two days earlier. A missing result reads to the model like a negative result.

Errors should travel the same road as data. When the customer typed A-1402, the lookup failed, and the application returned a structured error rather than nothing. The model turned it into a polite request to double-check the order number, which is the right answer, because the error said exactly what happened.

The takeaway is a division of labour. The model proposes a call and phrases the reply. Your code decides whether the call is valid and allowed, runs it, and always sends a result back, including when the result is an error.

## The maths

### One tool call is two model calls

```latex
\text{reply}_1 = M(\text{system},\, q,\, \text{tools}) = \{\, \text{call}(f, a),\ \text{text} = \varnothing \,\}, \qquad \text{reply}_2 = M(\text{system},\, q,\, \text{call},\, r)
```

The first call returns a tool call and no text: in all four recordings the model sent an empty reply alongside the request. The answer only exists after a second call that carries the call and its result r back in the messages.

### Your code sits in the middle

```latex
r = f_{\text{app}}(a) \quad \text{only if} \quad \text{valid}(a) \wedge \text{allowed}(f, a)
```

The model picks f and writes the arguments a as JSON. It never runs f. Your application parses a, checks it, decides whether the call is allowed, runs the function, and chooses what r says. The cancel request here was refused by one line of application code.

### An empty result is still an answer

```latex
r = \text{""} \;\Rightarrow\; \text{reply}_2 \approx \text{"not found"}
```

When the tool message came back empty, the model told the customer it could not find an order that had shipped two days earlier, in three runs of three. It reads a missing result as a negative result, so a bug in the plumbing becomes a confident wrong answer.

### Errors are data

```latex
r = \{\, \text{error}: \text{order\_not\_found} \,\} \;\Rightarrow\; \text{reply}_2 = \text{"please double-check the order id"}
```

Returning a structured error, rather than throwing or returning nothing, lets the model explain the failure in terms the customer understands. The typo exchange is exactly that: A-1402 does not exist, your code said so, and the reply asks the customer to check the number.

## Transcript

### 0:00 Models cannot act

A language model can only write text, so when a customer asks where order A-1042 is and when it will arrive, the model has no way to look it up by itself.

### 0:12 The request

So instead of answering, it writes a tool call that fits the schema you gave it: a small card naming get_order_status with the order id A-1042, and its reply text is empty.

### 0:26 Your code runs it

The card crosses to your application, and it is your code, not the model, that parses the arguments string, checks the id, reads the order table, and gets back shipped with DPD, arriving 14 September.

### 0:39 Back to the model

Only when that result goes back into the conversation as a tool message with the same call id does the model write the answer: your order A-1042 has shipped via DPD and should arrive on 14 September.

### 0:53 Stopped halfway

Here is the first failure mode: stop after the first turn, and the customer gets an empty reply, because that turn was only a tool call.

### 1:03 Result lost

Worse, send the result back empty, and the model does not report an error: it tells the customer it could not find an order that shipped two days ago.

### 1:14 Errors travel too

Errors travel the same road: a customer types A-1402, your code returns order not found, and the model turns that into a request to double-check the number, which is exactly right.

### 1:26 Who decides

Cancelling shows who is in charge: the model asks to cancel A-1042, your code refuses because it has shipped, so validate every call on your side and always send the result back.

## Related terms

- [Tool](https://understandingdata.com/ai-coding-dictionary/tool/)
- [Tool call](https://understandingdata.com/ai-coding-dictionary/tool-call/)
- [Tool result](https://understandingdata.com/ai-coding-dictionary/tool-result/)
- [Harness](https://understandingdata.com/ai-coding-dictionary/harness/)
- [Turn](https://understandingdata.com/ai-coding-dictionary/turn/)

## More visualisations

- [The anatomy of a good prompt](https://understandingdata.com/ai-engineering-visualised/anatomy-of-a-prompt/)
- [Few-shot prompting: examples define the pattern](https://understandingdata.com/ai-engineering-visualised/few-shot-prompting/)
- [What are tokens?](https://understandingdata.com/ai-engineering-visualised/what-are-tokens/)

---

Source: https://understandingdata.com/ai-engineering-visualised/tool-calling-round-trip/
From AI Engineering Visualised by James Phoenix, Understanding Data. Published 2026-09-11.
