Skip to content

Commit 1159863

Browse files
authored
Add example for file search with streaming and citation. (Azure#50567)
1 parent 27e4c99 commit 1159863

File tree

5 files changed

+445
-4
lines changed

5 files changed

+445
-4
lines changed

sdk/ai/Azure.AI.Agents.Persistent/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
### Sample updates
1414
- The Azure function sample was simplified.
15+
- Added samples for file search citation with streaming.
1516

1617
## 1.1.0-beta.2 (2025-06-04)
1718

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
# Sample file search with agent in Azure.AI.Agents.Persistent.
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. In this example we will stream the result.
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:AgentsFilesSearchStreamingExample_CreateClient
7+
var projectEndpoint = System.Environment.GetEnvironmentVariable("PROJECT_ENDPOINT");
8+
var modelDeploymentName = System.Environment.GetEnvironmentVariable("MODEL_DEPLOYMENT_NAME");
9+
PersistentAgentsClient client = new(projectEndpoint, new DefaultAzureCredential());
10+
```
11+
12+
2. Create a file and upload it to the data store.
13+
14+
Synchronous sample:
15+
```C# Snippet:AgentsUploadAgentFilesToUseStreaming_Sync
16+
// Upload a file and wait for it to be processed
17+
System.IO.File.WriteAllText(
18+
path: "sample_file_for_upload.txt",
19+
contents: "The word 'apple' uses the code 442345, while the word 'banana' uses the code 673457.");
20+
PersistentAgentFileInfo uploadedAgentFile = client.Files.UploadFile(
21+
filePath: "sample_file_for_upload.txt",
22+
purpose: PersistentAgentFilePurpose.Agents);
23+
Dictionary<string, string> fileIds = new()
24+
{
25+
{ uploadedAgentFile.Id, uploadedAgentFile.Filename }
26+
};
27+
```
28+
29+
Asynchronous sample:
30+
```C# Snippet:AgentsUploadAgentFilesToUseStreaming
31+
// Upload a file and wait for it to be processed
32+
System.IO.File.WriteAllText(
33+
path: "sample_file_for_upload.txt",
34+
contents: "The word 'apple' uses the code 442345, while the word 'banana' uses the code 673457.");
35+
PersistentAgentFileInfo uploadedAgentFile = await client.Files.UploadFileAsync(
36+
filePath: "sample_file_for_upload.txt",
37+
purpose: PersistentAgentFilePurpose.Agents);
38+
Dictionary<string, string> fileIds = new()
39+
{
40+
{ uploadedAgentFile.Id, uploadedAgentFile.Filename }
41+
};
42+
```
43+
44+
3. To create agent capable of using file search, we will create `VectorStore`, with the ID of uploaded file.
45+
46+
Synchronous sample:
47+
```C# Snippet:AgentsCreateVectorStoreStreaming_Sync
48+
// Create a vector store with the file and wait for it to be processed.
49+
// If you do not specify a vector store, create_message will create a vector store with a default expiration policy of seven days after they were last active
50+
PersistentAgentsVectorStore vectorStore = client.VectorStores.CreateVectorStore(
51+
fileIds: new List<string> { uploadedAgentFile.Id },
52+
name: "my_vector_store");
53+
```
54+
55+
Asynchronous sample:
56+
```C# Snippet:AgentsCreateVectorStoreStreaming
57+
// Create a vector store with the file and wait for it to be processed.
58+
// If you do not specify a vector store, create_message will create a vector store with a default expiration policy of seven days after they were last active
59+
PersistentAgentsVectorStore vectorStore = await client.VectorStores.CreateVectorStoreAsync(
60+
fileIds: new List<string> { uploadedAgentFile.Id },
61+
name: "my_vector_store");
62+
```
63+
64+
65+
4 The ID of the created vector store will be used in the `FileSearchToolResource` needed for agent creation.
66+
67+
Synchronous sample:
68+
```C# Snippet:AgentsCreateAgentWithFilesStreaming_Sync
69+
FileSearchToolResource fileSearchToolResource = new FileSearchToolResource();
70+
fileSearchToolResource.VectorStoreIds.Add(vectorStore.Id);
71+
72+
// Create an agent with toolResources and process agent run
73+
PersistentAgent agent = client.Administration.CreateAgent(
74+
model: modelDeploymentName,
75+
name: "SDK Test Agent - Retrieval",
76+
instructions: "You are a helpful agent that can help fetch data from files you know about.",
77+
tools: new List<ToolDefinition> { new FileSearchToolDefinition() },
78+
toolResources: new ToolResources() { FileSearch = fileSearchToolResource });
79+
```
80+
81+
Asynchronous sample:
82+
```C# Snippet:AgentsCreateAgentWithFilesStreaming
83+
FileSearchToolResource fileSearchToolResource = new FileSearchToolResource();
84+
fileSearchToolResource.VectorStoreIds.Add(vectorStore.Id);
85+
86+
// Create an agent with toolResources and process agent run
87+
PersistentAgent agent = await client.Administration.CreateAgentAsync(
88+
model: modelDeploymentName,
89+
name: "SDK Test Agent - Retrieval",
90+
instructions: "You are a helpful agent that can help fetch data from files you know about.",
91+
tools: new List<ToolDefinition> { new FileSearchToolDefinition() },
92+
toolResources: new ToolResources() { FileSearch = fileSearchToolResource });
93+
```
94+
95+
5. To parse and print stream output, we will use the `ParseStreamingUdate` method, which replaces reference placeholders by file IDs or by file names and also prints cited text.
96+
```C# Snippet:AgentsFilesSearchExampleStreaming_Print
97+
private static void ParseStreamingUdate(StreamingUpdate streamingUpdate, Dictionary<string, string> fileIds)
98+
{
99+
if (streamingUpdate.UpdateKind == StreamingUpdateReason.RunCreated)
100+
{
101+
Console.WriteLine("--- Run started! ---");
102+
}
103+
else if (streamingUpdate is MessageContentUpdate contentUpdate)
104+
{
105+
if (contentUpdate.TextAnnotation != null)
106+
{
107+
if (fileIds.TryGetValue(contentUpdate.TextAnnotation.InputFileId, out string annotation))
108+
{
109+
Console.Write($" [see {annotation}]");
110+
}
111+
else
112+
{
113+
Console.Write($" [see {contentUpdate.TextAnnotation.InputFileId}]");
114+
}
115+
}
116+
else
117+
{
118+
//Detect the reference placeholder and skip it. Instead we will print the actual reference.
119+
if (contentUpdate.Text[0] != (char)12304 || contentUpdate.Text[contentUpdate.Text.Length - 1] != (char)12305)
120+
Console.Write(contentUpdate.Text);
121+
}
122+
}
123+
else if (streamingUpdate.UpdateKind == StreamingUpdateReason.RunStepCompleted)
124+
{
125+
if (streamingUpdate is RunStepUpdate runStep)
126+
{
127+
if (runStep.Value.StepDetails is RunStepToolCallDetails toolCallDetails)
128+
{
129+
foreach (RunStepToolCall toolCall in toolCallDetails.ToolCalls)
130+
{
131+
if (toolCall is RunStepFileSearchToolCall fileSearh)
132+
{
133+
Console.WriteLine($"The search tool has found the next relevant content in the file {fileSearh.FileSearch.Results[0].FileName}:");
134+
Console.WriteLine(fileSearh.FileSearch.Results[0].Content[0].Text);
135+
Console.WriteLine("===============================================================");
136+
}
137+
}
138+
}
139+
}
140+
}
141+
else if (streamingUpdate.UpdateKind == StreamingUpdateReason.RunCompleted)
142+
{
143+
Console.WriteLine();
144+
Console.WriteLine("--- Run completed! ---");
145+
}
146+
else if (streamingUpdate.UpdateKind == StreamingUpdateReason.Error && streamingUpdate is RunUpdate errorStep)
147+
{
148+
throw new InvalidOperationException($"Error: {errorStep.Value.LastError}");
149+
}
150+
}
151+
```
152+
153+
6. Now we will create thread and message used to ask a question to an Agent.
154+
155+
Synchronous sample:
156+
```C# Snippet:AgentsFilesSearchExampleStreaming_CreateThreadMessage_Sync
157+
// Create thread for communication
158+
PersistentAgentThread thread = client.Threads.CreateThread();
159+
160+
// Create message to thread
161+
PersistentThreadMessage messageResponse = client.Messages.CreateMessage(
162+
thread.Id,
163+
MessageRole.User,
164+
"Can you give me the documented codes for 'banana' and 'orange'?");
165+
```
166+
167+
Asynchronous sample:
168+
```C# Snippet:AgentsFilesSearchExampleStreaming_CreateThreadMessage
169+
// Create thread for communication
170+
PersistentAgentThread thread = await client.Threads.CreateThreadAsync();
171+
172+
// Create message to thread
173+
PersistentThreadMessage messageResponse = await client.Messages.CreateMessageAsync(
174+
thread.Id,
175+
MessageRole.User,
176+
"Can you give me the documented codes for 'banana' and 'orange'?");
177+
```
178+
179+
7. Stream the results.
180+
181+
Synchronous sample:
182+
```C# Snippet:AgentsFilesSearchExampleStreaming_StreamResults_Sync
183+
// Create the stream and parse output
184+
CollectionResult<StreamingUpdate> stream = client.Runs.CreateRunStreaming(thread.Id, agent.Id, include: [RunAdditionalFieldList.FileSearchContents]);
185+
foreach (StreamingUpdate streamingUpdate in stream)
186+
{
187+
ParseStreamingUdate(streamingUpdate, fileIds);
188+
}
189+
```
190+
191+
Asynchronous sample:
192+
```C# Snippet:AgentsFilesSearchExampleStreaming_StreamResults
193+
// Create the stream and parse output.
194+
AsyncCollectionResult<StreamingUpdate> stream = client.Runs.CreateRunStreamingAsync(thread.Id, agent.Id, include: [RunAdditionalFieldList.FileSearchContents]);
195+
await foreach (StreamingUpdate streamingUpdate in stream)
196+
{
197+
ParseStreamingUdate(streamingUpdate, fileIds);
198+
}
199+
```
200+
201+
8. Finally, we delete all the resources, we have created in this sample.
202+
203+
Synchronous sample:
204+
```C# Snippet:AgentsFilesSearchExampleSteaming_Cleanup_Sync
205+
client.VectorStores.DeleteVectorStore(vectorStore.Id);
206+
client.Files.DeleteFile(uploadedAgentFile.Id);
207+
client.Threads.DeleteThread(thread.Id);
208+
client.Administration.DeleteAgent(agent.Id);
209+
```
210+
211+
Asynchronous sample:
212+
```C# Snippet:AgentsFilesSearchExampleSteaming_Cleanup
213+
await client.VectorStores.DeleteVectorStoreAsync(vectorStore.Id);
214+
await client.Files.DeleteFileAsync(uploadedAgentFile.Id);
215+
await client.Threads.DeleteThreadAsync(thread.Id);
216+
await client.Administration.DeleteAgentAsync(agent.Id);
217+
```

sdk/ai/Azure.AI.Agents.Persistent/samples/Sample6_PersistentAgents_FileSearch.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ foreach (RunStep runStep in client.Runs.GetRunSteps(
213213
{
214214
if (toolCall is RunStepFileSearchToolCall fileSearh)
215215
{
216-
Console.WriteLine($"The search tool have found the next relevant content in the file {fileSearh.FileSearch.Results[0].FileName}:");
216+
Console.WriteLine($"The search tool has found the next relevant content in the file {fileSearh.FileSearch.Results[0].FileName}:");
217217
Console.WriteLine(fileSearh.FileSearch.Results[0].Content[0].Text);
218218
Console.WriteLine("===============================================================");
219219
}
@@ -236,7 +236,7 @@ await foreach (RunStep runStep in client.Runs.GetRunStepsAsync(
236236
{
237237
if (toolCall is RunStepFileSearchToolCall fileSearh)
238238
{
239-
Console.WriteLine($"The search tool have found the next relevant content in the file {fileSearh.FileSearch.Results[0].FileName}:");
239+
Console.WriteLine($"The search tool has found the next relevant content in the file {fileSearh.FileSearch.Results[0].FileName}:");
240240
Console.WriteLine(fileSearh.FileSearch.Results[0].Content[0].Text);
241241
Console.WriteLine("===============================================================");
242242
}

sdk/ai/Azure.AI.Agents.Persistent/tests/Samples/Sample_PersistentAgents_FileSearch.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ public async Task FilesSearchExampleAsync()
102102
{
103103
if (toolCall is RunStepFileSearchToolCall fileSearh)
104104
{
105-
Console.WriteLine($"The search tool have found the next relevant content in the file {fileSearh.FileSearch.Results[0].FileName}:");
105+
Console.WriteLine($"The search tool has found the next relevant content in the file {fileSearh.FileSearch.Results[0].FileName}:");
106106
Console.WriteLine(fileSearh.FileSearch.Results[0].Content[0].Text);
107107
Console.WriteLine("===============================================================");
108108
}
@@ -211,7 +211,7 @@ public void FilesSearchExample()
211211
{
212212
if (toolCall is RunStepFileSearchToolCall fileSearh)
213213
{
214-
Console.WriteLine($"The search tool have found the next relevant content in the file {fileSearh.FileSearch.Results[0].FileName}:");
214+
Console.WriteLine($"The search tool has found the next relevant content in the file {fileSearh.FileSearch.Results[0].FileName}:");
215215
Console.WriteLine(fileSearh.FileSearch.Results[0].Content[0].Text);
216216
Console.WriteLine("===============================================================");
217217
}

0 commit comments

Comments
 (0)