DOTNET EXPERT BLOG

Build an AI Agent in C# with Microsoft Agent Framework | Day 10

10/5/2026 12:14:20 AM Noor All Safaet Loading... 0

Our C# AI application has gained several capabilities.

It can:

Chat
Remember conversations
Stream responses
Return structured output
Call C# functions
Query business data
Retrieve document knowledge with RAG

But so far, we have mostly decided how those capabilities are used.

What happens when a user gives the application a broader goal?

For example:

Check the stock of Wireless Mouse.

If stock is low, check our inventory policy
and tell me what action should be taken.

This request may require more than one capability.

The application may need to:

Understand the goal
      ↓
Check product stock
      ↓
Observe the result
      ↓
Retrieve the relevant policy
      ↓
Combine the information
      ↓
Produce a final answer

This is where an AI agent becomes useful.

In Day 10, we will build our first agent in C# using Microsoft Agent Framework.

Our architecture will evolve into:

User Goal
   ↓
AI Agent
   ↓
Choose Capability
   ├── Inventory Tool
   └── Knowledge Search
   ↓
Execute Tool
   ↓
Observe Result
   ↓
Continue if Needed
   ↓
Final Answer

We will cover:

  • what an AI agent is

  • agent vs chatbot

  • agent vs function calling

  • Microsoft Agent Framework

  • AIAgent

  • ChatClientAgent

  • converting existing C# methods into tools

  • combining database and RAG capabilities

  • multi-step agent execution

  • agent instructions

  • tool design

  • authorization and approval boundaries

  • limiting agent autonomy

  • production considerations

By the end, the capabilities we built during Days 1–9 will begin working together as one controlled AI agent.

Previously in This Series

We have been building one practical AI application with C# and .NET.

Day 1: Build Your First AI Application
Connected C# to an AI model.

Day 2: Understanding IChatClient
Introduced a provider-independent chat abstraction.

Day 3: Build a Reusable AI Chat Service
Added ASP.NET Core and dependency injection.

Day 4: Conversation Memory
Added multi-turn conversations.

Day 5: Streaming
Streamed generated responses.

Day 6: Structured Output
Converted AI output into strongly typed C# results.

Day 7: Function Calling
Allowed the model to request approved C# functions.

Day 8: AI + Database
Connected tools to real business data through EF Core.

Day 9: Retrieval-Augmented Generation
Added document retrieval using embeddings and vector search.

Today we begin coordinating those capabilities with an agent.

What Is an AI Agent?

A normal AI request often looks like:

Prompt
  ↓
Model
  ↓
Response

An agent can operate in a loop.

Conceptually:

Goal
 ↓
Model
 ↓
Choose Action
 ↓
Execute Tool
 ↓
Observe Result
 ↓
Choose Next Action
 ↓
...
 ↓
Final Answer

The important difference is not simply that the model can call a function.

An agent can use available capabilities while working toward a goal.

For example:

User:
Check Wireless Mouse inventory and tell me
what our policy recommends if stock is low.

The agent may determine:

Step 1
Need current inventory.

→ get_product_stock

Step 2
Stock = 4
ReorderLevel = 10

Need relevant inventory policy.

→ search_knowledge

Step 3
Policy says low-stock products should
be reviewed for replenishment.

Step 4
Generate final response.

The application still controls which capabilities exist.

The model decides when an available capability is useful.

Agent vs Chatbot

A chatbot primarily focuses on conversation:

User
 ↓
Model
 ↓
Answer

An agent can use external capabilities:

User Goal
   ↓
Agent
   ├── Tool
   ├── Database
   ├── API
   └── Knowledge Retrieval
   ↓
Result

This means an agent can do more than generate language.

It can interact with application capabilities.

But that does not mean every chatbot should become an agent.

If your application only needs:

Question
 ↓
Answer

a normal IChatClient may be simpler.

Use an agent when the problem genuinely requires capability selection or multi-step execution.

Agent vs Function Calling

