How a .NET dev built an AI assistant A .NET developer building an AI assistant for an interactive 3D learning app describes the architectural decisions behind Cori, an assistant that can talk and act simultaneously in a live 3D viewer. The team used Microsoft.Extensions.AI abstractions to keep the codebase vendor-neutral, avoiding lock-in to any single AI provider. Did you just get the task to “make an AI assistant” — and you mainly do .NET? Same. I’m also one of those people who rolls their eyes at a lot of the AI hype, and the internet is full of articles where every confident tutorial contradicts the previous one. So instead of publishing one more “definitive guide” that will age badly in two weeks, here’s the version I wish I’d found: what my team actually decided to build, the wrong turns we took on the way, and the code that finally made it click — written for people who know C but have never built an AI feature. This is not a best-practices sermon. It’s more like: here’s the problem, here’s what we nearly built, here’s what annoyed me, and here’s what we landed on. I work on an app that helps kids learn through interactive 3D models — a heart, a cell, a volcano — in the browser, AR, and VR. We’re adding Cori , an assistant you can talk to about whatever model is currently on screen. “Rotate the heart left.” → it rotates “Why is this chamber bigger?” →it explains That means Cori has to talk and act at the same time , and both of those outputs have to reach a live 3D viewer. That, for me, is the actual problem. Not “which model is smartest?” Not “which SDK has the coolest demo?” The interesting part is how the output gets to the client . Because once you stop thinking about the model as the product and start thinking about delivery as the problem, the architectural decisions get a lot clearer. Our stack, for context: the backend is .NET with Wolverine + Marten on PostgreSQL and the frontend is Svelte . Fair warning: this is not exactly the most well-paved road in AI land. A lot of AI tooling assumes Python or TypeScript first, and .NET support often arrives later, half-finished, or not at all. So if you’re on a similar stack, you’re probably not picking from polished examples — you’re cutting the path by hand. Before the story, here are the four words every AI article uses as if everybody was born knowing them. Rotate or SearchContent . Mid-response, it can ask for one of those functions to be called. It does not run your C code itself — it asks, and your code executes it. AIAgent type.That’s the whole glossary. Enough vocabulary to survive the rest of the article without having to alt-tab every two minutes. The first decision had nothing to do with streaming or transport. It came from plain distrust. My biggest fear was not “will the model be smart enough?” It was tying the whole codebase to one vendor SDK, one framework, one opinionated stack, and then getting stranded the moment the ecosystem changed direction — which, in AI, it absolutely will. So the first rule became simple: talk to abstractions . Use generic interfaces in application code, and keep the actual provider — OpenAI, Deepgram, whoever wins this month — behind DI where it belongs. Pleasant surprise: .NET actually gives you this now. Microsoft.Extensions.AI is basically the ILogger pattern, but for AI: IChatClient — provider-neutral chat / LLM access IEmbeddingGenerator — embeddings for vector or semantic search ITextToSpeechClient — text-to-speech Microsoft.Extensions.VectorData — vendor-neutral vector store abstractionsThat means the provider stays a registration detail: // Register once: OpenAI hidden behind the generic IChatClient. builder.Services.AddKeyedSingleton