# Microsoft Agent Framework - Setup Obrigatório (.NET 10)

> **IMPORTANTE:** O MORPH-SPEC usa **exclusivamente** o Microsoft Agent Framework.
> Semantic Kernel foi descontinuado. Não use mais.

---

## 📦 Packages Necessários

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

**Nota:** O package `Microsoft.Agents.AI` já traz as dependências:
- `Microsoft.Extensions.AI`
- `OpenAI` (cliente)
- Suporte a múltiplos providers (Azure OpenAI, GitHub Models, Ollama)

---

## ⚙️ Configuração no Program.cs

### 1. Registrar ChatClient (Azure OpenAI)

```csharp
using Microsoft.Extensions.AI;

var builder = WebApplication.CreateBuilder(args);

// Registrar ChatClient como singleton
builder.Services.AddSingleton<IChatClient>(sp =>
{
    var config = sp.GetRequiredService<IConfiguration>();

    return new ChatClient(
        model: "gpt-4o-mini", // Modelo padrão para tasks simples
        credential: new ApiKeyCredential(config["AzureOpenAI:ApiKey"]!),
        endpoint: new Uri(config["AzureOpenAI:Endpoint"]!)
    );
});
```

### 2. Registrar ChatClient (GitHub Models - Desenvolvimento)

```csharp
builder.Services.AddSingleton<IChatClient>(sp =>
{
    var config = sp.GetRequiredService<IConfiguration>();

    return new ChatClient(
        model: "gpt-4.1-mini",
        credential: new ApiKeyCredential(config["GitHub:Token"]!),
        endpoint: new Uri("https://models.github.com")
    );
});
```

### 3. Registrar ChatClient (Ollama - Local)

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

**Escolha o provider adequado ao ambiente.**

---

## 🤖 Pattern de Agent (Obrigatório)

### Interface do Agent

```csharp
public interface IOrderAnalysisAgent
{
    Task<string> AnalyzeAsync(Order order, CancellationToken ct = default);
}
```

### Implementação do Agent

```csharp
using Microsoft.Agents.AI;

public class OrderAnalysisAgent : IOrderAnalysisAgent
{
    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)
    {
        // Criar agente com instruções específicas
        var agent = _chatClient.CreateAgent(
            instructions: """
                Você é um especialista em análise de pedidos de e-commerce.

                Analise o pedido fornecido e retorne:
                1. Resumo do pedido
                2. Sugestões de upsell baseadas nos itens
                3. Risco de cancelamento (baixo/médio/alto) com justificativa

                Seja conciso e objetivo.
                """,
            name: "OrderAnalyzer"
        );

        // Criar prompt com dados do pedido
        var prompt = $"""
            Pedido: {order.OrderNumber}
            Total: {order.Total:C}
            Itens: {order.Items.Count}
            Cliente: {order.Customer.Name}
            Histórico: {order.Customer.TotalOrders} pedidos anteriores

            Analise este pedido.
            """;

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

        // Executar agente
        var response = await agent.RunAsync(prompt, cancellationToken: ct);

        return response.Content;
    }
}
```

### Registrar Agent como Service

```csharp
// Program.cs
builder.Services.AddScoped<IOrderAnalysisAgent, OrderAnalysisAgent>();
```

---

## 🔧 Agents com Tools (Function Calling)

### Definir Tools

```csharp
public class WeatherAgent
{
    private readonly IChatClient _chatClient;
    private readonly IWeatherService _weatherService;

    public WeatherAgent(IChatClient chatClient, IWeatherService weatherService)
    {
        _chatClient = chatClient;
        _weatherService = weatherService;
    }

    // Tool 1: Buscar clima
    [AgentTool("get_weather")]
    [Description("Busca o clima atual de uma cidade")]
    public async Task<string> GetWeatherAsync(
        [Description("Nome da cidade")] string city,
        CancellationToken ct = default)
    {
        var weather = await _weatherService.GetCurrentWeatherAsync(city, ct);
        return $"Temp: {weather.Temperature}°C, Condição: {weather.Condition}";
    }

    // Tool 2: Previsão
    [AgentTool("get_forecast")]
    [Description("Busca a previsão para os próximos dias")]
    public async Task<string> GetForecastAsync(
        [Description("Nome da cidade")] string city,
        [Description("Número de dias")] 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"));
    }

    public async Task<string> ChatAsync(string userMessage, CancellationToken ct = default)
    {
        // Criar agente com tools
        var agent = _chatClient.CreateAgent(
            instructions: """
                Você é um assistente de clima.
                Use as tools disponíveis para responder perguntas sobre o tempo.
                Seja amigável e informativo.
                """,
            name: "WeatherAssistant",
            tools: new[]
            {
                AgentTool.FromMethod(nameof(GetWeatherAsync), this),
                AgentTool.FromMethod(nameof(GetForecastAsync), this)
            }
        );

        var response = await agent.RunAsync(userMessage, cancellationToken: ct);
        return response.Content;
    }
}
```

**Nota:** Cada método com `[AgentTool]` vira uma tool que o agente pode chamar automaticamente.

---

## 💬 Threads (Persistência de Conversação)

### Criar e Usar Thread

