|
| 1 | +--- |
| 2 | +title: 'How to use Azure AI Agent service with function calling' |
| 3 | +titleSuffix: Azure OpenAI |
| 4 | +description: Learn how to use Azure AI Agents with function calling. |
| 5 | +services: cognitive-services |
| 6 | +manager: nitinme |
| 7 | +ms.service: azure |
| 8 | +ms.topic: how-to |
| 9 | +ms.date: 11/20/2024 |
| 10 | +author: aahill |
| 11 | +ms.author: aahi |
| 12 | +zone_pivot_groups: selection-function-calling |
| 13 | +recommendations: false |
| 14 | +--- |
| 15 | + |
| 16 | +# Azure AI Agents function calling |
| 17 | + |
| 18 | +::: zone pivot="overview" |
| 19 | + |
| 20 | +Azure AI Agents supports function calling, which allows you to describe the structure of functions to an Assistant and then return the functions that need to be called along with their arguments. |
| 21 | + |
| 22 | +> [!NOTE] |
| 23 | +> Runs expire ten minutes after creation. Be sure to submit your tool outputs before the expiration. |
| 24 | +
|
| 25 | +### Supported models |
| 26 | + |
| 27 | +The [models page](../../concepts/model-region-support.md) contains the most up-to-date information on regions/models where Agents are supported. |
| 28 | + |
| 29 | +To use all features of function calling including parallel functions, you need to use a model that was released after November 6, 2023. |
| 30 | + |
| 31 | +::: zone-end |
| 32 | + |
| 33 | +::: zone pivot="code-example" |
| 34 | + |
| 35 | +## Define a function for your agent to call |
| 36 | + |
| 37 | +Start by defining a function for your agent to call. When you create a function for an agent to call, you describe its structure of it with any required parameters. |
| 38 | + |
| 39 | +# [Python](#tab/python) |
| 40 | + |
| 41 | +```python |
| 42 | +def fetch_weather(location: str) -> str: |
| 43 | + """ |
| 44 | + Fetches the weather information for the specified location. |
| 45 | +
|
| 46 | + :param location (str): The location to fetch weather for. |
| 47 | + :return: Weather information as a JSON string. |
| 48 | + :rtype: str |
| 49 | + """ |
| 50 | + # In a real-world scenario, you'd integrate with a weather API. |
| 51 | + # Here, we'll mock the response. |
| 52 | + mock_weather_data = {"New York": "Sunny, 25°C", "London": "Cloudy, 18°C", "Tokyo": "Rainy, 22°C"} |
| 53 | + weather = mock_weather_data.get(location, "Weather data not available for this location.") |
| 54 | + weather_json = json.dumps({"weather": weather}) |
| 55 | + return weather_json |
| 56 | +``` |
| 57 | + |
| 58 | + |
| 59 | +See the [python file on GitHub](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/samples/agents/user_functions.py) for an example of a full series of function definitions. This file is referred to as `user_functions.py` in the following example below. |
| 60 | + |
| 61 | + |
| 62 | + |
| 63 | +# [C#](#tab/csharp) |
| 64 | + |
| 65 | +```csharp |
| 66 | +// Example of a function that defines no parameters |
| 67 | +string GetUserFavoriteCity() => "Seattle, WA"; |
| 68 | +FunctionToolDefinition getUserFavoriteCityTool = new("getUserFavoriteCity", "Gets the user's favorite city."); |
| 69 | +// Example of a function with a single required parameter |
| 70 | +string GetCityNickname(string location) => location switch |
| 71 | +{ |
| 72 | + "Seattle, WA" => "The Emerald City", |
| 73 | + _ => throw new NotImplementedException(), |
| 74 | +}; |
| 75 | +FunctionToolDefinition getCityNicknameTool = new( |
| 76 | + name: "getCityNickname", |
| 77 | + description: "Gets the nickname of a city, e.g. 'LA' for 'Los Angeles, CA'.", |
| 78 | + parameters: BinaryData.FromObjectAsJson( |
| 79 | + new |
| 80 | + { |
| 81 | + Type = "object", |
| 82 | + Properties = new |
| 83 | + { |
| 84 | + Location = new |
| 85 | + { |
| 86 | + Type = "string", |
| 87 | + Description = "The city and state, e.g. San Francisco, CA", |
| 88 | + }, |
| 89 | + }, |
| 90 | + Required = new[] { "location" }, |
| 91 | + }, |
| 92 | + new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase })); |
| 93 | +``` |
| 94 | + |
| 95 | +<!--See the [C# file on GitHub](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/samples/agents/user_functions.py) for an additional function definition examples. --> |
| 96 | +
|
| 97 | +In the following sample, we create a helper function to get and parse the resolved tools' outputs, and return it. |
| 98 | + |
| 99 | +```csharp |
| 100 | +ToolOutput GetResolvedToolOutput(RequiredToolCall toolCall) |
| 101 | +{ |
| 102 | + if (toolCall is RequiredFunctionToolCall functionToolCall) |
| 103 | + { |
| 104 | + if (functionToolCall.Name == getUserFavoriteCityTool.Name) |
| 105 | + { |
| 106 | + return new ToolOutput(toolCall, GetUserFavoriteCity()); |
| 107 | + } |
| 108 | + using JsonDocument argumentsJson = JsonDocument.Parse(functionToolCall.Arguments); |
| 109 | + if (functionToolCall.Name == getCityNicknameTool.Name) |
| 110 | + { |
| 111 | + string locationArgument = argumentsJson.RootElement.GetProperty("location").GetString(); |
| 112 | + return new ToolOutput(toolCall, GetCityNickname(locationArgument)); |
| 113 | + } |
| 114 | + } |
| 115 | + return null; |
| 116 | +} |
| 117 | +``` |
| 118 | + |
| 119 | +--- |
| 120 | + |
| 121 | +## Create a client |
| 122 | + |
| 123 | +# [Python](#tab/python) |
| 124 | + |
| 125 | +In the sample below we create a client and define a `toolset` which will be used to process the functions defined in `user_functions`. |
| 126 | + |
| 127 | +```python |
| 128 | +import os |
| 129 | +from azure.ai.projects import AIProjectClient |
| 130 | +from azure.identity import DefaultAzureCredential |
| 131 | +from azure.ai.projects.models import FunctionTool, ToolSet |
| 132 | +from user_functions import user_functions # user functions which can be found in a user_functions.py file. |
| 133 | + |
| 134 | +# Create an Azure AI Client from a connection string, copied from your AI Studio project. |
| 135 | +# It should be in the format "<HostName>;<AzureSubscriptionId>;<ResourceGroup>;<HubName>" |
| 136 | +# Customers need to login to Azure subscription via Azure CLI and set the environment variables |
| 137 | + |
| 138 | +project_client = AIProjectClient.from_connection_string( |
| 139 | + credential=DefaultAzureCredential(), |
| 140 | + conn_str=os.environ["PROJECT_CONNECTION_STRING"], |
| 141 | +) |
| 142 | + |
| 143 | +# Initialize agent toolset with user functions |
| 144 | +functions = FunctionTool(user_functions) |
| 145 | +toolset = ToolSet() |
| 146 | +toolset.add(functions) |
| 147 | +``` |
| 148 | + |
| 149 | +# [C#](#tab/csharp) |
| 150 | + |
| 151 | +```csharp |
| 152 | +// note: parallel function calling is only supported with newer models like gpt-4-1106-preview |
| 153 | +Response<Agent> agentResponse = await client.CreateAgentAsync( |
| 154 | + model: "gpt-4-1106-preview", |
| 155 | + name: "SDK Test Agent - Functions", |
| 156 | + instructions: "You are a weather bot. Use the provided functions to help answer questions. " |
| 157 | + + "Customize your responses to the user's preferences as much as possible and use friendly " |
| 158 | + + "nicknames for cities whenever possible.", |
| 159 | + tools: new List<ToolDefinition> { getUserFavoriteCityTool, getCityNicknameTool, getCurrentWeatherAtLocationTool } |
| 160 | + ); |
| 161 | +Agent agent = agentResponse.Value; |
| 162 | +``` |
| 163 | + |
| 164 | +--- |
| 165 | + |
| 166 | + |
| 167 | +## Submitting function outputs |
| 168 | + |
| 169 | +# [Python](#tab/python) |
| 170 | + |
| 171 | +```python |
| 172 | + |
| 173 | +# Create agent with toolset and process a run |
| 174 | + |
| 175 | +agent = project_client.agents.create_agent( |
| 176 | + model="gpt-4o-mini", name="my-agent", instructions="You are a helpful agent", toolset=toolset |
| 177 | +) |
| 178 | +print(f"Created agent, ID: {agent.id}") |
| 179 | +``` |
| 180 | + |
| 181 | +# [C#](#tab/csharp) |
| 182 | + |
| 183 | +```csharp |
| 184 | +// note: parallel function calling is only supported with newer models like gpt-4-1106-preview |
| 185 | +Response<Agent> agentResponse = await client.CreateAgentAsync( |
| 186 | + model: "gpt-4o-mini", |
| 187 | + name: "SDK Test Agent - Functions", |
| 188 | + instructions: "You are a weather bot. Use the provided functions to help answer questions. " |
| 189 | + + "Customize your responses to the user's preferences as much as possible and use friendly " |
| 190 | + + "nicknames for cities whenever possible.", |
| 191 | + tools: new List<ToolDefinition> { getUserFavoriteCityTool, getCityNicknameTool, getCurrentWeatherAtLocationTool } |
| 192 | + ); |
| 193 | +Agent agent = agentResponse.Value; |
| 194 | +``` |
| 195 | + |
| 196 | +--- |
| 197 | + |
| 198 | +## Create a thread |
| 199 | + |
| 200 | +# [python](#tab/python) |
| 201 | + |
| 202 | +```python |
| 203 | +# Create thread for communication |
| 204 | +thread = project_client.agents.create_thread() |
| 205 | +print(f"Created thread, ID: {thread.id}") |
| 206 | + |
| 207 | +# Create message to thread |
| 208 | +message = project_client.agents.create_message( |
| 209 | + thread_id=thread.id, |
| 210 | + role="user", |
| 211 | + content="Hello, send an email with the datetime and weather information in New York?", |
| 212 | +) |
| 213 | +print(f"Created message, ID: {message.id}") |
| 214 | +``` |
| 215 | + |
| 216 | +# [C#](#tab/csharp) |
| 217 | + |
| 218 | +```csharp |
| 219 | +Response<AgentThread> threadResponse = await client.CreateThreadAsync(); |
| 220 | +AgentThread thread = threadResponse.Value; |
| 221 | + |
| 222 | +Response<ThreadMessage> messageResponse = await client.CreateMessageAsync( |
| 223 | + thread.Id, |
| 224 | + MessageRole.User, |
| 225 | + "What's the weather like in my favorite city?"); |
| 226 | +ThreadMessage message = messageResponse.Value; |
| 227 | +``` |
| 228 | + |
| 229 | +--- |
| 230 | + |
| 231 | +## Create a run and check the output |
| 232 | + |
| 233 | +# [python](#tab/python) |
| 234 | + |
| 235 | +```python |
| 236 | +# Create and process agent run in thread with tools |
| 237 | +run = project_client.agents.create_and_process_run(thread_id=thread.id, assistant_id=agent.id) |
| 238 | +print(f"Run finished with status: {run.status}") |
| 239 | + |
| 240 | +if run.status == "failed": |
| 241 | + print(f"Run failed: {run.last_error}") |
| 242 | + |
| 243 | +# Delete the agent when done |
| 244 | +project_client.agents.delete_agent(agent.id) |
| 245 | +print("Deleted agent") |
| 246 | + |
| 247 | +# Fetch and log all messages |
| 248 | +messages = project_client.agents.list_messages(thread_id=thread.id) |
| 249 | +print(f"Messages: {messages}") |
| 250 | +``` |
| 251 | + |
| 252 | +# [C#](#tab/csharp) |
| 253 | + |
| 254 | +```csharp |
| 255 | +Response<ThreadRun> runResponse = await client.CreateRunAsync(thread, agent); |
| 256 | + |
| 257 | +#region Snippet:FunctionsHandlePollingWithRequiredAction |
| 258 | +do |
| 259 | +{ |
| 260 | + await Task.Delay(TimeSpan.FromMilliseconds(500)); |
| 261 | + runResponse = await client.GetRunAsync(thread.Id, runResponse.Value.Id); |
| 262 | + |
| 263 | + if (runResponse.Value.Status == RunStatus.RequiresAction |
| 264 | + && runResponse.Value.RequiredAction is SubmitToolOutputsAction submitToolOutputsAction) |
| 265 | + { |
| 266 | + List<ToolOutput> toolOutputs = new(); |
| 267 | + foreach (RequiredToolCall toolCall in submitToolOutputsAction.ToolCalls) |
| 268 | + { |
| 269 | + toolOutputs.Add(GetResolvedToolOutput(toolCall)); |
| 270 | + } |
| 271 | + runResponse = await client.SubmitToolOutputsToRunAsync(runResponse.Value, toolOutputs); |
| 272 | + } |
| 273 | +} |
| 274 | +while (runResponse.Value.Status == RunStatus.Queued |
| 275 | + || runResponse.Value.Status == RunStatus.InProgress); |
| 276 | +#endregion |
| 277 | + |
| 278 | +Response<PageableList<ThreadMessage>> afterRunMessagesResponse |
| 279 | + = await client.GetMessagesAsync(thread.Id); |
| 280 | +IReadOnlyList<ThreadMessage> messages = afterRunMessagesResponse.Value.Data; |
| 281 | + |
| 282 | +// Note: messages iterate from newest to oldest, with the messages[0] being the most recent |
| 283 | +foreach (ThreadMessage threadMessage in messages) |
| 284 | +{ |
| 285 | + Console.Write($"{threadMessage.CreatedAt:yyyy-MM-dd HH:mm:ss} - {threadMessage.Role,10}: "); |
| 286 | + foreach (MessageContent contentItem in threadMessage.ContentItems) |
| 287 | + { |
| 288 | + if (contentItem is MessageTextContent textItem) |
| 289 | + { |
| 290 | + Console.Write(textItem.Text); |
| 291 | + } |
| 292 | + else if (contentItem is MessageImageFileContent imageFileItem) |
| 293 | + { |
| 294 | + Console.Write($"<image from ID: {imageFileItem.FileId}"); |
| 295 | + } |
| 296 | + Console.WriteLine(); |
| 297 | + } |
| 298 | +} |
| 299 | +``` |
| 300 | + |
| 301 | +--- |
| 302 | + |
| 303 | +::: zone-end |
| 304 | + |
| 305 | +## See also |
| 306 | + |
| 307 | +* [Lean how to ground agents by using Bing Web Search](./bing-grounding.md) |
0 commit comments