Skip to main content

AI Agents in .NET: Build Intelligent AI Agents with C# and Microsoft Agent Framework

AI Agents in .NET: Tools, RAG, MCP and Workflows

A practical guide to AI agent architecture for .NET developers, covering tool calling, RAG, MCP, workflows, security, testing, observability, and production design.

If you write software for a living, you've probably already used an AI model. The more interesting question is what happens when the model needs to do something instead of simply answer something.

Maybe it needs to search a knowledge base, inspect an order, call an API, create a support ticket, retrieve information from a database, request approval, or determine which step should happen next.

That's where AI agents become useful.

An agent is not simply a chatbot with a new label. An agent combines a model with instructions, context, tools, state, and an execution loop so that an application can work toward an objective.

For .NET developers, this is particularly interesting because much of the difficult work still looks like familiar software engineering: dependency injection, interfaces, authorization, persistence, retries, logging, testing, observability, rate limiting, cost controls, and failure handling.

This guide builds the mental model first and then moves into tools, memory, RAG, MCP, .NET architecture, workflows, testing, security, and production concerns.

The main idea: a production AI agent should not replace your application architecture. It should operate inside it, with clearly defined tools, permissions, limits, and business rules.

What You'll Learn

  • What an AI agent actually is.
  • How an agent differs from a normal LLM call.
  • The difference between tool calling, agents, and workflows.
  • How tools, memory, RAG, and MCP fit into an agent architecture.
  • How AI agents can be integrated into .NET applications.
  • Where Microsoft.Extensions.AI fits into the architecture.
  • When a deterministic workflow is better than an autonomous agent.
  • Why authorization and validation become critical when an agent can take action.
  • How to test and observe an agent in production.
  • Why starting with one bounded agent is usually better than immediately building a multi-agent system.

What Is an AI Agent?

A useful way to think about an AI agent is as a controlled decision-and-action loop.

Goal
  ↓
Instructions + context
  ↓
Model decides the next useful step
  ↓
Tool / retrieval / application action
  ↓
Application observes the result
  ↓
Model decides whether another step is required
  ↓
Final response or completed action

The important part is the loop.

A basic LLM request usually looks like:

Input → Model → Output

An agent can instead operate more like:

Goal
  ↓
Decide
  ↓
Act
  ↓
Observe
  ↓
Decide again
  ↓
Act again if necessary
  ↓
Finish

This does not mean that the model has unlimited autonomy. In a production application, the surrounding software should define exactly what the agent can access, which tools it can invoke, which data it can see, how long it can run, and which operations require approval.

LLM Call vs Tool Calling vs Agent vs Workflow

These concepts are often used interchangeably, but they represent different levels of application behavior.

Pattern Typical behavior Who controls execution?
LLM call Generate, summarize, classify, or transform content. The application controls the steps.
Tool calling The model requests a specific application capability. The application validates and executes the tool.
Agent The model can select among bounded capabilities and help determine the next step. The application defines the boundaries; the model helps choose the path.
Workflow Known steps execute in an explicit sequence or graph. The application controls the execution path.

This distinction matters because not every multi-step AI feature needs an agent.

If your process is already known, deterministic application code or a workflow is usually easier to test, secure, debug, and operate.

A Simple Agent Example

Imagine a customer asks:

Where is my order?

A basic agent might perform the following sequence:

  1. Read the customer's request.
  2. Determine that order information is required.
  3. Request the GetOrderStatus tool.
  4. Application code validates the order identifier.
  5. The application checks authorization.
  6. The application calls the order service.
  7. The tool result is returned to the model.
  8. The model determines whether additional information is required.
  9. The agent generates the final response.

The model does not directly become part of your database layer.

It should not be able to bypass authorization, invent a successful database result, or execute arbitrary application code.

Your application owns the boundary.

Tools Are What Give Agents Capabilities

A language model is good at reasoning over information and generating structured requests or natural-language responses. A tool gives the model access to a capability that exists outside the model.

Typical tools could include:

  • GetOrderStatus
  • SearchPolicyDocuments
  • CreateSupportTicket
  • GetCustomerProfile
  • SearchKnowledgeBase
  • GetInvoice
  • CheckInventory

