AutoGen to Microsoft Agent Framework: Step-by-Step Migration

Multi-agent orchestration was fragmented across two Microsoft frameworks. AutoGen provided flexible agent teams and GroupChat, while Semantic Kernel offered plugins and kernel-based composition. With the February 2026 release of Microsoft Agent Framework (MAF) in Release Candidate status, both converge into a unified API surface with stable orchestration primitives, cleaner tool registration, and integrated support for long-running, human-in-the-loop workflows (Source: Microsoft Foundry). If you are running AutoGen agents in production today, migrating to MAF is now practical: the API is stable, migration effort is low, and the payoff is simpler code with better state management. This guide walks you through three phases of refactoring - agent instantiation, tool definition, and multi-agent coordination - with concrete before/after code for Python.

Before You Start: Inventory and Validation

Understand what you are migrating. AutoGen deployments typically use AssistantAgent and UserProxyAgent classes paired with a dictionary-based llm_config for model and API settings. Semantic Kernel codebases organize logic into Plugin classes decorated with @kernel_function. Both patterns worked, but they diverge significantly from MAF's unified client model.

Start by auditing:

  • How many agents run across your system? (MAF tracks agent identity; you will assign each one on creation.)
  • How many share a single llm_config or API key? (This shared-credential pattern is exactly what MAF eliminates via per-agent authentication.)
  • What tool framework do you use? (AutoGen's FunctionTool objects, Semantic Kernel's decorated plugin methods, or raw Python functions?)
  • Do you have test coverage for agent behavior, tool invocation, and multi-turn conversations? (You will want it when validating the migration.)

Plan 2-3 days per agent for refactoring plus testing. Operator note (first-hand): migrate one agent end-to-end in a staging environment before rolling out to your fleet. Code that passes in AutoGen's loose llm_config dicts may fail in MAF's stricter client instantiation - particularly around credential passing, system message format, and tool schema validation. Test context persistence explicitly: create a thread, send two messages, and confirm the agent recalls the first message in its response to the second.

Phase 1: Agent Instantiation and Client Setup

The biggest single change is authentication and client initialization. AutoGen relied on passing API keys and model metadata in dictionaries; MAF uses a client object that you create once and reuse across all agents.

From AutoGen to MAF

In AutoGen, you set up agents like this:

from autogen import AssistantAgent, UserProxyAgent

llm_config = {
"model": "gpt-4",
"api_type": "azure",
"api_base": os.getenv("AZURE_OPENAI_ENDPOINT"),
"api_key": os.getenv("AZURE_OPENAI_KEY"),
"api_version": "2024-08-01-preview"
}

assistant = AssistantAgent(
name="Assistant",
llm_config=llm_config,
system_message="You are a helpful assistant."
)

user_proxy = UserProxyAgent(
name="User",
human_input_mode="ALWAYS",
code_execution_config=False
)

In MAF, the pattern is declarative and credential-driven:

from azure.identity import DefaultAzureCredential
from azure.ai.foundry import AzureOpenAIResponsesClient

client = AzureOpenAIResponsesClient(
credential=DefaultAzureCredential(),
endpoint=os.getenv("AZURE_OPENAI_ENDPOINT")
)

agent = client.create_agent(
name="Assistant",
instructions="You are a helpful assistant.",
model="gpt-4"
)

# For multi-agent coordination, create a thread
thread = client.create_thread()

Key differences:

  • Credentials: AutoGen passed API keys as strings; MAF uses DefaultAzureCredential() from azure-identity. This reads from environment variables, Managed Identity, or interactive login - whatever your Azure environment provides. If you run on Azure Container Instances or App Service, Managed Identity is automatic. Locally, az login authenticates via your default browser.
  • Agent vs. User: AutoGen's UserProxyAgent was a proxy for human input or code execution. MAF treats agents uniformly; if you need human-in-the-loop, you pause the orchestrator and prompt the user explicitly.
  • System message: AutoGen's system_message parameter becomes instructions in MAF. Same content, different name.
  • Thread for context: MAF's Thread object automatically persists conversation history and state. No more manual ChatHistory management.

From Semantic Kernel to MAF

If you are using Semantic Kernel, the shift is similar in spirit but different in practice:

AutoGen (before):

from semantic_kernel import Kernel
from semantic_kernel.services import AzureOpenAIChatCompletion

kernel = Kernel()
service = AzureOpenAIChatCompletion(
api_key=os.getenv("AZURE_OPENAI_KEY"),
endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
deployment_id="gpt-4"
)
kernel.add_service(service)

