In the previous post, we taught a model to read our documents. It could search a pile of files and answer from them, which was very useful.
But I still couldn't ask it if it was going to rain, check a live price or even what today's date is.
Because as we discussed this earlier, a foundation model on its own is frozen in time. Its knowledge stops at its training cutoff and it's locked in a box. No window to the outside world.
This post is about that window, tool calling. We give the model one tool and watch it reach out for live data, add a second tool, then get into the two very different ways an app can hand a model a fact it doesn't have. One of those two is the reason one of the AI assistant you've used can tell you today's date.
All the code is in my GitHub repo, in the ep07-tool-calling folder. Three tiny scripts, one idea each: one tool, two tools, and the injection trick.
When I first heard "the model calls a tool," I pictured the model reaching out and running code by itself.
The model does not run anything because it really can't. It is still just reading a prompt and producing text.
What it produces is a structured request that says "I'd like to call this tool, with these inputs."
It just hands you a note, that your code reads and then runs the actual tool. It then hands the result back to the model for further actions, either to tell you the answer or call another tool.
This is run every single time: You send the model your question, plus a description of the tools it's allowed to use. The model decides: can I answer this myself, or do I need a tool? If it needs one, it replies with a structured request. A little package that says call getweather, city is Toronto. Your code sees that request and runs the real function, the one that actually hits the weather API. You send the result back to the model. Now it writes the final answer, grounded in real data it could never have known on its own.
I'm using Amazon Bedrock again, same as the whole series, calling a Claude Model through the Converse API. Converse has a spot built in for tools, called toolConfig.
Describing a tool to the model is three parts: a name, a plain-English description, and an input schema for the arguments.
This description and schema are the only things the model reads to decide when and how to use this tool.
This is normal code. No AI in it. It hits Open-Meteo, a free weather API with no key required.
I never told it which tool to use, and I never told it the argument. It read one question and worked out both. But nothing has run yet.
So my code runs getweather("Toronto"), hits the API, and gets back the real conditions. Then I package that up and send it back to the model as a toolResult:
With a single tool, the whole thing is a straight line. Send, get the request, run it, send the result back, get the answer. Top to bottom, no loop:
One tool, one round trip. I know exactly what's going to happen, so I can just write it out.
