Semantic Kernel

Written by

in

Intermediate

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

SEMANTIC KERNEL ARCHITECTURE Your Application Kernel Plugins Native Functions Prompt Templates OpenAPI Specs AI Services Chat Completion Text Embedding Image Generation Memory Vector Store Semantic Search Chat History Filters & Middleware Pipeline

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();
Tip: In production, use 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.

Documents PDFs, emails, knowledge base Chunk & Embed Vector Store Azure AI Search Qdrant, Pinecone Semantic Search Kernel + Context Grounded Answer
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

AspectSemantic KernelLangChain
Primary languageC# (.NET), PythonPython, JavaScript
Plugin modelNative class methods with attributesTools as functions or classes
Enterprise integrationAzure-first, DI-nativeCloud-agnostic
Function callingAutomatic loop with filtersAgent executors
MemoryBuilt-in vector store abstractionMemory modules + retrievers
Best for.NET shops, Azure stack, enterprisePython-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

  1. Use managed identity — replace API keys with DefaultAzureCredential for all Azure services.
  2. Add filters — log every function call and prompt for debugging and compliance.
  3. Set token limits — configure MaxTokens and FunctionChoiceBehavior.Auto(maxRounds: 5) to prevent runaway loops.
  4. Handle errors in plugins — return user-friendly error messages instead of throwing exceptions.
  5. Version your prompts — store prompt templates in configuration, not hardcoded strings.
  6. Test with mocks — Semantic Kernel’s interfaces are mockable for unit testing without hitting real AI services.

Next steps

  1. Clone the samples repo: github.com/microsoft/semantic-kernel — the /dotnet/samples folder has production-grade examples.
  2. Try the Agents framework — Semantic Kernel includes an experimental Agent abstraction for multi-agent scenarios.
  3. Explore the Process framework — for complex, multi-step business workflows with state management.
  4. Read the official docs: learn.microsoft.com/semantic-kernel
What plugin would you build first? Semantic Kernel turns your existing C# code into AI-callable tools. Share your use case in the comments — I’ll help you design the plugin architecture.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *