Dependency Injection & Agent Framework

In the previous blog posts we ported a Python implementation of an agentic application to C# and Microsoft Agent Framework. We used interfaces, but we did not use Dependency Injection (DI). It is pretty easy to add.

Agents, tools, executors and workflows all depend on interfaces, and DI depends on registering the relationship between these interfaces and the concrete class that implements them.

For example, here is how you create a workflow

services.AddSingleton<Workflow>(sp =>
{
    var blogger = sp.GetRequiredService<BloggerExecutor>();
    var researcher = sp.GetRequiredService<ResearcherExecutor>();
    var author = sp.GetRequiredService<AuthorExecutor>();
    var reviewer = sp.GetRequiredService<ReviewerExecutor>();

    return new WorkflowBuilder(blogger)
        .AddEdge(blogger, researcher)
        .AddEdge(researcher, author)
        .AddEdge(author, reviewer)
        .AddEdge<ResearchState>(reviewer, author, s => s?.NeedsRevision == true)
        .WithOutputFrom(reviewer)
        .Build();
});
Continue reading
Posted in AI, C#, Essentials, Programming | Leave a comment

Migrating Agentic Code Python -> C# Part 6 (final)

Note, the complete source code for the .NET version of this demo application is now available at https://github.com/JesseLiberty/blogMigration—public

In the previous post we finished up creating our agents. You’ll remember that each of the agents declared nodes. We’re finally going to put them to use in a class BlogWorkflow. However, up to now we’ve not fully taken advantage of the Microsoft Agent Framework (MAF). Let’s fix that up first. To do so we’ll add a class BlogExecutors. We’ll create MAF workflow executors that will wrap existing “node” chains so that the business logic is reused unchanged.


Let’s start with the BloggerExecutor which will allow the blogger to plan the task and seed the sub-task:

using Microsoft.Agents.AI.Workflows;

namespace BlogMigration;

/// <summary>Entry executor: lets the blogger plan the task and seed the sub-task.</summary>
internal sealed partial class BloggerExecutor(IBloggerChain blogger) : Executor("Blogger")
{
    [MessageHandler]
    private async ValueTask<ResearchState> HandleAsync(ResearchState state, IWorkflowContext context)
        => await blogger.BloggerNodeAsync(state);
}

An executor is a node in Microsoft Agent Framework. Each executor can receive messages, process them and emit new messages. In this case, whenever the workflow engine routes a ResearchState message to the executor, this method is invoked. The method takes the current workflow state and the workflow context and returns the updated ResearchState.

The executor is just a thin wrapper around the Blogger agent. The handler calls the Blogger node and the node updates the state.

Continue reading
Posted in AI, C#, Programming | Leave a comment

.NET Live Turns the Tables

I had the great privilege of being interviewed about AI on .NET Live. You can find the video on YouTube.

Find my YouTube channel here.

Posted in AI | Leave a comment

Migrating Agentic Code Python -> C# Part 5

In the previous post we looked at implementing the Researcher in C#. In this, as promised, we’ll look at the Author and the Reviewer.

The Author is handed two objects when instantiated: the llm (an IChatClient object) and the chatOptions. Its primary method is InvokeAsync, which is passed the current ResearchState.

