Lab Guide — Tool Use
Three exercises building toward a full multi-tool, multi-turn agent loop.
lab.pdf in Jupyter. Run the setup cell first — it loads your .env file and defines the three tool schemas. You will implement the functions marked with TODO.
📥 Demo Files
⬇ tool_demo.pyExercise 1 — Define Tools and execute_tool()
Implement execute_tool(tool_name, tool_input)
Three tools are pre-defined in the notebook as a TOOLS list:
- calculator — fields:
operation(add/subtract/multiply/divide),a,b - unit_converter — fields:
value,from_unit,to_unit - string_tools — fields:
operation(upper/lower/reverse/count_words),text
Your task is to implement execute_tool(tool_name: str, tool_input: dict) -> str that dispatches to the correct logic for each tool and returns a string result.
Conversion factors to support
| From | To | Factor |
|---|---|---|
| km | miles | 0.621371 |
| miles | km | 1.60934 |
| kg | lb | 2.20462 |
| lb | kg | 0.453592 |
| celsius | fahrenheit | (C * 9/5) + 32 |
| fahrenheit | celsius | (F - 32) * 5/9 |
Test your implementation
print(execute_tool("calculator", {"operation": "multiply", "a": 17, "b": 843}))
# Expected: "14331.0" (or similar)
print(execute_tool("unit_converter", {"value": 100, "from_unit": "km", "to_unit": "miles"}))
# Expected: "62.1371 miles"
print(execute_tool("string_tools", {"operation": "reverse", "text": "Hello, World!"}))
# Expected: "!dlroW ,olleH"
Checkpoint: All three tool calls return correct string results without raising exceptions.
Exercise 2 — The Full Tool Loop
Implement run_with_tools(user_message, tools, max_iterations=10)
This function sends a user message to Claude along with the tools list, handles all tool calls, and returns Claude's final text response.
Requirements
- Initialise
messages = [{"role": "user", "content": user_message}] - Loop until
stop_reason == "end_turn"ormax_iterationsis reached - On
stop_reason == "tool_use": extract alltool_useblocks, callexecute_tool()for each, buildtool_resultentries, append assistant message then user results - On
stop_reason == "end_turn": return the text from the firstTextBlockinresponse.content - If max iterations reached: return a fallback string
Test your implementation
answer = run_with_tools(
"What is the square root of 144 multiplied by the number of words in 'the quick brown fox'?",
TOOLS
)
print(answer)
# Claude should call calculator (sqrt not available, but it may use multiply)
# and string_tools (count_words). Exact answer: sqrt(144)=12, words=4, 12*4=48
Checkpoint: Claude calls tools and returns a coherent final answer that incorporates the tool results.
Exercise 3 — Parallel Tool Calls
Verify parallel tool execution
Send a query that naturally requires two independent tool calls and verify they are returned in the same round.
query = (
"Convert 100 USD to EUR (use 1 USD = 0.92 EUR, so multiply 100 by 0.92) "
"AND calculate 15% tip on $47.50 (multiply 47.50 by 0.15). "
"Give me both results."
)
# Track tool calls per round
import anthropic
client = anthropic.Anthropic()
messages = [{"role": "user", "content": query}]
round_num = 0
while True:
response = client.messages.create(
model="claude-opus-4-5", tools=TOOLS, messages=messages, max_tokens=512
)
tool_calls_this_round = [b for b in response.content if b.type == "tool_use"]
print(f"Round {round_num}: stop_reason={response.stop_reason}, "
f"tool_calls={len(tool_calls_this_round)}")
for tc in tool_calls_this_round:
print(f" -> {tc.name}({tc.input})")
if response.stop_reason == "end_turn":
break
# ... execute tools and continue ...
round_num += 1
What to observe
- Ideally Round 0 shows 2 tool calls — Claude parallelised them
- If you see 2 separate rounds with 1 call each, adjust your query to make the independence more explicit
- Print Claude's final answer and verify both values appear
Checkpoint: You can observe (and document in a comment) whether Claude used parallel or sequential calls for this query.