Tools and function calling from scratch
Already hand-writing tool schemas and a dispatch table? Skip to the next lesson.
Last lesson the tool was handed to you. Now you write your own, and you find out what a "tool" really is: a normal function, plus a description the model can read.
The idea
You give the model a menu. Each item says what the tool is called, what it does, and what information it needs. That description is written in JSON, a plain way of writing structured data.
The model reads the menu and may reply with an order: this tool, these values. It does not run anything. Your code looks at the name, finds the matching function, and runs it. That step is called dispatch, and it is where most tool bugs live.
What to write
The loop from Module 2 is already there. You are filling in four things:
TOOLS, the menu, describingcalculatorandget_weather- The two Python functions that actually do the work
execute_tool, which matches a name to a function- Pass
tools=TOOLSintochatso the model can see the menu
Your tool is the risky part
The model chooses the arguments. Your code runs them. So treat every argument as text a stranger sent you.
The lazy calculator is eval(expression). It works first time and it hands
anyone who can steer the model a way to run their own code on your machine.
Wrapping it in {"__builtins__": {}} does not fix that. The worked solution
reads the expression apart and only allows the pieces it chose to allow.
The same rule shows up everywhere later. Check the input, do not trust it.
The point
The model only asks. You run the function. It never touches your system, and what it is allowed to do is a decision you make in code.
Break it on purpose
Delete the get_weather branch from execute_tool and run again. The check
tells you a tool was requested that nothing could handle, which is exactly
what happens in production when someone adds a tool to the menu and forgets
the dispatch.
Check yourself
Answer out loud first. Reading the answer without trying is where the learning leaks out.
1. Who executes the tool, the model or your code?
2. Why pass tools=TOOLS if the fixture already knows the calls?
3. What must you append before the second chat when there are two tool calls?