DEV Community

Cover image for Your First AI Feature in .NET 10 in 15 Minutes
MICHAEL-MAURICE
MICHAEL-MAURICE

Posted on

Your First AI Feature in .NET 10 in 15 Minutes

You don't need a vector database, an agent framework or a weekend to ship your first AI feature in .NET. You need one interface, IChatClient, and about fifteen minutes.

In this post we'll build a small but real feature: a support ticket triage endpoint. You POST a customer email, and you get back a category, a priority and a one-line summary as a typed C# object. Then we'll stream a reply draft and make the model swappable with one line.

📄 Want the full version of every snippet here? Grab the free .NET AI Starter Cheat Sheet. Enter your email and get it free: 8 pages of copy-paste C# covering the whole .NET AI stack, tool calling, structured output, RAG, MCP and a production checklist.

🔥 Tech Skill Builder is $5/month for a very limited time only (normally $10). Grab it now before it goes back to $10. Complete, tested .NET + AI projects with full source and step-by-step PDF guides: Join Tech Skill Builder

The plan (and the clock)

Minutes Step
0 to 2 Create the project and add two packages
2 to 5 Register IChatClient once, in DI
5 to 9 Typed triage with structured output
9 to 12 Stream a reply draft
12 to 15 Guardrails, and swapping the model

The code below shows the shape of each step. Some parts are trimmed and marked as such.

Minute 0 to 2: project and packages

dotnet new webapi -n TicketTriage --use-minimal-apis
cd TicketTriage
dotnet add package Microsoft.Extensions.AI
dotnet add package Microsoft.Extensions.AI.OpenAI
dotnet user-secrets init
dotnet user-secrets set "OpenAI:Key" "<your-key>"
Enter fullscreen mode Exit fullscreen mode

Microsoft.Extensions.AI gives you the abstractions (IChatClient, ChatMessage, ChatOptions) and the middleware. Microsoft.Extensions.AI.OpenAI is the adapter that turns the OpenAI SDK client into an IChatClient. Your app code will only ever see the interface.

Keep the key in user secrets (or environment variables in production). Never in appsettings.json, and never in Git.

Minute 2 to 5: one registration, used everywhere

The mistake I see most often in first AI features is new OpenAIClient(...) inside a controller. Register the client once and inject the interface instead:

using Microsoft.Extensions.AI;
using OpenAI;

builder.Services
    .AddChatClient(sp => new OpenAIClient(builder.Configuration["OpenAI:Key"])
        .GetChatClient("gpt-4o-mini")
        .AsIChatClient())
    .UseLogging();
    // more middleware (telemetry, caching, tool calls): see the cheat sheet
Enter fullscreen mode Exit fullscreen mode

That builder is a pipeline. Every call goes through the middleware you add, in order, before it reaches the model. Logging is enough for today.

Minute 5 to 9: typed triage, not string parsing

Here's the part that makes it feel like a real feature. Don't ask the model for "JSON please" and then fight JsonSerializer. Ask for a C# type:

public enum Priority { Low, Normal, High, Urgent }

public record Triage(string Category, Priority Priority, string Summary);
public record TicketRequest(string Body);

app.MapPost("/tickets/triage", async (IChatClient ai, TicketRequest req) =>
{
    ChatResponse<Triage> res = await ai.GetResponseAsync<Triage>(
    [
        new(ChatRole.System, "You triage support emails for an online shop. ..."),
        new(ChatRole.User, req.Body)
    ]);

    // ... validate res.Result before trusting it (step 5)
    return Results.Ok(res.Result);
});
Enter fullscreen mode Exit fullscreen mode

GetResponseAsync<T> generates a JSON schema from your record, sends it with the request and deserializes the answer into Triage. Using an enum for Priority means the model can't invent a "Kinda urgent" level.

The system prompt matters more than the code here. Mine lists the allowed categories and says what counts as urgent. I've cut it short above because the wording is where most of the tuning goes.

Minute 9 to 12: stream the reply draft

A triage result is small, so waiting for it is fine. A reply draft is longer, and a spinner for six seconds feels broken. Stream it:

app.MapPost("/tickets/draft", (IChatClient ai, TicketRequest req) =>
    StreamDraft(ai, req));

static async IAsyncEnumerable<string> StreamDraft(IChatClient ai, TicketRequest req)
{
    await foreach (var update in ai.GetStreamingResponseAsync(/* system + user messages */))
        yield return update.Text;
}
Enter fullscreen mode Exit fullscreen mode

Returning IAsyncEnumerable<string> from a minimal API endpoint sends the chunks as they arrive. The front end shows the text as the model writes it. Cancellation, retries and Server-Sent Events are the next layer up, and you can skip them for a first version.

Minute 12 to 15: guardrails and the one-line swap

Three things I'd add before anyone else uses this:

  1. Cap the cost. Pass new ChatOptions { MaxOutputTokens = 300, Temperature = 0.1f } for triage. Low temperature for classification, a little higher for drafts.
  2. Don't trust the result blindly. If Summary is empty or the category isn't in your list, fall back to "Needs a human". The model will be wrong sometimes, and your code should expect that.
  3. Cap the input. Truncate the email body before sending it. A 40-page pasted log shouldn't cost you a dollar.

And the payoff for coding against the interface: switching to a local model for development is one registration change.

// dev: a local model through Ollama (OllamaSharp implements IChatClient)
builder.Services.AddChatClient(new OllamaApiClient(new Uri("http://localhost:11434"), "llama3.2"));
Enter fullscreen mode Exit fullscreen mode

Your endpoints, prompts and records don't change at all.

What we skipped on purpose

To keep it to fifteen minutes I left out tool calling (letting the model look up the customer's order), RAG (answering from your own help-center docs), telemetry and evals. Those are the steps that turn a demo into something you'd put in front of customers, and each one has a few traps.

Get the full version, free

Every snippet in this post, completed, plus tool calling, RAG in six steps, an MCP server in C# and a production checklist, is in the free .NET AI Starter Cheat Sheet. Enter your email and get it free. 📄

When you're ready for complete projects instead of snippets, Tech Skill Builder gives you working, tested .NET + AI solutions with full source and step-by-step PDF guides. Tech Skill Builder is $5/month for a very limited time only (normally $10). Grab it now before it goes back to $10. Join Tech Skill Builder

What was the first AI feature you shipped in .NET, or the one you're planning? Tell me in the comments.

Top comments (0)