<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Roxeem</title>
    <description>The latest articles on Forem by Roxeem (@roxeem).</description>
    <link>https://forem.com/roxeem</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3482160%2Fbb08be33-35a9-4ab3-8b9f-5403d3a65d99.png</url>
      <title>Forem: Roxeem</title>
      <link>https://forem.com/roxeem</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/roxeem"/>
    <language>en</language>
    <item>
      <title>Incremental Source Generators in .NET</title>
      <dc:creator>Roxeem</dc:creator>
      <pubDate>Sat, 08 Nov 2025 20:26:18 +0000</pubDate>
      <link>https://forem.com/roxeem/incremental-source-generators-in-net-38gk</link>
      <guid>https://forem.com/roxeem/incremental-source-generators-in-net-38gk</guid>
      <description>&lt;p&gt;As .NET developers, we’re obsessed with performance and productivity. We build robust systems, but we often hit a wall with repetitive, boilerplate code. Whether it’s mapping DTOs, implementing &lt;code&gt;INotifyPropertyChanged&lt;/code&gt;, or wiring up API clients, we find ourselves writing the same tedious patterns over and over.&lt;/p&gt;

&lt;p&gt;For years, our primary tools for this “metaprogramming” were Reflection and T4 templates. Reflection is powerful, but it’s slow. It runs at runtime, it’s string-based, and it’s notoriously unfriendly to AOT (Ahead-of-Time) compilation and linkers. T4 templates are better, as they run before compilation, but they are “dumb.” They are just text files that don’t understand your code’s semantics.&lt;/p&gt;

&lt;p&gt;What if we could have the best of both worlds? What if we could write code that runs &lt;em&gt;during&lt;/em&gt; compilation, understands our code’s syntax and semantics, and generates new, high-performance C# files on the fly?&lt;/p&gt;

&lt;p&gt;That’s exactly what  &lt;strong&gt;.NET Source Generators&lt;/strong&gt;  do. They are a Roslyn compiler feature that lets you inspect a user’s code and add new source files to the compilation. In this post, I’ll show you what they are, why they’re a massive improvement over older techniques, and how to build a simple, high-performance generator from scratch.&lt;/p&gt;

&lt;p&gt;Continue reading &lt;a href="https://roxeem.com/2025/11/08/incremental-source-generators-in-net/" rel="noopener noreferrer"&gt;Incremental Source Generators in .NET&lt;/a&gt; on &lt;a href="https://roxeem.com" rel="noopener noreferrer"&gt;Roxeem&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>roslyn</category>
      <category>performance</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Testing HttpClient in .NET without Moq or NSubstitute</title>
      <dc:creator>Roxeem</dc:creator>
      <pubDate>Sun, 26 Oct 2025 11:33:54 +0000</pubDate>
      <link>https://forem.com/roxeem/testing-httpclient-in-net-without-moq-or-nsubstitute-57d4</link>
      <guid>https://forem.com/roxeem/testing-httpclient-in-net-without-moq-or-nsubstitute-57d4</guid>
      <description>&lt;p&gt;When you’re building .NET applications that interact with external APIs, writing reliable unit tests for your HTTP clients can feel like threading a needle. Most developers reach for mocking frameworks like Moq or NSubstitute to simulate HTTP responses. But what if I told you there’s a cleaner, more deterministic way to test your HTTP clients without relying on any mocking libraries?&lt;/p&gt;

&lt;p&gt;In this post, I’ll walk you through how to unit test HTTP clients in .NET using built-in tools only. We’ll build a simple client, make it testable, and write robust tests using &lt;code&gt;HttpMessageHandler&lt;/code&gt; and &lt;code&gt;HttpClient&lt;/code&gt; without any mocking frameworks.&lt;/p&gt;

&lt;p&gt;Continue reading &lt;a href="https://roxeem.com/2025/10/26/testing-httpclient-in-net-without-moq-or-nsubstitute?ref=devto" rel="noopener noreferrer"&gt;Testing HttpClient in .NET without Moq or NSubstitute&lt;/a&gt; on &lt;a href="https://roxeem.com" rel="noopener noreferrer"&gt;Roxeem&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>net</category>
      <category>csharp</category>
      <category>testing</category>
      <category>aspnetcore</category>
    </item>
    <item>
      <title>A Pragmatic Guide to Server-Sent Events (SSE) in ASP.NET Core</title>
      <dc:creator>Roxeem</dc:creator>
      <pubDate>Fri, 24 Oct 2025 14:44:33 +0000</pubDate>
      <link>https://forem.com/roxeem/a-pragmatic-guide-to-server-sent-events-sse-in-aspnet-core-fna</link>
      <guid>https://forem.com/roxeem/a-pragmatic-guide-to-server-sent-events-sse-in-aspnet-core-fna</guid>
      <description>&lt;p&gt;As developers, we often face the challenge of building real-time features. Imagine you are tasked with creating a live dashboard that displays system metrics, a breaking news feed, or real-time notifications. The initial, almost instinctual, thought for many of us is, “I need WebSockets.” This often leads us down a path of managing complex bidirectional connections, handling protocol upgrades, and writing custom reconnection logic. But what if you only need to push data from the server to the client? Is there a simpler, more elegant way?&lt;/p&gt;

&lt;p&gt;The answer is yes. Meet Server-Sent Events (SSE), a mature, browser-native HTML5 standard that leverages the simplicity and ubiquity of HTTP for powerful, unidirectional server-to-client streaming. SSE isn’t a new, flashy technology. It’s a reliable workhorse that has been part of the web platform for years, yet it remains one of its most underrated features.&lt;/p&gt;

