
In this article
Let’s build the loop from Lesson 1 for real. Our agent gets two tools β a GST calculator and a currency converter β and decides itself when to use them.
Setup
pip install anthropic
# set your key once (Windows: setx ANTHROPIC_API_KEY "sk-ant-...")
export ANTHROPIC_API_KEY="sk-ant-..."
Describe the tools
A tool is a name, a description the model reads, and a JSON schema for its inputs. Good descriptions matter more than clever code β the model chooses tools by reading them.
tools = [
{"name": "gst",
"description": "Add GST to an amount. Use for any GST question.",
"input_schema": {"type": "object",
"properties": {"amount": {"type": "number"}, "rate": {"type": "number", "description": "percent, e.g. 18"}},
"required": ["amount", "rate"]}},
{"name": "convert",
"description": "Convert an amount between currencies using today's rate.",
"input_schema": {"type": "object",
"properties": {"amount": {"type": "number"}, "src": {"type": "string"}, "dst": {"type": "string"}},
"required": ["amount", "src", "dst"]}},
]
RATES = {("USD", "INR"): 95.8, ("EUR", "INR"): 107.1} # demo rates
def run_tool(name, args):
if name == "gst":
tax = round(args["amount"] * args["rate"] / 100, 2)
return f"GST {tax}, total {args['amount'] + tax}"
if name == "convert":
r = RATES.get((args["src"].upper(), args["dst"].upper()))
return f"{round(args['amount'] * r, 2)} {args['dst']}" if r else "rate not available"
return "unknown tool"
The loop
import anthropic
client = anthropic.Anthropic()
messages = [{"role": "user", "content": "I paid 1,200 USD for software. What is that in INR, and the total with 18% GST?"}]
while True:
response = client.messages.create(model="claude-opus-5-5", max_tokens=4000, tools=tools, messages=messages)
if response.stop_reason != "tool_use":
break
messages.append({"role": "assistant", "content": response.content})
results = []
for block in response.content:
if block.type == "tool_use":
print("β", block.name, block.input)
results.append({"type": "tool_result", "tool_use_id": block.id, "content": run_tool(block.name, block.input)})
messages.append({"role": "user", "content": results}) # all results in ONE message
print(next(b.text for b in response.content if b.type == "text"))
Run it and you will see the agent call convert, read the result, then call gst on the INR amount, and finally answer in plain English. You didn’t write that order β the model chose it.
Three rules that save hours
- Send every tool result back in one user message, each with the matching
tool_use_id. - Return errors as text (“rate not available”) rather than crashing β the model can recover.
- Cap the loop (e.g. 10 turns) so a confused agent can’t run forever. We add this properly in Lesson 6.
The Anthropic Python SDK also has a beta “tool runner” helper that writes this loop for you; writing it once by hand is the best way to understand what it does.
Stuck on a step? Ask a question and the AI answers using this article.