The model can request a tool call. Your application should decide whether that request is valid, authorized, safe, and appropriate for the current user and agent.

Design Tools Like APIs

One useful engineering principle is to design agent tools the same way you would design normal APIs.

A good tool should have:

  • A narrow responsibility.
  • Clearly defined inputs.
  • Predictable outputs.
  • Input validation.
  • Authorization checks.
  • Reasonable timeout behavior.
  • Useful error information.
  • Logging and tracing.
public sealed class OrderTools
{
    private readonly IOrderService _orders;

    public OrderTools(IOrderService orders)
    {
        _orders = orders;
    }

    public async Task<OrderStatus> GetOrderStatus(
        string orderId,
        CancellationToken cancellationToken)
    {
        if (string.IsNullOrWhiteSpace(orderId))
        {
            throw new ArgumentException(
                "Order ID is required.",
                nameof(orderId));
        }

        // Authorization should happen here or
        // inside the application service.

        return await _orders.GetStatusAsync(
            orderId,
            cancellationToken);
    }
}

The important point is that the application does not trust the model simply because the model requested the operation.

Why Tool Boundaries Matter

Consider a tool named:

DeleteCustomerAccount()

Giving an agent access to that capability is very different from giving it access to:

GetCustomerProfile()

The first operation may be destructive and irreversible. The second is primarily a read operation.

Tool design should therefore reflect the risk of the operation.

For high-impact actions, your application may require:

  • Explicit user confirmation.
  • Additional authorization.
  • Human approval.
  • Transaction limits.
  • Audit logging.
  • Idempotency protection.
  • A reversible operation where possible.

Memory and Context

"Memory" can mean several different things in an AI application. Keeping these concepts separate makes the architecture much easier to understand.

Context type Purpose
Conversation history Keeps the current conversation coherent.
Application state Stores durable business information.
RAG context Retrieves relevant knowledge when required.
Long-term memory Stores selected information that should persist across interactions.

Do not put everything into "memory".

Business state should normally remain in your application's database or state store. Retrieved documents should remain traceable to their original sources. Conversation history should have explicit retention and privacy rules.

Agents and RAG Solve Different Problems

RAG and agents are often discussed together, but they solve different problems.

RAG asks: "What information should the model see?"
An agent asks: "What should the application do next?"

For example, an employee might ask:

How many weeks of parental leave am I entitled to,
and how do I submit the request?

The agent may need two different capabilities:

  1. Retrieve the relevant HR policy using RAG.
  2. Call an application service that explains or starts the request process.

The overall flow could look like:

User question
      ↓
Agent determines information is required
      ↓
RAG / search tool
      ↓
Relevant policy content
      ↓
Agent receives the context
      ↓
Agent determines next step
      ↓
Application tool or final answer

This separation is important. Retrieval provides knowledge. Tools provide capabilities. The agent coordinates the available capabilities within the boundaries defined by the application.

Where MCP Fits

Model Context Protocol (MCP) is a protocol for connecting AI applications with external tools, resources, and data sources through a standardized interface.

Conceptually, an agent application can act as an MCP client and communicate with an MCP server that exposes capabilities.

AI application
      ↓
   MCP client
      ↓
   MCP server
    ↙     ↘
 tools    resources

The important architectural idea is that MCP is an integration mechanism. It does not replace your application's authorization model, business rules, or security controls.

If an MCP server exposes a sensitive capability, the application still needs to decide whether the current user and agent are allowed to use it.

The .NET AI Agent Stack

For a .NET developer, it helps to separate the architecture into layers rather than treating "AI agent" as a single package.

Layer Responsibility
Application ASP.NET Core APIs, workers, services, business logic, authentication, and authorization.
Agent layer Agent execution, sessions, tool selection, orchestration, and workflows.
AI abstractions Chat models, embeddings, and provider-neutral AI capabilities.
Knowledge RAG, vector search, databases, and enterprise data.
Operations Logging, tracing, metrics, resilience, evaluation, rate limits, and cost controls.