In Day 7, we learned function calling.

That remains important.

An agent does not replace tools. It coordinates them.

Function calling gives the model capabilities such as:

get_product_stock
search_knowledge
get_order_status

Agent behavior allows those capabilities to participate in a broader execution loop.

Think of it like this:

Function Tool
=
One Capability

Agent
=
Model + Instructions + Tools + Execution Loop

The agent should still operate only through capabilities the application intentionally exposes.

Microsoft Agent Framework

Microsoft Agent Framework provides abstractions and infrastructure for building agents and agent workflows in .NET.

One important abstraction is:

AIAgent

Application code can work against this common agent abstraction.

For an application-owned agent backed by an IChatClient, we can use:

ChatClientAgent

Conceptually:

AIAgent
   ↑
ChatClientAgent
   ↓
IChatClient
   ↓
Model Provider

This fits naturally with the architecture we have already built because our application already uses IChatClient.

Step 1: Install Microsoft Agent Framework

Add the agent package:

dotnet add package Microsoft.Agents.AI --prerelease

Depending on your model provider, additional provider packages may also be required.

Our goal in this tutorial is to keep the agent architecture centered around the IChatClient we already configured.

Step 2: Start with a Simple Agent

Suppose we already have:

IChatClient chatClient

from our previous application.

We can create an agent:

using Microsoft.Agents.AI;

AIAgent agent =
    new ChatClientAgent(
        chatClient,
        instructions:
            """
            You are a business assistant.

            Help users understand inventory
            and internal business knowledge.

            Use available tools when current
            business information is required.

            Never invent inventory values,
            policies, or operational facts.
            """);

Then run it:

AgentResponse response =
    await agent.RunAsync(
        "What can you help me with?");

The agent now has:

Model
+
Instructions

But it does not yet have our application capabilities.

Let's add them.

Step 3: Reuse the Inventory Capability

In Day 7 and Day 8, we built an inventory service.

Our application already knows how to retrieve stock safely:

public interface IInventoryService
{
    Task<ProductStockInfo?>
        GetProductStockAsync(
            string productName,
            CancellationToken cancellationToken = default);
}

The implementation uses EF Core.

The agent does not need to know that.

Create an agent-facing tool:

using System.ComponentModel;

public sealed class InventoryAgentTools
{
    private readonly IInventoryService
        _inventoryService;

    public InventoryAgentTools(
        IInventoryService inventoryService)
    {
        _inventoryService =
            inventoryService;
    }

    [Description(
        "Gets the current stock and reorder level for a product.")]
    public async Task<ProductStockInfo?>
        GetProductStockAsync(
            [Description(
                "The exact product name to look up.")]
            string productName,
            CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrWhiteSpace(productName))
        {
            throw new ArgumentException(
                "Product name is required.",
                nameof(productName));
        }

        return await _inventoryService
            .GetProductStockAsync(
                productName.Trim(),
                cancellationToken);
    }
}

Notice that we are not giving the agent:

DbContext
Connection String
SQL Executor
Database Credentials

We are giving it one business capability:

get_product_stock

That boundary remains important.

Step 4: Turn the C# Method into an Agent Tool

Microsoft Agent Framework works with AIFunction.

We can create one using:

AIFunctionFactory.Create(...)

For example:

using Microsoft.Extensions.AI;

AIFunction getProductStock =
    AIFunctionFactory.Create(
        inventoryTools.GetProductStockAsync,
        name: "get_product_stock",
        description:
            "Gets the current stock and reorder level for a product.");

The agent can now understand that this capability exists.

But our user request may also require document knowledge.

So let's bring Day 9 into the agent.

Step 5: Expose RAG as a Knowledge Tool

In Day 9, we created:

IKnowledgeSearchService

which performs semantic retrieval over our vector store.

We can expose that capability through another controlled tool.

using System.ComponentModel;

public sealed class KnowledgeAgentTools
{
    private readonly IKnowledgeSearchService
        _knowledgeSearchService;

