// Copyright (c) Microsoft. All rights reserved. using Azure.AI.OpenAI; using Azure.Identity; using Microsoft.Agents.AI; using Microsoft.Agents.AI.Workflows; using Microsoft.Extensions.AI; namespace WorkflowAsAnAgentSample; /// /// This sample introduces the concept of workflows as agents, where a workflow can be /// treated as an . This allows you to interact with a workflow /// as if it were a single agent. /// /// In this example, we create a workflow that uses two language agents to process /// input concurrently, one that responds in French and another that responds in English. /// /// You will interact with the workflow in an interactive loop, sending messages and receiving /// streaming responses from the workflow as if it were an agent who responds in both languages. /// /// This sample also demonstrates , which is required /// for stateful executors that are shared across multiple workflow runs. Each iteration /// of the interactive loop triggers a new workflow run against the same workflow instance. /// Between runs, the framework automatically calls /// on shared executors so that accumulated state (e.g., collected messages) is cleared /// before the next run begins. See WorkflowFactory.ConcurrentAggregationExecutor /// for the implementation. /// /// /// Pre-requisites: /// - Foundational samples should be completed first. /// - This sample uses concurrent processing. /// - An Azure OpenAI endpoint and deployment name. /// public static class Program { private static async Task Main() { // Set up the Azure OpenAI client 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.4-mini"; var chatClient = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential()).GetChatClient(deploymentName).AsIChatClient(); // Create the workflow and turn it into an agent var workflow = WorkflowFactory.BuildWorkflow(chatClient); var agent = workflow.AsAIAgent("workflow-agent", "Workflow Agent"); var session = await agent.CreateSessionAsync(); // Start an interactive loop to interact with the workflow as if it were an agent. // Each iteration runs the workflow again on the same workflow instance. Between runs, // the framework calls IResettableExecutor.ResetAsync() on shared stateful executors // (like ConcurrentAggregationExecutor) to clear accumulated state from the previous run. while (true) { Console.WriteLine(); Console.Write("User (or 'exit' to quit): "); string? input = Console.ReadLine(); if (string.IsNullOrWhiteSpace(input) || input.Equals("exit", StringComparison.OrdinalIgnoreCase)) { break; } await ProcessInputAsync(agent, session, input); } // Helper method to process user input and display streaming responses. To display // multiple interleaved responses correctly, we buffer updates by message ID and // re-render all messages on each update. static async Task ProcessInputAsync(AIAgent agent, AgentSession? session, string input) { Dictionary> buffer = []; await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(input, session)) { if (update.MessageId is null || string.IsNullOrEmpty(update.Text)) { // skip updates that don't have a message ID or text continue; } Console.Clear(); if (!buffer.TryGetValue(update.MessageId, out List? value)) { value = []; buffer[update.MessageId] = value; } value.Add(update); foreach (var (messageId, segments) in buffer) { string combinedText = string.Concat(segments); Console.WriteLine($"{segments[0].AuthorName}: {combinedText}"); Console.WriteLine(); } } } } }