In the previous sections, we explored standard templates and the Liquid engine. However, when building high-performance, structured AI applications within the Semantic Kernel ecosystem, the Handlebars Prompt Template Factory is a standout choice. Handlebars offers a perfect balance: it is more powerful than basic templates but often more intuitive for developers coming from web development backgrounds.
Handlebars is widely loved for its "logic-less" philosophy, which actually makes prompts safer and cleaner. Its primary advantages include:
// See https://aka.ms/new-console-template for more information
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
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(
"gpt-4o",
apiKey)
.Build();
// Fix console encoding for emojis
Console.OutputEncoding = System.Text.Encoding.UTF8;
Console.WriteLine("=== ποΈ TechMart E-Commerce Support Chat ===\n");
Console.WriteLine("Welcome to TechMart Customer Support!");
Console.WriteLine("I'm your AI assistant, ready to help with your orders and questions.\n");
// 1. Customer Profile Setup
Console.Write("Please enter your first name: ");
var firstName = Console.ReadLine() ?? "Guest";
Console.Write("Select your membership tier (Gold/Silver/Standard): ");
var membershipInput = Console.ReadLine() ?? "Standard";
// Normalize membership to proper case
var membership = membershipInput.ToLower() switch
{
"gold" => "Gold",
"silver" => "Silver",
_ => "Standard"
};
Console.WriteLine($"\n⨠Welcome {firstName}! Your membership tier: {membership}");
Console.WriteLine("Type 'exit' to end the conversation.\n");
Console.WriteLine(new string('=', 80) + "\n");
// 2. Define customer data and prepare tier-specific text in C# (Simpler approach)
var totalSpent = membership == "Gold" ? 1250.50 : membership == "Silver" ? 450.00 : 125.00;
var currentDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm");
// Prepare tier instructions in C# to avoid complex Handlebars helpers
var tierInstructions = membership switch
{
"Gold" => "π VIP GOLD MEMBER - Priority: HIGHEST\n- Offer 10% discount code: GOLD10\n- Free express shipping on all orders\n- 24/7 priority support available\n- Tone: Extremely polite and grateful for loyalty",
"Silver" => "β¨ VALUED SILVER MEMBER - Priority: HIGH\n- Offer 5% shipping discount: SILVER5\n- Free shipping on orders over $50\n- Priority email support (2 hour reply)\n- Tone: Warm and appreciative",
_ => "π¦ STANDARD MEMBER - Priority: NORMAL\n- Welcome discount available\n- Standard shipping rates apply\n- Tone: Friendly and helpful"
};
var specialAlerts = "";
if (totalSpent > 1000)
{
specialAlerts += "π Customer spent over $1,000 - Offer loyalty rewards!\n";
}
var customer = new Dictionary<string, object>
{
["FirstName"] = firstName,
["LastName"] = "Customer",
["Email"] = $"{firstName.ToLower()}@email.com",
["Membership"] = membership,
["IsAccountActive"] = true,
["TotalSpent"] = totalSpent,
["JoinedDate"] = "2023-05-15",
["TierInstructions"] = tierInstructions
};
var orders = new List<Dictionary<string, object>>
{
new()
{
["Id"] = "ORD-101", ["Item"] = "Wireless Headphones", ["Status"] = "Delivered", ["Price"] = 120.00,
["DeliveryDate"] = "2026-01-15"
},
new()
{
["Id"] = "ORD-205", ["Item"] = "Mechanical Keyboard", ["Status"] = "In Transit", ["Price"] = 85.50,
["DeliveryDate"] = "2026-01-25"
},
new()
{
["Id"] = "ORD-309", ["Item"] = "USB-C Cable", ["Status"] = "Cancelled", ["Price"] = 15.00,
["DeliveryDate"] = "N/A"
}
};
if (orders.Count > 2)
{
specialAlerts += "π Frequent buyer - Show extra appreciation!";
}
// 3. Define the Handlebars System Prompt Template (Simplified syntax for Semantic Kernel)
var systemPromptTemplate = """
<message role="system">
You are a professional customer support assistant for TechMart E-Commerce.
Current Date: {{current_date}}
Support Hours: {{support_hours}}
# Customer Profile
- Name: {{user.FirstName}} {{user.LastName}}
- Email: {{user.Email}}
- Membership Tier: {{user.Membership}}
- Account Status: {{#if user.IsAccountActive}}Active{{else}}Inactive{{/if}}
- Total Spent: ${{user.TotalSpent}}
- Member Since: {{user.JoinedDate}}
# Tier Instructions
{{user.TierInstructions}}
# Order History ({{order_history.Count}} orders)
{{#each order_history}}
- Order {{Id}}: {{Item}} - {{Status}} (${{Price}})
{{/each}}
# Special Alerts
{{special_alerts}}
Instructions: Be concise, professional, and tier-appropriate. Help with orders, shipping, returns, and product questions.
</message>
""";
// 4. Configure Handlebars Prompt (Following reference code pattern)
var promptConfig = new PromptTemplateConfig
{
Name = "SupportSystemPrompt",
Template = systemPromptTemplate,
TemplateFormat = "handlebars", // Changed to handlebars
Description = "Handlebars prompt template for customer support with dynamic context",
InputVariables =
[
new() { Name = "user", AllowDangerouslySetContent = true },
new() { Name = "order_history", AllowDangerouslySetContent = true },
new() { Name = "support_hours" },
new() { Name = "current_date" }
]
};
// 5. Initialize Handlebars Factory (Following reference pattern)
var handlebarsFactory = new HandlebarsPromptTemplateFactory();
// 6. Create function from prompt config (Reference code approach)
var promptFunction = kernel.CreateFunctionFromPrompt(promptConfig, handlebarsFactory);
// 7. Prepare kernel arguments
var kernelArguments = new KernelArguments
{
{ "user", customer },
{ "order_history", orders },
{ "support_hours", "9 AM - 5 PM EST" },
{ "current_date", currentDate },
{ "special_alerts", specialAlerts }
};
// 8. Render and display system prompt (Using Handlebars template)
var template = handlebarsFactory.Create(promptConfig);
var systemPrompt = await template.RenderAsync(kernel, kernelArguments);
// Display the rendered system prompt
Console.WriteLine("=== π RENDERED HANDLEBARS PROMPT (Sent to AI) ===\n");
Console.WriteLine(systemPrompt);
Console.WriteLine("\n" + new string('=', 80) + "\n");
// 8. Initialize Chat History with rendered system prompt
var chat = kernel.GetRequiredService<IChatCompletionService>();
var history = new ChatHistory();
history.AddSystemMessage(systemPrompt);
// Add initial greeting
history.AddAssistantMessage($"Hello {firstName}! π I'm your TechMart support assistant. How can I help you today?");
Console.WriteLine($"Assistant: Hello {firstName}! π I'm your TechMart support assistant. How can I help you today?\n");
// 9. Configure execution settings
var executionSettings = new OpenAIPromptExecutionSettings
{
MaxTokens = 500,
Temperature = 0.7
};
// 10. Main conversation loop
while (true)
{
Console.Write($"{firstName} >>> ");
var userInput = Console.ReadLine();
if (string.IsNullOrWhiteSpace(userInput))
{
continue;
}
if (userInput.ToLower() == "exit")
{
Console.WriteLine("\nAssistant: Thank you for contacting TechMart! Have a great day! π");
break;
}
// Add user message to history
history.AddUserMessage(userInput);
// Get AI response with streaming
Console.Write("Assistant: ");
string fullMessage = string.Empty;
await foreach (var token in chat.GetStreamingChatMessageContentsAsync(history, executionSettings, kernel))
{
Console.Write(token.Content);
fullMessage += token.Content;
}
Console.WriteLine("\n");
// Add assistant response to history
history.AddAssistantMessage(fullMessage);
}
// 11. Display conversation summary
Console.WriteLine("\n" + new string('=', 80));
Console.WriteLine("=== π Conversation Summary ===\n");
Console.WriteLine($"Total messages: {history.Count}");
Console.WriteLine($"Customer: {customer["FirstName"]} ({customer["Membership"]} Member)");
Console.WriteLine($"Total spent: ${customer["TotalSpent"]}");
Console.WriteLine("\nThank you for using TechMart Support! ποΈ");
The power of Handlebars lies in its blocks.