public class AuthorChain(IChatClient llm, ChatOptions chatOptions) : IAuthorChain
{
    public async Task<string> InvokeAsync(ResearchState state)
    {
        List<string> research = state.ResearchFindings;
        string researchText = research.Count > 0 ? string.Join("\n\n", research) : "No research available.";

Its expectation is that the state object will have research information from the Researcher. It creates its prompt based on the state and then sends that prompt, along with its options, to the llm. What it gets back is its first draft which it will pass to the Reviewer

Continue reading
Posted in AI, C#, Programming | Leave a comment

Migrating Agentic Code Python -> C# Part 4

In the previous blog post we looked at the Blogger (orchestrator) code in C#. Let’s move on to some of the other agents.

The Blogger invokes the Researcher, so let’s go there next.

Continue reading
Posted in AI, C#, Programming | Leave a comment

Migrating Agentic Code Python -> C# Part 3

In the previous blog post (Part 2) we began the migration by setting up the configuration. In this post, we’ll tackle the Blogger, which acts as an orchestrator for the agents.

In the python version of our program the blogger_prompt_template is in its own cell and fairly short. In the C# version we create a file Prompts.cs. The class is static and has a const string for each prompt. Let’s start with the Blogger prompt:

namespace BlogMigration;

public static class Prompts
{
    public const string BloggerPromptTemplate = """
You are a blogger managing a blog post creation workflow.

Current Task: {main_task}

Current State:
- Research Findings: {research_findings}
- Blog Draft: {draft}
- Reviewer Feedback: {review_notes}
- Revision Number: {revision_number}

Your goal is to ensure a clear, engaging, and valuable blog post targeted at software developers.

Decide the next step and respond only with a JSON object (no extra text):
{
    "next_step": "researcher" or "author" or "END",
  "task_description": "Brief description of what needs to be done next"
}

Decision Rules:
- If no research exists, choose "researcher"
- If research exists but no draft, choose "author"
- If draft exists and reviewer says "APPROVED", choose "END"
- If draft needs revision, choose "author"
- If revision_number >= 4, choose "END"
""";

The triple quotes in a C# string create a raw string literal, introduced in C# 11. With this no escaping is needed and the string can be multi-line. There’s more to it, and I’ll refer you to the C# documentation.

Continue reading
Posted in AI, C#, Programming | Leave a comment

Migrating Agentic Code Python -> C# Part 2

In Part 1 of this multi-part series, I laid out my goal to migrate the Python agentics program from the previous series to C#. To do this migration I’m going to work my way down through my Python script and refactor it breaking out classes and refactoring to use Microsoft Agent Framework.

Note: to make sense of this code, you’ll want to start with the Python example. The code for that begins here.

We begin with bringing in the config.json file. We’ll use the identical file, and bring it into Program.cs

const string fileName = "config.json";

using var stream = File.OpenRead(fileName);
using var document = JsonDocument.Parse(stream);
JsonElement config = document.RootElement;

string? GetValue(string key) =>
    config.TryGetProperty(key, out JsonElement value) ? value.GetString() : null;

Environment.SetEnvironmentVariable("OPENAI_API_KEY", GetValue("API_KEY"));
Environment.SetEnvironmentVariable("OPENAI_BASE_URL", GetValue("OPENAI_API_BASE"));
Environment.SetEnvironmentVariable("TAVILY_API_KEY", GetValue("TAVILY_API_KEY"));

string modelName = "gpt-4o-mini";

var openAIClient = new OpenAIClient(
    new ApiKeyCredential(Environment.GetEnvironmentVariable("OPENAI_API_KEY")!),
    new OpenAIClientOptions
    {
        Endpoint = new Uri(Environment.GetEnvironmentVariable("OPENAI_BASE_URL")!)
    });
Continue reading
Posted in AI, C#, Essentials, Programming | Leave a comment

Migrating Agentic Code Python -> C# Part 1

In the last 5 posts we created an agentic application using Python. Let’s migrate that to C#.

Here’s the set of files we’ll create:

And here is the output after running it as a test using the prompt Use of multi-agents in writing a C# application:

Continue reading
Posted in AI, C#, Programming, Python | Leave a comment

Creating a multi-agent application – Part 5 (final)

In part 4 of this series we created our final two agents. In this final part of the series we’ll review the workflow that we create with the StateGraph class of LangGraph.

Continue reading
Posted in AI, Programming, Python | Leave a comment

Creating a multi-agent application – Part 4

In part 3 we looked at creating the researcher. As promised, today we’ll look at the author.

You’ll notice in the following code a great deal of similarity to what we’ve seen before. The goal is to create a code “template” that we can follow as we create any agent; departing only for the agent’s special requirements and abilities.

As usual, we start with the factory method:

def create_author_chain():
    """Creates the author chain."""
    def author_invoke(state):
        research = state.get("research_findings", [])
        research_text = "\n\n".join(research) if research else "No research available."

        prompt = author_prompt_template.format(
            main_task=state.get("main_task", ""),
            research_findings=research_text,
            draft=state.get("draft", ""),
            review_notes=state.get("review_notes", "")
        )

        try:
            response = llm.invoke(prompt)
            content = response.content if hasattr(response, 'content') else str(response)
            return content if content else "Draft in progress..."
        except Exception as e:
            print(f"Author error: {e}")
            return "Error generating draft. Please try again."

    return author_invoke

# Creating a callable object
author_chain = create_author_chain()
Continue reading
Posted in Agents, AI, Programming, Python | Leave a comment

Creating a multi-agent application — Part 3

In the previous post, we examined how to load the libraries we need and how to create the Blogger agent. In this post, we’ll examine the Research agent. You’ll no doubt notice the pattern of defining the template, the agent and the node. This will carry through for all the agents we’ll create.

researcher_prompt_template = """You are a researcher for a technical blog 
focused on .NET and AI with examples in C# and Python

Research Topic: {task}

Your goal is to find relevant, up-to-date insights for developers. Focus on:
- Key trends, challenges, or innovations
- Real-world use cases
- Supporting data or quotes from credible sources
- Simple explanations
- Short code examples in C# or Python

Summarize your findings concisely.
"""

In this template we start by telling the researcher what role it will play. We then provide a goal and narrow that goal to a series of topics to focus on and how to present that data.

Continue reading
Posted in AI, Programming, Python | Leave a comment

Creating a multi-agent application. Part 2

In my previous post, I showed the output of a multi-agent application I wrote to create blog posts (not to worry, it is for demonstration purposes only). In this post, I will begin the process of working through the code, line by line.

This application is written in Python, in a Colab notebook, using (among other things) LangChain and LangGraph. To follow along you will need to obtain an API key from OpenAI and a key from Tavily.

If you are a C# programmer with little or no Python experience, don’t panic! Python is pretty readable, and I’ll explain any part that is potentially obscure or confusing.

This will be a multi-agent application. The agents we’ll create will be:

  • Blogger which will orchestrate the others
  • Researcher, which will search the web for relevant information
  • Author, which will write drafts of the blog post
  • Reviewer, which will evaluate the drafts and suggest improvements

As a general rule, I try to limit the number of agents to 3-5. Any more than that can get terribly complicated with diminishing returns. Your mileage may vary.

Continue reading
Posted in Essentials | Leave a comment