| |

Microsoft Agent Framework: Conversations and Threads

In an earlier blog post, we took an initial look at the Microsoft Agent Framework in terms of the components that form this new open-source SDK.

In this post, we focus on a key component that was mentioned – threads.

We see how the Microsoft Agent Framework’s helps you manage conversations using threads.

Specifically, the following is covered:

  • foundational concepts
  • creating a conversation without a thread
  • creating a conversation with a thread
  • how to serialise a thread
  • how to deserialise a thread
  • agents and multiple threads
  • threading support and implementation options

 

Demos and code examples are included.  A video demo is also available.

~

Foundational Concepts

Before jumping into some examples, a few important points to note when building agents with the agent framework.

State 

First, agents are stateless by default.  They don’t maintain sate between interactions.

To handle the back and forth between the agent and the human (or another agent) -and persist this conversation state, an object is required.

Agent Thread

This is where the AgentThread object comes into play.

The AgentThread object lets you save conversation state between calls and makes it easier for you to implement multi-turn conversations.

Content in an agent thread contains all messages that have been sent back and forth throughout the entire conversation.

You can see the contents of a typical agent thread here:

In the above screenshot, we can see a message between a user and the associated response from the agent.

Storing conversational information automatically in a thread means the agent can refer to previous input and response messages in the conversation when responding to new inputs.

~

An Example – Iron Mind AI

Some examples now follow.

For context, we’re creating an agent called Iron Mind AI.  This agent is a personal trainer that answers questions about health, fitness and with a focus on the 5×5 weight training exercise programme.

Example – Creating a Conversation Without a Thread

The most basic example.  An agent without a thread.  Implementing this means the agent has no real awareness of the conversation being sent back and forth between itself and the human.  Context is lost with each interaction.

static async Task Main(string[] args)
{
    AIAgent agent = new OpenAIClient(apiKey)
                  .GetChatClient(model)
                  .CreateAIAgent(instructions: "You are a personal trainer. " +
                                               "You can only answer questions related to health, fitness and 5x5 stronglift.",
                                 name: "IronMind AI");

    while (true)
    {
        Console.Write("You: ");
        string? input = Console.ReadLine();

        if (string.IsNullOrWhiteSpace(input) || input.Equals("exit", StringComparison.OrdinalIgnoreCase))
        {
            break;
        }

        UserChatMessage chatMessage = new(input);

        AsyncCollectionResult<StreamingChatCompletionUpdate> completionUpdates =
            agent.RunStreamingAsync([chatMessage]);

        Console.Write("IronMind AI: ");

        await foreach (StreamingChatCompletionUpdate completionUpdate in completionUpdates)
        {
            if (completionUpdate.ContentUpdate.Count > 0)
            {
                Console.Write(completionUpdate.ContentUpdate[0].Text);
            }
        }

        Console.WriteLine();
    }
}

 

In the above example, only the most recent message is sent, processes and received.

We can see the example output here:

Example – Creating a Conversation with a Thread

In this example, we extend the agent to work with a thread.

private static async Task RunChatLoopWithThreadAsync(AIAgent agent)
 {
     // Start a separate thread to handle user input
     AgentThread agentThread = agent.GetNewThread();
     
     while (true)
     {
         Console.Write("You: ");
         string? input = Console.ReadLine();

         if (string.IsNullOrWhiteSpace(input) || input.Equals("exit", StringComparison.OrdinalIgnoreCase))
         {
             break;
         }

         UserChatMessage chatMessage = new(input);

         AsyncCollectionResult<StreamingChatCompletionUpdate> completionUpdates =
             agent.RunStreamingAsync([chatMessage], agentThread);

         Console.Write("IronMind AI: ");

         await foreach (StreamingChatCompletionUpdate completionUpdate in completionUpdates)
         {
             if (completionUpdate.ContentUpdate.Count > 0)
             {
                 Console.Write(completionUpdate.ContentUpdate[0].Text);
             }
         }

         Console.WriteLine();
     }

 }

Any interactions between the huma and the agent (or another agent for that matter) are encapsulated within this thread.

The example output:

We can see the thread contents:

Serialising a Thread

Here, we extend the earlier example and serialise a thread to JSON:

private static async Task RunChatLoopWithoutWithThreadSerialiseAsync(AIAgent agent)
{
    // Start a separate thread to handle user input
    AgentThread agentThread = agent.GetNewThread();

    while (true)
    {
        Console.Write("You: ");
        string? input = Console.ReadLine();

        if (string.IsNullOrWhiteSpace(input) || input.Equals("exit", StringComparison.OrdinalIgnoreCase))
        {
            break;
        }

        UserChatMessage chatMessage = new(input);

        AsyncCollectionResult<StreamingChatCompletionUpdate> completionUpdates =
            agent.RunStreamingAsync([chatMessage], agentThread);

        Console.Write("IronMind AI: ");

        await foreach (StreamingChatCompletionUpdate completionUpdate in completionUpdates)
        {
            if (completionUpdate.ContentUpdate.Count > 0)
            {
                Console.Write(completionUpdate.ContentUpdate[0].Text);
            }
        }

        var serializedThreadElement = agentThread.Serialize();

        string serializedThread = JsonSerializer.Serialize(
            serializedThreadElement,
            new JsonSerializerOptions { WriteIndented = true });

        Console.WriteLine(serializedThread);

        Console.WriteLine();
    }

}

We get the typical response from the LLM:

With the thread serialised to JSON, it can be written to a file system, database or other storage device.

