Skip to main content

Command Palette

Search for a command to run...

Multi Agent Orchestration through Azure Durable Functions in Microsoft Agent Framework

Updated
5 min read
Multi Agent Orchestration through Azure Durable  Functions in Microsoft Agent Framework
S
From Synapse Analytics, Power BI, Spark, Microsoft Fabric,ASP.NET Core and recently Agentic AI on .NET I try to explore, learn and share all aspects of Microsoft Data Stack in this blog.

In my previous article, I introduced the process of setting up a single durable agent using the Azure Durable Framework and the Docker-based Durable Task Scheduler (DTS) Emulator.

This article will be a step ahead and is focused on setting up a Multi Agent Orchestration based on the similar principal.

The use case in this article is a multi-agent workflow involving two agents.

The first agent, the Football Content Creator Agent is responsible for generating a review of a football club. The second agent, the Football Content Reviewer Agent evaluates and reviews the generated content and produces a more refined version of the original review generated by Content Creator Agent.

I had used a similar use case in one of my earlier article that deep dived into workflow orchestration pattern.

https://www.azureguru.net/workflow-orchestration-patterns-in-microsoft-agent-framework#group-chat-orchestration-pattern

In this example, you could use the same agent for content creation and review but I used two separate agents just to give an idea on how execution of durable multi agents can be orchestrated .

SetUp

To get started, create a new Azure Function project and apply settings covered in my previous article including setting up of the Docker DTS Emulator.

Ensure Docker DTS Emulator is up and navigate to http://localhost:8082/ to ensure that it is running.

Code

After the above artifacts are in place, add the following code to read the settings from appsettings.json in Program.cs of the project.

var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false)
.Build();

Read the credentials and register a Chatclient

var credential = new AzureKeyCredential(configuration["AppSettings:ApiKey"]);

ServiceCollection servicecollection = new();

builder.Services.AddKeyedChatClient(
    "ChatClient",
    (
        sp =>
            new AzureOpenAIClient(new Uri(configuration["AppSettings:EndPoint"]), credential)
                .GetChatClient(configuration["AppSettings:Chat_DeploymentName"])
                .AsIChatClient()
    )
);

In the next step, register the following two AIAgents in the hosted DI container.

FootballContentCreatorAgent >>

servicecollection.AddSingleton<ChatClientAgent>(sp =>

{
    Func<ChatClientAgentOptions> func = () =>
   {
       return new ChatClientAgentOptions
       {
           ChatOptions = new ChatOptions
           {
                Instructions = "You are a content creator. You create content for football teams. Be concise and please stick to the topic.",
           },
           Name = "FootballContentCreatorAgent",
           Id = "1"

       };
   };

    return new ChatClientAgent(sp.GetKeyedService<IChatClient>("ChatClient"), options: func());

});

FootballContentReviewerAgent >>

      servicecollection.AddSingleton<ChatClientAgent>(sp =>

{
    Func<ChatClientAgentOptions> func = () =>
   {
       return new ChatClientAgentOptions
       {
           ChatOptions = new ChatOptions
           {
               Instructions = "You are a content reviewer.You review created content for football teams and make necessary changes to it.Be concise and please stick to the topic.",


           },
           Name = "FootballContentReviewerAgent",

           Id = "2"

       };
   };

    return new ChatClientAgent(sp.GetKeyedService<IChatClient>("ChatClient"), options: func());

});

Build the service and fetch agents from the ServiceProvider as ChatClientAgent.

ServiceProvider serviceProvider = servicecollection.BuildServiceProvider();

var agents = serviceProvider.GetServices<ChatClientAgent>();

List<ChatClientAgents> chatclientagent = new(agents);

Then add these agents as DurableAgent to the Azure Function worker.

using IHost app = FunctionsApplication
.CreateBuilder(args)
.ConfigureFunctionsWebApplication()
.ConfigureDurableAgents(options => { options.AddAIAgents(chatclientagent).DefaultTimeToLive = TimeSpan.FromHours(1); })
.Build();

app.Run();

The time to live for the agents is configured to an hour.

Orchestration

Define a type record that holds the AgentResponse in a new class file called FunctionTrigger.cs

public record TextResponse(string Response);

The orchestration occurs in two steps Start and Run.

Start is the trigger point that defines the route , creates an orchestration instance for an DurableTaskClient and returns a response of type HttpResponseData.

StartOrchestrationAsync >>

 [Function(nameof(StartOrchestrationAsync))]

public static async Task<HttpResponseData> StartOrchestrationAsync([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "footballagent/run")] HttpRequestData req, [DurableClient] DurableTaskClient client)

 {

var instanceId = await client.ScheduleNewOrchestrationInstanceAsync(orchestratorName: nameof(RunOrchestrationAsync));

     var response = req.CreateResponse(HttpStatusCode.Accepted);
         await response.WriteAsJsonAsync(new
         {
             message = "Orchestration started.",
             InstanceId = instanceId
         }
      );
     return response;
 }

Run is where an agent orchestration pipeline is triggered post creation of an orchestration instance through StartOrchestrationAsync

RunOrchestrationAsync >>

 [Function(nameof(RunOrchestrationAsync))]
 public static async Task<string> RunOrchestrationAsync([OrchestrationTrigger] TaskOrchestrationContext context)
 {
     DurableAIAgent FootballContentCreatorAgent = context.GetAgent("FootballContentCreatorAgent");
     DurableAIAgent FootballContentReviewerAgent = context.GetAgent("FootballContentReviewerAgent");

     AgentSession Session = await FootballContentCreatorAgent.CreateSessionAsync();

     AgentResponse<TextResponse> initial = await FootballContentCreatorAgent.RunAsync<TextResponse>(
         message: "Summarize the glory of football club FC Barcelona",
         session: Session);

     AgentResponse<TextResponse> refined = await FootballContentReviewerAgent.RunAsync<TextResponse>(
      message: $"Improve and expand the review further while keeping it under 1000 words: {initial.Result.Response}",
      session: Session);

     return refined.Result.Response;

 }

In the code above through **TaskOrchestrationContext ,**we get a list of available agents from the context.

The AgentResponse output generated by FootballContentCreatorAgent acts as input to the FootballContentReviewerAgent. The input is reviewed and post modification the modified reviewed is returned as the final output.

Add the above two functions to the FunctionTrigger.cs file created earlier.

Our endpoint is "footballagent/run". Invoke it through Powershell.

Invoke-RestMethod -Method Post -Uri http://localhost:7001/api/footballagent/run

Post invocation , underlying artifacts are visible on the Durable Task Scheduler Dashboard.

Orchestration >>

Microsoft Agent Framework Azure Durable Functions

Entities >>

Microsoft Agent Framework Azure Durable Functions

Agents >>

Microsoft Agent Framework Azure Durable Functions

Execution >>

Conclusion

In this article, I tried to explore how Azure Durable Functions can be used to orchestrate a multi-agent execution within the Microsoft Agent Framework. I hope this article helps you understand how durable orchestrations can be leveraged to coordinate multi agent execution effectively.

Thanks for reading !!!