Microsoft.Extensions.AI provides provider-neutral abstractions for working with AI services in .NET applications. Higher-level agent and workflow frameworks can then build on top of these kinds of primitives.

The exact packages and APIs available in the .NET AI ecosystem change over time, so production applications should verify the current documentation and package versions before implementation.

A Simple Agent Architecture in .NET

Before introducing a complete agent framework, it is useful to understand the architecture using familiar .NET concepts.

public sealed class SupportAgent
{
    private readonly IChatClient _chatClient;
    private readonly IOrderService _orders;

    public SupportAgent(
        IChatClient chatClient,
        IOrderService orders)
    {
        _chatClient = chatClient;
        _orders = orders;
    }

    public async Task<string> RunAsync(
        string request,
        CancellationToken cancellationToken)
    {
        // 1. Build instructions and context.
        // 2. Give the model access to approved tools.
        // 3. Validate requested tool calls.
        // 4. Execute application services.
        // 5. Return tool results to the model.
        // 6. Continue until the task is complete.
        // 7. Return the final response.

        return "...";
    }
}

This example is intentionally simplified. A production agent needs considerably more around the loop: tool schemas, validation, authorization, limits, error handling, tracing, cancellation, and evaluation.

Why Dependency Injection Still Matters

One advantage of implementing agents in .NET is that the surrounding architecture can continue using familiar dependency-injection patterns.

builder.Services.AddScoped<IOrderService, OrderService>();
builder.Services.AddScoped<OrderTools>();
builder.Services.AddScoped<SupportAgent>();

The agent should not contain database implementation details. It should depend on application services and narrowly scoped capabilities.

This makes the system easier to test because tools and business services can be mocked independently from the model.

When Should You Use an Agent?

An agent can be a good fit when several of the following conditions are true:

  • The objective is clear but the exact sequence of steps is not.
  • The system needs to select between multiple tools.
  • The next step depends on the result of a previous step.
  • The application needs to retrieve information dynamically.
  • The user may change direction during the interaction.
  • The task requires some degree of model-driven decision making.

Examples include:

  • Enterprise knowledge assistants.
  • Support agents.
  • Research assistants.
  • Controlled operations assistants.
  • Internal developer assistants.
  • Applications that coordinate multiple business capabilities.

When Should You Not Use an Agent?

This is arguably the more important question.

Suppose your process is always:

Validate input
    ↓
Call service A
    ↓
Call service B
    ↓
Save result
    ↓
Return response

There is little reason to ask a model to decide which step should happen next.

A normal application service or deterministic workflow is likely to be easier to test, cheaper to operate, more predictable, and easier to secure.

Use AI to handle uncertainty. Use deterministic code for decisions that your application already knows how to make.

Agents vs Workflows

Consider an employee onboarding process:

Create employee record
        ↓
Create account
        ↓
Assign standard permissions
        ↓
Send welcome email
        ↓
Create onboarding tasks

If this process is deterministic, a workflow is a better fit.

Now imagine a research assistant where the user asks:

Compare these three vendors and tell me
which one best fits our requirements.

The system may need to decide:

  • Which information to retrieve.
  • Which sources to inspect.
  • Whether more research is necessary.
  • Which comparison criteria matter.
  • When it has enough information to finish.

That is a stronger candidate for agent-style behavior.

Do You Need Multiple Agents?

Usually, start with one.

A multi-agent architecture introduces additional complexity:

  • More model calls.
  • More state management.
  • More latency.
  • More token usage.
  • More failure modes.
  • More complicated debugging.
  • More security boundaries.

A simple architecture such as this is often enough:

User
  ↓
Agent
  ├── Search tool
  ├── Database tool
  ├── Business API
  └── Human approval
  ↓
Result

Only split the system into multiple agents when specialization solves a real architectural problem.

Agent Security Is Application Security

Once an agent can take action, the security requirements become more important.

Authorize Every Sensitive Operation

The model should never be your authorization system.

If the user is not allowed to access an order, the agent should not be able to retrieve it simply because the model requested the correct tool.

Validate Model-Generated Arguments

Tool arguments generated by a model should be treated as untrusted input.

