|
| 1 | +# Sample file search with agent in Azure.AI.Projects. |
| 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:FilesSearchExample_CreateClient |
| 7 | +var connectionString = System.Environment.GetEnvironmentVariable("PROJECT_CONNECTION_STRING"); |
| 8 | +var modelDeploymentName = System.Environment.GetEnvironmentVariable("MODEL_DEPLOYMENT_NAME"); |
| 9 | +AgentsClient client = new(connectionString, new DefaultAzureCredential()); |
| 10 | +``` |
| 11 | + |
| 12 | +2. Now we will create a file and upload it to the data store. |
| 13 | + |
| 14 | +Synchronous sample: |
| 15 | +```C# Snippet:UploadAgentFilesToUse_Sync |
| 16 | +// Upload a file and wait for it to be processed |
| 17 | +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 | +AgentFile uploadedAgentFile = client.UploadFile( |
| 21 | + filePath: "sample_file_for_upload.txt", |
| 22 | + purpose: AgentFilePurpose.Agents); |
| 23 | +Dictionary<string, string> fileIds = new() |
| 24 | +{ |
| 25 | + { uploadedAgentFile.Id, uploadedAgentFile.Filename } |
| 26 | +}; |
| 27 | +``` |
| 28 | + |
| 29 | +Asynchronous sample: |
| 30 | +```C# Snippet:UploadAgentFilesToUse |
| 31 | +// Upload a file and wait for it to be processed |
| 32 | +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 | +AgentFile uploadedAgentFile = await client.UploadFileAsync( |
| 36 | + filePath: "sample_file_for_upload.txt", |
| 37 | + purpose: AgentFilePurpose.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:CreateVectorStore_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 | +VectorStore vectorStore = client.CreateVectorStore( |
| 51 | + fileIds: new List<string> { uploadedAgentFile.Id }, |
| 52 | + name: "my_vector_store"); |
| 53 | +``` |
| 54 | + |
| 55 | +Asynchronous sample: |
| 56 | +```C# Snippet:CreateVectorStore |
| 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 | +VectorStore vectorStore = await client.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` used for agent creation. |
| 66 | + |
| 67 | +Synchronous sample: |
| 68 | +```C# Snippet:CreateAgentWithFiles_Sync |
| 69 | +FileSearchToolResource fileSearchToolResource = new FileSearchToolResource(); |
| 70 | +fileSearchToolResource.VectorStoreIds.Add(vectorStore.Id); |
| 71 | + |
| 72 | +// Create an agent with toolResources and process assistant run |
| 73 | +Agent agent = client.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:CreateAgentWithFiles |
| 83 | +FileSearchToolResource fileSearchToolResource = new FileSearchToolResource(); |
| 84 | +fileSearchToolResource.VectorStoreIds.Add(vectorStore.Id); |
| 85 | + |
| 86 | +// Create an agent with toolResources and process assistant run |
| 87 | +Agent agent = await client.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 properly render the links to the file name we use the `WriteMessages` method, which internally calls `replaceReferences` method to replace reference placeholders by file IDs or by file names. |
| 96 | +```C# Snippet:FilesSearchExample_Print |
| 97 | +private static void WriteMessages(IEnumerable<ThreadMessage> messages, Dictionary<string, string> fileIds) |
| 98 | +{ |
| 99 | + foreach (ThreadMessage threadMessage in messages) |
| 100 | + { |
| 101 | + Console.Write($"{threadMessage.CreatedAt:yyyy-MM-dd HH:mm:ss} - {threadMessage.Role,10}: "); |
| 102 | + foreach (MessageContent contentItem in threadMessage.ContentItems) |
| 103 | + { |
| 104 | + if (contentItem is MessageTextContent textItem) |
| 105 | + { |
| 106 | + if (threadMessage.Role == MessageRole.Agent && textItem.Annotations.Count > 0) |
| 107 | + { |
| 108 | + string strMessage = textItem.Text; |
| 109 | + foreach (MessageTextAnnotation annotation in textItem.Annotations) |
| 110 | + { |
| 111 | + if (annotation is MessageTextFilePathAnnotation pathAnnotation) |
| 112 | + { |
| 113 | + strMessage = replaceReferences(fileIds, pathAnnotation.FileId, pathAnnotation.Text, strMessage); |
| 114 | + } |
| 115 | + else if (annotation is MessageTextFileCitationAnnotation citationAnnotation) |
| 116 | + { |
| 117 | + strMessage = replaceReferences(fileIds, citationAnnotation.FileId, citationAnnotation.Text, strMessage); |
| 118 | + } |
| 119 | + } |
| 120 | + Console.Write(strMessage); |
| 121 | + } |
| 122 | + else |
| 123 | + { |
| 124 | + Console.Write(textItem.Text); |
| 125 | + } |
| 126 | + } |
| 127 | + else if (contentItem is MessageImageFileContent imageFileItem) |
| 128 | + { |
| 129 | + Console.Write($"<image from ID: {imageFileItem.FileId}"); |
| 130 | + } |
| 131 | + Console.WriteLine(); |
| 132 | + } |
| 133 | + } |
| 134 | +} |
| 135 | + |
| 136 | +private static string replaceReferences(Dictionary<string, string> fileIds, string fileID, string placeholder, string text) |
| 137 | +{ |
| 138 | + if (fileIds.TryGetValue(fileID, out string replacement)) |
| 139 | + return text.Replace(placeholder, $" [{replacement}]"); |
| 140 | + else |
| 141 | + return text.Replace(placeholder, $" [{fileID}]"); |
| 142 | +} |
| 143 | +``` |
| 144 | + |
| 145 | +6. We will ask a question to the file contents and add it to the thread, create run and wait while it will terminate. If the run was successful, we will render the response and provide the reference to the uploaded file. |
| 146 | + |
| 147 | +Synchronous sample: |
| 148 | +```C# Snippet:FilesSearchExample_CreateThreadAndRun_Sync |
| 149 | +// Create thread for communication |
| 150 | +AgentThread thread = client.CreateThread(); |
| 151 | + |
| 152 | +// Create message to thread |
| 153 | +ThreadMessage messageResponse = client.CreateMessage( |
| 154 | + thread.Id, |
| 155 | + MessageRole.User, |
| 156 | + "Can you give me the documented codes for 'banana' and 'orange'?"); |
| 157 | + |
| 158 | +// Run the agent |
| 159 | +ThreadRun run = client.CreateRun(thread, agent); |
| 160 | + |
| 161 | +do |
| 162 | +{ |
| 163 | + Thread.Sleep(TimeSpan.FromMilliseconds(500)); |
| 164 | + run = client.GetRun(thread.Id, run.Id); |
| 165 | +} |
| 166 | +while (run.Status == RunStatus.Queued |
| 167 | + || run.Status == RunStatus.InProgress); |
| 168 | +Assert.AreEqual( |
| 169 | + RunStatus.Completed, |
| 170 | + run.Status, |
| 171 | + run.LastError?.Message); |
| 172 | +PageableList<ThreadMessage> messages = client.GetMessages( |
| 173 | + threadId: thread.Id, |
| 174 | + order: ListSortOrder.Ascending |
| 175 | +); |
| 176 | +WriteMessages(messages, fileIds); |
| 177 | +``` |
| 178 | + |
| 179 | +Asynchronous sample: |
| 180 | +```C# Snippet:FilesSearchExample_CreateThreadAndRun |
| 181 | +// Create thread for communication |
| 182 | +AgentThread thread = await client.CreateThreadAsync(); |
| 183 | + |
| 184 | +// Create message to thread |
| 185 | +ThreadMessage messageResponse = await client.CreateMessageAsync( |
| 186 | + thread.Id, |
| 187 | + MessageRole.User, |
| 188 | + "Can you give me the documented codes for 'banana' and 'orange'?"); |
| 189 | + |
| 190 | +// Run the agent |
| 191 | +ThreadRun run = await client.CreateRunAsync(thread, agent); |
| 192 | + |
| 193 | +do |
| 194 | +{ |
| 195 | + await Task.Delay(TimeSpan.FromMilliseconds(500)); |
| 196 | + run = await client.GetRunAsync(thread.Id, run.Id); |
| 197 | +} |
| 198 | +while (run.Status == RunStatus.Queued |
| 199 | + || run.Status == RunStatus.InProgress); |
| 200 | +Assert.AreEqual( |
| 201 | + RunStatus.Completed, |
| 202 | + run.Status, |
| 203 | + run.LastError?.Message); |
| 204 | +PageableList<ThreadMessage> messages = await client.GetMessagesAsync( |
| 205 | + threadId: thread.Id, |
| 206 | + order: ListSortOrder.Ascending |
| 207 | +); |
| 208 | +WriteMessages(messages, fileIds); |
| 209 | +``` |
| 210 | + |
| 211 | +7. Finally, we delete all the resources, we have created in this sample. |
| 212 | + |
| 213 | +Synchronous sample: |
| 214 | +```C# Snippet:FilesSearchExample_Cleanup_Sync |
| 215 | +client.DeleteVectorStore(vectorStore.Id); |
| 216 | +client.DeleteFile(uploadedAgentFile.Id); |
| 217 | +client.DeleteThread(thread.Id); |
| 218 | +client.DeleteAgent(agent.Id); |
| 219 | +``` |
| 220 | + |
| 221 | +Asynchronous sample: |
| 222 | +```C# Snippet:FilesSearchExample_Cleanup |
| 223 | +await client.DeleteVectorStoreAsync(vectorStore.Id); |
| 224 | +await client.DeleteFileAsync(uploadedAgentFile.Id); |
| 225 | +await client.DeleteThreadAsync(thread.Id); |
| 226 | +await client.DeleteAgentAsync(agent.Id); |
| 227 | +``` |
0 commit comments