Skip to content

Commit f7281a3

Browse files
authored
Merge pull request #20 from petehauge/initial-sample-changes
Update File Search sample & other updates
2 parents d722a16 + 75ced0f commit f7281a3

File tree

3 files changed

+264
-7
lines changed

3 files changed

+264
-7
lines changed

doc-samples/agents/c#/azure-ai-agent-sdk/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ This table tracks the current status of code samples for each supported tool in
1616
| Tool | Sample Description | Status | Notes / Known Issues |
1717
|--------------------|-----------------------------------------|-----------------|-----------------------------------------------|
1818
| **Basic Agent** | Using agent with no tools | ❌ Doesn't exist| |
19-
| **Bing** | Using Bing in an agent | ❌ Doesn't exist| |
20-
| **File Search** | Uploading files | ❌ Doesn't exist| |
19+
| **Bing** | Using Bing in an agent | ⚠️ Exists| Sample updated - works with project connection string, but not with project endpoint |
20+
| **File Search** | Uploading files | ⚠️ Exists| Sample updated - works with project connection string, but not with project endpoint |
2121
| | Using blob storage (project data assets)| ❌ Doesn't exist| |
2222
| | Managing files | ❌ Doesn't exist| |
2323
| **Azure AI Search**| Using a knowledge store | ❌ Doesn't exist| |

doc-samples/agents/c#/azure-ai-agent-sdk/Sample4_PersistentAgents_Bing_Grounding.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ var connectionId = System.Environment.GetEnvironmentVariable("AZURE_BING_CONECTI
1515
PersistentAgentsClient agentClient = new(projectEndpoint, new DefaultAzureCredential());
1616
```
1717

18-
2. We will use the Bing connection ID to initialize the `BingGroundingToolDefinition`.
18+
2. We will use the Bing connection Id to initialize the `BingGroundingToolDefinition`.
1919

2020
```C# Snippet:AgentsBingGrounding_GetConnection
2121
ToolConnectionList connectionList = new()
@@ -47,7 +47,7 @@ PersistentAgent agent = await agentClient.CreateAgentAsync(
4747
tools: [ bingGroundingTool ]);
4848
```
4949

50-
4. Now we will create the thread, add the message , containing a question for agent and start the run.
50+
4. Now we will create the thread, add the message containing a question for agent and start the run.
5151

5252
Synchronous sample:
5353
```C# Snippet:AgentsBingGrounding_CreateThreadMessage
@@ -105,15 +105,17 @@ if (run.Status != RunStatus.Completed)
105105

106106
```
107107

108-
5. Print the agent messages to console in chronological order.
108+
5. Print the agent messages to console in chronological order (including formatting URL citations).
109109

110110
Synchronous sample:
111111
```C# Snippet:AgentsBingGrounding_Print
112+
// Retrieve all messages from the agent client
112113
PageableList<ThreadMessage> messages = agentClient.GetMessages(
113114
threadId: thread.Id,
114115
order: ListSortOrder.Ascending
115116
);
116117