    public KnowledgeAgentTools(
        IKnowledgeSearchService knowledgeSearchService)
    {
        _knowledgeSearchService =
            knowledgeSearchService;
    }

    [Description(
        "Searches internal business documentation and returns relevant knowledge.")]
    public async Task<
        IReadOnlyList<KnowledgeSearchResult>>
        SearchKnowledgeAsync(
            [Description(
                "The question or topic to search for.")]
            string query,
            CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrWhiteSpace(query))
        {
            return [];
        }

        return await _knowledgeSearchService
            .SearchAsync(
                query.Trim(),
                top: 5,
                cancellationToken);
    }
}

Then create another AIFunction:

AIFunction searchKnowledge =
    AIFunctionFactory.Create(
        knowledgeTools.SearchKnowledgeAsync,
        name: "search_knowledge",
        description:
            "Searches approved internal business documentation for relevant information.");

Our agent now has two useful capabilities:

get_product_stock
search_knowledge

One accesses structured business data.

The other accesses unstructured document knowledge.

Step 6: Create the Agent with Tools

Now combine the model, instructions, and tools.

using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;

AIAgent agent =
    new ChatClientAgent(
        chatClient,
        instructions:
            """
            You are an inventory and business
            knowledge assistant.

            Use get_product_stock when current
            inventory information is required.

            Use search_knowledge when company
            policies, manuals, or internal
            documentation are required.

            You may use more than one tool when
            necessary to answer the user's request.

            Never invent stock quantities,
            reorder levels, policies, or other
            business facts.

            If required information cannot be
            found, clearly say so.
            """,
        tools:
        [
            getProductStock,
            searchKnowledge
        ]);

We now have:

ChatClientAgent
      │
      ├── get_product_stock
      │        ↓
      │   InventoryService
      │        ↓
      │      EF Core
      │
      └── search_knowledge
               ↓
          Vector Search
               ↓
         Knowledge Store

The agent chooses which approved capability to use.

Step 7: Give the Agent a Multi-Step Goal

Now send:

AgentResponse response =
    await agent.RunAsync(
        """
        Check the stock of Wireless Mouse.

        If the product is below its reorder level,
        check our inventory policy and explain
        what action should be taken.
        """);

A possible execution path is:

User Goal
   ↓
Agent
   ↓
get_product_stock
   ↓
CurrentStock = 4
ReorderLevel = 10
   ↓
Agent observes:
Stock is below reorder level
   ↓
search_knowledge
   ↓
Relevant inventory policy
   ↓
Agent
   ↓
Final Answer

A possible final answer might be:

Wireless Mouse currently has 4 units in stock,
which is below its reorder level of 10.

According to the retrieved inventory policy,
low-stock products should be reviewed for
replenishment.

A replenishment review should therefore be
started for this product.

The important point is that the model did not invent:

4 units
10-unit reorder level
Inventory policy

Those facts came from controlled application capabilities.

What Makes This Agentic?

Consider the original request:

Check stock.

If it is low,
check the policy.

Then explain what we should do.

The second action depends on the first result.

Conceptually:

Observe
   ↓
Decide
   ↓
Act
   ↓
Observe
   ↓
Decide Again

This is different from blindly executing a fixed list:

Call Tool A
Call Tool B
Return Answer

The model can decide that the second capability is needed based on the information it receives.

That is a basic but useful agent pattern.

Do Not Confuse Agents with Unlimited Autonomy

An agent does not need unrestricted access to your system.

A safer model is:

Agent
  ↓
Approved Capabilities
  ├── Read Inventory
  ├── Search Documentation
  └── Get Order Status

rather than:

Agent
  ↓
Entire Application
Database
File System
Operating System
External Network

The application defines the capability boundary.

The agent operates inside it.

Tool Descriptions Matter

The model uses tool names, descriptions, and parameter descriptions to understand when a capability is appropriate.

Compare:

get_data

with:

get_product_stock

The second is much clearer.

Likewise:

search

is less specific than:

