DEV Community

Cover image for MAF Agents with Azure AI Foundry Project
Gonzalo Sosa
Gonzalo Sosa

Posted on

MAF Agents with Azure AI Foundry Project

Introduction

Microsoft Agent Framework (MAF) is an open-source development kit designed to streamline the creation and management of agents and workflows that automate tasks and processes. It builds on well-known OSS projects such as Semantic Kernel and Autogen, serving as their natural evolution while keep their strengths and ease of use.

MAF provides several building blocks for creating AI solutions:

  • LLM Model clients, for chat completions and responses.
  • Agent Thread, to handle context, state and memory providers.
  • Middleware, to intercept messages.
  • MCP Clients, to add up tools to be used by the agents.

Currently, MAF supports .NET and Python for creating agents and workflows. Additional languages may be supported in the future.

At high level MAF has this agentic architecture

MAF Agentic Architecture

Source: Microsoft Agent Framework - Overview

Creating Azure AI Foundry Project

In this article, we will create a TripAdvisor agent that helps users with travel recommendations and answer questions related to trips.

We will use Azure AI Foundry as our AI infrastructure. Follow these steps to create a new project:

Azure AI Foundry portal

  • Once in there, click Create a New Project button. Select Microsoft Foundry resource when prompted.

New Project Screen

  • Next set required parameters as you consider convenient.

Foundry Project Parameters screen

  • Once project is created in the overview section take note and keep these values:
    • Azure OpenAI endpoint
    • Api Key

Foundry Project Overview

  • Now, go to section My Assets > Models + endpoints and click in Deploy Model button.
  • In screen that shows up, search for gpt-4o or the model you prefer and press Confirm button

New Model Deployment Screen

  • Now select a name for your deployment and take note of it.
  • In deployment type choose Global Standard
  • Select the region where you have enough quota available. Take into account you should have enough capacity, in tokens per minute, to not exhaust them when interacting with your agent.

New Model Deployment parameters

Once you already finish creating your project in Azure AI Foundry, and your deployment is completed we can move forward coding the agent.

Code

For coding our Agent we’re going to use C#. So, first of all we’re going to create a new console project with command

dotnet new console -n TripAdvisorAgent -f net10.0
Enter fullscreen mode Exit fullscreen mode

once project is created, we move into its folder.

cd TripAdvisorAgent/
Enter fullscreen mode Exit fullscreen mode

then we need to install these packages.

dotnet add package Azure.Identity
dotnet add package Microsoft.Agent.Framework
dotnet add package Azure.AI.OpenAI --prerelease
dotnet add package Microsoft.Agents.AI.OpenAI --prerelease 
dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.Configuration.UserSecrets
dotnet add package Spectre.Console
Enter fullscreen mode Exit fullscreen mode

Next, set up the secrets we will use, instead of hardcoding them in the code.

dotnet user-secrets init
dotnet user-secrets set AZURE_OPENAI_ENDPOINT <your_azure_openai_endpoint>
dotnet user-secrets set AZURE_OPENAI_APIKEY <your_azure_openai_apikey>
dotnet user-secrets set AZURE_OPENAI_DEPLOYMENT <your_azure_openai_deployment>
Enter fullscreen mode Exit fullscreen mode

with that set now we started adding code to our project.

The TripAdvisor App

Program.cs - Initial version

using System.ClientModel;
using Azure.AI.OpenAI;
using Microsoft.Extensions.Configuration;
using OpenAI.Chat;
using Spectre.Console;

// retrieves user secrets
var configuration = new ConfigurationBuilder ()
    .AddUserSecrets<Program>()
    .Build ();

var systemPrompt = @"
    Your are a helpful trip advisor assistant, specialized in recommend activities, places to visit,
    culinary tours and activities that tourist will enjoy.
    If questions/requests are no related to any of previous subjects just respond 'I'm not able to respond to your request'.
    ";

// creation of an agent using deployed model in Azure AI Foundry    
var agent = new AzureOpenAIClient (
    endpoint: new Uri (configuration["AZURE_OPENAI_ENDPOINT"]),
    new ApiKeyCredential (configuration["AZURE_OPENAI_APIKEY"])
)
.GetChatClient(configuration["AZURE_OPENAI_DEPLOYMENT"])
.CreateAIAgent(instructions: systemPrompt);

Console.Clear();

while (true)
{
    var input = AnsiConsole.Ask<string>("[green]User:[/]");
    if (string.IsNullOrEmpty (input)) 
        continue;

    if (string.Equals (input, "exit", StringComparison.InvariantCultureIgnoreCase))
        break;

        // pass the user request to the agent 
    var response = await agent.RunAsync(input);
    Console.WriteLine ($"Agent: {response.Text}");
}
Enter fullscreen mode Exit fullscreen mode

Although is not mentioned explicitly the agent runs inside a AgentThread that handles the state of a specific conversation. If not defined one, a new AgentThread is created.

Now, we can run it and interact with our agent as shown here

dotnet run
Enter fullscreen mode Exit fullscreen mode

Agent first run

however, our agent forgets everything once it responds.

Agent without memory

To fix that we need to provide memory to our agent, so it can track the conversation. To do that MAF has two types of scenarios supported.

  • In-memory storage. Memory is managed by the AgentThread where agent runs in.
  • In-service storage. Memory is hold in the service where the agent is built. AgentThread that runs the agent have to retrieve memory from that store.

In our example we are going to give memory to our agent by creating a temporary In-Memory store.

var messageHistory = new InMemoryChatMessageStore ();
var agentThread = agent.GetNewThread(messageHistory);
Enter fullscreen mode Exit fullscreen mode

By explicitly creating an AgentThread we can pass a memory provider to it, InMemoryChatMessageStore in this case, to save the whole conversation, so the agent can be aware of its previous responses. But also, it gives us the possibility of managing messages history to do operations on it, for example, logging it as shown here.

Program.cs - with memory

using System.ClientModel;
using Azure.AI.OpenAI;
using Microsoft.Agents.AI;
using Microsoft.Extensions.Configuration;
using OpenAI.Chat;
using Spectre.Console;

// retrieves user secrets
var configuration = new ConfigurationBuilder ()
    .AddUserSecrets<Program>()
    .Build ();

var systemPrompt = @"
    Your are a helpful trip advisor assistant, specialized in recommend activities, places to visit,
    culinary tours and activities that tourist will enjoy.
    If questions/requests are no related to any of previous subjects just respond 'I'm not able to respond to your request'.
    ";

// creation of an agent using deployed model in Azure AI Foundry    
var agent = new AzureOpenAIClient (
    endpoint: new Uri (configuration["AZURE_OPENAI_ENDPOINT"]),
    new ApiKeyCredential (configuration["AZURE_OPENAI_APIKEY"])
)
.GetChatClient(configuration["AZURE_OPENAI_DEPLOYMENT"])
.CreateAIAgent(instructions: systemPrompt);

// create an InMemory store to hold all conversation messages
var messageHistory = new InMemoryChatMessageStore ();
var agentThread = agent.GetNewThread(messageHistory);

Console.Clear();

while (true)
{
    var input = AnsiConsole.Ask<string>("[green]User:[/]");
    if (string.IsNullOrEmpty (input)) 
        continue;

    if (string.Equals (input, "exit", StringComparison.InvariantCultureIgnoreCase))
        break;

        // pass the user request to the agent along with the AgentThread
        // that will handle conversation state
    var response = await agent.RunAsync(input, agentThread);
    Console.WriteLine ($"Agent: {response.Text}");
}

// print every message in the history conversation (memory) to the console
AnsiConsole.Write(new Rule("[orange1]Conversation thread[/]") { Justification = Justify.Right });  
foreach (var message in messageHistory)
{
    AnsiConsole.Write(new Text (message.Role.Value + ": ", new Style(Color.Orange1)));
    AnsiConsole.Write(new Text (message.Text + "\n\n", new Style(Color.Orange1)));
    AnsiConsole.Write(new Rule("[orange1]Message[/]") { Justification = Justify.Right });  
}
Enter fullscreen mode Exit fullscreen mode

Now our agent can remember its previous responses and respond accordingly.

Agent with memory

And, as an extra benefit, now the whole conversation can be retrieved, managed and audited.

Conversation history

One last thing our agent could benefit from is the ability of get currency conversion rates, therefore, if the user requires some information that involves prices, we can be sure it has the most updated rates. To do so, we are going to add tools from a public MCP server that expose that kind of operations.

MAF supports integration with model context protocol servers through the official MCP SDKs. And for add it to our agent we need to create a MCP client and then fetch all tools it exposes.

So, the first thing is to add the official MCP Client package.

dotnet add package ModelContextProtocol --prerelease
Enter fullscreen mode Exit fullscreen mode

And create the MCP Client pointing to the endpoint that expose the tools.

var sharedHandler = new SocketsHttpHandler
{
    PooledConnectionLifetime = TimeSpan.FromMinutes(2),
    PooledConnectionIdleTimeout = TimeSpan.FromMinutes(1)
};
using var httpClient = new HttpClient (sharedHandler);
var transport = new HttpClientTransport (new ()
{
    Endpoint = new Uri ("https://currency-mcp.wesbos.com/mcp"),
    Name = "Currency Conversion",
}, httpClient);
var mcpClient = await McpClient.CreateAsync(transport);
var mcpTools = await mcpClient.ListToolsAsync().ConfigureAwait(false);
Enter fullscreen mode Exit fullscreen mode

finally, update the system prompt to instruct the agent to use tools when needed.

var systemPrompt = @"
    Your are a helpful trip advisor assistant, specialized in recommend activities, places to visit,
    culinary tours and activities that tourist will enjoy.
    If questions/requests are no related to any of previous subjects just respond 'I'm not able to respond to your request'.
    Use tools provided if your information is outdated or need refinement.
    ";
Enter fullscreen mode Exit fullscreen mode

With those last changes our Program.cs looks like.

using System.ClientModel;
using Azure.AI.OpenAI;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
using ModelContextProtocol.Client;
using OpenAI.Chat;
using Spectre.Console;

// retrieves user secrets
var configuration = new ConfigurationBuilder ()
    .AddUserSecrets<Program>()
    .Build ();

// creates an HttpClient to be used to request MCP Server
var sharedHandler = new SocketsHttpHandler
{
    PooledConnectionLifetime = TimeSpan.FromMinutes(2),
    PooledConnectionIdleTimeout = TimeSpan.FromMinutes(1)
};
using var httpClient = new HttpClient (sharedHandler);

// create MCP client transport that will use HttpClient created
// when request to the endpoint
var transport = new HttpClientTransport (new ()
{
    Endpoint = new Uri ("https://currency-mcp.wesbos.com/mcp"),
    Name = "Currency Conversion",
}, httpClient);

// create mcp client and retrieve tools list from the server
var mcpClient = await McpClient.CreateAsync(transport);
var mcpTools = await mcpClient.ListToolsAsync().ConfigureAwait(false);

var systemPrompt = @"
    Your are a helpful trip advisor assistant, specialized in recommend activities, places to visit,
    culinary tours and activities that tourist will enjoy.
    If questions/requests are no related to any of previous subjects just respond 'I'm not able to respond to your request'.
    Use tools provided if your information is outdated or need refinement.
    ";

// creation of an agent using deployed model in Azure AI Foundry
// now including tools retrieved from mcp server  
var agent = new AzureOpenAIClient (
    endpoint: new Uri (configuration["AZURE_OPENAI_ENDPOINT"]),
    new ApiKeyCredential (configuration["AZURE_OPENAI_APIKEY"])
)
.GetChatClient(configuration["AZURE_OPENAI_DEPLOYMENT"])
.CreateAIAgent(instructions: systemPrompt, tools: [..mcpTools.Cast<AITool>()]);

// create an InMemory store to hold all conversation messages
var messageHistory = new InMemoryChatMessageStore ();
var agentThread = agent.GetNewThread(messageHistory);

Console.Clear();

while (true)
{
    var input = AnsiConsole.Ask<string>("[green]User:[/]");
    if (string.IsNullOrEmpty (input)) 
        continue;

    if (string.Equals (input, "exit", StringComparison.InvariantCultureIgnoreCase))
        break;

        // pass the user request to the agent along with the agentthread
        // that will handle conversation
    var response = await agent.RunAsync(input, agentThread);
    Console.WriteLine ($"Agent: {response.Text}");
}

// print every message in the history conversation (memory) to the console
AnsiConsole.Write(new Rule("[orange1]Conversation thread[/]") { Justification = Justify.Right });  
foreach (var message in messageHistory)
{
    AnsiConsole.Write(new Text (message.Role.Value + ": ", new Style(Color.Orange1)));
    AnsiConsole.Write(new Text (message.Text + "\n\n", new Style(Color.Orange1)));
    AnsiConsole.Write(new Rule("[orange1]Message[/]") { Justification = Justify.Right });  
}
Enter fullscreen mode Exit fullscreen mode

And we can now ask for prices with more confidence about conversion rates.

Agent working with tools

Next Steps

The purpose of this article is to serve as a sneak peek about MAF capabilities and its basic usage. However, the example presented here can be extended by adding some other elements like:

  • RAG. Having a repository with foundational documents where to look first when the user makes a request will give the agent grounding to respond promptly and accurately.
  • Improve logging by intercepting messages with a middleware.
  • Add observability with OpenTelemetry.
  • Publish our Agent to make it publicly available to everyone.

References

Resource links

Top comments (0)