chat_history = ChatHistory()
chat_history.add_user_message("Summarize the main themes...")
result = await kernel.invoke_prompt("{{$input}}", input="...")

MAF (after):

from azure.identity import DefaultAzureCredential
from azure.ai.foundry import AzureOpenAIResponsesClient

client = AzureOpenAIResponsesClient(
credential=DefaultAzureCredential(),
endpoint=os.getenv("AZURE_OPENAI_ENDPOINT")
)

agent = client.create_agent(
name="Summarizer",
instructions="You are a concise summarizer.",
model="gpt-4"
)

thread = client.create_thread()
response = await client.run_agent(agent, thread, "Summarize the main themes...")

The Kernel object disappears; you work directly with the client and thread. The invoke_prompt call becomes a simple run_agent call. Semantic Kernel's service registration abstraction is gone - the client handles it (Source: Nithin Mohan TK, dataa.dev migration series).

Phase 2: Tools and Function Definition

Tools changed most visibly. AutoGen required you to wrap Python functions in FunctionTool objects with explicit schema dictionaries. Semantic Kernel used class-based Plugin decorators. MAF unified both under a single @ai_function decorator and Pydantic-based parameter documentation.

AutoGen FunctionTool to MAF @ai_function

In AutoGen:

from autogen import FunctionTool

def get_weather(city: str) -> str:
"""Get the current weather for a city."""
import requests
response = requests.get(f"https://api.weather.example.com/current?q={city}")
return response.json().get("description", "Unknown")

weather_tool = FunctionTool(
func=get_weather,
description="Fetch the current weather for a given city",
schema={
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "Name of the city (e.g., San Francisco)"
}
},
"required": ["city"]
}
)

# Pass to agent when you create it
assistant = AssistantAgent(name="Assistant", llm_config=..., tools=[weather_tool])

In MAF:

from microsoft.agents import ai_function
from typing import Annotated
from pydantic import Field

@ai_function
def get_weather(city: Annotated[str, Field(description="Name of the city (e.g., San Francisco)")]) -> str:
"""Get the current weather for a city."""
import requests
response = requests.get(f"https://api.weather.example.com/current?q={city}")
return response.json().get("description", "Unknown")

# Pass function reference directly
agent = client.create_agent(name="Assistant", tools=[get_weather], ...)

The advantages are clear:

  • No schema dict: Type hints and Pydantic's Field() replace the verbose JSON schema.
  • No wrapper object: The decorator sits on the function itself, not a separate class.
  • Automatic registration: Pass the function to create_agent() directly; MAF inspects it.
  • Cleaner type-checking: Type hints are Python-native and IDE-discoverable.

The migration path is straightforward: unwrap each FunctionTool, move the description from the schema dict to Field(description="..."), and apply the @ai_function decorator (Source: Nithin Mohan TK).

Semantic Kernel Plugins to MAF Tools

Semantic Kernel plugins were class-based:

from semantic_kernel.functions import kernel_function
from semantic_kernel.functions.kernel_function_decorator import kernel_function

class WeatherPlugin:
@kernel_function(description="Get weather for a city")
def get_weather(self, city: str) -> str:
"""Fetch the current weather."""
...

kernel.add_plugin(WeatherPlugin(), "weather")

In MAF, you move the method out of the class:

from microsoft.agents import ai_function
from typing import Annotated
from pydantic import Field

@ai_function
def get_weather(city: Annotated[str, Field(description="Name of the city")]) -> str:
"""Fetch the current weather."""
...

agent = client.create_agent(name="...", tools=[get_weather], ...)

The [Description] attributes in C# or docstrings in Python become Field(description="...") in the annotation. That is the essence of the change (Source: Nithin Mohan TK).

Phase 3: Multi-Agent Orchestration and Workflows

The third and most consequential shift is how agents coordinate. AutoGen's GroupChat was a messaging loop with heuristics (round-robin, max rounds, speaker selection). MAF replaces it with explicit orchestrator classes and workflow definitions.

In AutoGen:

from autogen import GroupChat, GroupChatManager

group_chat = GroupChat(
agents=[researcher, writer, editor],
messages=[],
max_round=5,
speaker_selection_method="round_robin"
)

manager = GroupChatManager(groupchat=group_chat, llm_config=llm_config)

# Kick off the chat
user_proxy.initiate_chat(
manager,
message="Write a blog post about AI agents. When done, announce FINAL ANSWER."
)

# Extract final output
last_msg = assistant.last_message() # or similar

In MAF:

from microsoft.agents import SequentialOrchestrator

