Estimated — self-paced course, go at your own speed.
C# to F#: Building an AI Agent Without Python
A direct follow-up to "C# to F#: Language Fundamentals", this tier assumes you're comfortable with dotnet fsi, let, the pipe, pattern matching, and records; if needed, revisit that course's "Pipe and Composition", "Pattern Matching", and "Records" modules. You'll learn discriminated unions, Option, and Result by building an AI agent: an e-commerce assistant that answers "where is my order?". No paid API key, not a single line of Python. Prerequisites: the .NET 10 SDK; for modules 2, 5, and 7, Ollama and the qwen3.5:4b model (about 3.4 GB, up to 4 GB on Mac), whose installation is walked through in module 2. Six modules (1, 3, 4, 6, 9, 10) work without Ollama; module 8 only needs Ollama running for one of its scenarios; modules 2, 5, and 7 need the model. The duration is indicative: it mostly reflects Ollama's responses on a CPU alone and varies from one machine to another (model download not included). A few model observations in this English version come from runs of the French scripts; they are marked as such.
Modules
- 1Why Python?Training a model is not building an agent: what actually does the work of an AI agent, and why it's a job for a .NET developer.
- 2First LLM Call from an .fsx ScriptInstall a local model with Ollama, then query it from an F# script through Microsoft.Extensions.AI's IChatClient abstraction — no project, no API key.
- 3Discriminated Unions: Modeling an Order's StatusDescribe a value that has several shapes, each with its own data, and let the compiler check that every shape is handled.
- 4Option: An Order That Doesn't ExistRepresent a possible absence in the type, without null, and write getOrder, the function the model will call.
- 5Giving the Model a ToolDeclare getOrder as a tool, read the model's call request with the Microsoft.Extensions.AI types, run the tool, and send its result back — a round trip done by hand.
- 6Result: A Boundary Against the Model's ResponsesTurn an untrusted tool request into a typed value or a named error, chaining steps with Result.bind and Result.map.
- 7The Agent LoopAssemble model, tool, and boundary into a bounded loop where each turn is a discriminated union that your code handles case by case.
- 8Guardrails: When the Model (or the Network) Goes Off the RailsMake every failure a typed, reproducible case — forbidden tool, invalid argument, endless loop, stopped server, exceeded timeout — then name what types don't protect against.
- 9Exposing the Tool Through an MCP ServerMake getOrder usable by any MCP client with the official C# SDK, from an F# script, and understand what the protocol standardizes.
- 10Wrap-up: Choosing the Right Type, Switching ModelsRecap where the discriminated union, Option, and Result were used, learn to choose between them in new situations, and check that switching models touches only one file.