Validate identifiers, ranges, formats, permissions, resource ownership, and business rules just as you would validate input from an HTTP request.

Limit Tool Access

An agent should receive only the tools it actually needs.

A support agent that only needs to read order status should not automatically have access to account deletion, payment processing, or administrative tools.

Treat Retrieved Content as Untrusted

Documents retrieved through RAG can contain instructions. Those instructions should not automatically become instructions for the agent.

This is especially important when documents come from user-generated or externally supplied sources. Retrieval should provide information to the model, not silently grant new permissions to the model.

Protect Secrets

API keys, database credentials, access tokens, and other secrets should remain in appropriate application configuration and secret-management systems.

They should not be placed into prompts or exposed through tool results.

Require Approval for High-Impact Actions

Actions such as deleting records, transferring money, changing permissions, or sending sensitive communications may require explicit user or human approval.

Put Hard Limits Around Agents

Never design an agent as:

Keep going until you figure it out.

Production systems should have explicit boundaries:

  • Maximum tool calls.
  • Maximum model calls.
  • Maximum execution time.
  • Token or budget limits.
  • Allowed tools.
  • Maximum tool argument size.
  • Approval requirements.
  • Fallback behavior.
public sealed class AgentLimits
{
    public int MaxToolCalls { get; init; } = 8;

    public int MaxModelCalls { get; init; } = 12;

    public TimeSpan Timeout { get; init; }
        = TimeSpan.FromSeconds(60);
}

The exact values depend on the application. The important part is that the limits are explicit and enforced by application code.

Observability Becomes More Important

A normal API request may have one obvious execution path. An agent request can contain several model calls, retrieval operations, tool invocations, retries, and intermediate decisions.

For an important agent run, you may want to know:

  • Which model and provider were used?
  • How many model calls occurred?
  • How many tokens were consumed?
  • Which tools were requested?
  • Which tools actually executed?
  • How long did each step take?
  • Did a tool fail?
  • Did the request hit a timeout?
  • Did the agent reach a configured limit?
  • What was the final application outcome?

Familiar .NET observability practices remain valuable when AI is introduced. Structured logging, distributed tracing, metrics, correlation IDs, and centralized error handling can help reconstruct an agent run without relying on the final response alone.

Cost Is Part of the Architecture

An agent can potentially make several model calls for one user request.

User request
    ↓
Model call
    ↓
Tool call
    ↓
Model call
    ↓
RAG search
    ↓
Model call
    ↓
Final response

A feature that looks inexpensive when measured as one model call can become significantly more expensive when the complete execution loop is considered.

Track at least:

  • Model calls per successful task.
  • Input and output tokens.
  • Embedding usage where applicable.
  • Tool execution time.
  • Average cost per completed task.
  • Failed and abandoned runs.

Cost limits should be part of the design rather than something added after the system has already been deployed.

How Do You Test an AI Agent?

Testing an agent is different from testing a deterministic method because the model may take different valid paths to reach the same result.

Instead of asserting only the exact wording of the final response, test important behaviors:

  • Required tools are available.
  • Unauthorized tools cannot execute.
  • Invalid tool arguments are rejected.
  • Sensitive actions require approval.
  • The agent stops at configured limits.
  • Tool failures produce safe behavior.
  • Retrieved information is used appropriately.
  • The final response does not claim an action succeeded when the tool failed.

For unit tests, mock the model and tool boundaries. For broader evaluation, maintain a representative test dataset containing realistic user requests and expected outcomes.

Separate Retrieval Testing From Answer Testing

This is especially important for RAG-based agents.

Suppose an agent answers a question incorrectly.

There are at least two different possibilities:

  1. The retrieval system returned the wrong information.
  2. The correct information was retrieved but the model used it incorrectly.

These are different problems and should be evaluated separately.

During development, inspect:

  • Retrieved document IDs.
  • Similarity scores.
  • Source documents.
  • Metadata filters.
  • Final context supplied to the model.

This makes troubleshooting much easier than looking only at the final answer.

Design for Failure, Not Just the Happy Path

Agents depend on several components that can fail independently:

User
  ↓
