# Microsoft Agent Framework — Production Patterns

> **Scope:** stacks=["dotnet"]
> **Layer:** 1 (on-keyword)
> **Keywords:** middleware, observability, telemetry, mcp, a2a, caching, anti-hallucination, production agent
> **Read by Claude in:** implement (when wiring middleware/telemetry/MCP into an agent)

**Verified against:** Microsoft.Agents.AI 1.20.0 (ai-pin 2026-09-08) + Microsoft.Agents.AI.Hosting `1.20.0-preview.260831.1` e Microsoft.Agents.AI.A2A `1.20.0-preview.260831.1` (**fora do pin**; versões apuradas no nuget.org em 2026-09-08). **Verificação documental**, sem cláusula `provado por` — este standard é panorama, e cada tema tem dono canônico noutro standard. Os nomes de isolamento multi-tenant foram **medidos por reflexão sobre `Microsoft.Agents.AI.Hosting`** em 2026-09-08. Last-verified: 2026-09-08.

---

## Middleware Pipeline

Três tipos de middleware interceptam o comportamento do agent em camadas distintas:

| Tipo | Intercepts | Use Case |
|------|-----------|----------|
| **Agent Run** | Invocação completa do agent | Logging, auth, rate limiting |
| **Function Calling** | Chamadas individuais de tools | Error handling, validação, auditoria |
| **IChatClient** | Chamadas brutas ao LLM | Telemetry, caching, content filtering |

O **tratamento canônico** de middleware — código dos 3 níveis, registro via `.AsBuilder().Use(...)`,
e os patterns de telemetria, resilência (retry + rate-limit) e anti-hallucination — está em
**`ai-agents-middleware-patterns`**. Não duplicado aqui.

> Em produção: registre `ResilientToolMiddleware` (ou equivalente) em qualquer agent que chame
> APIs externas, e um middleware de telemetria no `IChatClient`. Ver `ai-agents-middleware-patterns`
> para o código e a decision tree de qual nível usar.

---

## A2A Protocol (Agent-to-Agent)

Exponha agents como serviços web interoperáveis via protocolo A2A padronizado.

**Package — PRERELEASE, e fora do `ai-pin.json`:**

```xml
<!-- Apurado no nuget.org em 2026-09-08: não há versão estável de Microsoft.Agents.AI.A2A. -->
<PackageReference Include="Microsoft.Agents.AI.A2A" Version="1.20.0-preview.260831.1" />
```

> **Correção de 2026-09-08:** a versão anterior deste standard rotulava o A2A como **GA** com faixa `1.0.*`. Nenhuma das duas coisas se sustenta: o pacote nunca teve versão estável, e faixa flutuante num SDK de cadência semanal é o defeito que `ai-agents-setup` §1 proíbe.

### Expor um agent via A2A

```csharp
// Program.cs
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.A2A;
using Microsoft.Extensions.AI;

var builder = WebApplication.CreateBuilder(args);

// Registrar agent via Model Registry
IChatClient chatClient = /* from ModelRegistry singleton */;
builder.Services.AddSingleton(chatClient);

builder.AddAIAgent("math", instructions: "You are a math expert.");
builder.AddAIAgent("history", instructions: "You are a history expert.");

var app = builder.Build();

app.MapA2A("math", path: "/a2a/math", agentCard: new()
{
    Name = "Math Agent",
    Description = "Expert in mathematics and calculations.",
    Version = "1.0"
});

app.MapA2A("history", path: "/a2a/history", agentCard: new()
{
    Name = "History Agent",
    Description = "Expert in world history.",
    Version = "1.0"
});

app.Run();
```

### Consumir um agent A2A remoto

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

// Wrap a remote A2A endpoint as a local AIAgent
AIAgent remoteAgent = new A2AClient(new Uri("https://agents.example.com/a2a/math"))
    .AsAIAgent();

