# Microsoft Agent Framework through Dependency Injection

After working on Semantic Kernel (SK) for sometime and then gradually shifting focus towards Microsoft Agent Framework (MAF), I’ve personally started to like MAF over SK.

Implementation through SK had a relatively high level of complexity which I felt was unnecessary for simpler use cases and also SK was heavily dependent on the Dependency Injection (DI) pattern that made setup more involved.

If interested you could read my article on SK Dependency Injection pattern [here](https://www.azureguru.net/dependency-injection-in-semantic-kernel).

In SK everything revolves around the `kernel`. And not to mention, managing conversation context across multiple sessions was not straightforward as it was up to developers on how to reconstruct chat history due to the stateless nature of LLM's.

Though [`ChatHistory`](https://learn.microsoft.com/en-us/semantic-kernel/concepts/ai-services/chat-completion/chat-history?pivots=programming-language-csharp) helped with in-session context handling, multi-session persistence and state management in SK, it still required custom implementation that added unnecessary complexity.

In MAF, the introduction of middleware gives more flexibility and provides a powerful way to intercept and modify agent behavior.

Not to say that there aren't some shortcomings in MAF.

For example I couldn't find a way to create functions through prompts in MAF.

In SK, this was quite straightforward. You could do something like this easily.

```csharp
kernel.CreateFunctionFromPrompt("My Prompt")
```

However, there isn’t an equivalent approach for this aspect in MAF.

Another drawback was lack of in built and third party PromptTemplate support.

I’ve written a two-part article on implementing LiquidPrompt in SK available [here](https://www.azureguru.net/liquid-format-for-semantic-kernel-prompts-part-1) and [here](https://www.azureguru.net/liquid-format-for-semantic-kernel-prompts-part-2) .

With SK you could use the inbuilt `kernelPromptTemplateFactory` object that natively protects prompt injection attacks.

[https://learn.microsoft.com/en-us/semantic-kernel/concepts/prompts/prompt-injection-attacks?pivots=programming-language-csharp](https://learn.microsoft.com/en-us/semantic-kernel/concepts/prompts/prompt-injection-attacks?pivots=programming-language-csharp)

Though using external providers like Azure ShieldPrompt can be an option to prevent injection attacks ,it would have been great if there would had been something equivalent to `kernelPromptTemplateFactory`in MAF wrt to prompt templates.

If you would like to know how to use ShieldPrompt to prevent prompt injection attacks, I have an article on that topic [here](https://www.azureguru.net/shield-prompt-to-prevent-prompt-injection) .

Lets get back to the topic of this article.

Earlier, I mentioned that SK was heavily DI dependent but in MAF its optional. You could still use DI in MAF.

In this article I would show how it can be done. I think I will be using this approach extensively for foreseeable future.

I would recommend to go through my an article on integrating C# native functions in SK. You can find that article [here](https://www.azureguru.net/integrating-c-sharp-functions-with-semantic-kernel) and it might help you understand the the intricacies on function calling in SK.

Some important differences between SK and MAF wrt to function calling ,

*   `AIFunction` from `Extensions.AI` in MAF is equivalent to `KernelFunction` in SK
    
*   `AIFunctionFactory` from `Extensions.AI` in MAF is equivalent to `KernelFunctionFactory` in SK
    
*   `AIFunctionArguments` from `Extensions.AI` in MAF is equivalent to `KernelArguments` in SK
    

I will pen down a detailed article shortly on invoking function calls in MAF.

### SetUp

Add the following dependencies to your C# console application

```csharp
dotnet add package Azure.AI.OpenAI
dotnet add package Microsoft.Extensions.AI
dotnet add package Microsoft.Agents.AI
dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.DependencyInjection
```

Create `appsettings.json` file with the following details

```csharp
  "AppSettings": {     
      "Chat_DeploymentName": "Deployment name",
      "EndPoint": "Azure AI endpoint",
      "ApiKey": "Azure AI API key"    
  }
```

### Code

Read the config file

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

Create a class called `UserPlugin`, that has two methods `HelloUser` and `HelloCity`.

```csharp
[Description("Use this function to greet the user")]
public static string HelloUser([Description("Name of the user")] string user)
{
    return $"Hello from : {user}  ";
}

[Description("Use this function when the user mentions his city ")]
public static string HelloCity([Description("Name of the city")] string city)
{
    return $"Nice city : {city}  ";
}
```

In MAF, you have the option to expose the functions as `AITool`. It is the base class for all tools that can be provided to agents.

In our example we can do it through through the custom function `AsAITools` by exposing the two functions as a sequence of type `IEnumerable`.

```csharp
 public  IEnumerable<AITool> AsAITools()
 {
     yield return AIFunctionFactory.Create(HelloCity);
     yield return AIFunctionFactory.Create(HelloUser);
 }
```

> The `AIFunctionFactory` in MAF is similar to `KernelFunctionFactory` in SK

Our `UserPlugin`class looks like this

```csharp
using Microsoft.Extensions.AI;
using System.ComponentModel;

namespace Plugins_Functions
{
    internal class UserPlugin
    {
        [Description("Use this function to greet the user")]
        public static string HelloUser([Description("Name of the user")] string user)
        {
            return $"Hello from : {user}  ";

        }

       [Description("Use this function when the user mentions his city")]
        public static string HelloCity([Description("Name of the city")] string city)
        {
            return $"Nice city : {city}  ";

        }

        public  IEnumerable<AITool> AsAITools()
        {
            yield return AIFunctionFactory.Create(HelloCity);
            yield return AIFunctionFactory.Create(HelloUser);
        }
    }
}
```

As we are using DI, we have to inject the class `UserPlugin`. We can register and resolve through `ServiceCollection`.

```csharp
ServiceCollection servicecollection = new();
servicecollection.AddSingleton<UserPlugin>();
```

We can validate the credentials, through `AzureKeyCredential`. Though I don't like this approach, for brevity we will use this approach in this article.

My preferred approach would be using SPN based authentication. I had penned a article on that topic that you can read it [here](https://www.azureguru.net/authentication-tokens-for-azure-ai-foundry-data-agents-through-entra-service-principal-part-2).

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

Next, we register the `ChatClient`with the servicecollection.

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

We then register an `AIAgent` with a factory in the `ServiceCollection`, allowing the DI container to construct it.

```csharp
 servicecollection.AddSingleton<AIAgent>(sp =>
           {
               return new ChatClientAgent(
                    chatClient: sp.GetRequiredKeyedService<IChatClient>("ChatClient"),
                    options: new ChatClientAgentOptions
                    {
                        ChatOptions = new ChatOptions
                        {
                            Instructions = "You are a helpful AI assistant.You must call the provided function to greet users and return the EXACT output of the function without modifying it.",
                            Tools = sp.GetRequiredService<UserPlugin>().AsAITools().ToList(),                        

                        },                              

                    }                           

                );

           });
```

Note the instructions above

> "You are a helpful AI assistant. You must call the provided function to greet users and return the EXACT output of the function without modifying it."

This ensures that the agent delegates the greeting to the function and returns its output as exactly stated in the description of the function without adding, removing or altering any content by the agent.

We then build the DI container that can be used to create the `AIAgent`.

```csharp
var serviceProvider = servicecollection.BuildServiceProvider();
var agent = serviceProvider.GetRequiredService<AIAgent>();    
```

We then create an `AgentSession` and send a prompt

```csharp
AgentSession session = await agent.CreateSessionAsync();
Console.WriteLine(await agent.RunAsync("My name is Sachin and I am from Mumbai ", session));
```

**Screengrab :**

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/3b31a8b7-8bd6-4e74-a5bb-981e03fd5928.gif align="center")

An alternate approach for function calling , is that you can directly use the functions created through `AIFunctionFactory`in the agent registration by exposing the function as `AITool`.

Its similar to the SK approach. I have article on that topic [here](https://www.azureguru.net/integrating-c-sharp-functions-with-semantic-kernel).

MAF Example :

```csharp
var hellouser = AIFunctionFactory.Create(UserPlugin.HelloUser, name: "HelloUser", description: "Use this function to greet the user");

var hellocity = AIFunctionFactory.Create(UserPlugin.HelloCity, name: "HelloCity", description: "Use this function when the user mentions his city");

List<AITool> AllFunctions = new List<AITool> { hellouser, hellocity };
```

Replace the following line of code in the `ChatOptions`of the `AIAgent` service registration

```csharp
 ChatOptions = new ChatOptions
 {
     Instructions = "You are a helpful AI assistant.You must call the provided function to greet users and return the EXACT output of the function without modifying it.",
     Tools = sp.GetRequiredService<UserPlugin> ().AsAITools().ToList()
},    
```

with this

```csharp
 ChatOptions = new ChatOptions
 {
     Instructions = "You are a helpful AI assistant.You must call the provided function to greet users and return the EXACT output of the function without modifying it.",
     Tools = AllFunctions
 },     
```

I personally prefer the `AITool`approach as it give more abstraction and is more like a whitelist approach unlike the dump everything approach.

### Conclusion

In this article I touch based on an very important and crucial concept in MAF. In upcoming articles, I’ll dive deeper into MAF covering additional patterns and more advanced use cases.

So stay tuned !!!
