

Introduction
De plus en plus de solutions intègrent des agents IA : des composants logiciels autonomes capables de raisonner, de planifier, d’appeler des API, de collaborer entre eux et de s’adapter pour atteindre un objectif donné. Pour concevoir ces agents, plusieurs frameworks existent déjà, comme LangChain, Semantic Kernel ou AutoGen. Mais j’ai récemment découvert un framework qui simplifie réellement la création d’agents.
Il s’agit de Microsoft Agent Framework (MAF), un SDK open source publié par Microsoft en octobre 2025, qui permet de construire et d’orchestrer des systèmes multi-agents en Python et en .NET.
Dans cet article je vous fais decouvrir ce framework par la pratique : construire un vrai pipeline multi-agents en C#, avec Ollama en local, des outils, un moyen d'accéder à un modèle de contexte (MCP), et un workflow d'orchestration. Ces concepts seront introduits au fil du code.
Préparez votre café ☕, on commence.
Microsoft Agent Framework : qu'est-ce que c'est exactement ?
Pour comprendre MAF, il faut connaître ses prédécesseurs :
- Semantic Kernel est le SDK Microsoft pour l'IA. Bien outillé, avec de la télémétrie, de la gestion d'état, des connecteurs vers les systèmes internes, mais parfois un peu verbeux et avec une courbe d'apprentissage un peu raide.
- AutoGen est aussi un framework de Microsoft dédié à l'orchestration multi-agents. Il est innovant, mais n'a pas d'outillage pour la production (pas de télémétrie intégrée, pas de gestion d'état robuste).
L'idée avec Agent Framework est de combiner les deux. La documentation officielle le dit clairement :
"Agent Framework combines AutoGen's simple agent abstractions with Semantic Kernel's enterprise features — session-based state management, type safety, middleware, telemetry — and adds graph-based workflows for explicit multi-agent orchestration."
MAF est disponible sur GitHub (microsoft/agent-framework) sous licence MIT.
Pourquoi utiliser MAF ?
MAF offre une flexibilité pour les développeurs. Il va nous permettre de créer, de déployer rapidement et efficacement des agents IA.
Accélérer le prototypage et la mise en production
MAF nous offre des abstractions de haut niveau : sessions, mémoire, orchestration, et observabilité permettant aux développeurs de se libérer des tâches d'ingénierie à faible valeur ajoutée pour se concentrer sur la logique métier et la création de valeur.
MAF, un outil puissant, une prise en main immédiate
Avec MAF, Microsoft permet aux développeurs d'implémenter en quelques lignes des architectures multi-agents. Concrètement, MAF nous permet de :
- Expérimenter en local, puis déployer sur Azure AI Foundry sans réécriture.
- Connecter n'importe quelle API via OpenAPI, collaborer entre runtimes grâce au protocole Agent2Agent (A2A), et brancher dynamiquement des outils externes via MCP.
- Orchestrer des agents dans des workflows en utilisant les patterns les plus récents comme Magentic-One.
- Supprimer les frictions entre outils et plateformes : fini le jonglage entre SDK incompatibles !
- Interconnecter vos systèmes multi-agents avec Azure AI Foundry, Microsoft 365 Copilot et d'autres plateformes d'agents tierces.
Le cas d'étude : un pipeline d'analyse de tickets support à traiter
Imaginons une équipe support qui reçoit des dizaines de tickets par jour. Notre objectif sera d'automatiser leur traitement en les faisant passer par 3 agents spécialisés, qui se passent le relai.
Voici le pipeline que nous allons construire :