orchestrator = SequentialOrchestrator(agents=[researcher, writer, editor])

result = await orchestrator.run(
user_message="Write a blog post about AI agents. When done, announce FINAL ANSWER."
)

final_message = result.messages[-1].content

The difference is declarative vs. imperative. AutoGen's GroupChat was a live message loop with heuristic speaker selection. MAF's orchestrators are explicit:

  • Sequential: Each agent runs in order; output from one becomes input to the next.
  • Concurrent: All agents run in parallel (useful for independent research tasks).
  • Handoff: An agent passes control to the next based on a condition or signal.
  • Magnetic: Context-aware coordination, similar to LangGraph's state management (Source: Seenivasa Ramadurai, Dev.to).
  • Custom Graph: Build your own orchestration topology.

For a blog-writing pipeline (research -> draft -> edit), Sequential is the right choice. For parallel research across multiple sources, use Concurrent and then feed results to a synthesis step.

Testing and Validation Checklist

Validate the migration systematically:

  1. Imports resolved: No remaining from autogen import statements. Check that azure-identity and azure-ai-foundry packages are installed.
  2. Credentials work: Run DefaultAzureCredential() and confirm it finds credentials. If using local development, run az login.
  3. Agent creation succeeds: Call client.create_agent() and verify the returned object has name, instructions, and model attributes.
  4. Tool invocation: Call each tool directly in Python to confirm it works, then pass it to an agent and check the agent can invoke it.
  5. Context persistence: Create a thread, send message A ("My favorite color is blue"), send message B ("What is my favorite color?"), confirm the agent answers correctly.
  6. Workflow execution: Run the orchestrator with a 3-agent pipeline and confirm each agent runs in order and passes output forward.
  7. Error messages: Test failure paths: missing credentials, invalid model name, tool that raises an exception. Confirm errors are clear, not silent hangs.

Common Pitfalls and Fixes

  • "ModuleNotFoundError: No module named 'azure.ai.foundry'": Install via pip install azure-ai-foundry azure-identity.
  • "Agent refuses to invoke tools": Ensure tool function names and signatures match exactly. MAF is strict about type hints; if a tool expects str and the agent passes int, it will fail.
  • "Thread context is lost after the first message": Reuse the same thread object across multiple run_agent() calls. Creating a new thread for each message discards history.
  • "Orchestrator hangs waiting for human input": MAF's default is fully autonomous. If you need a human-in-the-loop step, explicitly pause the orchestrator and prompt the user.
  • "Cannot find my system message in the response": MAF calls it instructions, not system_message. Update your create_agent() call.
  • "Tool returns a complex object and the agent chokes": MAF requires tools to return JSON-serializable types. If you return a Pandas DataFrame or custom class, serialize it first (to dict or JSON string).

FAQ

Q: Does MAF run my existing AutoGen code unchanged?
No. The agent and tool APIs diverged enough that you need to refactor. Expect 2-3 days per agent, plus testing.

Q: Can I use MAF with my existing Azure OpenAI setup?
Yes, but authentication changes from explicit API keys to DefaultAzureCredential(). This is a security improvement and works in any Azure compute environment (VMs, containers, App Service) automatically.

Q: What if my multi-agent logic is more complex than Sequential or Handoff?
MAF's five orchestration patterns cover most real-world cases. If you need bespoke logic, build a custom orchestrator class inheriting from the base Orchestrator class and define your own run logic.

Q: Is migration reversible?
No. Once you refactor to MAF, reverting to AutoGen is impractical. Test thoroughly in staging before production rollout.

Q: Does this guide cover C#?
The steps are identical in C#, but syntax differs. The patterns - client creation, agent instantiation, tool definition, orchestrator setup - are language-agnostic. Check the Microsoft Foundry docs for C# examples.

Q: What about support for non-Azure models?
MAF supports OpenAI, Anthropic Claude, AWS Bedrock, and Ollama via appropriate client classes. The pattern is the same: create the client, define agents, pass tools, run the orchestrator (Source: Microsoft Foundry blog).

References

  • Doran Gao - From AutoGen to Microsoft Agent Framework case study (Medium, January 2026)
  • Microsoft Foundry - Microsoft Agent Framework Release Candidate announcement (February 2026)
  • Nithin Mohan TK - Migration Guide: From Semantic Kernel & AutoGen to Microsoft Agent Framework, Part 10 (dataa.dev, November 2025)
  • Seenivasa Ramadurai - Microsoft Agent Framework: Combining Semantic Kernel & AutoGen for Advanced AI Agents (Dev.to, October 2025)