Featured image of post Microsoft Agent Framework I: Get Started with AI Agents

Microsoft Agent Framework I: Get Started with AI Agents

Learn the foundations of Microsoft Agent Framework by building and testing a small .NET customer-support agent.

Introduction

A language model can generate an answer, but an AI agent is an application boundary around that model. The boundary gives the model instructions, accepts user input, and can later grow to include tools, conversation state, memory, and workflows.

Microsoft Agent Framework provides the .NET abstractions for building these applications. Its central abstraction is AIAgent: an object that turns user input into an AgentResponse and can keep a conversation in an AgentSession.

This is the first article in the series. We start with the smallest useful agent, expose it through an ASP.NET Core API, and test the application without making unit tests depend on a real model provider. Later articles will add tools, MCP, memory, RAG, Harness, workflows, and observability.

What Is an AI Agent?

An AI agent is a software component that uses a model to interpret a goal, decide what to do next, and produce an outcome. The model provides language understanding and generation; the application provides instructions, boundaries, state, tools, and the code that actually performs side effects.

A useful mental model is:

flowchart LR I((User goal)) --> P[Agent instructions] P --> D{Model decides next step} D -->|Answer| O([Response]) D -->|Need information| K[(Context and memory)] D -->|Need action| T[[Tool or API]] K --> D T --> R[Tool result] R --> D classDef input fill:#fef3c7,stroke:#d97706,stroke-width:2px,color:#78350f classDef instruction fill:#dbeafe,stroke:#2563eb,stroke-width:2px,color:#1e3a8a classDef decision fill:#fce7f3,stroke:#db2777,stroke-width:2px,color:#831843 classDef result fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#14532d classDef state fill:#e0e7ff,stroke:#4f46e5,stroke-width:2px,color:#312e81 classDef tool fill:#cffafe,stroke:#0891b2,stroke-width:2px,color:#164e63 class I input class P instruction class D decision class O,R result class K state class T tool

The agent loop is not magic. It is a controlled cycle:

  1. Receive a goal or message.
  2. Combine it with instructions and available context.
  3. Ask the model for the next response or action.
  4. Execute an approved tool when one is selected.
  5. Return the response or continue with the tool result.

This article stops before step 4 by design. The sample has no tools, so readers can first understand the agent boundary without confusing model output with application actions.

What Is Microsoft Agent Framework?

Microsoft Agent Framework is a set of .NET and Python abstractions for building AI agents and workflows. It combines model clients with agent execution, sessions, tools, context providers, middleware, and orchestration features.

The framework does not replace an LLM provider. It gives application code a consistent way to create and invoke agents while keeping provider-specific connection code at the edge.

Here is the concept map for this series:

Concept Meaning
IChatClient Common model-client abstraction used to send chat requests to a provider
AIAgent Framework abstraction that receives input and produces agent responses
AgentSession Conversation state passed between runs
Function tool Application function that an agent may request through a defined schema
Context provider Component that supplies relevant memory or external context
Workflow Explicit path that connects agents, functions, branches, and results
Agent Harness Opinionated runtime for planning, context management, and long-running tasks

The first article uses only IChatClient and AIAgent. That is enough to build a useful conversational application and gives each later abstraction a clear starting point.

Chat Client Versus Agent

IChatClient is the model connection. It sends messages to the provider and receives completions.

AIAgent is the application-level abstraction built around that connection. It adds instructions, agent execution, sessions, tools, and future capabilities.

In this sample, DeepSeek is the model provider, IChatClient communicates with DeepSeek, AIAgent applies the support instructions and runs the request, and ASP.NET Core exposes the result through HTTP.

Where Each Concept Appears in This Sample

The customer-support example makes the boundaries concrete:

  • The customer prompt is the agent input.
  • The support instructions are the agent’s behavior contract.
  • DeepSeek is the model provider.
  • IChatClient is the provider-neutral connection.
  • AIAgent combines the instructions and client and executes the request.
  • AgentResponse is the framework response returned to the API.
  • The ASP.NET Core endpoint is the host boundary that validates input and serializes output.

There is no tool in Part I. If the customer later asks for an order status, the agent should not invent one. Part II will add a typed function tool that reads order data, and the host will decide which operations are authorized.

What You Will Build

You will build a customer-support assistant that can explain safe troubleshooting steps for a checkout problem. The sample uses DeepSeek’s OpenAI-compatible endpoint, but the application depends on the common IChatClient abstraction rather than DeepSeek-specific APIs.

Real-World Scenario: A Failed Checkout

Imagine a customer who sees a payment error during checkout. A useful support assistant should understand the customer’s plain-language description, follow a support policy, and produce a clear response. It should not claim to have checked an order or changed a payment because this first version has no tool that can perform those actions.

The customer sends:

You:

Customer Prompt
I tried to pay for my order twice. Both attempts failed, but I can see a pending charge. What should I do?

The agent instructions establish its responsibility:

Agent Instructions
You are a customer support assistant.
Give safe, clear, concise troubleshooting steps.
Do not claim to access orders, payments, or customer accounts.
Escalate possible duplicate charges to the payment-support team.

The expected response is guidance such as checking the bank statement, avoiding repeated payment attempts, and contacting support with the order information. The exact wording can vary because a model generates it, but the response should follow the instructions and avoid inventing access to systems.

This is a good first agent scenario because it combines natural-language understanding with a policy-driven response:

flowchart LR C((Customer reports payment problem)) --> E[Agent receives plain-language message] E --> I[Agent applies support instructions] I --> A([Agent explains safe next steps]) A --> H[Host returns response to customer] H --> N[Later: add authorized order and payment tools] classDef customer fill:#fef3c7,stroke:#d97706,stroke-width:2px,color:#78350f classDef agent fill:#dbeafe,stroke:#2563eb,stroke-width:2px,color:#1e3a8a classDef response fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#14532d classDef tool fill:#cffafe,stroke:#0891b2,stroke-width:2px,color:#164e63 class C customer class E,I agent class A,H response class N tool

Part I implements the safe-response path. Later articles add authorized tools and MCP. This progression shows an important design rule: the model can suggest an action, but application code must own authorization and side effects.

The finished request flow is:

flowchart LR U((Support client)) -->|JSON prompt| API[ASP.NET Core endpoint] API --> R[AgentRuntime] subgraph FRAMEWORK[Microsoft Agent Framework] A[AIAgent] S[(Optional AgentSession)] A -. future .-> S end subgraph PROVIDER[Model provider boundary] C[[IChatClient]] M[(DeepSeek model)] C --> M M --> C end R --> A A --> C C --> A A --> R --> API --> U classDef client fill:#fef3c7,stroke:#d97706,stroke-width:2px,color:#78350f classDef host fill:#ccfbf1,stroke:#0f766e,stroke-width:2px,color:#134e4a classDef runtime fill:#dbeafe,stroke:#2563eb,stroke-width:2px,color:#1e3a8a classDef state fill:#e0e7ff,stroke:#4f46e5,stroke-width:2px,color:#312e81 classDef provider fill:#cffafe,stroke:#0891b2,stroke-width:2px,color:#164e63 classDef model fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#14532d class U client class API host class R,A runtime class S state class C provider class M model style FRAMEWORK fill:#eff6ff,stroke:#60a5fa,stroke-width:2px,color:#1e3a8a style PROVIDER fill:#ecfeff,stroke:#22d3ee,stroke-width:2px,color:#164e63

The first version intentionally does one thing: receive a prompt and return an answer. This small boundary makes it easier to understand what the framework owns and what the host application still owns.

Agent Framework Building Blocks in Detail

Model client

IChatClient is the model-facing abstraction. It sends chat messages to a provider and returns model output. The provider can be OpenAI-compatible, Azure OpenAI, Microsoft Foundry, or another supported service.

The model client does not automatically make an application an agent. It is the connection to the model.

AIAgent

AIAgent is the application-facing agent abstraction. It carries instructions and exposes operations such as RunAsync. The agent can later coordinate tools, sessions, middleware, and other framework features.

AgentSession

An AgentSession represents conversation state. For this introductory sample, each API request creates a fresh session so the behavior stays single-turn. Reusing one session across calls is the next step for multi-turn conversations.

For example, a later version can keep the same session for two turns:

1
2
3
4
5
6
AgentSession session = await agent.CreateSessionAsync();

await agent.RunAsync("My checkout failed after payment.", session);
AgentResponse response = await agent.RunAsync(
  "Summarize the issue and tell me when to contact support.",
  session);

The second call can use the first call’s conversation context because both calls share session. A session is not an authorization mechanism; a hosted application must still verify that the caller owns it before resuming it.

Messages and Responses

The input prompt is converted into a model request together with the agent’s instructions and any available context. The resulting AgentResponse contains the generated response text and can also carry framework-specific response information as capabilities are added.

The application should treat model text as untrusted output. Validate structured values, constrain tool arguments, and apply authorization in application code before performing side effects.

Tools and Context

Tools let an agent request deterministic application behavior, such as looking up an order or creating a support ticket. Context providers supply relevant information from memory or another source. Neither should be confused with model knowledge:

  • A tool executes code under application control.
  • A context provider selects information to add to the model request.
  • The model decides whether the available information helps answer the request.

Part I introduces these concepts only. Part II implements them with function tools, MCP, memory, and RAG.

Workflows and Harness

An agent is useful for open-ended decisions. A workflow is better when the application needs an explicit sequence, branch, or parallel path. An Agent Harness adds an opinionated runtime for long, multi-step tasks, planning, context management, and related controls.

Those features solve different problems from the basic AIAgent invocation shown here. Keeping them separate helps us choose the smallest abstraction that meets a requirement.

Host application

ASP.NET Core owns HTTP, configuration, dependency injection, authentication, and deployment. Microsoft Agent Framework supplies the agent behavior; it does not replace the rest of the application architecture.

Create the Agent

The agent is created once when the API starts. The model provider is configured behind IChatClient, then adapted to AIAgent with AsAIAgent:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
using System.ClientModel;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;

IChatClient chatClient = new OpenAIClient(
    new ApiKeyCredential(apiKey),
    new OpenAIClientOptions { Endpoint = new Uri(endpoint) })
    .GetChatClient(model)
    .AsIChatClient();

AIAgent agent = chatClient.AsAIAgent(
    instructions: "You are a customer support assistant. " +
                  "Give safe, clear, concise troubleshooting steps.");

AgentResponse response = await agent.RunAsync(
    "A customer cannot complete checkout after a card payment.");

Console.WriteLine(response.Text);

AsAIAgent keeps the agent independent from the provider-specific client. Replacing the endpoint or model should not change the runtime code.

The Sample Application

The sample has two application projects:

  • AgentGetStarted contains AgentRuntime, which creates and invokes the agent.
  • AgentGetStarted.Api contains the HTTP host and endpoint.

The runtime receives an IAgentRunner abstraction. The production runner calls Microsoft Agent Framework; unit tests provide a fake runner. This keeps the tests focused on session-independent application behavior instead of model output.

The endpoint accepts a prompt:

1
2
3
4
5
6
POST /api/agent/chat
Content-Type: application/json

{
  "prompt": "A customer cannot complete checkout after a card payment."
}

It returns:

1
2
3
{
  "response": "..."
}

The API validates the prompt before invoking the agent. Authentication, rate limiting, request limits, and provider error handling remain host-application responsibilities.

Configure and Run

From the sample directory:

1
2
$env:DS_KEY = "<your-deepseek-api-key>"
dotnet run --project AgentGetStarted.Api

The sample reads these settings:

  • DS_KEY: API key for the model provider.
  • DeepSeek:Endpoint: defaults to https://api.deepseek.com.
  • DeepSeek:Model: defaults to deepseek-v4-flash.

Send a request:

1
2
3
curl -X POST http://localhost:5000/api/agent/chat \
  -H "Content-Type: application/json" \
  -d '{"prompt":"I tried to pay for my order twice. Both attempts failed, but I can see a pending charge. What should I do?"}'

The model response is nondeterministic. Tests should verify application contracts such as status codes and non-empty output, while exact wording belongs in carefully designed evaluation tests rather than ordinary unit tests.

What This Sample Does Not Do Yet

This first application is deliberately narrow:

  • It is single-turn; it does not preserve an AgentSession between HTTP requests.
  • It has no function tools, so the model cannot change application state.
  • It does not retrieve documents or use long-term memory.
  • It does not plan a multi-step task or coordinate multiple agents.
  • It does not add Harness behavior, approvals, workflows, or telemetry.

These omissions are useful boundaries. An agent should not be given autonomy or tools before the application has a clear contract for each capability.

Agent or Ordinary Code?

Use an agent when the task is open-ended, conversational, or requires the model to interpret natural language. Use ordinary application code when the process is deterministic and can be expressed as a function.

For example, calculating an invoice total should be normal code. Explaining a failed checkout in customer-friendly language can be an agent task. In later posts, the agent can call a deterministic order lookup function while the host controls authorization and side effects.

Testing Strategy

The sample includes two test layers:

  • Unit tests use a fake IAgentRunner to verify that prompts reach the runtime and responses are returned.
  • Integration tests host the real ASP.NET Core endpoint in memory. A provider-backed test runs only when DS_KEY is configured.

The live test is intentionally optional because it consumes provider quota and depends on network access. It checks that the real agent can be created and return non-empty output; it does not assert an exact model response.

Run tests:

1
dotnet test agent-get-started-sample.slnx

Run the optional live-provider test:

1
2
$env:DS_KEY = "<your-deepseek-api-key>"
dotnet test tests/AgentGetStarted.IntegrationTests/AgentGetStarted.IntegrationTests.csproj

Next Steps

The next article adds function tools, MCP, memory, and RAG. After that, Agent Harness will provide planning, context management, and approval-oriented runtime behavior. The final article will connect workflows, multi-agent systems, and observability.

Conclusion

Microsoft Agent Framework starts with a simple model: configure an IChatClient, adapt it to an AIAgent, and invoke it with RunAsync. ASP.NET Core remains responsible for hosting concerns, while the framework provides a consistent agent abstraction that can grow with the application.

Starting with this small boundary gives every later feature a clear place: sessions for conversation state, tools for actions, context providers for knowledge, Harness for long-running execution, workflows for explicit orchestration, and observability for operating the result.

You can find the sample code in this repository:

Reference

Built with Hugo
Theme Stack designed by Jimmy