Skip to content

Commit 1be4c6d

Browse files
author
Peter Hauge
committed
Updating Azure AI Search sample
1 parent 49a12a9 commit 1be4c6d

File tree

4 files changed

+477
-1
lines changed

4 files changed

+477
-1
lines changed
Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
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+
```
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
using Azure.AI.Agents.Persistent;
2+
using Azure.Identity;
3+
using Azure;
4+
using Microsoft.Extensions.Configuration;
5+
using System;
6+
using System.Threading.Tasks;
7+
8+
// Get Connection information from app configuration
9+
IConfigurationRoot configuration = new ConfigurationBuilder()
10+
.SetBasePath(AppContext.BaseDirectory)
11+
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
12+
.Build();
13+
14+
var projectEndpoint = configuration["ProjectEndpoint"];
15+
var modelDeploymentName = configuration["ModelDeploymentName"];
16+
var azureAiSearchConnectionId = configuration["AzureAiSearchConnectionId"];
17+
18+
AzureAISearchResource searchResource = new(
19+
indexConnectionId: azureAiSearchConnectionId,
20+
indexName: "sample_index",
21+
topK: 5,
22+
filter: "category eq 'sleeping bag'",
23+
queryType: AzureAISearchQueryType.Simple
24+
);
25+
26+
ToolResources toolResource = new() { AzureAISearch = searchResource };
27+
28+
// Create the Agent Client
29+
PersistentAgentsClient agentClient = new(
30+
projectEndpoint,
31+
new DefaultAzureCredential(),
32+
new PersistentAgentsAdministrationClientOptions(
33+
PersistentAgentsAdministrationClientOptions.ServiceVersion.V2025_05_01
34+
));
35+
36+
// Create an agent with Tools and Tool Resources
37+
PersistentAgent agent = await agentClient.Administration.CreateAgentAsync(
38+
model: modelDeploymentName,
39+
name: "my-agent",
40+
instructions: "You are a helpful agent.",
41+
tools: [new AzureAISearchToolDefinition()],
42+
toolResources: toolResource);
43+
44+
// Create thread for communication
45+
PersistentAgentThread thread = await agentClient.Threads.CreateThreadAsync();
46+
47+
// Create message and run the agent
48+
ThreadMessage message = await agentClient.Messages.CreateMessageAsync(
49+
thread.Id,
50+
MessageRole.User,
51+
"What is the temperature rating of the cozynights sleeping bag?");
52+
ThreadRun run = await agentClient.Runs.CreateRunAsync(thread, agent);
53+
54+
// Wait for the agent to finish running
55+
do
56+
{
57+
await Task.Delay(TimeSpan.FromMilliseconds(500));
58+
run = await agentClient.Runs.GetRunAsync(thread.Id, run.Id);
59+
}
60+
while (run.Status == RunStatus.Queued
61+
|| run.Status == RunStatus.InProgress);
62+
63+
// Confirm that the run completed successfully
64+
if (run.Status != RunStatus.Completed)
65+
{
66+
throw new Exception("Run did not complete successfully, error: " + run.LastError?.Message);
67+
}
68+
69+
// Retrieve the messages from the agent client
70+
AsyncPageable<ThreadMessage> messages = agentClient.Messages.GetMessagesAsync(
71+
threadId: thread.Id,
72+
order: ListSortOrder.Ascending
73+
);
74+
75+
// Process messages in order
76+
await foreach (ThreadMessage threadMessage in messages)
77+
{
78+
Console.Write($"{threadMessage.CreatedAt:yyyy-MM-dd HH:mm:ss} - {threadMessage.Role,10}: ");
79+
foreach (MessageContent contentItem in threadMessage.ContentItems)
80+
{
81+
if (contentItem is MessageTextContent textItem)
82+
{
83+
// We need to annotate only Agent messages.
84+
if (threadMessage.Role == MessageRole.Agent && textItem.Annotations.Count > 0)
85+
{
86+
string annotatedText = textItem.Text;
87+
88+
// If we have Text URL citation annotations, reformat the response to show title & URL for citations
89+
foreach (MessageTextAnnotation annotation in textItem.Annotations)
90+
{
91+
if (annotation is MessageTextUrlCitationAnnotation urlAnnotation)
92+
{
93+
annotatedText = annotatedText.Replace(
94+
urlAnnotation.Text,
95+
$" [see {urlAnnotation.UrlCitation.Title}] ({urlAnnotation.UrlCitation.Url})");
96+
}
97+
}
98+
Console.Write(annotatedText);
99+
}
100+
else
101+
{
102+
Console.Write(textItem.Text);
103+
}
104+
}
105+
else if (contentItem is MessageImageFileContent imageFileItem)
106+
{
107+
Console.Write($"<image from ID: {imageFileItem.FileId}");
108+
}
109+
Console.WriteLine();
110+
}
111+
}
112+
113+
// Clean up resources
114+
await agentClient.Threads.DeleteThreadAsync(thread.Id);
115+
await agentClient.Administration.DeleteAgentAsync(agent.Id);

0 commit comments

Comments
 (0)