# Microsoft Agent Framework - Setup Guide (.NET 10)

> **Scope:** universal
> **Layer:** 0 (always load)
> **Keywords:** agent, ai, openai, setup, microsoft agents, chatclient
> **Load When:** always

**IMPORTANT:** MORPH-SPEC uses **exclusively** Microsoft Agent Framework.
Semantic Kernel is superseded. Do NOT use `Kernel`, `KernelFunction`, or `InvokePromptAsync`.

---

## Packages

```xml
<!-- .csproj -->
<PackageReference Include="Microsoft.Agents.AI" Version="1.0.0-preview.*" />
<PackageReference Include="Microsoft.Agents.AI.Abstractions" Version="1.0.0-preview.*" />
<PackageReference Include="Microsoft.Agents.AI.OpenAI" Version="1.0.0-preview.*" />

<!-- For Web API hosting + A2A -->
<PackageReference Include="Microsoft.Agents.AI.Hosting" Version="1.0.0-preview.*" />

<!-- For Azure OpenAI -->
<PackageReference Include="Azure.AI.OpenAI" Version="2.*" />
<PackageReference Include="Azure.Identity" Version="1.*" />

<!-- For Microsoft.Extensions.AI abstractions -->
<PackageReference Include="Microsoft.Extensions.AI" Version="10.2.0" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.3.0-preview.*" />
```

---

## Quick Start (Minimal Agent)

```csharp
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;

AIAgent agent = new AzureOpenAIClient(
    new Uri("https://your-resource.openai.azure.com/"),
    new AzureCliCredential())
  .GetChatClient("gpt-4o-mini")
  .AsAIAgent(instructions: "You are a helpful assistant.");

Console.WriteLine(await agent.RunAsync("Analyze this order."));
```

---

## Setup by Provider

### Azure OpenAI (Production)

```csharp
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Extensions.AI;

builder.Services.AddSingleton<IChatClient>(sp =>
{
    var cfg = sp.GetRequiredService<IConfiguration>();
    return new AzureOpenAIClient(
            new Uri(cfg["AzureOpenAI:Endpoint"]!),
            new DefaultAzureCredential())  // Managed Identity
        .GetChatClient(cfg["AzureOpenAI:DeploymentName"] ?? "gpt-4o-mini")
        .AsIChatClient();
});
```

### GitHub Models (Development)

```csharp
using OpenAI;
using Microsoft.Extensions.AI;

builder.Services.AddSingleton<IChatClient>(sp =>
{
    var cfg = sp.GetRequiredService<IConfiguration>();
    return new ChatClient(
            "gpt-4o-mini",
            new ApiKeyCredential(cfg["GitHub:Token"]!),
            new OpenAIClientOptions { Endpoint = new Uri("https://models.github.ai/inference") })
        .AsIChatClient();
});
```

### Ollama (Local)

```csharp
builder.Services.AddSingleton<IChatClient>(sp =>
    new ChatClient("llama3.2:3b",
        endpoint: new Uri("http://localhost:11434"))
    .AsIChatClient());
```

---

## Agent Pattern (Required)

### Using ChatClientAgent

```csharp
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;

public class OrderAnalysisAgent
{
    private readonly IChatClient _chatClient;
    private readonly ILogger<OrderAnalysisAgent> _logger;

    public OrderAnalysisAgent(IChatClient chatClient, ILogger<OrderAnalysisAgent> logger)
    {
        _chatClient = chatClient;
        _logger = logger;
    }

    public async Task<string> AnalyzeAsync(Order order, CancellationToken ct = default)
    {
        var agent = new ChatClientAgent(
            _chatClient,
            new ChatClientAgentOptions
            {
                Name = "OrderAnalyzer",
                Instructions = """
                    You are an e-commerce order analyst.
                    Analyze the order and return:
                    1. Summary
                    2. Upsell suggestions
                    3. Cancellation risk (low/medium/high)
                    Be concise.
                    """
            });

        _logger.LogInformation("Analyzing order {OrderNumber}", order.OrderNumber);

        var response = await agent.RunAsync(
            $"Order: {order.OrderNumber}, Total: {order.Total:C}, Items: {order.Items.Count}",
            cancellationToken: ct);

        return response.Text;
    }
}
```

