|
| 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