Create a function and decorate it with @tool to make it available to your agent:
@tool(approval_mode="never_require")def get_weather( location: Annotated[str, Field(description="The location to get the weather for.")],) -> str: """Get the weather for a given location.""" conditions = ["sunny", "cloudy", "rainy", "stormy"] return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
The approval_mode="never_require" setting is used for simplicity in this example. In production, use approval_mode="always_require" to require user confirmation before executing tools.
Key components:
@tool decorator: Registers the function as a tool
Type annotations: Define parameter types for validation
Annotated with Field: Provides descriptions for the LLM
Docstring: Explains what the tool does (sent to the LLM)
Return type: Specifies what the function returns
3
Create the Agent with Tools
Initialize the agent and provide the tool:
async def main(): credential = AzureCliCredential() client = AzureOpenAIResponsesClient( project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], deployment_name=os.environ["AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME"], credential=credential, ) agent = client.as_agent( name="WeatherAgent", instructions="You are a helpful weather agent. Use the get_weather tool to answer questions.", tools=get_weather, # Pass the tool to the agent )
You can pass a single tool, a list of tools, or any iterable of tools to the tools parameter.
4
Run the Agent
Ask the agent a question that requires using the tool:
result = await agent.run("What's the weather like in Seattle?") print(f"Agent: {result}")if __name__ == "__main__": asyncio.run(main())
import asyncioimport osfrom random import randintfrom typing import Annotatedfrom agent_framework import toolfrom agent_framework.azure import AzureOpenAIResponsesClientfrom azure.identity import AzureCliCredentialfrom dotenv import load_dotenvfrom pydantic import Fieldload_dotenv()@tool(approval_mode="never_require")def get_weather( location: Annotated[str, Field(description="The location to get the weather for.")],) -> str: """Get the weather for a given location.""" conditions = ["sunny", "cloudy", "rainy", "stormy"] return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."async def main(): credential = AzureCliCredential() client = AzureOpenAIResponsesClient( project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"], deployment_name=os.environ["AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME"], credential=credential, ) agent = client.as_agent( name="WeatherAgent", instructions="You are a helpful weather agent. Use the get_weather tool to answer questions.", tools=get_weather, ) result = await agent.run("What's the weather like in Seattle?") print(f"Agent: {result}")if __name__ == "__main__": asyncio.run(main())
You can provide multiple tools to create more capable agents:
@tool(approval_mode="never_require")def get_weather( location: Annotated[str, Field(description="The location to get the weather for.")],) -> str: """Get the weather for a given location.""" conditions = ["sunny", "cloudy", "rainy", "stormy"] return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."@tool(approval_mode="never_require")def get_menu_specials() -> str: """Get today's menu specials.""" return """ Special Soup: Clam Chowder Special Salad: Cobb Salad Special Drink: Chai Tea """# Create agent with multiple toolsagent = client.as_agent( name="AssistantAgent", instructions="You are a helpful assistant that can provide weather and restaurant information.", tools=[get_weather, get_menu_specials], # Pass list of tools)# The agent can now use either tool as neededresult = await agent.run("What's the weather in Amsterdam and what are today's specials?")print(result)
import httpx@tool(approval_mode="never_require")async def fetch_weather_api( location: Annotated[str, Field(description="The location to get weather for")],) -> str: """Fetch real weather data from an API.""" async with httpx.AsyncClient() as client: response = await client.get(f"https://api.weather.com/v1/location/{location}") return response.json()["current"]["condition"]
For production applications, require user approval before executing tools:
@tool(approval_mode="always_require")def delete_file( path: Annotated[str, Field(description="Path to the file to delete")],) -> str: """Delete a file from the filesystem.""" os.remove(path) return f"Deleted {path}"
When approval_mode="always_require", the framework will pause execution and request user confirmation before calling the tool.
Provide detailed descriptions for both the function and its parameters. The LLM uses these to decide when and how to call your tools:
@tool(approval_mode="never_require")def search_database( query: Annotated[str, Field(description="The SQL query to execute. Use proper SQL syntax.")], limit: Annotated[int, Field(description="Maximum number of results to return. Default is 10.")] = 10,) -> str: """Execute a SQL query against the database and return formatted results.""" # Implementation...
Return error messages as strings rather than raising exceptions:
@tool(approval_mode="never_require")def divide_numbers(a: float, b: float) -> str: """Divide two numbers.""" if b == 0: return "Error: Cannot divide by zero" return str(a / b)
This allows the LLM to see the error and potentially retry or inform the user.
Keep Tools Focused
Each tool should do one thing well. Instead of a manage_database tool that can read, write, and delete, create separate tools:
@tool(approval_mode="never_require")def read_from_database(query: str) -> str: """Read data from the database.""" ...@tool(approval_mode="always_require")def write_to_database(query: str) -> str: """Write data to the database. Requires approval.""" ...