And here, we can see an export of the serialised thread:

{
  "storeState": {
    "messages": [
      {
        "role": "user",
        "contents": [
          {
            "$type": "text",
            "text": "what weights should I lift?"
          }
        ]
      },
      {
        "authorName": "IronMind AI",
        "createdAt": "2025-11-05T08:09:57\u002B00:00",
        "role": "assistant",
        "contents": [
          {
            "$type": "text",
            "text": "To determine the appropriate weights for your 5x5 StrongLifts program, start by following these steps:\n\n1. **Choose Your Starting Weight**: If you\u0027re a beginner, it\u0027s often recommended to start with just the barbell (usually 45 lbs/20 kg) for exercises like the squat, bench press, and deadlift, or slightly lighter for overhead presses. This allows you to focus on form.\n\n2. **Progression**: For each workout, aim to increase the weight by 5 lbs (2.5 kg) for upper body exercises and 10 lbs (5 kg) for lower body exercises if you successfully complete all 5 sets of 5 reps in the previous session.\n\n3. **Listening to Your Body**: If a weight feels too heavy or you\u0027re not able to complete your reps with good form, reduce the weight until you can comfortably perform the required sets and reps.\n\n4. **Use a 1RM Calculation**: If you have prior lifting experience, you can calculate a starting weight based on your one-rep max (1RM). A good general guideline for working weights is to use about 60% of your 1RM for your starting weights.\n\nAlways prioritize form over weight to avoid injury and maximize effectiveness. If you\u0027re unsure, consider consulting with a qualified personal trainer to evaluate your lifting form and provide personalized guidance."
          }
        ],
        "messageId": "chatcmpl-CYSsXaxwXBgr2IOdA0NpluXqA7nUL"
      }
    ]
  }
}

 

You might consider this in situations where you have an agent serving many users.

Serialise dormant conversations after a given timeout period, then rehydrate them when a specific action or event occurs.

Deserialise a Thread

You need a way to deserialise an agent thread.  The following code does this:

private static async Task RunChatLoopWithoutWithThreadSerialiseAsync(AIAgent agent)
{
    // Start a separate thread to handle user input
    AgentThread agentThread = agent.GetNewThread();

    while (true)
    {
        Console.Write("You: ");
        string? input = Console.ReadLine();

        if (string.IsNullOrWhiteSpace(input) || input.Equals("exit", StringComparison.OrdinalIgnoreCase))
        {
            break;
        }

        UserChatMessage chatMessage = new(input);

        AsyncCollectionResult<StreamingChatCompletionUpdate> completionUpdates =
            agent.RunStreamingAsync([chatMessage], agentThread);

        Console.Write("IronMind AI: ");

        await foreach (StreamingChatCompletionUpdate completionUpdate in completionUpdates)
        {
            if (completionUpdate.ContentUpdate.Count > 0)
            {
                Console.Write(completionUpdate.ContentUpdate[0].Text);
            }
        }

        var serializedThreadElement = agentThread.Serialize();

        string serializedThread = JsonSerializer.Serialize(
            serializedThreadElement,
            new JsonSerializerOptions { WriteIndented = true });

        Console.WriteLine(serializedThread);

  // Deserialize the thread state after loading from storage.
  AgentThread resumedThread = await agent.DeserializeThreadAsync(serializedThread)


        Console.WriteLine();
    }

}

The thread and associated context are now available for the user for use in the interaction.

~

Agents and Multiple Threads

One agent can support many threads.  This means you can create an agent to handle many users or many AI agents.   You implement this by creating a new thread for each instance you want to handle.

For example:

AgentThread thread1 = agent.GetNewThread();
AgentThread thread2 = agent.GetNewThread();

Console.WriteLine(await agent.RunAsync("What weights should I start with?.", thread1));

Console.WriteLine(await agent.RunAsync("How long will 5x5 take me to do?.", thread2));

One important thing to note is that you must use the same agent instance when serialising and deserialising threads.

~

Threading Support

Threading support differs across different services.  This means you must take this into consideration when creating you AI agent.

The following table details how threads are currently supported at the time of writing:

Foundry Agents Service managed persistent threads
OpenAI Responses Service managed persistent threads OR in-memory threads
OpenAI ChatCompletion In-memory threads
OpenAI Assistants Service managed threads
A2A Service managed threads

 

For initial implementations, in-memory threads are sufficient.  When you reach critical mass in terms of user volume, you will want to consider service managed persistent threads.

~

Demo – Iron Mind AI – Threads

In this demo, we see threads in action.  We see how to:

  • run an agent with a thread
  • run an agent without a thread
  • serialise a thread
  • serialise a thread

~

Summary

In this  blog post we introduced how conversations and threads and handled in the Microsoft Agent Framework.

We saw how to create a conversation with or without the thread.

We’ve seen how you can serialise and deserialise threads.  We also looked at how you can implement multiple threads per agent.

And finally, we looked at the support options you have for threads as well as a demo.

In our next blog post we’ll look at Agent Function Tools.  We’ll learn how to use these to extend our Iron Mind AI agents capabilities.

~

Enjoy what you’ve read, have questions about this content, or would like to see another topic? Drop me a note below.

You can schedule a call using my Calendly link to discuss consulting and development services.

~

Further Reading and Resources

You can find more blogs in this series here and other resources to help you on your agentic ai journey.

More from this series

Microsoft Learn

~

JOIN MY EXCLUSIVE EMAIL LIST
Get the latest content and code from the blog posts!
I respect your privacy. No spam. Ever.

Similar Posts