# How to Migrate from Semantic Kernel to Agent Framework: A Complete Guide

> Migrate from Semantic Kernel to Agent Framework easily. Replace KernelProcessStep with Executor, convert ProcessBuilder to WorkflowBuilder, and run workflows natively. Get our complete guide.

- Repository: [Microsoft/agent-framework](https://github.com/microsoft/agent-framework)
- Tags: migration-guide
- Published: 2026-04-05

---

**TLDR: To migrate from Semantic Kernel to the Agent Framework, replace `KernelProcessStep` subclasses with `Executor` classes using the `@handler` decorator, convert `ProcessBuilder` graph definitions to `WorkflowBuilder` edges, and execute workflows natively via `workflow.run()` instead of using the SK kernel runtime.**

The `microsoft/agent-framework` repository offers a native Python alternative to Semantic Kernel's process orchestration model, eliminating the need for an external kernel while maintaining a graph-based execution philosophy. Both frameworks compose AI agents through directed graphs, but the Agent Framework uses typed message passing via `WorkflowContext` rather than SK's event emission model. This guide maps each Semantic Kernel construct to its Agent Framework equivalent using production code from the official migration samples.

## Core Abstraction Mapping

Understanding the conceptual differences between the two frameworks simplifies the migration:

| Aspect | Semantic Kernel | Agent Framework |
|--------|-----------------|-----------------|
| **Primary abstraction** | `KernelProcess` – directed graph of steps | `Workflow` – graph of `Executor` nodes |
| **Step definition** | Sub-class `KernelProcessStep` with `@kernel_function` | Sub-class `Executor` with `@handler` |
| **Message passing** | Events (`ProcessEvent`) with visibility levels | Typed messages via `WorkflowContext[In, Out]` |
| **Orchestration** | `ProcessBuilder` + kernel-hosted `start` runtime | `WorkflowBuilder` + native `WorkflowExecutor` |
| **Nested workflows** | Sub-processes via `add_step_from_process` | `WorkflowExecutor` instances linked via `add_edge` |
| **AI clients** | `OpenAIChatCompletion` SK service | `OpenAIChatCompletionClient` direct injection |

## Migrating a Nested Process

The migration transforms a parent-child process hierarchy into a workflow containing nested `WorkflowExecutor` instances.

### Semantic Kernel Implementation

In Semantic Kernel, nested processes require registering a sub-process as a step:

```python

# From: python/samples/semantic-kernel-migration/processes/nested_process.py

process_builder = _create_linear_process("Outer")
nested_process_step = process_builder.add_step_from_process(
    _create_linear_process("Inner")
)
process_builder.steps[1].on_event(
    ProcessEvents.OUTPUT_READY_INTERNAL.value
).send_event_to(
    nested_process_step.where_input_event_is(ProcessEvents.START_PROCESS.value)
)

```

### Agent Framework Implementation

The Agent Framework treats nested workflows as first-class nodes:

```python

# From: python/samples/semantic-kernel-migration/processes/nested_process.py

inner_executor = _build_inner_workflow()  # Returns WorkflowExecutor

outer_workflow = (
    WorkflowBuilder(start_executor=kickoff)
    .add_edge(kickoff, outer_echo)
    .add_edge(outer_echo, outer_repeat)
    .add_edge(outer_repeat, inner_executor)  # Nested workflow as node

    .add_edge(inner_executor, collector)
    .build()
)

```

Key implementation files include `[python/samples/semantic-kernel-migration/processes/nested_process.py]` for the side-by-side comparison and `[python/packages/orchestrations/tests/test_sequential.py]` for unit test validation.

## Migrating Sequential Orchestration

For linear agent chains, replace `SequentialOrchestration` with `SequentialBuilder` from `[python/packages/orchestrations/_orchestration.py]`.

### Semantic Kernel Sequential Pattern

```python

# Requires SK kernel and runtime

sequential_orchestration = SequentialOrchestration(
    members=build_semantic_kernel_agents(),
    agent_response_callback=sk_agent_response_callback,
)
runtime = InProcessRuntime()
await sequential_orchestration.invoke(task=prompt, runtime=runtime)

```

### Agent Framework Sequential Pattern

```python

# Native Python execution, no kernel required

writer = Agent(client=client, instructions=..., name="writer")
reviewer = Agent(client=client, instructions=..., name="reviewer")

workflow = SequentialBuilder(participants=[writer, reviewer]).build()

async for event in workflow.run(prompt, stream=True):
    if event.type == "output":
        # Process final messages

```

Reference implementation available in `[python/samples/semantic-kernel-migration/orchestrations/sequential.py]`.

## Step-by-Step Migration Guide

Follow these six steps to convert existing Semantic Kernel processes:

1. **Identify SK steps** – Locate classes inheriting `KernelProcessStep` and functions decorated with `@kernel_function`.

2. **Create AF Executors** – Implement `Executor` subclasses with `@handler` decorated methods. Map `KernelProcessStep` input/output to `WorkflowContext` type parameters:

   ```python
   class OuterRepeatExecutor(Executor):
       def __init__(self, *, inner_target_id: str) -> None:
           super().__init__(id="outer_repeat")
           self._inner_target_id = inner_target_id

       @handler
       async def repeat(
           self, 
           payload: RepeatPayload, 
           ctx: WorkflowContext[RepeatPayload]
       ) -> None:
           repeated = " ".join([payload.message] * payload.count)
           await ctx.send_message(
               RepeatPayload(message=repeated, count=2),
               target_id=self._inner_target_id
           )
   ```

3. **Rebuild the graph** – Replace `ProcessBuilder` with `WorkflowBuilder`:
   - Define a **start executor** that receives initial input
   - Connect nodes using `.add_edge(parent, child)`
   - For nested processes, instantiate `WorkflowExecutor` and attach it like any other node

4. **Update runtime calls** – Remove `Kernel` imports and `start` invocations. Execute via:
   
   ```python
   async for event in outer_workflow.run(initial_message, stream=True):
       if event.type == "output":
           results.append(event.data)
   ```

5. **Update dependencies** – Remove `semantic-kernel` from [`requirements.txt`](https://github.com/microsoft/agent-framework/blob/main/requirements.txt), replace `OpenAIChatCompletion` with `OpenAIChatCompletionClient`, and ensure `agent-framework` is installed from `[python/packages/agent-framework]`.

6. **Validate** – Run AF unit tests in `[python/packages/orchestrations/tests/]` to verify functional parity with original SK behavior.

## Complete Migration Example: Nested Process

The following implementation from `[python/samples/semantic-kernel-migration/processes/nested_process.py]` demonstrates `KickoffExecutor`, message passing via `ctx.send_message()`, and final output collection via `ctx.yield_output()`:

```python
import asyncio
from dataclasses import dataclass
from typing import Never, cast, Sequence

from agent_framework import Executor, WorkflowBuilder, WorkflowContext, WorkflowExecutor, handler
from dotenv import load_dotenv

load_dotenv()

@dataclass
class RepeatPayload:
    message: str
    count: int = 2

class KickoffExecutor(Executor):
    @handler
    async def start(self, message: str, ctx: WorkflowContext[RepeatPayload]) -> None:
        await ctx.send_message(RepeatPayload(message=message, count=2))

class OuterEchoExecutor(Executor):
    @handler
    async def echo(self, payload: RepeatPayload, ctx: WorkflowContext[RepeatPayload]) -> None:
        await ctx.send_message(payload)

class OuterRepeatExecutor(Executor):
    def __init__(self, *, inner_target_id: str) -> None:
        super().__init__(id="outer_repeat")
        self._inner_target_id = inner_target_id

    @handler
    async def repeat(self, payload: RepeatPayload, ctx: WorkflowContext[RepeatPayload]) -> None:
        repeated = " ".join([payload.message] * payload.count)
        await ctx.send_message(
            RepeatPayload(message=repeated, count=2),
            target_id=self._inner_target_id
        )

class InnerEchoExecutor(Executor):
    @handler
    async def echo(self, payload: RepeatPayload, ctx: WorkflowContext[RepeatPayload]) -> None:
        await ctx.send_message(payload)

class InnerRepeatExecutor(Executor):
    @handler
    async def repeat(self, payload: RepeatPayload, ctx: WorkflowContext[Never, str]) -> None:
        repeated = " ".join([payload.message] * payload.count)
        await ctx.yield_output(repeated)

class CollectResultExecutor(Executor):
    @handler
    async def collect(self, result: str, ctx: WorkflowContext[Never, str]) -> None:
        await ctx.yield_output(result)

def _build_inner_workflow() -> WorkflowExecutor:
    inner_echo = InnerEchoExecutor()
    inner_repeat = InnerRepeatExecutor()
    inner_wf = WorkflowBuilder(
        start_executor=inner_echo
    ).add_edge(inner_echo, inner_repeat).build()
    return WorkflowExecutor(inner_wf, id="inner_workflow")

async def run_agent_framework_nested_workflow(initial_message: str) -> Sequence[str]:
    inner_executor = _build_inner_workflow()
    
    kickoff = KickoffExecutor()
    outer_echo = OuterEchoExecutor()
    outer_repeat = OuterRepeatExecutor(inner_target_id=inner_executor.id)
    collector = CollectResultExecutor()
    
    outer_wf = (
        WorkflowBuilder(start_executor=kickoff)
        .add_edge(kickoff, outer_echo)
        .add_edge(outer_echo, outer_repeat)
        .add_edge(outer_repeat, inner_executor)
        .add_edge(inner_executor, collector)
        .build()
    )
    
    results: list[str] = []
    async for ev in outer_wf.run(initial_message, stream=True):
        if ev.type == "output":
            results.append(cast(str, ev.data))
    return results

```

This implementation produces identical output to the Semantic Kernel version: `"Test Test Test Test"`.

## Key Implementation Files

Reference these files when migrating your own implementations:

- `[python/samples/semantic-kernel-migration/processes/nested_process.py]` – Side-by-side nested workflow comparison
- `[python/samples/semantic-kernel-migration/orchestrations/sequential.py]` – Linear agent chain migration
- `[python/packages/orchestrations/_orchestration.py]` – `SequentialBuilder` implementation
- `[python/agent_framework/__init__.py]` – Core exports including `Executor` and `WorkflowBuilder`
- `[python/packages/orchestrations/tests/test_sequential.py]` – Unit tests for workflow patterns
- `[python/packages/orchestrations/tests/test_orchestration_request_info.py]` – Orchestration request validation

## Summary

- **Replace** `KernelProcessStep` with `Executor` subclasses using `@handler` decorators instead of `@kernel_function`
- **Convert** SK events to typed AF messages via `WorkflowContext[In, Out]` using `send_message()` and `yield_output()`
- **Construct** workflows using `WorkflowBuilder.add_edge()` or `SequentialBuilder` instead of `ProcessBuilder` event wiring
- **Execute** using native Python `async for` loops with `workflow.run(stream=True)` rather than kernel-hosted runtimes
- **Nest** workflows by passing `WorkflowExecutor` instances as nodes to parent builders
- **Update** AI clients from SK services to direct `OpenAIChatCompletionClient` injection

## Frequently Asked Questions

### What replaces `KernelProcessStep` in the Agent Framework?

The **`Executor`** class replaces `KernelProcessStep`. While SK steps use `@kernel_function` to expose capabilities, AF executors use the **`@handler`** decorator on async methods that receive typed payloads and a `WorkflowContext` for message operations. The main entry point exports are defined in `[python/agent_framework/__init__.py]`.

### How do Semantic Kernel events map to Agent Framework constructs?

SK's `ProcessEvent` (with Public/Internal visibility) maps to **typed messages** passed via `ctx.send_message()`. Instead of event names and string payloads, AF uses Python dataclasses as message types with generic `WorkflowContext[In, Out]` typing, enabling compile-time safety for workflow edges.

### Can I migrate gradually or does it require a full rewrite?

Migration requires a full rewrite of the orchestration layer because the runtime models are fundamentally different: SK requires a kernel-hosted process runtime while AF uses native Python async execution. However, the business logic inside your step functions translates directly to handler methods, and the graph structure in `ProcessBuilder` ports one-to-one to `WorkflowBuilder` edges.

### Where are the official migration samples located?

The repository provides side-by-side implementations in `[python/samples/semantic-kernel-migration/]`, specifically `[python/samples/semantic-kernel-migration/processes/nested_process.py]` for hierarchical workflows and `[python/samples/semantic-kernel-migration/orchestrations/sequential.py]` for linear agent chains. Additional validation tests reside in `[python/packages/orchestrations/tests/test_sequential.py]`.