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")!)
    });

Next, we set up the Tavily search tool:

var tavilyHttpClient = new HttpClient { BaseAddress = new Uri("https://api.tavily.com/") };
tavilyHttpClient.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("TAVILY_API_KEY"));

AIFunction tavilyTool = AIFunctionFactory.Create(
    async (string query) =>
    {
        var request = new
        {
            query,
            max_results = 5,
            topic = "general",
            include_answer = false,
            include_raw_content = false,
            search_depth = "basic"
        };

        using HttpResponseMessage response = await tavilyHttpClient.PostAsJsonAsync("search", request);
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadAsStringAsync();
    },
    name: "tavily_search",
    description: "A search engine optimized for comprehensive, accurate, and trusted results.");

A little more verbose, but we’re doing a bit of extra work along the way. Let’s go down to where we create the ResearchState. To do that, we’ll create ResearchState.cs:

namespace BlogMigration;

/// <summary>State for the research workflow.</summary>
public class ResearchState
{
    public string MainTask { get; set; } = "";
    public List<string> ResearchFindings { get; set; } = [];
    public string Draft { get; set; } = "";
    public string ReviewNotes { get; set; } = "";
    public int RevisionNumber { get; set; }
    public string NextStep { get; set; } = "";
    public string CurrentSubTask { get; set; } = "";
}

In the next blog post we’ll create the first of our agents: Blogger. We’ll mimic BloggerChain, passing in the llm (IChatClient) and the chat options.

Unknown's avatar

About Jesse Liberty

** Note ** Jesse is currently looking for a new position. You can learn more about him at https://jesseliberty.bio Thank you. Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. His latest book, Building APIs with .NET, is now available wherever you buy your books. Liberty was a Team Lead and Senior Software Engineer for various corporations, a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a 13 year Microsoft MVP.
This entry was posted in AI, C#, Essentials, Programming. Bookmark the permalink.