diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx index 5da4e166d5..95812435d8 100644 --- a/dotnet/agent-framework-dotnet.slnx +++ b/dotnet/agent-framework-dotnet.slnx @@ -60,6 +60,7 @@ + diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step17_BackgroundResponses/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step17_BackgroundResponses/Program.cs index dbf86cf480..456d968c34 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step17_BackgroundResponses/Program.cs +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step17_BackgroundResponses/Program.cs @@ -1,6 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. -// This sample shows how to use background responses with ChatClientAgent and OpenAI Responses. +// This sample shows how to use background responses with ChatClientAgent and Azure OpenAI Responses. using Azure.AI.OpenAI; using Azure.Identity; diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step17_BackgroundResponses/README.md b/dotnet/samples/GettingStarted/Agents/Agent_Step17_BackgroundResponses/README.md index 64414a7f1e..5b7df74ca9 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step17_BackgroundResponses/README.md +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step17_BackgroundResponses/README.md @@ -1,6 +1,6 @@ # What This Sample Shows -This sample demonstrates how to use background responses with ChatCompletionAgent and OpenAI Responses for long-running operations. Background responses support: +This sample demonstrates how to use background responses with ChatCompletionAgent and Azure OpenAI Responses for long-running operations. Background responses support: - **Polling for completion** - Non-streaming APIs can start a background operation and return a continuation token. Poll with the token until the response completes. - **Resuming after interruption** - Streaming APIs can be interrupted and resumed from the last update using the continuation token. @@ -14,7 +14,10 @@ For more information, see the [official documentation](https://learn.microsoft.c Before you begin, ensure you have the following prerequisites: - .NET 8.0 SDK or later -- OpenAI api key +- Azure OpenAI service endpoint and deployment configured +- Azure CLI installed and authenticated (for Azure credential authentication) + +**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Azure OpenAI resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively). Set the following environment variables: diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step20_BackgroundResponsesWithToolsAndPersistence/Agent_Step20_BackgroundResponsesWithToolsAndPersistence.csproj b/dotnet/samples/GettingStarted/Agents/Agent_Step20_BackgroundResponsesWithToolsAndPersistence/Agent_Step20_BackgroundResponsesWithToolsAndPersistence.csproj new file mode 100644 index 0000000000..4735f4a7a0 --- /dev/null +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step20_BackgroundResponsesWithToolsAndPersistence/Agent_Step20_BackgroundResponsesWithToolsAndPersistence.csproj @@ -0,0 +1,20 @@ + + + + Exe + net9.0 + + enable + enable + + + + + + + + + + + + diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step20_BackgroundResponsesWithToolsAndPersistence/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step20_BackgroundResponsesWithToolsAndPersistence/Program.cs new file mode 100644 index 0000000000..f2a3bdf5c0 --- /dev/null +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step20_BackgroundResponsesWithToolsAndPersistence/Program.cs @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample demonstrates how to use background responses with ChatClientAgent and Azure OpenAI Responses for long-running operations. +// It shows polling for completion using continuation tokens, function calling during background operations, +// and persisting/restoring agent state between polling cycles. + +#pragma warning disable CA1050 // Declare types in namespaces + +using System.ComponentModel; +using System.Text.Json; +using Azure.AI.OpenAI; +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Extensions.AI; +using OpenAI; + +var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); +var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-5"; + +var stateStore = new Dictionary(); + +AIAgent agent = new AzureOpenAIClient( + new Uri(endpoint), + new AzureCliCredential()) + .GetOpenAIResponseClient(deploymentName) + .CreateAIAgent( + name: "SpaceNovelWriter", + instructions: "You are a space novel writer. Always research relevant facts and generate character profiles for the main characters before writing novels." + + "Write complete chapters without asking for approval or feedback. Do not ask the user about tone, style, pace, or format preferences - just write the novel based on the request.", + tools: [AIFunctionFactory.Create(ResearchSpaceFactsAsync), AIFunctionFactory.Create(GenerateCharacterProfilesAsync)]); + +// Enable background responses (only supported by {Azure}OpenAI Responses at this time). +AgentRunOptions options = new() { AllowBackgroundResponses = true }; + +AgentThread thread = agent.GetNewThread(); + +// Start the initial run. +AgentRunResponse response = await agent.RunAsync("Write a very long novel about a team of astronauts exploring an uncharted galaxy.", thread, options); + +// Poll for background responses until complete. +while (response.ContinuationToken is not null) +{ + PersistAgentState(thread, response.ContinuationToken); + + await Task.Delay(TimeSpan.FromSeconds(10)); + + RestoreAgentState(agent, out thread, out object? continuationToken); + + options.ContinuationToken = continuationToken; + response = await agent.RunAsync(thread, options); +} + +Console.WriteLine(response.Text); + +void PersistAgentState(AgentThread thread, object? continuationToken) +{ + stateStore["thread"] = thread.Serialize(); + stateStore["continuationToken"] = JsonSerializer.SerializeToElement(continuationToken, AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(ResponseContinuationToken))); +} + +void RestoreAgentState(AIAgent agent, out AgentThread thread, out object? continuationToken) +{ + JsonElement serializedThread = stateStore["thread"] ?? throw new InvalidOperationException("No serialized thread found in state store."); + JsonElement? serializedToken = stateStore["continuationToken"]; + + thread = agent.DeserializeThread(serializedThread); + continuationToken = serializedToken?.Deserialize(AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(ResponseContinuationToken))); +} + +[Description("Researches relevant space facts and scientific information for writing a science fiction novel")] +async Task ResearchSpaceFactsAsync(string topic) +{ + Console.WriteLine($"[ResearchSpaceFacts] Researching topic: {topic}"); + + // Simulate a research operation + await Task.Delay(TimeSpan.FromSeconds(10)); + + string result = topic.ToUpperInvariant() switch + { + var t when t.Contains("GALAXY") => "Research findings: Galaxies contain billions of stars. Uncharted galaxies may have unique stellar formations, exotic matter, and unexplored phenomena like dark energy concentrations.", + var t when t.Contains("SPACE") || t.Contains("TRAVEL") => "Research findings: Interstellar travel requires advanced propulsion systems. Challenges include radiation exposure, life support, and navigation through unknown space.", + var t when t.Contains("ASTRONAUT") => "Research findings: Astronauts undergo rigorous training in zero-gravity environments, emergency protocols, spacecraft systems, and team dynamics for long-duration missions.", + _ => $"Research findings: General space exploration facts related to {topic}. Deep space missions require advanced technology, crew resilience, and contingency planning for unknown scenarios." + }; + + Console.WriteLine("[ResearchSpaceFacts] Research complete"); + return result; +} + +[Description("Generates character profiles for the main astronaut characters in the novel")] +async Task> GenerateCharacterProfilesAsync() +{ + Console.WriteLine("[GenerateCharacterProfiles] Generating character profiles..."); + + // Simulate a character generation operation + await Task.Delay(TimeSpan.FromSeconds(10)); + + string[] profiles = [ + "Captain Elena Voss: A seasoned mission commander with 15 years of experience. Strong-willed and decisive, she struggles with the weight of responsibility for her crew. Former military pilot turned astronaut.", + "Dr. James Chen: Chief science officer and astrophysicist. Brilliant but socially awkward, he finds solace in data and discovery. His curiosity often pushes the mission into uncharted territory.", + "Lieutenant Maya Torres: Navigation specialist and youngest crew member. Optimistic and tech-savvy, she brings fresh perspective and innovative problem-solving to challenges.", + "Commander Marcus Rivera: Chief engineer with expertise in spacecraft systems. Pragmatic and resourceful, he can fix almost anything with limited resources. Values crew safety above all.", + "Dr. Amara Okafor: Medical officer and psychologist. Empathetic and observant, she helps maintain crew morale and mental health during the long journey. Expert in space medicine." + ]; + + Console.WriteLine($"[GenerateCharacterProfiles] Generated {profiles.Length} character profiles"); + return profiles; +} diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step20_BackgroundResponsesWithToolsAndPersistence/README.md b/dotnet/samples/GettingStarted/Agents/Agent_Step20_BackgroundResponsesWithToolsAndPersistence/README.md new file mode 100644 index 0000000000..146f418512 --- /dev/null +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step20_BackgroundResponsesWithToolsAndPersistence/README.md @@ -0,0 +1,28 @@ +# What This Sample Shows + +This sample demonstrates how to use background responses with ChatCompletionAgent and Azure OpenAI Responses for long-running operations. Background responses support: + +- **Polling for completion** - Non-streaming APIs can start a background operation and return a continuation token. Poll with the token until the response completes. +- **Function calling** - Functions can be called during background operations. +- **State persistence** - Thread and continuation token can be persisted and restored between polling cycles. + +> **Note:** Background responses are currently only supported by OpenAI Responses. + +For more information, see the [official documentation](https://learn.microsoft.com/en-us/agent-framework/user-guide/agents/agent-background-responses?pivots=programming-language-csharp). + +# Prerequisites + +Before you begin, ensure you have the following prerequisites: + +- .NET 8.0 SDK or later +- Azure OpenAI service endpoint and deployment configured +- Azure CLI installed and authenticated (for Azure credential authentication) + +**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Azure OpenAI resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively). + +Set the following environment variables: + +```powershell +$env:AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/" # Replace with your Azure OpenAI resource endpoint +$env:AZURE_OPENAI_DEPLOYMENT_NAME="gpt-5" # Optional, defaults to gpt-5 +``` diff --git a/dotnet/samples/GettingStarted/Agents/README.md b/dotnet/samples/GettingStarted/Agents/README.md index 2b8f7550c9..bf5c291f9e 100644 --- a/dotnet/samples/GettingStarted/Agents/README.md +++ b/dotnet/samples/GettingStarted/Agents/README.md @@ -45,6 +45,7 @@ Before you begin, ensure you have the following prerequisites: |[Background responses](./Agent_Step17_BackgroundResponses/)|This sample demonstrates how to use background responses for long-running operations with polling and resumption support| |[Adding RAG with text search](./Agent_Step18_TextSearchRag/)|This sample demonstrates how to enrich agent responses with retrieval augmented generation using the text search provider| |[Using Mem0-backed memory](./Agent_Step19_Mem0Provider/)|This sample demonstrates how to use the Mem0Provider to persist and recall memories across conversations| +|[Background responses with tools and persistence](./Agent_Step20_BackgroundResponsesWithToolsAndPersistence/)|This sample demonstrates advanced background response scenarios including function calling during background operations and state persistence| ## Running the samples from the console