Semantic Kernel(SK) Orchestration for Healthcare 💚 Administrative operations🩺

Sannidhi Siva
6 min readDec 29, 2023

--

As per Microsoft Learn “Semantic Kernel (SK), a versatile open-source SDK, simplifies the creation of agents capable of accessing your pre-existing code. With its impressive adaptability, Semantic Kernel is compatible with models from OpenAI, Azure OpenAI, Hugging Face, among others. Integrating your current C#, Python, and Java code with these models allows for the development of agents skilled in resolving inquiries and streamlining tasks”

A lightweight open-source SDK that allows you to orchestrate plugins (i.e., your existing code) with AI. With Semantic Kernel, you can leverage the same AI orchestration patterns that power Microsoft’s Copilots in your own app

We will explore how semantic kernel(SK) can help automate health care administrative operations.

Before delving deeper into Semantic Kernel, let’s explore prompt engineering.

Prompts are vital in guiding and managing the actions of Large Language Models (LLMs) AI. They function as input or requests that users can submit to obtain responses from a model.

Pre-requisites Nuget Packages:

Microsoft.SemanticKernel

 // Create a kernel builder
var builder = Kernel.CreateBuilder();

// Get environment variables for model configuration
var modelName = Environment.GetEnvironmentVariable("MODEL_NAME");
var modelEndpoint = Environment.GetEnvironmentVariable("MODEL_ENDPOINT");
var modelKey = Environment.GetEnvironmentVariable("MODEL_KEY");

// Check if environment variables are set
if (modelName == null || modelEndpoint == null || modelKey == null)
{
Console.WriteLine("Please set the MODEL_NAME, MODEL_ENDPOINT, and MODEL_KEY environment variables.");
return;
}

// Add Azure OpenAI Chat Completion service to the kernel builder
builder.Services.AddAzureOpenAIChatCompletion(modelName, modelEndpoint, modelKey);

// Build the kernel
var kernel = builder.Build();

// Read user request from console
Console.Write("Your request: ");

string request = Console.ReadLine();

// Create the first prompt to detect the intent
string prompt = $"What is the intent of this request? {request}";

// Invoke the kernel with the first prompt and print the result
Console.WriteLine(await kernel.InvokePromptAsync(prompt));

// Create the second prompt with instructions and choices
prompt = @$"Instructions: What is the intent of this request?
Choices: CreateAppointment, MedicalProcedure, Emergency, UpdateAppointment.
User Input: {request}
Intent: ";

// Invoke the kernel with the second prompt and print the result
Console.WriteLine(await kernel.InvokePromptAsync(prompt));


// output
// Your request: fever
// The intent of this request could be seeking information or remedies for fever, reporting a symptom of fever or getting a temperature check for fever. Without more context, it's hard to determine the exact intent.
// Emergency

Creating templates for your prompts

As you can see above prompts lacks reusability due to the hard-coded options. We could generate the prompts string dynamically, but a more efficient approach would be to utilize prompt templates for enhanced reusability.

Let's create HealthCare Assistant Template for chat conversation

 // Chat Message class for storing chat history  
public class ChatMessage
{
// Role of the message sender
public required string Role {get; set;}

// Message content
public required string Content {get; set;}
}

// Create a Semantic Kernel template for healthcare chat
var healthcareChat = kernel.CreateFunctionFromPrompt(
@"{{$history}}
User: {{$request}}
HealthcareAssistant: "
);

The updated prompt employs the request and history variables, enabling us to incorporate these values while executing our prompt.

To evaluate the prompt, we can establish a new kernel and a chat loop, facilitating a continuous conversation with our AI agent.

Upon invoking the prompt, we can provide the request and history variables as arguments.

// Create chat history  
List<ChatMessage> history = [];

// Start the chat loop
while (true)
{
// Get user input
Console.Write("User > ");
var request = Console.ReadLine();
// Get chat response
var chatResult = kernel.InvokeStreamingAsync<StreamingChatMessageContent>(
healthcareChat,
new()
{
{ "request", request },
{ "history", string.Join("\n", history.Select(x => x.Role + ": " + x.Content)) }
}
);
// Stream the response
string message = "";
await foreach (var chunk in chatResult)
{
if (chunk.Role.HasValue) Console.Write(chunk.Role + " > ");
message += chunk;
Console.Write(chunk);
}
Console.WriteLine();

// Append user message to history
history.Add(new ChatMessage { Role = "User", Content = request });

// Append assistant message to history
history.Add(new ChatMessage { Role = "HealthcareAssistant", Content = message });
}

Using the Handlebars template engine

Nuget Package:Microsoft.SemanticKernel.PromptTemplates.Handlebars

 // Construct handlebars template for intent in healthcare context
var getHealthcareIntent = kernel.CreateFunctionFromPrompt(
new()
{
Template = @"
<message role=""system"">Instructions: Identify the intent of this request in the healthcare context.
Do not provide reasoning, just state the intent. If uncertain, respond with {{choices[0]}}.
Choices: {{choices}}.</message>

{{#each fewShotExamples}}
{{#each this}}
<message role=""{{role}}"">{{content}}</message>
{{/each}}
{{/each}}

{{#each chatHistory}}
<message role=""{{role}}"">{{content}}</message>
{{/each}}

<message role=""user"">{{request}}</message>
<message role=""system"">Intent:</message>",
TemplateFormat = "handlebars"
},
new HandlebarsPromptTemplateFactory()
);

// Create choices
List<string> choices = ["ContinueAIAssistance", "SeekUrgentMedicalHelp"];

// Create few-shot examples
List<ChatHistory> fewShotExamples = [
[
new ChatMessageContent(AuthorRole.User, "Can you help me understand the side effects of this medication?"),
new ChatMessageContent(AuthorRole.System, "Intent:"),
new ChatMessageContent(AuthorRole.Assistant, "ContinueAIAssistance")
],
[
new ChatMessageContent(AuthorRole.User, "I'm experiencing severe chest pain, what should I do?"),
new ChatMessageContent(AuthorRole.System, "Intent:"),
new ChatMessageContent(AuthorRole.Assistant, "SeekUrgentMedicalHelp")
]
];
    // Invoke prompt
var intent = await kernel.InvokeAsync(
getHealthcareIntent,
new() {
{ "request", request },
{ "choices", choices },
{ "history", history },
{ "fewShotExamples", fewShotExamples }
}
);

Loading prompts from file

// Load prompts
var prompts = kernel.CreatePluginFromPromptDirectory("HealthPromptsDirectory");

Agents

An agent signifies an AI solution capable of responding to inquiries and automating procedures for users. The range of agents extends from basic chatbots to highly advanced AI assistants.

By utilizing Semantic Kernel, we furnish you with the means to create progressively intricate agents without the necessity for profound AI knowledge.

For example, in the healthcare domain, you can develop an AI assistant to help patients schedule appointments, answer frequently asked questions, and provide medical advice based on the given symptoms.

Healthcare Appointment Scheduler with Semantic Kernel

Healthcare Appointment Planner

 public class HealthcareAppointmentPlanner
{
[KernelFunction]
[Description ("Returns the necessary steps to schedule appointment")]
[return: Description ("The list of steps needed to schedule appointment")]
public async Task<string> GenerateRequiredStepsAsync(
Kernel kernel,
[Description("A brief description of the user's healthcare concern")] string healthConcern,
[Description("The user's preferred date and time for the appointment")] string preferredDateTime,
[Description("The proximity of the healthcare provider to the user")] string providerProximity
)
{
// Generate a list of steps to complete the task
var steps = await GetAppointmentStepsAsync(kernel, healthConcern, preferredDateTime, providerProximity);
// Return the plan back to the agent
return steps;
}

/// <summary>
/// Retrieves the appointment steps asynchronously.
/// </summary>
/// <param name="kernel">Kernel function parameter.</param>
/// <param name="healthConcern">A brief description of the user's healthcare concern.</param>
/// <param name="preferredDateTime">The user's preferred date and time for the appointment.</param>
/// <param name="providerProximity">The proximity of the healthcare provider to the user.</param>
/// <returns>A string containing the list of steps needed to schedule the appointment.</returns>
private async Task<string> GetAppointmentStepsAsync(Kernel kernel, string healthConcern, string preferredDateTime, string providerProximity)
{
var result = await kernel.InvokePromptAsync($"""
I'm going to schedule a healthcare appointment for a user with a concern about {healthConcern}, preferred date and time {preferredDateTime}, and provider proximity {providerProximity}.
Before I do that, can you succinctly recommend the top 3 steps I should take in a numbered list?
I want to make sure I don't miss any important factors that would help my user get the most suitable appointment.
""", new() {
{ "healthConcern", healthConcern },
{ "preferredDateTime", preferredDateTime },
{ "providerProximity", providerProximity }
});

return result.ToString();
}

}

HealthCare Appointment Plugin

/// <summary>
/// HealthCare Appointment Plugin
/// </summary>
public class HealthCareAppointmentPlugin
{
[KernelFunction]
[Description("schedule appointment")]
public async Task ScheduleAppointment(
Kernel kernel,
[Description("A brief description of the user's healthcare concern")] string healthConcern,
[Description("The user's preferred date and time for the appointment")] string preferredDateTime,
[Description("The proximity of the healthcare provider to the user")] string providerProximity
)
{
// Logic for scheduling an appointment would go here healthCocern, preferredDateTime, providerProximity
// For now, we'll just output a user inputs to the console
Console.WriteLine(healthConcern, preferredDateTime, providerProximity);
}
}

To proceed, you need to add Plugins and Planner to the builder by registering them.

 // Add the healthcare appointment planner plugin to the kernel builder
builder.Plugins.AddFromType<HealthcareAppointmentPlanner>();
// Add the healthcare appointment plugin to the kernel builder
builder.Plugins.AddFromType<HealthCareAppointmentPlugin>();
 // ToolCallBehavior.AutoInvokeKernelFunctions will automatically invoke any kernel functions
OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new()
{
ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
};

Note: Please use the latest version of OpenAI Models which will support function calling.

var result = chatCompletionService.GetStreamingChatMessageContentsAsync(
chatMessages,
executionSettings: openAIPromptExecutionSettings,
kernel: kernel);
Healthcare schedule appointment with Planner and Plugin for Virtual Scheduler Persona

Github:

sannidhisiva/SK.HealthCare (github.com)

Help links:

Building agents and copilots with Semantic Kernel | Microsoft Learn

Why use Semantic Kernel? (linkedin.com)

--

--