Tool Calling – Making Your Agent Act
In the previous chapters you gave your agent instructions and grounded it in your own data with File Search (RAG).
Now, let’s enable your agent to take actions by calling tools — small, well-defined functions your agent can invoke to perform tasks (e.g., calculations, lookups, API calls).
What Are Tools (Function Calling)?
Tools let your agent call your code with structured inputs.
When a user asks for something that matches a tool’s purpose, the agent will select that tool, pass validated arguments, and use the tool’s result to craft a final answer.
Why this matters
- Deterministic actions: offload precise work (math, lookup, API calls) to your code.
- Safety & control: you define what the agent is allowed to do.
- Better UX: the agent can provide concrete, actionable answers.
Adding the Pizza Size Calculator tool
We’ll add a tool that, given a group size and an appetite level, recommends how many and what size pizzas to order.
1) Create tools.py (new file)
def calculate_pizza_for_people(people_count: int, appetite_level: str = "normal") -> str:
"""
Calculate the number and size of pizzas needed for a group of people.
Args:
people_count (int): Number of people who will be eating
appetite_level (str): Appetite level - "light", "normal", or "heavy" (default: "normal")
Returns:
str: Recommendation for pizza size and quantity
"""
print(f"[TOOL CALLED] Calculating pizza for {people_count} people with {appetite_level} appetite.")
if people_count <= 0:
return "Please provide a valid number of people (greater than 0)."
# Base calculations assuming normal appetite
# Small: 1–2 people | Medium: 2–3 | Large: 3–4 | Extra Large: 4–6
appetite_multipliers = {"light": 0.7, "normal": 1.0, "heavy": 1.3}
multiplier = appetite_multipliers.get(appetite_level.lower(), 1.0)
adjusted_people = people_count * multiplier
recommendations = []
if adjusted_people <= 2:
if adjusted_people <= 1:
recommendations.append("1 Small pizza (perfect for 1-2 people)")
else:
recommendations.append("1 Medium pizza (great for 2-3 people)")
elif adjusted_people <= 4:
recommendations.append("1 Large pizza (serves 3-4 people)")
elif adjusted_people <= 6:
recommendations.append("1 Extra Large pizza (feeds 4-6 people)")
elif adjusted_people <= 8:
recommendations.append("2 Large pizzas (perfect for sharing)")
elif adjusted_people <= 12:
recommendations.append("2 Extra Large pizzas (great for groups)")
else:
# For larger groups, calculate multiple pizzas
extra_large_count = int(adjusted_people // 5)
remainder = adjusted_people % 5
pizza_list = []
if extra_large_count > 0:
pizza_list.append(f"{extra_large_count} Extra Large pizza{'s' if extra_large_count > 1 else ''}")
if remainder > 2:
pizza_list.append("1 Large pizza")
elif remainder > 0:
pizza_list.append("1 Medium pizza")
recommendations.append(" + ".join(pizza_list))
result = f"For {people_count} people with {appetite_level} appetite:\n"
result += f"🍕 Recommendation: {recommendations[0]}\n"
if appetite_level != "normal":
result += f"(Adjusted for {appetite_level} appetite level)"
return resultINFO
This function needs no imports; it uses only Python built-ins.
2) Import the function in agent.py
Add the import alongside your other imports:
from tools import calculate_pizza_for_peopleYour imports should look like this:
import os
from azure.ai.projects import AIProjectClient
from azure.identity import DefaultAzureCredential
from azure.ai.agents.models import MessageRole, FilePurpose, FunctionTool, FileSearchTool, ToolSet
from tools import calculate_pizza_for_people
from dotenv import load_dotenv3) Expose the function as a tool
Create a FunctionTool seeded with the Python function(s) the agent may call:
# Create a FunctionTool for the calculate_pizza_for_people function
function_tool = FunctionTool(functions={calculate_pizza_for_people})Insert this block immediately after your File Search setup and toolset creation, like so:
Existing
# Create the file_search tool
vector_store_id = "<INSERT COPIED VECTOR STORE ID>"
file_search = FileSearchTool(vector_store_ids=[vector_store_id])
# Creating the toolset
toolset = ToolSet()
toolset.add(file_search)New
# Create the file_search tool
vector_store_id = "<INSERT COPIED VECTOR STORE ID>"
file_search = FileSearchTool(vector_store_ids=[vector_store_id])
# Create the function tool
function_tool = FunctionTool(functions={calculate_pizza_for_people})
# Creating the toolset
toolset = ToolSet()
toolset.add(file_search)
toolset.add(function_tool)4) Enable automatic function calling (optional, if supported)
Right after you create your toolset, enable auto function calling so the agent can invoke tools without you routing calls manually:
toolset.add(function_tool)
# Enable automatic function calling for this toolset so the agent can call functions directly
project_client.agents.enable_auto_function_calls(toolset)Trying It Out
Run your agent and ask a question that should trigger the tool:
We are 7 people with heavy appetite. What pizzas should we order?The agent should call calculate_pizza_for_people and reply with the recommendation it returns.
Tips & Best Practices
- Schema first: if your SDK supports argument schemas, define clear types/enums/required fields.
- Validate inputs: the tool should handle bad or missing data gracefully.
- Single-purpose tools: small, focused tools are easier for the agent to choose and combine.
- Explainability: name/describe tools so the agent knows when to use them.
Recap
In this chapter you:
- Created a pizza calculator in a separate
tools.py. - Exposed it as a function tool the agent can call.
- Added it to your existing ToolSet (alongside File Search).
- (Optionally) enabled automatic function calling.
- Verified tool calling by prompting your agent.
Final code sample
import os
from azure.ai.projects import AIProjectClient
from azure.identity import DefaultAzureCredential
from azure.ai.agents.models import MessageRole, FilePurpose, FunctionTool, FileSearchTool, ToolSet
from tools import calculate_pizza_for_people
from dotenv import load_dotenv
load_dotenv(override=True)
# Creating the AIProjectClient
project_client = AIProjectClient(
endpoint=os.environ["PROJECT_CONNECTION_STRING"],
credential=DefaultAzureCredential()
)
# Create the file_search tool
vector_store_id = "<INSERT COPIED VECTOR STORE ID>"
file_search = FileSearchTool(vector_store_ids=[vector_store_id])
# Create the function tool
function_tool = FunctionTool(functions={calculate_pizza_for_people})
# Creating the toolset
toolset = ToolSet()
toolset.add(file_search)
toolset.add(function_tool)
# Enable automatic function calling for this toolset so the agent can call functions directly
project_client.agents.enable_auto_function_calls(toolset)
# Creating the agent
agent = project_client.agents.create_agent(
model="gpt-4o",
name="my-agent",
instructions=open("instructions.txt").read(),
top_p=0.7,
temperature=0.7,
toolset=toolset # Add the toolset to the agent
)
print(f"Created agent, ID: {agent.id}")
# Creating the thread
thread = project_client.agents.threads.create()
print(f"Created thread, ID: {thread.id}")
try:
while True:
# Get the user input
user_input = input("You: ")
# Break out of the loop
if user_input.lower() in ["exit", "quit"]:
break
# Add a message to the thread
message = project_client.agents.messages.create(
thread_id=thread.id,
role=MessageRole.USER,
content=user_input
)
# Process the agent run
run = project_client.agents.runs.create_and_process(
thread_id=thread.id,
agent_id=agent.id
)
# List messages and print the first text response from the agent
messages = project_client.agents.messages.list(thread_id=thread.id)
first_message = next(iter(messages), None)
if first_message:
print(next((item["text"]["value"] for item in first_message.content if item.get("type") == "text"), ""))
finally:
# Clean up the agent when done
project_client.agents.delete_agent(agent.id)
print("Deleted agent")