var response = await remoteAgent.RunAsync("What is the integral of x^2?");
```

### Agent Card Discovery

```
GET /a2a/math/v1/card         → metadata (name, description, capabilities)
POST /a2a/math/v1/message:stream  → send message, get streaming response
```

| Feature A2A | Descrição |
|-------------|-----------|
| Agent Discovery | Agent Cards com metadados padronizados |
| Cross-Framework | Agent .NET pode conversar com agent Python |
| Context Persistence | `contextId` mantém conversa entre mensagens |
| Streaming | Server-Sent Events para respostas em tempo real |

---

## MCP Integration (Model Context Protocol)

Agents MAF podem consumir tools de **MCP servers externos** (GitHub, filesystem, Postgres,
servidores internos) e expor capacidades do projeto **como** um MCP server.

Esses dois casos têm tratamento canônico em standards dedicados — **não duplicado aqui**:

| Caso | Standard |
|------|----------|
| Agente MAF consome um MCP server externo | `ai-agents-mcp-tools` (`McpClientFactory.CreateAsync`, transports, `await using`) |
| Projeto expõe capacidades como MCP server | `ai-agents-mcp-server` (`[McpServerTool]`, `AddMcpServer`, stdio/HTTP) |

> Em produção, lembre: MCP client sempre com `await using` (dispose do transporte); MCP server
> exposto via HTTP exige autenticação. Detalhes nos standards acima.

---

## Caching Patterns

### Semantic Caching (Redis)

Cache de respostas LLM por similaridade semântica — evita inferências repetidas.

> **Illustrative pseudo-code** — the exact Redis vector-search calls depend on your Redis client. For a complete, working semantic-search implementation see `ai-agents-rag-custom-pgvector` (Onda 2). The point of this section is the *pattern*: embed the query, vector-search a cache, return on a similarity hit.

```csharp
public async Task<string?> GetCachedResponseAsync(string question)
{
    var questionEmbedding = await _embedding.GenerateAsync(question);

    // pseudo-code: perform a KNN vector search against the cache index
    var (similarityScore, cachedResponse) = await VectorSearchCacheAsync(
        "idx:semantic_cache", questionEmbedding);

    if (similarityScore >= 0.95)   // cosine similarity: higher = more similar
        return cachedResponse;

    return null;  // cache miss
}
```

**Cost savings:** embedding ~$0.0001/1K tokens vs inferência ~$0.01-0.03/1K tokens = **100-300x savings**.

### Hybrid Cache (L1 + L2)

```csharp
builder.Services.AddHybridCache(options =>
{
    options.DefaultEntryOptions = new HybridCacheEntryOptions
    {
        Expiration = TimeSpan.FromMinutes(10),
        LocalCacheExpiration = TimeSpan.FromMinutes(1)
    };
});

