A Guide to Microsoft Semantic Kernel
Semantic Kernel is Microsoft's open-source SDK for building AI applications that combine large language models with your own code, data, and business logic. If you've used LangChain or LlamaIndex and wondered whether there's a more enterprise-friendly option with first-class C# and Python support, semantic kernel is the answer Microsoft ships for that gap. It gives you a "kernel" object that plugs prompts, native functions, memory, and planners together so an LLM can call your code the same way it calls its own reasoning.
This guide covers what semantic kernel actually is, how the kernel, plugins, and functions fit together, how to add memory and connectors, how planners and agents work, and where it fits next to alternatives like LangChain and the OpenAI Agents SDK. Every section includes code you can run, not just diagrams.
What Semantic Kernel Actually Is
At its core, semantic kernel is an orchestration layer. You register "plugins" (collections of functions) with a kernel instance, and the kernel exposes those functions to an LLM as callable tools. The LLM decides which function to call, with what arguments, and the kernel executes it and feeds the result back. This is the same function-calling pattern used across most agent frameworks, but semantic kernel wraps it in a structured object model that maps cleanly onto enterprise .NET and Python codebases.
The project started as a Microsoft Research experiment and is now a general-availability SDK used inside Microsoft's own Copilot stack. It supports three main languages: C#, Python, and Java, with C# and Python receiving the most active development.
Three ideas anchor everything in semantic kernel:
- Functions: units of work, either a prompt template ("semantic function" in older docs) or a native code function decorated for the kernel to discover.
- Plugins: named groups of functions, similar to a class with multiple public methods.
- Planners and agents: components that decide, at runtime, which functions to call and in what order to satisfy a user's goal.
Installing and Setting Up the Kernel
Python is the fastest way to try semantic kernel. Install the package and create a kernel bound to a chat completion service.
pip install semantic-kernelimport asyncio
from semantic_kernel import Kernel
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
kernel = Kernel()
kernel.add_service(
OpenAIChatCompletion(
ai_model_id="gpt-4o",
api_key="YOUR_API_KEY",
service_id="chat",
)
)
async def main():
result = await kernel.invoke_prompt("What is semantic kernel used for?")
print(result)
asyncio.run(main())The C# equivalent looks almost identical in structure, which is one of semantic kernel's selling points if your team ships both a .NET backend and a Python data science stack.
using Microsoft.SemanticKernel;
var builder = Kernel.CreateBuilder();
builder.AddOpenAIChatCompletion("gpt-4o", "YOUR_API_KEY");
var kernel = builder.Build();
var result = await kernel.InvokePromptAsync("What is semantic kernel used for?");
Console.WriteLine(result);Both languages register an "AI service" against the kernel, then invoke prompts or functions through that same kernel object. Swapping a model provider, from OpenAI to Azure OpenAI to a local model runner, is a one-line change on the service registration, not a rewrite of your application code.
Prompt Functions: Templates as First-Class Functions
A prompt function is a reusable prompt template with input variables, registered as a callable function on the kernel. This is where semantic kernel differs from raw API calls: instead of string-formatting prompts inline, you define them once and invoke them like any other function.
from semantic_kernel.functions import KernelArguments
summarize = kernel.add_function(
plugin_name="writer",
function_name="summarize",
prompt="Summarize the following text in {{$style}} style:\n\n{{$input}}",
)
async def summarize_text():
args = KernelArguments(input="Long article text goes here...", style="two bullet points")
result = await kernel.invoke(summarize, args)
print(result)
asyncio.run(summarize_text())Prompt templates support conditionals, loops, and function calls inside the template itself through the built-in templating engine, plus a Handlebars-based template option for teams who prefer that syntax. You can also load prompt templates from disk as .yaml files, which keeps prompt engineering out of your Python or C# source and lets non-engineers iterate on wording without touching code.
name: summarize
template: |
Summarize the following text in {{style}} style:
{{input}}
input_variables:
- name: input
- name: style
default: "one paragraph"Native Functions: Giving the Kernel Real Capabilities
Native functions are ordinary code, Python or C#, decorated so the kernel can discover and call them. This is how semantic kernel gives an LLM tools: math, file access, database lookups, HTTP calls, anything you can write as a function.
from semantic_kernel.functions import kernel_function
class MathPlugin:
@kernel_function(description="Add two numbers together")
def add(self, a: float, b: float) -> float:
return a + b
@kernel_function(description="Multiply two numbers together")
def multiply(self, a: float, b: float) -> float:
return a * b
kernel.add_plugin(MathPlugin(), plugin_name="math")Once a plugin is registered, you can either call its functions directly or let the model call them automatically through function calling. The second pattern is what makes semantic kernel useful for agents: you register a bundle of native functions (a search plugin, a database plugin, a calendar plugin), enable auto function calling, and the model decides at runtime which ones to invoke based on the user's request.
from semantic_kernel.connectors.ai import FunctionChoiceBehavior
from semantic_kernel.connectors.ai.open_ai import OpenAIChatPromptExecutionSettings
settings = OpenAIChatPromptExecutionSettings(
function_choice_behavior=FunctionChoiceBehavior.Auto()
)
async def ask_with_tools():
result = await kernel.invoke_prompt(
"What is 42 multiplied by 17, then add 8 to the result?",
settings=settings,
)
print(result)
asyncio.run(ask_with_tools())The model reads the function descriptions, chains multiply then add, and returns a final answer, all without you writing the control flow yourself. This is the exact mechanism behind tool use in Claude, GPT, and Gemini; semantic kernel just gives it a consistent object model across providers.
Plugins: Grouping Functions Like a Class
A plugin bundles related functions the same way a class bundles related methods. Microsoft ships several out-of-the-box plugins you can register directly:
- Text plugin: string manipulation (trim, uppercase, word count).
- Time plugin: current date, time zone conversion, date math.
- HTTP plugin: raw GET and POST requests to external APIs.
- File I/O plugin: read and write local files.
from semantic_kernel.core_plugins import TimePlugin, TextPlugin
kernel.add_plugin(TimePlugin(), plugin_name="time")
kernel.add_plugin(TextPlugin(), plugin_name="text")You can also import an OpenAPI spec directly as a plugin, which turns any REST API with a spec file into a set of callable kernel functions without hand-writing wrapper code.
await kernel.add_plugin_from_openapi(
plugin_name="weather_api",
openapi_document_path="https://example.com/weather/openapi.json",
)This is a genuinely useful shortcut: point semantic kernel at an internal API's OpenAPI document and the model gets tool access to every endpoint in that spec, complete with parameter validation, in one call.
Memory and Vector Store Connectors
Semantic kernel treats memory as a first-class concern rather than something you bolt on. The memory abstraction lets you embed text, store the embeddings in a vector store, and retrieve relevant context before a prompt runs, the standard retrieval-augmented generation (RAG) pattern.
from semantic_kernel.connectors.ai.open_ai import OpenAITextEmbedding
from semantic_kernel.connectors.in_memory import InMemoryVectorStore
kernel.add_service(
OpenAITextEmbedding(ai_model_id="text-embedding-3-small", api_key="YOUR_API_KEY")
)
vector_store = InMemoryVectorStore()
collection = vector_store.get_collection("docs", record_type=dict)
async def index_and_search():
await collection.upsert([
{"id": "1", "text": "Semantic Kernel is an SDK for orchestrating LLM calls."},
{"id": "2", "text": "Plugins group related kernel functions together."},
])
results = await collection.search("How do plugins work?", top=1)
async for r in results.results:
print(r.record["text"])
asyncio.run(index_and_search())Beyond the in-memory store, semantic kernel ships connectors for Azure AI Search, Qdrant, Redis, Chroma, Postgres with pgvector, and several other vector databases, so swapping the backing store rarely means rewriting your retrieval code. This is the piece that turns semantic kernel from a prompt-orchestration toy into something you'd actually put in front of a support inbox or an internal knowledge base search.
Planners: Letting the Kernel Decide the Steps
Early semantic kernel releases leaned heavily on explicit "planners" that would take a goal, look at the registered plugins, and generate a step-by-step plan before executing anything. The FunctionCallingStepwisePlanner is the modern version of this idea: it uses native function calling to decide each step interactively rather than generating a rigid upfront plan.
from semantic_kernel.planners import FunctionCallingStepwisePlanner
from semantic_kernel.planners.function_calling_stepwise_planner import (
FunctionCallingStepwisePlannerOptions,
)
planner = FunctionCallingStepwisePlanner(
service_id="chat",
options=FunctionCallingStepwisePlannerOptions(max_iterations=10),
)
async def run_plan():
result = await planner.invoke(kernel, "Find the sum of 12 and 30, then multiply by 4")
print(result.final_answer)
asyncio.run(run_plan())In practice, most teams now reach for auto function calling (shown earlier) instead of an explicit planner for simple tool chains, and reserve stepwise planners for tasks where you want visibility into the intermediate reasoning steps, useful for debugging or for compliance-sensitive workflows where you need an audit trail of what the model decided to do.
Agents: Multi-Agent Orchestration in Semantic Kernel
The Semantic Kernel Agent Framework adds a layer above the base kernel for building single agents and multi-agent conversations. An agent wraps a kernel, a persona (instructions), and a set of plugins into a reusable object you can hand a task to.
from semantic_kernel.agents import ChatCompletionAgent
research_agent = ChatCompletionAgent(
kernel=kernel,
name="ResearchAgent",
instructions="You research topics and summarize findings in three bullet points.",
)
async def run_agent():
response = await research_agent.get_response(messages="Summarize what semantic kernel is used for.")
print(response.content)
asyncio.run(run_agent())Where this gets interesting is multi-agent orchestration. Semantic Kernel supports group chat patterns where multiple agents, each with different instructions and plugins, take turns responding until a termination condition is met (a reviewer agent approving output, a fixed number of turns, or a custom strategy function).
from semantic_kernel.agents import AgentGroupChat
from semantic_kernel.agents.strategies import DefaultTerminationStrategy
writer = ChatCompletionAgent(kernel=kernel, name="Writer", instructions="Draft short blog posts.")
critic = ChatCompletionAgent(kernel=kernel, name="Critic", instructions="Critique drafts for clarity and suggest edits.")
chat = AgentGroupChat(
agents=[writer, critic],
termination_strategy=DefaultTerminationStrategy(maximum_iterations=4),
)
async def run_group_chat():
await chat.add_chat_message("Write a short post about semantic kernel plugins.")
async for message in chat.invoke():
print(f"{message.name}: {message.content}")
asyncio.run(run_group_chat())This pattern, a writer and a critic passing drafts back and forth, is a common way teams use semantic kernel to raise output quality without a human reviewing every intermediate step. It also mirrors what you'd build by hand with a state machine, except the group chat orchestration and turn-taking logic is already written for you.
Semantic Kernel vs LangChain vs the OpenAI Agents SDK
A fair question if you're picking a framework in 2026: why semantic kernel over the alternatives?
- Semantic Kernel is the strongest choice if your stack is .NET-heavy, if you need first-class Azure integration (Azure OpenAI, Azure AI Search, Azure Functions), or if you want a single object model (the kernel) that works nearly identically across C#, Python, and Java.
- LangChain has a larger open-source ecosystem of community integrations and is often faster to prototype in for pure Python teams, but its abstractions have churned more across versions.
- The OpenAI Agents SDK is leaner and provider-specific; it's a good fit if you're committed to OpenAI models and want minimal abstraction overhead, but you lose the multi-provider and multi-language flexibility semantic kernel gives you.
None of these are mutually exclusive with the model you use. Semantic kernel connectors exist for OpenAI, Azure OpenAI, and several other chat completion providers, so picking semantic kernel as your orchestration layer doesn't lock you into one model vendor.
Filters: Intercepting Function and Prompt Calls
Semantic kernel supports "filters," middleware hooks that run before and after function invocations, prompt rendering, or auto function calling. This is where you'd add logging, cost tracking, or a guardrail that blocks a function call under certain conditions.
from semantic_kernel.filters import FunctionInvocationContext
from typing import Callable, Awaitable
async def logging_filter(
context: FunctionInvocationContext,
next: Callable[[FunctionInvocationContext], Awaitable[None]],
):
print(f"Calling function: {context.function.name}")
await next(context)
print(f"Result: {context.result}")
kernel.add_filter("function_invocation", logging_filter)Filters are the mechanism most production teams use to bolt on observability, since they wrap every function call the kernel makes, whether it originated from your code or from the model deciding to call a tool.
A Minimal End-to-End Example
Putting the pieces together, here is a small but complete example: a kernel with a native plugin, memory-backed search, and auto function calling, answering a question that requires both retrieval and computation.
import asyncio
from semantic_kernel import Kernel
from semantic_kernel.functions import kernel_function
from semantic_kernel.connectors.ai.open_ai import (
OpenAIChatCompletion,
OpenAIChatPromptExecutionSettings,
)
from semantic_kernel.connectors.ai import FunctionChoiceBehavior
class InventoryPlugin:
@kernel_function(description="Look up how many units of a product are in stock")
def check_stock(self, product: str) -> str:
catalog = {"widget": 42, "gadget": 7}
return str(catalog.get(product.lower(), 0))
kernel = Kernel()
kernel.add_service(OpenAIChatCompletion(ai_model_id="gpt-4o", api_key="YOUR_API_KEY"))
kernel.add_plugin(InventoryPlugin(), plugin_name="inventory")
settings = OpenAIChatPromptExecutionSettings(
function_choice_behavior=FunctionChoiceBehavior.Auto()
)
async def main():
answer = await kernel.invoke_prompt(
"How many widgets do we have in stock, and is that enough for an order of 30?",
settings=settings,
)
print(answer)
asyncio.run(main())The model calls check_stock, gets back 42, and reasons about the comparison to 30 on its own, no manual parsing or control flow required on your side.
When Semantic Kernel Is the Wrong Tool
Semantic kernel adds real value once you have multiple plugins, memory, or multi-agent orchestration to manage. For a single prompt hitting a single API with no tools, it's overhead you don't need, a direct SDK call to your model provider is simpler and easier to debug. It's also worth weighing team familiarity: if your team already has deep LangChain expertise and no .NET surface area, switching frameworks purely for semantic kernel's plugin model is rarely worth the migration cost. Pick it when the project's shape, multi-language teams, Azure-centric infrastructure, or agent orchestration needs, actually matches what it's built for.
FAQ
What is Semantic Kernel used for? Semantic kernel is used to orchestrate calls between large language models and your own application code: registering functions as tools an LLM can call, managing prompt templates, storing and retrieving memory through vector stores, and coordinating multiple agents on a shared task.
Is Semantic Kernel free and open source? Yes, semantic kernel is released under an open-source license and is free to use. You still pay standard usage costs to whichever model provider you connect it to (OpenAI, Azure OpenAI, or others), since those are billed separately by the provider.
Does Semantic Kernel only work with OpenAI models? No. Semantic kernel ships connectors for OpenAI, Azure OpenAI, and other chat completion providers, and its abstractions are designed so switching the underlying model doesn't require rewriting plugins, planners, or agent code.
What is the difference between a plugin and a function in Semantic Kernel? A function is a single callable unit, either a prompt template or native code. A plugin is a named group of related functions, similar to a class grouping methods, that gets registered with the kernel as one unit.
Can Semantic Kernel build multi-agent systems? Yes, through the Agent Framework and AgentGroupChat, which let multiple agents with different instructions and plugins take turns on a shared conversation until a termination condition (like a maximum number of turns or a reviewer's approval) is met.
How does Semantic Kernel compare to LangChain? Both orchestrate LLM calls, tools, and memory. Semantic kernel has stronger first-class support for C# and Azure infrastructure and a more consistent object model across languages; LangChain has a larger Python-first community integration ecosystem. Neither locks you into a single model provider.
Do I need a vector database to use Semantic Kernel? No. Vector storage and memory are optional pieces you add when you need retrieval-augmented generation. You can use semantic kernel purely for prompt templates and native function calling without ever registering a memory connector.
BootcampA 30-day guided bootcamp: build, harden and ship a production autonomous agent from scratch.
AI AgentsUnderstand how AI agents really work: the loop, the tools, the memory, and why most agent projects fail.