Most “call an LLM from your app” tutorials hand you a vendor SDK and call it a day. It works — until the day you want to swap models or add a second provider, and suddenly you’re rewriting plumbing.
This Microsoft quickstart takes a different route: a small .NET console chat app built on Microsoft.Extensions.AI, the abstraction layer that sits above OpenAI, Azure OpenAI, Ollama and the rest. You write against IChatClient once; the model behind it becomes a registration detail.
Here’s the whole thing, OpenAI flavour.
What you need
- .NET 8.0 SDK or higher — download it here
- An OpenAI API key — from the OpenAI platform
That’s it. No Azure subscription for this path.
Scaffold the app
Create a console project and drop into it:
| |
Add the packages. The star is Microsoft.Extensions.AI.OpenAI — the bridge between the abstractions and the OpenAI SDK. It’s prerelease, so the flag is required:
| |
Keep the key out of your code
Never hardcode an API key. .NET’s user-secrets store keeps it off disk-in-your-repo and out of source control:
| |
ModelName is whatever model you’re entitled to — gpt-4o-mini, gpt-4o, and so on.
Wire up the client
In Program.cs, read the secrets and build an IChatClient. Note the shape: you create an OpenAIClient, ask it for a chat client, then call .AsIChatClient() to hand yourself back the abstraction. Everything after this line is provider-agnostic.
| |
Give the model a personality
The app plays a hiking guide. A system message sets the role and the rules up front — what to ask, what to return, how to close. This goes in first as the seed of the conversation history:
| |
The conversation loop
Here’s the part that makes it feel like a chat rather than a one-shot prompt: a List<ChatMessage> that grows. Every user turn and every assistant reply gets appended, and the whole list is sent on each request. That accumulating history is the app’s memory — the model has no state of its own between calls.
Responses stream token-by-token via GetStreamingResponseAsync, so text appears as it’s generated instead of after a pause:
| |
Run it
| |
Type a prompt, watch the reply stream in, ask a follow-up. Because history is preserved, the guide remembers your location and intensity across turns — the conversation actually builds.
Why the abstraction matters
Nothing here is exotic. The payoff is the seam: swap to Azure OpenAI and only the client-construction line changes — from new OpenAIClient(key)... to new AzureOpenAIClient(endpoint, credential).... The system prompt, the history list, the streaming loop all stay identical.
That’s the whole pitch of Microsoft.Extensions.AI: your app talks to IChatClient, and the model market underneath can shift as often as it likes. The next provider is a one-line change, not a rewrite.
If you want the layer of the picture this sits in, see my note on the core .NET AI building blocks.
Based on Microsoft’s Build an AI chat app with .NET quickstart.