```csharp
public class ChatService
{
    private readonly IChatClient _chatClient;
    private readonly Dictionary<string, AgentThread> _threads = new();

    public async Task<string> StartConversationAsync(string userId)
    {
        var agent = _chatClient.CreateAgent(
            instructions: "Você é um assistente útil e amigável.",
            name: "ChatAssistant"
        );

        // Criar thread
        var thread = await agent.CreateThreadAsync();

        // Armazenar thread ID para o usuário
        _threads[userId] = thread;

        return thread.Id;
    }

    public async Task<string> SendMessageAsync(
        string userId,
        string message,
        CancellationToken ct = default)
    {
        if (!_threads.TryGetValue(userId, out var thread))
            throw new InvalidOperationException("Thread não encontrada");

        var agent = _chatClient.CreateAgent(
            instructions: "Você é um assistente útil e amigável.",
            name: "ChatAssistant"
        );

        // Adicionar mensagem do usuário
        await thread.AddMessageAsync(message, ct);

        // Executar agente no contexto do thread
        var response = await agent.RunAsync(thread, cancellationToken: ct);

        // Histórico é mantido automaticamente
        return response.Content;
    }
}
```

**Vantagem:** Threads mantêm todo o histórico da conversa automaticamente.

---

## 🔄 Multi-Agent Communication (A2A)

### Agent-to-Agent Pattern

```csharp
public class ResearchAgent
{
    private readonly IChatClient _chatClient;

    public async Task<string> ResearchTopicAsync(string topic)
    {
        // Agent 1: Researcher
        var researcher = _chatClient.CreateAgent(
            instructions: "Você pesquisa tópicos e fornece informações detalhadas.",
            name: "Researcher"
        );

        // Agent 2: Summarizer
        var summarizer = _chatClient.CreateAgent(
            instructions: "Você resume textos longos de forma concisa.",
            name: "Summarizer"
        );

        // Passo 1: Pesquisar
        var research = await researcher.RunAsync($"Pesquise sobre: {topic}");

        // Passo 2: Resumir resultado da pesquisa
        var summary = await summarizer.RunAsync(
            $"Resuma o seguinte texto:\n\n{research.Content}"
        );

        return summary.Content;
    }
}
```

---

## 📊 Modelos Recomendados

| Uso | Modelo | Provider | Custo |
|-----|--------|----------|-------|
| Tarefas simples | gpt-4o-mini | Azure OpenAI | $0.15/1M input |
| Análise complexa | gpt-4o | Azure OpenAI | $2.50/1M input |
| Desenvolvimento local | llama3.2:3b | Ollama | Grátis |
| Desenvolvimento cloud | gpt-4.1-mini | GitHub Models | Grátis (beta) |

**Padrão MORPH-SPEC:** `gpt-4o-mini` para produção.

---

## 🔐 Segurança

### API Keys no Azure Key Vault

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

builder.Services.AddSingleton<IChatClient>(sp =>
{
    var config = sp.GetRequiredService<IConfiguration>();

    // API Key vem do Key Vault em produção
    var apiKey = config["AzureOpenAI:ApiKey"]!;
    var endpoint = config["AzureOpenAI:Endpoint"]!;

    return new ChatClient(
        model: "gpt-4o-mini",
        credential: new ApiKeyCredential(apiKey),
        endpoint: new Uri(endpoint)
    );
});
```

### Managed Identity (Recomendado)

```csharp
builder.Services.AddSingleton<IChatClient>(sp =>
{
    var config = sp.GetRequiredService<IConfiguration>();
    var endpoint = config["AzureOpenAI:Endpoint"]!;

    return new ChatClient(
        model: "gpt-4o-mini",
        credential: new DefaultAzureCredential(), // Managed Identity
        endpoint: new Uri(endpoint)
    );
});
```

---

## 📈 Observabilidade com .NET Aspire

### Integrar com Aspire Dashboard

```csharp
// Program.cs
builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        tracing.AddSource("Microsoft.Agents.AI");
        tracing.AddAspireTracing();
    })
    .WithMetrics(metrics =>
    {
        metrics.AddMeter("Microsoft.Agents.AI");
        metrics.AddAspireMetrics();
    });
```

**Resultado:** Visualize chamadas a agentes e tools no Aspire Dashboard.

---

## ⚠️ Limitações e Troubleshooting

### Tool Calling com Modelos Pequenos

**Problema:** Modelos abaixo de 3B parâmetros não fazem tool calling bem.

**Solução:** Use `gpt-4o-mini` (mínimo) para agents com tools.

### Rate Limits

```csharp
public async Task<string> AnalyzeWithRetryAsync(Order order, CancellationToken ct = default)
{
    var retryPolicy = Policy
        .Handle<RateLimitException>()
        .WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)));

    return await retryPolicy.ExecuteAsync(async () =>
    {
        var agent = _chatClient.CreateAgent(...);
        var response = await agent.RunAsync(..., cancellationToken: ct);
        return response.Content;
    });
}
```

---

## ✅ Checklist de Implementação

- [ ] Package `Microsoft.Agents.AI` instalado
- [ ] `IChatClient` registrado como singleton
- [ ] Agents criados com `client.CreateAgent()`
- [ ] Agents registrados como services (scoped/transient)
- [ ] API keys no Key Vault (produção)
- [ ] Logging configurado para agents
- [ ] Observabilidade com Aspire (opcional)
- [ ] Error handling para rate limits
- [ ] Testes de integração para agents

---

## 🚫 O Que NÃO Fazer

❌ Usar Semantic Kernel
❌ Usar `Kernel.CreateFunctionFromPrompt()`
❌ Usar `ChatHistory` (use Threads)
❌ Hardcoded API keys
❌ Modelos pequenos (<3B) para tool calling

---

## 📚 Referências

- [Microsoft Agent Framework Documentation](https://learn.microsoft.com/agent-framework/)
- [Agent Framework GitHub](https://github.com/microsoft/agent-framework)
- [Microsoft.Extensions.AI](https://learn.microsoft.com/dotnet/ai/ai-extensions)
- [Azure OpenAI Service](https://learn.microsoft.com/azure/ai-services/openai/)

---

*MORPH-SPEC by Polymorphism Tech*
