← All guides
Interactive guide Β· 10 levels

Your first agent with Microsoft Foundry

From your first ChatAgent with Microsoft Agent Framework, to a production multi-agent workflow on Microsoft Foundry, with load balancing.

0 / 10 completed
Level 1 Β· Foundations

Your first ChatAgent

Goal: install Microsoft Agent Framework and create your first agent connected to a model hosted on Microsoft Foundry.

Microsoft Agent Framework (GA since April 2026) is the official convergence of Semantic Kernel and AutoGen into a single production SDK, available in Python and .NET. ChatAgent is its main abstraction: an agent that uses a "chat client" to talk to any model, including those deployed on Microsoft Foundry.

1. Install the dependencies

TERMINAL
pip install agent-framework agent-framework-azure-ai azure-identity az login # authenticate against your Azure subscription

2. Your first agent

agent.py
import asyncio from agent_framework import ChatAgent from agent_framework.azure import AzureAIAgentClient from azure.identity.aio import DefaultAzureCredential async def main(): async with DefaultAzureCredential() as cred: async with ChatAgent( chat_client=AzureAIAgentClient(async_credential=cred), name="assistant", instructions="Answer briefly and clearly.", ) as agent: response = await agent.run("What is an AI agent, in one sentence?") print(response.text) asyncio.run(main())

AzureAIAgentClient connects directly to a Microsoft Foundry project β€” the model, authentication, and endpoint are already managed on the Azure side, you just reference the project.

Challenge

Run the same agent with agent-framework devui to inspect the execution in the local visual debugger.

Level 2 Β· Personality

Instructions and model parameters

Goal: control the agent's behavior with instructions and generation parameters.
agent.py
agent = ChatAgent( chat_client=AzureAIAgentClient(async_credential=cred), name="support", instructions=( "You are a technical support assistant. Answer in English, " "briefly and with numbered steps. If you don't know something, say so." ), temperature=0.3, max_tokens=400, ) response = await agent.run("The app crashes when I open the camera")

The temperature and max_tokens parameters are passed directly to ChatAgent, with no need to configure a separate model object.

Challenge

Write instructions for an agent that only answers questions about a specific domain and politely declines any other question.

Level 3 Β· Tools

Tools: plain Python functions

