The .NET agentic ecosystem deserves a genuine architectural choice. Workflow-driven is not the whole answer. A developer argues that the .NET agentic AI ecosystem lacks a genuine architectural choice beyond workflow-driven orchestration, which has real limits when tasks do not fit a predefined shape. The developer proposes a model-driven alternative where the LLM owns its own event loop, enabling it to reason about unexpected failures at runtime rather than relying on developer-written catch blocks. This shift moves the developer's role from defining execution paths to defining the tool surface and system prompt. Something is rapidly happening in the .NET agentic AI ecosystem. Frameworks are emerging, patterns are being established, and developers are making architectural decisions that will be hard to reverse. Most of those decisions are converging on the same pattern: workflow-driven orchestration with an LLM attached at key steps. That pattern has real merit. It also has real limits. Right now, .NET developers are not being offered a genuine alternative. Workflow-driven agent frameworks inherit their shape from orchestration thinking. You define the steps. You define the transitions. The LLM is invoked at points you determine, within boundaries you have set. The developer is the architect of the execution. The LLM is a capable participant in a process you have already designed. You can look at the code and understand exactly what will happen. When something fails, you know where and why. That visibility is built into the architecture. When the shape of a task is known before execution begins, workflow-driven is the right architectural choice. The steps are explicit, the transitions are defined, and the execution path is predetermined by design. Document processing, classification, structured data extraction, and report generation all fit well in a pre-defined workflow pattern because they share the same characteristic: you can draw the flowchart before you write the code. Workflow-driven is the right choice when that flowchart is exactly what you want. When the value is in the agent following a precise, repeatable sequence, with the developer owning the execution shape deliberately. The LLM and tools serve bounded roles within a structure you designed. That is not a constraint, it is the intent. The limits appear when the task does not fit a predefined shape. A user asks an agent to research a topic, synthesise findings from multiple sources, identify gaps, decide whether to go deeper or surface what it has and produce something useful. You cannot draw that flowchart in advance. The decisions about which sources are worth pursuing, when it has enough, and what counts as a gap are judgment calls that belong to the model, not the developer. The deeper problem appears under failure. When a workflow-driven agent encounters something unexpected such as a tool failing, an API returning an unusual response, or a task turning out more complex than anticipated, the framework routes to whatever error handling the developer wrote. A catch block. A retry. A fallback step. Code that was written before the failure happened, by a developer who had to guess which failures were worth anticipating. What never gets invoked is the LLM itself. And that matters, because the LLM can reason about a failure it has never seen before. It understands the user's intent. It knows what alternatives exist. It can decide whether to try a different tool, ask the user a clarifying question, or produce a useful partial result and explain why. A developer writing catch blocks cannot encode that breadth of reasoning in advance. The LLM brings it to every failure, at runtime, for free. But only if the architecture gives it the opportunity to do so. In a model-driven framework the relationship between developer and LLM is inverted. You give the agent a model, a system prompt, and a set of tools. The model drives its own event loop. It decides which tools to call, in what order, what to do when a tool returns an unexpected result, and when it has enough to respond. You do not write the orchestration. The model owns it. The developer's job shifts from defining execution paths to defining the tool surface: what capabilities the agent has access to and what it knows about itself through the system prompt. The system prompt becomes the architectural document. Failure modes are handled differently because the model has full context to reason about them rather than hitting an unhandled branch in code that was never written. The recovery story deserves its own treatment. What happens when an agent fails mid-task, and why the architectural choice determines whether the model or the developer handles it, is the subject of the next post in this series. The .NET ecosystem is converging on workflow-driven as the default. Not because anyone made a deliberate architectural argument for it. Because it arrived first, maps naturally to existing .NET mental models, and is being built into the frameworks gaining traction. The model-driven path simply was not there for .NET developers natively. That is the gap. A native, model-driven, zero-reflection .NET implementation of the kind that already exists in other ecosystems was not being built for .NET. That is what Jacquard.NET is. A framework I built, inspired by the Strands Agents design principles, ground-up in C 13, to give .NET developers a genuinely native, model-driven, zero runtime reflection path that fits the platform they already work in. js using Jacquard.Core; using Jacquard.Models.Bedrock; using MyApp; var agent = new Agent model: new BedrockModel "us-east-1" , systemPrompt: "You are a research assistant.", toolProviders: new WebSearchTools , new SummaryTools ; await foreach var evt in agent.StreamAsync userRequest { if evt is TextDeltaEvent delta Console.Write delta.Delta ; } The model decides which tools to call and in what sequence. You did not write a step for web search followed by summarisation. The model chose that path based on what the task required. Add a tool, remove a tool, the loop adapts. Tools are defined at compile time via Roslyn source generators with zero runtime reflection: using Jacquard.Core; namespace MyApp; public partial class WebSearchTools { Tool "Search the web for current information on a topic." public async Task