Agent
  ├── Model provider
  ├── Retrieval system
  ├── Tool A
  ├── Tool B
  └── Database
        ↓
      Result

A production design should define what happens when any of these components becomes unavailable.

  • If the model times out, should the request be retried?
  • If a tool fails, should the agent continue or stop?
  • If retrieval returns no useful documents, should the model answer from general knowledge?
  • If a destructive operation fails after partial execution, how is the state reconciled?
  • If the model repeatedly requests the same tool, when should the loop terminate?

Retries should also be bounded. Blindly retrying every model or tool call can increase latency and cost while making an already failing system worse.

Keep Agent State Separate From Business State

It is tempting to store everything an agent knows inside conversation history. That approach becomes difficult to manage as the application grows.

Separate concerns instead:

  • Conversation state: messages and interaction context.
  • Agent execution state: current run, tool calls, limits, and intermediate results.
  • Business state: orders, users, invoices, permissions, transactions, and other application data.
  • Knowledge: indexed documents and retrieved evidence.

This separation makes data retention, authorization, testing, and auditing much easier to reason about.

A Practical Learning Path for .NET Developers

  1. Start with model calls. Understand prompts, tokens, structured output, and model configuration.
  2. Learn tool calling. Give a model one small, safe application capability.
  3. Add RAG. Allow the application to retrieve private or changing information.
  4. Build a bounded agent loop. Let the model choose between a small number of approved tools.
  5. Add state. Keep conversation history separate from durable business state.
  6. Add authorization and approval. Make the security boundary explicit.
  7. Add observability. Trace model calls, tools, latency, failures, and usage.
  8. Add evaluation. Create a test set before changing models or prompts.
  9. Only then consider multi-agent architectures.

The objective is not to build the most autonomous system possible.

The objective is to build the smallest reliable system that can accomplish the task.

Production AI Agent Checklist

  • Purpose: Is an agent actually required?
  • Tools: Are tools narrow, documented, validated, and observable?
  • Authorization: Can every sensitive operation be independently authorized?
  • Context: Is the agent receiving only the information it needs?
  • Memory: Is durable business state stored outside the model?
  • Limits: Are execution time, model calls, tool calls, and cost bounded?
  • Safety: Can untrusted content influence tool behavior?
  • Observability: Can one agent run be traced from start to finish?
  • Cost: Do you know the average cost of a successful task?
  • Evaluation: Can you measure whether changes improve the system?
  • Fallback: What happens when the model, tool, or retrieval system fails?
  • Human control: Which operations require explicit approval?

Key Takeaways

  • An AI agent is more than an LLM call. It combines a model with instructions, context, tools, state, and an execution loop.
  • Tools create the action boundary. The model can request an operation, but application code should validate and execute it.
  • RAG and agents solve different problems. RAG provides relevant knowledge; agents help determine what to do with available capabilities.
  • Workflows are still important. When the execution path is known, deterministic workflows are often better than autonomous agents.
  • Start with one agent. Add multi-agent orchestration only when there is a clear architectural reason.
  • Security remains an application responsibility. Authorization, validation, secrets, limits, and approvals should not be delegated to the model.
  • Production agents need observability and evaluation. You need to understand not only the final response but also how the system reached it.

Frequently Asked Questions

What is an AI agent?

An AI agent is an application that uses a model together with instructions, context, tools, and state to work toward an objective, often through multiple steps.

Is an AI agent the same as a chatbot?

No. A chatbot may simply generate conversational responses. An agent can additionally retrieve information, select tools, maintain state, and perform bounded application actions.

Do .NET developers need a special programming language to build AI agents?

No. C# and the .NET ecosystem can be used to build AI-powered applications and agents. AI libraries provide abstractions and orchestration capabilities while your normal .NET application remains responsible for business logic and security.

Is RAG required for an AI agent?

No. An agent can work with tools and application state without RAG. RAG becomes useful when the agent needs relevant information from private, changing, or large knowledge sources.

Should every AI application become an agent?

No. A direct model call or deterministic workflow is often simpler when the application already knows what steps should happen.

Do agents need multiple AI models?