// Uso num tool
[Description("Gets weather for a location")]
public async Task<WeatherResponse> GetWeatherAsync(
    string location, string date, CancellationToken ct)
{
    return await _cache.GetOrCreateAsync(
        $"weather:{location}:{date}",
        async token => await FetchFromApiAsync(location, date, token),
        cancellationToken: ct);
}
```

| Level | Location | Speed | Scope |
|-------|----------|-------|-------|
| L1 | Process memory | Fastest | Per instance |
| L2 | Redis | Very fast | Distributed |

---

## Observability / OpenTelemetry

Setup, enrichment (span attributes, correlation IDs, structured logging) e o workflow de investigação/análise de logs em produção têm **tratamento canônico** em **`ai-agents-observability-patterns`**. Não duplicado aqui.

> Em produção: `AddSource` das **duas** fontes — `Experimental.Microsoft.Agents.AI` e `Experimental.Microsoft.Extensions.AI` — e `EnableSensitiveData = false` fora de dev/staging. (Correção de 2026-09-08: `AgentOpenTelemetryConsts` e `EnableSensitiveTelemetryData` **não existem** no pin; medido.) Ver `ai-agents-observability-patterns` para o setup completo, o que preencher em cada span/log, e o workflow de investigação quando um incidente for reportado.

---

## Isolamento multi-tenant — renomeado em 1.18

Um agente registrado como singleton atende todos os tenants. O que separa um do outro não é o agente: é a **chave de isolamento** que decide de qual balde vem a sessão.

Na **1.18** (2026-08-18) essa superfície foi renomeada. Nomes atuais, medidos por reflexão sobre `Microsoft.Agents.AI.Hosting` (pacote `1.20.0-preview.260831.1`) em 2026-09-08:

| Tipo | Papel |
|---|---|
| `Microsoft.Agents.AI.Hosting.AgentIsolationKeyProvider` | Produz a chave de isolamento do request corrente |
| `Microsoft.Agents.AI.Hosting.IsolationKeyScopedAgentSessionStore` | O `AgentSessionStore` que aplica a chave; tem `IsolationKeyScopedAgentSessionStoreOptions` |
| `UseClaimsBasedAgentIsolation()` (ASP.NET Core) | Deriva a chave das claims do usuário autenticado |

**Duas advertências que valem mais que os nomes:**

1. **O pacote é prerelease.** `Microsoft.Agents.AI.Hosting` não tem versão estável (apurado 2026-09-08). Isolamento multi-tenant por esse caminho é adoção de preview — registre em `decisions.md`.
2. **A chave vem da credencial, nunca do payload.** Uma chave derivada de algo que o cliente envia é uma chave que o cliente escolhe. `UseClaimsBasedAgentIsolation()` existe exatamente para não deixar essa decisão passar pelo corpo do request.

### Invocação concorrente de tools é opt-in desde 1.18

`FunctionInvokingChatClient.AllowConcurrentInvocation` (exposto como `ChatClientAgentOptions.AllowConcurrentInvocation`) tem default **`false`**: numa volta com várias chamadas de tool, elas rodam **em sequência**. Ligar a concorrência é decisão explícita — e só é segura se **todas** as tools daquele agente forem thread-safe. Um `DbContext` do EF Core **não** é: duas tools concorrentes sobre o mesmo `DbContext` scoped produzem `InvalidOperationException` intermitente sob carga, que é o pior formato possível de bug.

---

## Security Checklist (verifiable by morph-eval)

- [ ] API keys nunca em `appsettings.json` no repositório — bind de environment variables ou secret store
- [ ] Variáveis de provider (`OpenAI__ApiKey`, `Google__ApiKey`, `Anthropic__ApiKey`) em env vars / Docker secrets
- [ ] Input validation middleware registrado em agents públicos (PII detection, injection prevention)
- [ ] Output filtering / guardrails para conteúdo prejudicial
- [ ] Rate limiting middleware em agents expostos publicamente
- [ ] Endpoints A2A protegidos com autenticação
- [ ] Structured output usado para respostas — evita parsing de texto livre
- [ ] OpenTelemetry com `AddSource` das duas fontes `Experimental.Microsoft.*` configurado
- [ ] Multi-tenant: a chave de isolamento vem da credencial (claims), nunca do payload do request

---

## Production Checklist (verifiable by morph-eval)

- [ ] `Microsoft.Agents.AI` **1.20.0** (versão exata, do `ai-pin.json`) no `.csproj`; nenhum pacote SK de orquestração
- [ ] Nenhum `PackageReference` de SDK de IA com faixa flutuante; pacote fora do pin com versão exata + data de apuração
- [ ] `AllowConcurrentInvocation` continua `false`, ou todas as tools do agente são comprovadamente thread-safe
- [ ] Middleware registrado via `.AsBuilder().Use(...)` antes de `Build()`
- [ ] `ResilientToolMiddleware` (ou equivalente) em agents que chamam APIs externas
- [ ] Semantic cache configurado se o agent recebe queries repetitivas
- [ ] OpenTelemetry source = `Experimental.Microsoft.Agents.AI` **e** `Experimental.Microsoft.Extensions.AI`
- [ ] Dados sensíveis de telemetry desabilitados em produção (`EnableSensitiveData = false`)
- [ ] A2A endpoints com `MapA2A` protegidos por auth se expostos na internet
- [ ] MCP client `await using` para dispose correto do transporte

---

## References

- `ai-agents-setup` — pacotes, DI, quick start
- `ai-agents-sweet-spot` — decisão de arquitetura antes de middleware
- `ai-agents-providers-model-registry` — Model Registry para `_modelRegistry.GetChatClient(...)`
- `ai-agents-middleware-patterns` — **tratamento canônico** de middleware (código dos 3 níveis, telemetria, resilência, anti-hallucination)
- `ai-agents-observability-patterns` — **tratamento canônico** de observability (setup OpenTelemetry, enrichment de spans/logs, workflow de investigação)
- `ai-agents-mcp-tools` — agente MAF consumindo MCP servers externos
- `ai-agents-mcp-server` — projeto expondo capacidades como MCP server
- `ai-agents-rag-custom-pgvector` — implementação completa de busca semântica (referida em Caching)
- [Agent Middleware](https://learn.microsoft.com/agent-framework/user-guide/agents/agent-middleware)
- [A2A Integration](https://learn.microsoft.com/agent-framework/user-guide/hosting/agent-to-agent-integration)
- [Using MCP Tools](https://learn.microsoft.com/agent-framework/user-guide/model-context-protocol/using-mcp-tools)
- [MCP C# SDK](https://github.com/modelcontextprotocol/csharp-sdk)

---

*MORPH-SPEC by Polymorphism Tech — ai-agents/production.md v3.0 (2026-09-08)*
