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 readingIn 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 readingIn 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"
""";
Continue readingThe 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.
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 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 readingIn 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 readingIn 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 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 readingIn 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:
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 readingThe following text was created by a multi-agent application designed to create blog posts. In my next post we’ll take the application apart, step by step. For now, here is a test run with the prompt Use of multiagents in writing a C# application.”
Draft created: 2653 characters
{‘author’: {‘draft’: ‘# Harnessing Multi-Agent Systems in C# Applications\n’
‘\n’
‘In the evolving landscape of software development, ‘
‘multi-agent systems (MAS) have emerged as a powerful ‘
‘paradigm, particularly in enhancing the functionality of ‘
‘applications. However, the integration of these systems ‘
‘into C# applications comes with its own set of ‘
‘challenges and considerations. This post explores the ‘
‘key aspects of implementing multi-agent systems in C#, ‘
‘drawing from recent research findings.\n’
‘\n’
‘## Understanding Multi-Agent Systems\n’
‘\n’
‘At its core, a multi-agent system consists of multiple ‘
‘autonomous agents that interact with one another to ‘
‘achieve specific goals. These agents can be designed to ‘
‘perform tasks collaboratively, leading to improved ‘
‘efficiency and problem-solving capabilities. However, as ‘
‘highlighted by Elliot One, simply increasing the number ‘
‘of agents does not guarantee better outcomes. In fact, ‘
‘it can complicate the debugging process, making it more ‘
‘difficult to trace failures and understand system ‘
‘behavior. This underscores the importance of thoughtful ‘
‘design and implementation when developing multi-agent ‘
‘systems.\n’
‘\n’
In the previous post, we looked at the use of Chain of Thought (CoT) reasoning in the context of LLMs. For an LLM to take action in the world, however, it needs agents. The paradigm for this is called ReAct—that is, REason and ACT.
In order to interact with the world, the agent will use tools (such as code that accesses APIs, searches the Internet, etc.). This creates a dynamic cycle:

Think—the LLM reasons and decides what tool to use
ACT—the LLM uses the tool to take action in the world
Observe—the LLM observes the result of the action and adjusts accordingly, refining its plan
The cycle ends when the LLM has its final answer.
Continue readingUntil very recently, it was observed that LLMs had a very hard time with complex problems. Context was lost, memory of previous steps was distorted, and so forth. This led to unreliable results (hallucinations) and, consequently, to a lack of trust in the technology.

Recent research has shown that LLMs are, in fact, quite good at reasoning and planning if the problem is broken into a series of steps as a result of the right prompts. This reasoning and planning greatly improves the accuracy of the LLM’s output.
Continue readingA classic AI framework to define an agent’s task environment is PEAS. It stands for: