Semantic Kernel Chat History Created: 19 Jan 2026 Updated: 19 Jan 2026

Implementing Stateful AI with Semantic Kernel Chat History

In modern AI applications, "memory" is what differentiates a static completion from a dynamic conversation. In the Semantic Kernel (SK) ecosystem, this memory is managed through the ChatHistory class. Below is a complete, production-ready example of a Health & Fitness Assistant that utilizes short-term memory and multi-modal (image) processing.

The Full Implementation

using System.ComponentModel;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;

// 1. Setup Kernel and OpenAI Connector
var apiKey = Environment.GetEnvironmentVariable("OPEN_AI_KEY");

if (string.IsNullOrEmpty(apiKey))
{
Console.WriteLine("Please set the OPEN_AI_KEY environment variable.");
return;
}

var kernel = Kernel.CreateBuilder()
.AddOpenAIChatCompletion(
modelId: "gpt-4o",
apiKey: apiKey)
.Build();

// 2. Configure Execution Settings (Tool calling and Temperature)
var executionSettings = new OpenAIPromptExecutionSettings
{
Temperature = 0.1,
ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
};

var chat = kernel.GetRequiredService<IChatCompletionService>();

// 3. Define the System Persona
var systemPrompt = """
You are a professional health and fitness assistant specializing in nutrition analysis.
When analyzing meal photos, provide:
1. Estimated calorie count
2. Macronutrients breakdown (protein, carbs, fats)
3. Health rating (1-10)
4. Brief recommendations for improvement

Keep responses concise and practical.
""";

// 4. Initialize Chat History with pre-seeded messages
List<ChatMessageContent> messages =
[
new(AuthorRole.System, systemPrompt),
new(AuthorRole.User, "I want to track my daily calorie intake. What's my total so far?"),
new(AuthorRole.Assistant,
"Currently, I don't have any meals logged. Please share a photo of your meal or describe what you've eaten today."),
];

var history = new ChatHistory(messages);

Console.WriteLine("=== Health & Fitness Assistant ===");
Console.WriteLine("Track your meals, analyze nutrition, and get fitness advice.");
Console.WriteLine("Commands:");
Console.WriteLine(" - Type 'meal' to analyze a sample meal photo");
Console.WriteLine(" - Ask about nutrition, calories, or fitness tips");
Console.WriteLine(" - Type 'exit' or press Enter to quit.\n");

// 5. Main Interaction Loop
while (true)
{
Console.Write(" User >>> ");
var prompt = Console.ReadLine();
if (string.IsNullOrEmpty(prompt) || prompt.ToLower() == "exit") break;

if (prompt.ToLower() == "meal")
{
// Adding multi-modal content (Text + Image) to history
history.AddMessage(AuthorRole.User,
[
new TextContent("Can you analyze this meal? Tell me the estimated calories, macronutrients, and if it's healthy."),
new ImageContent(new Uri("https://images.unsplash.com/photo-1546069901-ba9599a7e63c?w=800"))
]);

Console.WriteLine("\n[Analyzing meal photo...]");
}
else
{
history.AddUserMessage(prompt);
}

Console.Write(" Bot >>> ");
string fullMessage = string.Empty;

// 6. Streaming the AI response back to the console
await foreach (var token in chat.GetStreamingChatMessageContentsAsync(history, executionSettings, kernel))
{
Console.Write(token.Content);
fullMessage += token.Content;
}

Console.WriteLine("\n");

// 7. Critical Step: Save the Assistant's response to history to maintain context
history.AddAssistantMessage(fullMessage);
}

// 8. Display History Summary
Console.WriteLine("\n=== Chat History Summary ===");
foreach (var chatMessage in history)
{
Console.WriteLine($"\n{chatMessage.Role}:");

if (chatMessage.Items.Count > 1)
{
foreach (var item in chatMessage.Items)
{
if (item is TextContent textContent)
{
Console.WriteLine($" [Text] {textContent.Text}");
}
else if (item is ImageContent imageContent)
{
Console.WriteLine($" [Image] {imageContent.Uri}");
}
}
}
else
{
Console.WriteLine($" {chatMessage.Content}");
}
}

Architectural Breakdown

1. The Persona (System Role)

The systemPrompt is the most influential message in the history. It defines the constraints and the output format. By adding it to the ChatHistory first, every subsequent query is interpreted through this "Fitness Assistant" lens.

2. Multi-modal State

Notice the meal command logic. Unlike simple text-based chat, Semantic Kernel allows the User role to contain a collection of ChatMessageContentItem. Adding an ImageContent object to the history ensures that the LLM can reference the visual data in future turns (e.g., if the user later asks, "Was there too much oil in that salad?").

3. The Context Loop

The interaction follows a strict cycle:

  1. Append user input to history.
  2. Request completion using the entire history as context.
  3. Aggregate the streaming tokens into a full string.
  4. Append the final assistant response back to history.

4. History Iteration

At the end of the program, we iterate through the history. This demonstrates that ChatHistory is essentially a collection of ChatMessageContent. This is useful for auditing, logging, or saving the session to a database to resume later.

Share this lesson: