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:
The agent loop is not magic. It is a controlled cycle:
- Receive a goal or message.
- Combine it with instructions and available context.
- Ask the model for the next response or action.
- Execute an approved tool when one is selected.
- 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.
IChatClientis the provider-neutral connection.AIAgentcombines the instructions and client and executes the request.AgentResponseis 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:
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:
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:
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:
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:
|
|
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:
|
|
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:
AgentGetStartedcontainsAgentRuntime, which creates and invokes the agent.AgentGetStarted.Apicontains 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:
|
|
It returns:
|
|
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:
|
|
The sample reads these settings:
DS_KEY: API key for the model provider.DeepSeek:Endpoint: defaults tohttps://api.deepseek.com.DeepSeek:Model: defaults todeepseek-v4-flash.
Send a request:
|
|
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
AgentSessionbetween 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
IAgentRunnerto 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_KEYis 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:
|
|
Run the optional live-provider test:
|
|
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.