### Using AsAIAgent (Shorthand)

```csharp
AIAgent agent = chatClient.AsAIAgent(
    instructions: "You are a helpful assistant.",
    name: "MyAgent");

var response = await agent.RunAsync("Hello!", cancellationToken: ct);
Console.WriteLine(response.Text);
```

---

## Tools (Function Calling)

### Define Tools with AIFunctionFactory

```csharp
using Microsoft.Extensions.AI;

public class WeatherFunctions
{
    private readonly IWeatherService _weatherService;

    public WeatherFunctions(IWeatherService weatherService) => _weatherService = weatherService;

    [Description("Gets current weather for a city")]
    public async Task<string> GetWeatherAsync(
        [Description("City name")] string city,
        CancellationToken ct = default)
    {
        var weather = await _weatherService.GetCurrentAsync(city, ct);
        return $"Temp: {weather.Temperature}°C, Condition: {weather.Condition}";
    }

    [Description("Gets weather forecast for upcoming days")]
    public async Task<string> GetForecastAsync(
        [Description("City name")] string city,
        [Description("Number of days")] int days = 3,
        CancellationToken ct = default)
    {
        var forecast = await _weatherService.GetForecastAsync(city, days, ct);
        return string.Join("\n", forecast.Select(f => $"{f.Date:dd/MM}: {f.Temperature}°C"));
    }
}
```

### Register Agent with Tools

```csharp
var weatherFunctions = new WeatherFunctions(weatherService);

var agent = new ChatClientAgent(
    chatClient,
    new ChatClientAgentOptions
    {
        Name = "WeatherAssistant",
        Instructions = "You help with weather questions. Use available tools.",
        ChatOptions = new ChatOptions
        {
            Tools =
            [
                AIFunctionFactory.Create(weatherFunctions.GetWeatherAsync),
                AIFunctionFactory.Create(weatherFunctions.GetForecastAsync)
            ]
        }
    });
```

### Static Function Tools

```csharp
[Description("Gets the current date and time")]
static string GetCurrentDateTime() => DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");

var agent = chatClient.AsAIAgent(
    instructions: "You are a helpful assistant.",
    tools: [AIFunctionFactory.Create(GetCurrentDateTime)]);
```

---

## Dependency Injection (Web API / Blazor)

### Register Agents with Keyed Services

```csharp
// Program.cs
builder.AddAIAgent("OrderAnalyzer", (sp, key) =>
{
    var chatClient = sp.GetRequiredService<IChatClient>();
    var orderFunctions = sp.GetRequiredService<OrderFunctions>();

    return new ChatClientAgent(
        chatClient,
        name: key,
        instructions: "You are an order analysis expert.",
        tools:
        [
            AIFunctionFactory.Create(orderFunctions.GetOrderAsync),
            AIFunctionFactory.Create(orderFunctions.CalculateDiscountAsync)
        ]);
});

// Simple agent (no tools)
builder.AddAIAgent(
    name: "Editor",
    instructions: "You are an editor who improves text quality.");
```

### Inject in Controllers / Blazor Components

```csharp
// Controller
public class AgentController(
    [FromKeyedServices("OrderAnalyzer")] AIAgent orderAgent) : ControllerBase
{
    [HttpPost("analyze")]
    public async Task<IActionResult> Analyze(OrderRequest request)
    {
        var response = await orderAgent.RunAsync(request.Prompt);
        return Ok(response.Text);
    }
}

// Blazor component
@inject IServiceProvider ServiceProvider
@code {
    private AIAgent _agent = default!;

    protected override void OnInitialized()
    {
        _agent = ServiceProvider.GetRequiredKeyedService<AIAgent>("OrderAnalyzer");
    }
}
```

---

## Streaming

```csharp
await foreach (var update in agent.RunStreamingAsync(
    "Write a detailed analysis.",
    cancellationToken: ct))
{
    if (update is AgentResponseUpdateEvent e)
    {
        Console.Write(e.Data);
    }
}
```

---

## Recommended Models

