03 — Tools & Function Calling
So far the agent can only generate text. In this lesson you will give it tools — Python functions that the agent can call during a conversation to look up information or perform actions.
How tool calling works
User message ──▶ Agent ──▶ LLM decides to call a tool
│
▼
Tool executes (your code)
│
▼
LLM uses tool result to answer
- The user sends a message.
- The LLM decides it needs external information and emits a tool call.
- Agent Framework executes the matching Python function.
- The tool result is sent back to the LLM.
- The LLM generates the final answer.
Defining a function tool
Any Python function can be a tool. Use type annotations and Pydantic Field to
describe the parameters so the LLM knows how to call it:
from typing import Annotated
from pydantic import Field
from agent_framework import tool
@tool(name="check_drug_interaction", description="Check for known interactions between two drugs.")
def check_drug_interaction(
drug_a: Annotated[str, Field(description="First drug name")],
drug_b: Annotated[str, Field(description="Second drug name")],
) -> str:
"""Return a simulated drug-interaction result."""
interactions = {
("warfarin", "aspirin"): "High risk — increased bleeding risk when combined.",
("lisinopril", "potassium"): "Moderate risk — may cause hyperkalaemia.",
}
key = (drug_a.lower(), drug_b.lower())
reverse_key = (drug_b.lower(), drug_a.lower())
if key in interactions:
return interactions[key]
if reverse_key in interactions:
return interactions[reverse_key]
return f"No known interaction found between {drug_a} and {drug_b}."
Tip
If you omit name and description in @tool, the framework uses the
function name and docstring as defaults.
Providing tools to the agent
Pass your tools to the tools parameter when creating the agent:
agent = Agent(
client=client,
name="ClinicalAssistant",
instructions="You are a clinical assistant. Use the available tools to answer questions.",
tools=[check_drug_interaction, get_patient_vitals],
)
Full example
File: examples/03-tools/function_tools.py
import asyncio
import os
from typing import Annotated
from dotenv import load_dotenv
from pydantic import Field
from agent_framework import Agent, tool
from agent_framework.foundry import FoundryChatClient
from azure.identity import AzureCliCredential
load_dotenv()
@tool(name="check_drug_interaction", description="Check for known interactions between two drugs.")
def check_drug_interaction(
drug_a: Annotated[str, Field(description="First drug name")],
drug_b: Annotated[str, Field(description="Second drug name")],
) -> str:
"""Return a simulated drug-interaction result."""
interactions = {
("warfarin", "aspirin"): "High risk — increased bleeding risk when combined.",
("lisinopril", "potassium"): "Moderate risk — may cause hyperkalaemia.",
}
key = (drug_a.lower(), drug_b.lower())
reverse_key = (drug_b.lower(), drug_a.lower())
if key in interactions:
return interactions[key]
if reverse_key in interactions:
return interactions[reverse_key]
return f"No known interaction found between {drug_a} and {drug_b}."
@tool(name="get_patient_vitals", description="Look up the latest vitals for a patient by ID.")
def get_patient_vitals(
patient_id: Annotated[str, Field(description="The patient identifier, e.g. P-1234")],
) -> str:
"""Return simulated patient vitals."""
vitals = {
"P-1001": "Heart rate: 72 bpm, Blood pressure: 120/80, Temperature: 36.6°C",
"P-1002": "Heart rate: 95 bpm, Blood pressure: 145/92, Temperature: 37.1°C",
}
return vitals.get(patient_id, f"No vitals found for patient {patient_id}.")
async def main() -> None:
client = FoundryChatClient(
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
model=os.environ.get("FOUNDRY_MODEL", "gpt-5.4-mini"),
credential=AzureCliCredential(),
)
agent = Agent(
client=client,
name="ClinicalAssistant",
instructions=(
"You are a clinical assistant. Use the available tools to answer "
"questions about drug interactions and patient vitals. "
"Always note that results are simulated."
),
tools=[check_drug_interaction, get_patient_vitals],
)
# The agent should call check_drug_interaction
print("User: Is it safe to take warfarin and aspirin together?\n")
result1 = await agent.run("Is it safe to take warfarin and aspirin together?")
print(f"Agent: {result1}\n")
# The agent should call get_patient_vitals
print("User: What are the latest vitals for patient P-1002?\n")
result2 = await agent.run("What are the latest vitals for patient P-1002?")
print(f"Agent: {result2}\n")
if __name__ == "__main__":
asyncio.run(main())
Other tool types
Function tools are the most common type, but Agent Framework supports several more:
| Tool type | Description |
|---|---|
| Function Tools | Custom Python code (what you learned here) |
| Web Search | Search the web for information |
| File Search | Search through uploaded files |
| Code Interpreter | Execute code in a sandboxed environment |
| MCP Tools | Tools from Model Context Protocol servers |
All providers support function tools. Other tool types depend on the provider — see the provider support matrix.
Try it
The agent should call the drug-interaction tool for the first question and the vitals tool for the second.
Key takeaways
- A function tool is any Python function exposed to the agent via the
toolsparameter. - Use
@toolandAnnotated[str, Field(description=...)]to give the LLM clear information about what the tool does and what arguments it expects. - The agent decides when to call a tool based on the user's message.
- You can combine multiple tools in a single agent.