- Le Triage lit le ticket et décide s'il est pertinent
- L'Analyste creuse le problème, consulte l'historique via un outil local et interroge une base de données via MCP
- Le Rédacteur génère une réponse claire destinée au client
Étape 0 — Ollama et packages NuGet
MAF est provider-agnostic : le même code fonctionne avec Azure OpenAI, OpenAI, GitHub Models, ou un modèle local. On choisit ici Ollama.
<pre><code># Installer Ollama : https://ollama.com
ollama pull qwen3:4b</code></pre>
⚠️ Tous les modèles Ollama ne supportent pas function calling. Utilisezqwen3:4boullama3.2pour cet exemple.
Créer un projet console puis ajouter les packages NuGet suivants :
<pre><code>dotnet add package OllamaSharp
dotnet add package Microsoft.Agents.AI --version 1.3.0
dotnet add package Microsoft.Agents.AI.Workflows
dotnet add package Microsoft.Data.Sqlite
dotnet add package ModelContextProtocol</code></pre>
Étape 1 — Les outils locaux
Un agent qui ne peut que "appeler un LLM" peu être limité. Les outils lui permettent d'agir sur un environnement donné : appeler une API, interroger une base de données, exécuter du code... Dans MAF, un outil est simplement une méthode C# décorée avec [Description].
<pre><code>public static class SupportTools
{
[Description("Calcule la priorité d'un ticket selon sa criticité.")]
public static string CalculatePriority(
[Description("Niveau de criticité : low, medium, high, critical")] string severity)
{
return severity switch
{
"critical" => "P0 - Intervention immédiate",
"high" => "P1 - Traitement sous 4h",
"medium" => "P2 - Traitement sous 8h",
_ => "P3 - Traitement sous 24h"
};
}
}</code></pre>
💡MAFconvertit automatiquement ces méthodes enAIToolvia la réflexion .NET. Pas de schéma JSON à écrire à la main.
Étape 2 — La base de connaissances
Notre Analyste va avoir besoin de consulter la documentation interne pour identifier les problèmes connus. On crée une base SQLite peuplée au démarrage de l'app :
<pre><code>public static class KnowledgeBaseSeeder
{
public static void Seed(string dbPath)
{
using var connection = new SqliteConnection($"Data Source={dbPath}");
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = """
CREATE TABLE IF NOT EXISTS known_issues (
id INTEGER PRIMARY KEY AUTOINCREMENT,
type TEXT NOT NULL,
error_code TEXT,
description TEXT NOT NULL,
solution TEXT NOT NULL,
introduced TEXT
)
""";
command.ExecuteNonQuery();
}
// Idempotent : on ne re-seede pas si des données existent déjà
using (var countCommand = connection.CreateCommand())
{
countCommand.CommandText = "SELECT COUNT(*) FROM known_issues";
var result = countCommand.ExecuteScalar();
var count = result != null ? (long)result : 0;
if (count > 0) return;
}
var entries = new[]
{
new {
Type = "403",
ErrorCode = "AUTH_TOKEN_EXPIRED",
Description = "Erreur 403 Forbidden après la mise à jour du 10/03/2026. " +
"Les tokens OAuth expirent prématurément (bug #4521).",
Solution = "Se déconnecter et vider le cache navigateur. " +
"Si persistant : POST /auth/refresh. Fix en v2.4.1.",
Introduced = "2026-03-10"
},
new {
Type = "performance",
ErrorCode = "DASHBOARD_SLOW",
Description = "Lenteurs dashboard pour comptes > 500 entrées. " +
"Requête N+1 sur /metrics (ticket #4389).",
Solution = "Contournement : activer 'Vue allégée' dans les paramètres.",
Introduced = "2026-02-01"
},
new {
Type = "billing",
ErrorCode = "INVOICE_MISMATCH",
Description = "Écart de facturation sur changement de plan en cours de mois.",
Solution = "Remboursement manuel via backoffice. Contacter l'équipe finance.",
Introduced = "2026-01-15"
}
};
foreach (var entry in entries)
{
using (var insertCommand = connection.CreateCommand())
{
insertCommand.CommandText = """
INSERT INTO known_issues (type, error_code, description, solution, introduced)
VALUES (@Type, @ErrorCode, @Description, @Solution, @Introduced)
""";
insertCommand.Parameters.AddWithValue("@Type", entry.Type);
insertCommand.Parameters.AddWithValue("@ErrorCode", entry.ErrorCode ?? (object)DBNull.Value);
insertCommand.Parameters.AddWithValue("@Description", entry.Description);
insertCommand.Parameters.AddWithValue("@Solution", entry.Solution);
insertCommand.Parameters.AddWithValue("@Introduced", entry.Introduced);
insertCommand.ExecuteNonQuery();
}
}
Console.WriteLine($"BD initialisée ({entries.Length} entrées)");
}
}</code></pre>
Dans Program.cs, on appelle le seeder avant tout :
<pre><code>KnowledgeBaseSeeder.Seed("knowledge_base.db");</code></pre>
Étape 3 — Le serveur MCP
Plutôt que d'écrire un connecteur SQLite à la main, on va utiliser MCP (Model Context Protocol), un standard ouvert qui permet à un agent de découvrir et d'utiliser des outils fournis par un serveur externe.
<pre><code>using ModelContextProtocol.Client;
await using McpClient mcpClient = await McpClient.CreateAsync(
new StdioClientTransport(new StdioClientTransportOptions
{
Name = "KnowledgeBase",
Command = "uvx",
Arguments = ["mcp-server-sqlite", "--db-path", "knowledge_base.db"]
}));
// Les outils sont découverts automatiquement
var mcpTools = await mcpClient.ListToolsAsync();
Console.WriteLine($"Outils MCP : {string.Join(", ", mcpTools.Select(t => t.Name))}");</code></pre>
Installer ensuite uv (qui inclut uvx) depuis : https://github.com/astral-sh/uv
Le serveur MCP SQLite tourne dans son propre processus Python. Notre agent .NET lui parle via stdio de manière totalement transparente.
📌 C'est toute la promesse de MCP : le serveur est en Python, l'agent en .NET, et ça fonctionne. Les outils peuvent être partagés entre plusieurs projets et équipes, développés indépendamment.
Étape 4 — Les agents
Dans MAF, l'unité de base est ChatClientAgent, une implémentation de AIAgent qui accepte n'importe quel IChatClient. On crée nos 3 agents en leur donnant chacun un rôle précis via ses instructions, et uniquement les outils dont il a besoin.
<pre><code>// Un seul client Ollama partagé. Pas de cloud, pas de clé API.
var chatClient = new OllamaApiClient(
new Uri("http://localhost:11434"),
"qwen3:4b");
// Outil local pour l'Analyste
var localTool = AIFunctionFactory.Create(SupportTools.CalculatePriority);
// Agent 1 : Triage — raisonne sur le texte. Pas d'outils.
ChatClientAgent triageAgent = new(chatClient, """
Tu es un agent de triage support.
Lis le ticket, vérifie qu'il est complet et pertinent.
Résume le problème en 2-3 phrases claires.
Identifie : le type d'erreur, l'ID client, le niveau d'urgence.
""", "Triage");
// Agent 2 : Analyste — consulte l'historique et la base de connaissances
ChatClientAgent analysteAgent = new(chatClient, """
Tu es un analyste support expérimenté. Suis ces étapes dans l'ordre :
1. Identifie le type d'erreur du ticket (ex: "403", "performance", "billing").
2. Appelle read_query avec : SELECT * FROM known_issues WHERE type = '<type_identifié>'
puis analyse la réponse pour vérifier si ce problème est déjà répertorié et résolu.
3. Appelle CalculatePriority selon la gravité du ticket.
Fournis : si le problème est connu (oui/non), la solution trouvée, la priorité.
""", "Analyste",
tools: [localTool, .. mcpTools]);
// Agent 3 : Rédacteur — traduit l'analyse en communication client
ChatClientAgent redacteurAgent = new(chatClient, """
Tu es un expert en communication client.
À partir de l'analyse fournie, rédige OBLIGATOIREMENT ces deux blocs :
[CLIENT] : réponse claire, sans jargon technique
[INTERNE] : note technique pour l'équipe support: cause, priorité, solution, actions à faire
""", "Redacteur");</code></pre>
Étape 5 — Le workflow
On définit maintenant le graphe d'exécution : ce sont des étapes exécutées dans un ordre fixe. On parle de Sequential Workflow. Un agent transfère le contrôle complet au suivant quand il estime avoir terminé.
<pre><code>var workflowAgent = AgentWorkflowBuilder.BuildSequential(
triageAgent,
analysteAgent,
redacteurAgent
).AsAIAgent();</code></pre>
📌 MAF propose d'autres patterns selon vos besoins : Handoff (étapes dynamique), Concurrent (agents en parallèle), Group Chat (discussion commune), Magentic-One (un manager coordonne des sous-agents).Étape 6 — Exécution
Maintenant que notre pipeline est en place, il ne nous reste plus qu'à lui soumettre un ticket de test.
<pre><code>var ticket = """
De : jean.dupont@acme.fr
Objet : Impossible de me connecter depuis ce matin
Salut,
Je n'arrive plus à accéder à mon dashboard.
J'obtiens une erreur "403 Forbidden". Mon ID client est ACME-00421. C'est urgent, j'ai une démo client dans 2 heures !
""";
var response = await workflowAgent.RunAsync(ticket);
Console.WriteLine(response);</code></pre>
Et voici le résultat obtenu dans la console :

💡 Pour aller loin — Utiliser un Middleware pour tracer chaque action de chaque agent. En production, on branche OpenTelemetry pour envoyer ces traces vers Azure Monitor ou tout autre dashboard de monitoring.Conclusion
On a seulement touché du doigt les concepts de MAF, mais les workflows peuvent aller bien plus loin : human-in-the-loop, agents distribués via A2A... Mais en tout cas, nous venons de voir l'essentiel. Vous avez maintenant les bases pour construire vos propres pipelines.
La meilleure façon de continuer ? Prendre ce cas d'étude, remplacer Ollama par le modèle de votre choix, adapter les outils à votre domaine métier et voir jusqu'où ça va.
Le code complet de cet article est disponible sur GitHub →
Pour aller plus loin, voici quelques ressources supplémentaires :
.png)
Brahima est développeur full-stack avec une préférence pour le backend .Net.
Il s’intéresse particulièrement à l’architecture logicielle et aux applications concrètes de l’intelligence artificielle. Pour lui, une application, c’est comme un poème : chaque ligne de code est un vers, chaque fonction une strophe, et quand tout est bien structuré, l’ensemble devient fluide, lisible… et (presque) agréable à maintenir, sauf quand il faut déboguer à 17h un vendredi.
Curieux de nature, il aime partager ses réflexions et apprentissages. En dehors du code, il apprécie le sport.


