Why This Article
If you're a .NET developer, you've probably noticed the extraordinary momentum surrounding artificial intelligence. AI is no longer just a research topic or a set of models exposed through APIs — it's rapidly becoming the foundation of a new paradigm: intelligent automation. Tasks that were once exclusively performed by humans — analysis, interpretation, decision-making — are increasingly being delegated to autonomous software systems capable of reasoning, acting, and interacting with the external world. In other words, to AI agents.
There's an interesting irony here. Many of us already write code with the help of AI — tools like Claude Code, Codex, GitHub Copilot, and similar agentic coding assistants have become part of our daily workflow. This raises a legitimate question: does it still make sense to study programming languages and development frameworks when AI lets you jump effortlessly from one language to another, from one tool to the next?
I believe the answer is yes — more than ever. Even though we rarely write every line of code by hand anymore, production software still demands understanding, control, and judgment. You need to analyze the code that's being generated, spot potential issues, write meaningful tests, and decide when and how to improve it. AI accelerates the writing; it doesn't replace the thinking.
There's another practical reason: introducing AI capabilities into existing software. Most of us don't get to start from scratch — we work on established codebases, with existing architectures and technology stacks. Adding an AI agent module to a production .NET application means working in C#, with the patterns and tools your team already knows. Preserving the structure and consistency of your software matters, and doing it in the language you master is a significant advantage.
This article is the first in a series where I want to explore exactly that: how .NET developers can build AI agents using familiar tools and skills. We'll work in C# with the Microsoft Agent Framework, step by step, with working code you can run and experiment with. My goal is to share practical knowledge and help the .NET community get started with AI agent development.
From Chatbot to Agent: When AI Learns to Take Action
There's a fundamental difference between a chatbot and an AI agent. A chatbot can talk. An agent can do things.
Ask a chatbot "What time is it?" and it will guess, hallucinate, or apologize. Ask an AI agent the same question, and it will call a function, check the system clock, and give you the actual time. That's the difference: reasoning + action.
In this article, we'll build an AI agent from scratch in C# using the Microsoft Agent Framework. We'll start with a simple conversational agent, then give it tools — turning it from a chatbot into something that can actually take action in the real world.
What is the Microsoft Agent Framework?
The Microsoft Agent Framework is Microsoft's unified framework for building AI agents in .NET. It provides:
- Provider abstraction — Same code works with OpenAI, Azure OpenAI, Anthropic Claude, Google Gemini, and local models via Ollama
- Built-in tool execution — The framework handles the entire function calling protocol automatically
-
Conversation management —
AgentThreadmaintains context across multiple interactions - Composability — Agents, tools, memory, and middleware compose together naturally
It builds on top of Microsoft.Extensions.AI, the standard AI abstraction layer for .NET.
The framework is in preview (this article uses version
1.0.0-preview.251219.1). Core concepts and patterns are stable, but APIs may evolve before the 1.0 release.
Prerequisites
- .NET 9 SDK (or later) — download here
- An OpenAI API key — get one here
- Your favorite IDE (Visual Studio, VS Code, or Rider)
Step 1: Create the Project
mkdir HelloAgent && cd HelloAgent
dotnet new console
dotnet add package Microsoft.Agents.AI.OpenAI --version 1.0.0-preview.251219.1
dotnet add package OpenAI --version 2.8.0
Set your API key:
# PowerShell
$env:OPENAI_API_KEY = "sk-your-key-here"
# Linux/Mac
export OPENAI_API_KEY=sk-your-key-here
Step 2: Your First Agent (The Chatbot)
Replace Program.cs with:
using Microsoft.Agents.AI;
using OpenAI;
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")
?? throw new InvalidOperationException("Set OPENAI_API_KEY");
var agent = new OpenAIClient(apiKey)
.GetChatClient("gpt-4o-mini")
.CreateAIAgent(
name: "Assistant",
instructions: """
You are a friendly and knowledgeable AI assistant.
Be concise but thorough. If you don't know something, admit it.
Always respond in the user's language.
""");
Console.WriteLine("Agent ready! Type 'exit' to quit.\n");
var thread = agent.GetNewThread();
while (true)
{
Console.Write("You > ");
var input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input)) continue;
if (input.Equals("exit", StringComparison.OrdinalIgnoreCase)) break;
Console.Write("Agent > ");
await foreach (var update in agent.RunStreamingAsync(input, thread))
{
Console.Write(update.ToString());
}
Console.WriteLine("\n");
}
Run it with dotnet run and you have a working conversational agent. The AgentThread maintains context:
You > My name is Marco
Agent > Nice to meet you, Marco! How can I help you today?
You > What's my name?
Agent > Your name is Marco!
But this is still just a chatbot. It can talk, but it can't do anything. Let's change that.
Step 3: Give Your Agent Superpowers with Tools
Tools are what transform an LLM from a text generator into a capable agent. In the Microsoft Agent Framework, a tool is a C# method decorated with a [Description] attribute.
Create a DateTimeTools.cs file:
using System.ComponentModel;
public static class DateTimeTools
{
[Description("Gets the current date and time. Use when the user asks about the current time or date.")]
public static string GetCurrentDateTime(
[Description("The type of time: 'local' for local time, 'utc' for UTC")]
string timeType = "local")
{
var dateTime = timeType.ToLowerInvariant() == "utc"
? DateTime.UtcNow
: DateTime.Now;
return $"{dateTime:dddd, MMMM dd, yyyy - HH:mm:ss} ({timeType.ToUpperInvariant()})";
}
[Description("Calculates days between two dates. Use when the user asks how much time until a date.")]
public static string CalculateDateDifference(
[Description("First date in yyyy-MM-dd format (e.g., 2026-12-25)")]
string fromDate,
[Description("Second date in yyyy-MM-dd format. If omitted, uses today.")]
string? toDate = null)
{
try
{
var from = DateTime.Parse(fromDate);
var to = toDate != null ? DateTime.Parse(toDate) : DateTime.Now;
var diff = to - from;
var direction = diff.TotalDays >= 0 ? "remaining" : "have passed";
var absDiff = diff.TotalDays >= 0 ? diff : -diff;
return $"From {from:MM/dd/yyyy} to {to:MM/dd/yyyy}: " +
$"{(int)absDiff.TotalDays} days {direction}";
}
catch (FormatException)
{
return "Error: invalid date format. Use yyyy-MM-dd (e.g., 2026-12-25)";
}
}
}
And a CalculatorTools.cs file:
using System.ComponentModel;
public static class CalculatorTools
{
[Description("Calculates a math expression. Use for any mathematical calculation.")]
public static string Calculate(
[Description("First operand")] double a,
[Description("Operator: +, -, *, /")] string op,
[Description("Second operand")] double b)
{
var result = op switch
{
"+" => a + b,
"-" => a - b,
"*" => a * b,
"/" => b != 0 ? a / b : double.NaN,
_ => double.NaN
};
return double.IsNaN(result)
? $"Error: invalid operation '{a} {op} {b}'"
: $"{a} {op} {b} = {result}";
}
[Description("Calculates a percentage of a number.")]
public static string CalculatePercentage(
[Description("The percentage value (e.g., 15 for 15%)")] double percentage,
[Description("The number to calculate the percentage of")] double number)
{
var result = number * percentage / 100;
return $"{percentage}% of {number} = {result}";
}
}
Notice the pattern:
-
[Description]on methods tells the LLM what the tool does and when to use it -
[Description]on parameters provides format guidance and valid options - Return values are human-readable strings the LLM incorporates into its response
- Error handling returns descriptive messages the LLM can explain to the user
Step 4: Register Tools with the Agent
Update Program.cs:
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")
?? throw new InvalidOperationException("Set OPENAI_API_KEY");
// Define the tools
var tools = new List<AITool>
{
AIFunctionFactory.Create(DateTimeTools.GetCurrentDateTime),
AIFunctionFactory.Create(DateTimeTools.CalculateDateDifference),
AIFunctionFactory.Create(CalculatorTools.Calculate),
AIFunctionFactory.Create(CalculatorTools.CalculatePercentage),
};
// Create the agent WITH tools
var agent = new OpenAIClient(apiKey)
.GetChatClient("gpt-4o-mini")
.CreateAIAgent(
name: "DevAssistant",
instructions: """
You are an AI assistant with access to tools.
BEHAVIOR:
- Be concise but thorough
- Use tools when appropriate instead of making up answers
- Always respond in the user's language
IMPORTANT:
- For calculations, ALWAYS use the calculator tool
- Never make up data: use tools to get real information
""",
tools: tools);
Console.WriteLine("Agent ready with tools! Type 'exit' to quit.\n");
var thread = agent.GetNewThread();
while (true)
{
Console.Write("You > ");
var input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input)) continue;
if (input.Equals("exit", StringComparison.OrdinalIgnoreCase)) break;
Console.Write("Agent > ");
await foreach (var update in agent.RunStreamingAsync(input, thread))
{
Console.Write(update.ToString());
}
Console.WriteLine("\n");
}
AIFunctionFactory.Create() takes your C# methods and generates JSON schemas the LLM can understand. The framework handles argument deserialization, method invocation, and result serialization — all automatically.
Step 5: See the Magic
Run it and try these:
You > What time is it?
Agent > The current time is Sunday, February 08, 2026 - 14:30:45 (LOCAL).
You > What is 15% of 847.50?
Agent > 15% of 847.50 is 127.125.
You > How many days until Christmas?
Agent > There are 320 days remaining until December 25, 2026.
This is the key moment. Your agent isn't guessing the time or doing mental math — it's calling your C# functions, getting real data, and weaving those results into natural language.
How It Works Under the Hood
When you ask "What time is it?":
1. YOUR CODE → Sends "What time is it?" to the LLM
2. LLM DECIDES → "I need GetCurrentDateTime"
3. FRAMEWORK → Deserializes args, invokes your C# method
4. YOUR METHOD → Returns "Sunday, February 08, 2026 - 14:30:45"
5. FRAMEWORK → Sends the result back to the LLM
6. LLM RESPONDS → "The current time is Sunday, February 08, 2026..."
All of this happens inside a single RunStreamingAsync call. The LLM can even call multiple tools in one turn:
You > What's the date and how many days until New Year?
Agent > Today is February 8, 2026. There are 327 days remaining
until January 1, 2027.
Two tool calls, one seamless response.
Chatbot vs Agent: The Summary
| Capability | Chatbot | Agent |
|---|---|---|
| Understand language | Yes | Yes |
| Remember context | Yes (with threads) | Yes (with threads) |
| Current date/time | Guesses | Checks via tools |
| Math calculations | Often wrong | Accurate via tools |
| File operations | Impossible | Can read/write files |
| API calls | Impossible | Can call any API |
The formula: Reasoning (LLM) + Action (Tools) = Agent.
What's Next
This is just the foundation. The Microsoft Agent Framework supports much more — memory and context persistence, multi-agent collaboration, workflow orchestration, and more.
I'll explore some of these topics in upcoming articles in this series. Stay tuned!
If you want to explore all of these topics hands-on with complete working projects, check out my book Practical AI Agents with Microsoft Agent Framework — it covers everything from your first agent to building a complete AI-powered Expense Tracker with Console, Telegram, and Web API interfaces.
This is Part 1 of the "AI Agents with .NET" series. Follow me to get notified when the next article drops!
What would you build with an AI agent? Let me know in the comments!
Top comments (0)