Premiers pas avec le noyau sémantique : orchestration de l'IA en C#
Si vous avez créé des applications .NET et observé l’évolution du paysage de l’IA, vous vous êtes probablement demandé : quel est le meilleur moyen d’intégrer de grands modèles de langage dans mes projets C# sans transformer ma base de code en spaghetti ? C’est exactement le problème que le noyau sémantique de Microsoft résout, et après avoir passé l’année dernière à créer des applications de production avec lui, je peux vous dire qu’il est devenu l’un des outils les plus importants de ma boîte à outils de développement.
Dans cet article, je vais vous expliquer tout ce dont vous avez besoin pour démarrer avec Semantic Kernel, de la compréhension des concepts de base à la création d’un assistant d’IA du monde réel. Que vous vous lancez simplement dans le développement de l’IA ou que vous recherchiez un moyen structuré d’orchestrer les appels LLM dans vos applications .NET existantes, ce guide est là pour vous.
Qu’est-ce que le noyau sémantique ?
Semantic Kernel (SK) est un SDK open source de Microsoft qui agit comme une couche d’orchestration entre le code de votre application et les grands modèles de langage comme GPT-4o, Azure OpenAI ou d’autres services d’IA. Considérez-le comme un middleware léger qui vous permet de combiner le code C# traditionnel avec des capacités d’IA de manière propre et composable.
Mais pourquoi ne pas simplement appeler directement l’API OpenAI ? Vous pouvez tout à fait le faire – et pour les cas d’utilisation simples, c’est très bien. Mais à ce moment-là, vous devez :
- Laissez l’IA décider quelles fonctions appeler en fonction de la saisie de l’utilisateur
- Combinez plusieurs appels IA avec du code traditionnel dans un pipeline
- Ajoutez de la mémoire et du contexte pour que l’IA se souvienne des interactions précédentes
- Créez des agents en plusieurs étapes qui raisonnent à travers des tâches complexes
… vous vous retrouverez à réinventer la roue. Semantic Kernel vous offre tout cela immédiatement, avec une prise en charge .NET de première classe, une intégration par injection de dépendances et une architecture de plugin qui semble naturelle à tout développeur C#.
Le projet réside sur GitHub sous le référentiel microsoft/semantic-kernel et dispose de SDK pour C#, Python et Java. Le SDK C# est le plus mature et c’est celui sur lequel nous nous concentrerons ici.
Concepts de base
Avant d’écrire du code, comprenons les éléments constitutifs.
Le noyau
Le Kernel est l’objet central du noyau sémantique. C’est l’orchestrateur, l’élément qui relie vos services d’IA, vos plugins et votre configuration. Vous en créez un, vous y enregistrez vos services et plugins, puis vous l’utilisez pour exécuter des invites ou appeler des fonctions. Si vous êtes familier avec l’injection de dépendances dans ASP.NET Core, le noyau vous semblera très familier : il s’agit essentiellement d’un conteneur de services doté de super pouvoirs d’IA.
Plugins et fonctions
Un plugin est un ensemble de fonctions associées que le noyau peut invoquer. Les fonctions sont disponibles en deux versions :
- Fonctions d’invite — définies comme des modèles en langage naturel envoyés au LLM
- Fonctions natives — méthodes C# classiques décorées d’attributs que le noyau peut découvrir et appeler
Par exemple, vous pourriez avoir un WeatherPlugin avec une fonction native GetCurrentWeather(string city) et une fonction d’invite qui résume les données météorologiques de manière conviviale.### Connecteurs IA
Les connecteurs permettent au noyau sémantique de communiquer avec les services d’IA. Les plus courants sont :
AzureOpenAIChatCompletion— pour Azure OpenAI ServiceOpenAIChatCompletion— pour l’API d’OpenAI directement- Intégration de connecteurs pour la recherche vectorielle et la mémoire
Vous les enregistrez sur le noyau au démarrage, et tout le reste fonctionne.
Configurer votre projet
Mettons-nous les mains dans le cambouis. Commencez par créer une nouvelle application console :
dotnet new console -n SemanticKernelDemo
cd SemanticKernelDemo
Ajoutez maintenant les packages Semantic Kernel NuGet :
dotnet add package Microsoft.SemanticKernel
dotnet add package Microsoft.SemanticKernel.Connectors.AzureOpenAI
Si vous utilisez OpenAI directement au lieu d’Azure OpenAI :
dotnet add package Microsoft.SemanticKernel.Connectors.OpenAI
Pour la prise en charge de la mémoire et des intégrations (nous l’utiliserons plus tard) :
dotnet add package Microsoft.SemanticKernel.Plugins.Memory
dotnet add package Microsoft.Extensions.VectorData.Abstractions
Votre .csproj doit cibler .NET 8 ou version ultérieure. Les dernières versions de Semantic Kernel tirent pleinement parti des fonctionnalités modernes de .NET.
Votre premier noyau
Commençons par l’exemple le plus simple possible : créer un noyau, le connecter à un service d’IA et lui poser une question.
using Microsoft.SemanticKernel;
// Build the kernel with Azure OpenAI
var builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(
deploymentName: "gpt-4o",
endpoint: "https://your-resource.openai.azure.com/",
apiKey: "your-api-key"
);
var kernel = builder.Build();
// Invoke a simple prompt
var result = await kernel.InvokePromptAsync(
"Explain dependency injection in C# in three sentences."
);
Console.WriteLine(result);
Si vous utilisez OpenAI directement, échangez l’enregistrement du service :
builder.AddOpenAIChatCompletion(
modelId: "gpt-4o",
apiKey: "your-openai-api-key"
);
C’est tout. Exécutez-le et vous obtiendrez une explication concise de l’injection de dépendances. Mais cela ne fait qu’effleurer la surface.
Utilisation de modèles d’invite
Les modèles d’invites vous permettent de paramétrer vos invites avec des variables en utilisant la syntaxe de style guidon :
var prompt = """
You are a technical writer. Write a brief summary of {{$topic}}
aimed at developers with {{$experienceLevel}} experience.
Keep it under 200 words.
""";
var function = kernel.CreateFunctionFromPrompt(prompt);
var arguments = new KernelArguments
{
["topic"] = "gRPC in .NET",
["experienceLevel"] = "intermediate"
};
var result = await kernel.InvokeAsync(function, arguments);
Console.WriteLine(result);
C’est là que le noyau sémantique commence à briller : vous pouvez définir des modèles d’invites réutilisables, les versionner et les composer dans des flux de travail plus vastes.
Plugins et fonctions natives
Les plugins sont l’endroit où Semantic Kernel comble le fossé entre l’IA et votre code C# existant. Une fonction native n’est qu’une méthode standard que vous exposez au noyau.
using Microsoft.SemanticKernel;
using System.ComponentModel;
public class TimePlugin
{
[KernelFunction("get_current_time")]
[Description("Gets the current date and time in UTC")]
public string GetCurrentTime()
{
return DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss UTC");
}
[KernelFunction("get_time_in_timezone")]
[Description("Gets the current time in a specific timezone")]
public string GetTimeInTimezone(
[Description("The IANA timezone identifier, e.g. 'America/New_York'")] string timezone)
{
var tz = TimeZoneInfo.FindSystemTimeZoneById(timezone);
var time = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tz);
return time.ToString("yyyy-MM-dd HH:mm:ss");
}
}
Notez les attributs [KernelFunction] et [Description]. Celles-ci sont essentielles : les descriptions sont ce que l’IA lit pour comprendre quand et comment appeler vos fonctions. De bonnes descriptions font la différence entre une IA qui utilise efficacement vos outils et une IA confuse.
Enregistrez le plugin sur votre noyau :
var builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(
deploymentName: "gpt-4o",
endpoint: "https://your-resource.openai.azure.com/",
apiKey: "your-api-key"
);
builder.Plugins.AddFromType<TimePlugin>();
var kernel = builder.Build();
Vous pouvez également créer des plugins plus complexes qui injectent des services. Puisque Semantic Kernel s’intègre à Microsoft.Extensions.DependencyInjection, vos plugins peuvent recevoir des dépendances de constructeur comme n’importe quel autre service :
public class OrderPlugin
{
private readonly IOrderRepository _repository;
public OrderPlugin(IOrderRepository repository)
{
_repository = repository;
}
[KernelFunction("get_order_status")]
[Description("Retrieves the status of an order by its ID")]
public async Task<string> GetOrderStatus(
[Description("The order ID to look up")] string orderId)
{
var order = await _repository.GetByIdAsync(orderId);
return order is null
? $"No order found with ID {orderId}"
: $"Order {orderId}: {order.Status}, placed on {order.CreatedAt:d}";
}
}
Appel de fonction et invocation automatique
C’est là que les choses deviennent vraiment intéressantes. Avec l’appel de fonction (également appelé appel d’outil), vous laissez le modèle d’IA décider laquelle de vos fonctions enregistrées appeler en fonction du contexte de la conversation. Le modèle n’exécute pas de code : il renvoie une requête structurée indiquant “Je veux appeler la fonction X avec ces arguments”, et le noyau gère l’invocation proprement dite.
Voici comment activer l’appel automatique de fonction :
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
var builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(
deploymentName: "gpt-4o",
endpoint: "https://your-resource.openai.azure.com/",
apiKey: "your-api-key"
);
builder.Plugins.AddFromType<TimePlugin>();
builder.Plugins.AddFromType<WeatherPlugin>();
var kernel = builder.Build();
// Enable automatic function calling
var settings = new OpenAIPromptExecutionSettings
{
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};
var result = await kernel.InvokePromptAsync(
"What time is it in Tokyo and what's the weather like there?",
new KernelArguments(settings)
);
Console.WriteLine(result);
Avec FunctionChoiceBehavior.Auto(), le noyau va :
- Envoyez votre invite à l’IA avec des descriptions de toutes les fonctions disponibles
- L’IA décide qu’elle doit appeler
get_time_in_timezoneetget_weather - Le noyau exécute automatiquement ces fonctions
- Les résultats sont renvoyés à l’IA
- L’IA compose une réponse en langage naturel en utilisant les résultats de la fonctionCette boucle peut se produire plusieurs fois au cours d’un seul appel : l’IA peut appeler plusieurs fonctions en séquence pour rassembler toutes les informations dont elle a besoin. Vous pouvez également utiliser
FunctionChoiceBehavior.Required()pour forcer l’IA à appeler au moins une fonction, ou fournir une liste spécifique de fonctions qu’elle est autorisée à utiliser.
Fin du chat avec historique
Pour les applications conversationnelles, vous souhaiterez utiliser le ChatCompletionService directement avec un objet ChatHistory :
using Microsoft.SemanticKernel.ChatCompletion;
var chatService = kernel.GetRequiredService<IChatCompletionService>();
var history = new ChatHistory();
history.AddSystemMessage("""
You are a helpful developer assistant. You have access to tools
for checking the time and weather. Be concise and friendly.
""");
while (true)
{
Console.Write("You: ");
var input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input)) break;
history.AddUserMessage(input);
var response = await chatService.GetChatMessageContentAsync(
history,
executionSettings: settings,
kernel: kernel
);
history.AddAssistantMessage(response.Content ?? "");
Console.WriteLine($"Assistant: {response.Content}");
}
Cela vous donne un chatbot entièrement interactif qui conserve l’historique des conversations et peut appeler vos plugins si nécessaire.
Mémoire et intégrations
L’un des modèles les plus puissants dans les applications d’IA est la Génération augmentée par récupération (RAG) : elle permet à l’IA d’accéder à vos propres données en les intégrant dans l’espace vectoriel et en récupérant les morceaux pertinents au moment de la requête.
Semantic Kernel fournit des abstractions pour travailler avec des magasins de vecteurs et des intégrations. Voici comment configurer un magasin de vecteurs en mémoire pour le développement :
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Connectors.AzureOpenAI;
using Microsoft.SemanticKernel.Embeddings;
#pragma warning disable SKEXP0010
// Create an embedding generation service
var builder = Kernel.CreateBuilder();
builder.AddAzureOpenAITextEmbeddingGeneration(
deploymentName: "text-embedding-ada-002",
endpoint: "https://your-resource.openai.azure.com/",
apiKey: "your-api-key"
);
var kernel = builder.Build();
var embeddingService = kernel.GetRequiredService<ITextEmbeddingGenerationService>();
// Generate embeddings for your documents
var documents = new[]
{
"Semantic Kernel is an open-source SDK for AI orchestration.",
"Azure OpenAI provides enterprise-grade AI models.",
"Plugins in SK allow you to expose C# methods to AI models."
};
var embeddings = await embeddingService.GenerateEmbeddingsAsync(documents);
Pour les scénarios de production, vous stockeriez ces intégrations dans une base de données vectorielles dédiée comme Azure AI Search, Qdrant ou Pinecone. Le noyau sémantique a des connecteurs pour tout cela via les abstractions Microsoft.Extensions.VectorData .
Un flux RAG typique ressemble à ceci :
- Ingérer : regroupez vos documents, générez des intégrations, stockez-les dans une base de données vectorielle
- Récupérer : lorsqu’un utilisateur pose une question, intégrez la requête et recherchez les documents les plus similaires
- Générer : Transmettez les documents récupérés comme contexte au LLM avec la question de l’utilisateur
[KernelFunction("search_knowledge_base")]
[Description("Searches the internal knowledge base for relevant information")]
public async Task<string> SearchKnowledgeBase(
[Description("The search query")] string query)
{
var queryEmbedding = await _embeddingService.GenerateEmbeddingAsync(query);
var results = await _vectorStore.SearchAsync(queryEmbedding, limit: 3);
return string.Join("\n\n", results.Select(r => r.Text));
}
En exposant votre pipeline RAG en tant que fonction du noyau, l’IA peut décider automatiquement quand elle doit effectuer une recherche dans votre base de connaissances, gardant ainsi l’orchestration propre et laissant le modèle faire ce qu’il fait de mieux.
Planificateurs et agents
À mesure que vos applications d’IA deviennent plus complexes, vous aurez besoin de l’IA pour planifier et exécuter des tâches en plusieurs étapes. C’est là qu’intervient le cadre d’agent de Semantic Kernel.
Les bases : agent de complétion de chat
Le type d’agent le plus simple enveloppe un modèle de complétion de chat avec des instructions et des plugins :
using Microsoft.SemanticKernel.Agents;
#pragma warning disable SKEXP0110
var agent = new ChatCompletionAgent
{
Name = "DevAssistant",
Instructions = """
You are a senior .NET developer assistant. Help users with code
reviews, architecture decisions, and debugging. Always provide
code examples when relevant. Use your available tools to look up
current information when needed.
""",
Kernel = kernel,
Arguments = new KernelArguments(settings)
};
var history = new ChatHistory();
history.AddUserMessage("How should I structure a clean architecture project in .NET 8?");
await foreach (var message in agent.InvokeAsync(history))
{
Console.WriteLine(message.Content);
history.Add(message);
}
Collaboration multi-agents
Là où les choses deviennent vraiment puissantes, c’est lorsque vous avez plusieurs agents travaillant ensemble. Semantic Kernel prend en charge les modèles de discussion de groupe dans lesquels des agents de différentes spécialisations collaborent :
#pragma warning disable SKEXP0110
var codeReviewer = new ChatCompletionAgent
{
Name = "CodeReviewer",
Instructions = """
You review C# code for bugs, performance issues, and best practices.
Be specific about what you find and suggest concrete fixes.
""",
Kernel = kernel
};
var securityAuditor = new ChatCompletionAgent
{
Name = "SecurityAuditor",
Instructions = """
You focus exclusively on security vulnerabilities in code.
Look for injection attacks, authentication issues, data exposure,
and OWASP Top 10 violations.
""",
Kernel = kernel
};
var groupChat = new AgentGroupChat(codeReviewer, securityAuditor)
{
ExecutionSettings = new AgentGroupChatSettings
{
TerminationStrategy = new MaximumIterationTerminationStrategy(4)
}
};
groupChat.AddChatMessage(
new ChatMessageContent(AuthorRole.User, "Review this code: ...")
);
await foreach (var message in groupChat.InvokeAsync())
{
Console.WriteLine($"[{message.AuthorName}]: {message.Content}");
}
Ce modèle est incroyablement utile pour les tâches complexes où différentes perspectives ou domaines d’expertise doivent intervenir. Chaque agent fonctionne avec sa propre invite système et peut disposer de son propre ensemble de plugins.
Exemple concret : création d’un assistant de documentation de projet
Relions le tout avec un exemple pratique : un assistant IA qui aide les développeurs à comprendre une base de code en lisant des fichiers et en répondant aux questions.
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using System.ComponentModel;
// Define our plugins
public class FileSystemPlugin
{
private readonly string _rootPath;
public FileSystemPlugin(string rootPath)
{
_rootPath = rootPath;
}
[KernelFunction("read_file")]
[Description("Reads the contents of a file from the project directory")]
public async Task<string> ReadFile(
[Description("Relative path to the file")] string path)
{
var fullPath = Path.Combine(_rootPath, path);
if (!File.Exists(fullPath))
return $"File not found: {path}";
var content = await File.ReadAllTextAsync(fullPath);
// Truncate very large files
if (content.Length > 8000)
content = content[..8000] + "\n... [truncated]";
return content;
}
[KernelFunction("list_files")]
[Description("Lists files in a directory, optionally filtered by extension")]
public string ListFiles(
[Description("Relative directory path")] string directory,
[Description("File extension filter like '.cs' or '.json'")] string? extension = null)
{
var fullPath = Path.Combine(_rootPath, directory);
if (!Directory.Exists(fullPath))
return $"Directory not found: {directory}";
var files = Directory.GetFiles(fullPath, "*.*", SearchOption.AllDirectories)
.Select(f => Path.GetRelativePath(_rootPath, f))
.Where(f => extension is null || f.EndsWith(extension))
.Take(50);
return string.Join("\n", files);
}
}
public class DocumentationPlugin
{
[KernelFunction("generate_summary")]
[Description("Generates a structured markdown summary for documentation")]
public string GenerateSummaryTemplate(
[Description("Name of the component")] string componentName,
[Description("Brief description")] string description,
[Description("Key responsibilities as comma-separated values")] string responsibilities)
{
var items = responsibilities.Split(',', StringSplitOptions.TrimEntries);
var bullets = string.Join("\n", items.Select(r => $"- {r}"));
return $"""
## {componentName}
{description}
### Responsibilities
{bullets}
---
*Generated documentation — review and expand as needed.*
""";
}
}
// Wire it all up
var builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(
deploymentName: "gpt-4o",
endpoint: "https://your-resource.openai.azure.com/",
apiKey: "your-api-key"
);
builder.Plugins.AddFromObject(new FileSystemPlugin("./src"));
builder.Plugins.AddFromType<DocumentationPlugin>();
var kernel = builder.Build();
var chatService = kernel.GetRequiredService<IChatCompletionService>();
var history = new ChatHistory();
history.AddSystemMessage("""
You are a codebase documentation assistant. You help developers understand
projects by reading source files and explaining architecture, patterns,
and design decisions.
When asked about code, use your tools to read the actual files rather
than guessing. Be specific and reference actual code when possible.
Generate documentation artifacts when asked.
""");
var settings = new OpenAIPromptExecutionSettings
{
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};
Console.WriteLine("Documentation Assistant ready. Ask me about your codebase!");
Console.WriteLine("Type 'exit' to quit.\n");
while (true)
{
Console.Write("You: ");
var input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input) || input.Equals("exit", StringComparison.OrdinalIgnoreCase))
break;
history.AddUserMessage(input);
var response = await chatService.GetChatMessageContentAsync(
history,
executionSettings: settings,
kernel: kernel
);
Console.WriteLine($"\nAssistant: {response.Content}\n");
history.AddAssistantMessage(response.Content ?? "");
}
Cet assistant peut :- Liste et lis les fichiers de votre répertoire de projet
- Répondez aux questions sur la base de code en lisant les fichiers sources réels
- Générer de la documentation au format markdown
- Maintenir le contexte de la conversation pour que les questions de suivi fonctionnent naturellement
L’IA décide automatiquement quand appeler read_file, list_files ou generate_summary en fonction de ce que vous demandez. Demandez-lui « Que fait OrderService ? » et il lira le fichier, l’analysera et l’expliquera. Demandez-lui de « Générer la documentation pour le module d’authentification » et il explorera les fichiers, comprendra la structure et produira un résumé formaté.
Conseils pour la production
Avant de livrer votre application Semantic Kernel, quelques choses que j’ai apprises à mes dépens :
Utilisez correctement l’injection de dépendances. Dans les applications ASP.NET Core, enregistrez le noyau et les services dans votre conteneur DI plutôt que de les créer en ligne :
builder.Services.AddKernel()
.AddAzureOpenAIChatCompletion(
deploymentName: "gpt-4o",
endpoint: configuration["AzureOpenAI:Endpoint"]!,
apiKey: configuration["AzureOpenAI:ApiKey"]!
)
.Plugins.AddFromType<TimePlugin>()
.AddFromType<OrderPlugin>();
Gérez les erreurs avec élégance. Les appels LLM peuvent échouer, expirer ou renvoyer des résultats inattendus. Enveloppez vos invocations dans des blocs try-catch et implémentez des politiques de nouvelle tentative avec Polly ou les fonctionnalités de résilience intégrées.
Surveillez l’utilisation des jetons. Chaque invite, chaque description de fonction et chaque élément de l’historique des discussions consomme des jetons. Utilisez des filtres pour enregistrer et suivre l’utilisation :
kernel.FunctionInvocationFilters.Add(new LoggingFilter());
Gardez les descriptions de vos fonctions précises. Des descriptions vagues conduisent à des appels incorrects des fonctions de l’IA. Testez vos descriptions en demandant : « Si je lisais seulement la description, saurais-je exactement quand et comment utiliser cette fonction ? »
Conclusion
Semantic Kernel est l’une de ces bibliothèques qui change fondamentalement votre façon de concevoir la création d’applications. Il ne s’agit pas simplement d’un wrapper d’API : il s’agit d’un cadre d’orchestration qui vous permet de composer des fonctionnalités d’IA avec du code traditionnel d’une manière maintenable, testable et prête pour la production.
Ce que j’aime le plus, c’est qu’il respecte l’écosystème .NET. Il utilise des modèles que vous connaissez déjà (injection de dépendances, attributs, asynchrone/attente, interfaces) et les étend au monde de l’IA. Vous n’êtes pas obligé d’apprendre un paradigme complètement nouveau ; vous ajoutez simplement l’IA comme autre fonctionnalité dans votre boîte à outils.
Si vous créez des applications .NET et n’avez pas encore exploré le noyau sémantique, c’est le moment. Le SDK est stable, la communauté est active et les modèles qu’il permet (de la simple orchestration rapide à la collaboration multi-agents) deviennent des compétences essentielles pour les développeurs modernes.
Commencez petit. Créez un noyau, enregistrez un plugin et regardez l’IA appeler votre code. Une fois que vous aurez cliqué, vous commencerez à voir des opportunités d’ajouter de l’intelligence partout dans vos applications.
La documentation officielle et le dépôt GitHub sont d’excellentes ressources pour poursuivre votre voyage. Bonne construction !