118+
// Process messages in order
117119
foreach (ThreadMessage threadMessage in messages)
118120
{
119121
Console.Write($"{threadMessage.CreatedAt:yyyy-MM-dd HH:mm:ss} - {threadMessage.Role,10}: ");
@@ -122,6 +124,8 @@ foreach (ThreadMessage threadMessage in messages)
122124
if (contentItem is MessageTextContent textItem)
123125
{
124126
string response = textItem.Text;
127+
128+
// If we have Text URL citation annotations, reformat the response to show title & URL for citations
125129
if (textItem.Annotations != null)
126130
{
127131
foreach (MessageTextAnnotation annotation in textItem.Annotations)
@@ -185,14 +189,14 @@ foreach (ThreadMessage threadMessage in messages)
185189
6. Clean up resources by deleting thread and agent.
186190

187191
Synchronous sample:
188-
```C# Snippet:AgentsBingGroundingCleanup
192+
```C# Snippet:AgentsBingGrounding_Cleanup
189193
// Delete thread and agent
190194
agentClient.DeleteThread(threadId: thread.Id);
191195
agentClient.DeleteAgent(agentId: agent.Id);
192196
```
193197

194198
Asynchronous sample:
195-
```C# Snippet:AgentsBingGroundingCleanupAsync
199+
```C# Snippet:AgentsBingGroundingAsync_Cleanup
196200
// Delete thread and agent
197201
await agentClient.DeleteThreadAsync(threadId: thread.Id);
198202
await agentClient.DeleteAgentAsync(agentId: agent.Id);
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
# Sample file search with agent in Azure.AI.Agents.
2+
3+
In this example we will create the local file, upload it to the newly created `VectorStore`, which will be used in the file search.
4+
5+
1. First we need to create agent client and read the environment variables that will be used in the next steps.
6+
```C# Snippet:AgentsFileSearch_CreateClient
7+
// Get Connection information from Environment Variables
8+
// To use App Config instead: https://learn.microsoft.com/en-us/visualstudio/ide/managing-application-settings-dotnet
9+
var projectEndpoint = System.Environment.GetEnvironmentVariable("PROJECT_ENDPOINT");
10+
var modelDeploymentName = System.Environment.GetEnvironmentVariable("MODEL_DEPLOYMENT_NAME");
11+
12+
// Create the Agent Client
13+
PersistentAgentsClient client = new(projectEndpoint, new DefaultAzureCredential());
14+
```
15+
16+
2. Now we will create a local file and upload it to the data store.
17+
18+
Synchronous sample:
19+
```C# Snippet:AgentsFileSearch_UploadAgentFiles
20+
// Upload a file and wait for it to be processed
21+
// Create a local sample file
22+
System.IO.File.WriteAllText(
23+
path: "sample_file_for_upload.txt",
24+
contents: "The word 'apple' uses the code 442345, while the word 'banana' uses the code 673457."
25+
);
26+
27+
// Upload local sample file to the agent
28+
PersistentAgentFile uploadedAgentFile = agentClient.UploadFile(
29+
filePath: "sample_file_for_upload.txt",
30+
purpose: PersistentAgentFilePurpose.Agents
31+
);
32+
33+
// Setup dictionary with list of File IDs for the vector store
34+
Dictionary<string, string> fileIds = new()
35+
{
36+
{ uploadedAgentFile.Id, uploadedAgentFile.Filename }
37+
};
38+
```
39+
40+
Asynchronous sample:
41+
```C# Snippet:AgentsFileSearchAsync_UploadAgentFiles
42+
// Create a local sample file
43+
await System.IO.File.WriteAllTextAsync(
44+
path: "sample_file_for_upload.txt",
45+
contents: "The word 'apple' uses the code 442345, while the word 'banana' uses the code 673457."
46+
);
47+
48+
// Upload local sample file to the agent
49+
PersistentAgentFile uploadedAgentFile = await agentClient.UploadFileAsync(
50+
filePath: "sample_file_for_upload.txt",
51+
purpose: PersistentAgentFilePurpose.Agents
52+
);
53+
54+
// Setup dictionary with list of File IDs for the vector store
55+
Dictionary<string, string> fileIds = new()
56+
{
57+
{ uploadedAgentFile.Id, uploadedAgentFile.Filename }
58+
};
59+
```
60+
61+
3. To create agent capable of using file search, we will create `VectorStore`, with the ID of uploaded file.
62+
63+
Synchronous sample:
64+
```C# Snippet:AgentsFileSearch_CreateVectorStore
65+
// Create a vector store with the file and wait for it to be processed.
66+
// If you do not specify a vector store, CreateMessage will create a vector
67+
// store with a default expiration policy of seven days after they were last active
68+
VectorStore vectorStore = agentClient.CreateVectorStore(
69+
fileIds: new List<string> { uploadedAgentFile.Id },
70+
name: "my_vector_store");
71+
72+
```
73+
74+
Asynchronous sample:
75+
```C# Snippet:AgentsFileSearchAsync_CreateVectorStore
76+
// Create a vector store with the file and wait for it to be processed.
77+
// If you do not specify a vector store, CreateMessage will create a vector
78+
// store with a default expiration policy of seven days after they were last active
79+
VectorStore vectorStore = await agentClient.CreateVectorStoreAsync(
80+
fileIds: new List<string> { uploadedAgentFile.Id },
81+
name: "my_vector_store");
82+
83+
```
84+
85+
4. Create the `FileSearchToolResource` using the Id of the created vector store and then create the agent
86+
87+
Synchronous sample:
88+
```C# Snippet:AgentsFileSearch_CreateAgentWithFiles
89+
// Create tool definition for File Search
90+
FileSearchToolResource fileSearchToolResource = new FileSearchToolResource();
91+
fileSearchToolResource.VectorStoreIds.Add(vectorStore.Id);
92+
93+
// Create an agent with Tools and Tool Resources
94+
PersistentAgent agent = agentClient.CreateAgent(
95+
model: modelDeploymentName,
96+
name: "SDK Test Agent - Retrieval",
97+
instructions: "You are a helpful agent that can help fetch data from files you know about.",
98+
tools: new List<ToolDefinition> { new FileSearchToolDefinition() },
99+
toolResources: new ToolResources() { FileSearch = fileSearchToolResource });
100+
```
101+
102+
Asynchronous sample:
103+
```C# Snippet:AgentsFileSearchAsync_CreateAgentWithFiles
104+
// Create tool definition for File Search
105+
FileSearchToolResource fileSearchToolResource = new FileSearchToolResource();
106+
fileSearchToolResource.VectorStoreIds.Add(vectorStore.Id);
107+
108+
// Create an agent with Tools and Tool Resources
109+
PersistentAgent agent = await agentClient.CreateAgentAsync(
110+
model: modelDeploymentName,
111+
name: "SDK Test Agent - Retrieval",
112+
instructions: "You are a helpful agent that can help fetch data from files you know about.",
113+
tools: new List<ToolDefinition> { new FileSearchToolDefinition() },
114+
toolResources: new ToolResources() { FileSearch = fileSearchToolResource });
115+
```
116+
117+
5. Now we will create the thread, add the message containing a question for agent related to the file contents and start the run.
118+
119+
Synchronous sample:
120+
```C# Snippet:AgentsFileSearch_CreateThreadAndRun
121+
// Create the agent thread for communication
122+
PersistentAgentThread thread = agentClient.CreateThread();
123+
124+
// Create message and run the agent
125+
ThreadMessage messageResponse = agentClient.CreateMessage(
126+
thread.Id,
127+
MessageRole.User,
128+
"Can you give me the documented codes for 'banana' and 'orange'?");
129+
130+
ThreadRun run = agentClient.CreateRun(thread, agent);
131+
132+
// Wait for the agent to finish running
133+
do
134+
{
135+
Thread.Sleep(TimeSpan.FromMilliseconds(500));
136+
run = agentClient.GetRun(thread.Id, run.Id);
137+
}
138+
while (run.Status == RunStatus.Queued
139+
|| run.Status == RunStatus.InProgress);
140+
141+
// Confirm that the run completed successfully
142+
if (run.Status != RunStatus.Completed)
143+
{
144+
throw new Exception("Run did not complete successfully, error: " + run.LastError?.Message);
145+
}
146+
147+
```
148+
149+
Asynchronous sample:
150+
```C# Snippet:AgentsFileSearchAsync_CreateThreadAndRun
151+
// Create the agent thread for communication
152+
PersistentAgentThread thread = await agentClient.CreateThreadAsync();
153+
154+
// Create message and run the agent
155+
ThreadMessage messageResponse = await agentClient.CreateMessageAsync(
156+
thread.Id,
157+
MessageRole.User,
158+
"Can you give me the documented codes for 'banana' and 'orange'?");
159+
160+
ThreadRun run = await agentClient.CreateRunAsync(thread, agent);
161+
162+
// Wait for the agent to finish running
163+
do
164+
{
165+
await Task.Delay(TimeSpan.FromMilliseconds(500));
166+
run = await agentClient.GetRunAsync(thread.Id, run.Id);
167+
}
168+
while (run.Status == RunStatus.Queued
169+
|| run.Status == RunStatus.InProgress);
170+
171+
// Confirm that the run completed successfully
172+
if (run.Status != RunStatus.Completed)
173+
{
174+
throw new Exception("Run did not complete successfully, error: " + run.LastError?.Message);
175+
}
176+
177+
```
178+
179+
6. Print the agent messages to console in chronological order (including formatting file citations).
180+
```C# Snippet:AgentsFileSearch_Print
181+
182+
// Retrieve all messages from the agent client
183+
PageableList<ThreadMessage> messages = agentClient.GetMessages(
184+
threadId: thread.Id,
185+
order: ListSortOrder.Ascending
186+
);
187+
188+
// Helper method for replacing references
189+
static string replaceReferences(Dictionary<string, string> fileIds, string fileID, string placeholder, string text)
190+
{
191+
if (fileIds.TryGetValue(fileID, out string replacement))
192+
return text.Replace(placeholder, $" [{replacement}]");
193+
else
194+
return text.Replace(placeholder, $" [{fileID}]");
195+
}
196+
197+
// Process messages in order
198+
foreach (ThreadMessage threadMessage in messages)
199+
{
200+
Console.Write($"{threadMessage.CreatedAt:yyyy-MM-dd HH:mm:ss} - {threadMessage.Role,10}: ");
201+
202+
foreach (MessageContent contentItem in threadMessage.ContentItems)
203+
{
204+
if (contentItem is MessageTextContent textItem)
205+
{
206+
if (threadMessage.Role == MessageRole.Agent && textItem.Annotations.Count > 0)
207+
{
208+
string strMessage = textItem.Text;
209+
210+
// If we file path or file citation annotations - rewrite the 'source' FileId with the file name
211+
foreach (MessageTextAnnotation annotation in textItem.Annotations)
212+
{
213+
if (annotation is MessageTextFilePathAnnotation pathAnnotation)
214+
{
215+
strMessage = replaceReferences(fileIds, pathAnnotation.FileId, pathAnnotation.Text, strMessage);
216+
}
217+
else if (annotation is MessageTextFileCitationAnnotation citationAnnotation)
218+
{
219+
strMessage = replaceReferences(fileIds, citationAnnotation.FileId, citationAnnotation.Text, strMessage);
220+
}
221+
}
222+
Console.Write(strMessage);
223+
}
224+
else
225+
{
226+
Console.Write(textItem.Text);
227+
}
228+
}
229+
else if (contentItem is MessageImageFileContent imageFileItem)
230+
{
231+
Console.Write($"<image from ID: {imageFileItem.FileId}");
232+
}
233+
Console.WriteLine();
234+
}
235+
}
236+
```
237+
7. Finally, we delete all the resources created in this sample.
238+
239+
Synchronous sample:
240+
```C# Snippet:AgentsFileSearch_Cleanup
241+
client.DeleteVectorStore(vectorStore.Id);
242+
client.DeleteFile(uploadedAgentFile.Id);
243+
client.DeleteThread(thread.Id);
244+
client.DeleteAgent(agent.Id);
245+
```
246+
247+
Asynchronous sample:
248+
```C# Snippet:AgentsFileSearchAsync_Cleanup
249+
await client.DeleteVectorStoreAsync(vectorStore.Id);
250+
await client.DeleteFileAsync(uploadedAgentFile.Id);
251+
await client.DeleteThreadAsync(thread.Id);
252+
await client.DeleteAgentAsync(agent.Id);
253+
```

0 commit comments

Comments
 (0)