Création de flux de travail d'agent avec Microsoft Agent Framework
##Présentation
Si vous avez lu mes articles précédents sur Agent Framework de Microsoft, vous savez comment créer des agents individuels et même des discussions de groupe multi-agents. Mais dans les scénarios du monde réel, vous avez souvent besoin de quelque chose de plus structuré : un flux de travail dans lequel les agents s’exécutent dans un ordre spécifique, se transmettent les résultats et gèrent la logique de branchement en fonction des résultats.
C’est exactement ce que vous offrent les fonctionnalités de flux de travail d’Agent Framework. Au lieu de lancer des agents dans une discussion de groupe en espérant qu’ils comprendront, vous définissez des étapes explicites, des dépendances et un flux de données entre les agents.
Que sont les workflows des agents ?
Considérez les flux de travail des agents comme un pipeline. Chaque étape est gérée par un agent spécialisé et le résultat d’une étape alimente la suivante. Vous pouvez exécuter des étapes de manière séquentielle, en parallèle ou conditionnellement en fonction des résultats.
Quelques exemples :
- Pipeline de contenu : Recherche → Brouillon → Révision → Publier
- Traitement des données : Extraire → Transformer → Valider → Charger
- Support client : Classer → Itinéraire → Répondre → Suivi
Prérequis
Assurez-vous de disposer des derniers packages Agent Framework :
dotnet add package Microsoft.SemanticKernel
dotnet add package Microsoft.SemanticKernel.Agents.Core
Et un point de terminaison Azure OpenAI ou OpenAI configuré.
Créer un workflow séquentiel
Créons un workflow de création de contenu avec trois agents : un chercheur, un rédacteur et un éditeur.
Définir les agents
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
var kernel = Kernel.CreateBuilder()
.AddAzureOpenAIChatCompletion(
deploymentName: "gpt-4o",
endpoint: config["AzureOpenAI:Endpoint"],
apiKey: config["AzureOpenAI:ApiKey"])
.Build();
var researcher = new ChatCompletionAgent
{
Name = "Researcher",
Instructions = """
You are a research specialist. Given a topic, find key facts, statistics,
and talking points. Return a structured research brief with bullet points.
Be thorough but concise.
""",
Kernel = kernel
};
var writer = new ChatCompletionAgent
{
Name = "Writer",
Instructions = """
You are a technical blog writer. Given a research brief, write a clear
and engaging blog post. Use a conversational tone, include code examples
where relevant, and structure the post with clear headings.
""",
Kernel = kernel
};
var editor = new ChatCompletionAgent
{
Name = "Editor",
Instructions = """
You are a senior editor. Review the blog post for clarity, accuracy,
grammar, and flow. Return the corrected version with a summary of
changes made at the end.
""",
Kernel = kernel
};
Exécution séquentielle
Le workflow le plus simple est séquentiel : chaque agent traite le résultat du précédent :
using Microsoft.SemanticKernel.Agents.Chat;
using Microsoft.SemanticKernel.ChatCompletion;
async Task<string> RunSequentialWorkflow(string topic)
{
var history = new ChatHistory();
string currentInput = $"Research the following topic: {topic}";
var agents = new[] { researcher, writer, editor };
foreach (var agent in agents)
{
history.AddUserMessage(currentInput);
var response = new System.Text.StringBuilder();
await foreach (var message in agent.InvokeAsync(history))
{
response.Append(message.Content);
}
currentInput = response.ToString();
Console.WriteLine($"✅ {agent.Name} completed");
}
return currentInput;
}
var result = await RunSequentialWorkflow("Blazor render modes in .NET 9");
Console.WriteLine(result);
Chaque agent récupère le contexte accumulé, le traite et le résultat passe à l’étape suivante.
Exécution parallèle
Parfois, les agents peuvent travailler de manière indépendante. Par exemple, vous souhaiterez peut-être rechercher plusieurs sous-thèmes en même temps :
async Task<List<string>> RunParallelResearch(string[] topics)
{
var tasks = topics.Select(async topic =>
{
var history = new ChatHistory();
history.AddUserMessage($"Research: {topic}");
var response = new System.Text.StringBuilder();
await foreach (var message in researcher.InvokeAsync(history))
{
response.Append(message.Content);
}
Console.WriteLine($"✅ Research completed: {topic}");
return response.ToString();
});
var results = await Task.WhenAll(tasks);
return results.ToList();
}
var topics = new[]
{
"Blazor SSR streaming",
"Enhanced navigation in .NET 9",
"Render mode boundaries"
};
var briefs = await RunParallelResearch(topics);
Ensuite, vous pouvez transmettre tous les résumés de recherche à un seul agent rédacteur pour produire un article cohérent.
Branchement conditionnel
Les vrais flux de travail nécessitent des décisions. Peut-être souhaitez-vous une étape de contrôle de qualité qui renvoie à l’auteur si le message n’est pas assez bon :
var qualityChecker = new ChatCompletionAgent
{
Name = "QualityChecker",
Instructions = """
You are a quality assurance reviewer. Evaluate the blog post on:
1. Technical accuracy
2. Clarity and readability
3. Completeness
Respond with either:
- "APPROVED" if the post meets all criteria
- "REVISION NEEDED: [specific feedback]" if changes are required
Be strict. Only approve posts that are truly ready to publish.
""",
Kernel = kernel
};
async Task<string> RunWithQualityLoop(string topic, int maxRevisions = 3)
{
var history = new ChatHistory();
// Step 1: Research
history.AddUserMessage($"Research: {topic}");
string research = await InvokeAgent(researcher, history);
// Step 2: Write
history.AddUserMessage(research);
string draft = await InvokeAgent(writer, history);
// Step 3: Quality loop
for (int i = 0; i < maxRevisions; i++)
{
var qaHistory = new ChatHistory();
qaHistory.AddUserMessage($"Review this post:\n\n{draft}");
string qaResult = await InvokeAgent(qualityChecker, qaHistory);
if (qaResult.Contains("APPROVED", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine($"✅ Approved after {i + 1} review(s)");
return draft;
}
Console.WriteLine($"🔄 Revision {i + 1}: {qaResult[..Math.Min(100, qaResult.Length)]}...");
// Send back to writer with feedback
history.AddUserMessage($"Please revise based on this feedback:\n{qaResult}");
draft = await InvokeAgent(writer, history);
}
Console.WriteLine("⚠️ Max revisions reached, returning latest draft");
return draft;
}
async Task<string> InvokeAgent(ChatCompletionAgent agent, ChatHistory history)
{
var response = new System.Text.StringBuilder();
await foreach (var message in agent.InvokeAsync(history))
{
response.Append(message.Content);
}
return response.ToString();
}
Ce modèle est super utile. Le vérificateur de qualité agit comme une porte et le flux de travail boucle jusqu’à ce que le résultat soit suffisamment bon ou atteigne la limite de révision maximale.
Ajout de plugins aux agents de workflow
Les agents des workflows peuvent utiliser des plugins tout comme les agents autonomes. C’est là que les choses deviennent vraiment puissantes : les agents peuvent appeler des API, interroger des bases de données ou effectuer des opérations sur les fichiers dans le cadre de leur étape de flux de travail :
var researcherWithTools = new ChatCompletionAgent
{
Name = "Researcher",
Instructions = "Research the topic using available tools. Summarize findings.",
Kernel = kernel
};
// Add a web search plugin
kernel.Plugins.AddFromType<WebSearchPlugin>();
// Add a database plugin
kernel.Plugins.AddFromObject(new DatabasePlugin(connectionString), "Database");
public class WebSearchPlugin
{
[KernelFunction, Description("Search the web for information")]
public async Task<string> SearchAsync(
[Description("The search query")] string query)
{
// Your search implementation
using var httpClient = new HttpClient();
var response = await httpClient.GetStringAsync(
$"https://api.search.example.com?q={Uri.EscapeDataString(query)}");
return response;
}
}
Gestion des erreurs dans les workflows
Lorsque vous enchaînez plusieurs agents, la gestion des erreurs devient critique. Vous ne voulez pas qu’une étape échouée fasse planter l’ensemble du flux de travail en silence :
async Task<WorkflowResult> RunResilientWorkflow(string input)
{
var result = new WorkflowResult();
try
{
result.Research = await InvokeAgent(researcher, new ChatHistory(input));
}
catch (Exception ex)
{
result.Errors.Add($"Research failed: {ex.Message}");
return result;
}
try
{
var writeHistory = new ChatHistory(result.Research);
result.Draft = await InvokeAgent(writer, writeHistory);
}
catch (Exception ex)
{
result.Errors.Add($"Writing failed: {ex.Message}");
result.Draft = result.Research; // Fallback to research output
}
result.Success = result.Errors.Count == 0;
return result;
}
class WorkflowResult
{
public string Research { get; set; } = "";
public string Draft { get; set; } = "";
public List<string> Errors { get; set; } = new();
public bool Success { get; set; }
}
## Bonnes pratiques
Après avoir créé plusieurs workflows d’agent, voici ce que j’ai appris :- Gardez les instructions des agents concentrées : chaque agent doit bien faire une chose. N’essayez pas de faire un agent couteau suisse.
- Limiter le contexte : ne transmettez pas l’intégralité de l’historique des conversations à chaque agent. Donnez à chaque étape uniquement ce dont elle a besoin.
- Définissez des limites de révision — les boucles de qualité sont excellentes mais peuvent s’exécuter indéfiniment si vous ne faites pas attention. Ayez toujours un plafond d’itérations maximum.
- Tout enregistrer : les résultats de l’agent peuvent être imprévisibles. Enregistrez les entrées et sorties de chaque étape pour le débogage.
- Utilisez judicieusement l’exécution parallèle : cela accélère les choses, mais surveillez les limites de débit de votre API et les coûts des jetons.
- Testez d’abord avec des modèles plus petits : développez et testez votre logique de flux de travail avec GPT-3.5 avant de passer à GPT-4o pour la production.
Conclusion
Les workflows d’agent vous permettent de créer des pipelines d’IA complexes en plusieurs étapes dans lesquels chaque agent est un spécialiste. Commencez par des flux de travail séquentiels, ajoutez une exécution parallèle où les étapes sont indépendantes et utilisez le branchement conditionnel pour les contrôles de qualité. Les modèles sont composables : une fois que vous avez compris, vous pouvez créer une automatisation assez sophistiquée.
Bon codage !