search_knowledge

A good tool should have:

Clear Name
Clear Description
Focused Parameters
Focused Result

Avoid creating one giant tool such as:

DoBusinessOperationAsync(
    string command,
    string data)

Narrow capabilities are easier to understand, validate, authorize, test, and monitor.

Do Not Give the Agent Arbitrary SQL

The rule from Day 8 still applies.

Avoid:

execute_sql

Prefer:

get_product_stock
get_order_status
get_customer_balance

The agent expresses intent through an approved capability.

Your C# application controls the actual query.

The architecture remains:

Agent
 ↓
Business Capability
 ↓
Application Service
 ↓
EF Core
 ↓
Database

not:

Agent
 ↓
Generated SQL
 ↓
Database

Do Not Turn RAG into a Generic Data Escape Hatch

The same principle applies to document retrieval.

Avoid a knowledge tool that ignores:

Tenant
Authorization
Document access
Result limits
Sensitive metadata

The retrieval service should enforce those rules before content reaches the model.

The agent should see only information the current user is allowed to access.

Agent Instructions Are Not Authorization

Suppose the agent instructions say:

Only managers may access financial reports.

That is not sufficient authorization.

The actual tool must enforce access in application code.

The secure flow is:

Authenticated Request
      ↓
Trusted User Context
      ↓
Tool
      ↓
Authorization Check
      ↓
Business Service

not:

Prompt says user is allowed
      ↓
Execute operation

Prompts guide model behavior.

Application code enforces security.

Keep Trusted Context Outside Model Arguments

Imagine an application is multi-tenant.

Do not expose:

GetProductStockAsync(
    int tenantId,
    string productName)

and allow the model to choose tenantId.

Instead:

Authenticated User
      ↓
Trusted Tenant Context
      ↓
Inventory Service

The tool should receive only information the model genuinely needs to choose.

For example:

GetProductStockAsync(
    string productName)

while the tenant comes from authenticated application context.

The same applies to:

UserId
OrganizationId
Permissions
Security Scope

These values should come from trusted application state.

Read Tools and Write Tools Are Different

So far, our agent has read-only capabilities:

get_product_stock
search_knowledge

These retrieve information.

Now imagine adding:

create_purchase_order
cancel_order
issue_refund
delete_customer
send_payment

Those tools change real business state.

They require stronger controls.

A useful classification is:

Read Tool
   ↓
Retrieve Information

Write Tool
   ↓
Change Business State

For write operations, consider:

Authentication
Authorization
Input validation
Business rules
Human approval
Idempotency
Transactions
Audit logs
Rate limits

Do not treat a write tool as just another prompt feature.

Add Human Approval for Sensitive Actions

Suppose a future agent can create purchase orders.

The agent may determine:

Wireless Mouse is below reorder level.

Recommended action:
Create a purchase order for 50 units.

Recommendation and execution are different.

A safer workflow is:

Agent Recommendation
       ↓
Human Approval
       ↓
Application Validation
       ↓
Create Purchase Order

not:

Agent Decides
       ↓
Immediately Changes Business Data

We will explore human approval more deeply later in this series.

Limit the Agent's Scope

An agent's instructions should define its role clearly.

For example:

You are an inventory and business
knowledge assistant.

is better than:

You can do anything the user asks.

Scope should also be reflected in the available tools.

If an inventory assistant has no reason to:

Delete users
Modify payroll
Send marketing emails

those capabilities should not be available to it.

The safest unnecessary tool is the one you never expose.

Avoid Too Many Tools

It can be tempting to give one agent dozens of tools:

Inventory
Sales
HR
Finance
CRM
Email
Calendar
Documents
Payments
Reporting
Administration
...

But more tools do not automatically create a better agent.

As responsibilities grow, tool selection becomes harder and instructions become broader.

Start with a small set of focused capabilities.

For example:

Inventory Agent

get_product_stock
search_inventory_policy
get_supplier_info

A separate financial agent might have a different set.