&lt;p&gt;In this post, we will embark on a journey to master Server-Sent Events in ASP.NET Core. We will start by deconstructing the protocol, then build a production-ready, scalable SSE solution. Finally, I will equip you with the architectural knowledge to make the right choice between SSE, WebSockets, and SignalR. By the end, you will not only know how to implement SSE but, more importantly, when and why to choose it&lt;/p&gt;

&lt;p&gt;Read more on: &lt;a href="https://roxeem.com/2025/10/24/a-pragmatic-guide-to-server-sent-events-sse-in-asp-net-core/" rel="noopener noreferrer"&gt;A Pragmatic Guide to Server-Sent Events (SSE) in ASP.NET Core&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>net</category>
      <category>aspnetcore</category>
      <category>csharp</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Strategic Pagination Patterns for .NET APIs</title>
      <dc:creator>Roxeem</dc:creator>
      <pubDate>Sat, 11 Oct 2025 13:31:31 +0000</pubDate>
      <link>https://forem.com/roxeem/strategic-pagination-patterns-for-net-apis-4kec</link>
      <guid>https://forem.com/roxeem/strategic-pagination-patterns-for-net-apis-4kec</guid>
      <description>&lt;p&gt;When you design APIs that return large datasets, pagination is not optional. Without it, you risk overwhelming your database, your network, and your consumers. Pagination is the art of breaking data into manageable chunks, and like most things in software architecture, there is no single best way to do it. Each strategy has trade-offs that affect performance, consistency, and developer experience.&lt;/p&gt;

&lt;p&gt;In this post, I will walk you through six common pagination strategies: Offset-based, Cursor-based, Keyset-based, Page-based, Time-based, and Hybrid approaches. For each, I will explain how it works, show you C# examples, and highlight the advantages and disadvantages. Along the way, I will use diagrams and analogies to make the concepts stick.&lt;/p&gt;

&lt;p&gt;Continue reading &lt;a href="https://roxeem.com/2025/10/11/strategic-pagination-patterns-for-net-apis/" rel="noopener noreferrer"&gt;Strategic Pagination Patterns for .NET APIs&lt;/a&gt; on &lt;a href="https://roxeem.com" rel="noopener noreferrer"&gt;Roxeem&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>net</category>
      <category>api</category>
      <category>aspnet</category>
      <category>csharp</category>
    </item>
    <item>
      <title>What .NET 10 GC Changes Mean for Developers</title>
      <dc:creator>Roxeem</dc:creator>
      <pubDate>Tue, 30 Sep 2025 15:20:25 +0000</pubDate>
      <link>https://forem.com/roxeem/what-net-10-gc-changes-mean-for-developers-1dmn</link>
      <guid>https://forem.com/roxeem/what-net-10-gc-changes-mean-for-developers-1dmn</guid>
      <description>&lt;p&gt;What if I told you that starting with .NET 10, several of your fundamental ideas about garbage collection are now outdated? Imagine that there are actual improvements that can sometimes cause two to three times better memory usage and speed. These improvements are available through a series of runtime switches and new optimization behaviors. However, it is important to consider that these improvements come with trade-offs that you need to evaluate instead of simply enabling them on faith.&lt;/p&gt;

&lt;p&gt;In this post, I’ll take you through the real story in .NET 10, show you the rationale behind the new GC features, give you actionable patterns, code, and measurement tools, and help you answer: should you rely on these improvements or tune and even disable them for your scenario?&lt;/p&gt;

&lt;p&gt;Continue reading &lt;a href="https://roxeem.com/2025/09/30/what-net-10-gc-changes-mean-for-developers/" rel="noopener noreferrer"&gt;What .NET 10 GC Changes Mean for Developers&lt;/a&gt;&lt;/p&gt;

</description>
      <category>net</category>
      <category>dotnet10</category>
      <category>performance</category>
      <category>cloudnative</category>
    </item>
    <item>
      <title>The Hidden Architecture Behind Fast and Reliable AI Prompts</title>
      <dc:creator>Roxeem</dc:creator>
      <pubDate>Wed, 17 Sep 2025 07:23:48 +0000</pubDate>
      <link>https://forem.com/roxeem/the-hidden-architecture-behind-fast-and-reliable-ai-prompts-2ngp</link>
      <guid>https://forem.com/roxeem/the-hidden-architecture-behind-fast-and-reliable-ai-prompts-2ngp</guid>
      <description>&lt;p&gt;Stop treating prompts like magic spells. Treat them like software and build conditional, parallel, dynamic, and stateful prompt pipelines in .NET with safety guardrails, faster latency, and a testable design.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://roxeem.com/2025/09/16/the-hidden-architecture-behind-fast-and-reliable-ai-prompts/" rel="noopener noreferrer"&gt;Continue reading on Roxeem.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>promptengineering</category>
      <category>ai</category>
    </item>
    <item>
      <title>How to orchestrate multi-tool AI workflows in .NET</title>
      <dc:creator>Roxeem</dc:creator>
      <pubDate>Fri, 12 Sep 2025 08:10:19 +0000</pubDate>
      <link>https://forem.com/roxeem/how-to-orchestrate-multi-tool-ai-workflows-in-net-541b</link>
      <guid>https://forem.com/roxeem/how-to-orchestrate-multi-tool-ai-workflows-in-net-541b</guid>
      <description>&lt;p&gt;This is the third part of my Practical .NET Guide to AI &amp;amp; LLM series. If you’ve followed along, you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learned about Microsoft.Extensions.AI (ME.AI): &lt;a href="https://roxeem.com/2025/09/04/the-practical-net-guide-to-ai-llm-introduction/" rel="noopener noreferrer"&gt;Practical .NET Guide to AI &amp;amp; LLM: Introduction&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;How to integrate LLMs correctly into your .NET applications and build robust AI features: &lt;a href="https://roxeem.com/2025/09/08/how-to-correctly-build-ai-features-in-dotnet/" rel="noopener noreferrer"&gt;How to Correctly Build AI Features in .NET&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this post, we will focus on orchestrating multiple tools ( &lt;strong&gt;tool chaining&lt;/strong&gt; ) using &lt;code&gt;Microsoft.Extensions.AI&lt;/code&gt;. First, we will explore various patterns for chaining tools. Next, we will discuss how to design multi-tool workflows and implement these patterns using familiar .NET dependency injection. Additionally, we will cover strategies for robust error handling and debugging your tool chains, as well as how to avoid common pitfalls.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Tool Chaining and Why Should You Care?