| Use Case | Model | Provider | Cost (per 1M input tokens) |
|----------|-------|----------|---------------------------|
| Simple tasks (default) | gpt-4o-mini | Azure OpenAI | $0.15 |
| Complex analysis | gpt-4o | Azure OpenAI | $2.50 |
| Embeddings | text-embedding-3-small | Azure OpenAI | $0.02 |
| Local development | llama3.2:3b | Ollama | Free |
| Cloud development | gpt-4o-mini | GitHub Models | Free (preview) |

**MORPH-SPEC default:** `gpt-4o-mini` for production.

---

## Security

### API Keys in Azure Key Vault

```csharp
if (builder.Environment.IsProduction())
{
    var keyVaultUri = new Uri(builder.Configuration["KeyVault:Uri"]!);
    builder.Configuration.AddAzureKeyVault(keyVaultUri, new DefaultAzureCredential());
}
```

### Managed Identity (Recommended)

```csharp
// Use DefaultAzureCredential — works with Managed Identity in production,
// AzureCliCredential in development
var client = new AzureOpenAIClient(
    new Uri(config["AzureOpenAI:Endpoint"]!),
    new DefaultAzureCredential());
```

---

## Observability

```csharp
// Enable OpenTelemetry on agents
agent.WithOpenTelemetry();

// Program.cs — Aspire Dashboard integration
builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        tracing.AddSource("Experimental.Microsoft.Extensions.AI.*");
        tracing.AddAspNetCoreInstrumentation();
    })
    .WithMetrics(metrics =>
    {
        metrics.AddMeter("Experimental.Microsoft.Extensions.AI.*");
        metrics.AddAspNetCoreInstrumentation();
    });
```

---

## Limitations & Troubleshooting

| Problem | Cause | Solution |
|---------|-------|----------|
| Tool calling fails | Model < 3B params | Use gpt-4o-mini minimum |
| Rate limit errors | Too many requests | Add retry middleware (see production.md) |
| Tools not invoked | Missing `[Description]` | Add `[Description]` to method AND parameters |
| Agent returns empty | No instructions | Always set `Instructions` in agent options |

---

## Checklist

- [ ] `Microsoft.Agents.AI` + `Microsoft.Agents.AI.OpenAI` installed
- [ ] `IChatClient` registered as singleton via DI
- [ ] Agents created with `ChatClientAgent` or `AsAIAgent()`
- [ ] Agents registered with `builder.AddAIAgent()` (keyed services)
- [ ] Tools use `AIFunctionFactory.Create()` + `[Description]`
- [ ] API keys in Key Vault (production)
- [ ] Managed Identity configured
- [ ] gpt-4o-mini as default model
- [ ] OpenTelemetry enabled on agents
- [ ] Error handling via middleware (see `production.md`)

---

## What NOT To Do

| Wrong | Correct |
|-------|---------|
| `using Microsoft.SemanticKernel` | `using Microsoft.Agents.AI` |
| `Kernel.InvokePromptAsync()` | `agent.RunAsync()` |
| `[KernelFunction]` | `AIFunctionFactory.Create()` |
| `[AgentTool]` attribute | `[Description]` + `AIFunctionFactory` |
| `AgentTool.FromMethod()` | `AIFunctionFactory.Create(method)` |
| `_chatClient.CreateAgent()` | `new ChatClientAgent()` or `.AsAIAgent()` |
| `agent.CreateThreadAsync()` | Use session/conversation management |
| `ChatHistory` | `IEnumerable<ChatMessage>` |
| Hardcoded API keys | Key Vault / Managed Identity |
| Models < 3B for tools | gpt-4o-mini minimum |

---

## References

- [Agent Framework Quick Start](https://learn.microsoft.com/agent-framework/tutorials/quick-start)
- [Agent Tools](https://learn.microsoft.com/agent-framework/user-guide/agents/agent-tools)
- [Microsoft.Extensions.AI](https://learn.microsoft.com/dotnet/ai/ai-extensions)
- [GitHub: microsoft/agent-framework](https://github.com/microsoft/agent-framework)
- `workflows.md` — Orchestration patterns
- `production.md` — Middleware, A2A, MCP, caching
- `blazor-ui.md` — Natural Language First UI patterns

---

*MORPH-SPEC by Polymorphism Tech*