Later, agents can be composed when the application genuinely requires multiple specialized roles.

Create an Agent Service

We should not construct the agent inside a controller.

Create an application service.

public interface IBusinessAgentService
{
    Task<string> RunAsync(
        string message,
        CancellationToken cancellationToken = default);
}

Implement it:

using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;

public sealed class BusinessAgentService
    : IBusinessAgentService
{
    private readonly AIAgent _agent;

    public BusinessAgentService(
        IChatClient chatClient,
        InventoryAgentTools inventoryTools,
        KnowledgeAgentTools knowledgeTools)
    {
        AIFunction getProductStock =
            AIFunctionFactory.Create(
                inventoryTools.GetProductStockAsync,
                name: "get_product_stock",
                description:
                    "Gets current stock and reorder level for a product.");

        AIFunction searchKnowledge =
            AIFunctionFactory.Create(
                knowledgeTools.SearchKnowledgeAsync,
                name: "search_knowledge",
                description:
                    "Searches approved internal business documentation.");

        _agent =
            new ChatClientAgent(
                chatClient,
                instructions:
                    """
                    You are an inventory and business
                    knowledge assistant.

                    Use tools when current business
                    facts or internal documentation
                    are required.

                    Use multiple tools when necessary.

                    Never invent inventory quantities,
                    policies, or operational facts.

                    If the required information is not
                    available, clearly say so.
                    """,
                tools:
                [
                    getProductStock,
                    searchKnowledge
                ]);
    }

    public async Task<string> RunAsync(
        string message,
        CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrWhiteSpace(message))
        {
            throw new ArgumentException(
                "Message is required.",
                nameof(message));
        }

        AgentResponse response =
            await _agent.RunAsync(
                message.Trim(),
                cancellationToken:
                    cancellationToken);

        return response.Text;
    }
}

Our controller now does not need to understand agent internals.

Register the Agent Dependencies

In Program.cs:

builder.Services
    .AddScoped<InventoryAgentTools>();

builder.Services
    .AddScoped<KnowledgeAgentTools>();

builder.Services
    .AddScoped<IBusinessAgentService,
        BusinessAgentService>();

The rest of the dependencies from previous days remain registered normally:

IChatClient
IInventoryService
IKnowledgeSearchService
AppDbContext
Vector Store

The agent service coordinates those capabilities.

Create the Agent API Endpoint

Create:

using System.ComponentModel.DataAnnotations;

public sealed class AgentRequest
{
    [Required]
    [StringLength(
        4000,
        MinimumLength = 1)]
    public string Message { get; set; }
        = string.Empty;
}

Then create the controller:

using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/agent")]
public sealed class AgentController
    : ControllerBase
{
    private readonly IBusinessAgentService
        _agentService;

    public AgentController(
        IBusinessAgentService agentService)
    {
        _agentService =
            agentService;
    }

    [HttpPost("run")]
    public async Task<ActionResult<object>>
        RunAsync(
            [FromBody] AgentRequest request,
            CancellationToken cancellationToken)
    {
        string answer =
            await _agentService.RunAsync(
                request.Message,
                cancellationToken);

        return Ok(
            new
            {
                answer
            });
    }
}

The endpoint becomes:

POST /api/agent/run

Test the Agent

Send:

{
  "message": "Check Wireless Mouse stock. If it is below the reorder level, search our inventory policy and tell me what action is recommended."
}

The internal execution may look like:

Agent receives goal
        ↓
get_product_stock
        ↓
4 units
Reorder level = 10
        ↓
Agent determines stock is low
        ↓
search_knowledge
        ↓
Relevant inventory policy
        ↓
Agent combines results
        ↓
Final response

Notice that the controller did not explicitly say:

CallStockTool();
CallKnowledgeTool();

The agent selected the capabilities while working toward the goal.

Agent Execution Should Still Be Observable

If an agent performs multiple operations, production systems need visibility into what happened.

Useful telemetry may include:

