Tool Calling / Function Calling
Theme
Palette
Download
Caption (tap to copy)
📖 Deep dive (full written explanation)
This is the hands-on post, and the cover promises the full tool-calling round-trip in real, runnable Python. After three posts of concept and mechanics, the reader wants to see the model actually emit a call, watch their own code execute it, and see the result flow back into a grounded answer.
The post is structured to run top to bottom: install, define a real function, describe it with a schema, send the request and read the call, execute it and append the result by id, make the second call for the answer, and finally show the same flow in the Anthropic SDK. The payoff to watch for is the model answering from real data instead of a guess.
The install slide is deliberately minimal: a single dependency. Using the OpenAI SDK directly keeps the focus on the round-trip itself rather than on framework abstractions, and tool calling is a first-class feature of the API, so nothing else is needed.
Keeping the footprint to one package means the reader can run the entire post in a fresh environment with just an API key. The Anthropic version shown later demonstrates that the same pattern is provider-agnostic, but the core lesson stands on its own with this one install.
This slide defines the tool as an ordinary Python function, reinforcing the post-1 point that a tool is just a function the model is allowed to request. get_weather takes a city and an optional units argument, looks up canned data so the example runs offline, and converts to Fahrenheit when asked. In real life the comment notes you'd call a weather API here.
Writing it as a normal function with a default argument and a not-found case models good practice: the function handles its own edge cases rather than assuming the inputs are valid. That habit becomes critical in post 5, where validating tool arguments is one of the key mistakes to avoid.
This slide describes the function to the model using the JSON-schema format the API expects. The tool gets a name, a description, and a parameters object declaring 'city' as a string and 'units' as a string constrained to an enum of celsius or fahrenheit, with city marked required. The enum is worth noting: it tells the model exactly which values are valid, preventing it from inventing a third unit.
This schema is the only thing the model sees about the tool — not the function body. That's why the description and the typed, constrained parameters matter so much, and it's the concrete counterpart to post 3's explanation of how schemas drive the model's choice.
This slide starts the round-trip: create the client, seed the messages with a user question that needs live data and a unit conversion, and make the first model call with the tools attached. The model responds not with prose but with a tool_call, which we append to the messages to preserve the conversation, then read the call's function name.
The critical habit shown here is appending the model's message to the history before doing anything else. The conversation must stay intact — the model's tool call, your result, and the final answer all live in the same growing message list. Printing the function name confirms the model chose get_weather, exactly as post 3 described.
This slide is the execution half, and it's where the model's decision becomes a real function call. The arguments arrive as a JSON string, so we parse them into a Python dict — note the model correctly chose units 'fahrenheit' because the question said 'in F.' We call the real get_weather with those arguments, then append a message with role 'tool' carrying the matching tool_call_id and the result.
That tool_call_id linkage is the mechanism from post 3 made concrete: it tells the model exactly which call this result answers. Once this tool message is appended, the conversation is complete enough for the model to finish on the next call.
This slide makes the second model call — the one that turns the tool result into a human answer. We send the same messages list, now containing the user question, the model's tool call, and the tool result, back to the model. With real data in hand, the model writes a natural-language answer: roughly 72 degrees and clear in Tokyo.
The comment drives home the whole point of the day: the model now answers from real data, not a guess. This is the second of the two trips post 3 described, and seeing the grounded sentence come out the other end is the concrete payoff for all the machinery.
The pipeline diagram summarizes the five moves the code just performed so the reader can map syntax back to concept: ask, call, run, return, answer. Placing it after the code lets it act as a recap rather than an introduction — the reader has now seen the user question, the emitted call, the execution, the result returned by id, and the grounded reply.
The 'Return' stage carrying 'result by id' deliberately echoes the tool_call_id thread from post 3, reinforcing that the id is what stitches the result to its call. The diagram lets the reader step back and confirm how the pieces connect end to end before moving on.
This slide shows the same round-trip in the Anthropic SDK so the reader sees the pattern is provider-agnostic. The shapes differ slightly — Anthropic uses 'input_schema' instead of nesting under 'function,' and signals a tool call via stop_reason 'tool_use' — but the loop is identical: the model emits a call, you run it, you return the result, you ask again.
Reusing the same parameters object from the OpenAI schema makes the point vivid: the underlying contract is the same JSON Schema, just wrapped differently. The lesson is reassurance and portability at once — learn the round-trip once and you can apply it across providers, because they all implement the same primitive.
This tips slide walks the reader through reading the actual run, because seeing the trace cements how tool calling behaves. On call one the model returns tool_calls and no text. You parse the arguments, run get_weather, and get a result. You append a role 'tool' message with the matching id. On call two the model has the data and writes the answer.
Learning to read this trace is also the foundation for debugging. When something goes wrong, you'll inspect exactly this sequence — which call the model made, what arguments it passed, what your function returned — which is why post 5 treats handling each step carefully as the core of shipping tool calling safely.
The CTA hands off to the final post of the day. The reader can now wire the full tool-calling round-trip themselves; the remaining risk is the subtle, expensive ways it breaks once it leaves the demo.
Day 78's teaser flags the mistakes post: vague descriptions that make the model pick the wrong tool, trusting arguments you never validated, forgetting to return a result, exposing dangerous ungated actions, and crashing when the model returns no tool call at all. These are the failure modes that bite even when the happy-path code runs fine.