Migrating from Semantic Kernel to Microsoft Agent Framework in .NET: A Step-by-Step Guide

Search for a command to run...

No comments yet. Be the first to comment.
The first time an AI feature I shipped went down in production, nothing in my code had changed. The model provider had a bad afternoon: a wave of 429 Too Many Requests, then a stretch of 503s, and eve

If you have tried to add OpenTelemetry.Extensions.Logging to a .NET project, you already know how this ends. The restore fails, NuGet tells you the package cannot be found, and you start second-guessi

You called an endpoint that returns an Entity Framework Core entity, and instead of clean JSON you got a wall of red: System.Text.Json.JsonException: A possible object cycle was detected. If you are s

Multi-agent orchestration in .NET has quietly crossed the line from research demo to something you can actually ship. With Microsoft Agent Framework reaching 1.0 and its orchestration layer stable acr

Coding Droplets
295 posts
Coding Droplets is your go-to resource for .NET and ASP.NET Core development. Whether you're just starting out or building production systems, you'll find practical guides, real-world patterns, and clear explanations that actually make sense.
From beginner-friendly tutorials to advanced architecture decisions. We publish fresh .NET content every day to help you grow at every stage of your career.
If you shipped an AI agent in .NET over the last two years, there is a good chance it runs on Semantic Kernel. That was the right call at the time. But with Microsoft Agent Framework reaching GA in April 2026, migrating from Semantic Kernel to Microsoft Agent Framework is now the path Microsoft's own migration guide points existing teams toward, and the API surface is different enough that a "find and replace" migration will not compile. This guide walks the exact changes I make when moving a production agent across, from ChatCompletionAgent to AIAgent, tool registration, sessions, and the dependency injection wiring inside an ASP.NET Core API.
I have done this migration on a real support-triage service, and the trade-off that bit us early was treating it as a mechanical rename. It is not. The two frameworks share vocabulary but differ in how agents are created, how tools attach, and how a conversation keeps state. If you want the complete, runnable version of these patterns wired into a working ASP.NET Core API, the annotated source on Patreon carries the full implementation with the edge cases and error handling this article deliberately keeps short.
Agent Framework is the same primitive the AI-Powered .NET APIs course builds on from the first agent onwards. Chapter 12 constructs an AIAgent with instructions, tools, and agent threads inside one running support API, so the framework you are migrating to is shown in the context you actually deploy it in, not as an isolated console demo.
Semantic Kernel is not being deleted. Microsoft has committed to critical bug fixes for the Semantic Kernel agent abstractions for at least a year after Agent Framework GA, so a stable, in-production agent that needs nothing new is safe to leave alone for now. The reasons to move are concrete rather than cosmetic:
A simpler mental model. Agent Framework works directly against IChatClient from Microsoft.Extensions.AI. There is no Kernel object to construct, own, and thread through every agent.
One agent type instead of many. Semantic Kernel gives you ChatCompletionAgent, OpenAIAssistantAgent, AzureAIAgent, and more. Agent Framework consolidates these into a single ChatClientAgent (exposed through the AIAgent base type) that works with any provider offering an IChatClient.
Less boilerplate for tools. Exposing a method as a tool no longer needs an attribute, a plugin wrapper, and a kernel registration.
A unified future. Agent Framework is where AutoGen and Semantic Kernel's agent stories converge, so new orchestration patterns land here first.
The honest answer to "should I migrate today?" is: migrate new agent work now, and plan the migration of stable production agents rather than rushing it. If your agent is actively growing new capabilities, every week you wait is more Semantic Kernel code to convert later.
Before touching code, it helps to hold the shape of the change in your head. Almost every migration edit falls into one of five buckets.
| Concern | Semantic Kernel | Microsoft Agent Framework |
|---|---|---|
| Namespaces | Microsoft.SemanticKernel, Microsoft.SemanticKernel.Agents |
Microsoft.Extensions.AI, Microsoft.Agents.AI |
| Agent type | ChatCompletionAgent, AzureAIAgent, etc. |
AIAgent / ChatClientAgent |
| Creation | new ChatCompletionAgent { Kernel = ... } |
chatClient.AsAIAgent(...) |
| Tools | [KernelFunction] + KernelPlugin + Kernel |
AIFunctionFactory.Create(method) |
| Conversation state | Manually constructed AgentThread |
agent.CreateSessionAsync() |
| Invocation | InvokeAsync / InvokeStreamingAsync |
RunAsync / RunStreamingAsync |
These snippets assume .NET 10 and Microsoft.Agents.AI 1.x (GA April 2026), with Microsoft.Extensions.AI providing the IChatClient your agent talks through.
Add the Microsoft.Agents.AI package (plus the provider integration you use, for example the OpenAI or Azure AI Foundry package) and update the using directives. Semantic Kernel leans on Microsoft.SemanticKernel.Agents; Agent Framework lives under Microsoft.Agents.AI and borrows its message and content types from Microsoft.Extensions.AI.
// Before
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
// After
using Microsoft.Extensions.AI;
using Microsoft.Agents.AI;
In Semantic Kernel every agent needs a Kernel instance, even an empty one. Agent Framework drops that requirement: you build the agent straight off an IChatClient using the AsAIAgent extension.
// Before - Semantic Kernel
ChatCompletionAgent agent = new()
{
Instructions = "You triage support tickets and set a priority.",
Kernel = kernel // every agent must carry a Kernel
};
// After - Agent Framework
AIAgent agent = chatClient.AsAIAgent(
instructions: "You triage support tickets and set a priority.");
That chatClient is any IChatClient you already register for OpenAI, Azure OpenAI, GitHub Models, or a local Ollama model. The agent no longer owns provider configuration; the chat client does.
This is where Semantic Kernel asked for the most ceremony: decorate the method with [KernelFunction], wrap it in a plugin, add the plugin to a kernel, and hand the kernel to the agent. Agent Framework collapses that into a single argument.
// Before - Semantic Kernel
KernelFunction fn = KernelFunctionFactory.CreateFromMethod(GetTicketStatus);
KernelPlugin plugin = KernelPluginFactory.CreateFromFunctions("Support", [fn]);
kernel.Plugins.Add(plugin);
// ...then the kernel is passed to the agent
// After - Agent Framework
AIAgent agent = chatClient.AsAIAgent(
instructions: "...",
tools: [AIFunctionFactory.Create(GetTicketStatus)]);
The [KernelFunction] attribute is gone. A plain method works as a tool, and a [Description] attribute on the method or its parameters is optional context for the model rather than a requirement.
Semantic Kernel made the caller pick and construct the correct thread type. Agent Framework asks the agent to create a session for you, which keeps provider-specific details out of your code.
// Before - Semantic Kernel
AgentThread thread = new AzureAIAgentThread(client);
// After - Agent Framework
AgentSession session = await agent.CreateSessionAsync();
Semantic Kernel returned an async stream even when you wanted a single reply. Agent Framework's non-streaming call returns one AgentResponse, with the text in response.Text.
// Before - Semantic Kernel
await foreach (var item in agent.InvokeAsync(userInput, thread))
Console.WriteLine(item.Message);
// After - Agent Framework
AgentResponse response = await agent.RunAsync(userInput, session);
Console.WriteLine(response.Text);
For token-by-token output, InvokeStreamingAsync becomes RunStreamingAsync, which yields AgentResponseUpdate objects you concatenate as they arrive.
This is the step that matters most for a web API, and it is where the simplification pays off. Under Semantic Kernel you registered a Kernel and built the agent from it. Under Agent Framework you register the IChatClient once and produce the agent directly.
// Before - Semantic Kernel
services.AddKernel();
services.AddKeyedSingleton<Agent>("triage", (sp, _) =>
new ChatCompletionAgent { Kernel = sp.GetRequiredService<Kernel>() });
// After - Agent Framework
services.AddKeyedSingleton<AIAgent>("triage", (sp, _) =>
sp.GetRequiredService<IChatClient>().AsAIAgent(
instructions: "You triage support tickets and set a priority."));
Getting the DI boundary right in a real API means also thinking about how the agent shares the same IChatClient, resilience, and telemetry you already configured. Wiring an agent into a production endpoint alongside those concerns is exactly what Chapter 12 of the AI-Powered .NET APIs course walks through against a running support API, so the registration above is shown inside a full request pipeline rather than in isolation.
Most migrations fail in the same few places. Here is the short answer, then the detail.
The biggest mistakes are migrating agent creation without migrating orchestration, forgetting that RunAsync returns a single response instead of a stream, and leaving [KernelFunction] attributes on methods that no longer need them.
Splitting agents from their orchestration. The instinct is to convert each ChatCompletionAgent to an AIAgent first and deal with AgentGroupChat later. That leaves you with a hybrid where Semantic Kernel's group chat is trying to coordinate Agent Framework agents, and it does not work. Migrate a group of agents and the code that orchestrates them in the same pass, or keep the whole group on Semantic Kernel until you are ready to move all of it.
Assuming the invocation shape is unchanged. InvokeAsync streamed; RunAsync does not. If you paste a RunAsync call into an old await foreach, it will not compile, and if you only skim the change you may drop tool-call and reasoning messages that now live in response.Messages.
Carrying dead ceremony across. Old [KernelFunction] attributes, plugin factories, and Kernel plumbing compile away slowly. Delete them as you migrate each tool; leaving them makes the new code look more complex than it is and confuses the next reader.
Mapping options one to one. Semantic Kernel's OpenAIPromptExecutionSettings and AgentInvokeOptions do not carry over verbatim. Per-run options move to ChatClientAgentRunOptions, so budget a little time to re-express settings like MaxOutputTokens rather than expecting a direct swap.
Do not declare the migration done until each of these holds:
The project no longer references Microsoft.SemanticKernel.Agents, and the build is clean with no leftover Kernel construction.
Every tool is a plain method registered through AIFunctionFactory.Create, with no [KernelFunction] attributes remaining.
Non-streaming endpoints read response.Text; streaming endpoints consume AgentResponseUpdate from RunStreamingAsync.
Multi-agent flows use Agent Framework orchestration end to end, with no AgentGroupChat left coordinating migrated agents.
Your integration tests exercise a real request through the ASP.NET Core endpoint, not just the agent in isolation, so the DI wiring is proven.
If you are still deciding whether Agent Framework is even the right destination versus staying on Semantic Kernel or using Microsoft.Extensions.AI directly, my comparison of MEAI vs Semantic Kernel vs Agent Framework lays out when each one fits. And once you are on Agent Framework, when to reach for an agent at all in ASP.NET Core is worth a read before you add more of them.
No. Semantic Kernel is not deprecated, and its agent abstractions receive critical bug fixes for at least a year after Agent Framework's GA. Microsoft's guidance is to start new agent projects on Agent Framework and plan the migration of existing production agents, not to treat Semantic Kernel as dead the day you read this.
Yes, within limits. You can migrate one agent or one service at a time, but you cannot mix them inside a single orchestration. If Semantic Kernel's AgentGroupChat is coordinating a set of agents, migrate that whole group together. Isolated single agents are the safe unit to move one at a time.
No. That is one of the central simplifications. Agent Framework builds agents directly from an IChatClient using AsAIAgent, so there is no Kernel to create, register in DI, or pass into each agent. Your provider configuration lives on the chat client instead.
A Semantic Kernel plugin method decorated with [KernelFunction] becomes a plain method passed through AIFunctionFactory.Create in the agent's tools argument. You drop the attribute and the plugin wrapper entirely. An optional [Description] on the method or parameters gives the model extra hints, but nothing is required.
The pattern stays familiar but the types change. InvokeStreamingAsync becomes RunStreamingAsync, and each yielded item is an AgentResponseUpdate instead of a StreamingChatMessageContent. If you already stream over Server-Sent Events, you keep that transport and only adjust how you read each update before writing it to the response.
I'm Celin Daniel, Co-founder of Coding Droplets. I've been building .NET and ASP.NET Core systems in production for 13+ years - APIs, distributed backends, enterprise platforms. Everything I write here comes from real shipping experience: patterns that held up, trade-offs that bit us, and lessons learned the hard way.
GitHub: codingdroplets
YouTube: Coding Droplets
Website: codingdroplets.com