Agent Run ID
Conversation / Session ID
Tool Name
Tool Duration
Success / Failure
Model Latency
Token Usage
Total Run Duration
Approval Decisions

But avoid unnecessarily logging:

Passwords
API Keys
Connection Strings
Sensitive Documents
Private Tool Results

Observability should help diagnose the agent without becoming a new data-leak risk.

Handle Tool Failures

Tools can fail.

For example:

Database unavailable
Vector store unavailable
External API timeout
Product not found
Authorization denied

The agent should not turn infrastructure failure into invented business information.

Tools should return or throw controlled application-level failures.

For example:

Inventory information is temporarily unavailable.

rather than exposing:

Server=...
Database=...
Login failed...

The agent can then explain the limitation without receiving sensitive infrastructure details.

Put Limits Around Agent Execution

Agent loops should not continue indefinitely.

Production applications should consider limits such as:

Maximum tool calls
Maximum execution time
Cancellation
Token budget
Maximum retrieved data
Maximum retries

For example:

User Request
   ↓
Agent
   ↓
Tool
   ↓
Agent
   ↓
Tool
   ↓
Agent
   ↓
...

should not become an uncontrolled loop.

Agent autonomy should always exist inside application-defined limits.

Agent vs Workflow

There is another important distinction.

Suppose your business process must always execute:

Validate Order
      ↓
Check Payment
      ↓
Reserve Stock
      ↓
Create Shipment

The sequence is deterministic.

You may not need an agent to decide the order.

A workflow or normal application code may be more appropriate.

Agents are useful when some decisions depend on context:

Goal
 ↓
Determine Needed Capability
 ↓
Observe Result
 ↓
Determine Next Step

A useful principle is:

Use deterministic code for deterministic processes and agents where model-driven decisions genuinely add value.

Agent + RAG + Database

After Day 10, our application architecture looks much more complete.

                       User
                         ↓
                    AI Agent
                         ↓
            ┌────────────┴────────────┐
            ↓                         ↓
     Business Tools             Knowledge Tool
            ↓                         ↓
     Application Service          RAG Search
            ↓                         ↓
         EF Core                Vector Store
            ↓                         ↓
        Database                  Documents
            └────────────┬────────────┘
                         ↓
                     AI Agent
                         ↓
                    Final Answer

The agent does not replace these layers.

It coordinates them.

Project Structure After Day 10

Our project may now look like:

CSharpAiApi
│
├── AI
│   ├── InventoryAgentTools.cs
│   └── KnowledgeAgentTools.cs
│
├── Controllers
│   ├── AiController.cs
│   ├── InventoryAssistantController.cs
│   ├── KnowledgeController.cs
│   └── AgentController.cs
│
├── Data
│   └── AppDbContext.cs
│
├── Entities
│   └── Product.cs
│
├── Models
│   ├── AgentRequest.cs
│   ├── KnowledgeChunk.cs
│   ├── KnowledgeSearchResult.cs
│   ├── ProductStockInfo.cs
│   └── RagAnswer.cs
│
├── Services
│   ├── IInventoryService.cs
│   ├── InventoryService.cs
│   ├── IKnowledgeSearchService.cs
│   ├── KnowledgeSearchService.cs
│   ├── IBusinessAgentService.cs
│   └── BusinessAgentService.cs
│
├── Utilities
│   └── TextChunker.cs
│
├── Program.cs
└── appsettings.json

The new layer is:

BusinessAgentService

which coordinates existing capabilities through the agent.

Common Agent Mistakes

Calling Every AI Application an Agent

A single prompt followed by a response does not automatically require agent architecture.

Keep simple applications simple.

Giving the Agent Too Many Tools

Start with a focused capability set.

Add tools only when the agent genuinely needs them.

Using Prompts as Security

Instructions are not authorization.

Enforce permissions in application code.

Exposing Generic Infrastructure Tools

Avoid broad capabilities such as:

execute_sql
run_command
delete_record
call_any_url

Prefer narrow business operations.

