Part 2/15 | The Developer's Guide to Semantic Kernel | Understanding Prompt Templates in Semantic Kernel
A Practical Guide to Creating and Using Prompt Templates in Semantic Kernel for Python Developers
As a seasoned senior web developer with a wealth of experience in Python, Javascript, web development, MySQL, MongoDB, and React, I am passionate about crafting exceptional digital experiences that delight users and drive business success.
Introduction to Prompt
Prompts gives context to a user query as well as provide guidance to LLM models. In simple terms prompting is the art of providing information and instructions to the LLM model so that it can generate the best answer. You can choose how much information you want to provide and what instructions you give the LLM. This is the idea behind different types of prompts.
In Zero-Shot prompting you ask the questions directly without giving any example. One-Shot is when you provide one clear and descriptive example what LLM shout output. In Few-Shot example you provide more than one example outputs to instruct LLM exactly how you want the output to be structured. Chain-of-Thought or COT is to be used when you want the LLM to explain its thought process. You can combine few-shot example with COT to get the best result. You do not want to specify how the LLM shows its reasoning, you will use Zero-Shot COT.
Zero-Shot Prompt
Can you name which anime Goku is from?
---
One-Shot Prompt
Can you name which anime Monkey D. Luffy is from?
Monkey D. Luffy is from anime name One Piece.
Can you name which anime Goku is from?
---
Few-Shot Prompt
Can you name which anime Naruto Uzumaki is from?
Naruto Uzumaki is from the anime named Naruto.
Can you name which anime Ichigo Kurosaki is from?
Ichigo Kurosaki is from the anime named Bleach.
Can you name which anime Monkey D. Luffy is from?
Monkey D. Luffy is from the anime named One Piece.
Can you name which anime Goku is from?
---
Chain-of-Thought Prompt (Zero-Shot COT)
Who would win in a fight: Zoro from One Piece or Levi from Attack on Titan? Explain your reasoning.
---
COT with Few-Shot Prompt
Example 1
Q: Who would win in a fight: Naruto vs Gon? Explain your reasoning.
Chain-of-Thought:
Naruto has access to Kurama, Sage Mode, and large chakra reserves.
Gon’s Adult Form grants large power boosts but is temporary and less versatile.
Naruto is superior in strength, agility, and speed due to chakra enhancements.
A: Naruto would win because his overall physical capabilities and techniques exceed Gon’s limits.
Example 2
Q: Who would win in a fight: Ichigo vs Tanjiro? Explain your reasoning.
Chain-of-Thought:
Ichigo’s Bankai and Hollow powers give him superhuman strength, agility, and extreme speed.
Tanjiro is fast and skilled but remains human-level compared to Ichigo’s spiritual power scaling.
Ichigo’s combat speed and destructive feats far surpass Tanjiro’s.
A: Ichigo would win because he operates on a vastly higher power scale than Tanjiro.
Q: Who would win in a fight: Zoro vs Luffy? Explain your reasoning.
Prompt Template in Semantic Kernel
Our main focus will be learning how you can use the prompt and prompt templates to get response from LLM. Prompt template is the predefined structure that you use to organize the information and instructions you want to pass to LLM. Semantic Kernel supports many template formats like semantic-kernel, handlebars and jinja for Python SDK.
At the very basic level prompt templates contain variables that can be replaced by user input or rag data. After all the data is available framework renders the template to generate the actual prompt that will be passed to LLM.
Semantic-Kernel Template Format
This is the most common and easy to use template format. This is the default format for prompt template as well. You can pass a variable to prompt template using syntax {{$var}}
prompt_template = "Write two lines summerizing the character's description. Character name {{$name}}"
Here name is the variable that you will pass to the template. You can pass the variables directly to invoke function like
kernel.invoke(anime_character_summery_function, name="Monkey D. Luffy")
But the best and recommended way is using KernelArguments. Both of these expressions are valid but KernelArguments provide some benefit over direct invocation.
import os
import semantic_kernel as sk
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
from semantic_kernel.functions.kernel_arguments import KernelArguments
kernel = sk.Kernel()
chat_completion = OpenAIChatCompletion(
service_id="default",
ai_model_id="gpt-5-nano-2025-08-07",
api_key=os.getenv('OPEN_AI_API_KEY')
)
kernel.add_service(chat_completion)
prompt_template = "Write two lines summerizing the character's description. Character name {{$name}}"
anime_character_summery_function = kernel.add_function(
plugin_name="AnimeDetails",
function_name="get_character_summary",
prompt=prompt_template,
template_format="semantic-kernel"
)
arguments = KernelArguments(name="Monkey D. Luffy")
async def main():
result = await kernel.invoke(anime_character_summery_function, arguments)
print(result)
if __name__ == '__main__':
import asyncio
asyncio.run(main())
Handlebars Template Format
Unlike semantic-kernel format, handlebars provides a lot of features to template. You can write loops and conditions in template along with declaring variables.
Syntax for writing variable
Character Name: {{name}}
Syntax for writing if/else
Role: {{#if role}}
{{role}}
{{else}}
Main Protagonist
{{/if}}
Syntax for writing array of strings
{{#each enemies}}
- {{this}}
{{/each}}
Syntax for writing array of nested objects
{{#each abilities}}
- {{this.name}}: {{this.description}}
{{/each}}
Combining all
Abilities:
{{#if abilities}}
{{#each abilities}}
- {{this.name}}: {{this.description}}
{{/each}}
{{else}}
- Not enough information available.
{{/if}}
To use the handlebars template format you have to use a class PromptTemplateConfig. You pass the configuration of your prompt template like reference to template, template format, input variables and more. If you want you can give more context to the input variables. You will see it in Prompt execution settings example
prompt_template_config = PromptTemplateConfig(
template=prompt,
allow_dangerously_set_content=True,
template_format="handlebars",
)
Once the configuration is ready you can use the class HandlebarsPromptTemplate to create your prompt template.
prompt_template = HandlebarsPromptTemplate(prompt_template_config=prompt_template_config)
You can render the prompt to see the actual prompt generated for LLM
prompt_rendered = await prompt_template.render(kernel, arguments)
print(prompt_rendered)
Putting it all togather
import os
import semantic_kernel as sk
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.prompt_template import PromptTemplateConfig, HandlebarsPromptTemplate
kernel = sk.Kernel()
chat_completion = OpenAIChatCompletion(
service_id="default",
ai_model_id="gpt-5-nano-2025-08-07",
api_key=os.getenv('OPEN_AI_API_KEY')
)
kernel.add_service(chat_completion)
prompt = r"""
{{!--
Handlebars Prompt Template demonstrating:
- Variables
- Conditionals
- Loops
- Default values
- Nested conditions
- Dynamic sections
- Optional fields
--}}
Generate a detailed anime character summary using the information below.
Character Name: {{name}}
{{!-- Default fallback if no role is provided --}}
Role: {{#if role}}{{role}}{{else}}Main Protagonist{{/if}}
{{!-- Show aliases if provided --}}
{{#if aliases}}
Aliases:
{{#each aliases}}
- {{this}}
{{/each}}
{{else}}
Aliases: None listed.
{{/if}}
{{!-- Show abilities --}}
Abilities:
{{#if abilities}}
{{#each abilities}}
- {{this.name}}: {{this.description}}
{{/each}}
{{else}}
- Not enough information available.
{{/if}}
{{!-- Optional friends section --}}
{{#if friends}}
Friends:
{{#each friends}}
- {{this}}
{{/each}}
{{/if}}
{{!-- Optional enemies section --}}
{{#if enemies}}
Enemies:
{{#each enemies}}
- {{this}}
{{/each}}
{{/if}}
{{!-- Additional personality notes --}}
Personality Summary:
{{#if personality}}
{{personality}}
{{else}}
A vibrant and adventurous anime character.
{{/if}}
{{!-- Closing instruction --}}
Write the final summary in a descriptive narrative format, with 3 to 5 paragraphs.
"""
prompt_template_config = PromptTemplateConfig(
template=prompt,
allow_dangerously_set_content=True,
template_format="handlebars",
)
prompt_template = HandlebarsPromptTemplate(prompt_template_config=prompt_template_config)
anime_character_summary_function = kernel.add_function(
plugin_name="AnimeCharacterInfo",
function_name="character_summary",
description="Generates a detailed summary of an anime character using Handlebars templating features.",
template_format="handlebars",
prompt_template=prompt_template
)
arguments = KernelArguments(
name="Monkey D. Luffy",
role="Pirate Captain",
aliases=["Straw Hat", "Future Pirate King"],
abilities=[
{"name": "Gum-Gum Powers", "description": "Elastic body enabling incredible attacks."},
{"name": "Conqueror's Haki", "description": "Ability to overwhelm others with sheer willpower."}
],
friends=["Zoro", "Nami", "Sanji", "Usopp"],
enemies=["Kaido", "Blackbeard"],
personality="Energetic, loyal, and driven by freedom."
)
async def main():
prompt_rendered = await prompt_template.render(kernel, arguments)
print(prompt_rendered, end="\n\n\n")
result = await kernel.invoke(anime_character_summary_function, arguments)
print(result)
if __name__ == '__main__':
import asyncio
asyncio.run(main())
Prompt Execution Settings
It is a configuration of how you want to run your LLM. You implement execution configuration by instantiating the class OpenAIChatPromptExecutionSettings. You can configure the response format and ask LLM to generate a json output
settings = OpenAIChatPromptExecutionSettings(
response_format={
"type": "json_schema",
"json_schema": {
"name": "joke_response",
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"anime": {"type": "string"},
"description": {"type": "string"},
},
"required": ["name", "anime", "description"],
"additionalProperties": False # REQUIRED by OpenAI
},
"strict": True
}
}
)
You can configure the kernel to call or not call any tools by setting function_choice_behavior param.
# May call LLM based on user query
settings.function_choice_behavior = FunctionChoiceBehavior.Auto()
# Will not call LLM
settings.function_choice_behavior = FunctionChoiceBehavior.None()
# Must call tool
settings.function_choice_behavior = FunctionChoiceBehavior.Required()
Configure the max token, temperature and top_p
settings = OpenAIPromptExecutionSettings(
max_tokens=150,
temperature=0.2,
top_p=1.0,
)
These are some common configurations but execution settings supports a lot more configuration.
You have to change the execution settings class based on which platform you use. If you use claude then instead of OpenAIPromptExecutionSettings you have to use AnthropicPromptExecutionSettings
import os
import pprint
import semantic_kernel as sk
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion, OpenAIChatPromptExecutionSettings, OpenAIPromptExecutionSettings
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.prompt_template import PromptTemplateConfig, InputVariable, KernelPromptTemplate
kernel = sk.Kernel()
chat_completion = OpenAIChatCompletion(
service_id="default",
ai_model_id="gpt-5-nano-2025-08-07",
api_key=os.getenv('OPEN_AI_API_KEY')
)
kernel.add_service(chat_completion)
prompt = "Write name, anime name, two lines summerizing the character's description. Character name {{$name}}"
settings = OpenAIChatPromptExecutionSettings(
response_format={
"type": "json_schema",
"json_schema": {
"name": "joke_response",
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"anime": {"type": "string"},
"description": {"type": "string"},
},
"required": ["name", "anime", "description"],
"additionalProperties": False # REQUIRED by OpenAI
},
"strict": True
}
}
)
config = PromptTemplateConfig(
template=prompt,
template_format="semantic-kernel",
input_variables=[
InputVariable(name="name", description="Name of the anime character", is_required=True)
],
description="Pass prompt to llm",
execution_settings=settings
)
function = kernel.add_function(
plugin_name="AnimeDetails",
function_name="get_character_summary",
prompt_template_config=config
)
arguments = KernelArguments(name="Monkey D. Luffy")
async def main():
import json
result = await kernel.invoke(function, arguments)
print(result)
if __name__ == '__main__':
import asyncio
asyncio.run(main())
In Template Function Call
One powerful feature of prompt template in Semantic Kernel is calling functions directly from template.
Create a kernel function, register it into kernel and then call it in the template directly. This can be a powerful and effective way of calling tools that needs to be called before passing the prompt to final LLM call. Example like getting current date, current exchange rate or weather status of a location. Basically data which is dynamic and should not be predicted.
import os
import semantic_kernel as sk
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.functions import kernel_function
from characters import characters
kernel = sk.Kernel()
chat_completion = OpenAIChatCompletion(
service_id="default",
ai_model_id="gpt-5-nano-2025-08-07",
api_key=os.getenv('OPEN_AI_API_KEY')
)
kernel.add_service(chat_completion)
class CharacterDetailsPlugin:
@kernel_function(description="Get character details if exists or return the name only")
def get_character(self, name: str):
if name in characters:
return characters[name]
return {"name": name}
kernel.add_plugin(CharacterDetailsPlugin(), plugin_name="CharacterDetails")
prompt_template = """
Write two lines summarizing the character's description.
Character details: {{CharacterDetails.get_character name=$name}}
"""
anime_character_summery_function = kernel.add_function(
plugin_name="AnimeDetails",
function_name="get_character_summary",
prompt=prompt_template,
template_format="semantic-kernel"
)
arguments = [
KernelArguments(name="Monkey D. Luffy"),
KernelArguments(name="Kenshi Ryu"),
]
async def main():
for argument in arguments:
result = await kernel.invoke(anime_character_summery_function, argument)
print(result)
if __name__ == '__main__':
import asyncio
asyncio.run(main())