Build a Tool-Calling AI Agent
Give Claude tools to call and build an interactive AI agent in Python.
Introduction
30 Second Summary
AI assistants like ChatGPT and Claude can answer questions all day long. But the real magic happens when they can take action on their own, like checking the weather or running a calculation without you writing the logic for every possible question.
In this project, you will build a Python AI agent that decides which tool to call based on your question. You will write the code, define the tools, and watch Claude handle the rest.
What You'll Build
You will build an interactive command-line AI agent powered by Claude Haiku 4.5 that autonomously calls Python functions to check weather, perform math calculations, and tell the time.
By the end of this project, you'll have:
- A working Python script that communicates with Claude's API and handles the tool-calling round trip.
- Three custom tool definitions using JSON Schema that Claude can call on its own.
- A reusable agent loop pattern that is the foundation of every production AI agent.
- Secret Mission: Add conversation memory so your agent remembers what you discussed earlier in the chat.
Are there any prerequisites?
Nope. This project assumes basic computer literacy only. Python syntax is introduced as you go, so you do not need prior programming experience.
Before We Start
To build your AI agent, you need a few things in place first.
In this step, you will create a project folder, install the Anthropic Python SDK, and generate an API key so your code can talk to Claude.
In this step, get ready to:
- Create your project folder with two Python files.
- Install the Anthropic Python SDK.
- Generate and set your API key as an environment variable.
Create your project folder
You need a dedicated folder to keep your agent files organized.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type VS Code and press Enter to open it.
- Open a terminal by clicking Terminal in the top menu bar.
- Click New Terminal.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir tool-calling-agent
cd tool-calling-agent
- Now open this folder as your VS Code workspace by clicking File in the top menu bar.
- Click Open Folder.
- Navigate to your Desktop, select the tool-calling-agent folder, and click Open (or Select Folder on Windows).
VS Code reloads with your project folder as the active workspace.
- Open a fresh terminal in your project folder by clicking Terminal in the top menu bar.
- Click New Terminal.
- Create your two project files by running this command:
macOS
touch hello_claude.py agent.py
Windows
New-Item hello_claude.py, agent.py
- Confirm the files exist by running:
ls
You should see hello_claude.py and agent.py in the output.
Don't see both files?
Make sure you ran the file creation command in the correct terminal. The terminal prompt should show tool-calling-agent as the current directory.
If you see a different folder, run cd ~/Desktop/tool-calling-agent and try the file creation command again.
Help me find my project folder in the VS Code terminal.
Install the Anthropic Python SDK
The Anthropic Python SDK is the library that lets your Python code communicate with Claude's API.
- Install the SDK by running this command in your terminal:
pip install anthropic
What does this command do?
`pip` is Python's package manager. This command downloads the anthropic library from the internet and installs it on your machine so you can import it in your scripts.
- Verify the installation succeeded by running:
python -c "import anthropic; print(anthropic.__version__)"
You should see a version number like 0.116.0 printed in your terminal.
Seeing an error instead of a version number?
If you see a message about the command not being found, try replacing python with python3 in the command. Some systems use python3 as the default.
If you see ModuleNotFoundError, the install may not have completed. Run pip install anthropic again and check for any error messages during installation.
Help me troubleshoot my Anthropic SDK installation.
Generate and set your API key
Your code needs an API key to authenticate with Claude. The key proves to Anthropic's servers that your requests are authorized.
- Open your browser and go to console.anthropic.com.
- Sign in or create an account if you do not have one yet.
- Click API Keys in the left sidebar.
- Click Create Key.
- Give the key a name like tool-calling-agent and click Create Key.
- Copy the key immediately. You will not be able to see it again after closing this dialog.
Why can I only see the key once?
Anthropic only shows your full API key at the moment of creation for security reasons. After you close the dialog, only a masked version is visible. Copy it now and paste it into your terminal in the next step.
- Back in your VS Code terminal, set the API key as an environment variable by running the command for your system:
macOS
export ANTHROPIC_API_KEY="your-key-here"
Replace your-key-here with the key you just copied. Keep the quotes.
Windows
If your terminal shows PS at the start of the prompt (PowerShell):
$env:ANTHROPIC_API_KEY = "your-key-here"
If your terminal shows > without PS (Command Prompt):
set ANTHROPIC_API_KEY=your-key-here
Replace your-key-here with the key you just copied.
Keep this terminal open
The environment variable only lives in the current terminal session. If you close this terminal, you will need to set it again. Keep this terminal open for the rest of the project.
Not sure if the key was set correctly?
You can verify the variable is set by running echo $ANTHROPIC_API_KEY (macOS) or echo $env:ANTHROPIC_API_KEY (PowerShell). You should see your key printed back.
If it prints nothing, re-run the export or set command and make sure there are no extra spaces around the = sign.
Help me set my Anthropic API key in the terminal.
Your environment is ready. Next up, you will write your first API call to Claude and see it respond.
Make Your First API Call
Your environment is set up and ready to go. Now it's time to talk to Claude directly from your Python code.
In this step, you'll write a script that sends a message to Claude and prints the response. This is the foundation every AI application starts with.
In this step, get ready to:
- Send your first message to Claude using the Anthropic Python SDK.
- Run your script and see Claude's response in the terminal.
- Explore the response metadata to understand what the API returns.
Write a basic message to Claude
The Anthropic Python SDK handles all the complexity of authenticating and communicating with Claude's API. You just need to create a client, craft a message, and send it.
- Open hello_claude.py in VS Code by clicking on it in the Explorer sidebar.
- Add the following code to send a message to Claude and print the response:
import anthropic
# Create a client (automatically reads ANTHROPIC_API_KEY from your environment)
client = anthropic.Anthropic()
# Send a message to Claude Haiku 4.5
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello! What can you help me with?"}],
)
# Print Claude's response text
print(response.content[0].text)
What does this code do?
- import anthropic loads the SDK you installed in the previous step.
- anthropic.Anthropic() creates a client that automatically reads your ANTHROPIC_API_KEY environment variable.
- client.messages.create() sends your message to the claude-haiku-4-5 model with a maximum response length of 1024 tokens.
- response.content[0].text extracts Claude's text reply from the response object. The response contains a list of content blocks, and you access the text of the first one.
- Save hello_claude.py.
Run and observe the response
Time to see Claude respond. Your script will send the message over the internet to Anthropic's servers and print back whatever Claude says.
- Run your script by entering this command in the terminal:
python hello_claude.py
You should see a paragraph of text from Claude describing what it can help with. The exact wording will vary each time you run it.
What is happening behind the scenes?
Your script sends an HTTP request to Anthropic's API with your message. Claude processes it and streams back a response object containing the generated text. The SDK handles all the networking for you.
Seeing an authentication error?
- Make sure you are running the command in the same terminal session where you set ANTHROPIC_API_KEY. If you opened a new terminal, the variable is gone and you need to set it again.
- Double-check that your API key is correct by visiting console.anthropic.com and comparing it to the key you pasted.
- Make sure your Anthropic account has credits available. New accounts receive a small free credit balance.
Still stuck? Help me debug my Anthropic API authentication error.
Examine the response object
The API returns more than just text. It includes metadata about which model answered, why it stopped generating, and how many tokens it used. Understanding this metadata helps you debug and optimize your AI applications.
- In hello_claude.py, find the line that says print(response.content[0].text).
- Add these three lines below it to print the response metadata:
print(f"\nModel: {response.model}")
print(f"Stop reason: {response.stop_reason}")
print(f"Usage: {response.usage.input_tokens} input, {response.usage.output_tokens} output tokens")
What does this metadata tell you?
- response.model confirms which Claude model handled your request.
- response.stop_reason tells you why Claude stopped generating. A value of end_turn means Claude finished its response naturally. Later, you will see tool_use when Claude wants to call a tool instead.
- response.usage shows how many tokens your message used (input) and how many Claude generated (output). This is what determines cost.
- Save hello_claude.py.
- Run the script again:
python hello_claude.py
You should now see Claude's response text followed by three lines of metadata. The stop reason should show end_turn.
Not seeing the metadata lines?
- Make sure the three new print lines are outside and below the client.messages.create() call, not inside it.
- Check for typos in the f-string curly braces. Each {response.model} must have matching opening and closing braces.
Need help? Help me fix my print statements in hello_claude.py.
✔️ Awesome, I've got everything!
Great. Double check you've saved your file before moving on.
ⓧ I'd like to double check the full code
Here is the complete hello_claude.py file. Make sure yours matches:
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello! What can you help me with?"}],
)
print(response.content[0].text)
print(f"\nModel: {response.model}")
print(f"Stop reason: {response.stop_reason}")
print(f"Usage: {response.usage.input_tokens} input, {response.usage.output_tokens} output tokens")
You've just talked to Claude from your own code. Next up, you'll give Claude the ability to call tools and take actions on its own.
Add Your First Tool
In the previous step, you called Claude's API and got a plain text response back. The response had a stop_reason of "end_turn", meaning Claude decided on its own that it was done talking.
Now you'll give Claude something it has never had before: the ability to take action. Instead of only generating text, Claude will call a function in your code to look up weather data. This is the core pattern behind every production AI agent in 2026.
In this step, get ready to:
- Define a weather tool schema that tells Claude what the tool does and what inputs it needs.
- Write a Python function that returns mock weather data.
- Handle the full tool-calling round trip between Claude and your code.
Define a tool schema and handler function
Tool-calling works by sending Claude a description of each tool alongside your message. Claude reads the description, decides whether to use the tool, and responds with a structured request that your code can parse.
The description follows JSON Schema format. It tells Claude the tool's name, what it does, and what inputs it expects.
- In VS Code, open agent.py and add the following code to set up the SDK and define your first tool:
import anthropic
from datetime import datetime
client = anthropic.Anthropic()
tools = [
{
"name": "get_weather",
"description": "Get the current weather for a given location",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
}
},
"required": ["location"],
},
},
]
What does this code do?
client = anthropic.Anthropic() creates a connection to Claude's API, reading your ANTHROPIC_API_KEY from the environment automatically.
The tools list contains one tool definition. Each tool has a name (how Claude refers to it), a description (so Claude knows when to use it), and an input_schema (what inputs the tool accepts).
The datetime import isn't used yet. You'll need it in the next step when you add a time tool.
- You won't see a change yet. The next chunk adds the function this tool will call.
The tool schema tells Claude what it can do, but you also need a Python function that actually does the work. When Claude asks to use the weather tool, your code runs this function and sends the result back.
- Add the following function below the tools list in agent.py:
def get_weather(location):
# Mock weather data for a few cities
weather_data = {
"New York, NY": {"temp": 72, "condition": "Partly cloudy"},
"San Francisco, CA": {"temp": 58, "condition": "Foggy"},
"London, UK": {"temp": 61, "condition": "Rainy"},
}
# Return matching city data, or default sunny weather
data = weather_data.get(location, {"temp": 68, "condition": "Sunny"})
return f"The weather in {location} is {data['condition']} with a temperature of {data['temp']}F."
What does this function do?
This function uses mock data instead of a real weather API. It has hardcoded weather for three cities. If Claude asks about a city not in the dictionary, it returns a default (68F and Sunny).
The important thing here is the pattern, not the data source. In production, you'd swap this mock for a real API call. The tool-calling round trip works identically either way.
- You won't see a change yet. The next substep adds the code that sends this tool to Claude.
Send a tool-enabled request and handle the tool call
Now you'll wire everything together. Your code will send a message to Claude with the tool definition, check if Claude wants to use the tool, execute the function, and send the result back for a final answer.
- Add the following code at the bottom of agent.py, below the get_weather function:
messages = [{"role": "user", "content": "What's the weather in San Francisco?"}]
# Send the message to Claude with our tool definitions
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=1024,
tools=tools,
messages=messages,
)
# Check if Claude wants to use a tool
if response.stop_reason == "tool_use":
# Add Claude's response (with tool request) to the conversation
messages.append({"role": "assistant", "content": response.content})
tool_results = []
for block in response.content:
if block.type == "tool_use":
# Execute our weather function with Claude's chosen input
result = get_weather(block.input["location"])
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": result,
})
# Send the tool result back to Claude for a final answer
messages.append({"role": "user", "content": tool_results})
final_response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=1024,
tools=tools,
messages=messages,
)
print(final_response.content[0].text)
What does this code do?
This is the full tool-calling round trip in action:
- You send a user message ("What's the weather in San Francisco?") along with the tools list so Claude knows what's available.
- Claude responds with stop_reason of "tool_use" instead of "end_turn". This means Claude wants to call a tool rather than reply with text.
- Your code finds the tool_use block, runs get_weather() with the location Claude chose, and packages the result.
- You send the result back as a tool_result message. Claude reads it and writes a natural-language answer.
- Save agent.py.
Run and observe the full round trip
Your script is ready to run. When you execute it, Claude will read the tool definitions, decide to call get_weather, and your code will handle the entire exchange.
- Run your agent script by entering this command in your terminal:
python agent.py
You should see Claude's final response incorporating the weather data. It will mention that San Francisco is foggy with a temperature of 58F, phrased in Claude's own words.
What just happened behind the scenes?
Your script made two API calls to Claude, not one. The first call sent your question and the tool definitions. Claude responded with a tool_use block (not text) asking to call get_weather with "San Francisco, CA" as the location.
Your code executed the function locally, got the weather string, and sent it back as a tool_result. The second API call gave Claude that result, and Claude turned it into a conversational answer. This two-step loop is the foundation of every AI agent.
Seeing an error instead of a weather response?
- If you see an authentication error, your ANTHROPIC_API_KEY environment variable is not set in this terminal session. Re-run the export command from Step 1.
- If you see a NameError, make sure the get_weather function is defined above the code that calls it. Python reads files top to bottom.
- If Claude responds with text but no weather data, check that you passed the tools list in your client.messages.create() call. Without it, Claude does not know any tools exist.
Still stuck? Help me debug my agent.py tool-calling script.
✔️ Awesome, I've got everything!
Great. Double check you've saved agent.py and move on.
ⓧ I'd like to double check the full code
Here is the complete agent.py up to this point:
import anthropic
from datetime import datetime
client = anthropic.Anthropic()
tools = [
{
"name": "get_weather",
"description": "Get the current weather for a given location",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
}
},
"required": ["location"],
},
},
]
def get_weather(location):
weather_data = {
"New York, NY": {"temp": 72, "condition": "Partly cloudy"},
"San Francisco, CA": {"temp": 58, "condition": "Foggy"},
"London, UK": {"temp": 61, "condition": "Rainy"},
}
data = weather_data.get(location, {"temp": 68, "condition": "Sunny"})
return f"The weather in {location} is {data['condition']} with a temperature of {data['temp']}F."
messages = [{"role": "user", "content": "What's the weather in San Francisco?"}]
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=1024,
tools=tools,
messages=messages,
)
if response.stop_reason == "tool_use":
messages.append({"role": "assistant", "content": response.content})
tool_results = []
for block in response.content:
if block.type == "tool_use":
result = get_weather(block.input["location"])
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": result,
})
messages.append({"role": "user", "content": tool_results})
final_response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=1024,
tools=tools,
messages=messages,
)
print(final_response.content[0].text)
You just built the core pattern behind every AI agent: Claude decides to call a tool, your code executes it, and the result feeds back into a final answer. Next up, you'll expand this into a multi-tool agent that handles weather, math, and time queries in an interactive loop.
Build the Multi-Tool Agent Loop
In the previous step, you proved that the tool-calling round trip works. Claude received a question, asked for a tool, your code executed it, and Claude used the result to answer. But that code only handles one tool and one hardcoded question.
Now you will turn that single-shot script into a real agent. You will give Claude multiple tools to choose from, wrap the logic in a loop that keeps running until Claude is done, and let the user type any question they want.
In this step, get ready to:
- Add calculator and time tools so Claude can pick the right one for any question.
- Build an agentic loop that keeps calling tools until Claude has a final answer.
- Add interactive input so you can chat with your agent in the terminal.
Add the calculator and time tool definitions
Right now Claude only knows about one tool: get_weather. For the agent to be useful, it needs options. You will add two more tools so Claude can autonomously decide which one fits the user's question.
- In agent.py, find the closing ] of the tools list (after the get_weather definition).
- Add these two new tool definitions inside the list, after the get_weather dictionary and before the closing ]:
{
"name": "calculate",
"description": "Perform a mathematical calculation",
"input_schema": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "The math expression to evaluate, e.g. '2 + 2' or '15 * 7'",
}
},
"required": ["expression"],
},
},
{
"name": "get_time",
"description": "Get the current date and time",
"input_schema": {
"type": "object",
"properties": {},
"required": [],
},
},
What do these tool definitions do?
Each tool definition is a dictionary that tells Claude what the tool does and what inputs it needs. Claude reads these descriptions to decide which tool fits the user's question.
- calculate accepts an expression string (like '2 + 2') and evaluates it.
- get_time takes no inputs. Its properties and required are both empty because the current time needs no user input.
What is a JSON Schema?
The input_schema field uses JSON Schema, a standard format for describing the shape of data. It tells Claude what parameters to provide and what types they should be. Claude uses this to generate valid inputs when calling the tool.
- Save agent.py.
You won't see a change yet. The next chunk adds the Python functions that actually execute these tools.
Add the handler functions and dispatch dictionary
Tool definitions tell Claude what tools exist. Handler functions are the Python code that actually runs when Claude calls a tool. You also need a dispatch dictionary that maps tool names to their functions, so the agent loop can call the right one automatically.
- In agent.py, find the get_weather() function. Add these two new functions and the dispatch dictionary directly below it (before the messages = [...] line):
def calculate(expression):
try:
result = eval(expression)
return f"The result of {expression} is {result}"
except Exception as e:
return f"Error calculating: {e}"
def get_time():
now = datetime.now()
return f"The current date and time is {now.strftime('%Y-%m-%d %H:%M:%S')}"
tool_functions = {
"get_weather": lambda inputs: get_weather(inputs["location"]),
"calculate": lambda inputs: calculate(inputs["expression"]),
"get_time": lambda inputs: get_time(),
}
What does this code do?
- calculate() uses Python's eval() to evaluate a math expression as a string. The try/except block catches invalid expressions and returns a friendly error instead of crashing.
- get_time() uses the datetime module (already imported at the top of your file) to return the current date and time.
- tool_functions is a dictionary that maps each tool name to a small function that extracts the right input and calls the handler. This lets the agent loop call any tool by name without a chain of if/else statements.
Is eval() safe to use?
In production code, eval() is a security risk because it can execute arbitrary Python. For this local learning project it is fine. A production agent would use a dedicated math parser instead.
- Save agent.py.
You won't see a change yet. The next chunk replaces the old one-shot code with the agent loop that uses this dispatch dictionary.
Seeing a red underline on datetime?
Make sure from datetime import datetime is still at the top of your file. It was added in the previous step.
help me fix a datetime import error in my agent.py
Build the agent loop
The one-shot code in your file only handles a single tool call and then stops. A real agent needs a loop. If Claude calls a tool and then wants to call another one, the loop keeps going until Claude signals it is done.
What is an agentic loop?
An agentic loop is the core pattern behind every production AI agent. The code sends a message to the model, checks the response, and either returns the final answer or executes the requested tool and loops again. This continues until the model decides it has enough information to respond.
- In agent.py, find this line:
messages = [{"role": "user", "content": "What's the weather in San Francisco?"}]
- Delete everything from that line to the end of the file. Replace it with this run_agent() function:
def run_agent(user_message):
messages = [{"role": "user", "content": user_message}]
while True:
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=1024,
tools=tools,
messages=messages,
)
if response.stop_reason == "end_turn":
for block in response.content:
if block.type == "text":
return block.text
return ""
if response.stop_reason == "tool_use":
messages.append({"role": "assistant", "content": response.content})
tool_results = []
for block in response.content:
if block.type == "tool_use":
tool_name = block.name
tool_input = block.input
print(f" [Using tool: {tool_name}]")
result = tool_functions[tool_name](tool_input)
tool_results.append(
{
"type": "tool_result",
"tool_use_id": block.id,
"content": result,
}
)
messages.append({"role": "user", "content": tool_results})
What does this code do?
- while True creates the loop. Each iteration sends the current conversation to Claude and checks the response.
- If stop_reason is "end_turn", Claude is done thinking. The function extracts the text and returns it.
- If stop_reason is "tool_use", Claude wants to call a tool. The code executes it, appends the result to the conversation, and loops again so Claude can see the result.
- tool_functions[tool_name](tool_input) looks up the right handler in your dispatch dictionary and calls it. This is why you built that dictionary in the previous chunk.
- Save agent.py.
You won't see a change yet. The next chunk adds the interactive input loop so you can actually talk to the agent.
Indentation errors?
Make sure you deleted all of the old one-shot code (from messages = [...] to the end of the file). If any old lines remain, Python will throw an indentation or syntax error.
help me fix indentation errors in my agent loop
Add interactive input
The agent loop handles the conversation with Claude, but there is no way for a user to type questions yet. You will add an input loop that reads from the terminal and feeds each question into run_agent().
- At the bottom of agent.py (after the run_agent() function), add this interactive loop:
print("AI Agent Ready! (type 'quit' to exit)")
print("Try asking about the weather, math, or the current time.")
print()
while True:
user_input = input("You: ")
if user_input.lower() == "quit":
print("Goodbye!")
break
response = run_agent(user_input)
print(f"\nClaude: {response}\n")
What does this code do?
- The three print() lines display a welcome message when the script starts.
- input("You: ") pauses and waits for the user to type something.
- If the user types quit, the loop breaks and the program ends. Otherwise, the input goes to run_agent() and Claude's response prints back.
- Save agent.py.
- Run your agent by entering this command in the terminal:
python agent.py
- You should see AI Agent Ready! (type 'quit' to exit) followed by a You: prompt waiting for your input.
- Type What's 237 times 18? and press Enter.
- You should see [Using tool: calculate] printed, followed by Claude's answer with the result (4266).
- Try What's the weather in London? and What time is it? to test the other tools.
- Type quit to exit the agent.
Agent not responding or throwing an error?
- If you see AuthenticationError, your ANTHROPIC_API_KEY environment variable may have expired. Set it again in the same terminal session.
- If you see KeyError, check that the keys in your tool_functions dictionary exactly match the name fields in your tool definitions (get_weather, calculate, get_time).
- If the script prints nothing and hangs, make sure the print() lines and while True loop are at the top level (no indentation), not inside the run_agent() function.
help me debug my AI agent
✔️ Awesome, I've got everything!
Great. Double check you have saved agent.py and that all three tools respond correctly.
ⓧ I'd like to double check the full code
Here is the complete agent.py file. Compare yours against it:
import anthropic
from datetime import datetime
client = anthropic.Anthropic()
tools = [
{
"name": "get_weather",
"description": "Get the current weather for a given location",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
}
},
"required": ["location"],
},
},
{
"name": "calculate",
"description": "Perform a mathematical calculation",
"input_schema": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "The math expression to evaluate, e.g. '2 + 2' or '15 * 7'",
}
},
"required": ["expression"],
},
},
{
"name": "get_time",
"description": "Get the current date and time",
"input_schema": {
"type": "object",
"properties": {},
"required": [],
},
},
]
def get_weather(location):
weather_data = {
"New York, NY": {"temp": 72, "condition": "Partly cloudy"},
"San Francisco, CA": {"temp": 58, "condition": "Foggy"},
"London, UK": {"temp": 61, "condition": "Rainy"},
}
data = weather_data.get(location, {"temp": 68, "condition": "Sunny"})
return f"The weather in {location} is {data['condition']} with a temperature of {data['temp']}F."
def calculate(expression):
try:
result = eval(expression)
return f"The result of {expression} is {result}"
except Exception as e:
return f"Error calculating: {e}"
def get_time():
now = datetime.now()
return f"The current date and time is {now.strftime('%Y-%m-%d %H:%M:%S')}"
tool_functions = {
"get_weather": lambda inputs: get_weather(inputs["location"]),
"calculate": lambda inputs: calculate(inputs["expression"]),
"get_time": lambda inputs: get_time(),
}
def run_agent(user_message):
messages = [{"role": "user", "content": user_message}]
while True:
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=1024,
tools=tools,
messages=messages,
)
if response.stop_reason == "end_turn":
for block in response.content:
if block.type == "text":
return block.text
return ""
if response.stop_reason == "tool_use":
messages.append({"role": "assistant", "content": response.content})
tool_results = []
for block in response.content:
if block.type == "tool_use":
tool_name = block.name
tool_input = block.input
print(f" [Using tool: {tool_name}]")
result = tool_functions[tool_name](tool_input)
tool_results.append(
{
"type": "tool_result",
"tool_use_id": block.id,
"content": result,
}
)
messages.append({"role": "user", "content": tool_results})
print("AI Agent Ready! (type 'quit' to exit)")
print("Try asking about the weather, math, or the current time.")
print()
while True:
user_input = input("You: ")
if user_input.lower() == "quit":
print("Goodbye!")
break
response = run_agent(user_input)
print(f"\nClaude: {response}\n")
Your multi-tool AI agent is working. Claude reads a question, decides which tool to call based on the descriptions you wrote, executes the tool, and gives you a natural language answer. Next up, you will give your agent memory so it can handle follow-up questions.
Secret mission
Add Conversation Memory
Your agent forgets everything between questions. Can you make it remember the full conversation so follow-up questions actually work?
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs locally with an API key for Claude. There are no ongoing costs unless you make additional API calls.
Resources you used:
- Anthropic API key (used to authenticate with Claude).
- Local project files: hello_claude.py and agent.py.
- Anthropic Python SDK (installed via pip).
Keep everything running
No action needed. Choose this if you plan to keep experimenting with Claude's API or building on this agent.
- Your API key remains active. There is no ongoing cost unless you make API calls.
- Your project files stay on your machine ready to run again.
- The Anthropic Python SDK stays installed for future projects.
Pause - I'll come back to this later
Free up your terminal session. Your files and API key stay intact for when you return.
- Close the terminal you used for this project. The ANTHROPIC_API_KEY environment variable only lasts for that session, so you will need to set it again next time.
- Your project files and installed SDK remain unchanged.
Delete - I don't want to use this again
Remove all project resources and revoke access to ensure zero future charges.
- Revoke your API key by going to console.anthropic.com, selecting API Keys from the sidebar, clicking the key you created, and selecting Revoke Key.
- Delete your project files by running this in your terminal:
macOS
cd ~/Desktop
rm -rf hello_claude.py agent.py
Windows
cd ~\Desktop
Remove-Item hello_claude.py, agent.py
- Uninstall the Anthropic Python SDK by running:
pip uninstall anthropic -y
That's a Wrap!
That's a Wrap!
Nice work! You just built a tool-calling AI agent from scratch using Python and the Anthropic SDK.
You've learned how to:
- Make programmatic API calls to Claude using the Anthropic Python SDK and interpret the response object.
- Define tool schemas using JSON Schema and handle the full tool-calling round trip: Claude requests a tool, your code executes it, and the result feeds back for a final answer.
- Build a multi-tool agent loop that autonomously decides which tool to call based on your question.
- Secret Mission: Added conversation memory so your agent maintains context across multiple exchanges.
Ready to quiz yourself?