Letting the Model Supply Trusted IDs

Tenant IDs, authenticated user IDs, permissions, and security scopes should come from trusted application context.

Automatically Executing High-Impact Actions

Read operations and write operations have different risk profiles.

Sensitive changes may require explicit approval.

Using an Agent for Deterministic Work

If the process is known in advance, normal C# code or a workflow may be clearer and safer.

Ignoring Execution Limits

Bound tool calls, execution time, retries, context size, and cancellation.

Production Checklist

Before deploying an agent that can access real business capabilities, verify:

  • the agent has a clearly defined role

  • only required tools are exposed

  • tool descriptions are specific

  • authentication is enforced

  • authorization happens inside application code

  • tenant identity comes from trusted context

  • arbitrary SQL is unavailable

  • sensitive document retrieval is filtered

  • retrieved content is treated as untrusted

  • tool inputs are validated

  • tool results expose only necessary data

  • write tools have stronger controls

  • high-impact actions can require approval

  • agent execution has limits

  • cancellation is propagated

  • tool failures are handled safely

  • sensitive information is excluded from logs

  • agent and tool activity is observable

  • deterministic workflows remain deterministic where appropriate

What We Built Today

Before Day 10, our application had several separate AI capabilities:

IChatClient
Function Calling
EF Core Data
Vector Search
RAG

Today we introduced:

AIAgent

and used a ChatClientAgent to coordinate selected capabilities.

Our architecture evolved from:

User
 ↓
Single AI Operation
 ↓
Response

to:

User Goal
   ↓
Agent
   ↓
Select Capability
   ↓
Execute
   ↓
Observe
   ↓
Select Next Capability if Needed
   ↓
Final Answer

The important principle is that the agent does not own the application.

Your application still controls:

Which tools exist
What each tool can do
Which data can be accessed
Who is authorized
Which actions require approval
How long execution may continue

The model operates within those boundaries.

Day 10 Checklist

Before moving to Day 11, make sure you understand:

  • what an AI agent is

  • how an agent differs from a chatbot

  • how agents build on function calling

  • what AIAgent represents

  • how ChatClientAgent uses IChatClient

  • how C# methods become agent tools

  • how an agent can combine database and RAG capabilities

  • why tool names and descriptions matter

  • why agents should receive narrow business capabilities

  • why prompts are not authorization

  • why trusted identity should stay outside model arguments

  • why read and write tools need different controls

  • when human approval becomes useful

  • why agent execution needs limits

  • when deterministic workflows are better than agents

What's Next: Day 11

Our first agent can now choose between several capabilities.

But real applications often need a richer tool ecosystem.

In Day 11: Build and Design AI Agent Tools in C#, we will focus specifically on creating production-quality tools.

We will explore:

Tool Contracts
      ↓
Input Validation
      ↓
Authorization
      ↓
Execution
      ↓
Structured Results
      ↓
Error Handling
      ↓
Observability

We will also look at when a capability should be:

A Function Tool
An API-backed Tool
An MCP Tool
Another Agent

The goal will not be to give the agent more power simply for the sake of it.

The goal will be to design safer, clearer, and more reliable capabilities.

Final Thoughts

Building an AI agent is not about giving a model unrestricted control over an application.

A production-friendly design looks more like:

User Goal
   ↓
Agent
   ↓
Approved Capabilities
   ↓
Application Security
   ↓
Business Logic
   ↓
External Systems

The agent can decide which approved capability helps achieve the user's goal.

But your C# application remains responsible for:

Security
Authorization
Validation
Business Rules
Data Access
Approval
Execution Limits
Auditing

That separation is critical.

The model provides flexible reasoning and language understanding.

The application provides authority and control.

A useful principle for agent development is:

Let the agent decide which approved capability it needs. Let the application decide what that capability is allowed to do.

Day 9 gave our application access to document knowledge.

Day 10 brings our existing capabilities together through an AI agent.

In Day 11, we will make those agent tools more robust and production-ready.

Comments 0