Understanding AI plugins in Semantic Kernel

With plugins, you can encapsulate AI capabilities into a single unit of functionality. Plugins are the building blocks of the Semantic Kernel and can interoperate with plugins in ChatGPT, Bing, and Microsoft 365.
Note
Skills are currently being renamed to plugins. This article has been updated to reflect the latest terminology, but some images and code samples may still refer to skills.
What is a plugin?
To drive alignment across the industry, we've adopted the OpenAI plugin specification as the standard for plugins. This will help create an ecosystem of interoperable plugins that can be used across all of the major AI apps and services like ChatGPT, Bing, and Microsoft 365.

For developers using Semantic Kernel, this means any plugins you build will soon be usable in ChatGPT, Bing, and Microsoft 365, allowing you to increase the reach of your AI capabilities without rewriting your code. It also means that plugins built for ChatGPT, Bing, and Microsoft 365 will be able to integrate with Semantic Kernel.
To show how to make interoperable plugins, we've created an in-depth walkthrough on how to create a ChatGPT plugin using OpenAI's specification and how to use that same plugin in Semantic Kernel. You can find the walkthrough in the Create and run ChatGPT plugins article.
What does a plugin look like?
At a high-level, a plugin is a group of functions that can be exposed to AI apps and services. The functions within plugins can then be orchestrated by an AI application to accomplish user requests. Within Semantic Kernel, you can invoke these functions either manually (see chaining functions) or automatically with a planner.
Just providing functions, however, is not enough to make a plugin. To power automatic orchestration with a planner, plugins also need to provide details that semantically describe how they behave. Everything from the function's input, output, and side effects need to be described in a way that the AI can understand, otherwise, planner will provide unexpected results.
For example, in the WriterSkill plugin, each function has a semantic description that describes what the function does. Planner can then use this description to choose the best function to call based on a user's ask.
In the picture on the right, planner would likely use the ShortPoem and StoryGen functions to satisfy the users ask thanks to the provided semantic descriptions.

Adding functions to plugins
Now that you know what a plugin is, let's take a look at how to create one. Within a plugin, you can create two types of functions: semantic functions and native functions. The following sections describe how to create each type. For further details, please refer to the Creating semantic functions and Creating native functions articles.
Semantic functions
If plugins represent the "body" of your AI app, then semantic functions would represent the ears and mouth of your AI. They allow your AI app to listen to users asks and respond back with a natural language response.
To connect the ears and the mouth to the "brain," Semantic Kernel uses connectors. This allows you to easily swap out the AI services without rewriting code.

Below is an sample called Summarize that can be found in the samples folder in the GitHub repository.
[SUMMARIZATION RULES]
DONT WASTE WORDS
USE SHORT, CLEAR, COMPLETE SENTENCES.
DO NOT USE BULLET POINTS OR DASHES.
USE ACTIVE VOICE.
MAXIMIZE DETAIL, MEANING
FOCUS ON THE CONTENT
[BANNED PHRASES]
This article
This document
This page
This material
[END LIST]
Summarize:
Hello how are you?
+++++
Hello
Summarize this
{{$input}}
+++++
To semantically describe this function (as well as define the configuration for the AI service), you must also create a config.json file in the same folder as the prompt. This file describes the function's input parameters and description. Below is the config.json file for the Summarize function.
{
"schema": 1,
"type": "completion",
"description": "Summarize given text or any text document",
"completion": {
"max_tokens": 512,
"temperature": 0.0,
"top_p": 0.0,
"presence_penalty": 0.0,
"frequency_penalty": 0.0
},
"input": {
"parameters": [
{
"name": "input",
"description": "Text to summarize",
"defaultValue": ""
}
]
}
}
Both description fields are used by planner, so it's important to provide a detailed, yet concise, description so planner can make the best decision when orchestrating functions together. We recommend testing multiple descriptions to see which one works best for the widest range of scenarios.
You can learn more about creating semantic functions in the Creating semantic functions article. In this article you'll learn the best practices for the following:
- How to create semantic functions
- Adding input parameters
- Calling functions within semantic functions
Native functions
With native functions, you can have the kernel call C# or Python code directly so that you can manipulate data or perform other operations. In this way, native functions are like the hands of your AI app. They can be used to save data, retrieve data, and perform any other operation that you can do in code that is ill-suited for LLMs (e.g., performing calculations).

Instead of providing a separate configuration file with semantic descriptions, planner is able to use annotations in the code to understand how the function behaves. Below are examples of the annotations used by planner in both C# and Python for out-of-the-box native functions.
The following code is an excerpt from the DocumentSkill plugin, which can be found in the document plugin folder in the GitHub repository. It demonstrates how you can use the SKFunction and SKFunctionInput attributes to describe the function's input and output to planner.
[SKFunction, Description("Read all text from a document")]
[SKFunctionInput(Description = "Path to the file to read")]
public async Task<string> ReadTextAsync(string filePath, SKContext context)
{
this._logger.LogInformation("Reading text from {0}", filePath);
using var stream = await this._fileSystemConnector.GetFileContentStreamAsync(filePath, context.CancellationToken).ConfigureAwait(false);
return this._documentConnector.ReadText(stream);
}
You can learn more about creating native functions in the Creating native functions article. In this article you'll learn the best practices for the following:
- How to create simple native functions
- Calling Semantic Kernel functions from within native functions
- Different ways to invoke native functions
Take the next step
Now that you understand the basics of plugins, you can now go deeper into the details of creating semantic and native functions for your plugin.

