|
| 1 | +--- |
| 2 | +title: 'Invoke an OpenAPI App Service web app from Azure AI Agent Service' |
| 3 | +description: Learn how to integrate App Service with AI Agent Service and get started with agentic AI |
| 4 | +author: seligj95 |
| 5 | +ms.author: jordanselig |
| 6 | +ms.date: 02/19/2025 |
| 7 | +ms.topic: article |
| 8 | +ms.custom: devx-track-dotnet |
| 9 | +ms.collection: ce-skilling-ai-copilot |
| 10 | +--- |
| 11 | + |
| 12 | +# Invoke an OpenAPI App Service web app from Azure AI Agent Service |
| 13 | + |
| 14 | +[Azure AI Agent Service](/azure/ai-services/agents/overview.md) allows you to create AI agents tailored to your needs through custom instructions and augmented by advanced tools like code interpreter, and custom functions. You can now connect your Azure AI Agent to an external API using an [OpenAPI 3.0](https://www.openapis.org/what-is-openapi) specified tool, allowing for scalable interoperability with various applications. In the following tutorial, you're using an Azure AI Agent to invoke an API hosted on Azure App Service. |
| 15 | + |
| 16 | +## Prerequisites |
| 17 | + |
| 18 | +To complete this tutorial, you need an Azure AI Agent project as well as a RESTful API hosted on Azure App Service. The API should have an OpenAPI specification that defines the API. The OpenAPI specification for the sample app in this tutorial is provided. |
| 19 | + |
| 20 | +1. Ensure you've completed the prerequisites and setup steps in the [quickstart](/azure/ai-services/agents/quickstart.md). This quickstart walks you through creating your Azure AI Hub and Agent project. You can also complete the agent configuration and sample the quickstart provides to get a full understanding of the tool and ensure your setup works. |
| 21 | +1. Ensure you've completed the [RESTful API tutorial](./app-service-web-tutorial-rest-api.md) in Azure App Service. You don't need to complete the part of the tutorial where CORS functionality is added. This tutorial walks you through creating a to-do list app. |
| 22 | + |
| 23 | +## Connect your AI Agent to your App Service API |
| 24 | + |
| 25 | +### Create and review the OpenAPI specification |
| 26 | + |
| 27 | +Now that you've created the required infrastructure, you can put it all together and start interacting with your API using your AI Agent. For a general overview on how to do this, see [How to use Azure AI Agent Service with OpenAPI Specified Tools](/azure/ai-services/agents/how-to/tools/openapi-spec.md). That overview includes prerequisites and additional requirements including how to include authentication if your API requires it. The provided sample API is publicly accessible so authentication isn't required. |
| 28 | + |
| 29 | +1. The following is the OpenAPI specification for the sample app that is provided. On your local machine, create a file called `swagger.json` and copy the following contents. |
| 30 | + |
| 31 | + ```json |
| 32 | + { |
| 33 | + "openapi": "3.0.1", |
| 34 | + "info": { |
| 35 | + "title": "My API", |
| 36 | + "version": "v1" |
| 37 | + }, |
| 38 | + "servers": [ |
| 39 | + { |
| 40 | + "url": "https://brave-meadow-7e969c3ff5174aca8210cab2cd8264bb.azurewebsites.net/" |
| 41 | + } |
| 42 | + ], |
| 43 | + "paths": { |
| 44 | + "/api/Todo": { |
| 45 | + "get": { |
| 46 | + "tags": [ |
| 47 | + "Todo" |
| 48 | + ], |
| 49 | + "operationId": "getToDoListItems", |
| 50 | + "responses": { |
| 51 | + "200": { |
| 52 | + "description": "Success", |
| 53 | + "content": { |
| 54 | + "text/plain": { |
| 55 | + "schema": { |
| 56 | + "type": "array", |
| 57 | + "items": { |
| 58 | + "$ref": "#/components/schemas/TodoItem" |
| 59 | + } |
| 60 | + } |
| 61 | + }, |
| 62 | + "application/json": { |
| 63 | + "schema": { |
| 64 | + "type": "array", |
| 65 | + "items": { |
| 66 | + "$ref": "#/components/schemas/TodoItem" |
| 67 | + } |
| 68 | + } |
| 69 | + }, |
| 70 | + "text/json": { |
| 71 | + "schema": { |
| 72 | + "type": "array", |
| 73 | + "items": { |
| 74 | + "$ref": "#/components/schemas/TodoItem" |
| 75 | + } |
| 76 | + } |
| 77 | + } |
| 78 | + } |
| 79 | + } |
| 80 | + } |
| 81 | + }, |
| 82 | + "post": { |
| 83 | + "tags": [ |
| 84 | + "Todo" |
| 85 | + ], |
| 86 | + "operationId": "createToDoListItem", |
| 87 | + "requestBody": { |
| 88 | + "content": { |
| 89 | + "application/json": { |
| 90 | + "schema": { |
| 91 | + "$ref": "#/components/schemas/TodoItem" |
| 92 | + } |
| 93 | + }, |
| 94 | + "text/json": { |
| 95 | + "schema": { |
| 96 | + "$ref": "#/components/schemas/TodoItem" |
| 97 | + } |
| 98 | + }, |
| 99 | + "application/*+json": { |
| 100 | + "schema": { |
| 101 | + "$ref": "#/components/schemas/TodoItem" |
| 102 | + } |
| 103 | + } |
| 104 | + } |
| 105 | + }, |
| 106 | + "responses": { |
| 107 | + "200": { |
| 108 | + "description": "Success", |
| 109 | + "content": { |
| 110 | + "text/plain": { |
| 111 | + "schema": { |
| 112 | + "$ref": "#/components/schemas/TodoItem" |
| 113 | + } |
| 114 | + }, |
| 115 | + "application/json": { |
| 116 | + "schema": { |
| 117 | + "$ref": "#/components/schemas/TodoItem" |
| 118 | + } |
| 119 | + }, |
| 120 | + "text/json": { |
| 121 | + "schema": { |
| 122 | + "$ref": "#/components/schemas/TodoItem" |
| 123 | + } |
| 124 | + } |
| 125 | + } |
| 126 | + } |
| 127 | + } |
| 128 | + } |
| 129 | + }, |
| 130 | + "/api/Todo/{id}": { |
| 131 | + "get": { |
| 132 | + "tags": [ |
| 133 | + "Todo" |
| 134 | + ], |
| 135 | + "operationId": "getToDoListItemById", |
| 136 | + "parameters": [ |
| 137 | + { |
| 138 | + "name": "id", |
| 139 | + "in": "path", |
| 140 | + "required": true, |
| 141 | + "schema": { |
| 142 | + "type": "integer", |
| 143 | + "format": "int64" |
| 144 | + } |
| 145 | + } |
| 146 | + ], |
| 147 | + "responses": { |
| 148 | + "200": { |
| 149 | + "description": "Success", |
| 150 | + "content": { |
| 151 | + "text/plain": { |
| 152 | + "schema": { |
| 153 | + "$ref": "#/components/schemas/TodoItem" |
| 154 | + } |
| 155 | + }, |
| 156 | + "application/json": { |
| 157 | + "schema": { |
| 158 | + "$ref": "#/components/schemas/TodoItem" |
| 159 | + } |
| 160 | + }, |
| 161 | + "text/json": { |
| 162 | + "schema": { |
| 163 | + "$ref": "#/components/schemas/TodoItem" |
| 164 | + } |
| 165 | + } |
| 166 | + } |
| 167 | + } |
| 168 | + } |
| 169 | + }, |
| 170 | + "put": { |
| 171 | + "tags": [ |
| 172 | + "Todo" |
| 173 | + ], |
| 174 | + "operationId": "updateToDoListItem", |
| 175 | + "parameters": [ |
| 176 | + { |
| 177 | + "name": "id", |
| 178 | + "in": "path", |
| 179 | + "required": true, |
| 180 | + "schema": { |
| 181 | + "type": "integer", |
| 182 | + "format": "int64" |
| 183 | + } |
| 184 | + } |
| 185 | + ], |
| 186 | + "requestBody": { |
| 187 | + "content": { |
| 188 | + "application/json": { |
| 189 | + "schema": { |
| 190 | + "$ref": "#/components/schemas/TodoItem" |
| 191 | + } |
| 192 | + }, |
| 193 | + "text/json": { |
| 194 | + "schema": { |
| 195 | + "$ref": "#/components/schemas/TodoItem" |
| 196 | + } |
| 197 | + }, |
| 198 | + "application/*+json": { |
| 199 | + "schema": { |
| 200 | + "$ref": "#/components/schemas/TodoItem" |
| 201 | + } |
| 202 | + } |
| 203 | + } |
| 204 | + }, |
| 205 | + "responses": { |
| 206 | + "200": { |
| 207 | + "description": "Success" |
| 208 | + } |
| 209 | + } |
| 210 | + }, |
| 211 | + "delete": { |
| 212 | + "tags": [ |
| 213 | + "Todo" |
| 214 | + ], |
| 215 | + "operationId": "deleteToDoListItem", |
| 216 | + "parameters": [ |
| 217 | + { |
| 218 | + "name": "id", |
| 219 | + "in": "path", |
| 220 | + "required": true, |
| 221 | + "schema": { |
| 222 | + "type": "integer", |
| 223 | + "format": "int64" |
| 224 | + } |
| 225 | + } |
| 226 | + ], |
| 227 | + "responses": { |
| 228 | + "200": { |
| 229 | + "description": "Success", |
| 230 | + "content": { |
| 231 | + "text/plain": { |
| 232 | + "schema": { |
| 233 | + "$ref": "#/components/schemas/TodoItem" |
| 234 | + } |
| 235 | + }, |
| 236 | + "application/json": { |
| 237 | + "schema": { |
| 238 | + "$ref": "#/components/schemas/TodoItem" |
| 239 | + } |
| 240 | + }, |
| 241 | + "text/json": { |
| 242 | + "schema": { |
| 243 | + "$ref": "#/components/schemas/TodoItem" |
| 244 | + } |
| 245 | + } |
| 246 | + } |
| 247 | + } |
| 248 | + } |
| 249 | + } |
| 250 | + } |
| 251 | + }, |
| 252 | + "components": { |
| 253 | + "schemas": { |
| 254 | + "TodoItem": { |
| 255 | + "type": "object", |
| 256 | + "properties": { |
| 257 | + "id": { |
| 258 | + "type": "integer", |
| 259 | + "format": "int64" |
| 260 | + }, |
| 261 | + "name": { |
| 262 | + "type": "string", |
| 263 | + "nullable": true |
| 264 | + }, |
| 265 | + "isComplete": { |
| 266 | + "type": "boolean" |
| 267 | + } |
| 268 | + }, |
| 269 | + "additionalProperties": false |
| 270 | + } |
| 271 | + }, |
| 272 | + "securitySchemes": {} |
| 273 | + }, |
| 274 | + "security": [ |
| 275 | + { |
| 276 | + "apiKeyHeader": [] |
| 277 | + } |
| 278 | + ] |
| 279 | + } |
| 280 | + ``` |
| 281 | + |
| 282 | +1. Review the file to understand the API and its endpoints. The `operationId` values are CRUD operations that can be performed on the to-do list. Once this is up and running, you can use your AI Agent to invoke the API and perform the various operations on your behalf using natural language. At the end of the specification, you'll see the `security` section. This is where you add authentication if your API requires it. This section is left blank for the sample app, but is included because it's required by the AI Agent Service. |
| 283 | + |
| 284 | +### Create the OpenAPI Spec tool definition |
| 285 | + |
| 286 | +1. Create a file in the same directory as your `swagger.json` file called `tool.py`. Copy the following contents into the file. Ensure you've completed the prerequisites and setup steps in the [quickstart](/azure/ai-services/agents/quickstart.md) to get the required packages installed as well as get you logged into your Azure account. |
| 287 | + |
| 288 | + ```python |
| 289 | + import os |
| 290 | + import jsonref |
| 291 | + from azure.ai.projects import AIProjectClient |
| 292 | + from azure.identity import DefaultAzureCredential |
| 293 | + from azure.ai.projects.models import OpenApiTool, OpenApiAnonymousAuthDetails |
| 294 | + |
| 295 | + # Set the environment variable for your connection string, copied from your Azure AI Foundry project |
| 296 | + os.environ["PROJECT_CONNECTION_STRING"] = "<HostName>;<AzureSubscriptionId>;<ResourceGroup>;<HubName>" |
| 297 | + |
| 298 | + # Create an Azure AI Client from a connection string |
| 299 | + project_client = AIProjectClient.from_connection_string( |
| 300 | + credential=DefaultAzureCredential(), conn_str=os.environ["PROJECT_CONNECTION_STRING"] |
| 301 | + ) |
| 302 | + |
| 303 | + # Read and print the OpenAPI spec |
| 304 | + with open('./swagger.json', 'r') as f: |
| 305 | + openapi_spec = jsonref.loads(f.read()) |
| 306 | + |
| 307 | + # Create Auth object for the OpenApiTool (note that connection or managed identity auth setup requires additional setup in Azure) |
| 308 | + auth = OpenApiAnonymousAuthDetails() |
| 309 | + |
| 310 | + # Initialize agent OpenAPI tool using the read in OpenAPI spec |
| 311 | + # openapi = OpenApiTool(name="GetWeatherForecast", spec=openapi_spec, description="Retrieve weather information", auth=auth) |
| 312 | + openapi = OpenApiTool(name="toDolistAgent", spec=openapi_spec, description="Manage the to do list", auth=auth) |
| 313 | + |
| 314 | + # Prompt for the message content |
| 315 | + message_content = input("Message content: ") |
| 316 | + |
| 317 | + # Create agent with OpenAPI tool and process assistant run |
| 318 | + with project_client: |
| 319 | + agent = project_client.agents.create_agent( |
| 320 | + model="gpt-4o-mini", |
| 321 | + name="my-assistant", |
| 322 | + instructions="You are a helpful assistant", |
| 323 | + tools=openapi.definitions |
| 324 | + ) |
| 325 | + print(f"Created agent, ID: {agent.id}") |
| 326 | + |
| 327 | + # Create thread for communication |
| 328 | + thread = project_client.agents.create_thread() |
| 329 | + print(f"Created thread, ID: {thread.id}") |
| 330 | + |
| 331 | + # Create message to thread |
| 332 | + message = project_client.agents.create_message( |
| 333 | + thread_id=thread.id, |
| 334 | + role="user", |
| 335 | + content=message_content, |
| 336 | + ) |
| 337 | + print(f"Created message, ID: {message.id}") |
| 338 | + |
| 339 | + # Create and process agent run in thread with tools |
| 340 | + run = project_client.agents.create_and_process_run(thread_id=thread.id, assistant_id=agent.id) |
| 341 | + print(f"Run finished with status: {run.status}") |
| 342 | + |
| 343 | + if run.status == "failed": |
| 344 | + # Check if you got "Rate limit is exceeded.", then you want to get more quota |
| 345 | + print(f"Run failed: {run.last_error}") |
| 346 | + |
| 347 | + # Fetch and log all messages |
| 348 | + messages = project_client.agents.list_messages(thread_id=thread.id) |
| 349 | + # print(f"Messages: {messages}") |
| 350 | + |
| 351 | + # Get the last message from the sender |
| 352 | + last_msg = messages.get_last_text_message_by_role("assistant") |
| 353 | + if last_msg: |
| 354 | + print(f"Last Message: {last_msg.text.value}") |
| 355 | + |
| 356 | + # Delete the assistant when done |
| 357 | + project_client.agents.delete_agent(agent.id) |
| 358 | + print("Deleted agent") |
| 359 | + ``` |
| 360 | + |
| 361 | +1. Replace the placeholder for your project's connection string. If you need help with this, see the [Configure and run agent section of the quickstart](/azure/ai-services/agents/quickstart.md#configure-and-run-agent) for details on how to get your connection string. |
| 362 | +1. Review the file to understand how the OpenAPI tool is created and how the AI Agent is invoked. Each time time the tool is invoked, the AI Agent will use the OpenAPI specification to determine how to interact with the API. The OpenAPI specification is passed into the file. The `message_content` variable is where you enter the natural language command that you want the AI Agent to perform. You're prompted to enter the message once you run the script. The AI Agent will then invoke the API and return the results. It will create and delete the AI Agent each time you run the script. |
| 363 | + |
| 364 | +## Run the OpenAPI Spec tool |
| 365 | + |
| 366 | +1. Open a terminal and navigate to the directory where you created the `tool.py` and `swagger.json` files. Run the following command to run the script. |
| 367 | + |
| 368 | + ```bash |
| 369 | + python tool.py |
| 370 | + ``` |
| 371 | + |
| 372 | +1. When prompted, enter a message for your Agent. For example, you can enter "What's on my to-do list?", "Add a task to buy bread", "Mark all my tasks as done", or any other question or action that can be performed on the list. The AI Agent then invokes the API and returns the results. You can also enter `Add an item to the to do list` and the AI Agent prompts you for the details of the item to add. |
0 commit comments