Jump to content

Connect SuperML | Leeroopedia MCP: Equip your AI agents with best practices, code verification, and debugging knowledge. Powered by Leeroo — building Organizational Superintelligence. Contact us at founders@leeroo.com.

Implementation:Microsoft Semantic kernel ChatHistoryAgentThread

From Leeroopedia

Overview

ChatHistoryAgentThread is the primary implementation of the AgentThread abstraction in Microsoft Semantic Kernel, providing in-memory conversation state management for agent interactions. It stores the sequence of messages exchanged between users and agents as a ChatHistory object, enabling multi-turn conversations where the agent can reference prior context.

Source file: dotnet/src/Agents/Core/ChatHistoryAgentThread.cs:L16-101

Principle page: Conversation Thread Management

Principle:Microsoft_Semantic_kernel_Conversation_Thread_Management

Code Reference

Class Definition

public class ChatHistoryAgentThread : AgentThread
{
    public ChatHistory ChatHistory { get; }

    public ChatHistoryAgentThread();
    public ChatHistoryAgentThread(IEnumerable<ChatMessageContent>? initialMessages);
}

Constructors

Constructor Description
ChatHistoryAgentThread() Creates a new empty thread with no prior messages.
ChatHistoryAgentThread(IEnumerable<ChatMessageContent>?) Creates a thread pre-populated with the specified messages, enabling conversation resumption or context injection.

Key Properties

Property Type Description
ChatHistory ChatHistory The underlying ordered collection of ChatMessageContent messages. Contains all user messages, assistant responses, system messages, and tool call results.
Id string A unique identifier for the thread instance, inherited from AgentThread.

Internal Behavior

When a ChatHistoryAgentThread is used in an InvokeAsync call:

  1. The agent reads the thread's ChatHistory to reconstruct the conversation so far.
  2. The agent prepends its Instructions as a system message.
  3. The new user message is appended to the history.
  4. The chat completion service is called with the full assembled history.
  5. The assistant's response is appended to the history.
  6. The updated thread is returned via response.Thread.

I/O Contract

Input (Construction)

new ChatHistoryAgentThread(initialMessages?)
├── initialMessages: IEnumerable<ChatMessageContent>?
│   ├── Each message has:
│   │   ├── Role: AuthorRole (User, Assistant, System, Tool)
│   │   └── Content: string (the message text)
│   └── null or omitted → empty thread
└── Returns: ChatHistoryAgentThread instance

Output (Thread State)

ChatHistoryAgentThread instance
├── ChatHistory: ChatHistory
│   ├── Contains all messages in chronological order
│   ├── Grows with each agent invocation
│   └── Accessible for inspection or serialization
├── Id: string → Unique thread identifier
└── Passable to agent.InvokeAsync(message, thread)

Thread as Return Value

After each invocation, the thread is accessible from the response:

AgentResponseItem<ChatMessageContent> response
└── response.Thread → AgentThread (the same or updated thread instance)
    └── Cast to ChatHistoryAgentThread to access ChatHistory

Usage Examples

Empty Thread (Implicit Creation via InvokeAsync)

// When no thread is provided, InvokeAsync creates one automatically
AgentThread? thread = null;

ChatMessageContent message = new(AuthorRole.User, "Hello!");
await foreach (var response in agent.InvokeAsync(message, thread))
{
    thread = response.Thread; // Capture the implicitly created thread
    Console.WriteLine(response.Message.Content);
}

// Thread now contains: [User: "Hello!", Assistant: "<response>"]

Explicit Empty Thread

// Explicitly create an empty thread
AgentThread thread = new ChatHistoryAgentThread();

ChatMessageContent message = new(AuthorRole.User, "What is the capital of France?");
await foreach (var response in agent.InvokeAsync(message, thread))
{
    thread = response.Thread;
    Console.WriteLine(response.Message.Content);
}

Pre-Seeded Thread for Context Injection

AgentThread thread = new ChatHistoryAgentThread([
    new ChatMessageContent(AuthorRole.User, "Tell me a joke."),
    new ChatMessageContent(AuthorRole.Assistant, "Why did the chicken cross the road? To get to the other side!"),
]);

// Agent knows about the prior exchange
ChatMessageContent message = new(AuthorRole.User, "That was terrible. Tell a better one.");
await foreach (var response in agent.InvokeAsync(message, thread))
{
    thread = response.Thread;
    Console.WriteLine(response.Message.Content);
}

Multi-Turn Conversation Loop

AgentThread? thread = null;

string[] userMessages = [
    "What is machine learning?",
    "How does it differ from traditional programming?",
    "Give me a simple example."
];

foreach (string userInput in userMessages)
{
    ChatMessageContent message = new(AuthorRole.User, userInput);
    await foreach (var response in agent.InvokeAsync(message, thread))
    {
        thread = response.Thread;
        Console.WriteLine($"Agent: {response.Message.Content}");
    }
}
// Thread now contains all 6 messages (3 user + 3 assistant)

Inspecting Thread History

if (thread is ChatHistoryAgentThread chatThread)
{
    foreach (var msg in chatThread.ChatHistory)
    {
        Console.WriteLine($"[{msg.Role}]: {msg.Content}");
    }
}

Related Pages

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment