The purpose of this article is to help you quickly grasp the key concepts in Semantic Kernel and get started quickly.
In Semantic Kernel Java, the builder pattern is extensively used. If you are not familiar with the builder pattern, I recommend you check out: Builder Design Pattern
All the code examples below are from samples/semantickernel-concepts/semantickernel-syntax-examples.
ChatCompletionService chatCompletionService = ChatCompletionService.builder()
.withOpenAIAsyncClient(client)
.withModelId("gpt-3.5-turbo-0613")
.withServiceId("fridayChatGeneration")
.build();-
Retrieve the AI Service from the Kernel
ChatCompletionService service = kernel.getService(ChatCompletionService.class);
-
Directly call
service.getChatMessageContentsAsyncto get the LLM responseChatCompletionService service = kernel.getService(ChatCompletionService.class); var chatHistory = new ChatHistory(systemMessage); chatHistory.addUserMessage(userMessage); var answer = service.getChatMessageContentsAsync(chatHistory, kernel, null).block();
The KernelBuilder is a builder used to create and configure a new Kernel with necessary services and plugins.
ChatCompletionService chatCompletionService = ChatCompletionService.builder()
.withOpenAIAsyncClient(client)
.withModelId("gpt-3.5-turbo-0613")
.withServiceId("fridayChatGeneration")
.build();
return Kernel.builder()
.withAIService(ChatCompletionService.class, chatCompletionService);A Kernel is created using the KernelBuilder, where various services and plugins are configured via withXXX().
Create a Kernel using KernelBuilder and configure the necessary parameters
Kernel kernel = Kernel.builder()
.withPlugin(myPlugin)
.withAIService(openAiChatService)
.withServiceSelector()
.build();-
Define a custom class
-
Construct using
KernelPluginFactory
public static class Time {
@DefineKernelFunction(name = "date")
public String date() {
System.out.println("date is called");
Date now = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
return dateFormat.format(now);
}
@DefineKernelFunction(name = "time")
public String time() {
System.out.println("time is called");
Date now = new Date();
SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
return timeFormat.format(now);
}
}
KernelPlugin time = KernelPluginFactory.createFromObject(new Time(), "time");A native function in Semantic Kernel performs precise tasks like data retrieval, time checks, and complex math, which large language models (LLMs) may make mistake. Native functions are written in code and ensure accuracy. In contrast, LLMs offer flexibility, generality, and creativity, excelling in generating and predicting text. Combining both leverages their respective strengths for optimal performance. For more details, refer to Microsoft Documentation on Kernel Functions.
Here’s an example of how to define a native kernel function:
public class TextPlugin {
@DefineKernelFunction(description = "Change all string chars to uppercase.", name = "Uppercase")
public String uppercase(@KernelFunctionParameter(description = "Text to uppercase", name = "input") String text) {
return text.toUpperCase(Locale.ROOT);
}
}To create a inline KernelFunction from a prompt, you can use either of the following methods, which are equivalent:
KernelFunctionFromPrompt.builder().withTemplate(promptTemplate).build();KernelFunction.createFromPrompt(message).build();
String promptTemplate = """
Generate a creative reason or excuse for the given event.
Be creative and be funny. Let your imagination run wild.
Event: I am running late.
Excuse: I was being held ransom by giraffe gangsters.
Event: I haven't been to the gym for a year
Excuse: I've been too busy training my pet dragon.
Event: {{$input}}
""".stripIndent();
var excuseFunction = KernelFunctionFromPrompt.builder()
.withTemplate(promptTemplate)
.withDefaultExecutionSettings(
PromptExecutionSettings.builder()
.withTemperature(0.4)
.withTopP(1)
.withMaxTokens(500)
.withUser("bx-h")
.build()
)
.withName("ExcuseGeneratorFunction")
.build();var message = "Translate this date " + date + " to French format";
var fixedFunction = KernelFunction
.createFromPrompt(message)
.withDefaultExecutionSettings(
PromptExecutionSettings.builder()
.withMaxTokens(100)
.build())
.withTemplateFormat(PromptTemplateConfig.SEMANTIC_KERNEL_TEMPLATE_FORMAT)
.withName("translator")
.build();The SEMANTIC_KERNEL_TEMPLATE_FORMAT corresponds to the 'semantic-kernel' rendering engine, which uses the syntax {{$variable}} for variables.
Another rendering engine is 'handlebars', which uses the syntax {{variable}}.
Here's an example of how to use both:
runPrompt(kernel,
"semantic-kernel",
"Hello AI, my name is {{$name}}. What is the origin of my name?",
templateFactory);
runPrompt(kernel,
"handlebars",
"Hello AI, my name is {{name}}. What is the origin of my name?",
templateFactory);The runPrompt method is defined as follows:
public static void runPrompt(Kernel kernel, String templateFormat, String prompt,
PromptTemplateFactory templateFactory) {
var function = new KernelFunctionFromPrompt.Builder<>()
.withTemplate(prompt)
.withTemplateFormat(templateFormat)
.withPromptTemplateFactory(templateFactory)
.build();
var arguments = KernelFunctionArguments.builder()
.withVariable("name", "Bob")
.build();
var result = kernel.invokeAsync(function).withArguments(arguments).block();
System.out.println(result.getResult());
}For more information, please refer to the following resources:
- Microsoft Documentation on Prompt Template Syntax
- Microsoft Devblogs on Using Handlebars Planner in Semantic Kernel
Define a function from a configuration file (json)
var prompt = "Hello AI, what can you do for me?";
String configPayload = """
{
"schema": 1,
"name": "HelloAI",
"description": "Say hello to an AI",
"type": "completion",
"completion": {
"max_tokens": 256,
"temperature": 0.5,
"top_p": 0.0,
"presence_penalty": 0.0,
"frequency_penalty": 0.0
}
}
""".stripIndent();
PromptTemplateConfig promptConfig = PromptTemplateConfig
.parseFromJson(configPayload)
.copy()
.withTemplate(prompt)
.build();
var func = KernelFunction
.createFromPrompt(promptConfig)
.build();KernelFunctionArguments.builder().withVariable("input", "Jupiter").build();This can also be done as:
KernelFunctionArguments.builder().withInput("Jupiter").build();- Direct call:
TextPlugin text = new TextPlugin();
return text.uppercase("ciao!");- Invoke via
Kernel.invokeAsync(KernelFunction)
Kernel kernel = Kernel.builder().build();
KernelPlugin kernelPlugin = KernelPluginFactory.createFromObject(new StaticTextPlugin(), "text");
KernelFunctionArguments arguments = KernelFunctionArguments.builder()
.withVariable("input", "Today is: ")
.withVariable("day", "Monday")
.build();
return kernel.invokeAsync(kernelPlugin.get("AppendDay"))
.withArguments(arguments)
.block()
.getResult();OR:
var result = kernel
.invokeAsync(excuseFunction)
.withArguments(
KernelFunctionArguments.builder()
.withInput("I missed the F1 final race")
.build()
)
.block();The purpose of a prompt template is to:
- Render the prompt
- Be passed as a parameter to
createFromPrompt()to constructKernelFunction
-
Define the prompt template
-
Create using
KernelPromptTemplateFactory()
String functionDefinition = """
Today is: {{time.date}}
Current time is: {{time.time}}
Answer the following questions using JSON syntax, including the data used.
Is it morning, afternoon, evening, or night (morning/afternoon/evening/night)?
Is it weekend time (weekend/not weekend)?
""";
PromptTemplate promptTemplate = new KernelPromptTemplateFactory().tryCreate(
PromptTemplateConfig
.builder()
.withTemplate(functionDefinition)
.build()
);String systemPromptTemplate = "...";
PromptTemplate promptTemplate = PromptTemplateFactory.build(
PromptTemplateConfig
.builder()
.withTemplate(systemPromptTemplate)
.build()
);var renderedPrompt = promptTemplate.renderAsync(kernel, KernelFunctionArguments, InvocationContext).block();
System.out.println(renderedPrompt);Hooks are functions triggered in specific situations attached to the kernel.
-
Global Registration: If added to
kernel.getGlobalKernelHooks(), it is globally effectivekernel.getGlobalKernelHooks().addHook("hookName", KernelHook);
-
Single Call Registration: If passed as a parameter in
invokeAsync, it is effective for that call onlyKernelHooks kernelHooks = new KernelHooks(); kernelHooks.addPreChatCompletionHook(...); var result = kernel.invokeAsync(writerFunction) .withArguments(KernelFunctionArguments.builder().build()) .addKernelHooks(kernelHooks) .block();
Triggered before function call.
FunctionInvokingHook preHook = event -> {
System.out.println(event.getFunction().getName() + " : Pre Execution Handler - Triggered");
return event;
};
kernel.getGlobalKernelHooks().addHook("", preHook);Triggered after function call
FunctionInvokedHook hook = event -> {
String result = (String) event.getResult().getResult();
System.out.println(event.getFunction().getName() + " : Modified result via FunctionInvokedHook: " + result);
result = result.replaceAll("[aeiouAEIOU0-9]", "*");
return new FunctionInvokedEvent(
event.getFunction(),
event.getArguments(),
new FunctionResult<>(ContextVariable.of(result), event.getResult().getMetadata(), result)
);
};
kernel.getGlobalKernelHooks().addHook(hook);PromptRenderingHook myRenderingHandler = event -> {
System.out.println(event.getFunction().getName() + " : Triggered PromptRenderingHook");
event.getArguments().put("style", ContextVariable.of("Seinfeld"));
return event;
};PromptRenderedHook myRenderedHandler = event -> {
System.out.println(event.getFunction().getName() + " : Triggered PromptRenderedHook");
String prompt = event.getPrompt() + "\nUSE SHORT, CLEAR, COMPLETE SENTENCES.";
return new PromptRenderedEvent(event.getFunction(), event.getArguments(), prompt);
};
PromptRenderingHookandPromptRenderedHookare triggered only at the start of a conversation. They won't trigger during multiple tool calls. To trigger at every LLM interaction, useChatCompletionsHook
Add a pre-chat completion hook to add instructions before ChatCompletion.
kernel.getGlobalKernelHooks().addPreChatCompletionHook(event -> {
ChatCompletionsOptions options = event.getOptions();
List<ChatRequestMessage> messages = options.getMessages();
messages = new ArrayList<>(messages);
messages.add(new ChatRequestSystemMessage("Use upper case text when responding to the prompt."));
System.out.println("------- Triggered before ChatCompletion -------");
System.out.println("---- Added: Use upper case text when responding to the prompt. ----");
for (ChatRequestMessage msg : messages) {
System.out.println(msg);
}
return new PreChatCompletionEvent(
PreChatCompletionHook.cloneOptionsWithMessages(options, messages)
);
});Add a post-chat completion hook to adjust the output format
kernel.getGlobalKernelHooks().addPostChatCompletionHook(event -> {
System.out.println("------- Triggered after ChatCompletion -------");
System.out.println("--- Output ChatCompletion and id ----");
System.out.println("Chat completion");
System.out.println("Id: " + event.getChatCompletions().getId());
return event;
});