No. An agent can use a single model with multiple tools. Multiple models or multiple agents are architectural choices rather than requirements.

How do I keep an AI agent from performing unauthorized actions?

Do not rely on the model to enforce authorization. Put authorization, validation, resource ownership checks, approval requirements, and tool restrictions in application code around the agent.

How should I debug an agent that gives incorrect answers?

Separate retrieval, tool execution, and generation when debugging. Inspect which tools were selected, which data was retrieved, what context reached the model, and whether the underlying application operation succeeded before evaluating the final response.

Final Thoughts

AI agents are best understood as an application architecture rather than a new type of chatbot.

The model provides reasoning and language capabilities. Tools provide access to application capabilities. RAG provides relevant knowledge. State provides continuity. Your application provides the security, business rules, permissions, limits, and reliability mechanisms around all of them.

That distinction is important because it prevents a common mistake: giving the model too much responsibility.

A reliable agent should not replace the architecture of your application. It should operate inside that architecture.

For .NET developers, that means many of the engineering principles you already know still apply:

Dependency Injection
        +
Application Services
        +
Authorization
        +
Validation
        +
Observability
        +
Resilience
        +
AI Model
        +
Tools
        +
RAG
        =
Controlled AI Agent

The most useful question is therefore not:

"How autonomous can I make my agent?"

A better question is:

"What is the smallest amount of AI-driven decision making that solves this problem reliably?"

That mindset leads to simpler architectures, clearer security boundaries, easier testing, and AI systems that are much easier to operate in production.

Further Reading

Continue exploring related .NET and AI engineering topics:

  • RAG and vector search in .NET
  • Calling multiple AI providers from .NET
  • Understanding embeddings for RAG applications

For the latest implementation details, always verify the current official documentation and package versions before using a specific AI framework, provider API, model, protocol, or .NET package in production.

Comments

Popular posts from this blog

AI Engineering with .NET: A Practical Guide to Building AI Applications

AI Engineering with .NET: A Practical Guide If you write C# for a living, there is a good chance someone has already asked you to "add AI" to an application. Maybe the requirement is document summarization, answering questions over company policies, classifying support tickets, generating content, extracting structured data, or building a chatbot. The interesting part is that most of these problems do not require you to become a machine-learning researcher. They require you to understand how AI models fit into a software system: how to call them, structure their output, provide application context, handle failures, control cost, evaluate responses, and keep the system observable in production. That is where AI engineering with .NET becomes useful. If you already work with C#, ASP.NET Core, dependency injection, APIs, databases, background services, logging, and distributed systems, you already have much of the foundation requir...

Calling LLMs from .NET: IChatClient in Practice

IChatClient in .NET: A Practical Guide for Building AI Applications If you are building AI features in a .NET application, getting your first response from a large language model is usually the easy part. The harder problem appears when the application grows. Provider-specific SDKs start appearing inside application services, prompts become coupled to infrastructure code, conversation history needs to be managed, and concerns such as logging, telemetry, caching, rate limiting, security, and cost tracking begin spreading across the codebase. This is where IChatClient becomes useful. IChatClient is part of the Microsoft.Extensions.AI ecosystem. It provides a common .NET abstraction for interacting with chat-capable AI services while allowing the underlying implementation to come from a provider SDK or another compatible implementation. The architectural idea is simple: your application can depend on an AI capabilit...

RAG with .NET: A Practical Guide to Retrieval-Augmented Generation

Retrieval-Augmented Generation in .NET: A Practical RAG Guide If you're building AI applications with .NET, there is a point where simply sending a prompt to an LLM stops being enough. Generating a summary is easy. Generating an answer from your company's documents, policies, product information, support knowledge, database records, or internal documentation is a different problem. The model does not automatically know your private data. And even when information is available somewhere in a model's training data, you generally do not want an application to depend on the model remembering it correctly or knowing the latest version of a business policy. This is where Retrieval-Augmented Generation (RAG) becomes useful. Instead of asking an LLM to answer entirely from its general knowledge, your application first retrieves relevant information from an external knowledge source and provides that information to the model as context. RAG = retrieve releva...