Goal: give the agent tools by passing Python functions directly in tools.
agent.py
def current_weather(location: str) -> str: """Returns the current reported weather for a given location.""" data = {"Guatemala City": "22Β°C, partly cloudy"} return data.get(location, "no data for that location") agent = ChatAgent( chat_client=AzureAIAgentClient(async_credential=cred), name="weather-assistant", instructions="You help look up the weather in cities.", tools=current_weather, # also accepts a list: tools=[current_weather, other_tool] ) response = await agent.run("What's the weather like in Guatemala City?")

Just like in other modern frameworks, the function's docstring and type hints are what the model uses to decide when and how to call it β€” there's no need to hand-write a JSON schema.

Challenge

Add HostedCodeInterpreterTool() to the tools list and ask the agent to compute basic statistics for a list of numbers.

Level 4 Β· Memory

Threads: conversational memory

Goal: keep context across turns using an explicit conversation thread.

In Agent Framework, each stateful conversation lives in a thread β€” you create it once and pass it to every agent.run() call so the agent remembers previous turns.

chat_with_memory.py
thread = agent.get_new_thread() r1 = await agent.run("I'm looking for a laptop for graphic design", thread=thread) r2 = await agent.run("Which is the cheapest of the ones you mentioned?", thread=thread) print(r2.text) # the agent remembers the laptops mentioned in r1

Each thread is independent β€” that's what lets you serve several users in parallel with the same agent object, simply by creating a separate thread per user or session.

Challenge

Simulate two different users with two separate threads and confirm their conversations don't mix.

Level 5 Β· Structured output

Typed responses with Pydantic

Goal: force the agent to return a validated object instead of free-form text.
agent.py
from pydantic import BaseModel class SupportTicket(BaseModel): category: str urgency: str summary: str agent = ChatAgent( chat_client=AzureAIAgentClient(async_credential=cred), name="classifier", instructions="Classify the incoming support ticket.", response_format=SupportTicket, ) result = await agent.run("The app crashes when uploading a profile photo, it's urgent") ticket: SupportTicket = result.value print(ticket.category, ticket.urgency, ticket.summary)
Challenge

Define a Pydantic model to extract the rating, sentiment, and whether a specific problem is mentioned from a product review.

Level 6 Β· Streaming

run_stream for live responses

Goal: show the agent's response in real time, token by token.
stream.py
async for update in agent.run_stream("Explain what RAG is in 3 steps", thread=thread): if update.text: print(update.text, end="", flush=True)

ChatAgent supports both streaming and non-streaming responses with the same base API β€” you swap run() for run_stream() without restructuring the rest of the code.

Challenge

Wrap run_stream in a FastAPI endpoint with StreamingResponse for a live chat.

Level 7 Β· Multi-agent

Several specialized ChatAgents

Goal: create independent specialized agents as a first step before orchestrating them.

Before building a full workflow, the first multi-agent step is simply to have several ChatAgent instances with different instructions, each good at one thing β€” you'll handle composing them with a workflow in the next level.

specialists.py
sales_agent = ChatAgent( chat_client=AzureAIAgentClient(async_credential=cred), name="sales", instructions="Specialist in pricing and product questions.", ) support_agent = ChatAgent( chat_client=AzureAIAgentClient(async_credential=cred), name="support", instructions="Specialist in technical errors and bugs.", ) # An agent can use another agent as a tool, just like in other frameworks: async def ask_sales(question: str) -> str: """Delegate a sales question to the sales specialist.""" r = await sales_agent.run(question) return r.text
Challenge

Add ask_sales as a tool for a third "front desk" agent and confirm it delegates correctly.

Level 8 Β· Orchestrator

Workflows: graph-based orchestration

Goal: build an explicit orchestrator with WorkflowBuilder, Agent Framework's graph-based workflow system.

For predictable multi-agent orchestration in production, Agent Framework provides graph-based workflows β€” inherited from the "Semantic Kernel" side of the merger β€” where each agent is a node and the edges define the flow of data between them, with built-in patterns for sequential, concurrent, and handoff orchestration.

orchestrator.py
from agent_framework import WorkflowBuilder supervisor = ChatAgent( chat_client=AzureAIAgentClient(async_credential=cred), name="supervisor", instructions="Analyze the message and decide which specialist to send it to.", ) workflow = ( WorkflowBuilder() .add_node(supervisor) .add_node(sales_agent) .add_node(support_agent) .add_edge(supervisor, sales_agent, condition=lambda r: "price" in r.text.lower()) .add_edge(supervisor, support_agent, condition=lambda r: "error" in r.text.lower()) .set_start(supervisor) .build() ) result = await workflow.run("I was charged twice for the subscription")

This gives you explicit, traceable control over routing (as opposed to letting a single agent "decide on its own" with tools) β€” key when you need to audit why the system made each decision.

Challenge

Add a third billing node with its own routing condition, and test an ambiguous message to see how the tie is resolved.

Level 9 Β· Production

DevUI, telemetry, and guardrails

Goal: visually debug the workflow and trace its execution with OpenTelemetry.
TERMINAL
agent-framework devui # Local debugger in the browser: shows execution traces, # message flow between agents, tool calls, and routing decisions in real time
  • Native telemetry via OpenTelemetry β€” every workflow step, every tool call, is traced and exportable to your observability backend
  • Inherited Semantic Kernel middleware: you can intercept every model call for logging, rate limiting, or content validation
  • Strongly typed state management β€” the state flowing between workflow nodes is validated, not a free-form dictionary
Challenge

Add middleware that logs to a structured log how long each node in the Level 8 workflow took to respond.

Level 10 Β· Final architecture

Microsoft Foundry Agent Service + load balancing

Goal: deploy the workflow as hosted agents on Microsoft Foundry, scaled and load-balanced.

Microsoft Foundry Agent Service is Azure's hosted layer for running agents in production without managing your own compute infrastructure β€” but when you need to control horizontal scaling yourself (for example, for workflows with custom logic like Level 8), you host it on Azure Container Apps or App Service behind a load balancer.

1. Each specialist as its own service

support_service.py
from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class Query(BaseModel): message: str thread_id: str @app.post("/query") async def query(q: Query): thread = threads_by_id.setdefault(q.thread_id, support_agent.get_new_thread()) r = await support_agent.run(q.message, thread=thread) return {"response": r.text} # uvicorn support_service:app --host 0.0.0.0 --port 8001

2. Load balancing with Azure

final architecture
Client β”‚ β–Ό Azure Front Door / Application Gateway (load balancer) β”‚ β–Ό Supervisor workflow (Container Apps, auto-scaled) β”‚ β–Ό Azure Service Bus (queue, decouples traffic spikes) β”‚ β”œβ”€β”€β–Ά Replicas ──▢ [Sales Specialist x3 β€” Container Apps] β”œβ”€β”€β–Ά Replicas ──▢ [Support Specialist x3 β€” Container Apps] └──▢ Replicas ──▢ [Billing Specialist x3 β€” Container Apps] β”‚ β–Ό Application Insights (OpenTelemetry, end-to-end traces)

With this you have the full path: from a few-line ChatAgent in Level 1, to a production multi-agent workflow on Azure, with queues, balanced replicas, and end-to-end telemetry.

Final challenge

Package the support specialist from Level 7 as a container, deploy it with 2 replicas on Azure Container Apps, and put it behind Application Gateway.