|
| 1 | +# Sample using agents with Azure AI Search tool in Azure.AI.Agents. |
| 2 | + |
| 3 | +Azure AI Search is an enterprise search system for high-performance applications. |
| 4 | +It integrates with Azure OpenAI Service and Azure Machine Learning, offering advanced |
| 5 | +search technologies like vector search and full-text search. Ideal for knowledge base |
| 6 | +insights, information discovery, and automation. Creating an agent with Azure AI |
| 7 | +Search requires an existing Azure AI Search Index. For more information and setup |
| 8 | +guides, see [Azure AI Search Tool Guide](https://learn.microsoft.com/azure/ai-services/agents/how-to/tools/azure-ai-search). |
| 9 | +In this example we will use the existing Azure AI Search Index as a tool for an agent. |
| 10 | + |
| 11 | +1. First we need to create an agent and read the configuration, which will be used in the next steps. |
| 12 | +```C# Snippet:AgentsAzureAISearchExample_CreateProjectClient |
| 13 | +var projectEndpoint = configuration["ProjectEndpoint"]; |
| 14 | +var modelDeploymentName = configuration["ModelDeploymentName"]; |
| 15 | +var azureAiSearchConnectionId = configuration["AzureAiSearchConnectionId"]; |
| 16 | +``` |
| 17 | + |
| 18 | +2. Create an agent with `AzureAISearchToolDefinition` and `ToolResources` with the only member `AzureAISearchResource` to be able to perform search. We will use `azureAiSearchConnectionId` to get the Azure AI Search resource. |
| 19 | + |
| 20 | +Synchronous sample: |
| 21 | +```C# Snippet:AgentsCreateAgentWithAzureAISearchTool_Sync |
| 22 | +AzureAISearchResource searchResource = new( |
| 23 | + indexConnectionId: azureAiSearchConnectionId, |
| 24 | + indexName: "sample_index", |
| 25 | + topK: 5, |
| 26 | + filter: "category eq 'sleeping bag'", |
| 27 | + queryType: AzureAISearchQueryType.Simple |
| 28 | +); |
| 29 | + |
| 30 | +ToolResources toolResource = new() { AzureAISearch = searchResource }; |
| 31 | + |
| 32 | +// Create the Agent Client |
| 33 | +PersistentAgentsClient agentClient = new( |
| 34 | + projectEndpoint, |
| 35 | + new DefaultAzureCredential(), |
| 36 | + new PersistentAgentsAdministrationClientOptions( |
| 37 | + PersistentAgentsAdministrationClientOptions.ServiceVersion.V2025_05_01 |
| 38 | + )); |
| 39 | + |
| 40 | +// Create an agent with Tools and Tool Resources |
| 41 | +PersistentAgent agent = agentClient.Administration.CreateAgent( |
| 42 | + model: modelDeploymentName, |
| 43 | + name: "my-agent", |
| 44 | + instructions: "You are a helpful agent.", |
| 45 | + tools: [new AzureAISearchToolDefinition()], |
| 46 | + toolResources: toolResource); |
| 47 | +``` |
| 48 | + |
| 49 | +Asynchronous sample: |
| 50 | +```C# Snippet:AgentsCreateAgentWithAzureAISearchTool |
| 51 | +AzureAISearchResource searchResource = new( |
| 52 | + indexConnectionId: azureAiSearchConnectionId, |
| 53 | + indexName: "sample_index", |
| 54 | + topK: 5, |
| 55 | + filter: "category eq 'sleeping bag'", |
| 56 | + queryType: AzureAISearchQueryType.Simple |
| 57 | +); |
| 58 | + |
| 59 | +ToolResources toolResource = new() { AzureAISearch = searchResource }; |
| 60 | + |
| 61 | +// Create the Agent Client |
| 62 | +PersistentAgentsClient agentClient = new( |
| 63 | + projectEndpoint, |
| 64 | + new DefaultAzureCredential(), |
| 65 | + new PersistentAgentsAdministrationClientOptions( |
| 66 | + PersistentAgentsAdministrationClientOptions.ServiceVersion.V2025_05_01 |
| 67 | + )); |
| 68 | + |
| 69 | +// Create an agent with Tools and Tool Resources |
| 70 | +PersistentAgent agent = await agentClient.Administration.CreateAgentAsync( |
| 71 | + model: modelDeploymentName, |
| 72 | + name: "my-agent", |
| 73 | + instructions: "You are a helpful agent.", |
| 74 | + tools: [new AzureAISearchToolDefinition()], |
| 75 | + toolResources: toolResource); |
| 76 | +``` |
| 77 | + |
| 78 | +3. Now we will create a `Thread`, add a `Message` and `Run` to run the agent, then wait until the run completes. If the run will not be successful, we will print the last error. |
| 79 | + |
| 80 | +Synchronous sample: |
| 81 | +```C# Snippet:AgentsAzureAISearchExample_CreateRun_Sync |
| 82 | +// Create thread for communication |
| 83 | +PersistentAgentThread thread = agentClient.Threads.CreateThread(); |
| 84 | + |
| 85 | +// Create message and run the agent |
| 86 | +ThreadMessage message = agentClient.Messages.CreateMessage( |
| 87 | + thread.Id, |
| 88 | + MessageRole.User, |
| 89 | + "What is the temperature rating of the cozynights sleeping bag?"); |
| 90 | +ThreadRun run = agentClient.Runs.CreateRun(thread, agent); |
| 91 | + |
| 92 | +// Wait for the agent to finish running |
| 93 | +do |
| 94 | +{ |
| 95 | + Thread.Sleep(TimeSpan.FromMilliseconds(500)); |
| 96 | + run = agentClient.Runs.GetRun(thread.Id, run.Id); |
| 97 | +} |
| 98 | +while (run.Status == RunStatus.Queued |
| 99 | + || run.Status == RunStatus.InProgress); |
| 100 | + |
| 101 | +// Confirm that the run completed successfully |
| 102 | +if (run.Status != RunStatus.Completed) |
| 103 | +{ |
| 104 | + throw new Exception("Run did not complete successfully, error: " + run.LastError?.Message); |
| 105 | +} |
| 106 | +``` |
| 107 | + |
| 108 | +Asynchronous sample: |
| 109 | +```C# Snippet:AgentsAzureAISearchExample_CreateRun |
| 110 | +// Create thread for communication |
| 111 | +PersistentAgentThread thread = await agentClient.Threads.CreateThreadAsync(); |
| 112 | + |
| 113 | +// Create message and run the agent |
| 114 | +ThreadMessage message = await agentClient.Messages.CreateMessageAsync( |
| 115 | + thread.Id, |
| 116 | + MessageRole.User, |
| 117 | + "What is the temperature rating of the cozynights sleeping bag?"); |
| 118 | +ThreadRun run = await agentClient.Runs.CreateRunAsync(thread, agent); |
| 119 | + |
| 120 | +// Wait for the agent to finish running |
| 121 | +do |
| 122 | +{ |
| 123 | + await Task.Delay(TimeSpan.FromMilliseconds(500)); |
| 124 | + run = await agentClient.Runs.GetRunAsync(thread.Id, run.Id); |
| 125 | +} |
| 126 | +while (run.Status == RunStatus.Queued |
| 127 | + || run.Status == RunStatus.InProgress); |
| 128 | + |
| 129 | +// Confirm that the run completed successfully |
| 130 | +if (run.Status != RunStatus.Completed) |
| 131 | +{ |
| 132 | + throw new Exception("Run did not complete successfully, error: " + run.LastError?.Message); |
| 133 | +} |
| 134 | +``` |
| 135 | + |
| 136 | +4. In our search we have used an index containing "embedding", "token", "category" and also "title" fields. This allowed us to get reference title and url. In the code below, we iterate messages in chronological order and replace the reference placeholders by url and title. |
| 137 | + |
| 138 | +Synchronous sample: |
| 139 | +```C# Snippet:AgentsPopulateReferencesAgentWithAzureAISearchTool_Sync |
| 140 | +// Retrieve the messages from the agent client |
| 141 | +Pageable<ThreadMessage> messages = agentClient.Messages.GetMessages( |
| 142 | + threadId: thread.Id, |
| 143 | + order: ListSortOrder.Ascending |
| 144 | +); |
| 145 | + |
| 146 | +// Process messages in order |
| 147 | +foreach (ThreadMessage threadMessage in messages) |
| 148 | +{ |
| 149 | + Console.Write($"{threadMessage.CreatedAt:yyyy-MM-dd HH:mm:ss} - {threadMessage.Role,10}: "); |
| 150 | + foreach (MessageContent contentItem in threadMessage.ContentItems) |
| 151 | + { |
| 152 | + if (contentItem is MessageTextContent textItem) |
| 153 | + { |
| 154 | + // We need to annotate only Agent messages. |
| 155 | + if (threadMessage.Role == MessageRole.Agent && textItem.Annotations.Count > 0) |
| 156 | + { |
| 157 | + string annotatedText = textItem.Text; |
| 158 | + |
| 159 | + // If we have Text URL citation annotations, reformat the response to show title & URL for citations |
| 160 | + foreach (MessageTextAnnotation annotation in textItem.Annotations) |
| 161 | + { |
| 162 | + if (annotation is MessageTextUrlCitationAnnotation urlAnnotation) |
| 163 | + { |
| 164 | + annotatedText = annotatedText.Replace( |
| 165 | + urlAnnotation.Text, |
| 166 | + $" [see {urlAnnotation.UrlCitation.Title}] ({urlAnnotation.UrlCitation.Url})"); |
| 167 | + } |
| 168 | + } |
| 169 | + Console.Write(annotatedText); |
| 170 | + } |
| 171 | + else |
| 172 | + { |
| 173 | + Console.Write(textItem.Text); |
| 174 | + } |
| 175 | + } |
| 176 | + else if (contentItem is MessageImageFileContent imageFileItem) |
| 177 | + { |
| 178 | + Console.Write($"<image from ID: {imageFileItem.FileId}"); |
| 179 | + } |
| 180 | + Console.WriteLine(); |
| 181 | + } |
| 182 | +} |
| 183 | +``` |
| 184 | + |
| 185 | +Asynchronous sample: |
| 186 | +```C# Snippet:AgentsPopulateReferencesAgentWithAzureAISearchTool |
| 187 | +// Retrieve the messages from the agent client |
| 188 | +AsyncPageable<ThreadMessage> messages = agentClient.Messages.GetMessagesAsync( |
| 189 | + threadId: thread.Id, |
| 190 | + order: ListSortOrder.Ascending |
| 191 | +); |
| 192 | + |
| 193 | +// Process messages in order |
| 194 | +await foreach (ThreadMessage threadMessage in messages) |
| 195 | +{ |
| 196 | + Console.Write($"{threadMessage.CreatedAt:yyyy-MM-dd HH:mm:ss} - {threadMessage.Role,10}: "); |
| 197 | + foreach (MessageContent contentItem in threadMessage.ContentItems) |
| 198 | + { |
| 199 | + if (contentItem is MessageTextContent textItem) |
| 200 | + { |
| 201 | + // We need to annotate only Agent messages. |
| 202 | + if (threadMessage.Role == MessageRole.Agent && textItem.Annotations.Count > 0) |
| 203 | + { |
| 204 | + string annotatedText = textItem.Text; |
| 205 | + |
| 206 | + // If we have Text URL citation annotations, reformat the response to show title & URL for citations |
| 207 | + foreach (MessageTextAnnotation annotation in textItem.Annotations) |
| 208 | + { |
| 209 | + if (annotation is MessageTextUrlCitationAnnotation urlAnnotation) |
| 210 | + { |
| 211 | + annotatedText = annotatedText.Replace( |
| 212 | + urlAnnotation.Text, |
| 213 | + $" [see {urlAnnotation.UrlCitation.Title}] ({urlAnnotation.UrlCitation.Url})"); |
| 214 | + } |
| 215 | + } |
| 216 | + Console.Write(annotatedText); |
| 217 | + } |
| 218 | + else |
| 219 | + { |
| 220 | + Console.Write(textItem.Text); |
| 221 | + } |
| 222 | + } |
| 223 | + else if (contentItem is MessageImageFileContent imageFileItem) |
| 224 | + { |
| 225 | + Console.Write($"<image from ID: {imageFileItem.FileId}"); |
| 226 | + } |
| 227 | + Console.WriteLine(); |
| 228 | + } |
| 229 | +} |
| 230 | +``` |
| 231 | + |
| 232 | +5. Finally, we delete all the resources, we have created in this sample. |
| 233 | + |
| 234 | +Synchronous sample: |
| 235 | +```C# Snippet:AgentsAzureAISearchExample_Cleanup_Sync |
| 236 | +// Clean up resources |
| 237 | +agentClient.Threads.DeleteThread(thread.Id); |
| 238 | +agentClient.Administration.DeleteAgent(agent.Id); |
| 239 | +``` |
| 240 | + |
| 241 | +Asynchronous sample: |
| 242 | +```C# Snippet:AgentsAzureAISearchExample_Cleanup |
| 243 | +// Clean up resources |
| 244 | +await agentClient.Threads.DeleteThreadAsync(thread.Id); |
| 245 | +await agentClient.Administration.DeleteAgentAsync(agent.Id); |
| 246 | +``` |
0 commit comments