&lt;/h2&gt;

&lt;p&gt;Tool chaining, sometimes called function calling or tool invocation in LLM, is the process of linking discrete AI-powered tools or agents into a pipeline, where the output of one tool becomes the input for the next. This approach allows complex workflows to be constructed by composing simple, well-defined units of behavior, each focused on a particular subtask.&lt;/p&gt;

&lt;p&gt;By breaking down workflows into composable, swappable components, tool chaining enables high scalability, maintainability, and adaptability. Imagine a user query that needs to be classified, enriched with retrieved data, summarized, and then sent as a formatted email, with each step performed by a specialized tool:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F77mn5jp30or0yeu20iiv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F77mn5jp30or0yeu20iiv.png" alt="What Is Tool Chaining" width="800" height="53"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This modular approach stands in stark contrast to monolithic workflows, where a single large model handles every stage, usually leading to rigid, hard-to-maintain solutions. There are many reasons to use tool chaining, but to summarize:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt; : Isolate performance hotspots at the tool level.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainability&lt;/strong&gt; : Swap or upgrade individual tools without rewriting the workflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility&lt;/strong&gt; : Adapt quickly to evolving requirements or new AI capabilities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Robustness&lt;/strong&gt; : Failures can be contained at tool boundaries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interoperability&lt;/strong&gt; : Bring together LLMs, APIs, data services, and more, using standard .NET patterns.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Multi-Tool Workflow
&lt;/h2&gt;

&lt;p&gt;Multi-tool workflows extend tool chaining by introducing orchestration logic. This includes methods for more than two tools to interact in complex sequences. Additionally, it allows for handling branching or looping and aggregating results. Let us discuss common approaches seen in AI orchestration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sequential Pipelines (“Chain of Responsibility”)
&lt;/h3&gt;

&lt;p&gt;In a sequential pipeline, tools are arranged in a linear sequence, with each tool passing its result to the next. This is very similar to the classic Chain of Responsibility design pattern, that many developers are familiar with.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzg8putn9k77a154zchca.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzg8putn9k77a154zchca.png" alt="Sequential Pipelines" width="800" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This approach is best for linear, staged transformations (e.g., preprocess → enrich → postprocess), and you can structure it using interfaces and dependency injection (DI) to construct a pipeline of handlers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agent-Oriented (“Swarm”) Models
&lt;/h3&gt;

&lt;p&gt;Inspired by agent frameworks and multi-agent systems, these workflows delegate subtasks to specialized agents, which may run in parallel or communicate iteratively.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft57d6wfuadmwnxetxk31.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft57d6wfuadmwnxetxk31.png" alt="Agent-Oriented Swarm Models" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This model is well-suited for complex tasks that benefit from specialized agents working together, such as multi-step reasoning or collaborative problem-solving.&lt;/p&gt;

&lt;h3&gt;
  
  
  Branching/Conditional Workflows
&lt;/h3&gt;

&lt;p&gt;Sometimes, which tool(s) are engaged depends on input or intermediate results (e.g., classify intent, then route to a specific downstream workflow).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh2cco2tlrjejmjng6g7c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh2cco2tlrjejmjng6g7c.png" alt="Branching Conditional Workflows" width="800" height="170"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Parallel (“Fan-out/Fan-in”) Workflows
&lt;/h3&gt;

&lt;p&gt;In AI-powered systems, not every task needs to be carried out in a strict sequence. Some tasks can and should run in parallel. This is where the Fan-out/Fan-in pattern is particularly effective. It is a workflow model in which a single input is sent to multiple independent tools (fan-out), and their outputs are later collected and combined (fan-in).&lt;/p&gt;

&lt;p&gt;This pattern is especially beneficial when tasks are non-blocking, stateless, and do not rely on each other’s outputs. You can think of it like a parallel assembly line. Each station works on the same raw material, and the final product is assembled at the end.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpih0xj0k4qsj5tkd1dod.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpih0xj0k4qsj5tkd1dod.png" alt="Parallel Workflows" width="800" height="613"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing Tool Chains in .NET
&lt;/h2&gt;

&lt;p&gt;Modern .NET applications thrive on dependency injection (DI). As we discussed in &lt;a href="https://roxeem.com/2025/09/08/how-to-correctly-build-ai-features-in-dotnet/" rel="noopener noreferrer"&gt;previous&lt;/a&gt; posts, &lt;code&gt;Microsoft.Extensions.AI&lt;/code&gt; is designed from the ground up to support DI-friendly architecture for AI features. With DI, your tools and orchestrators become cleanly swappable, testable, and maintainable components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the Chain
&lt;/h3&gt;

