The recent 1.0 release of Semantic Kernel introduced multiple breaking changes, with the goal to align the naming and the features with the AI industry. In the previous post, we focused on the basic scenarios, like setting up the connection to an AI service and executing a basic prompt. In this post, instead, we’ll cover one of the features that was heavily affected by the breaking changes: semantic functions, which are now called prompt functions. In this post, we’re going to review the changes and see how to migrate our existing code to the new version.
From semantic functions to prompt functions
Due to the rebranding into prompt functions, now we have also a different method to import a semantic function inside the kernel. Instead of using ImportSemanticFunctionsFromDirectory()
, we must use a new method called ImportPluginFromPromptDirectory()
, as in the following example:
|
|
Compared to the previous code, there’s an important difference. In the original version, it was enough to pass as parameter the path of the entire Plugins folder (YourProject/Plugins
), then the kernel was able to figure out the specific plugin folder to use by leveraging the name of the plugin (MailPlugin
, in our case). In the new version, we must pass, instead, the full path of the folder which contains the specific plugin (YourProject/Plugins/MailPlugin
, in our case).
Changing the configuration of the prompt
When you create a prompt function using the classic approach we have learned about in the original post, you have two files inside the folder which represents your plugin: one for the prompt (named skprompt.txt
) and one for the configuration (named config.json
).
The configuration file includes the configuration of the prompt, like the LLM parameters, the description, etc. This is an example of the configuration file we have seen in the original post:
|
|
Semantic Kernel 1.0 introduced a few minor changes in the way you define the input and output parameters, as in the following example:
|
|
As you can notice, we don’t have anymore the input
property with, as a child, the parameters
collection. Instead, we have a new property called input_variables
, which is a collection of objects, each of them representing a parameter. The defaultValue
property has been renamed to default
and the required
property has been renamed to is_required
.
Changing the configuration of the prompt to use the new schema is critically important when we use planners and automatic function calling, otherwise they won’t be able to figure out which the input parameter are accepted by the prompt.
Executing the function
Another important change involves function storage. Functions are no longer stored in the kernel’s Functions
collection. Instead, they now reside in the Plugins
collection. This means that, to get a reference to a function, we must call the GetFunction()
method exposed by the Plugins
collection passing, as before, the name of the plugin and the name of the function we want to use.
|
|
The rest of the changes are the same ones we have learned about in the previous post:
ContextVariables
has been renamed toKernelArguments
.- The
RunAsync()
method has been renamed toInvokeAsync()
and we must pass the parameters in the reverse order (first the function, then the variables). If you prefer, you can use also theInvokeStreamingAsync()
method, if you prefer to stream the response as it gets generated, instead of waiting for it to be fully available.
Using YAML to define a prompt function
One of the most relevant new features in 1.0 around prompt functions is the ability to define them using a YAML file. The advantage of this approach, over the traditional one, is that we don’t have anymore to maintain two different files, one for the prompt and one for the configuration, but we can consolidate everything in a single file. Let’s see how it works.
The first step is to add a YAML file in your project. When you use this approach, you don’t have to follow a specific folder structure for your plugin, since we’re going to read the YAML file as a string. To keep a consistent structure, I’ve created a folder called MailPluginYaml
inside the Plugins
folder and I’ve added a file called WriteBusinessMail.yaml
, which is defined like this:
|
|
The declaration should be easy to understand: with the various properties, we define the key features of the function, like the name, the template, the description, the inputs and the outputs. All these information will be used by Semantic Kernel to determine if the function is a good candidate to solve a specific problem.
Now that we have a prompt function defined using a YAML file, the only difference is the method we must use to load the plugin in the kernel:
|
|
First, we use File.ReadAllText()
to read the content of the YAML file as a string. Then, we pass the string to the CreateFunctionFromPromptYaml()
method, which returns a reference to the function. The rest of the code is the same as the previous example: we define the input variables using a KernelArguments
collection and we pass it to the InvokeAsync()
method, together with the function.
It’s important to highlight that the CreateFunctionFromPromptYaml()
method only generates a function out of the YAML definition, but it doesn’t store it into the kernel. This means that you can use this semantic prompt if you explicitly invoke it (like we did in the previous example), but if you want to use a more automated approach (like with the planner), it wouldn’t work.
If you want to store it into the kernel, you have to use the KernelPluginFactory
helper to create a KernelPlugin
object and then import it using the ImportPluginFromObject()
method, as in the following example:
|
|
In our case, we already have a function that we want to embed into a plugin, so we use the CreateFromFunctions()
method of the KernePluginFactory
class, which accepts as input the name of the plugin and a collection of functions which, in our case, contains only one element.
Wrapping up
In this post, you have learned how to migrate semantic functions to prompt functions, using the new syntax and the new features included in Semantic Kernel 1.0. You have also learned how to transition from the classic plugins approach, based on a text file with the prompt and a configuration file with the parameters, to a new approach based on a single YAML file.
You will find the updated example in the GitHub repository of the project.
Happy coding!