diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx index 92968ac153..320b2e27a9 100644 --- a/dotnet/agent-framework-dotnet.slnx +++ b/dotnet/agent-framework-dotnet.slnx @@ -176,6 +176,7 @@ + diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step18_FileSearch/FoundryAgents_Step18_FileSearch.csproj b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step18_FileSearch/FoundryAgents_Step18_FileSearch.csproj new file mode 100644 index 0000000000..4a34560946 --- /dev/null +++ b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step18_FileSearch/FoundryAgents_Step18_FileSearch.csproj @@ -0,0 +1,22 @@ + + + + Exe + net10.0 + + enable + enable + $(NoWarn);CA1812 + + + + + + + + + + + + + diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step18_FileSearch/Program.cs b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step18_FileSearch/Program.cs new file mode 100644 index 0000000000..7b133e0651 --- /dev/null +++ b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step18_FileSearch/Program.cs @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to use File Search Tool with AI Agents. + +using Azure.AI.Projects; +using Azure.AI.Projects.OpenAI; +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Extensions.AI; +using OpenAI.Assistants; +using OpenAI.Files; +using OpenAI.Responses; + +string endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); +string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +const string AgentInstructions = "You are a helpful assistant that can search through uploaded files to answer questions."; + +// Get a client to create/retrieve/delete server side agents with Azure Foundry Agents. +// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. +// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid +// latency issues, unintended credential probing, and potential security risks from fallback mechanisms. +AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential()); +var projectOpenAIClient = aiProjectClient.GetProjectOpenAIClient(); +var filesClient = projectOpenAIClient.GetProjectFilesClient(); +var vectorStoresClient = projectOpenAIClient.GetProjectVectorStoresClient(); + +// 1. Create a temp file with test content and upload it. +string searchFilePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + "_lookup.txt"); +File.WriteAllText( + path: searchFilePath, + contents: """ + Employee Directory: + - Alice Johnson, 28 years old, Software Engineer, Engineering Department + - Bob Smith, 35 years old, Sales Manager, Sales Department + - Carol Williams, 42 years old, HR Director, Human Resources Department + - David Brown, 31 years old, Customer Support Lead, Support Department + """ +); + +Console.WriteLine($"Uploading file: {searchFilePath}"); +OpenAIFile uploadedFile = filesClient.UploadFile( + filePath: searchFilePath, + purpose: FileUploadPurpose.Assistants +); +Console.WriteLine($"Uploaded file, file ID: {uploadedFile.Id}"); + +// 2. Create a vector store with the uploaded file. +var vectorStoreResult = await vectorStoresClient.CreateVectorStoreAsync( + options: new() { FileIds = { uploadedFile.Id }, Name = "EmployeeDirectory_VectorStore" } +); +string vectorStoreId = vectorStoreResult.Value.Id; +Console.WriteLine($"Created vector store, vector store ID: {vectorStoreId}"); + +AIAgent agent = await CreateAgentWithMEAI(); +// AIAgent agent = await CreateAgentWithNativeSDK(); + +// Run the agent +Console.WriteLine("\n--- Running File Search Agent ---"); +AgentResponse response = await agent.RunAsync("Who is the youngest employee?"); +Console.WriteLine($"Response: {response}"); + +// Getting any file citation annotations generated by the tool +foreach (AIAnnotation annotation in response.Messages.SelectMany(m => m.Contents).SelectMany(c => c.Annotations ?? [])) +{ + if (annotation.RawRepresentation is TextAnnotationUpdate citationAnnotation) + { + Console.WriteLine($$""" + File Citation: + File Id: {{citationAnnotation.OutputFileId}} + Text to Replace: {{citationAnnotation.TextToReplace}} + """); + } +} + +// Cleanup. +Console.WriteLine("\n--- Cleanup ---"); +await aiProjectClient.Agents.DeleteAgentAsync(agent.Name); +await vectorStoresClient.DeleteVectorStoreAsync(vectorStoreId); +await filesClient.DeleteFileAsync(uploadedFile.Id); +File.Delete(searchFilePath); +Console.WriteLine("Cleanup completed successfully."); + +// --- Agent Creation Options --- + +#pragma warning disable CS8321 // Local function is declared but never used +// Option 1 - Using HostedFileSearchTool (MEAI + AgentFramework) +async Task CreateAgentWithMEAI() +{ + return await aiProjectClient.CreateAIAgentAsync( + model: deploymentName, + name: "FileSearchAgent-MEAI", + instructions: AgentInstructions, + tools: [new HostedFileSearchTool() { Inputs = [new HostedVectorStoreContent(vectorStoreId)] }]); +} + +// Option 2 - Using PromptAgentDefinition with ResponseTool.CreateFileSearchTool (Native SDK) +async Task CreateAgentWithNativeSDK() +{ + return await aiProjectClient.CreateAIAgentAsync( + name: "FileSearchAgent-NATIVE", + creationOptions: new AgentVersionCreationOptions( + new PromptAgentDefinition(model: deploymentName) + { + Instructions = AgentInstructions, + Tools = { + ResponseTool.CreateFileSearchTool(vectorStoreIds: [vectorStoreId]) + } + }) + ); +} diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step18_FileSearch/README.md b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step18_FileSearch/README.md new file mode 100644 index 0000000000..e8f431ba44 --- /dev/null +++ b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step18_FileSearch/README.md @@ -0,0 +1,52 @@ +# Using File Search with AI Agents + +This sample demonstrates how to use the file search tool with AI agents. The file search tool allows agents to search through uploaded files stored in vector stores to answer user questions. + +## What this sample demonstrates + +- Uploading files and creating vector stores +- Creating agents with file search capabilities +- Using HostedFileSearchTool (MEAI abstraction) +- Using native SDK file search tools (ResponseTool.CreateFileSearchTool) +- Handling file citation annotations +- Managing agent and resource lifecycle (creation and deletion) + +## Prerequisites + +Before you begin, ensure you have the following prerequisites: + +- .NET 10 SDK or later +- Azure Foundry service endpoint and deployment configured +- Azure CLI installed and authenticated (for Azure credential authentication) + +**Note**: This demo uses `DefaultAzureCredential` for authentication. For local development, make sure you're logged in with `az login` and have access to the Azure Foundry resource. For more information, see the [Azure Identity documentation](https://learn.microsoft.com/dotnet/api/azure.identity.defaultazurecredential). + +Set the following environment variables: + +```powershell +$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint +$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +``` + +## Run the sample + +Navigate to the FoundryAgents sample directory and run: + +```powershell +cd dotnet/samples/GettingStarted/FoundryAgents +dotnet run --project .\FoundryAgents_Step18_FileSearch +``` + +## Expected behavior + +The sample will: + +1. Create a temporary text file with employee directory information +2. Upload the file to Azure Foundry +3. Create a vector store with the uploaded file +4. Create an agent with file search capabilities using one of: + - Option 1: Using HostedFileSearchTool (MEAI abstraction) + - Option 2: Using native SDK file search tools +5. Run a query against the agent to search through the uploaded file +6. Display file citation annotations from responses +7. Clean up resources (agent, vector store, and uploaded file) diff --git a/dotnet/samples/GettingStarted/FoundryAgents/README.md b/dotnet/samples/GettingStarted/FoundryAgents/README.md index 4b297105aa..20e9e8683f 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/README.md +++ b/dotnet/samples/GettingStarted/FoundryAgents/README.md @@ -58,6 +58,7 @@ Before you begin, ensure you have the following prerequisites: |[Using plugins](./FoundryAgents_Step13_Plugins/)|This sample demonstrates how to use plugins with a Foundry agent| |[Code interpreter](./FoundryAgents_Step14_CodeInterpreter/)|This sample demonstrates how to use the code interpreter tool with a Foundry agent| |[Computer use](./FoundryAgents_Step15_ComputerUse/)|This sample demonstrates how to use computer use capabilities with a Foundry agent| +|[File search](./FoundryAgents_Step18_FileSearch/)|This sample demonstrates how to use the file search tool with a Foundry agent| |[Local MCP](./FoundryAgents_Step27_LocalMCP/)|This sample demonstrates how to use a local MCP client with a Foundry agent| ## Evaluation Samples