Getting Started with Semantic Kernel for .NET Developers
Microsoft released Semantic Kernel 1.0 in December 2023, and it is worth paying attention to if you are a .NET developer who wants to integrate large language models into your applications. Unlike framework-agnostic tools, Semantic Kernel is designed with .NET idioms in mind: dependency injection, strongly typed plugins, and a familiar configuration model. We will walk through the fundamentals and build a working example using .NET 8 and C#.
What Is Semantic Kernel?
Semantic Kernel is an open-source SDK from Microsoft that lets you orchestrate AI models and conventional code together. Think of it as middleware between your application logic and LLM providers like OpenAI or Azure OpenAI. The core abstractions include:
- Kernel: The central object that manages services, plugins, and execution.
- Plugins: Collections of functions (both native C# methods and prompt-based) that the kernel can invoke.
- Planners: Components that use an LLM to decompose a user goal into a sequence of plugin calls.
Setting Up the Project
Create a new .NET 8 console application and add the Semantic Kernel NuGet package:
dotnet new console -n SemanticKernelDemo
cd SemanticKernelDemo
dotnet add package Microsoft.SemanticKernel --version 1.0.1
You will also need an OpenAI or Azure OpenAI API key. For this tutorial, we will use the OpenAI endpoint directly.
Configuring the Kernel
The kernel is the entry point for everything in Semantic Kernel. Here is how to set it up with the OpenAI chat completion service:
using Microsoft.SemanticKernel;
var builder = Kernel.CreateBuilder();
builder.AddOpenAIChatCompletion(
modelId: "gpt-3.5-turbo",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")!
);
var kernel = builder.Build();
The builder pattern should feel natural if you have worked with ASP.NET Core. You register AI services the same way you would register any other dependency.
Invoking a Prompt Directly
The simplest thing you can do with Semantic Kernel is send a prompt and get a response:
var result = await kernel.InvokePromptAsync(
"What are three benefits of using .NET for AI development?");
Console.WriteLine(result);
This is useful for quick experiments, but the real power comes from plugins.
Creating a Native Plugin
A native plugin is a C# class whose methods can be called by the kernel. You annotate methods with [KernelFunction] so the kernel knows they are available.
using System.ComponentModel;
using Microsoft.SemanticKernel;
public class WeatherPlugin
{
[KernelFunction("get_weather")]
[Description("Gets the current weather for a given city")]
public string GetWeather(
[Description("The city name")] string city)
{
// In a real application, this would call a weather API
var forecasts = new Dictionary<string, string>
{
["London"] = "12C, cloudy with light rain",
["New York"] = "5C, clear skies",
["Tokyo"] = "8C, partly cloudy",
};
return forecasts.TryGetValue(city, out var weather)
? quot;The weather in {city} is: {weather}"
: quot;Weather data not available for {city}";
}
[KernelFunction("get_temperature_unit")]
[Description("Returns the standard temperature unit for a country")]
public string GetTemperatureUnit(
[Description("The country name")] string country)
{
var fahrenheitCountries = new HashSet<string>
{
"United States", "Bahamas", "Cayman Islands"
};
return fahrenheitCountries.Contains(country)
? "Fahrenheit" : "Celsius";
}
}
Register the plugin with the kernel:
var builder = Kernel.CreateBuilder();
builder.AddOpenAIChatCompletion(
modelId: "gpt-3.5-turbo",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")!
);
builder.Plugins.AddFromType<WeatherPlugin>();
var kernel = builder.Build();
Creating a Semantic (Prompt) Plugin
Not every function needs to be written in C#. Semantic functions are defined as prompt templates:
var summarizeFunction = kernel.CreateFunctionFromPrompt(
promptTemplate: @"Summarize the following text in exactly
{{$sentence_count}} sentences. Be concise and capture the key points.
Text: {{$input}}",
functionName: "Summarize",
description: "Summarizes text into a specified number of sentences"
);
var summary = await kernel.InvokeAsync(summarizeFunction,
new KernelArguments
{
["input"] = "Semantic Kernel is an open-source SDK that integrates "
+ "Large Language Models with conventional programming languages. "
+ "It was developed by Microsoft and supports .NET, Python, and "
+ "Java. The SDK provides abstractions for AI services, plugins, "
+ "and planners that help developers build intelligent applications.",
["sentence_count"] = "1"
});
Console.WriteLine(summary);
Using the Planner
Planners are one of the most powerful features in Semantic Kernel. A planner takes a natural language goal and figures out which plugin functions to call and in what order. Semantic Kernel 1.0 ships with the FunctionCallingStepwisePlanner:
using Microsoft.SemanticKernel.Planning;
var planner = new FunctionCallingStepwisePlanner(
new FunctionCallingStepwisePlannerOptions
{
MaxIterations = 5,
});
var planResult = await planner.ExecuteAsync(
kernel,
"What is the weather like in London, and what temperature "
+ "unit do they use in the UK?"
);
Console.WriteLine(planResult.FinalAnswer);
The planner will automatically identify that it needs to call both get_weather("London") and get_temperature_unit("United Kingdom") to answer the question. It then synthesizes the results into a coherent response.
Chat Completion with History
For building conversational applications, you will want to use the chat completion service directly with a chat history:
using Microsoft.SemanticKernel.ChatCompletion;
var chatService = kernel.GetRequiredService<IChatCompletionService>();
var history = new ChatHistory();
history.AddSystemMessage(
"You are a helpful .NET development assistant.");
while (true)
{
Console.Write("You: ");
var userMessage = Console.ReadLine();
if (string.IsNullOrWhiteSpace(userMessage)
|| userMessage == "quit")
break;
history.AddUserMessage(userMessage);
var response = await chatService
.GetChatMessageContentAsync(history);
history.AddAssistantMessage(response.Content!);
Console.WriteLine(quot;Assistant: {response.Content}");
}
This gives you full control over the conversation flow and history management, which is essential for production applications.
Integrating with Dependency Injection
In a real ASP.NET Core application, you would register Semantic Kernel with the DI container:
// In Program.cs or Startup
builder.Services.AddKernel()
.AddOpenAIChatCompletion(
modelId: "gpt-3.5-turbo",
apiKey: builder.Configuration["OpenAI:ApiKey"]!
)
.Plugins.AddFromType<WeatherPlugin>();
Then inject Kernel into your controllers or services just like any other dependency. This makes testing straightforward since you can mock the AI services.
Key takeaways
Semantic Kernel 1.0 brings a mature, production-ready AI orchestration layer to the .NET ecosystem. A few things stand out after working with it:
- The plugin model is excellent. The
[KernelFunction] and [Description] attributes make it trivial to expose C# methods to the LLM. The descriptions are used by the model to understand when and how to call each function.
- Planners are powerful but need guardrails. Letting the LLM decide which functions to call is impressive, but you should limit iterations and validate outputs in production scenarios.
- It integrates cleanly with .NET conventions. Dependency injection, configuration, and async patterns all work exactly as you would expect.
If you are a .NET developer looking to add AI capabilities to your applications, Semantic Kernel is a solid option. It is actively maintained and designed for the patterns you already know. Start with a simple plugin, get comfortable with the kernel, and then explore planners when you are ready for more complex orchestration.