Skip to main content

Command Palette

Search for a command to run...

AG-UI Protocol In Microsoft Agent Framework

Updated
8 min read
AG-UI Protocol 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.

The Agent-User Interaction Protocol or commonly known as AG-UI is a protocol developed to expose AI agents to web/mobile frontends.

Think of AG-UI as HTTP protocol for AI Agents. With AG-UI its now possible to call and render tools through agents invoked from your frontends. Before AG-UI all frontend calls to AI agents required custom integration but AG-UI has made it the integration pretty straightforward.

Most people get confused between AG-UI, MCP and A2A. The following table from https://docs.ag-ui.com/introduction clears the distinction between all the three protocols.

AG-UI protocol in Microsoft Agent Framework

In this article, I will not delve into the in-depth details of AG-UI. Instead, I will focus on its implementation. You can refer to the AGUI documentation here and here.

There are two approaches through which AG-UI Integration with Microsoft Agent Framework can be implemented.

  • Backend Tool Rendering

  • Frontend Tool Rendering

In Backend Tool Rendering , the function tools are defined on the server and the tools are executed on the server through client calls. The client receives updates about tool execution progress and the results are streamed to the client in real time.

In Frontend Tool Rendering , the function tools are registered and defined on the client and the tools get executed in the client environment . The results are sent back to the server to be incorporate into the server responses.

In this article I will demonstrate implementation of server side tool rendering.

We will have two functions :

  • ReturnCityTemperature >> Returns temperature of a given city

  • ReturnCountryCapital >> Returns capital city of a given country

We will use a single agent to handle requests for both the methods.

SetUp

AG-UI Server >>

The first step is to create an AG-UI server.

Create a new ASP.NET core application and add the following packages

dotnet add package Azure
dotnet add package Azure.AI.OpenAI
dotnet add package Microsoft.Agents.AI
dotnet add package Microsoft.Extensions.AI
dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.DependencyInjection
dotnet add package Microsoft.Extensions.Logging
dotnet add package Microsoft.Agents.AI
dotnet add package Microsoft.Agents.AI.Hosting.AGUI.AspNetCore

Add appsetting.json to the project

"AppSettings": { 
    "Chat_DeploymentName": "Deployment Name",
    "EndPoint": "Azure OpenAI endpoint",
    "ApiKey": "Azure OpenAI API key"
}

In launchSettings.json, configure the ports on which the server should listen.

{
  "profiles": {
    "AGUIServer": {
      "commandName": "Project",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      },
      "applicationUrl": "https://localhost:49408;http://localhost:49409"
    }
  }
}

In the above settings , the application is configured to listen on ports 49408 (HTTPS) and 49409 (HTTP). For this article, we will use 49408 on https.

Code

Now that we have all the underlying artifacts in place, add the following code to read the settings from appsettings.json

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

Create a DI container

 ServiceCollection servicecollection = new ServiceCollection();

Read the credentials and register Chatclient

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

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

Register an AI Agent in the DI container

