You’ve used Azure OpenAI to send prompts and get completions. But how do you build a real AI application — one that remembers context, calls your business logic, and orchestrates multi-step workflows? Semantic Kernel is Microsoft’s answer: an open-source SDK that turns LLMs into the reasoning engine of your application.
This guide goes beyond the basics. We’ll explore the architecture, build plugins, wire up planners, and integrate memory — all with production-ready C# code.
Why Semantic Kernel?
Calling an LLM directly works for simple scenarios, but production AI applications need more: structured function calling, conversation memory, multi-step reasoning, and integration with your existing codebase. Semantic Kernel provides these capabilities through a modular architecture that plugs into .NET’s dependency injection system.
Key advantages over raw API calls:
- Plugin system — expose your C# methods as tools the AI can call automatically.
- Automatic function calling — the kernel handles the tool-call loop, including retries and argument marshaling.
- Memory & embeddings — built-in support for vector stores and semantic search.
- Filters & middleware — intercept and modify prompts, function calls, and responses.
- Multi-model support — Azure OpenAI, OpenAI, Hugging Face, Ollama, and more through connectors.
Architecture overview
The Kernel is the central orchestrator. It connects your application to AI services, manages plugins (your code exposed to the AI), and coordinates the execution loop. Filters let you intercept every step for logging, validation, or modification.
Setting up your project
Start with a .NET 8 console application:
dotnet new console -n SemanticKernelDemo
cd SemanticKernelDemo
dotnet add package Microsoft.SemanticKernel
dotnet add package Microsoft.SemanticKernel.Plugins.Core
Configure the kernel with Azure OpenAI:
using Microsoft.SemanticKernel;
var builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(
deploymentName: "gpt-4o",
endpoint: "https://your-resource.openai.azure.com/",
apiKey: "your-api-key"
);
var kernel = builder.Build();
DefaultAzureCredential instead of API keys. Semantic Kernel supports token credentials natively through the AddAzureOpenAIChatCompletion overload that accepts a TokenCredential.
Building plugins
Plugins are the core mechanism for giving the AI access to your business logic. A plugin is simply a class with methods decorated with [KernelFunction].
Native plugin example
using System.ComponentModel;
using Microsoft.SemanticKernel;
public class OrderPlugin
{
private readonly IOrderService _orderService;
public OrderPlugin(IOrderService orderService)
{
_orderService = orderService;
}
[KernelFunction]
[Description("Gets the current status of an order by its ID")]
public async Task<string> GetOrderStatus(
[Description("The order ID, e.g. ORD-12345")] string orderId)
{
var order = await _orderService.GetByIdAsync(orderId);
return order is null
? $"Order {orderId} not found."
: $"Order {orderId}: {order.Status}, shipped {order.ShipDate:d}";
}
[KernelFunction]
[Description("Lists the most recent orders for a customer")]
public async Task<string> GetRecentOrders(
[Description("Customer email address")] string email,
[Description("Max results to return")] int count = 5)
{
var orders = await _orderService.GetRecentAsync(email, count);
return JsonSerializer.Serialize(orders);
}
}
Register the plugin with the kernel:
kernel.Plugins.AddFromObject(new OrderPlugin(orderService), "Orders");
The [Description] attributes are critical — the AI reads them to decide when and how to call each function. Write them as clear, concise instructions.
Prompt plugin (semantic function)
You can also define plugins as prompt templates:
var summarize = kernel.CreateFunctionFromPrompt(
"""
Summarize the following text in {{$style}} style.
Keep it under {{$maxWords}} words.
Text: {{$input}}
""",
new OpenAIPromptExecutionSettings
{
MaxTokens = 300,
Temperature = 0.3
}
);
var result = await kernel.InvokeAsync(summarize, new()
{
["input"] = longArticle,
["style"] = "executive briefing",
["maxWords"] = "100"
});
Automatic function calling
This is where Semantic Kernel shines. When you enable automatic function calling, the kernel handles the entire tool-call loop: the AI decides which functions to call, the kernel executes them, feeds the results back, and lets the AI formulate the final answer.
using Microsoft.SemanticKernel.Connectors.OpenAI;
var settings = new OpenAIPromptExecutionSettings
{
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};
var chatService = kernel.GetRequiredService<IChatCompletionService>();
var history = new ChatHistory();
history.AddSystemMessage("You are a helpful order support assistant.");
history.AddUserMessage("What's the status of order ORD-78901?");
var response = await chatService.GetChatMessageContentAsync(
history, settings, kernel);
Console.WriteLine(response.Content);
// "Order ORD-78901 is currently shipped and was dispatched on June 28."
Behind the scenes, the AI called GetOrderStatus("ORD-78901"), received the result, and composed a natural language response. You didn’t write any routing logic — the kernel handled it.
Filters: intercepting the pipeline
Filters let you hook into every prompt and function call for logging, validation, caching, or modification. This is essential for production applications.
public class AuditFilter : IFunctionInvocationFilter
{
private readonly ILogger<AuditFilter> _logger;
public AuditFilter(ILogger<AuditFilter> logger) => _logger = logger;
public async Task OnFunctionInvocationAsync(
FunctionInvocationContext context, Func<FunctionInvocationContext, Task> next)
{
_logger.LogInformation(
"Calling {Plugin}.{Function} with {Args}",
context.Function.PluginName,
context.Function.Name,
context.Arguments);
await next(context);
_logger.LogInformation(
"Result from {Function}: {Result}",
context.Function.Name,
context.Result?.ToString()?[..200]);
}
}
// Register the filter
builder.Services.AddSingleton<IFunctionInvocationFilter, AuditFilter>();
There are three filter types: IPromptRenderFilter (before/after prompt rendering), IFunctionInvocationFilter (before/after function execution), and IAutoFunctionInvocationFilter (specifically for the auto function calling loop).
Memory and vector search
Semantic Kernel integrates with vector databases to give your AI long-term memory and the ability to search through documents semantically.
using Microsoft.SemanticKernel.Memory;
using Microsoft.SemanticKernel.Connectors.AzureAISearch;
// Configure memory with Azure AI Search as the vector store
var memoryBuilder = new MemoryBuilder()
.WithAzureOpenAITextEmbeddingGeneration(
"text-embedding-3-small", endpoint, apiKey)
.WithMemoryStore(new AzureAISearchMemoryStore(
searchEndpoint, searchApiKey));
var memory = memoryBuilder.Build();
// Save documents to memory
await memory.SaveInformationAsync(
collection: "company-policies",
id: "remote-work-policy",
text: "Employees may work remotely up to 3 days per week...");
// Search memory semantically
var results = memory.SearchAsync(
collection: "company-policies",
query: "Can I work from home on Fridays?",
limit: 3,
minRelevanceScore: 0.75);
await foreach (var result in results)
{
Console.WriteLine($"[{result.Relevance:P0}] {result.Metadata.Text}");
}
Putting it together: a support agent
Here’s a complete example that combines plugins, automatic function calling, and chat history into a functional support agent:
var builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion("gpt-4o", endpoint, apiKey);
builder.Services.AddSingleton<IOrderService, OrderService>();
builder.Services.AddSingleton<IFunctionInvocationFilter, AuditFilter>();
var kernel = builder.Build();
kernel.Plugins.AddFromType<OrderPlugin>("Orders");
var chat = kernel.GetRequiredService<IChatCompletionService>();
var history = new ChatHistory("""
You are a customer support agent for Contoso Electronics.
Use the available tools to look up order information.
Be concise and helpful. If you can't find information,
say so — don't make anything up.
""");
var settings = new OpenAIPromptExecutionSettings
{
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};
// Chat loop
while (true)
{
Console.Write("You: ");
var input = Console.ReadLine();
if (string.IsNullOrEmpty(input)) break;
history.AddUserMessage(input);
var response = await chat.GetChatMessageContentAsync(
history, settings, kernel);
history.Add(response);
Console.WriteLine($"Agent: {response.Content}");
}
Semantic Kernel vs. LangChain
| Aspect | Semantic Kernel | LangChain |
|---|---|---|
| Primary language | C# (.NET), Python | Python, JavaScript |
| Plugin model | Native class methods with attributes | Tools as functions or classes |
| Enterprise integration | Azure-first, DI-native | Cloud-agnostic |
| Function calling | Automatic loop with filters | Agent executors |
| Memory | Built-in vector store abstraction | Memory modules + retrievers |
| Best for | .NET shops, Azure stack, enterprise | Python-first teams, rapid prototyping |
If your team works in .NET and Azure, Semantic Kernel is the natural choice — it integrates with your existing DI, logging, and deployment patterns. LangChain offers broader community tools but less polish in the .NET ecosystem.
Production checklist
- Use managed identity — replace API keys with
DefaultAzureCredentialfor all Azure services. - Add filters — log every function call and prompt for debugging and compliance.
- Set token limits — configure
MaxTokensandFunctionChoiceBehavior.Auto(maxRounds: 5)to prevent runaway loops. - Handle errors in plugins — return user-friendly error messages instead of throwing exceptions.
- Version your prompts — store prompt templates in configuration, not hardcoded strings.
- Test with mocks — Semantic Kernel’s interfaces are mockable for unit testing without hitting real AI services.
Next steps
- Clone the samples repo: github.com/microsoft/semantic-kernel — the
/dotnet/samplesfolder has production-grade examples. - Try the Agents framework — Semantic Kernel includes an experimental
Agentabstraction for multi-agent scenarios. - Explore the Process framework — for complex, multi-step business workflows with state management.
- Read the official docs: learn.microsoft.com/semantic-kernel
Leave a Reply