&lt;p&gt;Let’s start by defining a common interface that all tools in the chain will implement. This interface should have a method for executing the tool’s logic and returning a result.&lt;/p&gt;

&lt;p&gt;C#&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;textarea tabindex="-1" aria-hidden="true" readonly&amp;gt;public interface IAIWorkflowStep
{
    Task&amp;amp;lt;AIWorkflowResult&amp;amp;gt; ExecuteAsync(AIWorkflowContext context, CancellationToken token = default);
}

public class AIWorkflowContext
{
    public string Input { get; set; } = string.Empty;
    public string? Intent { get; set; }
    public string? Location { get; set; }
    public object? WeatherForecast { get; set; }
    public IDictionary&amp;amp;lt;string, object?&amp;amp;gt; Metadata { get; } = new Dictionary&amp;amp;lt;string, object?&amp;amp;gt;();
    public DateTimeOffset CreatedAt { get; } = DateTimeOffset.UtcNow;
}&amp;lt;/textarea&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public interface IAIWorkflowStep
{
    Task&amp;lt;AIWorkflowResult&amp;gt; ExecuteAsync(AIWorkflowContext context, CancellationToken token = default);
}

public class AIWorkflowContext
{
    public string Input { get; set; } = string.Empty;
    public string? Intent { get; set; }
    public string? Location { get; set; }
    public object? WeatherForecast { get; set; }
    public IDictionary&amp;lt;string, object?&amp;gt; Metadata { get; } = new Dictionary&amp;lt;string, object?&amp;gt;();
    public DateTimeOffset CreatedAt { get; } = DateTimeOffset.UtcNow;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Implement Individual Tools as Services&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example, a classifier:&lt;/p&gt;

&lt;p&gt;C#&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;textarea tabindex="-1" aria-hidden="true" readonly&amp;gt;public class IntentClassifierStep : IAIWorkflowStep
{
    private readonly IChatClient _chatClient;

    public IntentClassifierStep(IChatClient chatClient)
    {
        _chatClient = chatClient;
    }

    public async Task&amp;amp;lt;AIWorkflowResult&amp;amp;gt; ExecuteAsync(
        AIWorkflowContext context, 
        CancellationToken token = default)
    {
        var chatMessages = [new ChatMessage(ChatRole.User, context.Input)];

        var response = await _chatClient.GetResponseAsync(chatMessages, token);
        context.Intent = response.Result.Trim();

        return AIWorkflowResult.Success(context);
    }
}&amp;lt;/textarea&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class IntentClassifierStep : IAIWorkflowStep
{
    private readonly IChatClient _chatClient;

    public IntentClassifierStep(IChatClient chatClient)
    {
        _chatClient = chatClient;
    }

    public async Task&amp;lt;AIWorkflowResult&amp;gt; ExecuteAsync(
        AIWorkflowContext context, 
        CancellationToken token = default)
    {
        var chatMessages = [new ChatMessage(ChatRole.User, context.Input)];

        var response = await _chatClient.GetResponseAsync(chatMessages, token);
        context.Intent = response.Result.Trim();

        return AIWorkflowResult.Success(context);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a weather lookup tool:&lt;/p&gt;

&lt;p&gt;C#&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;textarea tabindex="-1" aria-hidden="true" readonly&amp;gt;public class WeatherLookupStep : IAIWorkflowStep
{
    private readonly IWeatherApi _weatherApi;

    public WeatherLookupStep(IWeatherApi weatherApi)
    {
        _weatherApi = weatherApi;
    }

    public async Task&amp;amp;lt;AIWorkflowResult&amp;amp;gt; ExecuteAsync(
        AIWorkflowContext context, 
        CancellationToken token = default)
    {        
        if (context.Intent != "GetWeather") return AIWorkflowResult.Skipped(context);

        var forecast = await _weatherApi.GetForecastAsync(context.Location, token);
        context.WeatherForecast = forecast;

        return AIWorkflowResult.Success(context);
    }
}&amp;lt;/textarea&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class WeatherLookupStep : IAIWorkflowStep
{
    private readonly IWeatherApi _weatherApi;

    public WeatherLookupStep(IWeatherApi weatherApi)
    {
        _weatherApi = weatherApi;
    }

    public async Task&amp;lt;AIWorkflowResult&amp;gt; ExecuteAsync(
        AIWorkflowContext context, 
        CancellationToken token = default)
    {        
        if (context.Intent != "GetWeather") return AIWorkflowResult.Skipped(context);

        var forecast = await _weatherApi.GetForecastAsync(context.Location, token);
        context.WeatherForecast = forecast;

        return AIWorkflowResult.Success(context);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Compose the Chain in DI Configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can build a list of &lt;code&gt;IAIWorkflowStep&lt;/code&gt;s, or for more advanced scenarios, use a pipeline builder:&lt;/p&gt;

&lt;p&gt;C#&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;textarea tabindex="-1" aria-hidden="true" readonly&amp;gt;services.AddTransient&amp;amp;lt;IAIWorkflowStep, IntentClassifierStep&amp;amp;gt;();
services.AddTransient&amp;amp;lt;IAIWorkflowStep, WeatherLookupStep&amp;amp;gt;();
services.AddTransient&amp;amp;lt;IAIWorkflowStep, ResponseGeneratorStep&amp;amp;gt;();&amp;lt;/textarea&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services.AddTransient&amp;lt;IAIWorkflowStep, IntentClassifierStep&amp;gt;();
services.AddTransient&amp;lt;IAIWorkflowStep, WeatherLookupStep&amp;gt;();
services.AddTransient&amp;lt;IAIWorkflowStep, ResponseGeneratorStep&amp;gt;();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, for true “Chain of Responsibility” style, have each step reference the next (constructor injection or factory):&lt;/p&gt;

&lt;p&gt;C#&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;textarea tabindex="-1" aria-hidden="true" readonly&amp;gt;// psuedo-code

services.AddTransient&amp;amp;lt;IIntentClassifier, IntentClassifierStep&amp;amp;gt;();
services.AddTransient&amp;amp;lt;IWeatherLookup&amp;amp;gt;(sp =&amp;amp;gt;
{
    var next = sp.GetRequiredService&amp;amp;lt;IResponseGenerator&amp;amp;gt;();
    return new WeatherLookupStep(next);
});

services.AddTransient&amp;amp;lt;IResponseGenerator, ResponseGeneratorStep&amp;amp;gt;();&amp;lt;/textarea&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// psuedo-code

services.AddTransient&amp;lt;IIntentClassifier, IntentClassifierStep&amp;gt;();
services.AddTransient&amp;lt;IWeatherLookup&amp;gt;(sp =&amp;gt;
{
    var next = sp.GetRequiredService&amp;lt;IResponseGenerator&amp;gt;();
    return new WeatherLookupStep(next);
});

services.AddTransient&amp;lt;IResponseGenerator, ResponseGeneratorStep&amp;gt;();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure matches the advanced DI-chain patterns seen in production .NET systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Orchestrating the Execution
&lt;/h3&gt;

&lt;p&gt;At runtime, either iterate through the list of steps or recursively call the next step.&lt;/p&gt;

&lt;p&gt;C#&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;textarea tabindex="-1" aria-hidden="true" readonly&amp;gt;public class ToolChainOrchestrator
{
    private readonly IEnumerable&amp;amp;lt;IAIWorkflowStep&amp;amp;gt; _steps;

    public ToolChainOrchestrator(IEnumerable&amp;amp;lt;IAIWorkflowStep&amp;amp;gt; steps)
    {
        _steps = steps;
    }

    public async Task&amp;amp;lt;AIWorkflowResult&amp;amp;gt; ExecuteAsync(string input, CancellationToken token = default)
    {
        var context = new AIWorkflowContext { Input = input };
        foreach(var step in _steps)
        {
            var result = await step.ExecuteAsync(context, token);
            if (result.IsFailure)
            {
                // Handle failure (log, abort, etc.)
                return result;
            }
        }

        return AIWorkflowResult.Success(context);
    }
}&amp;lt;/textarea&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class ToolChainOrchestrator
{
    private readonly IEnumerable&amp;lt;IAIWorkflowStep&amp;gt; _steps;

    public ToolChainOrchestrator(IEnumerable&amp;lt;IAIWorkflowStep&amp;gt; steps)
    {
        _steps = steps;
    }

    public async Task&amp;lt;AIWorkflowResult&amp;gt; ExecuteAsync(string input, CancellationToken token = default)
    {
        var context = new AIWorkflowContext { Input = input };
        foreach(var step in _steps)
        {
            var result = await step.ExecuteAsync(context, token);
            if (result.IsFailure)
            {
                // Handle failure (log, abort, etc.)
                return result;
            }
        }

        return AIWorkflowResult.Success(context);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tool Registration Patterns
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Explicit ordering:&lt;/strong&gt;  Ensures pipeline steps run in the correct sequence (use &lt;code&gt;IEnumerable&amp;lt;&amp;gt;&lt;/code&gt; registration order).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conditional execution:&lt;/strong&gt;  Allow steps to skip themselves using flags in the context, or based on prior results.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extensibility:&lt;/strong&gt;  Add new steps or tools without refactoring the pipeline or orchestrator.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tool Calling and Function Invocation with Microsoft.Extensions.AI
&lt;/h2&gt;

&lt;p&gt;The newest versions of Microsoft.Extensions.AI (ME.AI) provide first-class abstractions like &lt;code&gt;IChatClient&lt;/code&gt; and the ability to wire up function/tool calling directly into your pipelines, supporting LLMs from OpenAI, Azure, Ollama, and custom plugins.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Function Calling Works
&lt;/h3&gt;

&lt;p&gt;The mechanism centers around models and frameworks that support &lt;em&gt;function calling&lt;/em&gt;—structured invocations where an LLM is told it can select and call a function, passing JSON arguments described via JSON Schema. &lt;code&gt;Microsoft.Extensions.AI&lt;/code&gt; wiring allows both the LLM and your server to negotiate which tool should be invoked and when.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example: Registering Tool Functions
&lt;/h4&gt;

&lt;p&gt;C#&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;textarea tabindex="-1" aria-hidden="true" readonly&amp;gt;var builder = services.AddOpenAIChatClient("gpt-4o")
    .AddTool("GetWeather", async (parameters, ct) =&amp;amp;gt;
    {
        var location = parameters["location"].GetString();
        return await weatherApiClient.GetForecastAsync(location, ct);
    });&amp;lt;/textarea&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var builder = services.AddOpenAIChatClient("gpt-4o")
    .AddTool("GetWeather", async (parameters, ct) =&amp;gt;
    {
        var location = parameters["location"].GetString();
        return await weatherApiClient.GetForecastAsync(location, ct);
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Tools are described with signature and argument schema.&lt;/li&gt;
&lt;li&gt;The LLM can &lt;em&gt;choose&lt;/em&gt; when to call, and &lt;code&gt;Microsoft.Extensions.AI&lt;/code&gt; orchestrates invocation and result passing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Tool Invocation Orchestration
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fojbdozqr0r136eqh6qnp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fojbdozqr0r136eqh6qnp.png" alt="Tool Invocation Orchestration" width="800" height="274"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This mechanism naturally extends to multiple tools, with ME.AI acting as both protocol and execution layer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ Models must support function calling, and your orchestration must register tool/function schemas for the LLM to invoke.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Error Handling Strategies Across Chained Tools
&lt;/h2&gt;

&lt;p&gt;Multi-tool workflows can introduce new failure modes, and a robust AI-enabled application must coordinate error handling, graceful degradation, retries, and clear logging across tool boundaries to ensure seamless operation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common Error Handling Patterns
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Try/Catch in Every Tool Step&lt;/strong&gt; : Each workflow step should be prepared to catch and log its own exceptions, returning error context in the result.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;C#&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;textarea tabindex="-1" aria-hidden="true" readonly&amp;gt;public async Task&amp;amp;lt;AIWorkflowResult&amp;amp;gt; ExecuteAsync(…)
{
    try
    {
        // Tool logic goes here
    }
    catch(Exception ex)
    {
        // Optionally, log at step level
        return AIWorkflowResult.Failure("Weather lookup failed", ex);
    }
}&amp;lt;/textarea&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public async Task&amp;lt;AIWorkflowResult&amp;gt; ExecuteAsync(…)
{
    try
    {
        // Tool logic goes here
    }
    catch(Exception ex)
    {
        // Optionally, log at step level
        return AIWorkflowResult.Failure("Weather lookup failed", ex);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pipeline-Level Error Interception&lt;/strong&gt; : The orchestrator can wrap the entire pipeline with an error boundary (try/catch), ensuring that unhandled exceptions bubble up in a controlled and observable manner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Middleware Pattern for Cross-Cutting Concerns:&lt;/strong&gt;  &lt;code&gt;Microsoft.Extensions.AI&lt;/code&gt; allows for &lt;code&gt;UseXxx()&lt;/code&gt; middleware additions on your &lt;code&gt;IChatClientBuilder&lt;/code&gt;, similar to ASP.NET Core’s HTTP pipeline, e.g. for telemetry, retries, or timeouts:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;C#&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;textarea tabindex="-1" aria-hidden="true" readonly&amp;gt;builder.UseOpenTelemetry(loggerFactory)
       .UseRetryPolicy(policy);&amp;lt;/textarea&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;builder.UseOpenTelemetry(loggerFactory)
       .UseRetryPolicy(policy);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Unified Error/Result Object&lt;/strong&gt; : Define a common “Result” record or class that carries either value or error details, instead of raw exceptions.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;C#&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;textarea tabindex="-1" aria-hidden="true" readonly&amp;gt;public record AIWorkflowResult(
    bool Success, 
    string? FailureReason = null, 
    Exception? Exception = null, 
    AIWorkflowContext? Context = null);&amp;lt;/textarea&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public record AIWorkflowResult(
    bool Success, 
    string? FailureReason = null, 
    Exception? Exception = null, 
    AIWorkflowContext? Context = null);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Best Practices for Error Handling in Tool Chains
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fail Fast and Isolate:&lt;/strong&gt;  If a critical step fails, return promptly and don’t proceed to dependent steps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Surface Contextual Error Messages:&lt;/strong&gt;  Distinguish between user errors (bad input) and system errors (time-outs, network failures).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Centralize Logging and Telemetry:&lt;/strong&gt;  Prefer structured logs with clear step boundaries and correlation IDs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fallback Paths:&lt;/strong&gt;  For some steps, return a default value or send an apology message instead of complete failure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do not silently swallow errors&lt;/strong&gt; : Partial failures should be traceable and observable, especially in complex orchestration scenarios.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Debugging Multi-Tool Workflows
&lt;/h2&gt;

&lt;p&gt;Debugging multi-tool workflows is a challenge: errors can propagate, data can transform across steps, and failures may occur in “foreign” code (e.g., LLM output, external API responses). You can streamline and optimize this process for efficiency by leveraging the .NET ecosystem and AI-powered tooling.&lt;/p&gt;

&lt;h3&gt;
  
  
  End-to-End Logging and Correlation
&lt;/h3&gt;

&lt;p&gt;Instrument your pipeline to capture input, output, and exceptions at each tool boundary. Use correlation IDs, structured logs (&lt;code&gt;ILogger&amp;lt;T&amp;gt;&lt;/code&gt;), and telemetry to make traces easy to filter and analyze.&lt;/p&gt;

&lt;p&gt;C#&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;textarea tabindex="-1" aria-hidden="true" readonly&amp;gt;public class LoggingAIWorkflowStep : IAIWorkflowStep
{
    private readonly IAIWorkflowStep _inner;
    private readonly ILogger&amp;amp;lt;LoggingAIWorkflowStep&amp;amp;gt; _logger;

    public async Task&amp;amp;lt;AIWorkflowResult&amp;amp;gt; ExecuteAsync(AIWorkflowContext ctx, CancellationToken token)
    {
        _logger.LogDebug("Executing {Step} with context: {Context}", _inner.GetType().Name, ctx);

        var result = await _inner.ExecuteAsync(ctx, token);
        if (!result.Success)
        {
            _logger.LogError(
                "Step {Step} failed: {Reason}", 
                _inner.GetType().Name, result.FailureReason);
        }

        return result;
    }
}&amp;lt;/textarea&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class LoggingAIWorkflowStep : IAIWorkflowStep
{
    private readonly IAIWorkflowStep _inner;
    private readonly ILogger&amp;lt;LoggingAIWorkflowStep&amp;gt; _logger;

    public async Task&amp;lt;AIWorkflowResult&amp;gt; ExecuteAsync(AIWorkflowContext ctx, CancellationToken token)
    {
        _logger.LogDebug("Executing {Step} with context: {Context}", _inner.GetType().Name, ctx);

        var result = await _inner.ExecuteAsync(ctx, token);
        if (!result.Success)
        {
            _logger.LogError(
                "Step {Step} failed: {Reason}", 
                _inner.GetType().Name, result.FailureReason);
        }

        return result;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Stepwise Pipeline Testing
&lt;/h3&gt;

&lt;p&gt;Write unit tests and integration tests for each tool separately. Then, add scenario tests for common input flows. Use mocks/stubs for slow or external dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Pitfalls
&lt;/h2&gt;

&lt;p&gt;Not all tool chains are created equal. AI-powered workflows, particularly when utilizing LLMs, introduce new possibilities for subtle bugs and architectural vulnerabilities.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Treating Probabilistic LLMs as Deterministic Engines&lt;/strong&gt;
LLMs generate plausible, and not guaranteed results. Don’t use them for tasks requiring strict correctness (e.g., math, database queries) unless verifiable by another step.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unbounded Sequence Depth&lt;/strong&gt;
Overly long or deeply nested tool chains are hard to debug, maintain, and optimize. Keep chains focused, and favor composition over nesting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ignoring Error Handling at Boundaries&lt;/strong&gt;
Every external call (to LLM, API, DB) is a potential failure point.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Overfitting to a Specific Model or Provider&lt;/strong&gt;
Hardwiring tools tightly to OpenAI/GPT, for example, can create vendor lock-in. Favor abstractions such as Microsoft.Extensions.AI and dependency injection for portable, flexible code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measuring Success with Technology Metrics, Not Business Outcomes&lt;/strong&gt;
The ultimate metric is improved business value, not just the number of chained tools or response speed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Anti-Patterns to Watch For
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;All-in-One LLM&lt;/strong&gt;
Overloading a single model with all instructions and tools (monolith). Prefer clear separation of concerns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Precision Anti-Pattern&lt;/strong&gt;
Expecting LLM-based steps to provide mathematically precise answers on every run; instead, layer in validation and post-processing as needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Overuse of Side-Effects&lt;/strong&gt;
Tool steps should avoid mutating shared state unexpectedly; rely on explicit context passing and immutability where possible.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Orchestrating multi-tool workflows is essential for creating robust, scalable, and maintainable AI-powered .NET applications. By adopting the right mindset and focusing on modular pipelines, effective dependency injection, clear error boundaries, and comprehensive diagnostics, you can transform complex business challenges into manageable and testable code structures.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/dotnet/ai/microsoft-extensions-ai" rel="noopener noreferrer"&gt;Microsoft.Extensions.AI&lt;/a&gt; offers foundational abstractions and patterns for these workflows. Modern .NET practices, such as middleware, dependency injection, and logging, ensure that your code remains testable and adaptable for the future. It is important to avoid common pitfalls, such as assuming that large language models (LLMs) are infallible or creating monolithic structures. Instead, you should leverage workflows that can evolve alongside AI tools and requirements.&lt;/p&gt;

&lt;p&gt;As a next step, select a workflow relevant to your domain. Break it down into distinct tool stages. Connect these stages using dependency injection. Register the appropriate function calling schemas and implement end-to-end logging and visualization. Remember to iterate and refactor. Your AI features and your users will appreciate it.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://roxeem.com/2025/09/12/how-to-orchestrate-multi-tool-ai-workflows-in-net/" rel="noopener noreferrer"&gt;How to orchestrate multi-tool AI workflows in .NET&lt;/a&gt; first appeared on &lt;a href="https://roxeem.com" rel="noopener noreferrer"&gt;Roxeem&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>llm</category>
      <category>dotnet</category>
      <category>ai</category>
      <category>csharp</category>
    </item>
    <item>
      <title>The Practical .NET Guide to AI &amp; LLM: Introduction</title>
      <dc:creator>Roxeem</dc:creator>
      <pubDate>Thu, 04 Sep 2025 12:01:31 +0000</pubDate>
      <link>https://forem.com/roxeem/the-practical-net-guide-to-ai-llm-introduction-4p7k</link>
      <guid>https://forem.com/roxeem/the-practical-net-guide-to-ai-llm-introduction-4p7k</guid>
      <description>&lt;p&gt;&lt;a href="https://medium.com/@roxeem/the-practical-net-guide-to-ai-llm-introduction-2225b82684c6?source=rss-db55f1d22ba6------2" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwdckbkwh612cidpvx8ij.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Model-agnostic guide to integrating LLMs in .NET — patterns, DI, provider abstraction, and security best practices.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@roxeem/the-practical-net-guide-to-ai-llm-introduction-2225b82684c6?source=rss-db55f1d22ba6------2" rel="noopener noreferrer"&gt;Continue reading on Medium »&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>openai</category>
      <category>llm</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Blazor’s Secret Speed: Static Web Asset in .NET 10</title>
      <dc:creator>Roxeem</dc:creator>
      <pubDate>Mon, 01 Sep 2025 06:01:37 +0000</pubDate>
      <link>https://forem.com/roxeem/blazors-secret-speed-static-web-asset-in-net-10-2gj5</link>
      <guid>https://forem.com/roxeem/blazors-secret-speed-static-web-asset-in-net-10-2gj5</guid>
      <description>&lt;p&gt;&lt;a href="https://medium.com/@roxeem/stop-shipping-stale-blazor-bits-make-the-framework-script-fast-and-cache-proof-22ec9ff89809?source=rss-db55f1d22ba6------2" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffyxeo4sb9ufzjpu9olik.png" alt="Blazor’s Secret Speed: Static Web Asset in .NET 10" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this post, I’ll unpack what changed for the Blazor script in .NET 10, why it matters for performance and deploys, and exactly where you…&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@roxeem/stop-shipping-stale-blazor-bits-make-the-framework-script-fast-and-cache-proof-22ec9ff89809?source=rss-db55f1d22ba6------2" rel="noopener noreferrer"&gt;Continue reading on Medium »&lt;/a&gt;&lt;/p&gt;

</description>
      <category>performance</category>
      <category>blazor</category>
      <category>webperf</category>
      <category>aspnetcore</category>
    </item>
    <item>
      <title>C# 14 Will Change How You Code — Here’s Why</title>
      <dc:creator>Roxeem</dc:creator>
      <pubDate>Sat, 30 Aug 2025 17:22:14 +0000</pubDate>
      <link>https://forem.com/roxeem/c-14-will-change-how-you-code-heres-why-5e7j</link>
      <guid>https://forem.com/roxeem/c-14-will-change-how-you-code-heres-why-5e7j</guid>
      <description>&lt;p&gt;&lt;a href="https://medium.com/@roxeem/c-14-will-change-how-you-code-heres-why-e72f1ac2f006?source=rss-db55f1d22ba6------2" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcmi5lu94babpfyge4pxy.png" alt="C# 14 Will Change How You Code — Here’s Why" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;C# 14 ships with .NET 10 and brings a set of focused, developer‑friendly improvements: extension members, null‑conditional assignment…&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@roxeem/c-14-will-change-how-you-code-heres-why-e72f1ac2f006?source=rss-db55f1d22ba6------2" rel="noopener noreferrer"&gt;Continue reading on Medium »&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>roslyn</category>
      <category>net10</category>
      <category>languagefeature</category>
    </item>
    <item>
      <title>The Union Revolution in C#: Closed Hierarchies, Case Declarations, and Safer APIs</title>
      <dc:creator>Roxeem</dc:creator>
      <pubDate>Thu, 28 Aug 2025 17:42:09 +0000</pubDate>
      <link>https://forem.com/roxeem/the-union-revolution-in-c-closed-hierarchies-case-declarations-and-safer-apis-42fk</link>
      <guid>https://forem.com/roxeem/the-union-revolution-in-c-closed-hierarchies-case-declarations-and-safer-apis-42fk</guid>
      <description>&lt;p&gt;&lt;a href="https://medium.com/@roxeem/the-union-revolution-in-c-closed-hierarchies-case-declarations-and-safer-apis-8cc52f225def?source=rss-db55f1d22ba6------2" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftbfessohuqfhz4z5rq6p.png" alt="The Union Revolution in C#: Closed Hierarchies, Case Declarations, and Safer APIs" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’ve ever wished your C# code could precisely model “one of these types, but never anything else”, discriminated unions are about to…&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@roxeem/the-union-revolution-in-c-closed-hierarchies-case-declarations-and-safer-apis-8cc52f225def?source=rss-db55f1d22ba6------2" rel="noopener noreferrer"&gt;Continue reading on Medium »&lt;/a&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>discriminatedunion</category>
      <category>patternmatching</category>
      <category>languagefeature</category>
    </item>
    <item>
      <title>.NET 10 Deep Dive: Ad‑hoc tools, the new dnx runner, and File‑based apps superpowers</title>
      <dc:creator>Roxeem</dc:creator>
      <pubDate>Mon, 25 Aug 2025 05:01:37 +0000</pubDate>
      <link>https://forem.com/roxeem/net-10-deep-dive-ad-hoc-tools-the-new-dnx-runner-and-file-based-apps-superpowers-1dph</link>
      <guid>https://forem.com/roxeem/net-10-deep-dive-ad-hoc-tools-the-new-dnx-runner-and-file-based-apps-superpowers-1dph</guid>
      <description>&lt;p&gt;&lt;a href="https://medium.com/@roxeem/net-10-deep-dive-ad-hoc-tools-the-new-dnx-runner-and-file-based-apps-superpowers-1e18d1f89dc8?source=rss-db55f1d22ba6------2" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa6yurmiewa7pzrbfzigl.png" alt=".NET 10 Deep Dive: Ad‑hoc tools, the new dnx runner, and File‑based apps superpowers" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In .NET 10, the CLI picked up a trio of quality‑of‑life upgrades that remove friction from everyday dev and CI workflows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@roxeem/net-10-deep-dive-ad-hoc-tools-the-new-dnx-runner-and-file-based-apps-superpowers-1e18d1f89dc8?source=rss-db55f1d22ba6------2" rel="noopener noreferrer"&gt;Continue reading on Medium »&lt;/a&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dnx</category>
      <category>dotnet10</category>
      <category>dotnettool</category>
    </item>
  </channel>
</rss>