servicecollection.AddScoped<AIAgent>(sp =>
       {
           Func<ChatClientAgentOptions> func = () =>

           {
               return new ChatClientAgentOptions
               {

                   ChatOptions = new ChatOptions
                   {

                      Instructions = "You are a helpful assistant.",
                   },
                    Id = "1",
                    Name = "HelpfulAgent"                  
               };

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

We define the required request and response objects for CityTemperature and CountryCapital.

CityTemperatureSearchRequest >>

 public class CityTemperatureSearchRequest
 {
     public string City { get; set; }
 }

 public class CityTemperatureSearchResponse
 {
     public string City { get; set; }
     public string Temperature { get; set; }
 }

CountryCapitalSearchRequest >>

 public class CountryCapitalSearchRequest
 {
     public string Country { get; set; }
 }

 public class CountryCapitalSearchResponse
 {
     public string Country { get; set; }
     public string Capital { get; set; }
 }

Next, create serialization metadata for both request and response types.

[JsonSerializable(typeof(CountryCapitalSearchRequest))]
[JsonSerializable(typeof(CountryCapitalSearchResponse))]
internal sealed partial class CountryCapitalSerializerContext : JsonSerializerContext;


[JsonSerializable(typeof(CityTemperatureSearchRequest))]
[JsonSerializable(typeof(CityTemperatureSearchResponse))]
internal sealed partial class CityTemperatureSerializerContext : JsonSerializerContext;

Register the above two serialization options and add the AIAgent support via AG-UI to the app.


WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
        builder.Services.AddHttpClient().AddLogging();
        builder.Services.ConfigureHttpJsonOptions(options => options.SerializerOptions.TypeInfoResolverChain.Add(CountryCapitalSerializerContext.Default));
        builder.Services.ConfigureHttpJsonOptions(options => options.SerializerOptions.TypeInfoResolverChain.Add(CityTemperatureSerializerContext.Default));
        builder.Services.AddAGUI();

Following methods return the underlying data

ReturnCountryCapital>>

 public static Func<CountryCapitalSearchRequest, CountryCapitalSearchResponse> ReturnCountryCapital = (CountryCapitalSearchRequest) =>

{
    switch (CountryCapitalSearchRequest.Country)
    {

        case "India":

            return new CountryCapitalSearchResponse
            {

                Country = "India",
                Capital = "New Delhi"
            };
            break;

        case "USA":

            return new CountryCapitalSearchResponse
            {

                Country = "USA",
                Capital = "Washington"
            };
            break;


        case "Germany":

            return new CountryCapitalSearchResponse
            {

                Country = "Germany",
                Capital = "Berlin"
            };
            break;

        case "Russia":

            return new CountryCapitalSearchResponse
            {

                Country = "Russia",
                Capital = "Moscow"
            };
            break;
    }

    return new CountryCapitalSearchResponse
    {

        Country = CountryCapitalSearchRequest.Country,
        Capital = "Unknown"
    };

};

ReturnCityTemperature >>

public static Func<CityTemperatureSearchRequest, CityTemperatureSearchResponse> ReturnCityTemperature = (CityTemperatureSearchRequest) =>

{
    switch (CityTemperatureSearchRequest.City)
    {

        case "Mumbai":

            return new CityTemperatureSearchResponse
            {

                City = "Mumbai",
                Temperature = "40"
            };

            break;

        case "Pune":

            return new CityTemperatureSearchResponse
            {

                City = "Pune",
                Temperature = "41"
            };

            break;

        case "Delhi":

            return new CityTemperatureSearchResponse
            {

                City = "Delhi",
                Temperature = "42"
            };

            break;

        case "Chennai":

            return new CityTemperatureSearchResponse
            {

                City = "Chennai",
                Temperature = "43"
            };
            break;
    }

    return new CityTemperatureSearchResponse
    {

        City = CityTemperatureSearchRequest.City,
        Temperature = "Unknown"
    };

};

Register the above two methods in the DI container as AIFunction with the corresponding SerializerOptions defined earlier.

  servicecollection.AddSingleton<AIFunction>(sp =>
      {
          return AIFunctionFactory.Create(ReturnCityTemperature, new AIFunctionFactoryOptions { Name = "ReturnCityTemperature", Description = "Gets the current weather for a specific city", SerializerOptions = CityTemperatureSerializerContext.Default.Options });
      }

  );

  servicecollection.AddSingleton<AIFunction>(sp =>
    {
        return AIFunctionFactory.Create(ReturnCountryCapital, new AIFunctionFactoryOptions { Name = "ReturnCountryCapital", Description = "Gets the capital city for a specific country.", SerializerOptions = CountryCapitalSerializerContext.Default.Options });
    }
);

Now create a DI container and fetch list of all AIFunction and ChatClientAgent


ServiceProvider serviceprovider = servicecollection.BuildServiceProvider();

var chatlient = serviceprovider.GetServices<ChatClientAgent>();

var aifunctions = serviceprovider.GetServices<AIFunction>();

List<ChatClientAgent> lstchatclient = new(chatlient);

List<AITool> functions = new(aifunctions);

Create an agent from the chatclient and register the ToolFunctions.

 var agent = lstchatclient[0].ChatClient.AsAIAgent(

     new ChatClientAgentOptions
     {
         ChatOptions = new ChatOptions
         {
             Tools = functions,
             Instructions = "You return capital city of a country and you also provide temperature of a city.You will use only the data provided to you and not use any external data"
         }
     }
  );

Expose the above MAF agent through an AG-UI endpoint.

WebApplication app = builder.Build();      
app.MapAGUI("/", agent);
await app.RunAsync();

Run the server and verify that it is listening on https://localhost:{port number}.

In our case it will be https://localhost:49408

AG-UI protocol in Microsoft Agent Framework

Now that the server is up and running the next step is to configure a client that can invoke server side function tools.

AG-UI Client >>

Create a new console application project and add the following packages

dotnet add package Microsoft.Agents.AI;
dotnet add package Microsoft.Agents.AI.AGUI;
dotnet add package Microsoft.Extensions.AI;

In the Main method of Program.cs, create a AGUIChatClient through an HTTP client that references to the agent on the AGUI server at https://localhost:49408

 string serverUrl = "https://localhost:49408";

 using HttpClient httpClient = new()
 {
     Timeout = TimeSpan.FromSeconds(60)
 };

 AGUIChatClient chatClient = new(httpClient, serverUrl);

 AIAgent agent = chatClient.AsAIAgent(
     name: "HelpfulAgent",
     description: "AG-UI Client Agent");

 AgentSession session = await agent.CreateSessionAsync();

Create a conversation history and send messages to the agent on the server.

  List<ChatMessage> messages =
  [
      new(ChatRole.System, "You are a helpful assistant.")
  ];

messages.Add(new ChatMessage(ChatRole.User, "What is temperature in Mumbai and what is capital of USA ?"));

With AG-UI in MAF, we can extend the client streaming to expose the following content types.

FunctionCallContent >> It contains details of the invoked function including the function name and arguments used if any.

FunctionResultContent >> It contains results of the function invocation and the results are exposed in json format.

TextContent >> Contains the textual response generated by the agent.

ErrorContent >> Contains errors if any.

await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(messages, session))
   {

       foreach (var content in update.Contents)
       {
           if (content is Microsoft.Extensions.AI.FunctionCallContent functioncallcontent)
           {
               Console.ForegroundColor = ConsoleColor.DarkCyan;
               Console.WriteLine($"\n[Function Call - Name: {functioncallcontent.Name}]");
               Console.ResetColor();

               foreach (var args in functioncallcontent.Arguments)
               {
                   Console.ForegroundColor = ConsoleColor.Cyan;
                   Console.WriteLine($"\n[Function Arguments - Argument Name : {args.Key} - Argument Value : {args.Value}");

                   Console.ResetColor();
               }
           }

           if (content is Microsoft.Extensions.AI.FunctionResultContent functionresultcontent)
           {
               Console.ForegroundColor = ConsoleColor.Red;
               Console.Write(($"\n[Function raw output : {functionresultcontent.Result}]"));
               Console.WriteLine();
               Console.ResetColor();
           }

           if (content is Microsoft.Extensions.AI.TextContent textContent)
           {                   
               Console.ForegroundColor = ConsoleColor.Green;
               Console.Write(textContent.Text);
               Console.ResetColor();
           }

           if (content is Microsoft.Extensions.AI.ErrorContent errorContent)
           {
               Console.ForegroundColor = ConsoleColor.Red;
               Console.WriteLine($"Error : {errorContent.Message}");
               Console.ResetColor();

           }
       }
   }

That's all. Test the client execution with a prompt .

What is the temperature in Delhi and capital of USA ?

Ensure that the server is up and running.

AG-UI protocol in Microsoft Agent Framework

Execution >>

Conclusion

In conclusion, AG-UI is a great step forward for front end tool interactions with agents deployed on server. It reduces the complexity of implementing custom integration for client interaction with agents running on server.

I hope this article helped you understand the caveats of implement AG-UI solution for Microsoft Agent Framework.

Thanks for reading !!!

More from this blog